cont.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. /****************************************************************************
  2. *
  3. * MODULE: r.contour
  4. *
  5. * AUTHOR(S): Terry Baker - CERL
  6. * Andrea Aime <aaime liberto it>
  7. *
  8. * PURPOSE: Produces a vector map of specified contours from a
  9. * raster map layer.
  10. *
  11. * COPYRIGHT: (C) 2001 by the GRASS Development Team
  12. *
  13. * This program is free software under the GNU General Public
  14. * License (>=v2). Read the file COPYING that comes with GRASS
  15. * for details.
  16. *
  17. ***************************************************************************/
  18. /* Algorithm comment from Jim Westervelt:
  19. It appears that the code is doing a linear interpolation between cells and
  20. finding where a given contour crosses through each cell. In the end,
  21. strings of coordinates are generated for user selected contours. In GRASS,
  22. these are lines and not necessarily polygons.
  23. */
  24. #include <stdio.h>
  25. #include <math.h>
  26. #include <grass/gis.h>
  27. #include <grass/raster.h>
  28. #include <grass/vector.h>
  29. #include <grass/glocale.h>
  30. #include "local_proto.h"
  31. struct cell
  32. {
  33. DCELL z[4];
  34. int r, c;
  35. int edge;
  36. };
  37. static int getnewcell(struct cell *, int, int, DCELL **);
  38. static void newedge(struct cell *);
  39. static int findcrossing(struct cell *, double,
  40. struct Cell_head, struct line_pnts *, int *);
  41. static void getpoint(struct cell *curr, double,
  42. struct Cell_head, struct line_pnts *);
  43. void contour(double levels[],
  44. int nlevels,
  45. struct Map_info Map,
  46. DCELL ** z, struct Cell_head Cell, int n_cut)
  47. {
  48. int nrow, ncol; /* number of rows and columns in current region */
  49. int startrow, startcol; /* start row and col of current line */
  50. int n, i, j; /* loop counters */
  51. double level; /* current contour level */
  52. char **hit; /* array of flags--1 if Cell has been hit; */
  53. /* 0 if Cell is still to be checked */
  54. struct line_pnts *Points;
  55. struct line_cats *Cats;
  56. int outside; /* 1 if line is exiting region; 0 otherwise */
  57. struct cell current;
  58. int p1, p2; /* indexes to end points of cell edges */
  59. int ncrossing; /* number of found crossing */
  60. Points = Vect_new_line_struct();
  61. Cats = Vect_new_cats_struct();
  62. nrow = Cell.rows;
  63. ncol = Cell.cols;
  64. hit = (char **)G_malloc((nrow - 1) * sizeof(char *));
  65. for (i = 0; i < nrow - 1; i++)
  66. hit[i] = (char *)G_malloc((ncol - 1) * sizeof(char));
  67. ncrossing = 0;
  68. G_message(n_("Writing vector contour (one level)...",
  69. "Writing vector contours (total levels %d)...", nlevels), nlevels);
  70. for (n = 0; n < nlevels; n++) {
  71. level = levels[n];
  72. G_percent(n+1, nlevels, 2); /* print progress */
  73. /* initialize hit array */
  74. for (i = 0; i < nrow - 1; i++) {
  75. for (j = 0; j < ncol - 1; j++) {
  76. hit[i][j] = 0;
  77. }
  78. }
  79. /* check each cell of top and bottom borders */
  80. for (startrow = 0; startrow < nrow; startrow += (nrow - 2)) {
  81. for (startcol = 0; startcol <= ncol - 2; startcol++) {
  82. /* look for starting point of new line */
  83. if (!hit[startrow][startcol]) {
  84. current.r = startrow;
  85. current.c = startcol;
  86. outside = getnewcell(&current, nrow, ncol, z);
  87. /* is this top or bottom? */
  88. if (startrow == 0) /* top */
  89. current.edge = 0;
  90. else /* bottom edge */
  91. current.edge = 2;
  92. p1 = current.edge;
  93. p2 = current.edge + 1;
  94. if (checkedge(current.z[p1], current.z[p2], level)) {
  95. getpoint(&current, level, Cell, Points);
  96. /* while not off an edge, follow line */
  97. while (!outside) {
  98. hit[current.r][current.c] +=
  99. findcrossing(&current, level, Cell, Points,
  100. &ncrossing);
  101. newedge(&current);
  102. outside = getnewcell(&current, nrow, ncol, z);
  103. }
  104. if ((n_cut <= 0) || ((Points->n_points) > n_cut)) {
  105. Vect_reset_cats(Cats);
  106. Vect_cat_set(Cats, 1, n + 1);
  107. Vect_write_line(&Map, GV_LINE, Points, Cats);
  108. }
  109. Vect_reset_line(Points);
  110. } /* if checkedge */
  111. } /* if ! hit */
  112. } /* for columns */
  113. } /* for rows */
  114. /* check right and left borders (each row of first and last column) */
  115. for (startcol = 0; startcol < ncol; startcol += (ncol - 2)) {
  116. for (startrow = 0; startrow <= nrow - 2; startrow++) {
  117. /* look for starting point of new line */
  118. if (!hit[startrow][startcol]) {
  119. current.r = startrow;
  120. current.c = startcol;
  121. outside = getnewcell(&current, nrow, ncol, z);
  122. /* is this left or right edge? */
  123. if (startcol == 0) /* left */
  124. current.edge = 3;
  125. else /* right edge */
  126. current.edge = 1;
  127. p1 = current.edge;
  128. p2 = (current.edge + 1) % 4;
  129. if (checkedge(current.z[p1], current.z[p2], level)) {
  130. getpoint(&current, level, Cell, Points);
  131. /* while not off an edge, follow line */
  132. while (!outside) {
  133. hit[current.r][current.c] +=
  134. findcrossing(&current, level, Cell, Points,
  135. &ncrossing);
  136. newedge(&current);
  137. outside = getnewcell(&current, nrow, ncol, z);
  138. }
  139. if ((n_cut <= 0) || ((Points->n_points) > n_cut)) {
  140. Vect_reset_cats(Cats);
  141. Vect_cat_set(Cats, 1, n + 1);
  142. Vect_write_line(&Map, GV_LINE, Points, Cats);
  143. }
  144. Vect_reset_line(Points);
  145. } /* if checkedge */
  146. } /* if ! hit */
  147. } /* for rows */
  148. } /* for columns */
  149. /* check each interior Cell */
  150. for (startrow = 1; startrow <= nrow - 3; startrow++) {
  151. for (startcol = 1; startcol <= ncol - 3; startcol++) {
  152. /* look for starting point of new line */
  153. if (!hit[startrow][startcol]) {
  154. current.r = startrow;
  155. current.c = startcol;
  156. current.edge = 0;
  157. outside = getnewcell(&current, nrow, ncol, z);
  158. if (!outside &&
  159. checkedge(current.z[0], current.z[1], level)) {
  160. getpoint(&current, level, Cell, Points);
  161. hit[current.r][current.c] +=
  162. findcrossing(&current, level, Cell, Points,
  163. &ncrossing);
  164. newedge(&current);
  165. outside = getnewcell(&current, nrow, ncol, z);
  166. /* while not back to starting point, follow line */
  167. while (!outside &&
  168. ((current.edge != 0) ||
  169. ((current.r != startrow) ||
  170. (current.c != startcol)))) {
  171. hit[current.r][current.c] +=
  172. findcrossing(&current, level, Cell, Points,
  173. &ncrossing);
  174. newedge(&current);
  175. outside = getnewcell(&current, nrow, ncol, z);
  176. }
  177. if ((n_cut <= 0) || ((Points->n_points) > n_cut)) {
  178. Vect_reset_cats(Cats);
  179. Vect_cat_set(Cats, 1, n + 1);
  180. Vect_write_line(&Map, GV_LINE, Points, Cats);
  181. }
  182. Vect_reset_line(Points);
  183. } /* if checkedge */
  184. } /* if ! hit */
  185. } /* for rows */
  186. } /* for columns */
  187. } /* for levels */
  188. if (ncrossing > 0) {
  189. G_warning(n_("%d crossing found",
  190. "%d crossings found",
  191. ncrossing), ncrossing);
  192. }
  193. Vect_destroy_line_struct(Points);
  194. Vect_destroy_cats_struct(Cats);
  195. }
  196. /***************************************************************************
  197. getnewcell
  198. if cell is in range, finds data values of corner points of current cell
  199. returns 0
  200. else returns 1
  201. ***************************************************************************/
  202. static int getnewcell(struct cell *current, int nrow, int ncol, DCELL ** z)
  203. {
  204. if ((current->r >= 0) && (current->r <= nrow - 2) &&
  205. (current->c >= 0) && (current->c <= ncol - 2)) {
  206. current->z[0] = z[current->r][current->c];
  207. current->z[1] = z[current->r][current->c + 1];
  208. current->z[2] = z[current->r + 1][current->c + 1];
  209. current->z[3] = z[current->r + 1][current->c];
  210. return 0;
  211. }
  212. return 1;
  213. }
  214. /*******************************************************************
  215. newedge-updates edge number and row and col to those of next cell
  216. *******************************************************************/
  217. static void newedge(struct cell *current)
  218. {
  219. switch (current->edge) {
  220. case 0:
  221. current->r -= 1;
  222. current->edge = 2;
  223. break;
  224. case 1:
  225. current->c += 1;
  226. current->edge = 3;
  227. break;
  228. case 2:
  229. current->r += 1;
  230. current->edge = 0;
  231. break;
  232. case 3:
  233. current->c -= 1;
  234. current->edge = 1;
  235. break;
  236. default:
  237. G_fatal_error(_("Illegal edge number"));
  238. }
  239. }
  240. /***********************************************************************
  241. findcrossing-- decides which edge exit point from cell is on and changes
  242. value of edge.
  243. Returns 1 if only 2 crossings found ( don't want to check this Cell again);
  244. 0 otherwise.
  245. ****************************************************************************/
  246. static int findcrossing(struct cell *current, double level,
  247. struct Cell_head Cell, struct line_pnts *Points,
  248. int *ncrossing)
  249. {
  250. int i, j;
  251. int numcross; /* number of crossings found in this Cell */
  252. int edgehit[4]; /* hit flag for each edge of Cell */
  253. int cellhit = 0;
  254. double mid;
  255. numcross = 0;
  256. for (i = 0; i < 4; i++) {
  257. edgehit[i] = 0;
  258. edgehit[i] = checkedge(current->z[i], current->z[(i + 1) % 4], level);
  259. if (edgehit[i])
  260. numcross++;
  261. }
  262. if (numcross == 2) {
  263. cellhit = 1;
  264. edgehit[current->edge] = 0;
  265. for (j = 0; j < 4; j++) {
  266. if (edgehit[j]) {
  267. current->edge = j;
  268. getpoint(current, level, Cell, Points);
  269. break;
  270. }
  271. }
  272. }
  273. else if (numcross == 4) {
  274. if (current->edge == 0)
  275. cellhit = 1;
  276. mid =
  277. (current->z[0] + current->z[1] + current->z[2] +
  278. current->z[3]) / 4;
  279. if (checkedge(mid, current->z[current->edge], level))
  280. current->edge = (current->edge == 0) ? 3 : current->edge - 1;
  281. else
  282. current->edge = (current->edge == 3) ? 0 : current->edge + 1;
  283. getpoint(current, level, Cell, Points);
  284. if (current->edge == 0)
  285. cellhit = 1;
  286. }
  287. else {
  288. if (1 == numcross) {
  289. G_debug(1, "%d crossings in cell %d, %d",
  290. numcross, current->r, current->c);
  291. (*ncrossing)++;
  292. }
  293. cellhit = 1;
  294. }
  295. return cellhit;
  296. }
  297. /************************************************************************
  298. getpoint-- finds crossing point using linear interpolation,
  299. converts from row-column to x-y space, and adds point to current line.
  300. ************************************************************************/
  301. static void getpoint(struct cell *curr, double level,
  302. struct Cell_head Cell, struct line_pnts *Points)
  303. {
  304. double x, y;
  305. double ratio;
  306. int p1, p2;
  307. p1 = curr->edge;
  308. p2 = (curr->edge + 1) % 4;
  309. if (Rast_raster_cmp(&curr->z[p1], &curr->z[p2], DCELL_TYPE) == 0)
  310. ratio = 1;
  311. else if (Rast_is_d_null_value(&curr->z[p1]))
  312. ratio = 0.5;
  313. else if (Rast_is_d_null_value(&curr->z[p2]))
  314. ratio = 0.5;
  315. else
  316. ratio = (level - curr->z[p1]) / (curr->z[p2] - curr->z[p1]);
  317. switch (curr->edge) {
  318. case 0:
  319. y = curr->r;
  320. x = curr->c + ratio;
  321. break;
  322. case 1:
  323. y = curr->r + ratio;
  324. x = curr->c + 1;
  325. break;
  326. case 2:
  327. y = curr->r + 1;
  328. x = curr->c + 1 - ratio;
  329. break;
  330. case 3:
  331. y = curr->r + 1 - ratio;
  332. x = curr->c;
  333. break;
  334. default:
  335. G_fatal_error(_("Edge number out of range"));
  336. }
  337. /* convert r/c values to x/y values */
  338. y = Cell.north - (y + .5) * Cell.ns_res;
  339. x = Cell.west + (x + .5) * Cell.ew_res;
  340. Vect_append_point(Points, x, y, level);
  341. }
  342. /***********************************************************************
  343. checkedge--returns 1 if level is between values d1 & d2;
  344. 0 otherwise.
  345. *********************************************************************/
  346. int checkedge(DCELL d1, DCELL d2, double level)
  347. {
  348. if (((d1 <= level) && (d2 > level)) || ((d1 > level) && (d2 <= level)))
  349. return 1;
  350. return 0;
  351. }
  352. /*********************************************************************/