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