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