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