PostGIS  3.2.2dev-r@@SVN_REVISION@@
lwalgorithm.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * PostGIS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * PostGIS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18  *
19  **********************************************************************
20  *
21  * Copyright 2008 Paul Ramsey
22  *
23  **********************************************************************/
24 
25 
26 #include "liblwgeom_internal.h"
27 #include "lwgeom_log.h"
28 #include <ctype.h> /* for tolower */
29 #include <stdbool.h>
30 
31 int
32 p4d_same(const POINT4D *p1, const POINT4D *p2)
33 {
34  if( FP_EQUALS(p1->x,p2->x) && FP_EQUALS(p1->y,p2->y) && FP_EQUALS(p1->z,p2->z) && FP_EQUALS(p1->m,p2->m) )
35  return LW_TRUE;
36  else
37  return LW_FALSE;
38 }
39 
40 int
41 p3d_same(const POINT3D *p1, const POINT3D *p2)
42 {
43  if( FP_EQUALS(p1->x,p2->x) && FP_EQUALS(p1->y,p2->y) && FP_EQUALS(p1->z,p2->z) )
44  return LW_TRUE;
45  else
46  return LW_FALSE;
47 }
48 
49 int
50 p2d_same(const POINT2D *p1, const POINT2D *p2)
51 {
52  return FP_EQUALS(p1->x, p2->x)
53  && FP_EQUALS(p1->y, p2->y);
54 }
55 
63 int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
64 {
65  double side = ( (q->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (q->y - p1->y) );
66  return SIGNUM(side);
67 }
68 
72 double
73 lw_seg_length(const POINT2D *A1, const POINT2D *A2)
74 {
75  return sqrt((A1->x-A2->x)*(A1->x-A2->x)+(A1->y-A2->y)*(A1->y-A2->y));
76 }
77 
83 int
84 lw_pt_in_arc(const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
85 {
86  int pt_side = lw_segment_side(A1, A3, P);
87  int arc_side = lw_segment_side(A1, A3, A2);
88  return pt_side == 0 || pt_side == arc_side;
89 }
90 
95 int
96 lw_pt_in_seg(const POINT2D *P, const POINT2D *A1, const POINT2D *A2)
97 {
98  return ((A1->x <= P->x && P->x < A2->x) || (A1->x >= P->x && P->x > A2->x)) ||
99  ((A1->y <= P->y && P->y < A2->y) || (A1->y >= P->y && P->y > A2->y));
100 }
101 
102 int
103 lw_pt_on_segment(const POINT2D* A1, const POINT2D* A2, const POINT2D* P)
104 {
105  int side = lw_segment_side(A1, A2, P);
106  if (side != 0) return LW_FALSE;
107  return lw_pt_in_seg(P, A1, A2);
108 }
109 
113 int
114 lw_arc_is_pt(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
115 {
116  if ( A1->x == A2->x && A2->x == A3->x &&
117  A1->y == A2->y && A2->y == A3->y )
118  return LW_TRUE;
119  else
120  return LW_FALSE;
121 }
122 
126 double
127 lw_arc_length(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
128 {
129  POINT2D C;
130  double radius_A, circumference_A;
131  int a2_side, clockwise;
132  double a1, a3;
133  double angle;
134 
135  if ( lw_arc_is_pt(A1, A2, A3) )
136  return 0.0;
137 
138  radius_A = lw_arc_center(A1, A2, A3, &C);
139 
140  /* Co-linear! Return linear distance! */
141  if ( radius_A < 0 )
142  {
143  double dx = A1->x - A3->x;
144  double dy = A1->y - A3->y;
145  return sqrt(dx*dx + dy*dy);
146  }
147 
148  /* Closed circle! Return the circumference! */
149  circumference_A = M_PI * 2 * radius_A;
150  if ( p2d_same(A1, A3) )
151  return circumference_A;
152 
153  /* Determine the orientation of the arc */
154  a2_side = lw_segment_side(A1, A3, A2);
155 
156  /* The side of the A1/A3 line that A2 falls on dictates the sweep
157  direction from A1 to A3. */
158  if ( a2_side == -1 )
159  clockwise = LW_TRUE;
160  else
161  clockwise = LW_FALSE;
162 
163  /* Angles of each point that defines the arc section */
164  a1 = atan2(A1->y - C.y, A1->x - C.x);
165  a3 = atan2(A3->y - C.y, A3->x - C.x);
166 
167  /* What's the sweep from A1 to A3? */
168  if ( clockwise )
169  {
170  if ( a1 > a3 )
171  angle = a1 - a3;
172  else
173  angle = 2*M_PI + a1 - a3;
174  }
175  else
176  {
177  if ( a3 > a1 )
178  angle = a3 - a1;
179  else
180  angle = 2*M_PI + a3 - a1;
181  }
182 
183  /* Length as proportion of circumference */
184  return circumference_A * (angle / (2*M_PI));
185 }
186 
187 int lw_arc_side(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, const POINT2D *Q)
188 {
189  POINT2D C;
190  double radius_A;
191  double side_Q, side_A2;
192  double d;
193 
194  side_Q = lw_segment_side(A1, A3, Q);
195  radius_A = lw_arc_center(A1, A2, A3, &C);
196  side_A2 = lw_segment_side(A1, A3, A2);
197 
198  /* Linear case */
199  if ( radius_A < 0 )
200  return side_Q;
201 
202  d = distance2d_pt_pt(Q, &C);
203 
204  /* Q is on the arc boundary */
205  if ( d == radius_A && side_Q == side_A2 )
206  {
207  return 0;
208  }
209 
210  /* Q on A1-A3 line, so its on opposite side to A2 */
211  if ( side_Q == 0 )
212  {
213  return -1 * side_A2;
214  }
215 
216  /*
217  * Q is inside the arc boundary, so it's not on the side we
218  * might think from examining only the end points
219  */
220  if ( d < radius_A && side_Q == side_A2 )
221  {
222  side_Q *= -1;
223  }
224 
225  return side_Q;
226 }
227 
236 double
237 lw_arc_center(const POINT2D *p1, const POINT2D *p2, const POINT2D *p3, POINT2D *result)
238 {
239  POINT2D c;
240  double cx, cy, cr;
241  double dx21, dy21, dx31, dy31, h21, h31, d;
242 
243  c.x = c.y = 0.0;
244 
245  LWDEBUGF(2, "lw_arc_center called (%.16f,%.16f), (%.16f,%.16f), (%.16f,%.16f).", p1->x, p1->y, p2->x, p2->y, p3->x, p3->y);
246 
247  /* Closed circle */
248  if (fabs(p1->x - p3->x) < EPSILON_SQLMM &&
249  fabs(p1->y - p3->y) < EPSILON_SQLMM)
250  {
251  cx = p1->x + (p2->x - p1->x) / 2.0;
252  cy = p1->y + (p2->y - p1->y) / 2.0;
253  c.x = cx;
254  c.y = cy;
255  *result = c;
256  cr = sqrt(pow(cx - p1->x, 2.0) + pow(cy - p1->y, 2.0));
257  return cr;
258  }
259 
260  /* Using cartesian eguations from page https://en.wikipedia.org/wiki/Circumscribed_circle */
261  dx21 = p2->x - p1->x;
262  dy21 = p2->y - p1->y;
263  dx31 = p3->x - p1->x;
264  dy31 = p3->y - p1->y;
265 
266  h21 = pow(dx21, 2.0) + pow(dy21, 2.0);
267  h31 = pow(dx31, 2.0) + pow(dy31, 2.0);
268 
269  /* 2 * |Cross product|, d<0 means clockwise and d>0 counterclockwise sweeping angle */
270  d = 2 * (dx21 * dy31 - dx31 * dy21);
271 
272  /* Check colinearity, |Cross product| = 0 */
273  if (fabs(d) < EPSILON_SQLMM)
274  return -1.0;
275 
276  /* Calculate centroid coordinates and radius */
277  cx = p1->x + (h21 * dy31 - h31 * dy21) / d;
278  cy = p1->y - (h21 * dx31 - h31 * dx21) / d;
279  c.x = cx;
280  c.y = cy;
281  *result = c;
282  cr = sqrt(pow(cx - p1->x, 2) + pow(cy - p1->y, 2));
283 
284  LWDEBUGF(2, "lw_arc_center center is (%.16f,%.16f)", result->x, result->y);
285 
286  return cr;
287 }
288 
289 int
290 pt_in_ring_2d(const POINT2D *p, const POINTARRAY *ring)
291 {
292  int cn = 0; /* the crossing number counter */
293  uint32_t i;
294  const POINT2D *v1, *v2;
295  const POINT2D *first, *last;
296 
297  first = getPoint2d_cp(ring, 0);
298  last = getPoint2d_cp(ring, ring->npoints-1);
299  if ( memcmp(first, last, sizeof(POINT2D)) )
300  {
301  lwerror("pt_in_ring_2d: V[n] != V[0] (%g %g != %g %g)",
302  first->x, first->y, last->x, last->y);
303  return LW_FALSE;
304 
305  }
306 
307  LWDEBUGF(2, "pt_in_ring_2d called with point: %g %g", p->x, p->y);
308  /* printPA(ring); */
309 
310  /* loop through all edges of the polygon */
311  v1 = getPoint2d_cp(ring, 0);
312  for (i=0; i<ring->npoints-1; i++)
313  {
314  double vt;
315  v2 = getPoint2d_cp(ring, i+1);
316 
317  /* edge from vertex i to vertex i+1 */
318  if
319  (
320  /* an upward crossing */
321  ((v1->y <= p->y) && (v2->y > p->y))
322  /* a downward crossing */
323  || ((v1->y > p->y) && (v2->y <= p->y))
324  )
325  {
326 
327  vt = (double)(p->y - v1->y) / (v2->y - v1->y);
328 
329  /* P->x <intersect */
330  if (p->x < v1->x + vt * (v2->x - v1->x))
331  {
332  /* a valid crossing of y=p->y right of p->x */
333  ++cn;
334  }
335  }
336  v1 = v2;
337  }
338 
339  LWDEBUGF(3, "pt_in_ring_2d returning %d", cn&1);
340 
341  return (cn&1); /* 0 if even (out), and 1 if odd (in) */
342 }
343 
344 
345 static int
346 lw_seg_interact(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2)
347 {
348  double minq=FP_MIN(q1->x,q2->x);
349  double maxq=FP_MAX(q1->x,q2->x);
350  double minp=FP_MIN(p1->x,p2->x);
351  double maxp=FP_MAX(p1->x,p2->x);
352 
353  if (FP_GT(minp,maxq) || FP_LT(maxp,minq))
354  return LW_FALSE;
355 
356  minq=FP_MIN(q1->y,q2->y);
357  maxq=FP_MAX(q1->y,q2->y);
358  minp=FP_MIN(p1->y,p2->y);
359  maxp=FP_MAX(p1->y,p2->y);
360 
361  if (FP_GT(minp,maxq) || FP_LT(maxp,minq))
362  return LW_FALSE;
363 
364  return LW_TRUE;
365 }
366 
381 int lw_segment_intersects(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2)
382 {
383 
384  int pq1, pq2, qp1, qp2;
385 
386  /* No envelope interaction => we are done. */
387  if (!lw_seg_interact(p1, p2, q1, p2))
388  {
389  return SEG_NO_INTERSECTION;
390  }
391 
392  /* Are the start and end points of q on the same side of p? */
393  pq1=lw_segment_side(p1,p2,q1);
394  pq2=lw_segment_side(p1,p2,q2);
395  if ((pq1>0 && pq2>0) || (pq1<0 && pq2<0))
396  {
397  return SEG_NO_INTERSECTION;
398  }
399 
400  /* Are the start and end points of p on the same side of q? */
401  qp1=lw_segment_side(q1,q2,p1);
402  qp2=lw_segment_side(q1,q2,p2);
403  if ( (qp1 > 0.0 && qp2 > 0.0) || (qp1 < 0.0 && qp2 < 0.0) )
404  {
405  return SEG_NO_INTERSECTION;
406  }
407 
408  /* Nobody is on one side or another? Must be colinear. */
409  if ( pq1 == 0.0 && pq2 == 0.0 && qp1 == 0.0 && qp2 == 0.0 )
410  {
411  return SEG_COLINEAR;
412  }
413 
414  /*
415  ** When one end-point touches, the sidedness is determined by the
416  ** location of the other end-point. Only touches by the first point
417  ** will be considered "real" to avoid double counting.
418  */
419  LWDEBUGF(4, "pq1=%.15g pq2=%.15g", pq1, pq2);
420  LWDEBUGF(4, "qp1=%.15g qp2=%.15g", qp1, qp2);
421 
422  /* Second point of p or q touches, it's not a crossing. */
423  if ( pq2 == 0 || qp2 == 0 )
424  {
425  return SEG_NO_INTERSECTION;
426  }
427 
428  /* First point of p touches, it's a "crossing". */
429  if ( pq1 == 0 )
430  {
431  if ( pq2 > 0 )
432  return SEG_CROSS_RIGHT;
433  else
434  return SEG_CROSS_LEFT;
435  }
436 
437  /* First point of q touches, it's a crossing. */
438  if ( qp1 == 0 )
439  {
440  if ( pq1 < pq2 )
441  return SEG_CROSS_RIGHT;
442  else
443  return SEG_CROSS_LEFT;
444  }
445 
446  /* The segments cross, what direction is the crossing? */
447  if ( pq1 < pq2 )
448  return SEG_CROSS_RIGHT;
449  else
450  return SEG_CROSS_LEFT;
451 
452  /* This should never happen! */
453  return SEG_ERROR;
454 }
455 
470 int lwline_crossing_direction(const LWLINE *l1, const LWLINE *l2)
471 {
472  uint32_t i = 0, j = 0;
473  const POINT2D *p1, *p2, *q1, *q2;
474  POINTARRAY *pa1 = NULL, *pa2 = NULL;
475  int cross_left = 0;
476  int cross_right = 0;
477  int first_cross = 0;
478  int this_cross = 0;
479 #if POSTGIS_DEBUG_LEVEL >= 4
480  char *geom_ewkt;
481 #endif
482 
483  pa1 = (POINTARRAY*)l1->points;
484  pa2 = (POINTARRAY*)l2->points;
485 
486  /* One-point lines can't intersect (and shouldn't exist). */
487  if ( pa1->npoints < 2 || pa2->npoints < 2 )
488  return LINE_NO_CROSS;
489 
490  /* Zero length lines don't have a side. */
491  if ( ptarray_length_2d(pa1) == 0 || ptarray_length_2d(pa2) == 0 )
492  return LINE_NO_CROSS;
493 
494 
495 #if POSTGIS_DEBUG_LEVEL >= 4
496  geom_ewkt = lwgeom_to_ewkt((LWGEOM*)l1);
497  LWDEBUGF(4, "l1 = %s", geom_ewkt);
498  lwfree(geom_ewkt);
499  geom_ewkt = lwgeom_to_ewkt((LWGEOM*)l2);
500  LWDEBUGF(4, "l2 = %s", geom_ewkt);
501  lwfree(geom_ewkt);
502 #endif
503 
504  /* Initialize first point of q */
505  q1 = getPoint2d_cp(pa2, 0);
506 
507  for ( i = 1; i < pa2->npoints; i++ )
508  {
509 
510  /* Update second point of q to next value */
511  q2 = getPoint2d_cp(pa2, i);
512 
513  /* Initialize first point of p */
514  p1 = getPoint2d_cp(pa1, 0);
515 
516  for ( j = 1; j < pa1->npoints; j++ )
517  {
518 
519  /* Update second point of p to next value */
520  p2 = getPoint2d_cp(pa1, j);
521 
522  this_cross = lw_segment_intersects(p1, p2, q1, q2);
523 
524  LWDEBUGF(4, "i=%d, j=%d (%.8g %.8g, %.8g %.8g)", this_cross, i, j, p1->x, p1->y, p2->x, p2->y);
525 
526  if ( this_cross == SEG_CROSS_LEFT )
527  {
528  LWDEBUG(4,"this_cross == SEG_CROSS_LEFT");
529  cross_left++;
530  if ( ! first_cross )
531  first_cross = SEG_CROSS_LEFT;
532  }
533 
534  if ( this_cross == SEG_CROSS_RIGHT )
535  {
536  LWDEBUG(4,"this_cross == SEG_CROSS_RIGHT");
537  cross_right++;
538  if ( ! first_cross )
539  first_cross = SEG_CROSS_RIGHT;
540  }
541 
542  /*
543  ** Crossing at a co-linearity can be turned handled by extending
544  ** segment to next vertex and seeing if the end points straddle
545  ** the co-linear segment.
546  */
547  if ( this_cross == SEG_COLINEAR )
548  {
549  LWDEBUG(4,"this_cross == SEG_COLINEAR");
550  /* TODO: Add logic here and in segment_intersects()
551  continue;
552  */
553  }
554 
555  LWDEBUG(4,"this_cross == SEG_NO_INTERSECTION");
556 
557  /* Turn second point of p into first point */
558  p1 = p2;
559 
560  }
561 
562  /* Turn second point of q into first point */
563  q1 = q2;
564 
565  }
566 
567  LWDEBUGF(4, "first_cross=%d, cross_left=%d, cross_right=%d", first_cross, cross_left, cross_right);
568 
569  if ( !cross_left && !cross_right )
570  return LINE_NO_CROSS;
571 
572  if ( !cross_left && cross_right == 1 )
573  return LINE_CROSS_RIGHT;
574 
575  if ( !cross_right && cross_left == 1 )
576  return LINE_CROSS_LEFT;
577 
578  if ( cross_left - cross_right == 1 )
580 
581  if ( cross_left - cross_right == -1 )
583 
584  if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_LEFT )
586 
587  if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_RIGHT )
589 
590  return LINE_NO_CROSS;
591 
592 }
593 
594 
595 
596 
597 
598 static char *base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
599 
600 /*
601 ** Calculate the geohash, iterating downwards and gaining precision.
602 ** From geohash-native.c, (c) 2008 David Troy <dave@roundhousetech.com>
603 ** Released under the MIT License.
604 */
605 lwvarlena_t *
606 geohash_point(double longitude, double latitude, int precision)
607 {
608  int is_even=1, i=0;
609  double lat[2], lon[2], mid;
610  char bits[] = {16,8,4,2,1};
611  int bit=0, ch=0;
614  char *geohash = v->data;
615 
616  lat[0] = -90.0;
617  lat[1] = 90.0;
618  lon[0] = -180.0;
619  lon[1] = 180.0;
620 
621  while (i < precision)
622  {
623  if (is_even)
624  {
625  mid = (lon[0] + lon[1]) / 2;
626  if (longitude >= mid)
627  {
628  ch |= bits[bit];
629  lon[0] = mid;
630  }
631  else
632  {
633  lon[1] = mid;
634  }
635  }
636  else
637  {
638  mid = (lat[0] + lat[1]) / 2;
639  if (latitude >= mid)
640  {
641  ch |= bits[bit];
642  lat[0] = mid;
643  }
644  else
645  {
646  lat[1] = mid;
647  }
648  }
649 
650  is_even = !is_even;
651  if (bit < 4)
652  {
653  bit++;
654  }
655  else
656  {
657  geohash[i++] = base32[ch];
658  bit = 0;
659  ch = 0;
660  }
661  }
662 
663  return v;
664 }
665 
666 
667 /*
668 ** Calculate the geohash, iterating downwards and gaining precision.
669 ** From geohash-native.c, (c) 2008 David Troy <dave@roundhousetech.com>
670 ** Released under the MIT License.
671 */
672 unsigned int geohash_point_as_int(POINT2D *pt)
673 {
674  int is_even=1;
675  double lat[2], lon[2], mid;
676  int bit=32;
677  unsigned int ch = 0;
678 
679  double longitude = pt->x;
680  double latitude = pt->y;
681 
682  lat[0] = -90.0;
683  lat[1] = 90.0;
684  lon[0] = -180.0;
685  lon[1] = 180.0;
686 
687  while (--bit >= 0)
688  {
689  if (is_even)
690  {
691  mid = (lon[0] + lon[1]) / 2;
692  if (longitude > mid)
693  {
694  ch |= 0x0001u << bit;
695  lon[0] = mid;
696  }
697  else
698  {
699  lon[1] = mid;
700  }
701  }
702  else
703  {
704  mid = (lat[0] + lat[1]) / 2;
705  if (latitude > mid)
706  {
707  ch |= 0x0001 << bit;
708  lat[0] = mid;
709  }
710  else
711  {
712  lat[1] = mid;
713  }
714  }
715 
716  is_even = !is_even;
717  }
718  return ch;
719 }
720 
721 /*
722 ** Decode a GeoHash into a bounding box. The lat and lon arguments should
723 ** both be passed as double arrays of length 2 at a minimum where the values
724 ** set in them will be the southwest and northeast coordinates of the bounding
725 ** box accordingly. A precision less than 0 indicates that the entire length
726 ** of the GeoHash should be used.
727 ** It will call `lwerror` if an invalid character is found
728 */
729 void decode_geohash_bbox(char *geohash, double *lat, double *lon, int precision)
730 {
731  bool is_even = 1;
732 
733  lat[0] = -90.0;
734  lat[1] = 90.0;
735  lon[0] = -180.0;
736  lon[1] = 180.0;
737 
738  size_t hashlen = strlen(geohash);
739  if (precision < 0 || (size_t)precision > hashlen)
740  {
741  precision = (int)hashlen;
742  }
743 
744  for (int i = 0; i < precision; i++)
745  {
746  char c = tolower(geohash[i]);
747 
748  /* Valid characters are all digits in base32 */
749  char *base32_pos = strchr(base32, c);
750  if (!base32_pos)
751  {
752  lwerror("%s: Invalid character '%c'", __func__, geohash[i]);
753  return;
754  }
755  char cd = base32_pos - base32;
756 
757  for (size_t j = 0; j < 5; j++)
758  {
759  const char bits[] = {16, 8, 4, 2, 1};
760  char mask = bits[j];
761  if (is_even)
762  {
763  lon[!(cd & mask)] = (lon[0] + lon[1]) / 2;
764  }
765  else
766  {
767  lat[!(cd & mask)] = (lat[0] + lat[1]) / 2;
768  }
769  is_even = !is_even;
770  }
771  }
772 }
773 
775 {
776  double minx, miny, maxx, maxy;
777  double latmax, latmin, lonmax, lonmin;
778  double lonwidth, latwidth;
779  double latmaxadjust, lonmaxadjust, latminadjust, lonminadjust;
780  int precision = 0;
781 
782  /* Get the bounding box, return error if things don't work out. */
783  minx = bbox.xmin;
784  miny = bbox.ymin;
785  maxx = bbox.xmax;
786  maxy = bbox.ymax;
787 
788  if ( minx == maxx && miny == maxy )
789  {
790  /* It's a point. Doubles have 51 bits of precision.
791  ** 2 * 51 / 5 == 20 */
792  return 20;
793  }
794 
795  lonmin = -180.0;
796  latmin = -90.0;
797  lonmax = 180.0;
798  latmax = 90.0;
799 
800  /* Shrink a world bounding box until one of the edges interferes with the
801  ** bounds of our rectangle. */
802  while ( 1 )
803  {
804  lonwidth = lonmax - lonmin;
805  latwidth = latmax - latmin;
806  latmaxadjust = lonmaxadjust = latminadjust = lonminadjust = 0.0;
807 
808  if ( minx > lonmin + lonwidth / 2.0 )
809  {
810  lonminadjust = lonwidth / 2.0;
811  }
812  else if ( maxx < lonmax - lonwidth / 2.0 )
813  {
814  lonmaxadjust = -1 * lonwidth / 2.0;
815  }
816  if ( lonminadjust || lonmaxadjust )
817  {
818  lonmin += lonminadjust;
819  lonmax += lonmaxadjust;
820  /* Each adjustment cycle corresponds to 2 bits of storage in the
821  ** geohash. */
822  precision++;
823  }
824  else
825  {
826  break;
827  }
828 
829  if ( miny > latmin + latwidth / 2.0 )
830  {
831  latminadjust = latwidth / 2.0;
832  }
833  else if (maxy < latmax - latwidth / 2.0 )
834  {
835  latmaxadjust = -1 * latwidth / 2.0;
836  }
837  /* Only adjust if adjustments are legal (we haven't crossed any edges). */
838  if ( latminadjust || latmaxadjust )
839  {
840  latmin += latminadjust;
841  latmax += latmaxadjust;
842  /* Each adjustment cycle corresponds to 2 bits of storage in the
843  ** geohash. */
844  precision++;
845  }
846  else
847  {
848  break;
849  }
850  }
851 
852  /* Save the edges of our bounds, in case someone cares later. */
853  bounds->xmin = lonmin;
854  bounds->xmax = lonmax;
855  bounds->ymin = latmin;
856  bounds->ymax = latmax;
857 
858  /* Each geohash character (base32) can contain 5 bits of information.
859  ** We are returning the precision in characters, so here we divide. */
860  return precision / 5;
861 }
862 
863 
864 /*
865 ** Return a geohash string for the geometry. <http://geohash.org>
866 ** Where the precision is non-positive, calculate a precision based on the
867 ** bounds of the feature. Big features have loose precision.
868 ** Small features have tight precision.
869 */
870 lwvarlena_t *
871 lwgeom_geohash(const LWGEOM *lwgeom, int precision)
872 {
873  GBOX gbox = {0};
874  GBOX gbox_bounds = {0};
875  double lat, lon;
876  int result;
877 
878  gbox_init(&gbox);
879  gbox_init(&gbox_bounds);
880 
881  result = lwgeom_calculate_gbox_cartesian(lwgeom, &gbox);
882  if ( result == LW_FAILURE ) return NULL;
883 
884  /* Return error if we are being fed something outside our working bounds */
885  if ( gbox.xmin < -180 || gbox.ymin < -90 || gbox.xmax > 180 || gbox.ymax > 90 )
886  {
887  lwerror("Geohash requires inputs in decimal degrees, got (%g %g, %g %g).",
888  gbox.xmin, gbox.ymin,
889  gbox.xmax, gbox.ymax);
890  return NULL;
891  }
892 
893  /* What is the center of our geometry bounds? We'll use that to
894  ** approximate location. */
895  lon = gbox.xmin + (gbox.xmax - gbox.xmin) / 2;
896  lat = gbox.ymin + (gbox.ymax - gbox.ymin) / 2;
897 
898  if ( precision <= 0 )
899  {
900  precision = lwgeom_geohash_precision(gbox, &gbox_bounds);
901  }
902 
903  /*
904  ** Return the geohash of the center, with a precision determined by the
905  ** extent of the bounds.
906  ** Possible change: return the point at the center of the precision bounds?
907  */
908  return geohash_point(lon, lat, precision);
909 }
static uint8_t precision
Definition: cu_in_twkb.c:25
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:267
int lwgeom_calculate_gbox_cartesian(const LWGEOM *lwgeom, GBOX *gbox)
Calculate the 2-4D bounding box of a geometry.
Definition: gbox.c:740
void gbox_init(GBOX *gbox)
Zero out all the entries in the GBOX.
Definition: gbox.c:40
#define LW_FALSE
Definition: liblwgeom.h:108
double distance2d_pt_pt(const POINT2D *p1, const POINT2D *p2)
Definition: measures.c:2306
#define LWVARHDRSZ
Definition: liblwgeom.h:325
#define LW_FAILURE
Definition: liblwgeom.h:110
double ptarray_length_2d(const POINTARRAY *pts)
Find the 2d length of the given POINTARRAY (even if it's 3d)
Definition: ptarray.c:2099
char * lwgeom_to_ewkt(const LWGEOM *lwgeom)
Return an alloced string.
Definition: lwgeom.c:548
void lwfree(void *mem)
Definition: lwutil.c:242
@ LINE_MULTICROSS_END_RIGHT
Definition: liblwgeom.h:1669
@ LINE_MULTICROSS_END_SAME_FIRST_LEFT
Definition: liblwgeom.h:1670
@ LINE_MULTICROSS_END_LEFT
Definition: liblwgeom.h:1668
@ LINE_CROSS_LEFT
Definition: liblwgeom.h:1666
@ LINE_MULTICROSS_END_SAME_FIRST_RIGHT
Definition: liblwgeom.h:1671
@ LINE_CROSS_RIGHT
Definition: liblwgeom.h:1667
@ LINE_NO_CROSS
Definition: liblwgeom.h:1665
#define LWSIZE_SET(varsize, len)
Definition: liblwgeom.h:339
void * lwalloc(size_t size)
Definition: lwutil.c:227
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:107
#define EPSILON_SQLMM
Tolerance used to determine equality.
#define FP_LT(A, B)
#define SIGNUM(n)
Macro that returns: -1 if n < 0, 1 if n > 0, 0 if n == 0.
#define FP_MAX(A, B)
#define FP_MIN(A, B)
@ SEG_NO_INTERSECTION
@ SEG_ERROR
@ SEG_COLINEAR
@ SEG_CROSS_RIGHT
@ SEG_CROSS_LEFT
#define FP_EQUALS(A, B)
#define FP_GT(A, B)
int lwgeom_geohash_precision(GBOX bbox, GBOX *bounds)
Definition: lwalgorithm.c:774
int p4d_same(const POINT4D *p1, const POINT4D *p2)
Definition: lwalgorithm.c:32
int p3d_same(const POINT3D *p1, const POINT3D *p2)
Definition: lwalgorithm.c:41
double lw_arc_length(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
Returns the length of a circular arc segment.
Definition: lwalgorithm.c:127
int lw_pt_on_segment(const POINT2D *A1, const POINT2D *A2, const POINT2D *P)
Definition: lwalgorithm.c:103
lwvarlena_t * lwgeom_geohash(const LWGEOM *lwgeom, int precision)
Calculate the GeoHash (http://geohash.org) string for a geometry.
Definition: lwalgorithm.c:871
lwvarlena_t * geohash_point(double longitude, double latitude, int precision)
Definition: lwalgorithm.c:606
unsigned int geohash_point_as_int(POINT2D *pt)
Definition: lwalgorithm.c:672
int lwline_crossing_direction(const LWLINE *l1, const LWLINE *l2)
lwline_crossing_direction: returns the kind of CG_LINE_CROSS_TYPE behavior of 2 linestrings
Definition: lwalgorithm.c:470
int lw_segment_intersects(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2)
returns the kind of CG_SEGMENT_INTERSECTION_TYPE behavior of lineseg 1 (constructed from p1 and p2) a...
Definition: lwalgorithm.c:381
double lw_arc_center(const POINT2D *p1, const POINT2D *p2, const POINT2D *p3, POINT2D *result)
Determines the center of the circle defined by the three given points.
Definition: lwalgorithm.c:237
int pt_in_ring_2d(const POINT2D *p, const POINTARRAY *ring)
Definition: lwalgorithm.c:290
int lw_arc_side(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, const POINT2D *Q)
Definition: lwalgorithm.c:187
double lw_seg_length(const POINT2D *A1, const POINT2D *A2)
Returns the length of a linear segment.
Definition: lwalgorithm.c:73
static int lw_seg_interact(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2)
Definition: lwalgorithm.c:346
int lw_pt_in_seg(const POINT2D *P, const POINT2D *A1, const POINT2D *A2)
Returns true if P is between A1/A2.
Definition: lwalgorithm.c:96
static char * base32
Definition: lwalgorithm.c:598
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
Definition: lwalgorithm.c:63
int lw_arc_is_pt(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
Returns true if arc A is actually a point (all vertices are the same) .
Definition: lwalgorithm.c:114
void decode_geohash_bbox(char *geohash, double *lat, double *lon, int precision)
Definition: lwalgorithm.c:729
int lw_pt_in_arc(const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
Returns true if P is on the same side of the plane partition defined by A1/A3 as A2 is.
Definition: lwalgorithm.c:84
int p2d_same(const POINT2D *p1, const POINT2D *p2)
Definition: lwalgorithm.c:50
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:83
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
static const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition: lwinline.h:101
double ymax
Definition: liblwgeom.h:371
double xmax
Definition: liblwgeom.h:369
double ymin
Definition: liblwgeom.h:370
double xmin
Definition: liblwgeom.h:368
POINTARRAY * points
Definition: liblwgeom.h:497
double y
Definition: liblwgeom.h:404
double x
Definition: liblwgeom.h:404
double z
Definition: liblwgeom.h:416
double x
Definition: liblwgeom.h:416
double y
Definition: liblwgeom.h:416
double m
Definition: liblwgeom.h:428
double x
Definition: liblwgeom.h:428
double z
Definition: liblwgeom.h:428
double y
Definition: liblwgeom.h:428
uint32_t npoints
Definition: liblwgeom.h:441
uint32_t size
Definition: liblwgeom.h:321
char data[]
Definition: liblwgeom.h:322