GRASS 8 Programmer's Manual 8.6.0dev(2026)-1878fdfec5
Loading...
Searching...
No Matches
diglib/poly.c
Go to the documentation of this file.
1/*****************************************************************************
2 *
3 * MODULE: Vector library
4 *
5 * AUTHOR(S): Original author CERL, probably Dave Gerdes.
6 * Update to GRASS 5.7 Radim Blazek.
7 * Update to GRASS 7.0 Markus Metz
8 *
9 * PURPOSE: Lower level functions for reading/writing/manipulating vectors.
10 *
11 * SPDX-FileCopyrightText: 2009 GRASS Development Team
12 * SPDX-License-Identifier: GPL-2.0-or-later
13 *
14 *****************************************************************************/
15
16#include <math.h>
17#include <grass/vector.h>
18
19#ifndef HUGE_VAL
20#define HUGE_VAL 9999999999999.0
21#endif
22
23/*
24 * fills BPoints (must be inited previously) by points from input
25 * array LPoints
26 *
27 * each input LPoints[i] must have at least 2 points
28 *
29 * returns number of points or -1 on error
30 */
31int dig_get_poly_points(int n_lines, struct line_pnts **LPoints,
32 int *direction, /* line direction: > 0 or < 0 */
33 struct line_pnts *BPoints)
34{
35 register int i, j, point, start, end, inc;
36 struct line_pnts *Points;
37 int n_points;
38
39 BPoints->n_points = 0;
40
41 if (n_lines < 1) {
42 return 0;
43 }
44
45 /* Calc required space */
46 n_points = 0;
47 for (i = 0; i < n_lines; i++) {
48 Points = LPoints[i];
49 n_points += Points->n_points - 1; /* each line from first to last - 1 */
50 }
51 n_points++; /* last point */
52
54 return (-1);
55
56 point = 0;
57 j = 0;
58 for (i = 0; i < n_lines; i++) {
59 Points = LPoints[i];
60 if (direction[i] > 0) {
61 start = 0;
62 end = Points->n_points - 1;
63 inc = 1;
64 }
65 else {
66 start = Points->n_points - 1;
67 end = 0;
68 inc = -1;
69 }
70
71 for (j = start; j != end; j += inc) {
72 BPoints->x[point] = Points->x[j];
73 BPoints->y[point] = Points->y[j];
74 point++;
75 }
76 }
77 /* last point */
78 BPoints->x[point] = Points->x[j];
79 BPoints->y[point] = Points->y[j];
80
81 BPoints->n_points = n_points;
82
83 return (BPoints->n_points);
84}
85
86/*
87 * calculate signed area size for polygon
88 *
89 * points must be closed polygon with first point = last point
90 *
91 * returns signed area, positive for clockwise, negative for
92 * counterclockwise, 0 for degenerate
93 */
94int dig_find_area_poly(struct line_pnts *Points, double *totalarea)
95{
96 int i, mid;
97 double *x, *y, xshift, yshift;
98 double tot_area;
99
100 x = Points->x;
101 y = Points->y;
102
103 /* Get a reference point close to the polygon as new origin.
104 * The first point would be good enough, particularly for small
105 * polygons. The average of the first and the mid-point is a fast
106 * approximation of a reference point with reduced distance to the
107 * ring's vertices, also for larger rings.
108 *
109 * Shift coordinates towards this reference point to make
110 * calculation of the signed area more robust by increasing the
111 * accuracy for given fp precision limits.
112 *
113 * Considering the basic formula
114 * area += (x2 - x1) * (y2 + y1)
115 * the shift is in theory only needed for addition, not subtraction,
116 * but does no harm and is kept for symmetry treating x and y coords.
117 *
118 * Keep in sync with G_planimetric_polygon_area() in lib/gis/area_poly2.c */
119
120 mid = Points->n_points / 2;
121 xshift = (x[0] + x[mid]) / 2.;
122 yshift = (y[0] + y[mid]) / 2.;
123
124 /* line integral: *Points do not need to be pruned */
125 /* surveyor's formula is more common, but more prone to
126 * fp precision limit errors, and *Points would need to be pruned */
127 tot_area = 0.0;
128 for (i = 1; i < Points->n_points; i++) {
129 tot_area += ((x[i] - xshift) - (x[i - 1] - xshift)) *
130 ((y[i] - yshift) + (y[i - 1] - yshift));
131 }
132 *totalarea = 0.5 * tot_area;
133
134 return (0);
135}
136
137/*
138 * find orientation of polygon (clockwise or counterclockwise)
139 * in theory faster than signed area for > 4 vertices, but is not robust
140 * against special cases
141 * use dig_find_area_poly instead
142 *
143 * points must be closed polygon with first point = last point
144 *
145 * this code uses bits and pieces from softSurfer and GEOS
146 * (C) 2000 softSurfer (www.softsurfer.com)
147 * (C) 2006 Refractions Research Inc.
148 *
149 * copes with partially collapsed boundaries and 8-shaped isles
150 * the code is long and not much faster than dig_find_area_poly
151 * it can be written much shorter, but that comes with speed penalty
152 *
153 * returns orientation, positive for CW, negative for CCW, 0 for degenerate
154 */
156{
157 unsigned int pnext, pprev, pcur = 0;
158 unsigned int lastpoint = Points->n_points - 1;
159 double *x, *y, orientation;
160
161 x = Points->x;
162 y = Points->y;
163
164 /* first find leftmost highest vertex of the polygon */
165 for (pnext = 1; pnext < lastpoint; pnext++) {
166 if (y[pnext] < y[pcur])
167 continue;
168 else if (y[pnext] == y[pcur]) { /* just as high */
169 if (x[pnext] > x[pcur]) /* but to the right */
170 continue;
171 if (x[pnext] ==
172 x[pcur]) { /* duplicate point, self-intersecting polygon ? */
173 pprev = (pcur == 0 ? lastpoint - 1 : pcur - 1);
174 if (y[pnext - 1] < y[pprev])
175 continue;
176 }
177 }
178 pcur = pnext; /* a new leftmost highest vertex */
179 }
180
181 /* Points are not pruned, so ... */
182 pnext = pcur;
183 pprev = pcur;
184
185 /* find next distinct point */
186 do {
187 if (pnext < lastpoint - 1)
188 pnext++;
189 else
190 pnext = 0;
191 } while (pnext != pcur && x[pcur] == x[pnext] && y[pcur] == y[pnext]);
192
193 /* find previous distinct point */
194 do {
195 if (pprev > 0)
196 pprev--;
197 else
198 pprev = lastpoint - 1;
199 } while (pprev != pcur && x[pcur] == x[pprev] && y[pcur] == y[pprev]);
200
201 /* orientation at vertex pcur == signed area for triangle pprev, pcur, pnext
202 * rather use robust determinant of Olivier Devillers? */
203 orientation = (x[pnext] - x[pprev]) * (y[pcur] - y[pprev]) -
204 (x[pcur] - x[pprev]) * (y[pnext] - y[pprev]);
205
206 if (orientation)
207 return orientation;
208
209 /* orientation is 0, can happen with dirty boundaries, next check */
210 /* find rightmost highest vertex of the polygon */
211 pcur = 0;
212 for (pnext = 1; pnext < lastpoint; pnext++) {
213 if (y[pnext] < y[pcur])
214 continue;
215 else if (y[pnext] == y[pcur]) { /* just as high */
216 if (x[pnext] < x[pcur]) /* but to the left */
217 continue;
218 if (x[pnext] ==
219 x[pcur]) { /* duplicate point, self-intersecting polygon ? */
220 pprev = (pcur == 0 ? lastpoint - 1 : pcur - 1);
221 if (y[pnext - 1] < y[pprev])
222 continue;
223 }
224 }
225 pcur = pnext; /* a new rightmost highest vertex */
226 }
227
228 /* Points are not pruned, so ... */
229 pnext = pcur;
230 pprev = pcur;
231
232 /* find next distinct point */
233 do {
234 if (pnext < lastpoint - 1)
235 pnext++;
236 else
237 pnext = 0;
238 } while (pnext != pcur && x[pcur] == x[pnext] && y[pcur] == y[pnext]);
239
240 /* find previous distinct point */
241 do {
242 if (pprev > 0)
243 pprev--;
244 else
245 pprev = lastpoint - 1;
246 } while (pprev != pcur && x[pcur] == x[pprev] && y[pcur] == y[pprev]);
247
248 /* orientation at vertex pcur == signed area for triangle pprev, pcur, pnext
249 * rather use robust determinant of Olivier Devillers? */
250 orientation = (x[pnext] - x[pprev]) * (y[pcur] - y[pprev]) -
251 (x[pcur] - x[pprev]) * (y[pnext] - y[pprev]);
252
253 if (orientation)
254 return orientation;
255
256 /* orientation is 0, next check */
257 /* find leftmost lowest vertex of the polygon */
258 pcur = 0;
259 for (pnext = 1; pnext < lastpoint; pnext++) {
260 if (y[pnext] > y[pcur])
261 continue;
262 else if (y[pnext] == y[pcur]) { /* just as low */
263 if (x[pnext] > x[pcur]) /* but to the right */
264 continue;
265 if (x[pnext] ==
266 x[pcur]) { /* duplicate point, self-intersecting polygon ? */
267 pprev = (pcur == 0 ? lastpoint - 1 : pcur - 1);
268 if (y[pnext - 1] > y[pprev])
269 continue;
270 }
271 }
272 pcur = pnext; /* a new leftmost lowest vertex */
273 }
274
275 /* Points are not pruned, so ... */
276 pnext = pcur;
277 pprev = pcur;
278
279 /* find next distinct point */
280 do {
281 if (pnext < lastpoint - 1)
282 pnext++;
283 else
284 pnext = 0;
285 } while (pnext != pcur && x[pcur] == x[pnext] && y[pcur] == y[pnext]);
286
287 /* find previous distinct point */
288 do {
289 if (pprev > 0)
290 pprev--;
291 else
292 pprev = lastpoint - 1;
293 } while (pprev != pcur && x[pcur] == x[pprev] && y[pcur] == y[pprev]);
294
295 /* orientation at vertex pcur == signed area for triangle pprev, pcur, pnext
296 * rather use robust determinant of Olivier Devillers? */
297 orientation = (x[pnext] - x[pprev]) * (y[pcur] - y[pprev]) -
298 (x[pcur] - x[pprev]) * (y[pnext] - y[pprev]);
299
300 if (orientation)
301 return orientation;
302
303 /* orientation is 0, last check */
304 /* find rightmost lowest vertex of the polygon */
305 pcur = 0;
306 for (pnext = 1; pnext < lastpoint; pnext++) {
307 if (y[pnext] > y[pcur])
308 continue;
309 else if (y[pnext] == y[pcur]) { /* just as low */
310 if (x[pnext] < x[pcur]) /* but to the left */
311 continue;
312 if (x[pnext] ==
313 x[pcur]) { /* duplicate point, self-intersecting polygon ? */
314 pprev = (pcur == 0 ? lastpoint - 1 : pcur - 1);
315 if (y[pnext - 1] > y[pprev])
316 continue;
317 }
318 }
319 pcur = pnext; /* a new rightmost lowest vertex */
320 }
321
322 /* Points are not pruned, so ... */
323 pnext = pcur;
324 pprev = pcur;
325
326 /* find next distinct point */
327 do {
328 if (pnext < lastpoint - 1)
329 pnext++;
330 else
331 pnext = 0;
332 } while (pnext != pcur && x[pcur] == x[pnext] && y[pcur] == y[pnext]);
333
334 /* find previous distinct point */
335 do {
336 if (pprev > 0)
337 pprev--;
338 else
339 pprev = lastpoint - 1;
340 } while (pprev != pcur && x[pcur] == x[pprev] && y[pcur] == y[pprev]);
341
342 /* orientation at vertex pcur == signed area for triangle pprev, pcur, pnext
343 * rather use robust determinant of Olivier Devillers? */
344 orientation = (x[pnext] - x[pprev]) * (y[pcur] - y[pprev]) -
345 (x[pcur] - x[pprev]) * (y[pnext] - y[pprev]);
346
347 return orientation; /* 0 for degenerate */
348}
int dig_alloc_points(struct line_pnts *, int)
allocate room for 'num' X and Y arrays in struct line_pnts
int dig_get_poly_points(int n_lines, struct line_pnts **LPoints, int *direction, struct line_pnts *BPoints)
Definition diglib/poly.c:31
int dig_find_area_poly(struct line_pnts *Points, double *totalarea)
Definition diglib/poly.c:94
double dig_find_poly_orientation(struct line_pnts *Points)
Feature geometry info - coordinates.
double * y
Array of Y coordinates.
double * x
Array of X coordinates.
int n_points
Number of points.