GRASS 8 Programmer's Manual 8.6.0dev(2026)-55de52a352
Loading...
Searching...
No Matches
merge_lines.c
Go to the documentation of this file.
1/*!
2 \file lib/vector/Vlib/merge_lines.c
3
4 \brief Vector library - clean geometry (merge lines/boundaries)
5
6 Higher level functions for reading/writing/manipulating vectors.
7
8 SPDX-FileCopyrightText: 2001-2009 GRASS Development Team
9 SPDX-License-Identifier: GPL-2.0-or-later
10
11 \author Markus Metz
12 */
13
14#include <stdlib.h>
15#include <math.h>
16#include <grass/vector.h>
17#include <grass/glocale.h>
18
19/* compare category structures
20 * return 0 identical
21 * return 1 not identical
22 */
23static int compare_cats(struct line_cats *ACats, struct line_cats *BCats)
24{
25 int i, j;
26
27 if (ACats->n_cats == 0 || BCats->n_cats == 0) {
28 if (ACats->n_cats == 0 && BCats->n_cats == 0)
29 return 0;
30
31 if (ACats->n_cats == 0 && BCats->n_cats > 0)
32 return 1;
33
34 if (ACats->n_cats > 0 && BCats->n_cats == 0)
35 return 1;
36 }
37
38 for (i = 0; i < ACats->n_cats; i++) {
39 int found = 0;
40
41 for (j = 0; j < BCats->n_cats; j++) {
42 if (ACats->cat[i] == BCats->cat[j] &&
43 ACats->field[i] == BCats->field[j]) {
44 found = 1;
45 break;
46 }
47 }
48 if (!found)
49 return 1;
50 }
51
52 return 0;
53}
54
55/*!
56 \brief Merge lines or boundaries in vector map.
57
58 Merges lines specified by type in vector map.
59 Useful for generalization and smoothing.
60 Adjacent boundaries are merged as long as topology is maintained.
61 Adjacent lines are merged as long as there are exactly two different
62 lines with identical categories connected at a given node.
63 Zero-length lines need to be removed first.
64 GV_BUILD_BASE as topo build level is sufficient, areas need not be built.
65
66 \param Map input vector map
67 \param type feature type
68 \param[out] Err vector map where merged lines/boundaries will be written or
69 NULL \param new_lines pointer to where number of new lines/boundaries is
70 stored or NULL
71
72 \return number of merged lines/boundaries
73 */
74int Vect_merge_lines(struct Map_info *Map, int type, int *new_lines,
75 struct Map_info *Err)
76{
77 int line, nlines, i, first, last, next_line, curr_line;
78 int merged = 0, newl = 0;
79 int next_node, direction, node_n_lines, ltype, lines_type;
80 struct Plus_head *Plus;
81 struct ilist *List;
82 struct line_pnts *MPoints, *Points;
83 struct line_cats *MCats, *Cats;
84 struct P_line *Line;
85
86 type &= GV_LINES;
87
88 if (!(type & GV_LINES)) {
89 G_warning("Merging is done with lines or boundaries only, not with "
90 "other types");
91 return 0;
92 }
93
94 Plus = &(Map->plus);
95 nlines = Vect_get_num_lines(Map);
96
97 Points = Vect_new_line_struct();
102
103 for (line = 1; line <= nlines; line++) {
104 G_percent(line, nlines, 2);
105
106 if (!Vect_line_alive(Map, line))
107 continue;
108
109 Line = Plus->Line[line];
110 ltype = Line->type;
111
112 if (!(ltype & type))
113 continue;
114
115 Vect_read_line(Map, NULL, MCats, line);
116
117 /* special cases:
118 * - loop back to start boundary via several other boundaries
119 * - one boundary forming closed loop
120 * - node with 3 entries but only 2 boundaries, one of them connecting
121 * twice, the other one must then be topologically incorrect (a bridge)
122 * in case of boundary */
123
124 /* go backward as long as there is only one other line/boundary at the
125 * current node */
126 G_debug(3, "go backward");
127 Vect_get_line_nodes(Map, line, &next_node, NULL);
128
129 first = -line;
130 while (1) {
132 /* count lines/boundaries at this node */
133 lines_type = 0;
134 next_line = first;
135 for (i = 0; i < node_n_lines; i++) {
136 curr_line = Vect_get_node_line(Map, next_node, i);
137 if ((Plus->Line[abs(curr_line)]->type & GV_LINES))
138 lines_type++;
139 if (Plus->Line[abs(curr_line)]->type == ltype) {
140 if (abs(curr_line) != abs(first)) {
142
143 /* categories must be identical */
144 if (compare_cats(MCats, Cats) == 0)
145 next_line = curr_line;
146 }
147 }
148 }
149 if (lines_type == 2 && abs(next_line) != abs(first) &&
150 abs(next_line) != line) {
151 first = next_line;
152
153 if (first < 0) {
154 Vect_get_line_nodes(Map, -first, &next_node, NULL);
155 }
156 else {
157 Vect_get_line_nodes(Map, first, NULL, &next_node);
158 }
159 }
160 else
161 break;
162 }
163
164 /* go forward as long as there is only one other line/boundary at the
165 * current node */
166 G_debug(3, "go forward");
167
168 /* reverse direction */
169 last = -first;
170
171 if (last < 0) {
172 Vect_get_line_nodes(Map, -last, &next_node, NULL);
173 }
174 else {
175 Vect_get_line_nodes(Map, last, NULL, &next_node);
176 }
177
179 while (1) {
180 G_ilist_add(List, last);
182 lines_type = 0;
183 next_line = last;
184 for (i = 0; i < node_n_lines; i++) {
185 curr_line = Vect_get_node_line(Map, next_node, i);
186 if ((Plus->Line[abs(curr_line)]->type & GV_LINES))
187 lines_type++;
188 if (Plus->Line[abs(curr_line)]->type == ltype) {
189 if (abs(curr_line) != abs(last)) {
191
192 if (compare_cats(MCats, Cats) == 0)
193 next_line = curr_line;
194 }
195 }
196 }
197
198 if (lines_type == 2 && abs(next_line) != abs(last) &&
199 abs(next_line) != abs(first)) {
200 last = next_line;
201
202 if (last < 0) {
203 Vect_get_line_nodes(Map, -last, &next_node, NULL);
204 }
205 else {
206 Vect_get_line_nodes(Map, last, NULL, &next_node);
207 }
208 }
209 else
210 break;
211 }
212
213 /* merge lines */
214 if (List->n_values > 1) {
215 G_debug(3, "merge %d lines", List->n_values);
217
218 for (i = 0; i < List->n_values; i++) {
219 Vect_reset_line(Points);
220 Vect_read_line(Map, Points, Cats, abs(List->value[i]));
221 direction = (List->value[i] < 0 ? GV_BACKWARD : GV_FORWARD);
223 MPoints->n_points--;
224 if (Err) {
225 /* write out lines/boundaries to be merged */
226 Vect_write_line(Err, ltype, Points, Cats);
227 }
228 Vect_delete_line(Map, abs(List->value[i]));
229 }
230 MPoints->n_points++;
232 merged += List->n_values;
233 newl++;
234 }
235
236 /* nlines = Vect_get_num_lines(Map); */
237 }
238
239 if (type == GV_LINE) {
240 G_verbose_message(_("%d lines merged"), merged);
241 G_verbose_message(_("%d new lines"), newl);
242 }
243 else if (type == GV_BOUNDARY) {
244 G_verbose_message(_("%d boundaries merged"), merged);
245 G_verbose_message(_("%d new boundaries"), newl);
246 }
247 else {
248 G_verbose_message(_("%d lines and boundaries merged"), merged);
249 G_verbose_message(_("%d new lines and boundaries"), newl);
250 }
251
252 if (new_lines)
253 *new_lines = newl;
254
260
261 return merged;
262}
#define NULL
Definition ccmath.h:32
void G_percent(long, long, int)
Print percent complete messages.
Definition percent.c:59
void G_warning(const char *,...) __attribute__((format(printf
void void G_verbose_message(const char *,...) __attribute__((format(printf
void G_ilist_add(struct ilist *, int)
Add item to ilist.
Definition ilist.c:75
int G_debug(int, const char *,...) __attribute__((format(printf
void Vect_destroy_line_struct(struct line_pnts *)
Frees all memory associated with a line_pnts structure, including the structure itself.
Definition line.c:75
int Vect_get_line_nodes(struct Map_info *, int, int *, int *)
Get line nodes.
Definition level_two.c:302
plus_t Vect_get_num_lines(struct Map_info *)
Fetch number of features (points, lines, boundaries, centroids) in vector map.
Definition level_two.c:73
void Vect_destroy_list(struct ilist *)
Frees all memory associated with a struct ilist, including the struct itself.
void Vect_destroy_cats_struct(struct line_cats *)
Frees all memory associated with line_cats structure, including the struct itself.
int Vect_read_line(struct Map_info *, struct line_pnts *, struct line_cats *, int)
Read vector feature (topological level required)
int Vect_line_alive(struct Map_info *, int)
Check if feature is alive or dead (topological level required)
int Vect_delete_line(struct Map_info *, off_t)
Delete existing feature (topological level required)
struct line_cats * Vect_new_cats_struct(void)
Creates and initializes line_cats structure.
struct ilist * Vect_new_list(void)
Creates and initializes a struct ilist.
off_t Vect_write_line(struct Map_info *, int, const struct line_pnts *, const struct line_cats *)
Writes a new feature.
int Vect_get_node_n_lines(struct Map_info *, int)
Get number of lines for node.
Definition level_two.c:379
void Vect_reset_line(struct line_pnts *)
Reset line.
Definition line.c:127
int Vect_get_node_line(struct Map_info *, int, int)
Get line id for node line index.
Definition level_two.c:395
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition line.c:43
int Vect_reset_list(struct ilist *)
Reset ilist structure.
int Vect_append_points(struct line_pnts *, const struct line_pnts *, int)
Appends points to the end of a line.
Definition line.c:333
#define GV_LINE
#define GV_LINES
#define GV_BOUNDARY
#define GV_FORWARD
Line direction indicator forward/backward.
#define GV_BACKWARD
#define _(str)
Definition glocale.h:10
int Vect_merge_lines(struct Map_info *Map, int type, int *new_lines, struct Map_info *Err)
Merge lines or boundaries in vector map.
Definition merge_lines.c:74
Vector map info.
Vector geometry.
char type
Line type.
Basic topology-related info.
List of integers.
Definition gis.h:712
Feature category info.
Feature geometry info - coordinates.