GRASS Programmer's Manual  6.5.svn(2014)-r66266
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Vlib/poly.c
Go to the documentation of this file.
1 
21 #include <math.h>
22 #include <stdlib.h>
23 #include <grass/Vect.h>
24 #include <grass/gis.h>
25 #include <grass/linkm.h>
26 #include <grass/glocale.h>
27 
28 struct Slink
29 {
30  struct Slink *next;
31  double x;
32 };
33 
34 
35 /* function prototypes */
36 static int comp_double(double *, double *);
37 static int V__within(double, double, double);
39 static void destroy_links(struct link_head *, struct Slink *);
40 static int Vect__divide_and_conquer(struct Slink *, struct line_pnts *,
41  struct link_head *, double *, double *,
42  int);
43 
44 
60 int
61 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 **)
79  G_realloc(IPoints, (1 + n_isles) * sizeof(struct line_pnts *));
80  for (i = isl_allocated; i < n_isles; i++)
81  IPoints[i] = Vect_new_line_struct();
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;
90  if (0 >
91  Vect_get_isle_points(Map, Vect_get_area_isle(Map, area, i),
92  IPoints[i]))
93  return -1;
94  }
95  return (Vect_get_point_in_poly_isl(Points, IPoints, n_isles, X, Y));
96 
97  return -1;
98 }
99 
100 static int comp_double(double *i, double *j)
101 {
102  if (*i < *j)
103  return -1;
104 
105  if (*i > *j)
106  return 1;
107 
108  return 0;
109 }
110 
111 static int V__within(double a, double x, double b)
112 {
113  double tmp;
114 
115  if (a > b) {
116  tmp = a;
117  a = b;
118  b = tmp;
119  }
120 
121  return (x >= a && x <= b);
122 }
123 
124 /*
125  \brief Intersects line with polygon
126 
127  For each intersection of a polygon w/ a line, stuff the X value in
128  the Inter Points array. I used line_pnts, just cuz the memory
129  management was already there. I am getting real tired of managing
130  realloc stuff. Assumes that no vertex of polygon lies on Y This is
131  taken care of by functions calling this function
132 
133  \param Points line
134  \param y ?
135  \param Iter intersection ?
136 
137  \return 0 on success
138  \return -1 on error
139  */
140 int
141 Vect__intersect_line_with_poly(struct line_pnts *Points,
142  double y, struct line_pnts *Inter)
143 {
144  int i;
145  double a, b, c, d, x;
146  double perc;
147 
148  for (i = 1; i < Points->n_points; i++) {
149  a = Points->y[i - 1];
150  b = Points->y[i];
151 
152  c = Points->x[i - 1];
153  d = Points->x[i];
154 
155  if (V__within(a, y, b)) {
156  if (a == b)
157  continue;
158 
159  perc = (y - a) / (b - a);
160  x = perc * (d - c) + c; /* interp X */
161 
162  if (0 > Vect_append_point(Inter, x, y, 0))
163  return -1;
164  }
165  }
166  return 0;
167 }
168 
180 int Vect_get_point_in_poly(struct line_pnts *Points, double *X, double *Y)
181 {
182  double cent_x, cent_y;
183  struct Slink *Head;
184  static struct link_head *Token;
185  struct Slink *tmp;
186  static int first_time = 1;
187  register int i;
188  double x_max, x_min;
189  int ret;
190 
191  /* get centroid */
192  Vect_find_poly_centroid(Points, &cent_x, &cent_y);
193  /* is it w/in poly? */
194  if (Vect_point_in_poly(cent_x, cent_y, Points) == 1) {
195  *X = cent_x;
196  *Y = cent_y;
197  return 0;
198  }
199 
200  /* guess we have to do it the hard way... */
201  /* get min and max x values */
202  x_max = x_min = Points->x[0];
203  for (i = 0; i < Points->n_points; i++) {
204  if (x_min > Points->x[i])
205  x_min = Points->x[i];
206  if (x_max < Points->x[i])
207  x_max = Points->x[i];
208  }
209 
210 
211  /* init the linked list */
212  if (first_time) {
213  /* will never call link_cleanup () */
214  link_exit_on_error(1); /* kill program if out of memory */
215  Token = (struct link_head *)link_init(sizeof(struct Slink));
216  first_time = 0;
217  }
218 
219  Head = (struct Slink *)link_new(Token);
220  tmp = (struct Slink *)link_new(Token);
221 
222  Head->next = tmp;
223  tmp->next = NULL;
224 
225  Head->x = x_min;
226  tmp->x = x_max;
227 
228  *Y = cent_y; /* pick line segment (x_min, cent_y) - (x_max, cent_y) */
229  ret = Vect__divide_and_conquer(Head, Points, Token, X, Y, 10);
230 
231  destroy_links(Token, Head);
232 
233  if (ret < 0) {
234  G_warning("Vect_get_point_in_poly(): %s",
235  _("Unable to find point in polygon"));
236  return -1;
237  }
238 
239  G_debug(3, "Found point in %d iterations", 10 - ret);
240 
241  return 0;
242 }
243 
244 
245 /*
246  \brief Provide a breadth first binary division of real space along line segment.
247 
248  Looking for a point w/in the polygon.
249 
250  This routine walks along the list of points on line segment
251  and divides each pair in half. It sticks that new point right into
252  the list, and then checks to see if it is inside the poly.
253 
254  After going through the whole list, it calls itself. The list
255  now has a whole extra set of points to divide again.
256 
257  \param Head
258  \param Points
259  \param Token
260  \param X,Y
261  \param levels
262 
263  \return # levels it took
264  \return -1 if exceeded # of levels
265  */
266 static int
267 Vect__divide_and_conquer(struct Slink *Head,
268  struct line_pnts *Points,
269  struct link_head *Token,
270  double *X, double *Y, int levels)
271 {
272  struct Slink *A, *B, *C;
273 
274  G_debug(3, "Vect__divide_and_conquer(): LEVEL %d", levels);
275  A = Head;
276  B = Head->next;
277 
278  do {
279  C = (struct Slink *)link_new(Token);
280  A->next = C;
281  C->next = B;
282 
283  C->x = (A->x + B->x) / 2.;
284 
285  if (Vect_point_in_poly(C->x, *Y, Points) == 1) {
286  *X = C->x;
287  return levels;
288  }
289 
290  A = B;
291  B = B->next;
292  }
293  while (B != NULL);
294 
295  /*
296  ** If it got through the entire loop and still no hits,
297  ** then lets go a level deeper and divide again.
298  */
299 
300  if (levels <= 0)
301  return -1;
302 
303  return Vect__divide_and_conquer(Head, Points, Token, X, Y, --levels);
304 }
305 
306 static void destroy_links(struct link_head *Token, struct Slink *Head)
307 {
308  struct Slink *p, *tmp;
309 
310  p = Head;
311 
312  while (p != NULL) {
313  tmp = p->next;
314  link_dispose(Token, (VOID_T *) p);
315  p = tmp;
316  }
317 }
318 
319 
329 int
330 Vect_find_poly_centroid(struct line_pnts *points,
331  double *cent_x, double *cent_y)
332 {
333  int i;
334  double *xptr1, *yptr1;
335  double *xptr2, *yptr2;
336  double cent_weight_x, cent_weight_y;
337  double len, tot_len;
338 
339  tot_len = 0.0;
340  cent_weight_x = 0.0;
341  cent_weight_y = 0.0;
342 
343  xptr1 = points->x;
344  yptr1 = points->y;
345  xptr2 = points->x + 1;
346  yptr2 = points->y + 1;
347 
348  for (i = 1; i < points->n_points; i++) {
349  len = hypot(*xptr1 - *xptr2, *yptr1 - *yptr2);
350  cent_weight_x += len * ((*xptr1 + *xptr2) / 2.);
351  cent_weight_y += len * ((*yptr1 + *yptr2) / 2.);
352  tot_len += len;
353  xptr1++;
354  xptr2++;
355  yptr1++;
356  yptr2++;
357  }
358 
359  if (tot_len == 0.0)
360  return -1;
361 
362  *cent_x = cent_weight_x / tot_len;
363  *cent_y = cent_weight_y / tot_len;
364 
365  return 0;
366 }
367 
368 
369 /*
370  ** returns true if point is in any of islands /w in area
371  ** returns 0 if not
372  ** returns -1 on error
373  */
374 /*
375  int
376  Vect_point_in_islands (
377  struct Map_info *Map,
378  int area,
379  double cent_x, double cent_y)
380  {
381  P_AREA *Area;
382  static struct line_pnts *TPoints;
383  static int first_time = 1;
384  int isle;
385 
386  if (first_time == 1)
387  {
388  TPoints = Vect_new_line_struct ();
389  first_time = 0;
390  }
391 
392  Area = &(Map->plus.Area[area]);
393 
394  for (isle = 0; isle < Area->n_isles; isle++)
395  {
396  if (0 > Vect_get_isle_points (Map, Area->isles[isle], TPoints))
397  return -1;
398 
399  if ( Vect_point_in_poly (cent_x, cent_y, TPoints) == 1 )
400  return 1;
401  }
402 
403  return 0;
404  }
405  */
406 
423 int
424 Vect_get_point_in_poly_isl(struct line_pnts *Points,
425  struct line_pnts **IPoints, int n_isles,
426  double *att_x, double *att_y)
427 {
428  static struct line_pnts *Intersects;
429  static int first_time = 1;
430  double cent_x, cent_y;
431  register int i, j;
432  double max, hi_y, lo_y;
433  int maxpos;
434  int point_in_sles = 0;
435  double diff;
436 
437  G_debug(3, "Vect_get_point_in_poly_isl(): n_isles = %d", n_isles);
438 
439  if (first_time) {
440  Intersects = Vect_new_line_struct();
441  first_time = 0;
442  }
443 
444  if (Points->n_points < 3) { /* test */
445  if (Points->n_points > 0) {
446  *att_x = Points->x[0];
447  *att_y = Points->y[0];
448  return 0;
449  }
450  return -1;
451  }
452 
453  /* get centroid */
454  Vect_find_poly_centroid(Points, &cent_x, &cent_y);
455  /* is it w/in poly? */
456  if (Vect_point_in_poly(cent_x, cent_y, Points) == 1)
457  /* if the point is iside the polygon */
458  {
459  for (i = 0; i < n_isles; i++) {
460  if (Vect_point_in_poly(cent_x, cent_y, IPoints[i]) >= 1) {
461  point_in_sles = 1;
462  break;
463  }
464  }
465  if (!point_in_sles) {
466  *att_x = cent_x;
467  *att_y = cent_y;
468  return 0;
469  }
470  }
471  /* guess we have to do it the hard way... */
472 
473  /* first find att_y close to cent_y so that no points lie on the line */
474  /* find the point closest to line from below, and point close to line
475  from above and take average of their y-coordinates */
476 
477  /* first initializing lo_y,hi_y to be any 2 pnts on either side of cent_y */
478  hi_y = cent_y - 1;
479  lo_y = cent_y + 1;
480  for (i = 0; i < Points->n_points; i++) {
481  if ((lo_y < cent_y) && (hi_y >= cent_y))
482  break; /* already initialized */
483  if (Points->y[i] < cent_y)
484  lo_y = Points->y[i];
485  if (Points->y[i] >= cent_y)
486  hi_y = Points->y[i];
487  }
488  /* first going throught boundary points */
489  for (i = 0; i < Points->n_points; i++) {
490  if ((Points->y[i] < cent_y) &&
491  ((cent_y - Points->y[i]) < (cent_y - lo_y)))
492  lo_y = Points->y[i];
493  if ((Points->y[i] >= cent_y) &&
494  ((Points->y[i] - cent_y) < (hi_y - cent_y)))
495  hi_y = Points->y[i];
496  }
497  for (i = 0; i < n_isles; i++)
498  for (j = 0; j < IPoints[i]->n_points; j++) {
499  if ((IPoints[i]->y[j] < cent_y) &&
500  ((cent_y - IPoints[i]->y[j]) < (cent_y - lo_y)))
501  lo_y = IPoints[i]->y[j];
502 
503  if ((IPoints[i]->y[j] >= cent_y) &&
504  ((IPoints[i]->y[j] - cent_y) < (hi_y - cent_y)))
505  hi_y = IPoints[i]->y[j];
506  }
507 
508  if (lo_y == hi_y)
509  return (-1); /* area is empty */
510  else
511  *att_y = (hi_y + lo_y) / 2.0;
512 
513  Intersects->n_points = 0;
514  Vect__intersect_line_with_poly(Points, *att_y, Intersects);
515 
516  /* add in intersections w/ holes */
517  for (i = 0; i < n_isles; i++) {
518  if (0 >
519  Vect__intersect_line_with_poly(IPoints[i], *att_y, Intersects))
520  return -1;
521  }
522 
523  if (Intersects->n_points < 2) /* test */
524  return -1;
525 
526  qsort(Intersects->x, (size_t) Intersects->n_points, sizeof(double),
527  (void *)comp_double);
528 
529  max = 0;
530  maxpos = 0;
531 
532  /* find area of MAX distance */
533  for (i = 0; i < Intersects->n_points; i += 2) {
534  diff = Intersects->x[i + 1] - Intersects->x[i];
535 
536  if (diff > max) {
537  max = diff;
538  maxpos = i;
539  }
540  }
541  if (max == 0.0) /* area was empty: example ((x1,y1), (x2,y2), (x1,y1)) */
542  return -1;
543 
544  *att_x = (Intersects->x[maxpos] + Intersects->x[maxpos + 1]) / 2.;
545 
546  return 0;
547 }
548 
549 
550 /* Intersect segments of Points with ray from point X,Y to the right.
551  * Returns: -1 point exactly on segment
552  * number of intersections
553  */
554 static int segments_x_ray(double X, double Y, struct line_pnts *Points)
555 {
556  double x1, x2, y1, y2;
557  double x_inter;
558  int n_intersects;
559  int n;
560 
561  G_debug(3, "segments_x_ray(): x = %f y = %f n_points = %d", X, Y,
562  Points->n_points);
563 
564  /* Follow the ray from X,Y along positive x and find number of intersections.
565  * Coordinates exactly on ray are considered to be slightly above. */
566 
567  n_intersects = 0;
568  for (n = 0; n < Points->n_points - 1; n++) {
569  x1 = Points->x[n];
570  y1 = Points->y[n];
571  x2 = Points->x[n + 1];
572  y2 = Points->y[n + 1];
573 
574  G_debug(3, "X = %f Y = %f x1 = %f y1 = %f x2 = %f y2 = %f", X, Y, x1,
575  y1, x2, y2);
576 
577  /* I know, it should be possible to do that with less conditions, but it should be
578  * enough readable also! */
579 
580  /* segment left from X -> no intersection */
581  if (x1 < X && x2 < X)
582  continue;
583 
584  /* point on vertex */
585  if ((x1 == X && y1 == Y) || (x2 == X && y2 == Y))
586  return -1;
587 
588  /* on vertical boundary */
589  if ((x1 == x2 && x1 == X) &&
590  ((y1 <= Y && y2 >= Y) || (y1 >= Y && y2 <= Y)))
591  return -1;
592 
593  /* on horizontal boundary */
594  if ((y1 == y2 && y1 == Y) &&
595  ((x1 <= X && x2 >= X) || (x1 >= X && x2 <= X)))
596  return -1;
597 
598  /* segment on ray (X is not important) */
599  if (y1 == Y && y2 == Y)
600  continue;
601 
602  /* segment above (X is not important) */
603  if (y1 > Y && y2 > Y)
604  continue;
605 
606  /* segment below (X is not important) */
607  if (y1 < Y && y2 < Y)
608  continue;
609 
610  /* one end on Y second above (X is not important) */
611  if ((y1 == Y && y2 > Y) || (y2 == Y && y1 > Y))
612  continue;
613 
614  /* For following cases we know that at least one of x1 and x2 is >= X */
615 
616  /* one end of segment on Y second below Y */
617  if (y1 == Y && y2 < Y) {
618  if (x1 >= X) /* x of the end on the ray is >= X */
619  n_intersects++;
620  continue;
621  }
622  if (y2 == Y && y1 < Y) {
623  if (x2 >= X)
624  n_intersects++;
625  continue;
626  }
627 
628  /* one end of segment above Y second below Y */
629  if ((y1 < Y && y2 > Y) || (y1 > Y && y2 < Y)) {
630  if (x1 >= X && x2 >= X) {
631  n_intersects++;
632  continue;
633  }
634 
635  /* now either x1 < X && x2 > X or x1 > X && x2 < X -> calculate intersection */
636  x_inter = dig_x_intersect(x1, x2, y1, y2, Y);
637  G_debug(3, "x_inter = %f", x_inter);
638  if (x_inter == X)
639  return -1; /* point on segment, do not assume inside/outside */
640  else if (x_inter > X)
641  n_intersects++;
642 
643  continue; /* would not be necessary, just to check, see below */
644  }
645  /* should not be reached (one condition is not necessary, but it is may be better readable
646  * and it is a check) */
647  G_warning
648  ("segments_x_ray() %s: X = %f Y = %f x1 = %f y1 = %f x2 = %f y2 = %f",
649  _("conditions failed"), X, Y, x1, y1, x2, y2);
650  }
651 
652  return n_intersects;
653 }
654 
665 int Vect_point_in_poly(double X, double Y, struct line_pnts *Points)
666 {
667  int n_intersects;
668 
669  G_debug(3, "Vect_point_in_poly(): x = %f y = %f n_points = %d", X, Y,
670  Points->n_points);
671 
672  n_intersects = segments_x_ray(X, Y, Points);
673 
674  if (n_intersects == -1)
675  return 2;
676 
677  if (n_intersects % 2)
678  return 1;
679  else
680  return 0;
681 }
682 
694 int
695 Vect_point_in_area_outer_ring(double X, double Y, struct Map_info *Map,
696  int area)
697 {
698  static int first = 1;
699  int n_intersects, inter;
700  int i, line;
701  static struct line_pnts *Points;
702  struct Plus_head *Plus;
703  P_LINE *Line;
704  P_AREA *Area;
705 
706  G_debug(3, "Vect_point_in_area_outer_ring(): x = %f y = %f area = %d", X,
707  Y, area);
708 
709  if (first == 1) {
710  Points = Vect_new_line_struct();
711  first = 0;
712  }
713 
714  Plus = &(Map->plus);
715  Area = Plus->Area[area];
716 
717  /* First it must be in box */
718  if (X < Area->W || X > Area->E || Y > Area->N || Y < Area->S)
719  return 0;
720 
721  n_intersects = 0;
722  for (i = 0; i < Area->n_lines; i++) {
723  line = abs(Area->lines[i]);
724  G_debug(3, " line[%d] = %d", i, line);
725 
726  Line = Plus->Line[line];
727 
728  /* dont check lines that obviously do not intersect with test ray */
729  if ((Line->N < Y) || (Line->S > Y) || (Line->E < X))
730  continue;
731 
732  Vect_read_line(Map, Points, NULL, line);
733 
734  inter = segments_x_ray(X, Y, Points);
735  G_debug(3, " inter = %d", inter);
736 
737  if (inter == -1)
738  return 2;
739  n_intersects += inter;
740  G_debug(3, " n_intersects = %d", n_intersects);
741  }
742 
743  if (n_intersects % 2)
744  return 1;
745  else
746  return 0;
747 }
748 
760 int Vect_point_in_island(double X, double Y, struct Map_info *Map, int isle)
761 {
762  static int first = 1;
763  int n_intersects, inter;
764  int i, line;
765  static struct line_pnts *Points;
766  struct Plus_head *Plus;
767  P_LINE *Line;
768  P_ISLE *Isle;
769 
770  G_debug(3, "Vect_point_in_island(): x = %f y = %f isle = %d", X, Y, isle);
771 
772  if (first == 1) {
773  Points = Vect_new_line_struct();
774  first = 0;
775  }
776 
777  Plus = &(Map->plus);
778  Isle = Plus->Isle[isle];
779 
780  if (X < Isle->W || X > Isle->E || Y > Isle->N || Y < Isle->S)
781  return 0;
782 
783  n_intersects = 0;
784  for (i = 0; i < Isle->n_lines; i++) {
785  line = abs(Isle->lines[i]);
786 
787  Line = Plus->Line[line];
788 
789  /* dont check lines that obviously do not intersect with test ray */
790  if ((Line->N < Y) || (Line->S > Y) || (Line->E < X))
791  continue;
792 
793  Vect_read_line(Map, Points, NULL, line);
794 
795  inter = segments_x_ray(X, Y, Points);
796  if (inter == -1)
797  return 2;
798  n_intersects += inter;
799  }
800 
801  if (n_intersects % 2)
802  return 1;
803  else
804  return 0;
805 }
#define VOID_T
Definition: qtree.h:17
double dig_x_intersect(double beg_x, double end_x, double beg_y, double end_y, double Y)
Definition: inside.c:22
float b
Definition: named_colr.c:8
int Vect_get_isle_points(struct Map_info *Map, int isle, struct line_pnts *BPoints)
Returns the polygon array of points in BPoints.
struct link_head * link_init(int size)
Definition: linkm/init.c:40
struct line_pnts * Vect_new_line_struct()
Creates and initializes a struct line_pnts.
Definition: line.c:57
struct link_head * link_new(struct link_head *Head)
Definition: new.c:12
int Vect_point_in_island(double X, double Y, struct Map_info *Map, int isle)
Determines if a point (X,Y) is inside an island.
Definition: Vlib/poly.c:760
#define Y(x)
Definition: display/draw.c:246
#define X(y)
Definition: display/draw.c:248
int Vect__intersect_line_with_poly()
int Vect_get_area_num_isles(struct Map_info *Map, int area)
Returns number of isles for area.
int Vect_get_area_isle(struct Map_info *Map, int area, int isle)
Returns isle for area.
int y
Definition: plot.c:34
#define max(x, y)
Definition: draw2.c:69
int Vect_append_point(struct line_pnts *Points, double x, double y, double z)
Appends one point to the end of a line.
Definition: line.c:168
int Vect_get_area_points(struct Map_info *Map, int area, struct line_pnts *BPoints)
Returns the polygon array of points in BPoints.
#define C
Definition: intr_char.c:17
int Vect_point_in_area_outer_ring(double X, double Y, struct Map_info *Map, int area)
Determines if a point (X,Y) is inside an area outer ring. Islands are not considered.
Definition: Vlib/poly.c:695
int Vect_get_point_in_poly_isl(struct line_pnts *Points, 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:424
int Vect_find_poly_centroid(struct line_pnts *points, double *cent_x, double *cent_y)
Get centroid of polygon.
Definition: Vlib/poly.c:330
void link_exit_on_error(int flag)
Definition: linkm/init.c:35
int first
Definition: form/open.c:25
void link_dispose(struct link_head *Head, VOID_T *ptr)
Definition: dispose.c:10
int Vect_point_in_poly(double X, double Y, struct line_pnts *Points)
Determines if a point (X,Y) is inside a polygon.
Definition: Vlib/poly.c:665
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
return NULL
Definition: dbfopen.c:1394
G_warning("category support for [%s] in mapset [%s] %s", name, mapset, type)
tuple Map
Definition: render.py:1310
int G_debug(int level, const char *msg,...)
Print debugging message.
Definition: gis/debug.c:51
int n
Definition: dataquad.c:291
int Vect_read_line(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c, int line)
Read vector feature.
int Vect_get_point_in_poly(struct line_pnts *Points, double *X, double *Y)
Get point inside polygon.
Definition: Vlib/poly.c:180