GRASS 8 Programmer's Manual 8.6.0dev(2026)-f808a6d29a
Loading...
Searching...
No Matches
Vlib/poly.c
Go to the documentation of this file.
1/*!
2 \file lib/vector/Vlib/poly.c
3
4 \brief Vector library - polygon related fns
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
13 \author Original author CERL, probably Dave Gerdes or Mike Higgins.
14 \author Update to GRASS 5.7 Radim Blazek and David D. Gray.
15 */
16
17#include <math.h>
18#include <stdlib.h>
19#include <grass/vector.h>
20#include <grass/linkm.h>
21#include <grass/glocale.h>
22
23/* numerically safe floating point average
24 * works best with A < B */
25#define FP_AVG(A, B) \
26 (((A) > 0 && (B) < 0) || ((A) < 0 && (B) > 0) ? (((A) + (B)) / 2.) \
27 : ((A) + ((B) - (A)) / 2.))
28
29struct Slink {
30 struct Slink *next;
31 double x;
32};
33
34/* function prototypes */
35static int comp_double(const void *, const void *);
36static int V__within(double, double, double);
37int Vect__intersect_y_line_with_poly(const struct line_pnts *, double,
38 struct line_pnts *);
39int Vect__intersect_x_line_with_poly(const struct line_pnts *, double,
40 struct line_pnts *);
41static void destroy_links(struct link_head *, struct Slink *);
42static int Vect__divide_and_conquer(struct Slink *, const struct line_pnts *,
43 struct link_head *, double *, double *,
44 int);
45
46/*!
47 \brief Get point inside area and outside all islands.
48
49 Take a line and intersect it with the polygon and any islands.
50 sort the list of X values from these intersections. This will
51 be a list of segments alternating IN/OUT/IN/OUT of the polygon.
52 Pick the largest IN segment and take the midpoint.
53
54 \param Map vector map
55 \param area area id
56 \param[out] X,Y point coordinateds
57
58 \return 0 on success
59 \return -1 on error
60 */
61int Vect_get_point_in_area(struct Map_info *Map, int area, double *X, double *Y)
62{
63 static struct line_pnts *Points;
64 static struct line_pnts **IPoints;
65 static int first_time = 1;
66 static int isl_allocated = 0;
67 int i, n_isles;
68
69 G_debug(3, "Vect_get_point_in_area()");
70
71 if (first_time) {
72 Points = Vect_new_line_struct();
73 IPoints = NULL;
74 first_time = 0;
75 }
76 n_isles = Vect_get_area_num_isles(Map, area);
77 if (n_isles > isl_allocated) {
78 IPoints = (struct line_pnts **)G_realloc(
79 IPoints, (1 + n_isles) * sizeof(struct line_pnts *));
80 for (i = isl_allocated; i < n_isles; i++)
82 isl_allocated = n_isles;
83 }
84
85 if (0 > Vect_get_area_points(Map, area, Points))
86 return -1;
87
88 for (i = 0; i < n_isles; i++) {
89 IPoints[i]->alloc_points = 0;
91 IPoints[i]))
92 return -1;
93 }
94 return (Vect_get_point_in_poly_isl((const struct line_pnts *)Points,
95 (const struct line_pnts **)IPoints,
96 n_isles, X, Y));
97
98 return -1;
99}
100
101static int comp_double(const void *i, const void *j)
102{
103 if (*(const double *)i < *(const double *)j)
104 return -1;
105
106 return (*(const double *)i > *(const double *)j);
107}
108
109static int V__within(double a, double x, double b)
110{
111 if (a < b)
112 return (x >= a && x < b);
113
114 return (x > b && x <= a);
115}
116
117/*
118 \brief Intersects line with polygon
119
120 For each intersection of a polygon w/ a line, stuff the X value in
121 the Inter Points array. I used line_pnts, just cuz the memory
122 management was already there. I am getting real tired of managing
123 realloc stuff.
124
125 \param Points line
126 \param y y coordinate of horizontal line
127 \param Inter intersections of horizontal line with points line
128
129 \return 0 on success
130 \return -1 on error
131 */
132int Vect__intersect_y_line_with_poly(const struct line_pnts *Points, double y,
133 struct line_pnts *Inter)
134{
135 int i;
136 double a, b, c, d, x;
137 double p;
138
139 for (i = 1; i < Points->n_points; i++) {
140 a = Points->y[i - 1];
141 b = Points->y[i];
142
143 c = Points->x[i - 1];
144 d = Points->x[i];
145
146 /* sort for numerical stability
147 * ray along X for given Y -> sort by Y */
148 if (b < a || (b == a && d < c)) {
149 p = d;
150 d = c;
151 c = p;
152
153 p = a;
154 a = b;
155 b = p;
156 }
157
158 if (V__within(a, y, b)) {
159 if (a == b)
160 continue;
161
162 p = (y - a) / (b - a); /* always within [0, 1] */
163 x = c + p * (d - c);
164
165 if (0 > Vect_append_point(Inter, x, y, 0))
166 return -1;
167 }
168 }
169 return 0;
170}
171
172/*
173 \brief Intersects line with polygon
174
175 For each intersection of a polygon w/ a line, stuff the Y value in
176 the Inter Points array. I used line_pnts, just cuz the memory
177 management was already there. I am getting real tired of managing
178 realloc stuff.
179
180 \param Points line
181 \param x x coordinate of vertical line
182 \param Inter intersections of horizontal line with points line
183
184 \return 0 on success
185 \return -1 on error
186 */
187int Vect__intersect_x_line_with_poly(const struct line_pnts *Points, double x,
188 struct line_pnts *Inter)
189{
190 int i;
191 double a, b, c, d, y;
192 double p;
193
194 for (i = 1; i < Points->n_points; i++) {
195 a = Points->x[i - 1];
196 b = Points->x[i];
197
198 c = Points->y[i - 1];
199 d = Points->y[i];
200
201 /* sort for numerical stability
202 * ray along Y for given X -> sort by X */
203 if (b < a || (b == a && d < c)) {
204 p = d;
205 d = c;
206 c = p;
207
208 p = a;
209 a = b;
210 b = p;
211 }
212
213 if (V__within(a, x, b)) {
214 if (a == b)
215 continue;
216
217 p = (x - a) / (b - a); /* always within [0, 1] */
218 y = c + p * (d - c);
219
220 if (0 > Vect_append_point(Inter, x, y, 0))
221 return -1;
222 }
223 }
224 return 0;
225}
226
227/*!
228 \brief Get point inside polygon.
229
230 This does NOT consider ISLANDS!
231
232 \param Points polygon
233 \param[out] X,Y point coordinates
234
235 \return 0 on success
236 \return -1 on error
237 */
238int Vect_get_point_in_poly(const struct line_pnts *Points, double *X, double *Y)
239{
240 double cent_x, cent_y;
241 struct Slink *Head;
242 static struct link_head *Token;
243 struct Slink *tmp;
244 static int first_time = 1;
245 register int i;
246 double x_max, x_min;
247 int ret;
248
249 /* get centroid */
251 /* is it w/in poly? */
252 if (Vect_point_in_poly(cent_x, cent_y, Points) == 1) {
253 *X = cent_x;
254 *Y = cent_y;
255 return 0;
256 }
257
258 /* guess we have to do it the hard way... */
259 G_debug(3, "Vect_get_point_in_poly(): divide and conquer");
260
261 /* get min and max x values */
262 x_max = x_min = Points->x[0];
263 for (i = 0; i < Points->n_points; i++) {
264 if (x_min > Points->x[i])
265 x_min = Points->x[i];
266 if (x_max < Points->x[i])
267 x_max = Points->x[i];
268 }
269
270 /* init the linked list */
271 if (first_time) {
272 /* will never call link_cleanup () */
273 link_exit_on_error(1); /* kill program if out of memory */
274 Token = (struct link_head *)link_init(sizeof(struct Slink));
275 first_time = 0;
276 }
277
278 Head = (struct Slink *)link_new(Token);
279 tmp = (struct Slink *)link_new(Token);
280
281 Head->next = tmp;
282 tmp->next = NULL;
283
284 Head->x = x_min;
285 tmp->x = x_max;
286
287 *Y = cent_y; /* pick line segment (x_min, cent_y) - (x_max, cent_y) */
288 ret = Vect__divide_and_conquer(Head, Points, Token, X, Y, 10);
289
290 destroy_links(Token, Head);
291
292 if (ret < 0) {
293 G_warning("Vect_get_point_in_poly(): %s",
294 _("Unable to find point in polygon"));
295 return -1;
296 }
297
298 G_debug(3, "Found point in %d iterations", 10 - ret);
299
300 return 0;
301}
302
303/*
304 \brief Provide a breadth first binary division of real space along line
305 segment.
306
307 Looking for a point w/in the polygon.
308
309 This routine walks along the list of points on line segment
310 and divides each pair in half. It sticks that new point right into
311 the list, and then checks to see if it is inside the poly.
312
313 After going through the whole list, it calls itself. The list
314 now has a whole extra set of points to divide again.
315
316 \param Head
317 \param Points
318 \param Token
319 \param X,Y
320 \param levels
321
322 \return # levels it took
323 \return -1 if exceeded # of levels
324 */
325static int Vect__divide_and_conquer(struct Slink *Head,
326 const struct line_pnts *Points,
327 struct link_head *Token, double *X,
328 double *Y, int levels)
329{
330 struct Slink *A, *B, *C;
331
332 G_debug(3, "Vect__divide_and_conquer(): LEVEL %d", levels);
333 A = Head;
334 B = Head->next;
335
336 do {
337 C = (struct Slink *)link_new(Token);
338 A->next = C;
339 C->next = B;
340
341 C->x = (A->x + B->x) / 2.;
342
343 if (Vect_point_in_poly(C->x, *Y, Points) == 1) {
344 *X = C->x;
345 return levels;
346 }
347
348 A = B;
349 B = B->next;
350 } while (B != NULL);
351
352 /*
353 ** If it got through the entire loop and still no hits,
354 ** then lets go a level deeper and divide again.
355 */
356
357 if (levels <= 0)
358 return -1;
359
360 return Vect__divide_and_conquer(Head, Points, Token, X, Y, --levels);
361}
362
363static void destroy_links(struct link_head *Token, struct Slink *Head)
364{
365 struct Slink *p, *tmp;
366
367 p = Head;
368
369 while (p != NULL) {
370 tmp = p->next;
372 p = tmp;
373 }
374}
375
376/*!
377 \brief Get centroid of polygon
378
379 \param points polygon
380 \param[out] cent_x,cent_y centroid coordinates
381
382 \return 0 on success
383 \return -1 on error
384 */
385int Vect_find_poly_centroid(const struct line_pnts *points, double *cent_x,
386 double *cent_y)
387{
388 int i;
389 double *xptr1, *yptr1;
390 double *xptr2, *yptr2;
392 double len, tot_len;
393
394 tot_len = 0.0;
395 cent_weight_x = 0.0;
396 cent_weight_y = 0.0;
397
398 xptr1 = points->x;
399 yptr1 = points->y;
400 xptr2 = points->x + 1;
401 yptr2 = points->y + 1;
402
403 /* center of gravity of the polygon line, not area */
404 for (i = 1; i < points->n_points; i++) {
405 len = hypot(*xptr1 - *xptr2, *yptr1 - *yptr2);
406 cent_weight_x += len * ((*xptr1 + *xptr2) / 2.);
407 cent_weight_y += len * ((*yptr1 + *yptr2) / 2.);
408 tot_len += len;
409 xptr1++;
410 xptr2++;
411 yptr1++;
412 yptr2++;
413 }
414
415 if (tot_len == 0.0)
416 return -1;
417
420
421 return 0;
422}
423
424/*!
425 \brief Get centroid of polygon
426
427 Calculate the center of gravity of the area considering any islands
428
429 \param Points polygon
430 \param IPoints isles (list of isle boundaries)
431 \param n_isles number of isles
432 \param[out] cent_x,cent_y centroid coordinates
433
434 \return 0 on success
435 \return -1 on error
436
437 \since version 8.6
438 */
439int Vect_find_poly_centroid_cog(const struct line_pnts *Points,
440 const struct line_pnts **IPoints, int n_isles,
441 double *cent_x, double *cent_y)
442{
443 struct bound_box box;
444 double x, y, meanx, meany;
445 double *xp, *yp;
446 double w, tot_w;
447 int i, isle;
448
449 /* surveyor's / shoelace formula */
450 /* the surveyor should not be too far away (fp precision limit) */
451 /* surveyor's position: */
452 Vect_line_box(Points, &box);
453 x = (box.W + box.E) / 2.;
454 y = (box.S + box.N) / 2.;
455 meanx = meany = 0.;
456 tot_w = 0.;
457
458 *cent_x = x;
459 *cent_y = y;
460
461 xp = Points->x;
462 yp = Points->y;
463 for (i = 1; i < Points->n_points; i++) {
464 w = (x - xp[i - 1]) * (yp[i] - y) - (x - xp[i]) * (yp[i - 1] - y);
465
466 meanx += (xp[i - 1] + xp[i] + x) * w;
467 meany += (yp[i - 1] + yp[i] + y) * w;
468 tot_w += w;
469 }
470
471 for (isle = 0; isle < n_isles; isle++) {
472 xp = IPoints[isle]->x;
473 yp = IPoints[isle]->y;
474 for (i = 1; i < IPoints[isle]->n_points; i++) {
475 w = (x - xp[i - 1]) * (yp[i] - y) - (x - xp[i]) * (yp[i - 1] - y);
476
477 meanx += (xp[i - 1] + xp[i] + x) * w;
478 meany += (yp[i - 1] + yp[i] + y) * w;
479 tot_w += w;
480 }
481 }
482 if (tot_w != 0) {
483 *cent_x = meanx / (tot_w * 3);
484 *cent_y = meany / (tot_w * 3);
485 }
486 else
487 return -1;
488
489 return 0;
490}
491
492/*
493 ** returns true if point is in any of islands /w in area
494 ** returns 0 if not
495 ** returns -1 on error
496 */
497/*
498 int
499 Vect_point_in_islands (
500 struct Map_info *Map,
501 int area,
502 double cent_x, double cent_y)
503 {
504 struct P_area *Area;
505 static struct line_pnts *TPoints;
506 static int first_time = 1;
507 int isle;
508
509 if (first_time == 1)
510 {
511 TPoints = Vect_new_line_struct ();
512 first_time = 0;
513 }
514
515 Area = &(Map->plus.Area[area]);
516
517 for (isle = 0; isle < Area->n_isles; isle++)
518 {
519 if (0 > Vect_get_isle_points (Map, Area->isles[isle], TPoints))
520 return -1;
521
522 if ( Vect_point_in_poly (cent_x, cent_y, TPoints) == 1 )
523 return 1;
524 }
525
526 return 0;
527 }
528 */
529
530/*!
531 \brief Get point inside polygon but outside the islands specifiled in
532 IPoints.
533
534 Take a line and intersect it with the polygon and any islands.
535 sort the list of X values from these intersections. This will be a
536 list of segments alternating IN/OUT/IN/OUT of the polygon. Pick the
537 largest IN segment and take the midpoint.
538
539 \param Points polygon (boundary)
540 \param IPoints isles (list of isle boundaries)
541 \param n_isles number of isles
542 \param[out] att_x,att_y point coordinates
543
544 \return 0 on success
545 \return -1 on error
546 */
547int Vect_get_point_in_poly_isl(const struct line_pnts *Points,
548 const struct line_pnts **IPoints, int n_isles,
549 double *att_x, double *att_y)
550{
551 static struct line_pnts *Intersects;
552 static int first_time = 1;
553 double cent_x, cent_y;
554 register int i, j;
555 double max, hi_x, lo_x, hi_y, lo_y;
556 double fa, fb, dmax;
557 int exp;
558 int maxpos;
559 int point_in_sles = 0;
560 double diff;
561 int ret;
562 bool success = true;
563
564 G_debug(3, "Vect_get_point_in_poly_isl(): n_isles = %d", n_isles);
565
566 if (first_time) {
568 first_time = 0;
569 }
570
571 /* need an outer ring with at least 4 vertices for
572 * areas with a size > 0 */
573 if (Points->n_points < 4) {
574 G_debug(
575 3,
576 "Vect_get_point_in_poly_isl(): outer ring with less than 4 points");
577 if (Points->n_points > 0) {
578 *att_x = Points->x[0];
579 *att_y = Points->y[0];
580 return 0;
581 }
582 return -1;
583 }
584
585 /* get centroid, center of gravity of the polygon line, not the area */
586 /* original version, does not consider isles
587 * the centroid can be inside an isle or completely outside of the area
588 * for very thin polygons, the centroid might be on the boundary */
590 /* is it w/in poly? */
591 point_in_sles = 0;
592 if (Vect_point_in_poly(cent_x, cent_y, Points) == 1) {
593 /* if the point is inside the polygon */
594 for (i = 0; i < n_isles; i++) {
595 if (Vect_point_in_poly(cent_x, cent_y, IPoints[i]) >= 1) {
596 point_in_sles = 1;
597 break;
598 }
599 }
600 if (!point_in_sles) {
601 *att_x = cent_x;
602 *att_y = cent_y;
603 return 0;
604 }
605 }
606
607 /* get centroid, center of gravity of the area */
608 /* new version, considers isles
609 * the centroid can still be inside an isle or completely outside of the
610 * area for very thin polygons, the centroid might be on the boundary */
611 Vect_find_poly_centroid_cog(Points, IPoints, n_isles, &cent_x, &cent_y);
612 /* is it w/in poly? */
613 point_in_sles = 0;
614 if (Vect_point_in_poly(cent_x, cent_y, Points) == 1) {
615 /* if the point is inside the polygon */
616 for (i = 0; i < n_isles; i++) {
617 if (Vect_point_in_poly(cent_x, cent_y, IPoints[i]) >= 1) {
618 point_in_sles = 1;
619 break;
620 }
621 }
622 if (!point_in_sles) {
623 *att_x = cent_x;
624 *att_y = cent_y;
625 return 0;
626 }
627 }
628 /* guess we have to do it the hard way... */
629 G_debug(3, "Vect_get_point_in_poly_isl(): the hard way");
630
631 /* first find att_y close to cent_y so that no points lie on the line */
632 /* find the point closest to line from below, and point close to line
633 from above and take average of their y-coordinates */
634 /* same for x */
635
636 /* first initializing lo_x,hi_x and lo_y,hi_y
637 * to be any 2 pnts on either side of cent_x and cent_y */
638 hi_y = cent_y - 1;
639 lo_y = cent_y + 1;
640
641 hi_x = cent_x - 1;
642 lo_x = cent_x + 1;
643 for (i = 0; i < Points->n_points; i++) {
644 if ((lo_y < cent_y) && (hi_y >= cent_y) && (lo_x < cent_x) &&
645 (hi_x >= cent_x))
646 break; /* already initialized */
647 if (Points->y[i] < cent_y)
648 lo_y = Points->y[i];
649 if (Points->y[i] >= cent_y)
650 hi_y = Points->y[i];
651
652 if (Points->x[i] < cent_x)
653 lo_x = Points->x[i];
654 if (Points->x[i] >= cent_x)
655 hi_x = Points->x[i];
656 }
657 /* first going through boundary points */
658 for (i = 0; i < Points->n_points; i++) {
659 if ((Points->y[i] < cent_y) &&
660 ((cent_y - Points->y[i]) < (cent_y - lo_y)))
661 lo_y = Points->y[i];
662 if ((Points->y[i] >= cent_y) &&
663 ((Points->y[i] - cent_y) < (hi_y - cent_y)))
664 hi_y = Points->y[i];
665
666 if ((Points->x[i] < cent_x) &&
667 ((cent_x - Points->x[i]) < (cent_x - lo_x)))
668 lo_x = Points->x[i];
669 if ((Points->x[i] >= cent_x) &&
670 ((Points->x[i] - cent_x) < (hi_x - cent_x)))
671 hi_x = Points->x[i];
672 }
673 for (i = 0; i < n_isles; i++) {
674 for (j = 0; j < IPoints[i]->n_points; j++) {
675 if ((IPoints[i]->y[j] < cent_y) &&
676 ((cent_y - IPoints[i]->y[j]) < (cent_y - lo_y)))
677 lo_y = IPoints[i]->y[j];
678 if ((IPoints[i]->y[j] >= cent_y) &&
679 ((IPoints[i]->y[j] - cent_y) < (hi_y - cent_y)))
680 hi_y = IPoints[i]->y[j];
681
682 if ((IPoints[i]->x[j] < cent_x) &&
683 ((cent_x - IPoints[i]->x[j]) < (cent_x - lo_x)))
684 lo_x = IPoints[i]->x[j];
685 if ((IPoints[i]->x[j] >= cent_x) &&
686 ((IPoints[i]->x[j] - cent_x) < (hi_x - cent_x)))
687 hi_x = IPoints[i]->x[j];
688 }
689 }
690
691 /* try y intersect */
692 if (lo_y == hi_y) {
693 G_debug(3, "Vect_get_point_in_poly_isl(): lo_y == hi_y");
694 /* don't give up yet */
695 success = false;
696 }
697 if (success) {
698 *att_y = FP_AVG(lo_y, hi_y);
699
700 Intersects->n_points = 0;
702 return -1;
703
704 /* add in intersections w/ holes */
705 for (i = 0; i < n_isles; i++) {
707 Intersects))
708 return -1;
709 }
710
711 if (Intersects->n_points < 2) { /* test */
712 G_debug(3, "Vect_get_point_in_poly_isl(): no x intersections");
713 /* don't give up yet */
714 success = false;
715 }
716 }
717 if (success) {
718 qsort(Intersects->x, (size_t)Intersects->n_points, sizeof(double),
719 comp_double);
720
721 max = 0;
722 maxpos = 0;
723
724 /* find area of MAX distance */
725 for (i = 0; i < Intersects->n_points; i += 2) {
726 diff = Intersects->x[i + 1] - Intersects->x[i];
727
728 if (diff > max) {
729 max = diff;
730 maxpos = i;
731 }
732 }
733 /* ULP single precision 23, double 52 bits, here 42 */
734 /* if the difference is too small, the point will be on a line
735 * ULP double is too small, ULP single too large */
736 fa = fabs(Intersects->x[maxpos]);
737 fb = fabs(Intersects->x[maxpos + 1]);
738 if (fa > fb)
739 dmax = frexp(fa, &exp);
740 else
741 dmax = frexp(fb, &exp);
742 exp -= 42;
743 dmax = ldexp(dmax, exp);
744
745 if (max > dmax) {
747 }
748 else
749 success = false;
750 }
751 if (!success) {
752 /* try x intersect */
753 G_debug(3, "Vect_get_point_in_poly_isl(): trying x intersect");
754
755 if (lo_x == hi_x) {
756 G_debug(3, "Vect_get_point_in_poly_isl(): lo_x == hi_x");
757
758 return (-1); /* area is empty */
759 }
760
761 *att_x = FP_AVG(lo_x, hi_x);
762
763 Intersects->n_points = 0;
765 return -1;
766
767 /* add in intersections w/ holes */
768 for (i = 0; i < n_isles; i++) {
770 Intersects))
771 return -1;
772 }
773
774 if (Intersects->n_points < 2) { /* test */
775 G_debug(3, "Vect_get_point_in_poly_isl(): no y intersections");
776
777 return -1;
778 }
779
780 qsort(Intersects->y, (size_t)Intersects->n_points, sizeof(double),
781 comp_double);
782
783 max = 0;
784 maxpos = 0;
785
786 /* find area of MAX distance */
787 for (i = 0; i < Intersects->n_points; i += 2) {
788 diff = Intersects->y[i + 1] - Intersects->y[i];
789
790 if (diff > max) {
791 max = diff;
792 maxpos = i;
793 }
794 }
795 /* ULP single precision 23, double 52 bits, here 42 */
796 fa = fabs(Intersects->y[maxpos]);
797 fb = fabs(Intersects->y[maxpos + 1]);
798 if (fa > fb)
799 dmax = frexp(fa, &exp);
800 else
801 dmax = frexp(fb, &exp);
802 exp -= 42;
803 dmax = ldexp(dmax, exp);
804 if (max > dmax) {
806 }
807 else {
808 /* area was (nearly) empty: example ((x1,y1), (x2,y2), (x1,y1)) */
809 G_warning("Vect_get_point_in_poly_isl(): collapsed area");
810 return -1;
811 }
812 }
813
814 /* is it now w/in poly? */
815 cent_x = *att_x;
816 cent_y = *att_y;
817 point_in_sles = 0;
818
819 /* even if this test succeeds, the centroid might be assigned to
820 * a different area later on by Vect_find_area() */
822 if (ret == 2) {
823 /* point on outer ring, should not happen because of ULP test above */
824 G_warning("Vect_get_point_in_poly_isl(), the hard way: centroid is on "
825 "outer ring, max dist is %g",
826 max);
827 return -1;
828 }
829 if (ret == 1) {
830 /* if the point is inside the polygon, should not happen because of ULP
831 * test above */
832 for (i = 0; i < n_isles; i++) {
833 if (Vect_point_in_poly(cent_x, cent_y, IPoints[i]) >= 1) {
834 point_in_sles = 1;
835 G_warning("Vect_get_point_in_poly_isl(), the hard way: "
836 "centroid is in isle, max dist is %g",
837 max);
838 break;
839 }
840 }
841 if (!point_in_sles) {
842 return 0;
843 }
844 }
845
846 return -1;
847}
848
849/* Intersect segments of Points with ray from point X,Y to the right.
850 * Returns: -1 point exactly on segment
851 * number of intersections
852 */
853static int segments_x_ray(double X, double Y, const struct line_pnts *Points)
854{
855 double x1, x2, y1, y2;
856 double x_inter;
857 int n_intersects;
858 int n;
859
860 G_debug(3, "segments_x_ray(): x = %f y = %f n_points = %d", X, Y,
861 Points->n_points);
862
863 /* Follow the ray from X,Y along positive x and find number of
864 * intersections. Coordinates exactly on ray are considered to be slightly
865 * above. */
866
867 n_intersects = 0;
868 for (n = 1; n < Points->n_points; n++) {
869 x1 = Points->x[n - 1];
870 y1 = Points->y[n - 1];
871 x2 = Points->x[n];
872 y2 = Points->y[n];
873
874 /* G_debug() is slow, avoid it in loops over points,
875 * activate when needed */
876 /*
877 G_debug(3, "X = %f Y = %f x1 = %f y1 = %f x2 = %f y2 = %f", X, Y, x1,
878 y1, x2, y2);
879 */
880
881 /* I know, it should be possible to do that with less conditions,
882 * but it should be enough readable also! */
883
884 /* first, skip segments that obviously do not intersect with test ray */
885
886 /* segment above (X is not important) */
887 if (y1 > Y && y2 > Y)
888 continue;
889
890 /* segment below (X is not important) */
891 if (y1 < Y && y2 < Y)
892 continue;
893
894 /* segment left from X -> no intersection */
895 if (x1 < X && x2 < X)
896 continue;
897
898 /* point on vertex */
899 if ((x1 == X && y1 == Y) || (x2 == X && y2 == Y))
900 return -1;
901
902 /* on vertical boundary */
903 if (x1 == x2 && x1 == X) {
904 if ((y1 <= Y && y2 >= Y) || (y1 >= Y && y2 <= Y))
905 return -1;
906 }
907
908 /* on horizontal boundary */
909 if (y1 == y2 && y1 == Y) {
910 if ((x1 <= X && x2 >= X) || (x1 >= X && x2 <= X))
911 return -1;
912 else
913 continue; /* segment on ray (X is not important) */
914 }
915
916 /* segment on ray (X is not important) */
917 /* if (y1 == Y && y2 == Y)
918 continue; */
919
920 /* one end on Y second above (X is not important) */
921 if ((y1 == Y && y2 > Y) || (y2 == Y && y1 > Y))
922 continue;
923
924 /* For following cases we know that at least one of x1 and x2 is >= X */
925
926 /* one end of segment on Y second below Y */
927 if (y1 == Y && y2 < Y) {
928 if (x1 >= X) /* x of the end on the ray is >= X */
929 n_intersects++;
930 continue;
931 }
932 if (y2 == Y && y1 < Y) {
933 if (x2 >= X)
934 n_intersects++;
935 continue;
936 }
937
938 /* one end of segment above Y second below Y */
939 if ((y1 < Y && y2 > Y) || (y1 > Y && y2 < Y)) {
940 if (x1 >= X && x2 >= X) {
941 n_intersects++;
942 continue;
943 }
944
945 /* now either x1 < X && x2 > X or x1 > X && x2 < X -> calculate
946 * intersection */
947 x_inter = dig_x_intersect(x1, x2, y1, y2, Y);
948 G_debug(3, "x_inter = %f", x_inter);
949 /* what if x_inter is not quite but nearly identical to X
950 * and x_inter - X is so small that calculations can lead to
951 * fp precision errors? */
952 if (x_inter == X)
953 return -1; /* point on segment, do not assume inside/outside */
954 else if (x_inter > X)
955 n_intersects++;
956
957 continue; /* would not be necessary, just to check, see below */
958 }
959 /* should not be reached (one condition is not necessary, but it is
960 * maybe better readable and it is a check) */
961 G_warning("segments_x_ray() %s: X = %f Y = %f x1 = %f y1 = %f x2 = %f "
962 "y2 = %f",
963 _("conditions failed"), X, Y, x1, y1, x2, y2);
964 }
965
966 return n_intersects;
967}
968
969/*!
970 \brief Determines if a point (X,Y) is inside a polygon.
971
972 \param X,Y point coordinates
973 \param Points polygon
974
975 \return 0 - outside
976 \return 1 - inside
977 \return 2 - on the boundary
978 */
979int Vect_point_in_poly(double X, double Y, const struct line_pnts *Points)
980{
981 int n_intersects;
982
983 G_debug(3, "Vect_point_in_poly(): x = %f y = %f n_points = %d", X, Y,
984 Points->n_points);
985
986 n_intersects = segments_x_ray(X, Y, Points);
987
988 if (n_intersects == -1)
989 return 2;
990
991 /* odd number of intersections: inside, return 1
992 * even number of intersections: outside, return 0 */
993 return (n_intersects & 1);
994}
995
996/*!
997 \brief Determines if a point (X,Y) is inside an area outer ring. Islands are
998 not considered.
999
1000 \param X,Y point coordinates
1001 \param Map vector map
1002 \param area area id
1003 \param box area bounding box
1004
1005 \return 0 - outside
1006 \return 1 - inside
1007 \return 2 - on the boundary
1008 */
1009int Vect_point_in_area_outer_ring(double X, double Y, struct Map_info *Map,
1010 int area, struct bound_box *box)
1011{
1012 static int first = 1;
1013 static struct line_pnts *Points;
1014
1015 /* keep in sync with Vect_point_in_island() */
1016
1017 G_debug(3, "Vect_point_in_area_outer_ring(): x = %f y = %f area = %d", X, Y,
1018 area);
1019
1020 if (first == 1) {
1021 Points = Vect_new_line_struct();
1022 first = 0;
1023 }
1024
1025 /* First it must be in box */
1026 if (X < box->W || X > box->E || Y > box->N || Y < box->S)
1027 return 0;
1028
1029 if (0 > Vect_get_area_points(Map, area, Points))
1030 return 0;
1031
1032 return Vect_point_in_poly(X, Y, Points);
1033}
1034
1035/*!
1036 \brief Determines if a point (X,Y) is inside an island.
1037
1038 \param X,Y point coordinates
1039 \param Map vector map
1040 \param isle isle id
1041 \param box isle bounding box
1042
1043 \return 0 - outside
1044 \return 1 - inside
1045 \return 2 - on the boundary
1046 */
1047int Vect_point_in_island(double X, double Y, struct Map_info *Map, int isle,
1048 struct bound_box *box)
1049{
1050 static int first = 1;
1051 static struct line_pnts *Points;
1052
1053 /* keep in sync with Vect_point_in_area_outer_ring() */
1054
1055 G_debug(3, "Vect_point_in_island(): x = %f y = %f isle = %d", X, Y, isle);
1056
1057 if (first == 1) {
1058 Points = Vect_new_line_struct();
1059 first = 0;
1060 }
1061
1062 /* First it must be in box */
1063 if (X < box->W || X > box->E || Y > box->N || Y < box->S)
1064 return 0;
1065
1066 if (0 > Vect_get_isle_points(Map, isle, Points))
1067 return 0;
1068
1069 return Vect_point_in_poly(X, Y, Points);
1070}
int Vect_find_poly_centroid_cog(const struct line_pnts *Points, const struct line_pnts **IPoints, int n_isles, double *cent_x, double *cent_y)
Get centroid of polygon.
Definition Vlib/poly.c:439
int Vect_get_point_in_poly_isl(const struct line_pnts *Points, const struct line_pnts **IPoints, int n_isles, double *att_x, double *att_y)
Get point inside polygon but outside the islands specifiled in IPoints.
Definition Vlib/poly.c:547
int Vect_point_in_poly(double X, double Y, const struct line_pnts *Points)
Determines if a point (X,Y) is inside a polygon.
Definition Vlib/poly.c:979
int Vect_get_point_in_poly(const struct line_pnts *Points, double *X, double *Y)
Get point inside polygon.
Definition Vlib/poly.c:238
int Vect_find_poly_centroid(const struct line_pnts *points, double *cent_x, double *cent_y)
Get centroid of polygon.
Definition Vlib/poly.c:385
int Vect__intersect_x_line_with_poly(const struct line_pnts *, double, struct line_pnts *)
Definition Vlib/poly.c:187
int Vect__intersect_y_line_with_poly(const struct line_pnts *, double, struct line_pnts *)
Definition Vlib/poly.c:132
int Vect_point_in_island(double X, double Y, struct Map_info *Map, int isle, struct bound_box *box)
Determines if a point (X,Y) is inside an island.
Definition Vlib/poly.c:1047
int Vect_point_in_area_outer_ring(double X, double Y, struct Map_info *Map, int area, struct bound_box *box)
Determines if a point (X,Y) is inside an area outer ring. Islands are not considered.
Definition Vlib/poly.c:1009
#define FP_AVG(A, B)
Definition Vlib/poly.c:25
int Vect_get_point_in_area(struct Map_info *Map, int area, double *X, double *Y)
Get point inside area and outside all islands.
Definition Vlib/poly.c:61
#define NULL
Definition ccmath.h:32
#define G_realloc(p, n)
Definition defs/gis.h:141
void G_warning(const char *,...) __attribute__((format(printf
int G_debug(int, const char *,...) __attribute__((format(printf
VOID_T * link_new(struct link_head *)
Definition new.c:12
void link_dispose(struct link_head *, VOID_T *)
void link_exit_on_error(int)
Definition linkm/init.c:37
struct link_head * link_init(int)
Definition linkm/init.c:42
void Vect_line_box(const struct line_pnts *, struct bound_box *)
Get bounding box of line.
Definition line.c:888
int Vect_get_isle_points(struct Map_info *, int, struct line_pnts *)
Returns polygon array of points for given isle.
int Vect_get_area_points(struct Map_info *, int, struct line_pnts *)
Returns polygon array of points (outer ring) of given area.
int Vect_get_area_isle(struct Map_info *, int, int)
Returns isle id for area.
int Vect_get_area_num_isles(struct Map_info *, int)
Returns number of isles for given area.
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition line.c:45
int Vect_append_point(struct line_pnts *, double, double, double)
Appends one point to the end of a line.
Definition line.c:148
double dig_x_intersect(double, double, double, double, double)
Definition inside.c:20
#define max(x, y)
Definition draw2.c:30
#define _(str)
Definition glocale.h:10
#define VOID_T
Definition linkm.h:8
#define X
Definition ogsf.h:140
#define W
Definition ogsf.h:143
#define Y
Definition ogsf.h:141
double b
Definition r_raster.c:39
Vector map info.
Bounding box.
Definition dig_structs.h:62
double W
West.
Definition dig_structs.h:78
double S
South.
Definition dig_structs.h:70
double N
North.
Definition dig_structs.h:66
double E
East.
Definition dig_structs.h:74
Feature geometry info - coordinates.
double * y
Array of Y coordinates.
double * x
Array of X coordinates.
int n_points
Number of points.
#define x