merge_lines.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /*!
  2. \file lib/vector/Vlib/merge_lines.c
  3. \brief Vector library - clean geometry (merge lines/boundaries)
  4. Higher level functions for reading/writing/manipulating vectors.
  5. (C) 2001-2009 by the GRASS Development Team
  6. This program is free software under the
  7. GNU General Public License (>=v2).
  8. Read the file COPYING that comes with GRASS
  9. for details.
  10. \author Markus Metz
  11. */
  12. #include <stdlib.h>
  13. #include <math.h>
  14. #include <grass/vector.h>
  15. #include <grass/glocale.h>
  16. /* compare category structures
  17. * return 0 identical
  18. * return 1 not identical
  19. */
  20. static int compare_cats(struct line_cats *ACats, struct line_cats *BCats)
  21. {
  22. int i, j;
  23. if (ACats->n_cats == 0 || BCats->n_cats == 0) {
  24. if (ACats->n_cats == 0 && BCats->n_cats == 0)
  25. return 0;
  26. if (ACats->n_cats == 0 && BCats->n_cats > 0)
  27. return 1;
  28. if (ACats->n_cats > 0 && BCats->n_cats == 0)
  29. return 1;
  30. }
  31. for (i = 0; i < ACats->n_cats; i++) {
  32. int found = 0;
  33. for (j = 0; j < BCats->n_cats; j++) {
  34. if (ACats->cat[i] == BCats->cat[j] &&
  35. ACats->field[i] == BCats->field[j]) {
  36. found = 1;
  37. break;
  38. }
  39. }
  40. if (!found)
  41. return 1;
  42. }
  43. return 0;
  44. }
  45. /*!
  46. \brief Merge lines or boundaries in vector map.
  47. Merges lines specified by type in vector map.
  48. Useful for generalization and smoothing.
  49. Adjacent boundaries are merged as long as topology is maintained.
  50. Adjacent lines are merged as long as there are exactly two different
  51. lines with identical categories connected at a given node.
  52. Zero-length lines need to be removed first.
  53. GV_BUILD_BASE as topo build level is sufficient, areas need not be built.
  54. \param Map input vector map
  55. \param type feature type
  56. \param[out] Err vector map where merged lines/boundaries will be written or NULL
  57. \param new_lines pointer to where number of new lines/boundaries is stored or NULL
  58. \return number of merged lines/boundaries
  59. */
  60. int Vect_merge_lines(struct Map_info *Map, int type, int *new_lines,
  61. struct Map_info *Err)
  62. {
  63. int line, nlines, i, first, last, next_line, curr_line;
  64. int merged = 0, newl = 0;
  65. int next_node, direction, node_n_lines, ltype, lines_type;
  66. struct Plus_head *Plus;
  67. struct ilist *List;
  68. struct line_pnts *MPoints, *Points;
  69. struct line_cats *MCats, *Cats;
  70. struct P_line *Line;
  71. type &= GV_LINES;
  72. if (!(type & GV_LINES)) {
  73. G_warning
  74. ("Merging is done with lines or boundaries only, not with other types");
  75. return 0;
  76. }
  77. Plus = &(Map->plus);
  78. nlines = Vect_get_num_lines(Map);
  79. Points = Vect_new_line_struct();
  80. Cats = Vect_new_cats_struct();
  81. MPoints = Vect_new_line_struct();
  82. MCats = Vect_new_cats_struct();
  83. List = Vect_new_list();
  84. for (line = 1; line <= nlines; line++) {
  85. G_percent(line, nlines, 2);
  86. if (!Vect_line_alive(Map, line))
  87. continue;
  88. Line = Plus->Line[line];
  89. ltype = Line->type;
  90. if (!(ltype & type))
  91. continue;
  92. Vect_read_line(Map, NULL, MCats, line);
  93. /* special cases:
  94. * - loop back to start boundary via several other boundaries
  95. * - one boundary forming closed loop
  96. * - node with 3 entries but only 2 boundaries, one of them connecting twice,
  97. * the other one must then be topologically incorrect (a bridge) in case of boundary */
  98. /* go backward as long as there is only one other line/boundary at the current node */
  99. G_debug(3, "go backward");
  100. Vect_get_line_nodes(Map, line, &next_node, NULL);
  101. first = -line;
  102. while (1) {
  103. node_n_lines = Vect_get_node_n_lines(Map, next_node);
  104. /* count lines/boundaries at this node */
  105. lines_type = 0;
  106. next_line = first;
  107. for (i = 0; i < node_n_lines; i++) {
  108. curr_line = Vect_get_node_line(Map, next_node, i);
  109. if ((Plus->Line[abs(curr_line)]->type & GV_LINES))
  110. lines_type++;
  111. if (Plus->Line[abs(curr_line)]->type == ltype) {
  112. if (abs(curr_line) != abs(first)) {
  113. Vect_read_line(Map, NULL, Cats, abs(curr_line));
  114. /* catgories must be identical */
  115. if (compare_cats(MCats, Cats) == 0)
  116. next_line = curr_line;
  117. }
  118. }
  119. }
  120. if (lines_type == 2 && abs(next_line) != abs(first) &&
  121. abs(next_line) != line) {
  122. first = next_line;
  123. if (first < 0) {
  124. Vect_get_line_nodes(Map, -first, &next_node, NULL);
  125. }
  126. else {
  127. Vect_get_line_nodes(Map, first, NULL, &next_node);
  128. }
  129. }
  130. else
  131. break;
  132. }
  133. /* go forward as long as there is only one other line/boundary at the current node */
  134. G_debug(3, "go forward");
  135. /* reverse direction */
  136. last = -first;
  137. if (last < 0) {
  138. Vect_get_line_nodes(Map, -last, &next_node, NULL);
  139. }
  140. else {
  141. Vect_get_line_nodes(Map, last, NULL, &next_node);
  142. }
  143. Vect_reset_list(List);
  144. while (1) {
  145. G_ilist_add(List, last);
  146. node_n_lines = Vect_get_node_n_lines(Map, next_node);
  147. lines_type = 0;
  148. next_line = last;
  149. for (i = 0; i < node_n_lines; i++) {
  150. curr_line = Vect_get_node_line(Map, next_node, i);
  151. if ((Plus->Line[abs(curr_line)]->type & GV_LINES))
  152. lines_type++;
  153. if (Plus->Line[abs(curr_line)]->type == ltype) {
  154. if (abs(curr_line) != abs(last)) {
  155. Vect_read_line(Map, NULL, Cats, abs(curr_line));
  156. if (compare_cats(MCats, Cats) == 0)
  157. next_line = curr_line;
  158. }
  159. }
  160. }
  161. if (lines_type == 2 && abs(next_line) != abs(last) &&
  162. abs(next_line) != abs(first)) {
  163. last = next_line;
  164. if (last < 0) {
  165. Vect_get_line_nodes(Map, -last, &next_node, NULL);
  166. }
  167. else {
  168. Vect_get_line_nodes(Map, last, NULL, &next_node);
  169. }
  170. }
  171. else
  172. break;
  173. }
  174. /* merge lines */
  175. if (List->n_values > 1) {
  176. G_debug(3, "merge %d lines", List->n_values);
  177. Vect_reset_line(MPoints);
  178. for (i = 0; i < List->n_values; i++) {
  179. Vect_reset_line(Points);
  180. Vect_read_line(Map, Points, Cats, abs(List->value[i]));
  181. direction = (List->value[i] < 0 ? GV_BACKWARD : GV_FORWARD);
  182. Vect_append_points(MPoints, Points, direction);
  183. MPoints->n_points--;
  184. if (Err) {
  185. /* write out lines/boundaries to be merged */
  186. Vect_write_line(Err, ltype, Points, Cats);
  187. }
  188. Vect_delete_line(Map, abs(List->value[i]));
  189. }
  190. MPoints->n_points++;
  191. Vect_write_line(Map, ltype, MPoints, MCats);
  192. merged += List->n_values;
  193. newl++;
  194. }
  195. /* nlines = Vect_get_num_lines(Map); */
  196. }
  197. if (type == GV_LINE) {
  198. G_verbose_message(_("%d lines merged"), merged);
  199. G_verbose_message(_("%d new lines"), newl);
  200. }
  201. else if (type == GV_BOUNDARY) {
  202. G_verbose_message(_("%d boundaries merged"), merged);
  203. G_verbose_message(_("%d new boundaries"), newl);
  204. }
  205. else {
  206. G_verbose_message(_("%d lines and boundaries merged"), merged);
  207. G_verbose_message(_("%d new lines and boundaries"), newl);
  208. }
  209. if (new_lines)
  210. *new_lines = newl;
  211. Vect_destroy_line_struct(Points);
  212. Vect_destroy_cats_struct(Cats);
  213. Vect_destroy_line_struct(MPoints);
  214. Vect_destroy_cats_struct(MCats);
  215. Vect_destroy_list(List);
  216. return merged;
  217. }