iclass_perimeter.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. /*!
  2. \file lib/imagery/iclass_perimeter.c
  3. \brief Imagery library - functions for wx.iclass
  4. Computation based on training areas for supervised classification.
  5. Based on i.class module (GRASS 6).
  6. Vector map with training areas is used to determine corresponding
  7. cells by computing cells on area perimeter.
  8. Copyright (C) 1999-2007, 2011 by the GRASS Development Team
  9. This program is free software under the GNU General Public License
  10. (>=v2). Read the file COPYING that comes with GRASS for details.
  11. \author David Satnik, Central Washington University (original author)
  12. \author Markus Neteler <neteler itc.it> (i.class module)
  13. \author Bernhard Reiter <bernhard intevation.de> (i.class module)
  14. \author Brad Douglas <rez touchofmadness.com>(i.class module)
  15. \author Glynn Clements <glynn gclements.plus.com> (i.class module)
  16. \author Hamish Bowman <hamish_b yahoo.com> (i.class module)
  17. \author Jan-Oliver Wagner <jan intevation.de> (i.class module)
  18. \author Anna Kratochvilova <kratochanna gmail.com> (rewriting for wx.iclass)
  19. \author Vaclav Petras <wenzeslaus gmail.com> (rewriting for wx.iclass)
  20. */
  21. #include <stdlib.h>
  22. #include <grass/vector.h>
  23. #include <grass/raster.h>
  24. #include <grass/glocale.h>
  25. #include "iclass_local_proto.h"
  26. #define extrema(x,y,z) (((x<y)&&(z<y))||((x>y)&&(z>y)))
  27. #define non_extrema(x,y,z) (((x<y)&&(y<z))||((x>y)&&(y>z)))
  28. /*!
  29. \brief Creates perimeters from vector areas of given category.
  30. \param Map vector map
  31. \param layer_name layer name (within vector map)
  32. \param category vector category (cat column value)
  33. \param[out] perimeters list of perimeters
  34. \param band_region region which determines perimeter cells
  35. \return number of areas of given cat
  36. \return -1 on error
  37. */
  38. int vector2perimeters(struct Map_info *Map, const char *layer_name,
  39. int category, IClass_perimeter_list * perimeters,
  40. struct Cell_head *band_region)
  41. {
  42. struct line_pnts *points;
  43. int nareas, nareas_cat, layer;
  44. int i, cat, ret;
  45. int j;
  46. G_debug(3, "iclass_vector2perimeters():layer = %s, category = %d",
  47. layer_name, category);
  48. layer = Vect_get_field_number(Map, layer_name);
  49. nareas = Vect_get_num_areas(Map);
  50. if (nareas == 0)
  51. return 0;
  52. nareas_cat = 0;
  53. /* find out, how many areas have given category */
  54. for (i = 1; i <= nareas; i++) {
  55. if (!Vect_area_alive(Map, i))
  56. continue;
  57. cat = Vect_get_area_cat(Map, i, layer);
  58. if (cat < 0) {
  59. /* no centroid, no category */
  60. }
  61. else if (cat == category) {
  62. nareas_cat++;
  63. }
  64. }
  65. if (nareas_cat == 0)
  66. return 0;
  67. perimeters->nperimeters = nareas_cat;
  68. perimeters->perimeters =
  69. (IClass_perimeter *) G_calloc(nareas_cat, sizeof(IClass_perimeter));
  70. j = 0; /* area with cat */
  71. for (i = 1; i <= nareas; i++) {
  72. if (!Vect_area_alive(Map, i))
  73. continue;
  74. cat = Vect_get_area_cat(Map, i, layer);
  75. if (cat < 0) {
  76. /* no centroid, no category */
  77. }
  78. else if (cat == category) {
  79. j++;
  80. points = Vect_new_line_struct(); /* Vect_destroy_line_struct */
  81. ret = Vect_get_area_points(Map, i, points);
  82. if (ret <= 0) {
  83. Vect_destroy_line_struct(points);
  84. free_perimeters(perimeters);
  85. G_warning(_("Get area %d failed"), i);
  86. return -1;
  87. }
  88. if (make_perimeter
  89. (points, &perimeters->perimeters[j - 1], band_region) <= 0) {
  90. Vect_destroy_line_struct(points);
  91. free_perimeters(perimeters);
  92. G_warning(_("Perimeter computation failed"));
  93. return -1;
  94. }
  95. Vect_destroy_line_struct(points);
  96. }
  97. }
  98. /* Vect_close(&Map); */
  99. return nareas_cat;
  100. }
  101. /*!
  102. \brief Frees all perimeters in list of perimeters.
  103. It also frees list of perimeters itself.
  104. \param perimeters list of perimeters
  105. */
  106. void free_perimeters(IClass_perimeter_list * perimeters)
  107. {
  108. int i;
  109. G_debug(5, "free_perimeters()");
  110. for (i = 0; i < perimeters->nperimeters; i++) {
  111. G_free(perimeters->perimeters[i].points);
  112. }
  113. G_free(perimeters->perimeters);
  114. }
  115. /*!
  116. \brief Creates one perimeter from vector area.
  117. \param points list of vertices represting area
  118. \param[out] perimeter perimeter
  119. \param band_region region which determines perimeter cells
  120. \return 1 on success
  121. \return 0 on error
  122. */
  123. int make_perimeter(struct line_pnts *points, IClass_perimeter * perimeter,
  124. struct Cell_head *band_region)
  125. {
  126. IClass_point *tmp_points;
  127. IClass_point *vertex_points;
  128. int i, first, prev, skip, next;
  129. int count, vertex_count;
  130. int np; /* perimeter estimate */
  131. G_debug(5, "iclass_make_perimeter()");
  132. count = points->n_points;
  133. tmp_points = (IClass_point *) G_calloc(count, sizeof(IClass_point)); /* TODO test */
  134. for (i = 0; i < count; i++) {
  135. G_debug(5, "iclass_make_perimeter(): points: x: %f y: %f",
  136. points->x[i], points->y[i]);
  137. tmp_points[i].y = Rast_northing_to_row(points->y[i], band_region);
  138. tmp_points[i].x = Rast_easting_to_col(points->x[i], band_region);
  139. }
  140. /* find first edge which is not horizontal */
  141. first = -1;
  142. prev = count - 1;
  143. for (i = 0; i < count; prev = i++) {
  144. /* non absurd polygon has vertexes with different y coordinate */
  145. if (tmp_points[i].y != tmp_points[prev].y) {
  146. first = i;
  147. break;
  148. }
  149. }
  150. if (first < 0) {
  151. G_free(tmp_points);
  152. G_warning(_("Absurd polygon."));
  153. return 0;
  154. }
  155. /* copy tmp to vertex list collapsing adjacent horizontal edges */
  156. /* vertex_count <= count, size of vertex_points is count */
  157. vertex_points = (IClass_point *) G_calloc(count, sizeof(IClass_point)); /* TODO test */
  158. skip = 0;
  159. vertex_count = 0;
  160. i = first; /* stmt not necssary */
  161. do {
  162. if (!skip) {
  163. vertex_points[vertex_count].x = tmp_points[i].x;
  164. vertex_points[vertex_count].y = tmp_points[i].y;
  165. vertex_count++;
  166. }
  167. prev = i++;
  168. if (i >= count)
  169. i = 0;
  170. if ((next = i + 1) >= count)
  171. next = 0;
  172. skip = ((tmp_points[prev].y == tmp_points[i].y) &&
  173. (tmp_points[next].y == tmp_points[i].y));
  174. }
  175. while (i != first);
  176. G_free(tmp_points);
  177. /* count points on the perimeter */
  178. np = 0;
  179. prev = vertex_count - 1;
  180. for (i = 0; i < vertex_count; prev = i++) {
  181. np += abs(vertex_points[prev].y - vertex_points[i].y);
  182. }
  183. /* allocate perimeter list */
  184. perimeter->points = (IClass_point *) G_calloc(np, sizeof(IClass_point));
  185. if (!perimeter->points) {
  186. G_free(vertex_points);
  187. G_warning(_("Outlined area is too large."));
  188. return 0;
  189. }
  190. /* store the perimeter points */
  191. perimeter->npoints = 0;
  192. prev = vertex_count - 1;
  193. for (i = 0; i < vertex_count; prev = i++) {
  194. edge2perimeter(perimeter, vertex_points[prev].x,
  195. vertex_points[prev].y, vertex_points[i].x,
  196. vertex_points[i].y);
  197. }
  198. /*
  199. * now decide which verticies should be included
  200. * local extrema are excluded
  201. * local non-extrema are included
  202. * verticies of horizontal edges which are pseudo-extrema
  203. * are excluded.
  204. * one vertex of horizontal edges which are pseudo-non-extrema
  205. * are included.
  206. */
  207. prev = vertex_count - 1;
  208. i = 0;
  209. do {
  210. next = i + 1;
  211. if (next >= vertex_count)
  212. next = 0;
  213. if (extrema
  214. (vertex_points[prev].y, vertex_points[i].y,
  215. vertex_points[next].y))
  216. skip = 1;
  217. else if (non_extrema
  218. (vertex_points[prev].y, vertex_points[i].y,
  219. vertex_points[next].y))
  220. skip = 0;
  221. else {
  222. skip = 0;
  223. if (++next >= vertex_count)
  224. next = 0;
  225. if (extrema
  226. (vertex_points[prev].y, vertex_points[i].y,
  227. vertex_points[next].y))
  228. skip = 1;
  229. }
  230. if (!skip)
  231. perimeter_add_point(perimeter, vertex_points[i].x,
  232. vertex_points[i].y);
  233. i = next;
  234. prev = i - 1;
  235. }
  236. while (i != 0);
  237. G_free(vertex_points);
  238. /* sort the edge points by row and then by col */
  239. qsort(perimeter->points, (size_t) perimeter->npoints,
  240. sizeof(IClass_point), edge_order);
  241. return 1;
  242. }
  243. /*!
  244. \brief Converts edge to cells.
  245. It rasterizes edge given by two vertices.
  246. Resterized points are added to perimeter.
  247. \param perimeter perimeter
  248. \param x0,y0 first edge point row and cell
  249. \param x1,y1 second edge point row and cell
  250. \return 1 on success
  251. \return 0 on error
  252. */
  253. int edge2perimeter(IClass_perimeter * perimeter, int x0, int y0, int x1,
  254. int y1)
  255. {
  256. float m;
  257. float x;
  258. if (y0 == y1)
  259. return 0;
  260. x = x0;
  261. m = (float)(x0 - x1) / (float)(y0 - y1);
  262. if (y0 < y1) {
  263. while (++y0 < y1) {
  264. x0 = (x += m) + .5;
  265. perimeter_add_point(perimeter, x0, y0);
  266. }
  267. }
  268. else {
  269. while (--y0 > y1) {
  270. x0 = (x -= m) + .5;
  271. perimeter_add_point(perimeter, x0, y0);
  272. }
  273. }
  274. return 1;
  275. }
  276. /*!
  277. \brief Adds point to perimeter.
  278. \a perimeter has to have allocated space for \c points memeber.
  279. \param perimeter perimeter
  280. \param x,y point row and cell
  281. */
  282. void perimeter_add_point(IClass_perimeter * perimeter, int x, int y)
  283. {
  284. int n;
  285. G_debug(5, "perimeter_add_point(): x: %d, y: %d", x, y);
  286. n = perimeter->npoints++;
  287. perimeter->points[n].x = x;
  288. perimeter->points[n].y = y;
  289. }
  290. /*!
  291. \brief Determines points order during sorting.
  292. \param aa first IClass_point
  293. \param bb second IClass_point
  294. */
  295. int edge_order(const void *aa, const void *bb)
  296. {
  297. const IClass_point *a = aa;
  298. const IClass_point *b = bb;
  299. if (a->y < b->y)
  300. return -1;
  301. if (a->y > b->y)
  302. return 1;
  303. if (a->x < b->x)
  304. return -1;
  305. if (a->x > b->x)
  306. return 1;
  307. return 0;
  308. }