1 /**********************************************************************
2 *
3 * rttopo - topology library
4 * http://git.osgeo.org/gitea/rttopo/librttopo
5 *
6 * rttopo 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 * rttopo 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 rttopo. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **********************************************************************
20 *
21 * Copyright 2008 Paul Ramsey
22 *
23 **********************************************************************/
24
25
26
27 #include "rttopo_config.h"
28 #include "librttopo_geom_internal.h"
29 #include "rtgeom_log.h"
30 #include <ctype.h> /* for tolower */
31
32
33 /**
34 * Returns -1 if n < 0.0 and 1 if n > 0.0
35 */
signum(const RTCTX * ctx,double n)36 int signum(const RTCTX *ctx, double n)
37 {
38 if( n < 0 ) return -1;
39 if( n > 0 ) return 1;
40 return 0;
41 }
42
43 int
p4d_same(const RTCTX * ctx,const RTPOINT4D * p1,const RTPOINT4D * p2)44 p4d_same(const RTCTX *ctx, const RTPOINT4D *p1, const RTPOINT4D *p2)
45 {
46 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) )
47 return RT_TRUE;
48 else
49 return RT_FALSE;
50 }
51
52 int
p3d_same(const RTCTX * ctx,const POINT3D * p1,const POINT3D * p2)53 p3d_same(const RTCTX *ctx, const POINT3D *p1, const POINT3D *p2)
54 {
55 if( FP_EQUALS(p1->x,p2->x) && FP_EQUALS(p1->y,p2->y) && FP_EQUALS(p1->z,p2->z) )
56 return RT_TRUE;
57 else
58 return RT_FALSE;
59 }
60
61 int
p2d_same(const RTCTX * ctx,const RTPOINT2D * p1,const RTPOINT2D * p2)62 p2d_same(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2)
63 {
64 if( FP_EQUALS(p1->x,p2->x) && FP_EQUALS(p1->y,p2->y) )
65 return RT_TRUE;
66 else
67 return RT_FALSE;
68 }
69
70 /**
71 * rt_segment_side(ctx)
72 *
73 * Return -1 if point Q is left of segment P
74 * Return 1 if point Q is right of segment P
75 * Return 0 if point Q in on segment P
76 */
rt_segment_side(const RTCTX * ctx,const RTPOINT2D * p1,const RTPOINT2D * p2,const RTPOINT2D * q)77 int rt_segment_side(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2, const RTPOINT2D *q)
78 {
79 double side = ( (q->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (q->y - p1->y) );
80 if ( side == 0.0 )
81 return 0;
82 else
83 return signum(ctx, side);
84 }
85
86 /**
87 * Returns the length of a linear segment
88 */
89 double
rt_seg_length(const RTCTX * ctx,const RTPOINT2D * A1,const RTPOINT2D * A2)90 rt_seg_length(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2)
91 {
92 return sqrt((A1->x-A2->x)*(A1->x-A2->x)+(A1->y-A2->y)*(A1->y-A2->y));
93 }
94
95 /**
96 * Returns true if P is on the same side of the plane partition
97 * defined by A1/A3 as A2 is. Only makes sense if P has already been
98 * determined to be on the circle defined by A1/A2/A3.
99 */
100 int
rt_pt_in_arc(const RTCTX * ctx,const RTPOINT2D * P,const RTPOINT2D * A1,const RTPOINT2D * A2,const RTPOINT2D * A3)101 rt_pt_in_arc(const RTCTX *ctx, const RTPOINT2D *P, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3)
102 {
103 return rt_segment_side(ctx, A1, A3, A2) == rt_segment_side(ctx, A1, A3, P);
104 }
105
106 /**
107 * Returns true if P is between A1/A2. Only makes sense if P has already been
108 * deterined to be on the line defined by A1/A2.
109 */
110 int
rt_pt_in_seg(const RTCTX * ctx,const RTPOINT2D * P,const RTPOINT2D * A1,const RTPOINT2D * A2)111 rt_pt_in_seg(const RTCTX *ctx, const RTPOINT2D *P, const RTPOINT2D *A1, const RTPOINT2D *A2)
112 {
113 return ((A1->x <= P->x && P->x < A2->x) || (A1->x >= P->x && P->x > A2->x)) ||
114 ((A1->y <= P->y && P->y < A2->y) || (A1->y >= P->y && P->y > A2->y));
115 }
116
117 /**
118 * Returns true if arc A is actually a point (all vertices are the same) .
119 */
120 int
rt_arc_is_pt(const RTCTX * ctx,const RTPOINT2D * A1,const RTPOINT2D * A2,const RTPOINT2D * A3)121 rt_arc_is_pt(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3)
122 {
123 if ( A1->x == A2->x && A2->x == A3->x &&
124 A1->y == A2->y && A2->y == A3->y )
125 return RT_TRUE;
126 else
127 return RT_FALSE;
128 }
129
130 /**
131 * Returns the length of a circular arc segment
132 */
133 double
rt_arc_length(const RTCTX * ctx,const RTPOINT2D * A1,const RTPOINT2D * A2,const RTPOINT2D * A3)134 rt_arc_length(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3)
135 {
136 RTPOINT2D C;
137 double radius_A, circumference_A;
138 int a2_side, clockwise;
139 double a1, a3;
140 double angle;
141
142 if ( rt_arc_is_pt(ctx, A1, A2, A3) )
143 return 0.0;
144
145 radius_A = rt_arc_center(ctx, 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(ctx, A1, A3) )
158 return circumference_A;
159
160 /* Determine the orientation of the arc */
161 a2_side = rt_segment_side(ctx, 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 = RT_TRUE;
167 else
168 clockwise = RT_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
rt_arc_side(const RTCTX * ctx,const RTPOINT2D * A1,const RTPOINT2D * A2,const RTPOINT2D * A3,const RTPOINT2D * Q)194 int rt_arc_side(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3, const RTPOINT2D *Q)
195 {
196 RTPOINT2D C;
197 double radius_A;
198 double side_Q, side_A2;
199 double d;
200
201 side_Q = rt_segment_side(ctx, A1, A3, Q);
202 radius_A = rt_arc_center(ctx, A1, A2, A3, &C);
203 side_A2 = rt_segment_side(ctx, A1, A3, A2);
204
205 /* Linear case */
206 if ( radius_A < 0 )
207 return side_Q;
208
209 d = distance2d_pt_pt(ctx, 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
235 /**
236 * Determines the center of the circle defined by the three given points.
237 * In the event the circle is complete, the midpoint of the segment defined
238 * by the first and second points is returned. If the points are colinear,
239 * as determined by equal slopes, then NULL is returned. If the interior
240 * point is coincident with either end point, they are taken as colinear.
241 */
242 double
rt_arc_center(const RTCTX * ctx,const RTPOINT2D * p1,const RTPOINT2D * p2,const RTPOINT2D * p3,RTPOINT2D * result)243 rt_arc_center(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2, const RTPOINT2D *p3, RTPOINT2D *result)
244 {
245 RTPOINT2D c;
246 double cx, cy, cr;
247 double dx21, dy21, dx31, dy31, h21, h31, d;
248
249 c.x = c.y = 0.0;
250
251 RTDEBUGF(ctx, 2, "rt_arc_center called (%.16f,%.16f), (%.16f,%.16f), (%.16f,%.16f).", p1->x, p1->y, p2->x, p2->y, p3->x, p3->y);
252
253 /* Closed circle */
254 if (fabs(p1->x - p3->x) < EPSILON_SQLMM &&
255 fabs(p1->y - p3->y) < EPSILON_SQLMM)
256 {
257 cx = p1->x + (p2->x - p1->x) / 2.0;
258 cy = p1->y + (p2->y - p1->y) / 2.0;
259 c.x = cx;
260 c.y = cy;
261 *result = c;
262 cr = sqrt(pow(cx - p1->x, 2.0) + pow(cy - p1->y, 2.0));
263 return cr;
264 }
265
266 /* Using cartesian eguations from page https://en.wikipedia.org/wiki/Circumscribed_circle */
267 dx21 = p2->x - p1->x;
268 dy21 = p2->y - p1->y;
269 dx31 = p3->x - p1->x;
270 dy31 = p3->y - p1->y;
271
272 h21 = pow(dx21, 2.0) + pow(dy21, 2.0);
273 h31 = pow(dx31, 2.0) + pow(dy31, 2.0);
274
275 /* 2 * |Cross product|, d<0 means clockwise and d>0 counterclockwise sweeping angle */
276 d = 2 * (dx21 * dy31 - dx31 * dy21);
277
278 /* Check colinearity, |Cross product| = 0 */
279 if (fabs(d) < EPSILON_SQLMM)
280 return -1.0;
281
282 /* Calculate centroid coordinates and radius */
283 cx = p1->x + (h21 * dy31 - h31 * dy21) / d;
284 cy = p1->y - (h21 * dx31 - h31 * dx21) / d;
285 c.x = cx;
286 c.y = cy;
287 *result = c;
288 cr = sqrt(pow(cx - p1->x, 2) + pow(cy - p1->y, 2));
289
290 RTDEBUGF(ctx, 2, "rt_arc_center center is (%.16f,%.16f)", result->x, result->y);
291
292 return cr;
293 }
294
295 int
pt_in_ring_2d(const RTCTX * ctx,const RTPOINT2D * p,const RTPOINTARRAY * ring)296 pt_in_ring_2d(const RTCTX *ctx, const RTPOINT2D *p, const RTPOINTARRAY *ring)
297 {
298 int cn = 0; /* the crossing number counter */
299 int i;
300 const RTPOINT2D *v1, *v2;
301 const RTPOINT2D *first, *last;
302
303 first = rt_getPoint2d_cp(ctx, ring, 0);
304 last = rt_getPoint2d_cp(ctx, ring, ring->npoints-1);
305 if ( memcmp(first, last, sizeof(RTPOINT2D)) )
306 {
307 rterror(ctx, "pt_in_ring_2d: V[n] != V[0] (%g %g != %g %g)",
308 first->x, first->y, last->x, last->y);
309 return RT_FALSE;
310
311 }
312
313 RTDEBUGF(ctx, 2, "pt_in_ring_2d called with point: %g %g", p->x, p->y);
314 /* printPA(ctx, ring); */
315
316 /* loop through all edges of the polygon */
317 v1 = rt_getPoint2d_cp(ctx, ring, 0);
318 for (i=0; i<ring->npoints-1; i++)
319 {
320 double vt;
321 v2 = rt_getPoint2d_cp(ctx, ring, i+1);
322
323 /* edge from vertex i to vertex i+1 */
324 if
325 (
326 /* an upward crossing */
327 ((v1->y <= p->y) && (v2->y > p->y))
328 /* a downward crossing */
329 || ((v1->y > p->y) && (v2->y <= p->y))
330 )
331 {
332
333 vt = (double)(p->y - v1->y) / (v2->y - v1->y);
334
335 /* P->x <intersect */
336 if (p->x < v1->x + vt * (v2->x - v1->x))
337 {
338 /* a valid crossing of y=p->y right of p->x */
339 ++cn;
340 }
341 }
342 v1 = v2;
343 }
344
345 RTDEBUGF(ctx, 3, "pt_in_ring_2d returning %d", cn&1);
346
347 return (cn&1); /* 0 if even (out), and 1 if odd (in) */
348 }
349
350
351 static int
rt_seg_interact(const RTCTX * ctx,const RTPOINT2D * p1,const RTPOINT2D * p2,const RTPOINT2D * q1,const RTPOINT2D * q2)352 rt_seg_interact(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2, const RTPOINT2D *q1, const RTPOINT2D *q2)
353 {
354 double minq=FP_MIN(q1->x,q2->x);
355 double maxq=FP_MAX(q1->x,q2->x);
356 double minp=FP_MIN(p1->x,p2->x);
357 double maxp=FP_MAX(p1->x,p2->x);
358
359 if (FP_GT(minp,maxq) || FP_LT(maxp,minq))
360 return RT_FALSE;
361
362 minq=FP_MIN(q1->y,q2->y);
363 maxq=FP_MAX(q1->y,q2->y);
364 minp=FP_MIN(p1->y,p2->y);
365 maxp=FP_MAX(p1->y,p2->y);
366
367 if (FP_GT(minp,maxq) || FP_LT(maxp,minq))
368 return RT_FALSE;
369
370 return RT_TRUE;
371 }
372
373 /**
374 ** @brief returns the kind of #RTCG_SEGMENT_INTERSECTION_TYPE behavior of lineseg 1 (constructed from p1 and p2) and lineseg 2 (constructed from q1 and q2)
375 ** @param p1 start point of first straight linesegment
376 ** @param p2 end point of first straight linesegment
377 ** @param q1 start point of second line segment
378 ** @param q2 end point of second line segment
379 ** @return a #RTCG_SEGMENT_INTERSECTION_TYPE
380 ** Returns one of
381 ** SEG_ERROR = -1,
382 ** SEG_NO_INTERSECTION = 0,
383 ** SEG_COLINEAR = 1,
384 ** SEG_CROSS_LEFT = 2,
385 ** SEG_CROSS_RIGHT = 3,
386 */
rt_segment_intersects(const RTCTX * ctx,const RTPOINT2D * p1,const RTPOINT2D * p2,const RTPOINT2D * q1,const RTPOINT2D * q2)387 int rt_segment_intersects(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2, const RTPOINT2D *q1, const RTPOINT2D *q2)
388 {
389
390 int pq1, pq2, qp1, qp2;
391
392 /* No envelope interaction => we are done. */
393 if (!rt_seg_interact(ctx, p1, p2, q1, p2))
394 {
395 return SEG_NO_INTERSECTION;
396 }
397
398 /* Are the start and end points of q on the same side of p? */
399 pq1=rt_segment_side(ctx, p1,p2,q1);
400 pq2=rt_segment_side(ctx, p1,p2,q2);
401 if ((pq1>0 && pq2>0) || (pq1<0 && pq2<0))
402 {
403 return SEG_NO_INTERSECTION;
404 }
405
406 /* Are the start and end points of p on the same side of q? */
407 qp1=rt_segment_side(ctx, q1,q2,p1);
408 qp2=rt_segment_side(ctx, q1,q2,p2);
409 if ( (qp1 > 0.0 && qp2 > 0.0) || (qp1 < 0.0 && qp2 < 0.0) )
410 {
411 return SEG_NO_INTERSECTION;
412 }
413
414 /* Nobody is on one side or another? Must be colinear. */
415 if ( pq1 == 0.0 && pq2 == 0.0 && qp1 == 0.0 && qp2 == 0.0 )
416 {
417 return SEG_COLINEAR;
418 }
419
420 /*
421 ** When one end-point touches, the sidedness is determined by the
422 ** location of the other end-point. Only touches by the first point
423 ** will be considered "real" to avoid double counting.
424 */
425 RTDEBUGF(ctx, 4, "pq1=%.15g pq2=%.15g", pq1, pq2);
426 RTDEBUGF(ctx, 4, "qp1=%.15g qp2=%.15g", qp1, qp2);
427
428 /* Second point of p or q touches, it's not a crossing. */
429 if ( pq2 == 0 || qp2 == 0 )
430 {
431 return SEG_NO_INTERSECTION;
432 }
433
434 /* First point of p touches, it's a "crossing". */
435 if ( pq1 == 0 )
436 {
437 if ( pq2 > 0 )
438 return SEG_CROSS_RIGHT;
439 else
440 return SEG_CROSS_LEFT;
441 }
442
443 /* First point of q touches, it's a crossing. */
444 if ( qp1 == 0 )
445 {
446 if ( pq1 < pq2 )
447 return SEG_CROSS_RIGHT;
448 else
449 return SEG_CROSS_LEFT;
450 }
451
452 /* The segments cross, what direction is the crossing? */
453 if ( pq1 < pq2 )
454 return SEG_CROSS_RIGHT;
455 else
456 return SEG_CROSS_LEFT;
457
458 /* This should never happen! */
459 return SEG_ERROR;
460 }
461
462 /**
463 ** @brief rtline_crossing_direction: returns the kind of #RTCG_LINE_CROSS_TYPE behavior of 2 linestrings
464 ** @param l1 first line string
465 ** @param l2 second line string
466 ** @return a #RTCG_LINE_CROSS_TYPE
467 ** LINE_NO_CROSS = 0
468 ** LINE_CROSS_LEFT = -1
469 ** LINE_CROSS_RIGHT = 1
470 ** LINE_MULTICROSS_END_LEFT = -2
471 ** LINE_MULTICROSS_END_RIGHT = 2
472 ** LINE_MULTICROSS_END_SAME_FIRST_LEFT = -3
473 ** LINE_MULTICROSS_END_SAME_FIRST_RIGHT = 3
474 **
475 */
rtline_crossing_direction(const RTCTX * ctx,const RTLINE * l1,const RTLINE * l2)476 int rtline_crossing_direction(const RTCTX *ctx, const RTLINE *l1, const RTLINE *l2)
477 {
478 int i = 0, j = 0;
479 const RTPOINT2D *p1, *p2, *q1, *q2;
480 RTPOINTARRAY *pa1 = NULL, *pa2 = NULL;
481 int cross_left = 0;
482 int cross_right = 0;
483 int first_cross = 0;
484 int this_cross = 0;
485
486 pa1 = (RTPOINTARRAY*)l1->points;
487 pa2 = (RTPOINTARRAY*)l2->points;
488
489 /* One-point lines can't intersect (and shouldn't exist). */
490 if ( pa1->npoints < 2 || pa2->npoints < 2 )
491 return LINE_NO_CROSS;
492
493 RTDEBUGF(ctx, 4, "l1 = %s", rtgeom_to_ewkt(ctx, (RTGEOM*)l1));
494 RTDEBUGF(ctx, 4, "l2 = %s", rtgeom_to_ewkt(ctx, (RTGEOM*)l2));
495
496 /* Initialize first point of q */
497 q1 = rt_getPoint2d_cp(ctx, pa2, 0);
498
499 for ( i = 1; i < pa2->npoints; i++ )
500 {
501
502 /* Update second point of q to next value */
503 q2 = rt_getPoint2d_cp(ctx, pa2, i);
504
505 /* Initialize first point of p */
506 p1 = rt_getPoint2d_cp(ctx, pa1, 0);
507
508 for ( j = 1; j < pa1->npoints; j++ )
509 {
510
511 /* Update second point of p to next value */
512 p2 = rt_getPoint2d_cp(ctx, pa1, j);
513
514 this_cross = rt_segment_intersects(ctx, p1, p2, q1, q2);
515
516 RTDEBUGF(ctx, 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 RTDEBUG(ctx, 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 RTDEBUG(ctx, 4,"this_cross == SEG_CROSS_RIGHT");
529 cross_right++;
530 if ( ! first_cross )
531 first_cross = SEG_CROSS_LEFT;
532 }
533
534 /*
535 ** Crossing at a co-linearity can be turned handled by extending
536 ** segment to next vertext and seeing if the end points straddle
537 ** the co-linear segment.
538 */
539 if ( this_cross == SEG_COLINEAR )
540 {
541 RTDEBUG(ctx, 4,"this_cross == SEG_COLINEAR");
542 /* TODO: Add logic here and in segment_intersects()
543 continue;
544 */
545 }
546
547 RTDEBUG(ctx, 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 RTDEBUGF(ctx, 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 )
571 return LINE_MULTICROSS_END_LEFT;
572
573 if ( cross_left - cross_right == -1 )
574 return LINE_MULTICROSS_END_RIGHT;
575
576 if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_LEFT )
577 return LINE_MULTICROSS_END_SAME_FIRST_LEFT;
578
579 if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_RIGHT )
580 return LINE_MULTICROSS_END_SAME_FIRST_RIGHT;
581
582 return LINE_NO_CROSS;
583
584 }
585
586
587
588
589
590 static 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 */
geohash_point(const RTCTX * ctx,double longitude,double latitude,int precision)597 char * geohash_point(const RTCTX *ctx, 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 = rtalloc(ctx, 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 */
geohash_point_as_int(const RTCTX * ctx,RTPOINT2D * pt)663 unsigned int geohash_point_as_int(const RTCTX *ctx, RTPOINT2D *pt)
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 |= 0x0001 << 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 */
decode_geohash_bbox(const RTCTX * ctx,char * geohash,double * lat,double * lon,int precision)719 void decode_geohash_bbox(const RTCTX *ctx, char *geohash, double *lat, double *lon, int precision)
720 {
721 int i, j, hashlen;
722 char c, cd, mask, is_even = 1;
723 static char bits[] = {16, 8, 4, 2, 1};
724
725 lat[0] = -90.0;
726 lat[1] = 90.0;
727 lon[0] = -180.0;
728 lon[1] = 180.0;
729
730 hashlen = strlen(geohash);
731
732 if (precision < 0 || precision > hashlen)
733 {
734 precision = hashlen;
735 }
736
737 for (i = 0; i < precision; i++)
738 {
739 c = tolower(geohash[i]);
740 cd = strchr(base32, c) - base32;
741
742 for (j = 0; j < 5; j++)
743 {
744 mask = bits[j];
745 if (is_even)
746 {
747 lon[!(cd & mask)] = (lon[0] + lon[1]) / 2;
748 }
749 else
750 {
751 lat[!(cd & mask)] = (lat[0] + lat[1]) / 2;
752 }
753 is_even = !is_even;
754 }
755 }
756 }
757
rtgeom_geohash_precision(const RTCTX * ctx,RTGBOX bbox,RTGBOX * bounds)758 int rtgeom_geohash_precision(const RTCTX *ctx, RTGBOX bbox, RTGBOX *bounds)
759 {
760 double minx, miny, maxx, maxy;
761 double latmax, latmin, lonmax, lonmin;
762 double lonwidth, latwidth;
763 double latmaxadjust, lonmaxadjust, latminadjust, lonminadjust;
764 int precision = 0;
765
766 /* Get the bounding box, return error if things don't work out. */
767 minx = bbox.xmin;
768 miny = bbox.ymin;
769 maxx = bbox.xmax;
770 maxy = bbox.ymax;
771
772 if ( minx == maxx && miny == maxy )
773 {
774 /* It's a point. Doubles have 51 bits of precision.
775 ** 2 * 51 / 5 == 20 */
776 return 20;
777 }
778
779 lonmin = -180.0;
780 latmin = -90.0;
781 lonmax = 180.0;
782 latmax = 90.0;
783
784 /* Shrink a world bounding box until one of the edges interferes with the
785 ** bounds of our rectangle. */
786 while ( 1 )
787 {
788 lonwidth = lonmax - lonmin;
789 latwidth = latmax - latmin;
790 latmaxadjust = lonmaxadjust = latminadjust = lonminadjust = 0.0;
791
792 if ( minx > lonmin + lonwidth / 2.0 )
793 {
794 lonminadjust = lonwidth / 2.0;
795 }
796 else if ( maxx < lonmax - lonwidth / 2.0 )
797 {
798 lonmaxadjust = -1 * lonwidth / 2.0;
799 }
800 if ( miny > latmin + latwidth / 2.0 )
801 {
802 latminadjust = latwidth / 2.0;
803 }
804 else if (maxy < latmax - latwidth / 2.0 )
805 {
806 latmaxadjust = -1 * latwidth / 2.0;
807 }
808 /* Only adjust if adjustments are legal (we haven't crossed any edges). */
809 if ( (lonminadjust || lonmaxadjust) && (latminadjust || latmaxadjust ) )
810 {
811 latmin += latminadjust;
812 lonmin += lonminadjust;
813 latmax += latmaxadjust;
814 lonmax += lonmaxadjust;
815 /* Each adjustment cycle corresponds to 2 bits of storage in the
816 ** geohash. */
817 precision += 2;
818 }
819 else
820 {
821 break;
822 }
823 }
824
825 /* Save the edges of our bounds, in case someone cares later. */
826 bounds->xmin = lonmin;
827 bounds->xmax = lonmax;
828 bounds->ymin = latmin;
829 bounds->ymax = latmax;
830
831 /* Each geohash character (base32) can contain 5 bits of information.
832 ** We are returning the precision in characters, so here we divide. */
833 return precision / 5;
834 }
835
836
837 /*
838 ** Return a geohash string for the geometry. <http://geohash.org>
839 ** Where the precision is non-positive, calculate a precision based on the
840 ** bounds of the feature. Big features have loose precision.
841 ** Small features have tight precision.
842 */
rtgeom_geohash(const RTCTX * ctx,const RTGEOM * rtgeom,int precision)843 char * rtgeom_geohash(const RTCTX *ctx, const RTGEOM *rtgeom, int precision)
844 {
845 RTGBOX gbox;
846 RTGBOX gbox_bounds;
847 double lat, lon;
848 int result;
849
850 gbox_init(ctx, &gbox);
851 gbox_init(ctx, &gbox_bounds);
852
853 result = rtgeom_calculate_gbox_cartesian(ctx, rtgeom, &gbox);
854 if ( result == RT_FAILURE ) return NULL;
855
856 /* Return error if we are being fed something outside our working bounds */
857 if ( gbox.xmin < -180 || gbox.ymin < -90 || gbox.xmax > 180 || gbox.ymax > 90 )
858 {
859 rterror(ctx, "Geohash requires inputs in decimal degrees, got (%g %g, %g %g).",
860 gbox.xmin, gbox.ymin,
861 gbox.xmax, gbox.ymax);
862 return NULL;
863 }
864
865 /* What is the center of our geometry bounds? We'll use that to
866 ** approximate location. */
867 lon = gbox.xmin + (gbox.xmax - gbox.xmin) / 2;
868 lat = gbox.ymin + (gbox.ymax - gbox.ymin) / 2;
869
870 if ( precision <= 0 )
871 {
872 precision = rtgeom_geohash_precision(ctx, gbox, &gbox_bounds);
873 }
874
875 /*
876 ** Return the geohash of the center, with a precision determined by the
877 ** extent of the bounds.
878 ** Possible change: return the point at the center of the precision bounds?
879 */
880 return geohash_point(ctx, lon, lat, precision);
881 }
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905