find.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /*!
  2. * \file lib/vector/Vlib/find.c
  3. *
  4. * \brief Vector library - Find nearest vector feature.
  5. *
  6. * Higher level functions for reading/writing/manipulating vectors.
  7. *
  8. * (C) 2001-2009 by the GRASS Development Team
  9. *
  10. * This program is free software under the GNU General Public License
  11. * (>=v2). Read the file COPYING that comes with GRASS for details.
  12. * \author Original author CERL, probably Dave Gerdes or Mike
  13. * Higgins.
  14. * \author Update to GRASS 5.7 Radim Blazek and David D. Gray.
  15. */
  16. #include <stdlib.h>
  17. #include <math.h>
  18. #include <grass/vector.h>
  19. #ifndef HUGE_VAL
  20. #define HUGE_VAL 9999999999999.0
  21. #endif
  22. /* for qsort */
  23. typedef struct {
  24. int i;
  25. double size;
  26. struct bound_box box;
  27. } BOX_SIZE;
  28. static int sort_by_size(const void *a, const void *b)
  29. {
  30. BOX_SIZE *as = (BOX_SIZE *)a;
  31. BOX_SIZE *bs = (BOX_SIZE *)b;
  32. if (as->size < bs->size)
  33. return -1;
  34. return (as->size > bs->size);
  35. }
  36. /*!
  37. * \brief Find the nearest node.
  38. *
  39. * \param Map vector map
  40. * \param ux,uy,uz point coordinates
  41. * \param maxdist max distance from the line
  42. * \param with_z 3D (WITH_Z, WITHOUT_Z)
  43. *
  44. * \return number of nearest node
  45. * \return 0 if not found
  46. */
  47. int
  48. Vect_find_node(struct Map_info *Map,
  49. double ux, double uy, double uz, double maxdist, int with_z)
  50. {
  51. int i, nnodes, node;
  52. struct bound_box box;
  53. struct ilist *NList;
  54. double x, y, z;
  55. double cur_dist, dist;
  56. G_debug(3, "Vect_find_node() for %f %f %f maxdist = %f", ux, uy, uz,
  57. maxdist);
  58. NList = Vect_new_list();
  59. /* Select all nodes in box */
  60. box.N = uy + maxdist;
  61. box.S = uy - maxdist;
  62. box.E = ux + maxdist;
  63. box.W = ux - maxdist;
  64. if (with_z) {
  65. box.T = uz + maxdist;
  66. box.B = uz - maxdist;
  67. }
  68. else {
  69. box.T = HUGE_VAL;
  70. box.B = -HUGE_VAL;
  71. }
  72. nnodes = Vect_select_nodes_by_box(Map, &box, NList);
  73. G_debug(3, " %d nodes in box", nnodes);
  74. if (nnodes == 0)
  75. return 0;
  76. /* find nearest */
  77. cur_dist = PORT_DOUBLE_MAX;
  78. node = 0;
  79. for (i = 0; i < nnodes; i++) {
  80. Vect_get_node_coor(Map, NList->value[i], &x, &y, &z);
  81. dist = Vect_points_distance(ux, uy, uz, x, y, z, with_z);
  82. if (dist < cur_dist) {
  83. cur_dist = dist;
  84. node = i;
  85. }
  86. }
  87. G_debug(3, " nearest node %d in distance %f", NList->value[node],
  88. cur_dist);
  89. /* Check if in max distance */
  90. if (cur_dist <= maxdist)
  91. return (NList->value[node]);
  92. else
  93. return 0;
  94. }
  95. /*!
  96. * \brief Find the nearest line.
  97. *
  98. * \param map vector map
  99. * \param ux,uy,uz points coordinates
  100. * \param type feature type (GV_LINE, GV_POINT, GV_BOUNDARY or GV_CENTROID)
  101. * if only want to search certain types of lines or -1 if search all lines
  102. * \param maxdist max distance from the line
  103. * \param with_z 3D (WITH_Z, WITHOUT_Z)
  104. * \param exclude if > 0 number of line which should be excluded from selection.
  105. * May be useful if we need line nearest to other one.
  106. *
  107. * \return number of nearest line
  108. * \return 0 if not found
  109. *
  110. */
  111. int
  112. Vect_find_line(struct Map_info *map,
  113. double ux, double uy, double uz,
  114. int type, double maxdist, int with_z, int exclude)
  115. {
  116. int line;
  117. struct ilist *exclude_list;
  118. exclude_list = Vect_new_list();
  119. Vect_list_append(exclude_list, exclude);
  120. line = Vect_find_line_list(map, ux, uy, uz,
  121. type, maxdist, with_z, exclude_list, NULL);
  122. Vect_destroy_list(exclude_list);
  123. return line;
  124. }
  125. /*!
  126. * \brief Find the nearest line(s).
  127. *
  128. * \param map vector map
  129. * \param ux,uy,uz points coordinates
  130. * \param type feature type (GV_LINE, GV_POINT, GV_BOUNDARY or GV_CENTROID)
  131. * if only want to search certain types of lines or -1 if search all lines
  132. * \param maxdist max distance from the line
  133. * \param with_z 3D (WITH_Z, WITHOUT_Z)
  134. * \param exclude list of lines which should be excluded from selection
  135. * \param found list of found lines (or NULL)
  136. *
  137. * \return number of nearest line
  138. * \return 0 if not found
  139. */
  140. int
  141. Vect_find_line_list(struct Map_info *map,
  142. double ux, double uy, double uz,
  143. int type, double maxdist, int with_z,
  144. const struct ilist *exclude, struct ilist *found)
  145. {
  146. int choice;
  147. double new_dist;
  148. double cur_dist;
  149. int gotone;
  150. int i, line;
  151. static struct line_pnts *Points;
  152. static int first_time = 1;
  153. struct bound_box box;
  154. struct boxlist *List;
  155. G_debug(3, "Vect_find_line_list() for %f %f %f type = %d maxdist = %f",
  156. ux, uy, uz, type, maxdist);
  157. if (first_time) {
  158. Points = Vect_new_line_struct();
  159. first_time = 0;
  160. }
  161. gotone = 0;
  162. choice = 0;
  163. cur_dist = HUGE_VAL;
  164. box.N = uy + maxdist;
  165. box.S = uy - maxdist;
  166. box.E = ux + maxdist;
  167. box.W = ux - maxdist;
  168. if (with_z) {
  169. box.T = uz + maxdist;
  170. box.B = uz - maxdist;
  171. }
  172. else {
  173. box.T = PORT_DOUBLE_MAX;
  174. box.B = -PORT_DOUBLE_MAX;
  175. }
  176. List = Vect_new_boxlist(0);
  177. if (found)
  178. Vect_reset_list(found);
  179. Vect_select_lines_by_box(map, &box, type, List);
  180. for (i = 0; i < List->n_values; i++) {
  181. line = List->id[i];
  182. if (Vect_val_in_list(exclude, line)) {
  183. G_debug(3, " line = %d exclude", line);
  184. continue;
  185. }
  186. /* No more needed */
  187. /*
  188. Line = Plus->Line[line];
  189. if ( Line == NULL ) continue;
  190. if ( !(type & Line->type) ) continue;
  191. */
  192. Vect_read_line(map, Points, NULL, line);
  193. Vect_line_distance(Points, ux, uy, uz, with_z, NULL, NULL, NULL,
  194. &new_dist, NULL, NULL);
  195. G_debug(3, " line = %d distance = %f", line, new_dist);
  196. if (found && new_dist <= maxdist) {
  197. Vect_list_append(found, line);
  198. }
  199. if ((++gotone == 1) || (new_dist <= cur_dist)) {
  200. if (new_dist == cur_dist) {
  201. /* TODO */
  202. /* choice = dig_center_check (map->Line, choice, a, ux, uy); */
  203. continue;
  204. }
  205. choice = line;
  206. cur_dist = new_dist;
  207. }
  208. }
  209. G_debug(3, "min distance found = %f", cur_dist);
  210. if (cur_dist > maxdist)
  211. choice = 0;
  212. Vect_destroy_boxlist(List);
  213. return (choice);
  214. }
  215. /*!
  216. * \brief Find the nearest area
  217. *
  218. * \param Map vector map
  219. * \param x,y point coordinates
  220. *
  221. * \return area number
  222. * \return 0 if not found
  223. */
  224. int Vect_find_area(struct Map_info *Map, double x, double y)
  225. {
  226. int i, j, ret, area, isle;
  227. struct bound_box box;
  228. static struct boxlist *List = NULL;
  229. static BOX_SIZE *size_list;
  230. static int alloc_size_list = 0;
  231. const struct Plus_head *Plus;
  232. struct P_area *Area;
  233. G_debug(3, "Vect_find_area() x = %f y = %f", x, y);
  234. if (!List) {
  235. List = Vect_new_boxlist(1);
  236. alloc_size_list = 10;
  237. size_list = G_malloc(alloc_size_list * sizeof(BOX_SIZE));
  238. }
  239. Plus = &(Map->plus);
  240. /* select areas by box */
  241. box.E = x;
  242. box.W = x;
  243. box.N = y;
  244. box.S = y;
  245. box.T = PORT_DOUBLE_MAX;
  246. box.B = -PORT_DOUBLE_MAX;
  247. Vect_select_areas_by_box(Map, &box, List);
  248. G_debug(3, " %d areas selected by box", List->n_values);
  249. /* sort areas by bbox size
  250. * get the smallest area that contains the point
  251. * using the bbox size is working because if 2 areas both contain
  252. * the point, one of these areas must be inside the other area
  253. * which means that the bbox of the outer area must be larger than
  254. * the bbox of the inner area, and equal bbox sizes are not possible */
  255. if (alloc_size_list < List->n_values) {
  256. alloc_size_list = List->n_values;
  257. size_list = G_realloc(size_list, alloc_size_list * sizeof(BOX_SIZE));
  258. }
  259. for (i = 0; i < List->n_values; i++) {
  260. size_list[i].i = List->id[i];
  261. box = List->box[i];
  262. size_list[i].box = List->box[i];
  263. size_list[i].size = (box.N - box.S) * (box.E - box.W);
  264. }
  265. if (List->n_values == 2) {
  266. /* simple swap */
  267. if (size_list[1].size < size_list[0].size) {
  268. size_list[0].i = List->id[1];
  269. size_list[1].i = List->id[0];
  270. size_list[0].box = List->box[1];
  271. size_list[1].box = List->box[0];
  272. }
  273. }
  274. else if (List->n_values > 2)
  275. qsort(size_list, List->n_values, sizeof(BOX_SIZE), sort_by_size);
  276. for (i = 0; i < List->n_values; i++) {
  277. area = size_list[i].i;
  278. /* outer ring */
  279. ret = Vect_point_in_area_outer_ring(x, y, Map, area, &size_list[i].box);
  280. G_debug(3, " area = %d Vect_point_in_area_outer_ring() = %d", area, ret);
  281. if (ret >= 1) {
  282. /* check if in islands */
  283. Area = Plus->Area[area];
  284. for (j = 0; j < Area->n_isles; j++) {
  285. isle = Area->isles[j];
  286. Vect_get_isle_box(Map, isle, &box);
  287. ret = Vect_point_in_island(x, y, Map, isle, &box);
  288. G_debug(3, " area = %d Vect_point_in_island() = %d", area, ret);
  289. if (ret >= 1) {
  290. /* point is not in area
  291. * point is also not in any inner area, those have
  292. * been tested before (sorted list)
  293. * -> area inside island could not be built */
  294. return 0;
  295. }
  296. }
  297. return (area);
  298. }
  299. }
  300. return 0;
  301. }
  302. /*!
  303. * \brief Find the nearest island
  304. *
  305. * \param Map vector map
  306. * \param x,y points coordinates
  307. *
  308. * \return island number,
  309. * \return 0 if not found
  310. */
  311. int Vect_find_island(struct Map_info *Map, double x, double y)
  312. {
  313. int i, ret, island, current, current_size, size;
  314. static int first = 1;
  315. struct bound_box box;
  316. static struct boxlist *List;
  317. static struct line_pnts *Points;
  318. /* TODO: sync to Vect_find_area() */
  319. G_debug(3, "Vect_find_island() x = %f y = %f", x, y);
  320. if (first) {
  321. List = Vect_new_boxlist(1);
  322. Points = Vect_new_line_struct();
  323. first = 0;
  324. }
  325. /* select islands by box */
  326. box.E = x;
  327. box.W = x;
  328. box.N = y;
  329. box.S = y;
  330. box.T = PORT_DOUBLE_MAX;
  331. box.B = -PORT_DOUBLE_MAX;
  332. Vect_select_isles_by_box(Map, &box, List);
  333. G_debug(3, " %d islands selected by box", List->n_values);
  334. current_size = -1;
  335. current = 0;
  336. for (i = 0; i < List->n_values; i++) {
  337. island = List->id[i];
  338. ret = Vect_point_in_island(x, y, Map, island, &List->box[i]);
  339. if (ret >= 1) { /* inside */
  340. if (current > 0) { /* not first */
  341. if (current_size == -1) { /* second */
  342. G_begin_polygon_area_calculations();
  343. Vect_get_isle_points(Map, current, Points);
  344. current_size =
  345. G_area_of_polygon(Points->x, Points->y,
  346. Points->n_points);
  347. }
  348. Vect_get_isle_points(Map, island, Points);
  349. size =
  350. G_area_of_polygon(Points->x, Points->y, Points->n_points);
  351. if (size < current_size) {
  352. current = island;
  353. current_size = size;
  354. }
  355. }
  356. else { /* first */
  357. current = island;
  358. }
  359. }
  360. }
  361. return current;
  362. }