area.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. /*!
  2. \file lib/vector/Vlib/area.c
  3. \brief Vector library - area-related functions
  4. Higher level functions for reading/writing/manipulating vectors.
  5. (C) 2001-2009, 2011-2013 by the GRASS Development Team
  6. This program is free software under the GNU General Public License
  7. (>=v2). Read the file COPYING that comes with GRASS for details.
  8. \author Original author CERL, probably Dave Gerdes or Mike Higgins.
  9. \author Update to GRASS 5.7 Radim Blazek and David D. Gray.
  10. */
  11. #include <stdlib.h>
  12. #include <grass/vector.h>
  13. #include <grass/glocale.h>
  14. #ifdef HAVE_POSTGRES
  15. #include "pg_local_proto.h"
  16. #endif
  17. #include "local_proto.h"
  18. /*!
  19. \brief Returns polygon array of points (outer ring) of given area
  20. \param Map pointer to Map_info structure
  21. \param area area id
  22. \param[out] BPoints points array
  23. \return number of points
  24. \return -1 on error
  25. */
  26. int Vect_get_area_points(const struct Map_info *Map,
  27. int area, struct line_pnts *BPoints)
  28. {
  29. const struct Plus_head *Plus;
  30. struct P_area *Area;
  31. G_debug(3, "Vect_get_area_points(): area = %d", area);
  32. Vect_reset_line(BPoints);
  33. Plus = &(Map->plus);
  34. Area = Plus->Area[area];
  35. if (Area == NULL) { /* dead area */
  36. G_warning(_("Attempt to read points of nonexistent area"));
  37. return -1; /* error, because we should not read dead areas */
  38. }
  39. G_debug(3, " n_lines = %d", Area->n_lines);
  40. return Vect__get_area_points(Map, Area->lines, Area->n_lines, BPoints);
  41. }
  42. /*!
  43. \brief Returns polygon array of points for given isle
  44. \param Map pointer to Map_info structure
  45. \param isle island id
  46. \param[out] BPoints points array
  47. \return number of points
  48. \return -1 on error
  49. */
  50. int Vect_get_isle_points(const struct Map_info *Map,
  51. int isle, struct line_pnts *BPoints)
  52. {
  53. const struct Plus_head *Plus;
  54. struct P_isle *Isle;
  55. G_debug(3, "Vect_get_isle_points(): isle = %d", isle);
  56. Vect_reset_line(BPoints);
  57. Plus = &(Map->plus);
  58. Isle = Plus->Isle[isle];
  59. if (Isle == NULL) { /* dead isle */
  60. G_warning(_("Attempt to read points of nonexistent isle"));
  61. return -1; /* error, because we should not read dead isles */
  62. }
  63. G_debug(3, " n_lines = %d", Isle->n_lines);
  64. if (Map->format == GV_FORMAT_POSTGIS &&
  65. Map->fInfo.pg.toposchema_name &&
  66. Map->fInfo.pg.cache.ctype != CACHE_MAP) {
  67. #ifdef HAVE_POSTGRES
  68. /* PostGIS Topology */
  69. return Vect__get_area_points_pg(Map, Isle->lines, Isle->n_lines, BPoints);
  70. #else
  71. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  72. #endif
  73. }
  74. /* native format */
  75. return Vect__get_area_points_nat(Map, Isle->lines, Isle->n_lines, BPoints);
  76. }
  77. /*!
  78. \brief Returns centroid id for given area
  79. \param Map pointer to Map_info structure
  80. \param area area id
  81. \return centroid id of area
  82. \return 0 if no centroid found
  83. */
  84. int Vect_get_area_centroid(const struct Map_info *Map, int area)
  85. {
  86. const struct Plus_head *Plus;
  87. struct P_area *Area;
  88. G_debug(3, "Vect_get_area_centroid(): area = %d", area);
  89. Plus = &(Map->plus);
  90. Area = Plus->Area[area];
  91. if (Area == NULL)
  92. G_fatal_error(_("Attempt to read topo for dead area (%d)"), area);
  93. return (Area->centroid);
  94. }
  95. /*!
  96. \brief Creates list of boundaries for given area
  97. Note that ids in <b>List</b> can be negative. The sign indicates in
  98. which direction the boundary should be read (negative for
  99. backward).
  100. \param Map pointer to Map_info structure
  101. \param area area id
  102. \param[out] List pointer to list of boundaries
  103. \return number of boundaries
  104. */
  105. int Vect_get_area_boundaries(const struct Map_info *Map, int area, struct ilist *List)
  106. {
  107. int i, line;
  108. const struct Plus_head *Plus;
  109. struct P_area *Area;
  110. G_debug(3, "Vect_get_area_boundaries(): area = %d", area);
  111. Vect_reset_list(List);
  112. Plus = &(Map->plus);
  113. Area = Plus->Area[area];
  114. if (Area == NULL)
  115. G_fatal_error(_("Attempt to read topo for dead area (%d)"), area);
  116. for (i = 0; i < Area->n_lines; i++) {
  117. line = Area->lines[i];
  118. Vect_list_append(List, line);
  119. }
  120. return (List->n_values);
  121. }
  122. /*!
  123. \brief Creates list of boundaries for given isle
  124. Note that ids in <b>List</b> can be negative. The sign indicates in
  125. which direction the boundary should be read (negative for forward).
  126. \param Map pointer to Map_info structur
  127. \param isle island number
  128. \param[out] List pointer to list where boundaries are stored
  129. \return number of boundaries
  130. */
  131. int Vect_get_isle_boundaries(const struct Map_info *Map, int isle, struct ilist *List)
  132. {
  133. int i, line;
  134. const struct Plus_head *Plus;
  135. struct P_isle *Isle;
  136. G_debug(3, "Vect_get_isle_boundaries(): isle = %d", isle);
  137. Vect_reset_list(List);
  138. Plus = &(Map->plus);
  139. Isle = Plus->Isle[isle];
  140. if (Isle == NULL)
  141. G_fatal_error(_("Attempt to read topo for dead isle (%d)"), isle);
  142. for (i = 0; i < Isle->n_lines; i++) {
  143. line = Isle->lines[i];
  144. Vect_list_append(List, line);
  145. }
  146. return (List->n_values);
  147. }
  148. /*!
  149. \brief Returns number of isles for given area
  150. \param Map pointer to Map_info structure
  151. \param area area id
  152. \return number of isles for area
  153. \return 0 if area not found
  154. */
  155. int Vect_get_area_num_isles(const struct Map_info *Map, int area)
  156. {
  157. const struct Plus_head *Plus;
  158. struct P_area *Area;
  159. G_debug(3, "Vect_get_area_num_isles(): area = %d", area);
  160. Plus = &(Map->plus);
  161. Area = Plus->Area[area];
  162. if (Area == NULL)
  163. G_fatal_error(_("Attempt to read topo for dead area (%d)"), area);
  164. G_debug(3, " n_isles = %d", Area->n_isles);
  165. return (Area->n_isles);
  166. }
  167. /*!
  168. \brief Returns isle id for area
  169. \param Map pointer to Map_info structure
  170. \param area area id
  171. \param isle isle index (0 .. nisles - 1)
  172. \return isle id
  173. \return 0 if no isle found
  174. */
  175. int Vect_get_area_isle(const struct Map_info *Map, int area, int isle)
  176. {
  177. const struct Plus_head *Plus;
  178. struct P_area *Area;
  179. G_debug(3, "Vect_get_area_isle(): area = %d isle = %d", area, isle);
  180. Plus = &(Map->plus);
  181. Area = Plus->Area[area];
  182. if (Area == NULL)
  183. G_fatal_error(_("Attempt to read topo for dead area (%d)"), area);
  184. G_debug(3, " -> isle = %d", Area->isles[isle]);
  185. return (Area->isles[isle]);
  186. }
  187. /*!
  188. \brief Returns area id for isle
  189. \param Map vector
  190. \param isle isle number (0 .. nisles - 1)
  191. \return area id
  192. \return 0 area not found
  193. */
  194. int Vect_get_isle_area(const struct Map_info *Map, int isle)
  195. {
  196. const struct Plus_head *Plus;
  197. struct P_isle *Isle;
  198. G_debug(3, "Vect_get_isle_area(): isle = %d", isle);
  199. Plus = &(Map->plus);
  200. Isle = Plus->Isle[isle];
  201. if (Isle == NULL)
  202. G_fatal_error(_("Attempt to read topo for dead isle (%d)"), isle);
  203. G_debug(3, " -> area = %d", Isle->area);
  204. return (Isle->area);
  205. }
  206. /*!
  207. \brief Returns perimeter of area with perimeter of isles
  208. \param Map pointer to Map_info structure
  209. \param area area id
  210. \return perimeter of area with perimeters of isles in meters
  211. */
  212. double Vect_get_area_perimeter(const struct Map_info *Map, int area)
  213. {
  214. const struct Plus_head *Plus;
  215. struct P_area *Area;
  216. struct line_pnts *Points;
  217. double d;
  218. int i;
  219. G_debug(3, "Vect_get_area_perimeter(): area = %d", area);
  220. Points = Vect_new_line_struct();
  221. Plus = &(Map->plus);
  222. Area = Plus->Area[area];
  223. Vect_get_area_points(Map, area, Points);
  224. Vect_line_prune(Points);
  225. d = Vect_line_geodesic_length(Points);
  226. /* adding island perimeters */
  227. for (i = 0; i < Area->n_isles; i++) {
  228. Vect_get_isle_points(Map, Area->isles[i], Points);
  229. Vect_line_prune(Points);
  230. d += Vect_line_geodesic_length(Points);
  231. }
  232. Vect_destroy_line_struct(Points);
  233. G_debug(3, " perimeter = %f", d);
  234. return (d);
  235. }
  236. /*!
  237. \brief Check if point is in area
  238. \param x,y point coordinates
  239. \param Map pointer to Map_info structure
  240. \param area area id
  241. \param box area bounding box
  242. \return 0 if point is outside area
  243. \return 1 if point is inside area
  244. \return 2 if point is on the area's outer ring
  245. */
  246. int Vect_point_in_area(double x, double y, const struct Map_info *Map,
  247. int area, struct bound_box *box)
  248. {
  249. int i, isle;
  250. const struct Plus_head *Plus;
  251. struct P_area *Area;
  252. struct bound_box ibox;
  253. int poly;
  254. Plus = &(Map->plus);
  255. Area = Plus->Area[area];
  256. if (Area == NULL)
  257. return 0;
  258. poly = Vect_point_in_area_outer_ring(x, y, Map, area, box);
  259. if (poly == 0)
  260. return 0;
  261. if (poly == 2) /* includes area boundary, OK? */
  262. return 2;
  263. /* check if in islands */
  264. for (i = 0; i < Area->n_isles; i++) {
  265. isle = Area->isles[i];
  266. Vect_get_isle_box(Map, isle, &ibox);
  267. poly = Vect_point_in_island(x, y, Map, isle, &ibox);
  268. if (poly >= 1)
  269. return 0; /* excludes island boundary (poly == 2), OK? */
  270. }
  271. return 1;
  272. }
  273. /*!
  274. \brief Returns area of area without areas of isles
  275. \param Map pointer to Map_info structure
  276. \param area area id
  277. \return area of area without areas of isles
  278. */
  279. double Vect_get_area_area(const struct Map_info *Map, int area)
  280. {
  281. const struct Plus_head *Plus;
  282. struct P_area *Area;
  283. struct line_pnts *Points;
  284. double size;
  285. int i;
  286. static int first_time = 1;
  287. G_debug(3, "Vect_get_area_area(): area = %d", area);
  288. if (first_time == 1) {
  289. G_begin_polygon_area_calculations();
  290. first_time = 0;
  291. }
  292. Points = Vect_new_line_struct();
  293. Plus = &(Map->plus);
  294. Area = Plus->Area[area];
  295. Vect_get_area_points(Map, area, Points);
  296. Vect_line_prune(Points);
  297. size = G_area_of_polygon(Points->x, Points->y, Points->n_points);
  298. /* subtracting island areas */
  299. for (i = 0; i < Area->n_isles; i++) {
  300. Vect_get_isle_points(Map, Area->isles[i], Points);
  301. Vect_line_prune(Points);
  302. size -= G_area_of_polygon(Points->x, Points->y, Points->n_points);
  303. }
  304. Vect_destroy_line_struct(Points);
  305. G_debug(3, " area = %f", size);
  306. return (size);
  307. }
  308. /*!
  309. \brief Get area categories
  310. \param Map pointer to Map_info structure
  311. \param area area id
  312. \param[out] Cats list of categories
  313. \return 0 centroid found (but may be without categories)
  314. \return 1 no centroid found
  315. */
  316. int Vect_get_area_cats(const struct Map_info *Map, int area, struct line_cats *Cats)
  317. {
  318. int centroid;
  319. Vect_reset_cats(Cats);
  320. centroid = Vect_get_area_centroid(Map, area);
  321. if (centroid > 0) {
  322. Vect_read_line(Map, NULL, Cats, centroid);
  323. }
  324. else {
  325. return 1; /* no centroid */
  326. }
  327. return 0;
  328. }
  329. /*!
  330. \brief Find FIRST category of given field and area
  331. \param Map pointer to Map_info structure
  332. \param area area id
  333. \param field layer number
  334. \return first found category of given field
  335. \return -1 no centroid or no category found
  336. */
  337. int Vect_get_area_cat(const struct Map_info *Map, int area, int field)
  338. {
  339. int i;
  340. static struct line_cats *Cats = NULL;
  341. if (!Cats)
  342. Cats = Vect_new_cats_struct();
  343. else
  344. Vect_reset_cats(Cats);
  345. if (Vect_get_area_cats(Map, area, Cats) == 1 || Cats->n_cats == 0) {
  346. return -1;
  347. }
  348. for (i = 0; i < Cats->n_cats; i++) {
  349. if (Cats->field[i] == field) {
  350. return Cats->cat[i];
  351. }
  352. }
  353. return -1;
  354. }
  355. /*!
  356. \brief Get area boundary points (internal use only)
  357. For PostGIS Topology calls Vect__get_area_points_pg() otherwise
  358. Vect__get_area_points_nat(),
  359. \param Map pointer to Map_info struct
  360. \param lines array of boundary lines
  361. \param n_lines number of lines in array
  362. \param[out] APoints pointer to output line_pnts struct
  363. \return number of points
  364. \return -1 on error
  365. */
  366. int Vect__get_area_points(const struct Map_info *Map, const plus_t *lines, int n_lines,
  367. struct line_pnts *BPoints)
  368. {
  369. if (Map->format == GV_FORMAT_POSTGIS &&
  370. Map->fInfo.pg.toposchema_name &&
  371. Map->fInfo.pg.cache.ctype != CACHE_MAP) {
  372. #ifdef HAVE_POSTGRES
  373. /* PostGIS Topology */
  374. return Vect__get_area_points_pg(Map, lines, n_lines, BPoints);
  375. #else
  376. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  377. #endif
  378. }
  379. /* native format */
  380. return Vect__get_area_points_nat(Map, lines, n_lines, BPoints);
  381. }
  382. /*!
  383. \brief Get area boundary points (native format)
  384. Used by Vect_build_line_area() and Vect_get_area_points().
  385. \param Map pointer to Map_info struct
  386. \param lines array of boundary lines
  387. \param n_lines number of lines in array
  388. \param[out] APoints pointer to output line_pnts struct
  389. \return number of points
  390. \return -1 on error
  391. */
  392. int Vect__get_area_points_nat(const struct Map_info *Map, const plus_t *lines, int n_lines,
  393. struct line_pnts *BPoints)
  394. {
  395. int i, line, aline, dir;
  396. static struct line_pnts *Points;
  397. if (!Points)
  398. Points = Vect_new_line_struct();
  399. Vect_reset_line(BPoints);
  400. for (i = 0; i < n_lines; i++) {
  401. line = lines[i];
  402. aline = abs(line);
  403. G_debug(5, " append line(%d) = %d", i, line);
  404. if (0 > Vect_read_line(Map, Points, NULL, aline))
  405. return -1;
  406. dir = line > 0 ? GV_FORWARD : GV_BACKWARD;
  407. Vect_append_points(BPoints, Points, dir);
  408. BPoints->n_points--; /* skip last point, avoids duplicates */
  409. }
  410. BPoints->n_points++; /* close polygon */
  411. return BPoints->n_points;
  412. }