iclass_perimeter.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  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. /* This functions are no longer used because of the different behavior
  138. of Rast_easting_to_col depending whether location is LL or not.
  139. It makes problem in interactive scatter plot tool,
  140. which defines its own coordinates systems for the plots and
  141. therefore it requires the function to work always in same way
  142. without hidden dependency on location type.
  143. tmp_points[i].y = Rast_northing_to_row(points->y[i], band_region);
  144. tmp_points[i].x = Rast_easting_to_col(points->x[i], band_region);
  145. */
  146. tmp_points[i].y = (band_region->north - points->y[i]) / band_region->ns_res;
  147. tmp_points[i].x = (points->x[i] - band_region->west) / band_region->ew_res;
  148. }
  149. /* find first edge which is not horizontal */
  150. first = -1;
  151. prev = count - 1;
  152. for (i = 0; i < count; prev = i++) {
  153. /* non absurd polygon has vertexes with different y coordinate */
  154. if (tmp_points[i].y != tmp_points[prev].y) {
  155. first = i;
  156. break;
  157. }
  158. }
  159. if (first < 0) {
  160. G_free(tmp_points);
  161. G_warning(_("Absurd polygon."));
  162. return 0;
  163. }
  164. /* copy tmp to vertex list collapsing adjacent horizontal edges */
  165. /* vertex_count <= count, size of vertex_points is count */
  166. vertex_points = (IClass_point *) G_calloc(count, sizeof(IClass_point)); /* TODO test */
  167. skip = 0;
  168. vertex_count = 0;
  169. i = first; /* stmt not necssary */
  170. do {
  171. if (!skip) {
  172. vertex_points[vertex_count].x = tmp_points[i].x;
  173. vertex_points[vertex_count].y = tmp_points[i].y;
  174. vertex_count++;
  175. }
  176. prev = i++;
  177. if (i >= count)
  178. i = 0;
  179. if ((next = i + 1) >= count)
  180. next = 0;
  181. skip = ((tmp_points[prev].y == tmp_points[i].y) &&
  182. (tmp_points[next].y == tmp_points[i].y));
  183. }
  184. while (i != first);
  185. G_free(tmp_points);
  186. /* count points on the perimeter */
  187. np = 0;
  188. prev = vertex_count - 1;
  189. for (i = 0; i < vertex_count; prev = i++) {
  190. np += abs(vertex_points[prev].y - vertex_points[i].y);
  191. }
  192. /* allocate perimeter list */
  193. perimeter->points = (IClass_point *) G_calloc(np, sizeof(IClass_point));
  194. if (!perimeter->points) {
  195. G_free(vertex_points);
  196. G_warning(_("Outlined area is too large."));
  197. return 0;
  198. }
  199. /* store the perimeter points */
  200. perimeter->npoints = 0;
  201. prev = vertex_count - 1;
  202. for (i = 0; i < vertex_count; prev = i++) {
  203. edge2perimeter(perimeter, vertex_points[prev].x,
  204. vertex_points[prev].y, vertex_points[i].x,
  205. vertex_points[i].y);
  206. }
  207. /*
  208. * now decide which vertices should be included
  209. * local extrema are excluded
  210. * local non-extrema are included
  211. * vertices of horizontal edges which are pseudo-extrema
  212. * are excluded.
  213. * one vertex of horizontal edges which are pseudo-non-extrema
  214. * are included.
  215. */
  216. prev = vertex_count - 1;
  217. i = 0;
  218. do {
  219. next = i + 1;
  220. if (next >= vertex_count)
  221. next = 0;
  222. if (extrema
  223. (vertex_points[prev].y, vertex_points[i].y,
  224. vertex_points[next].y))
  225. skip = 1;
  226. else if (non_extrema
  227. (vertex_points[prev].y, vertex_points[i].y,
  228. vertex_points[next].y))
  229. skip = 0;
  230. else {
  231. skip = 0;
  232. if (++next >= vertex_count)
  233. next = 0;
  234. if (extrema
  235. (vertex_points[prev].y, vertex_points[i].y,
  236. vertex_points[next].y))
  237. skip = 1;
  238. }
  239. if (!skip)
  240. perimeter_add_point(perimeter, vertex_points[i].x,
  241. vertex_points[i].y);
  242. i = next;
  243. prev = i - 1;
  244. }
  245. while (i != 0);
  246. G_free(vertex_points);
  247. /* sort the edge points by row and then by col */
  248. qsort(perimeter->points, (size_t) perimeter->npoints,
  249. sizeof(IClass_point), edge_order);
  250. return 1;
  251. }
  252. /*!
  253. \brief Converts edge to cells.
  254. It rasterizes edge given by two vertices.
  255. Resterized points are added to perimeter.
  256. \param perimeter perimeter
  257. \param x0,y0 first edge point row and cell
  258. \param x1,y1 second edge point row and cell
  259. \return 1 on success
  260. \return 0 on error
  261. */
  262. int edge2perimeter(IClass_perimeter * perimeter, int x0, int y0, int x1,
  263. int y1)
  264. {
  265. float m;
  266. float x;
  267. if (y0 == y1)
  268. return 0;
  269. x = x0;
  270. m = (float)(x0 - x1) / (float)(y0 - y1);
  271. if (y0 < y1) {
  272. while (++y0 < y1) {
  273. x0 = (x += m) + .5;
  274. perimeter_add_point(perimeter, x0, y0);
  275. }
  276. }
  277. else {
  278. while (--y0 > y1) {
  279. x0 = (x -= m) + .5;
  280. perimeter_add_point(perimeter, x0, y0);
  281. }
  282. }
  283. return 1;
  284. }
  285. /*!
  286. \brief Adds point to perimeter.
  287. \a perimeter has to have allocated space for \c points member.
  288. \param perimeter perimeter
  289. \param x,y point row and cell
  290. */
  291. void perimeter_add_point(IClass_perimeter * perimeter, int x, int y)
  292. {
  293. int n;
  294. G_debug(5, "perimeter_add_point(): x: %d, y: %d", x, y);
  295. n = perimeter->npoints++;
  296. perimeter->points[n].x = x;
  297. perimeter->points[n].y = y;
  298. }
  299. /*!
  300. \brief Determines points order during sorting.
  301. \param aa first IClass_point
  302. \param bb second IClass_point
  303. */
  304. int edge_order(const void *aa, const void *bb)
  305. {
  306. const IClass_point *a = aa;
  307. const IClass_point *b = bb;
  308. if (a->y < b->y)
  309. return -1;
  310. if (a->y > b->y)
  311. return 1;
  312. if (a->x < b->x)
  313. return -1;
  314. if (a->x > b->x)
  315. return 1;
  316. return 0;
  317. }