Coverage Report

Created: 2026-09-01 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/lwalgorithm.c
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * PostGIS - Spatial Types for PostgreSQL
4
 * http://postgis.net
5
 *
6
 * PostGIS is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * PostGIS is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with PostGIS.  If not, see <http://www.gnu.org/licenses/>.
18
 *
19
 **********************************************************************
20
 *
21
 * Copyright 2008 Paul Ramsey
22
 *
23
 **********************************************************************/
24
25
26
#include "liblwgeom_internal.h"
27
#include "lwgeom_log.h"
28
#include <ctype.h> /* for tolower */
29
#include <stdbool.h>
30
31
int
32
p4d_same(const POINT4D *p1, const POINT4D *p2)
33
0
{
34
0
  return p1->x == p2->x
35
0
      && p1->y == p2->y
36
0
      && p1->z == p2->z
37
0
      && p1->m == p2->m;
38
0
}
39
40
int
41
p3d_same(const POINT3D *p1, const POINT3D *p2)
42
1.10M
{
43
1.10M
  return p1->x == p2->x
44
1.10M
      && p1->y == p2->y
45
1.10M
      && p1->z == p2->z;
46
1.10M
}
47
48
int
49
p3dz_same(const POINT3DZ *p1, const POINT3DZ *p2)
50
0
{
51
0
  return p1->x == p2->x
52
0
      && p1->y == p2->y
53
0
      && p1->z == p2->z;
54
0
}
55
56
int
57
p2d_same(const POINT2D *p1, const POINT2D *p2)
58
0
{
59
0
  return p1->x == p2->x
60
0
      && p1->y == p2->y;
61
0
}
62
63
/**
64
* lw_segment_side()
65
*
66
* Return -1  if point Q is left of segment P
67
* Return  1  if point Q is right of segment P
68
* Return  0  if point Q in on segment P
69
*/
70
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
71
152k
{
72
152k
  double side = ( (q->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (q->y - p1->y) );
73
152k
  return SIGNUM(side);
74
152k
}
75
76
/**
77
* Returns the length of a linear segment
78
*/
79
double
80
lw_seg_length(const POINT2D *A1, const POINT2D *A2)
81
0
{
82
0
  return sqrt((A1->x-A2->x)*(A1->x-A2->x)+(A1->y-A2->y)*(A1->y-A2->y));
83
0
}
84
85
/**
86
* Returns true if P is on the same side of the plane partition
87
* defined by A1/A3 as A2 is. Only makes sense if P has already been
88
* determined to be on the circle defined by A1/A2/A3.
89
*/
90
int
91
lw_pt_in_arc(const POINT2D *P, const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
92
0
{
93
0
  int pt_side = lw_segment_side(A1, A3, P);
94
0
  int arc_side = lw_segment_side(A1, A3, A2);
95
0
  return pt_side == 0 || pt_side == arc_side;
96
0
}
97
98
/**
99
* Returns true if P is between A1/A2. Only makes sense if P has already been
100
* determined to be on the line defined by A1/A2.
101
*/
102
int
103
lw_pt_in_seg(const POINT2D *P, const POINT2D *A1, const POINT2D *A2)
104
0
{
105
0
  return ((A1->x <= P->x && P->x < A2->x) || (A1->x >= P->x && P->x > A2->x)) ||
106
0
         ((A1->y <= P->y && P->y < A2->y) || (A1->y >= P->y && P->y > A2->y));
107
0
}
108
109
int
110
lw_pt_on_segment(const POINT2D* A1, const POINT2D* A2, const POINT2D* P)
111
0
{
112
0
  int side = lw_segment_side(A1, A2, P);
113
0
  if (side != 0) return LW_FALSE;
114
0
  return lw_pt_in_seg(P, A1, A2);
115
0
}
116
117
/**
118
* Returns true if arc A is actually a point (all vertices are the same) .
119
*/
120
int
121
lw_arc_is_pt(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
122
0
{
123
0
  if ( A1->x == A2->x && A2->x == A3->x &&
124
0
       A1->y == A2->y && A2->y == A3->y )
125
0
    return LW_TRUE;
126
0
  else
127
0
    return LW_FALSE;
128
0
}
129
130
/**
131
* Returns the length of a circular arc segment
132
*/
133
double
134
lw_arc_length(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3)
135
0
{
136
0
  POINT2D C;
137
0
  double radius_A, circumference_A;
138
0
  int a2_side, clockwise;
139
0
  double a1, a3;
140
0
  double angle;
141
142
0
  if ( lw_arc_is_pt(A1, A2, A3) )
143
0
    return 0.0;
144
145
0
  radius_A = lw_arc_center(A1, A2, A3, &C);
146
147
  /* Co-linear! Return linear distance! */
148
0
  if ( radius_A < 0 )
149
0
  {
150
0
        double dx = A1->x - A3->x;
151
0
        double dy = A1->y - A3->y;
152
0
    return sqrt(dx*dx + dy*dy);
153
0
  }
154
155
  /* Closed circle! Return the circumference! */
156
0
  circumference_A = M_PI * 2 * radius_A;
157
0
  if ( p2d_same(A1, A3) )
158
0
    return circumference_A;
159
160
  /* Determine the orientation of the arc */
161
0
  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
0
  if ( a2_side == -1 )
166
0
    clockwise = LW_TRUE;
167
0
  else
168
0
    clockwise = LW_FALSE;
169
170
  /* Angles of each point that defines the arc section */
171
0
  a1 = atan2(A1->y - C.y, A1->x - C.x);
172
0
  a3 = atan2(A3->y - C.y, A3->x - C.x);
173
174
  /* What's the sweep from A1 to A3? */
175
0
  if ( clockwise )
176
0
  {
177
0
    if ( a1 > a3 )
178
0
      angle = a1 - a3;
179
0
    else
180
0
      angle = 2*M_PI + a1 - a3;
181
0
  }
182
0
  else
183
0
  {
184
0
    if ( a3 > a1 )
185
0
      angle = a3 - a1;
186
0
    else
187
0
      angle = 2*M_PI + a3 - a1;
188
0
  }
189
190
  /* Length as proportion of circumference */
191
0
  return circumference_A * (angle / (2*M_PI));
192
0
}
193
194
int lw_arc_side(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, const POINT2D *Q)
195
0
{
196
0
  POINT2D C;
197
0
  double radius_A;
198
0
  double side_Q, side_A2;
199
0
  double d;
200
201
0
  side_Q = lw_segment_side(A1, A3, Q);
202
0
  radius_A = lw_arc_center(A1, A2, A3, &C);
203
0
  side_A2 = lw_segment_side(A1, A3, A2);
204
205
  /* Linear case */
206
0
  if ( radius_A < 0 )
207
0
    return side_Q;
208
209
0
  d = distance2d_pt_pt(Q, &C);
210
211
  /* Q is on the arc boundary */
212
0
  if ( d == radius_A && side_Q == side_A2 )
213
0
  {
214
0
    return 0;
215
0
  }
216
217
  /* Q on A1-A3 line, so its on opposite side to A2 */
218
0
  if ( side_Q == 0 )
219
0
  {
220
0
    return -1 * side_A2;
221
0
  }
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
0
  if ( d < radius_A && side_Q == side_A2 )
228
0
  {
229
0
    side_Q *= -1;
230
0
  }
231
232
0
  return side_Q;
233
0
}
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 collinear,
239
* as determined by equal slopes, then -1.0 is returned.  If the interior
240
* point is coincident with either end point, they are taken as collinear.
241
* For non-collinear cases, arc radious is returned.
242
*/
243
double
244
lw_arc_center(const POINT2D *p1, const POINT2D *p2, const POINT2D *p3, POINT2D *result)
245
3.61M
{
246
3.61M
  POINT2D c;
247
3.61M
  double cx, cy, cr;
248
3.61M
  double dx21, dy21, dx31, dy31, h21, h31, d;
249
250
3.61M
  c.x = c.y = 0.0;
251
252
3.61M
  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
3.61M
  if (fabs(p1->x - p3->x) < EPSILON_SQLMM &&
256
3.60M
      fabs(p1->y - p3->y) < EPSILON_SQLMM)
257
3.59M
  {
258
3.59M
    cx = p1->x + (p2->x - p1->x) / 2.0;
259
3.59M
    cy = p1->y + (p2->y - p1->y) / 2.0;
260
3.59M
    c.x = cx;
261
3.59M
    c.y = cy;
262
3.59M
    *result = c;
263
3.59M
    cr = sqrt(pow(cx - p1->x, 2.0) + pow(cy - p1->y, 2.0));
264
3.59M
    return cr;
265
3.59M
  }
266
267
  /* Using cartesian eguations from page https://en.wikipedia.org/wiki/Circumscribed_circle */
268
16.2k
  dx21 = p2->x - p1->x;
269
16.2k
  dy21 = p2->y - p1->y;
270
16.2k
  dx31 = p3->x - p1->x;
271
16.2k
  dy31 = p3->y - p1->y;
272
273
16.2k
  h21 = pow(dx21, 2.0) + pow(dy21, 2.0);
274
16.2k
  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
16.2k
  d = 2 * (dx21 * dy31 - dx31 * dy21);
278
279
  /* Check colinearity, |Cross product| = 0 */
280
16.2k
  if (fabs(d) < EPSILON_SQLMM)
281
6.45k
    return -1.0;
282
283
  /* Calculate centroid coordinates and radius */
284
9.83k
  cx = p1->x + (h21 * dy31 - h31 * dy21) / d;
285
9.83k
  cy = p1->y - (h21 * dx31 - h31 * dx21) / d;
286
9.83k
  c.x = cx;
287
9.83k
  c.y = cy;
288
9.83k
  *result = c;
289
9.83k
  cr = sqrt(pow(cx - p1->x, 2) + pow(cy - p1->y, 2));
290
291
9.83k
  LWDEBUGF(2, "lw_arc_center center is (%.16f,%.16f)", result->x, result->y);
292
293
9.83k
  return cr;
294
16.2k
}
295
296
static int
297
lw_seg_interact(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2)
298
0
{
299
0
  double minq=FP_MIN(q1->x,q2->x);
300
0
  double maxq=FP_MAX(q1->x,q2->x);
301
0
  double minp=FP_MIN(p1->x,p2->x);
302
0
  double maxp=FP_MAX(p1->x,p2->x);
303
304
0
  if (FP_GT(minp,maxq) || FP_LT(maxp,minq))
305
0
    return LW_FALSE;
306
307
0
  minq=FP_MIN(q1->y,q2->y);
308
0
  maxq=FP_MAX(q1->y,q2->y);
309
0
  minp=FP_MIN(p1->y,p2->y);
310
0
  maxp=FP_MAX(p1->y,p2->y);
311
312
0
  if (FP_GT(minp,maxq) || FP_LT(maxp,minq))
313
0
    return LW_FALSE;
314
315
0
  return LW_TRUE;
316
0
}
317
318
/**
319
** @brief returns the kind of #CG_SEGMENT_INTERSECTION_TYPE  behavior of lineseg 1 (constructed from p1 and p2) and lineseg 2 (constructed from q1 and q2)
320
**  @param p1 start point of first straight linesegment
321
**  @param p2 end point of first straight linesegment
322
**  @param q1 start point of second line segment
323
**  @param q2 end point of second line segment
324
**  @return a #CG_SEGMENT_INTERSECTION_TYPE
325
**  Returns one of
326
**    SEG_ERROR = -1,
327
**    SEG_NO_INTERSECTION = 0,
328
**    SEG_COLINEAR = 1,
329
**    SEG_CROSS_LEFT = 2,
330
**    SEG_CROSS_RIGHT = 3,
331
*/
332
int lw_segment_intersects(const POINT2D *p1, const POINT2D *p2, const POINT2D *q1, const POINT2D *q2)
333
0
{
334
335
0
  int pq1, pq2, qp1, qp2;
336
337
  /* No envelope interaction => we are done. */
338
0
  if (!lw_seg_interact(p1, p2, q1, p2))
339
0
  {
340
0
    return SEG_NO_INTERSECTION;
341
0
  }
342
343
  /* Are the start and end points of q on the same side of p? */
344
0
  pq1=lw_segment_side(p1,p2,q1);
345
0
  pq2=lw_segment_side(p1,p2,q2);
346
0
  if ((pq1>0 && pq2>0) || (pq1<0 && pq2<0))
347
0
  {
348
0
    return SEG_NO_INTERSECTION;
349
0
  }
350
351
  /* Are the start and end points of p on the same side of q? */
352
0
  qp1=lw_segment_side(q1,q2,p1);
353
0
  qp2=lw_segment_side(q1,q2,p2);
354
0
  if ( (qp1 > 0.0 && qp2 > 0.0) || (qp1 < 0.0 && qp2 < 0.0) )
355
0
  {
356
0
    return SEG_NO_INTERSECTION;
357
0
  }
358
359
  /* Nobody is on one side or another? Must be colinear. */
360
0
  if ( pq1 == 0.0 && pq2 == 0.0 && qp1 == 0.0 && qp2 == 0.0 )
361
0
  {
362
0
    return SEG_COLINEAR;
363
0
  }
364
365
  /*
366
  ** When one end-point touches, the sidedness is determined by the
367
  ** location of the other end-point. Only touches by the first point
368
  ** will be considered "real" to avoid double counting.
369
  */
370
0
  LWDEBUGF(4, "pq1=%d pq2=%d", pq1, pq2);
371
0
  LWDEBUGF(4, "qp1=%d qp2=%d", qp1, qp2);
372
373
  /* Second point of p or q touches, it's not a crossing. */
374
0
  if ( pq2 == 0 || qp2 == 0 )
375
0
  {
376
0
    return SEG_NO_INTERSECTION;
377
0
  }
378
379
  /* First point of p touches, it's a "crossing". */
380
0
  if ( pq1 == 0 )
381
0
  {
382
0
    if ( pq2 > 0 )
383
0
      return SEG_CROSS_RIGHT;
384
0
    else
385
0
      return SEG_CROSS_LEFT;
386
0
  }
387
388
  /* First point of q touches, it's a crossing. */
389
0
  if ( qp1 == 0 )
390
0
  {
391
0
    if ( pq1 < pq2 )
392
0
      return SEG_CROSS_RIGHT;
393
0
    else
394
0
      return SEG_CROSS_LEFT;
395
0
  }
396
397
  /* The segments cross, what direction is the crossing? */
398
0
  if ( pq1 < pq2 )
399
0
    return SEG_CROSS_RIGHT;
400
0
  else
401
0
    return SEG_CROSS_LEFT;
402
403
  /* This should never happen! */
404
0
  return SEG_ERROR;
405
0
}
406
407
/**
408
** @brief lwline_crossing_direction: returns the kind of #CG_LINE_CROSS_TYPE behavior  of 2 linestrings
409
** @param l1 first line string
410
** @param l2 second line string
411
** @return a #CG_LINE_CROSS_TYPE
412
**   LINE_NO_CROSS = 0
413
**   LINE_CROSS_LEFT = -1
414
**   LINE_CROSS_RIGHT = 1
415
**   LINE_MULTICROSS_END_LEFT = -2
416
**   LINE_MULTICROSS_END_RIGHT = 2
417
**   LINE_MULTICROSS_END_SAME_FIRST_LEFT = -3
418
**   LINE_MULTICROSS_END_SAME_FIRST_RIGHT = 3
419
**
420
*/
421
int lwline_crossing_direction(const LWLINE *l1, const LWLINE *l2)
422
0
{
423
0
  uint32_t i = 0, j = 0;
424
0
  const POINT2D *p1, *p2, *q1, *q2;
425
0
  POINTARRAY *pa1 = NULL, *pa2 = NULL;
426
0
  int cross_left = 0;
427
0
  int cross_right = 0;
428
0
  int first_cross = 0;
429
0
  int this_cross = 0;
430
#if POSTGIS_DEBUG_LEVEL >= 4
431
  char *geom_ewkt;
432
#endif
433
434
0
  pa1 = (POINTARRAY*)l1->points;
435
0
  pa2 = (POINTARRAY*)l2->points;
436
437
  /* One-point lines can't intersect (and shouldn't exist). */
438
0
  if ( pa1->npoints < 2 || pa2->npoints < 2 )
439
0
    return LINE_NO_CROSS;
440
441
  /* Zero length lines don't have a side. */
442
0
  if ( ptarray_length_2d(pa1) == 0 || ptarray_length_2d(pa2) == 0 )
443
0
    return LINE_NO_CROSS;
444
445
446
#if POSTGIS_DEBUG_LEVEL >= 4
447
  geom_ewkt = lwgeom_to_ewkt((LWGEOM*)l1);
448
  LWDEBUGF(4, "l1 = %s", geom_ewkt);
449
  lwfree(geom_ewkt);
450
  geom_ewkt = lwgeom_to_ewkt((LWGEOM*)l2);
451
  LWDEBUGF(4, "l2 = %s", geom_ewkt);
452
  lwfree(geom_ewkt);
453
#endif
454
455
  /* Initialize first point of q */
456
0
  q1 = getPoint2d_cp(pa2, 0);
457
458
0
  for ( i = 1; i < pa2->npoints; i++ )
459
0
  {
460
461
    /* Update second point of q to next value */
462
0
    q2 = getPoint2d_cp(pa2, i);
463
464
    /* Initialize first point of p */
465
0
    p1 = getPoint2d_cp(pa1, 0);
466
467
0
    for ( j = 1; j < pa1->npoints; j++ )
468
0
    {
469
470
      /* Update second point of p to next value */
471
0
      p2 = getPoint2d_cp(pa1, j);
472
473
0
      this_cross = lw_segment_intersects(p1, p2, q1, q2);
474
475
0
      LWDEBUGF(4, "i=%d, j=%d (%.8g %.8g, %.8g %.8g)", i, j, p1->x, p1->y, p2->x, p2->y);
476
477
0
      if ( this_cross == SEG_CROSS_LEFT )
478
0
      {
479
0
        LWDEBUG(4,"this_cross == SEG_CROSS_LEFT");
480
0
        cross_left++;
481
0
        if ( ! first_cross )
482
0
          first_cross = SEG_CROSS_LEFT;
483
0
      }
484
485
0
      if ( this_cross == SEG_CROSS_RIGHT )
486
0
      {
487
0
        LWDEBUG(4,"this_cross == SEG_CROSS_RIGHT");
488
0
        cross_right++;
489
0
        if ( ! first_cross )
490
0
          first_cross = SEG_CROSS_RIGHT;
491
0
      }
492
493
      /*
494
      ** Crossing at a co-linearity can be turned handled by extending
495
      ** segment to next vertex and seeing if the end points straddle
496
      ** the co-linear segment.
497
      */
498
0
      if ( this_cross == SEG_COLINEAR )
499
0
      {
500
0
        LWDEBUG(4,"this_cross == SEG_COLINEAR");
501
        /* TODO: Add logic here and in segment_intersects()
502
        continue;
503
        */
504
0
      }
505
506
0
      LWDEBUG(4,"this_cross == SEG_NO_INTERSECTION");
507
508
      /* Turn second point of p into first point */
509
0
      p1 = p2;
510
511
0
    }
512
513
    /* Turn second point of q into first point */
514
0
    q1 = q2;
515
516
0
  }
517
518
0
  LWDEBUGF(4, "first_cross=%d, cross_left=%d, cross_right=%d", first_cross, cross_left, cross_right);
519
520
0
  if ( !cross_left && !cross_right )
521
0
    return LINE_NO_CROSS;
522
523
0
  if ( !cross_left && cross_right == 1 )
524
0
    return LINE_CROSS_RIGHT;
525
526
0
  if ( !cross_right && cross_left == 1 )
527
0
    return LINE_CROSS_LEFT;
528
529
0
  if ( cross_left - cross_right == 1 )
530
0
    return LINE_MULTICROSS_END_LEFT;
531
532
0
  if ( cross_left - cross_right == -1 )
533
0
    return LINE_MULTICROSS_END_RIGHT;
534
535
0
  if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_LEFT )
536
0
    return LINE_MULTICROSS_END_SAME_FIRST_LEFT;
537
538
0
  if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_RIGHT )
539
0
    return LINE_MULTICROSS_END_SAME_FIRST_RIGHT;
540
541
0
  return LINE_NO_CROSS;
542
543
0
}
544
545
546
547
548
549
static char *base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
550
551
0
#define GEOHASH_MAX_DOUBLE_PRECISION_CHARS 20
552
553
/*
554
** Calculate the geohash, iterating downwards and gaining precision.
555
** From geohash-native.c, (c) 2008 David Troy <dave@roundhousetech.com>
556
** Released under the MIT License.
557
*/
558
lwvarlena_t *
559
geohash_point(double longitude, double latitude, int precision)
560
0
{
561
0
  int is_even=1, i=0;
562
0
  double lat[2], lon[2], mid;
563
0
  char bits[] = {16,8,4,2,1};
564
0
  int bit=0, ch=0;
565
0
  lwvarlena_t *v = lwalloc(precision + LWVARHDRSZ);
566
0
  LWSIZE_SET(v->size, precision + LWVARHDRSZ);
567
0
  char *geohash = v->data;
568
569
0
  lat[0] = -90.0;
570
0
  lat[1] = 90.0;
571
0
  lon[0] = -180.0;
572
0
  lon[1] = 180.0;
573
574
0
  while (i < precision)
575
0
  {
576
0
    if (is_even)
577
0
    {
578
0
      mid = (lon[0] + lon[1]) / 2;
579
0
      if (longitude >= mid)
580
0
      {
581
0
        ch |= bits[bit];
582
0
        lon[0] = mid;
583
0
      }
584
0
      else
585
0
      {
586
0
        lon[1] = mid;
587
0
      }
588
0
    }
589
0
    else
590
0
    {
591
0
      mid = (lat[0] + lat[1]) / 2;
592
0
      if (latitude >= mid)
593
0
      {
594
0
        ch |= bits[bit];
595
0
        lat[0] = mid;
596
0
      }
597
0
      else
598
0
      {
599
0
        lat[1] = mid;
600
0
      }
601
0
    }
602
603
0
    is_even = !is_even;
604
0
    if (bit < 4)
605
0
    {
606
0
      bit++;
607
0
    }
608
0
    else
609
0
    {
610
0
      geohash[i++] = base32[ch];
611
0
      bit = 0;
612
0
      ch = 0;
613
0
    }
614
0
  }
615
616
0
  return v;
617
0
}
618
619
620
/*
621
** Calculate the geohash, iterating downwards and gaining precision.
622
** From geohash-native.c, (c) 2008 David Troy <dave@roundhousetech.com>
623
** Released under the MIT License.
624
*/
625
unsigned int geohash_point_as_int(POINT2D *pt)
626
0
{
627
0
  int is_even=1;
628
0
  double lat[2], lon[2], mid;
629
0
  int bit=32;
630
0
  unsigned int ch = 0;
631
632
0
  double longitude = pt->x;
633
0
  double latitude = pt->y;
634
635
0
  lat[0] = -90.0;
636
0
  lat[1] = 90.0;
637
0
  lon[0] = -180.0;
638
0
  lon[1] = 180.0;
639
640
0
  while (--bit >= 0)
641
0
  {
642
0
    if (is_even)
643
0
    {
644
0
      mid = (lon[0] + lon[1]) / 2;
645
0
      if (longitude > mid)
646
0
      {
647
0
        ch |= 0x0001u << bit;
648
0
        lon[0] = mid;
649
0
      }
650
0
      else
651
0
      {
652
0
        lon[1] = mid;
653
0
      }
654
0
    }
655
0
    else
656
0
    {
657
0
      mid = (lat[0] + lat[1]) / 2;
658
0
      if (latitude > mid)
659
0
      {
660
0
        ch |= 0x0001 << bit;
661
0
        lat[0] = mid;
662
0
      }
663
0
      else
664
0
      {
665
0
        lat[1] = mid;
666
0
      }
667
0
    }
668
669
0
    is_even = !is_even;
670
0
  }
671
0
  return ch;
672
0
}
673
674
/*
675
** Decode a GeoHash into a bounding box. The lat and lon arguments should
676
** both be passed as double arrays of length 2 at a minimum where the values
677
** set in them will be the southwest and northeast coordinates of the bounding
678
** box accordingly. A precision less than 0 indicates that the entire length
679
** of the GeoHash should be used.
680
** It will call `lwerror` if an invalid character is found
681
*/
682
void decode_geohash_bbox(char *geohash, double *lat, double *lon, int precision)
683
67
{
684
67
  bool is_even = 1;
685
686
67
  lat[0] = -90.0;
687
67
  lat[1] = 90.0;
688
67
  lon[0] = -180.0;
689
67
  lon[1] = 180.0;
690
691
67
  size_t hashlen = strlen(geohash);
692
67
  if (precision < 0 || (size_t)precision > hashlen)
693
40
  {
694
40
    precision = (int)hashlen;
695
40
  }
696
697
2.53k
  for (int i = 0; i < precision; i++)
698
2.49k
  {
699
2.49k
    char c = tolower(geohash[i]);
700
701
    /* Valid characters are all digits in base32 */
702
2.49k
    char *base32_pos = strchr(base32, c);
703
2.49k
    if (!base32_pos)
704
31
    {
705
31
      lwerror("%s: Invalid character '%c'", __func__, geohash[i]);
706
31
      return;
707
31
    }
708
2.46k
    char cd = base32_pos - base32;
709
710
14.7k
    for (size_t j = 0; j < 5; j++)
711
12.3k
    {
712
12.3k
      const char bits[] = {16, 8, 4, 2, 1};
713
12.3k
      char mask = bits[j];
714
12.3k
      if (is_even)
715
6.17k
      {
716
6.17k
        lon[!(cd & mask)] = (lon[0] + lon[1]) / 2;
717
6.17k
      }
718
6.14k
      else
719
6.14k
      {
720
6.14k
        lat[!(cd & mask)] = (lat[0] + lat[1]) / 2;
721
6.14k
      }
722
12.3k
      is_even = !is_even;
723
12.3k
    }
724
2.46k
  }
725
67
}
726
727
int lwgeom_geohash_precision(GBOX bbox, GBOX *bounds)
728
0
{
729
0
  double minx, miny, maxx, maxy;
730
0
  double latmax, latmin, lonmax, lonmin;
731
0
  double lonwidth, latwidth;
732
0
  double latmaxadjust, lonmaxadjust, latminadjust, lonminadjust;
733
0
  int precision = 0;
734
735
  /* Get the bounding box, return error if things don't work out. */
736
0
  minx = bbox.xmin;
737
0
  miny = bbox.ymin;
738
0
  maxx = bbox.xmax;
739
0
  maxy = bbox.ymax;
740
741
0
  if ( minx == maxx && miny == maxy )
742
0
  {
743
    /* It's a point. Doubles have 51 bits of precision.
744
    ** 2 * 51 / 5 == 20 */
745
0
    return GEOHASH_MAX_DOUBLE_PRECISION_CHARS;
746
0
  }
747
748
0
  lonmin = -180.0;
749
0
  latmin = -90.0;
750
0
  lonmax = 180.0;
751
0
  latmax = 90.0;
752
753
  /* Shrink a world bounding box until one of the edges interferes with the
754
  ** bounds of our rectangle. */
755
0
  while ( 1 )
756
0
  {
757
0
    double old_lonmin = lonmin;
758
0
    double old_lonmax = lonmax;
759
0
    double old_latmin = latmin;
760
0
    double old_latmax = latmax;
761
762
0
    lonwidth = lonmax - lonmin;
763
0
    latwidth = latmax - latmin;
764
0
    latmaxadjust = lonmaxadjust = latminadjust = lonminadjust = 0.0;
765
766
0
    if ( minx >= lonmin + lonwidth / 2.0 )
767
0
    {
768
0
      lonminadjust = lonwidth / 2.0;
769
0
    }
770
0
    else if ( maxx <= lonmax - lonwidth / 2.0 )
771
0
    {
772
0
      lonmaxadjust = -1 * lonwidth / 2.0;
773
0
    }
774
0
    if ( lonminadjust || lonmaxadjust )
775
0
    {
776
0
      lonmin += lonminadjust;
777
0
      lonmax += lonmaxadjust;
778
      /* Stop before counting bits that no longer move the double bounds. */
779
0
      if (lonmin == old_lonmin && lonmax == old_lonmax)
780
0
        break;
781
      /* Each adjustment cycle corresponds to 2 bits of storage in the
782
      ** geohash. */
783
0
      precision++;
784
0
    }
785
0
    else
786
0
    {
787
0
      break;
788
0
    }
789
790
0
    if ( miny >= latmin + latwidth / 2.0 )
791
0
    {
792
0
      latminadjust = latwidth / 2.0;
793
0
    }
794
0
    else if (maxy <= latmax - latwidth / 2.0 )
795
0
    {
796
0
      latmaxadjust = -1 * latwidth / 2.0;
797
0
    }
798
    /* Only adjust if adjustments are legal (we haven't crossed any edges). */
799
0
    if ( latminadjust || latmaxadjust )
800
0
    {
801
0
      latmin += latminadjust;
802
0
      latmax += latmaxadjust;
803
      /* Stop before counting bits that no longer move the double bounds. */
804
0
      if (latmin == old_latmin && latmax == old_latmax)
805
0
        break;
806
      /* Each adjustment cycle corresponds to 2 bits of storage in the
807
      ** geohash. */
808
0
      precision++;
809
0
    }
810
0
    else
811
0
    {
812
0
      break;
813
0
    }
814
0
  }
815
816
  /* Save the edges of our bounds, in case someone cares later. */
817
0
  bounds->xmin = lonmin;
818
0
  bounds->xmax = lonmax;
819
0
  bounds->ymin = latmin;
820
0
  bounds->ymax = latmax;
821
822
  /* Each geohash character (base32) can contain 5 bits of information.
823
  ** We are returning the precision in characters, so here we divide. */
824
0
  precision /= 5;
825
0
  return precision > GEOHASH_MAX_DOUBLE_PRECISION_CHARS ? GEOHASH_MAX_DOUBLE_PRECISION_CHARS : precision;
826
0
}
827
828
829
/*
830
** Return a geohash string for the geometry. <http://geohash.org>
831
** Where the precision is non-positive, calculate a precision based on the
832
** bounds of the feature. Big features have loose precision.
833
** Small features have tight precision.
834
*/
835
lwvarlena_t *
836
lwgeom_geohash(const LWGEOM *lwgeom, int precision)
837
0
{
838
0
  GBOX gbox = {0};
839
0
  GBOX gbox_bounds = {0};
840
0
  double lat, lon;
841
0
  int result;
842
843
0
  gbox_init(&gbox);
844
0
  gbox_init(&gbox_bounds);
845
846
0
  result = lwgeom_calculate_gbox_cartesian(lwgeom, &gbox);
847
0
  if ( result == LW_FAILURE ) return NULL;
848
849
  /* Return error if we are being fed something outside our working bounds */
850
0
  if ( gbox.xmin < -180 || gbox.ymin < -90 || gbox.xmax > 180 || gbox.ymax > 90 )
851
0
  {
852
0
    lwerror("Geohash requires inputs in decimal degrees, got (%g %g, %g %g).",
853
0
       gbox.xmin, gbox.ymin,
854
0
       gbox.xmax, gbox.ymax);
855
0
    return NULL;
856
0
  }
857
858
  /* What is the center of our geometry bounds? We'll use that to
859
  ** approximate location. */
860
0
  lon = gbox.xmin + (gbox.xmax - gbox.xmin) / 2;
861
0
  lat = gbox.ymin + (gbox.ymax - gbox.ymin) / 2;
862
863
0
  if ( precision <= 0 )
864
0
  {
865
0
    precision = lwgeom_geohash_precision(gbox, &gbox_bounds);
866
0
  }
867
868
  /*
869
  ** Return the geohash of the center, with a precision determined by the
870
  ** extent of the bounds.
871
  ** Possible change: return the point at the center of the precision bounds?
872
  */
873
0
  return geohash_point(lon, lat, precision);
874
0
}