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