Coverage Report

Created: 2026-03-31 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/gbox.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 2009 Paul Ramsey <pramsey@cleverelephant.ca>
22
 *
23
 **********************************************************************/
24
25
#include "liblwgeom_internal.h"
26
#include "lwgeodetic.h"
27
#include "lwgeom_log.h"
28
#include <stdlib.h>
29
#include <math.h>
30
31
32
GBOX* gbox_new(lwflags_t flags)
33
627
{
34
627
  GBOX *g = (GBOX*)lwalloc(sizeof(GBOX));
35
627
  gbox_init(g);
36
627
  g->flags = flags;
37
627
  return g;
38
627
}
39
40
void gbox_init(GBOX *gbox)
41
627
{
42
627
  memset(gbox, 0, sizeof(GBOX));
43
627
}
44
45
GBOX* gbox_clone(const GBOX *gbox)
46
0
{
47
0
  GBOX *g = lwalloc(sizeof(GBOX));
48
0
  memcpy(g, gbox, sizeof(GBOX));
49
0
  return g;
50
0
}
51
52
/* TODO to be removed */
53
BOX3D* box3d_from_gbox(const GBOX *gbox)
54
0
{
55
0
  BOX3D *b;
56
0
  assert(gbox);
57
58
0
  b = lwalloc(sizeof(BOX3D));
59
60
0
  b->xmin = gbox->xmin;
61
0
  b->xmax = gbox->xmax;
62
0
  b->ymin = gbox->ymin;
63
0
  b->ymax = gbox->ymax;
64
65
0
  if ( FLAGS_GET_Z(gbox->flags) )
66
0
  {
67
0
    b->zmin = gbox->zmin;
68
0
    b->zmax = gbox->zmax;
69
0
  }
70
0
  else
71
0
  {
72
0
    b->zmin = b->zmax = 0.0;
73
0
  }
74
75
0
  b->srid = SRID_UNKNOWN;
76
0
  return b;
77
0
}
78
79
/* TODO to be removed */
80
GBOX* box3d_to_gbox(const BOX3D *b3d)
81
0
{
82
0
  GBOX *b;
83
0
  assert(b3d);
84
85
0
  b = lwalloc(sizeof(GBOX));
86
87
0
  b->xmin = b3d->xmin;
88
0
  b->xmax = b3d->xmax;
89
0
  b->ymin = b3d->ymin;
90
0
  b->ymax = b3d->ymax;
91
0
  b->zmin = b3d->zmin;
92
0
  b->zmax = b3d->zmax;
93
94
0
  return b;
95
0
}
96
97
void gbox_expand(GBOX *g, double d)
98
0
{
99
0
  g->xmin -= d;
100
0
  g->xmax += d;
101
0
  g->ymin -= d;
102
0
  g->ymax += d;
103
0
  if (FLAGS_GET_Z(g->flags) || FLAGS_GET_GEODETIC(g->flags))
104
0
  {
105
0
    g->zmin -= d;
106
0
    g->zmax += d;
107
0
  }
108
0
  if (FLAGS_GET_M(g->flags))
109
0
  {
110
0
    g->mmin -= d;
111
0
    g->mmax += d;
112
0
  }
113
0
}
114
115
void gbox_expand_xyzm(GBOX *g, double dx, double dy, double dz, double dm)
116
0
{
117
0
  g->xmin -= dx;
118
0
  g->xmax += dx;
119
0
  g->ymin -= dy;
120
0
  g->ymax += dy;
121
122
0
  if (FLAGS_GET_Z(g->flags))
123
0
  {
124
0
    g->zmin -= dz;
125
0
    g->zmax += dz;
126
0
  }
127
128
0
  if (FLAGS_GET_M(g->flags))
129
0
  {
130
0
    g->mmin -= dm;
131
0
    g->mmax += dm;
132
0
  }
133
0
}
134
135
int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout)
136
0
{
137
0
  if ( ( ! g1 ) && ( ! g2 ) )
138
0
    return LW_FALSE;
139
0
  else if (!g1)
140
0
  {
141
0
    memcpy(gout, g2, sizeof(GBOX));
142
0
    return LW_TRUE;
143
0
  }
144
0
  else if (!g2)
145
0
  {
146
0
    memcpy(gout, g1, sizeof(GBOX));
147
0
    return LW_TRUE;
148
0
  }
149
150
0
  gout->flags = g1->flags;
151
152
0
  gout->xmin = FP_MIN(g1->xmin, g2->xmin);
153
0
  gout->xmax = FP_MAX(g1->xmax, g2->xmax);
154
155
0
  gout->ymin = FP_MIN(g1->ymin, g2->ymin);
156
0
  gout->ymax = FP_MAX(g1->ymax, g2->ymax);
157
158
0
  gout->zmin = FP_MIN(g1->zmin, g2->zmin);
159
0
  gout->zmax = FP_MAX(g1->zmax, g2->zmax);
160
161
0
  return LW_TRUE;
162
0
}
163
164
int gbox_same(const GBOX *g1, const GBOX *g2)
165
0
{
166
0
  if (FLAGS_GET_ZM(g1->flags) != FLAGS_GET_ZM(g2->flags))
167
0
    return LW_FALSE;
168
169
0
  if (!gbox_same_2d(g1, g2)) return LW_FALSE;
170
171
0
  if (FLAGS_GET_Z(g1->flags) && (g1->zmin != g2->zmin || g1->zmax != g2->zmax))
172
0
    return LW_FALSE;
173
0
  if (FLAGS_GET_M(g1->flags) && (g1->mmin != g2->mmin || g1->mmax != g2->mmax))
174
0
    return LW_FALSE;
175
176
0
  return LW_TRUE;
177
0
}
178
179
int gbox_same_2d(const GBOX *g1, const GBOX *g2)
180
0
{
181
0
    if (g1->xmin == g2->xmin && g1->ymin == g2->ymin &&
182
0
        g1->xmax == g2->xmax && g1->ymax == g2->ymax)
183
0
    return LW_TRUE;
184
0
  return LW_FALSE;
185
0
}
186
187
int gbox_same_2d_float(const GBOX *g1, const GBOX *g2)
188
0
{
189
0
  if  ((g1->xmax == g2->xmax || next_float_up(g1->xmax)   == next_float_up(g2->xmax))   &&
190
0
       (g1->ymax == g2->ymax || next_float_up(g1->ymax)   == next_float_up(g2->ymax))   &&
191
0
       (g1->xmin == g2->xmin || next_float_down(g1->xmin) == next_float_down(g1->xmin)) &&
192
0
       (g1->ymin == g2->ymin || next_float_down(g2->ymin) == next_float_down(g2->ymin)))
193
0
      return LW_TRUE;
194
0
  return LW_FALSE;
195
0
}
196
197
int gbox_is_valid(const GBOX *gbox)
198
0
{
199
  /* X */
200
0
  if ( ! isfinite(gbox->xmin) || isnan(gbox->xmin) ||
201
0
       ! isfinite(gbox->xmax) || isnan(gbox->xmax) )
202
0
    return LW_FALSE;
203
204
  /* Y */
205
0
  if ( ! isfinite(gbox->ymin) || isnan(gbox->ymin) ||
206
0
       ! isfinite(gbox->ymax) || isnan(gbox->ymax) )
207
0
    return LW_FALSE;
208
209
  /* Z */
210
0
  if ( FLAGS_GET_GEODETIC(gbox->flags) || FLAGS_GET_Z(gbox->flags) )
211
0
  {
212
0
    if ( ! isfinite(gbox->zmin) || isnan(gbox->zmin) ||
213
0
         ! isfinite(gbox->zmax) || isnan(gbox->zmax) )
214
0
      return LW_FALSE;
215
0
  }
216
217
  /* M */
218
0
  if ( FLAGS_GET_M(gbox->flags) )
219
0
  {
220
0
    if ( ! isfinite(gbox->mmin) || isnan(gbox->mmin) ||
221
0
         ! isfinite(gbox->mmax) || isnan(gbox->mmax) )
222
0
      return LW_FALSE;
223
0
  }
224
225
0
  return LW_TRUE;
226
0
}
227
228
int gbox_merge_point3d(const POINT3D *p, GBOX *gbox)
229
0
{
230
0
  if ( gbox->xmin > p->x ) gbox->xmin = p->x;
231
0
  if ( gbox->ymin > p->y ) gbox->ymin = p->y;
232
0
  if ( gbox->zmin > p->z ) gbox->zmin = p->z;
233
0
  if ( gbox->xmax < p->x ) gbox->xmax = p->x;
234
0
  if ( gbox->ymax < p->y ) gbox->ymax = p->y;
235
0
  if ( gbox->zmax < p->z ) gbox->zmax = p->z;
236
0
  return LW_SUCCESS;
237
0
}
238
239
int gbox_init_point3d(const POINT3D *p, GBOX *gbox)
240
0
{
241
0
  gbox->xmin = gbox->xmax = p->x;
242
0
  gbox->ymin = gbox->ymax = p->y;
243
0
  gbox->zmin = gbox->zmax = p->z;
244
0
  return LW_SUCCESS;
245
0
}
246
247
int gbox_contains_point3d(const GBOX *gbox, const POINT3D *pt)
248
0
{
249
0
  if ( gbox->xmin > pt->x || gbox->ymin > pt->y || gbox->zmin > pt->z ||
250
0
       gbox->xmax < pt->x || gbox->ymax < pt->y || gbox->zmax < pt->z )
251
0
  {
252
0
    return LW_FALSE;
253
0
  }
254
0
  return LW_TRUE;
255
0
}
256
257
int gbox_merge(const GBOX *new_box, GBOX *merge_box)
258
4.24k
{
259
4.24k
  assert(merge_box);
260
261
4.24k
  if ( FLAGS_GET_ZM(merge_box->flags) != FLAGS_GET_ZM(new_box->flags) )
262
0
    return LW_FAILURE;
263
264
4.24k
  if ( new_box->xmin < merge_box->xmin) merge_box->xmin = new_box->xmin;
265
4.24k
  if ( new_box->ymin < merge_box->ymin) merge_box->ymin = new_box->ymin;
266
4.24k
  if ( new_box->xmax > merge_box->xmax) merge_box->xmax = new_box->xmax;
267
4.24k
  if ( new_box->ymax > merge_box->ymax) merge_box->ymax = new_box->ymax;
268
269
4.24k
  if ( FLAGS_GET_Z(merge_box->flags) || FLAGS_GET_GEODETIC(merge_box->flags) )
270
1.13k
  {
271
1.13k
    if ( new_box->zmin < merge_box->zmin) merge_box->zmin = new_box->zmin;
272
1.13k
    if ( new_box->zmax > merge_box->zmax) merge_box->zmax = new_box->zmax;
273
1.13k
  }
274
4.24k
  if ( FLAGS_GET_M(merge_box->flags) )
275
0
  {
276
0
    if ( new_box->mmin < merge_box->mmin) merge_box->mmin = new_box->mmin;
277
0
    if ( new_box->mmax > merge_box->mmax) merge_box->mmax = new_box->mmax;
278
0
  }
279
280
4.24k
  return LW_SUCCESS;
281
4.24k
}
282
283
int gbox_overlaps(const GBOX *g1, const GBOX *g2)
284
0
{
285
286
  /* Make sure our boxes are consistent */
287
0
  if ( FLAGS_GET_GEODETIC(g1->flags) != FLAGS_GET_GEODETIC(g2->flags) )
288
0
    lwerror("gbox_overlaps: cannot compare geodetic and non-geodetic boxes");
289
290
  /* Check X/Y first */
291
0
  if ( g1->xmax < g2->xmin || g1->ymax < g2->ymin ||
292
0
       g1->xmin > g2->xmax || g1->ymin > g2->ymax )
293
0
    return LW_FALSE;
294
295
  /* Deal with the geodetic case special: we only compare the geodetic boxes (x/y/z) */
296
  /* Never the M dimension */
297
0
  if ( FLAGS_GET_GEODETIC(g1->flags) && FLAGS_GET_GEODETIC(g2->flags) )
298
0
  {
299
0
    if ( g1->zmax < g2->zmin || g1->zmin > g2->zmax )
300
0
      return LW_FALSE;
301
0
    else
302
0
      return LW_TRUE;
303
0
  }
304
305
  /* If both geodetic or both have Z, check Z */
306
0
  if ( FLAGS_GET_Z(g1->flags) && FLAGS_GET_Z(g2->flags) )
307
0
  {
308
0
    if ( g1->zmax < g2->zmin || g1->zmin > g2->zmax )
309
0
      return LW_FALSE;
310
0
  }
311
312
  /* If both have M, check M */
313
0
  if ( FLAGS_GET_M(g1->flags) && FLAGS_GET_M(g2->flags) )
314
0
  {
315
0
    if ( g1->mmax < g2->mmin || g1->mmin > g2->mmax )
316
0
      return LW_FALSE;
317
0
  }
318
319
0
  return LW_TRUE;
320
0
}
321
322
int
323
gbox_overlaps_2d(const GBOX *g1, const GBOX *g2)
324
0
{
325
326
  /* Make sure our boxes are consistent */
327
0
  if ( FLAGS_GET_GEODETIC(g1->flags) != FLAGS_GET_GEODETIC(g2->flags) )
328
0
    lwerror("gbox_overlaps: cannot compare geodetic and non-geodetic boxes");
329
330
  /* Check X/Y first */
331
0
  if ( g1->xmax < g2->xmin || g1->ymax < g2->ymin ||
332
0
       g1->xmin > g2->xmax || g1->ymin > g2->ymax )
333
0
    return LW_FALSE;
334
335
0
  return LW_TRUE;
336
0
}
337
338
int
339
gbox_contains_2d(const GBOX *g1, const GBOX *g2)
340
0
{
341
0
  if ( ( g2->xmin < g1->xmin ) || ( g2->xmax > g1->xmax ) ||
342
0
       ( g2->ymin < g1->ymin ) || ( g2->ymax > g1->ymax ) )
343
0
  {
344
0
    return LW_FALSE;
345
0
  }
346
0
  return LW_TRUE;
347
0
}
348
349
350
int
351
gbox_within_2d(const GBOX *g1, const GBOX *g2)
352
0
{
353
0
  if ( ( g1->xmin < g2->xmin ) || ( g1->xmax > g2->xmax ) ||
354
0
       ( g1->ymin < g2->ymin ) || ( g1->ymax > g2->ymax ) )
355
0
  {
356
0
    return LW_FALSE;
357
0
  }
358
0
  return LW_TRUE;
359
0
}
360
361
int
362
gbox_contains_point2d(const GBOX *g, const POINT2D *p)
363
0
{
364
0
  if ( ( g->xmin <= p->x ) && ( g->xmax >= p->x ) &&
365
0
       ( g->ymin <= p->y ) && ( g->ymax >= p->y ) )
366
0
  {
367
0
    return LW_TRUE;
368
0
  }
369
0
  return LW_FALSE;
370
0
}
371
372
/**
373
* Warning, this function is only good for x/y/z boxes, used
374
* in unit testing of geodetic box generation.
375
*/
376
GBOX* gbox_from_string(const char *str)
377
0
{
378
0
  const char *ptr = str;
379
0
  char *nextptr;
380
0
  char *gbox_start = strstr(str, "GBOX((");
381
0
  GBOX *gbox = gbox_new(lwflags(0,0,1));
382
0
  if ( ! gbox_start ) return NULL; /* No header found */
383
0
  ptr += 6;
384
0
  gbox->xmin = strtod(ptr, &nextptr);
385
0
  if ( ptr == nextptr ) return NULL; /* No double found */
386
0
  ptr = nextptr + 1;
387
0
  gbox->ymin = strtod(ptr, &nextptr);
388
0
  if ( ptr == nextptr ) return NULL; /* No double found */
389
0
  ptr = nextptr + 1;
390
0
  gbox->zmin = strtod(ptr, &nextptr);
391
0
  if ( ptr == nextptr ) return NULL; /* No double found */
392
0
  ptr = nextptr + 3;
393
0
  gbox->xmax = strtod(ptr, &nextptr);
394
0
  if ( ptr == nextptr ) return NULL; /* No double found */
395
0
  ptr = nextptr + 1;
396
0
  gbox->ymax = strtod(ptr, &nextptr);
397
0
  if ( ptr == nextptr ) return NULL; /* No double found */
398
0
  ptr = nextptr + 1;
399
0
  gbox->zmax = strtod(ptr, &nextptr);
400
0
  if ( ptr == nextptr ) return NULL; /* No double found */
401
0
  return gbox;
402
0
}
403
404
char* gbox_to_string(const GBOX *gbox)
405
0
{
406
0
  const size_t sz = 138;
407
0
  char *str = NULL;
408
409
0
  if ( ! gbox )
410
0
    return lwstrdup("NULL POINTER");
411
412
0
  str = (char*)lwalloc(sz);
413
414
0
  if ( FLAGS_GET_GEODETIC(gbox->flags) )
415
0
  {
416
0
    snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->xmax, gbox->ymax, gbox->zmax);
417
0
    return str;
418
0
  }
419
0
  if ( FLAGS_GET_Z(gbox->flags) && FLAGS_GET_M(gbox->flags) )
420
0
  {
421
0
    snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->mmin, gbox->xmax, gbox->ymax, gbox->zmax, gbox->mmax);
422
0
    return str;
423
0
  }
424
0
  if ( FLAGS_GET_Z(gbox->flags) )
425
0
  {
426
0
    snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->xmax, gbox->ymax, gbox->zmax);
427
0
    return str;
428
0
  }
429
0
  if ( FLAGS_GET_M(gbox->flags) )
430
0
  {
431
0
    snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->mmin, gbox->xmax, gbox->ymax, gbox->mmax);
432
0
    return str;
433
0
  }
434
0
  snprintf(str, sz, "GBOX((%.8g,%.8g),(%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->xmax, gbox->ymax);
435
0
  return str;
436
0
}
437
438
GBOX* gbox_copy(const GBOX *box)
439
0
{
440
0
  GBOX *copy = (GBOX*)lwalloc(sizeof(GBOX));
441
0
  memcpy(copy, box, sizeof(GBOX));
442
0
  return copy;
443
0
}
444
445
void gbox_duplicate(const GBOX *original, GBOX *duplicate)
446
1.75k
{
447
1.75k
  assert(duplicate);
448
1.75k
  assert(original);
449
1.75k
  memcpy(duplicate, original, sizeof(GBOX));
450
1.75k
}
451
452
size_t gbox_serialized_size(lwflags_t flags)
453
0
{
454
0
  if (FLAGS_GET_GEODETIC(flags))
455
0
    return 6 * sizeof(float);
456
0
  else
457
0
    return 2 * FLAGS_NDIMS(flags) * sizeof(float);
458
0
}
459
460
461
/* ********************************************************************************
462
** Compute cartesian bounding GBOX boxes from LWGEOM.
463
*/
464
465
int lw_arc_calculate_gbox_cartesian_2d(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, GBOX *gbox)
466
0
{
467
0
  POINT2D xmin, ymin, xmax, ymax;
468
0
  POINT2D C;
469
0
  int A2_side;
470
0
  double radius_A;
471
472
0
  LWDEBUG(2, "lw_arc_calculate_gbox_cartesian_2d called.");
473
474
0
  radius_A = lw_arc_center(A1, A2, A3, &C);
475
476
  /* Negative radius signals straight line, p1/p2/p3 are collinear */
477
0
  if (radius_A < 0.0)
478
0
  {
479
0
        gbox->xmin = FP_MIN(A1->x, A3->x);
480
0
        gbox->ymin = FP_MIN(A1->y, A3->y);
481
0
        gbox->xmax = FP_MAX(A1->x, A3->x);
482
0
        gbox->ymax = FP_MAX(A1->y, A3->y);
483
0
      return LW_SUCCESS;
484
0
  }
485
486
  /* Matched start/end points imply circle */
487
0
  if ( A1->x == A3->x && A1->y == A3->y )
488
0
  {
489
0
    gbox->xmin = C.x - radius_A;
490
0
    gbox->ymin = C.y - radius_A;
491
0
    gbox->xmax = C.x + radius_A;
492
0
    gbox->ymax = C.y + radius_A;
493
0
    return LW_SUCCESS;
494
0
  }
495
496
  /* First approximation, bounds of start/end points */
497
0
    gbox->xmin = FP_MIN(A1->x, A3->x);
498
0
    gbox->ymin = FP_MIN(A1->y, A3->y);
499
0
    gbox->xmax = FP_MAX(A1->x, A3->x);
500
0
    gbox->ymax = FP_MAX(A1->y, A3->y);
501
502
  /* Create points for the possible extrema */
503
0
  xmin.x = C.x - radius_A;
504
0
  xmin.y = C.y;
505
0
  ymin.x = C.x;
506
0
  ymin.y = C.y - radius_A;
507
0
  xmax.x = C.x + radius_A;
508
0
  xmax.y = C.y;
509
0
  ymax.x = C.x;
510
0
  ymax.y = C.y + radius_A;
511
512
  /* Divide the circle into two parts, one on each side of a line
513
     joining p1 and p3. The circle extrema on the same side of that line
514
     as p2 is on, are also the extrema of the bbox. */
515
516
0
  A2_side = lw_segment_side(A1, A3, A2);
517
518
0
  if ( A2_side == lw_segment_side(A1, A3, &xmin) )
519
0
    gbox->xmin = xmin.x;
520
521
0
  if ( A2_side == lw_segment_side(A1, A3, &ymin) )
522
0
    gbox->ymin = ymin.y;
523
524
0
  if ( A2_side == lw_segment_side(A1, A3, &xmax) )
525
0
    gbox->xmax = xmax.x;
526
527
0
  if ( A2_side == lw_segment_side(A1, A3, &ymax) )
528
0
    gbox->ymax = ymax.y;
529
530
0
  return LW_SUCCESS;
531
0
}
532
533
534
static int lw_arc_calculate_gbox_cartesian(const POINT4D *p1, const POINT4D *p2, const POINT4D *p3, GBOX *gbox)
535
0
{
536
0
  int rv;
537
538
0
  LWDEBUG(2, "lw_arc_calculate_gbox_cartesian called.");
539
540
0
  rv = lw_arc_calculate_gbox_cartesian_2d((POINT2D*)p1, (POINT2D*)p2, (POINT2D*)p3, gbox);
541
0
    gbox->zmin = FP_MIN(p1->z, p3->z);
542
0
    gbox->mmin = FP_MIN(p1->m, p3->m);
543
0
    gbox->zmax = FP_MAX(p1->z, p3->z);
544
0
    gbox->mmax = FP_MAX(p1->m, p3->m);
545
0
  return rv;
546
0
}
547
548
static void
549
ptarray_calculate_gbox_cartesian_2d(const POINTARRAY *pa, GBOX *gbox)
550
3.58k
{
551
3.58k
  const POINT2D *p = getPoint2d_cp(pa, 0);
552
553
3.58k
  gbox->xmax = gbox->xmin = p->x;
554
3.58k
  gbox->ymax = gbox->ymin = p->y;
555
556
112k
  for (uint32_t i = 1; i < pa->npoints; i++)
557
109k
  {
558
109k
    p = getPoint2d_cp(pa, i);
559
109k
    gbox->xmin = FP_MIN(gbox->xmin, p->x);
560
109k
    gbox->xmax = FP_MAX(gbox->xmax, p->x);
561
109k
    gbox->ymin = FP_MIN(gbox->ymin, p->y);
562
109k
    gbox->ymax = FP_MAX(gbox->ymax, p->y);
563
109k
  }
564
3.58k
}
565
566
/* Works with X/Y/Z. Needs to be adjusted after if X/Y/M was required */
567
static void
568
ptarray_calculate_gbox_cartesian_3d(const POINTARRAY *pa, GBOX *gbox)
569
1.28k
{
570
1.28k
  const POINT3D *p = getPoint3d_cp(pa, 0);
571
572
1.28k
  gbox->xmax = gbox->xmin = p->x;
573
1.28k
  gbox->ymax = gbox->ymin = p->y;
574
1.28k
  gbox->zmax = gbox->zmin = p->z;
575
576
29.6k
  for (uint32_t i = 1; i < pa->npoints; i++)
577
28.3k
  {
578
28.3k
    p = getPoint3d_cp(pa, i);
579
28.3k
    gbox->xmin = FP_MIN(gbox->xmin, p->x);
580
28.3k
    gbox->xmax = FP_MAX(gbox->xmax, p->x);
581
28.3k
    gbox->ymin = FP_MIN(gbox->ymin, p->y);
582
28.3k
    gbox->ymax = FP_MAX(gbox->ymax, p->y);
583
28.3k
    gbox->zmin = FP_MIN(gbox->zmin, p->z);
584
28.3k
    gbox->zmax = FP_MAX(gbox->zmax, p->z);
585
28.3k
  }
586
1.28k
}
587
588
static void
589
ptarray_calculate_gbox_cartesian_4d(const POINTARRAY *pa, GBOX *gbox)
590
0
{
591
0
  const POINT4D *p = getPoint4d_cp(pa, 0);
592
593
0
  gbox->xmax = gbox->xmin = p->x;
594
0
  gbox->ymax = gbox->ymin = p->y;
595
0
  gbox->zmax = gbox->zmin = p->z;
596
0
  gbox->mmax = gbox->mmin = p->m;
597
598
0
  for (uint32_t i = 1; i < pa->npoints; i++)
599
0
  {
600
0
    p = getPoint4d_cp(pa, i);
601
0
    gbox->xmin = FP_MIN(gbox->xmin, p->x);
602
0
    gbox->xmax = FP_MAX(gbox->xmax, p->x);
603
0
    gbox->ymin = FP_MIN(gbox->ymin, p->y);
604
0
    gbox->ymax = FP_MAX(gbox->ymax, p->y);
605
0
    gbox->zmin = FP_MIN(gbox->zmin, p->z);
606
0
    gbox->zmax = FP_MAX(gbox->zmax, p->z);
607
0
    gbox->mmin = FP_MIN(gbox->mmin, p->m);
608
0
    gbox->mmax = FP_MAX(gbox->mmax, p->m);
609
0
  }
610
0
}
611
612
int
613
ptarray_calculate_gbox_cartesian(const POINTARRAY *pa, GBOX *gbox)
614
154k
{
615
154k
  if (!pa || pa->npoints == 0)
616
149k
    return LW_FAILURE;
617
4.87k
  if (!gbox)
618
0
    return LW_FAILURE;
619
620
4.87k
  int has_z = FLAGS_GET_Z(pa->flags);
621
4.87k
  int has_m = FLAGS_GET_M(pa->flags);
622
4.87k
  gbox->flags = lwflags(has_z, has_m, 0);
623
4.87k
  LWDEBUGF(4, "ptarray_calculate_gbox Z: %d M: %d", has_z, has_m);
624
4.87k
  int coordinates = 2 + has_z + has_m;
625
626
4.87k
  switch (coordinates)
627
4.87k
  {
628
3.58k
  case 2:
629
3.58k
  {
630
3.58k
    ptarray_calculate_gbox_cartesian_2d(pa, gbox);
631
3.58k
    break;
632
0
  }
633
1.28k
  case 3:
634
1.28k
  {
635
1.28k
    if (has_z)
636
1.28k
    {
637
1.28k
      ptarray_calculate_gbox_cartesian_3d(pa, gbox);
638
1.28k
    }
639
0
    else
640
0
    {
641
0
      double zmin = gbox->zmin;
642
0
      double zmax = gbox->zmax;
643
0
      ptarray_calculate_gbox_cartesian_3d(pa, gbox);
644
0
      gbox->mmin = gbox->zmin;
645
0
      gbox->mmax = gbox->zmax;
646
0
      gbox->zmin = zmin;
647
0
      gbox->zmax = zmax;
648
0
    }
649
1.28k
    break;
650
0
  }
651
0
  default:
652
0
  {
653
0
    ptarray_calculate_gbox_cartesian_4d(pa, gbox);
654
0
    break;
655
0
  }
656
4.87k
  }
657
4.87k
  return LW_SUCCESS;
658
4.87k
}
659
660
static int lwcircstring_calculate_gbox_cartesian(LWCIRCSTRING *curve, GBOX *gbox)
661
0
{
662
0
  GBOX tmp = {0};
663
0
  POINT4D p1, p2, p3;
664
0
  uint32_t i;
665
666
0
  if (!curve) return LW_FAILURE;
667
0
  if (curve->points->npoints < 3) return LW_FAILURE;
668
669
0
  tmp.flags =
670
0
      lwflags(FLAGS_GET_Z(curve->flags), FLAGS_GET_M(curve->flags), 0);
671
672
  /* Initialize */
673
0
  gbox->xmin = gbox->ymin = gbox->zmin = gbox->mmin = FLT_MAX;
674
0
  gbox->xmax = gbox->ymax = gbox->zmax = gbox->mmax = -1*FLT_MAX;
675
676
0
  for ( i = 2; i < curve->points->npoints; i += 2 )
677
0
  {
678
0
    getPoint4d_p(curve->points, i-2, &p1);
679
0
    getPoint4d_p(curve->points, i-1, &p2);
680
0
    getPoint4d_p(curve->points, i, &p3);
681
682
0
    if (lw_arc_calculate_gbox_cartesian(&p1, &p2, &p3, &tmp) == LW_FAILURE)
683
0
      continue;
684
685
0
    gbox_merge(&tmp, gbox);
686
0
  }
687
688
0
  return LW_SUCCESS;
689
0
}
690
691
static int lwpoint_calculate_gbox_cartesian(LWPOINT *point, GBOX *gbox)
692
135k
{
693
135k
  if ( ! point ) return LW_FAILURE;
694
135k
  return ptarray_calculate_gbox_cartesian( point->point, gbox );
695
135k
}
696
697
static int lwline_calculate_gbox_cartesian(LWLINE *line, GBOX *gbox)
698
17.8k
{
699
17.8k
  if ( ! line ) return LW_FAILURE;
700
17.8k
  return ptarray_calculate_gbox_cartesian( line->points, gbox );
701
17.8k
}
702
703
static int lwtriangle_calculate_gbox_cartesian(LWTRIANGLE *triangle, GBOX *gbox)
704
0
{
705
0
  if ( ! triangle ) return LW_FAILURE;
706
0
  return ptarray_calculate_gbox_cartesian( triangle->points, gbox );
707
0
}
708
709
static int lwpoly_calculate_gbox_cartesian(LWPOLY *poly, GBOX *gbox)
710
1.75k
{
711
1.75k
  if ( ! poly ) return LW_FAILURE;
712
1.75k
  if ( poly->nrings == 0 ) return LW_FAILURE;
713
  /* Just need to check outer ring */
714
893
  return ptarray_calculate_gbox_cartesian( poly->rings[0], gbox );
715
1.75k
}
716
717
static int lwcollection_calculate_gbox_cartesian(LWCOLLECTION *coll, GBOX *gbox)
718
3.96k
{
719
3.96k
  GBOX subbox = {0};
720
3.96k
  uint32_t i;
721
3.96k
  int result = LW_FAILURE;
722
3.96k
  int first = LW_TRUE;
723
3.96k
  assert(coll);
724
3.96k
  if ( (coll->ngeoms == 0) || !gbox)
725
2.01k
    return LW_FAILURE;
726
727
1.95k
  subbox.flags = coll->flags;
728
729
160k
  for ( i = 0; i < coll->ngeoms; i++ )
730
158k
  {
731
158k
    if ( lwgeom_calculate_gbox_cartesian((LWGEOM*)(coll->geoms[i]), &subbox) == LW_SUCCESS )
732
6.00k
    {
733
      /* Keep a copy of the sub-bounding box for later
734
      if ( coll->geoms[i]->bbox )
735
        lwfree(coll->geoms[i]->bbox);
736
      coll->geoms[i]->bbox = gbox_copy(&subbox); */
737
6.00k
      if ( first )
738
1.75k
      {
739
1.75k
        gbox_duplicate(&subbox, gbox);
740
1.75k
        first = LW_FALSE;
741
1.75k
      }
742
4.24k
      else
743
4.24k
      {
744
4.24k
        gbox_merge(&subbox, gbox);
745
4.24k
      }
746
6.00k
      result = LW_SUCCESS;
747
6.00k
    }
748
158k
  }
749
1.95k
  return result;
750
3.96k
}
751
752
int lwgeom_calculate_gbox_cartesian(const LWGEOM *lwgeom, GBOX *gbox)
753
159k
{
754
159k
  if ( ! lwgeom ) return LW_FAILURE;
755
159k
  LWDEBUGF(4, "lwgeom_calculate_gbox got type (%d) - %s", lwgeom->type, lwtype_name(lwgeom->type));
756
757
159k
  switch (lwgeom->type)
758
159k
  {
759
135k
  case POINTTYPE:
760
135k
    return lwpoint_calculate_gbox_cartesian((LWPOINT *)lwgeom, gbox);
761
17.8k
  case LINETYPE:
762
17.8k
    return lwline_calculate_gbox_cartesian((LWLINE *)lwgeom, gbox);
763
0
  case CIRCSTRINGTYPE:
764
0
    return lwcircstring_calculate_gbox_cartesian((LWCIRCSTRING *)lwgeom, gbox);
765
1.75k
  case POLYGONTYPE:
766
1.75k
    return lwpoly_calculate_gbox_cartesian((LWPOLY *)lwgeom, gbox);
767
0
  case TRIANGLETYPE:
768
0
    return lwtriangle_calculate_gbox_cartesian((LWTRIANGLE *)lwgeom, gbox);
769
0
  case COMPOUNDTYPE:
770
0
  case CURVEPOLYTYPE:
771
2.08k
  case MULTIPOINTTYPE:
772
2.57k
  case MULTILINETYPE:
773
2.57k
  case MULTICURVETYPE:
774
3.11k
  case MULTIPOLYGONTYPE:
775
3.11k
  case MULTISURFACETYPE:
776
3.11k
  case POLYHEDRALSURFACETYPE:
777
3.11k
  case TINTYPE:
778
3.96k
  case COLLECTIONTYPE:
779
3.96k
    return lwcollection_calculate_gbox_cartesian((LWCOLLECTION *)lwgeom, gbox);
780
159k
  }
781
  /* Never get here, please. */
782
0
  lwerror("unsupported type (%d) - %s", lwgeom->type, lwtype_name(lwgeom->type));
783
0
  return LW_FAILURE;
784
159k
}
785
786
void gbox_float_round(GBOX *gbox)
787
0
{
788
0
  gbox->xmin = next_float_down(gbox->xmin);
789
0
  gbox->xmax = next_float_up(gbox->xmax);
790
791
0
  gbox->ymin = next_float_down(gbox->ymin);
792
0
  gbox->ymax = next_float_up(gbox->ymax);
793
794
0
  if ( FLAGS_GET_M(gbox->flags) )
795
0
  {
796
0
    gbox->mmin = next_float_down(gbox->mmin);
797
0
    gbox->mmax = next_float_up(gbox->mmax);
798
0
  }
799
800
0
  if ( FLAGS_GET_Z(gbox->flags) )
801
0
  {
802
0
    gbox->zmin = next_float_down(gbox->zmin);
803
0
    gbox->zmax = next_float_up(gbox->zmax);
804
0
  }
805
0
}
806
807
uint64_t
808
gbox_get_sortable_hash(const GBOX *g, const int32_t srid)
809
0
{
810
0
  union floatuint {
811
0
    uint32_t u;
812
0
    float f;
813
0
  };
814
815
0
  union floatuint x, y;
816
817
  /*
818
  * Since in theory the bitwise representation of an IEEE
819
  * float is sortable (exponents come before mantissa, etc)
820
  * we just copy the bits directly into an int and then
821
  * interleave those ints.
822
  */
823
0
  if (FLAGS_GET_GEODETIC(g->flags))
824
0
  {
825
0
    GEOGRAPHIC_POINT gpt;
826
0
    POINT3D p;
827
0
    p.x = (g->xmax + g->xmin) / 2.0;
828
0
    p.y = (g->ymax + g->ymin) / 2.0;
829
0
    p.z = (g->zmax + g->zmin) / 2.0;
830
0
    normalize(&p);
831
0
    cart2geog(&p, &gpt);
832
    /* We know range for geography, so build the curve taking it into account */
833
0
    x.f = 1.5 + gpt.lon / 512.0;
834
0
    y.f = 1.5 + gpt.lat / 256.0;
835
0
  }
836
0
  else
837
0
  {
838
0
    x.f = (g->xmax + g->xmin) / 2;
839
0
    y.f = (g->ymax + g->ymin) / 2;
840
    /*
841
     * Tweak for popular SRID values: push floating point values into 1..2 range,
842
     * a region where exponent is constant and thus Hilbert curve
843
     * doesn't have compression artifact when X or Y value is close to 0.
844
     * If someone has out of bounds value it will still expose the arifact but not crash.
845
     * TODO: reconsider when we will have machinery to properly get bounds by SRID.
846
     */
847
0
    if (srid == 3857 || srid == 3395)
848
0
    {
849
0
      x.f = 1.5 + x.f / 67108864.0;
850
0
      y.f = 1.5 + y.f / 67108864.0;
851
0
    }
852
0
    else if (srid == 4326)
853
0
    {
854
0
      x.f = 1.5 + x.f / 512.0;
855
0
      y.f = 1.5 + y.f / 256.0;
856
0
    }
857
0
  }
858
859
0
  return uint32_hilbert(y.u, x.u);
860
0
}