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