PostGIS  3.0.6dev-r@@SVN_REVISION@@
gbox.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * PostGIS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * PostGIS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18  *
19  **********************************************************************
20  *
21  * Copyright 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 
33 {
34  GBOX *g = (GBOX*)lwalloc(sizeof(GBOX));
35  gbox_init(g);
36  g->flags = flags;
37  return g;
38 }
39 
40 void gbox_init(GBOX *gbox)
41 {
42  memset(gbox, 0, sizeof(GBOX));
43 }
44 
45 GBOX* gbox_clone(const GBOX *gbox)
46 {
47  GBOX *g = lwalloc(sizeof(GBOX));
48  memcpy(g, gbox, sizeof(GBOX));
49  return g;
50 }
51 
52 /* TODO to be removed */
53 BOX3D* box3d_from_gbox(const GBOX *gbox)
54 {
55  BOX3D *b;
56  assert(gbox);
57 
58  b = lwalloc(sizeof(BOX3D));
59 
60  b->xmin = gbox->xmin;
61  b->xmax = gbox->xmax;
62  b->ymin = gbox->ymin;
63  b->ymax = gbox->ymax;
64 
65  if ( FLAGS_GET_Z(gbox->flags) )
66  {
67  b->zmin = gbox->zmin;
68  b->zmax = gbox->zmax;
69  }
70  else
71  {
72  b->zmin = b->zmax = 0.0;
73  }
74 
75  b->srid = SRID_UNKNOWN;
76  return b;
77 }
78 
79 /* TODO to be removed */
80 GBOX* box3d_to_gbox(const BOX3D *b3d)
81 {
82  GBOX *b;
83  assert(b3d);
84 
85  b = lwalloc(sizeof(GBOX));
86 
87  b->xmin = b3d->xmin;
88  b->xmax = b3d->xmax;
89  b->ymin = b3d->ymin;
90  b->ymax = b3d->ymax;
91  b->zmin = b3d->zmin;
92  b->zmax = b3d->zmax;
93 
94  return b;
95 }
96 
97 void gbox_expand(GBOX *g, double d)
98 {
99  g->xmin -= d;
100  g->xmax += d;
101  g->ymin -= d;
102  g->ymax += d;
103  if (FLAGS_GET_Z(g->flags) || FLAGS_GET_GEODETIC(g->flags))
104  {
105  g->zmin -= d;
106  g->zmax += d;
107  }
108  if (FLAGS_GET_M(g->flags))
109  {
110  g->mmin -= d;
111  g->mmax += d;
112  }
113 }
114 
115 void gbox_expand_xyzm(GBOX *g, double dx, double dy, double dz, double dm)
116 {
117  g->xmin -= dx;
118  g->xmax += dx;
119  g->ymin -= dy;
120  g->ymax += dy;
121 
122  if (FLAGS_GET_Z(g->flags))
123  {
124  g->zmin -= dz;
125  g->zmax += dz;
126  }
127 
128  if (FLAGS_GET_M(g->flags))
129  {
130  g->mmin -= dm;
131  g->mmax += dm;
132  }
133 }
134 
135 int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout)
136 {
137  if ( ( ! g1 ) && ( ! g2 ) )
138  return LW_FALSE;
139  else if (!g1)
140  {
141  memcpy(gout, g2, sizeof(GBOX));
142  return LW_TRUE;
143  }
144  else if (!g2)
145  {
146  memcpy(gout, g1, sizeof(GBOX));
147  return LW_TRUE;
148  }
149 
150  gout->flags = g1->flags;
151 
152  gout->xmin = FP_MIN(g1->xmin, g2->xmin);
153  gout->xmax = FP_MAX(g1->xmax, g2->xmax);
154 
155  gout->ymin = FP_MIN(g1->ymin, g2->ymin);
156  gout->ymax = FP_MAX(g1->ymax, g2->ymax);
157 
158  gout->zmin = FP_MIN(g1->zmin, g2->zmin);
159  gout->zmax = FP_MAX(g1->zmax, g2->zmax);
160 
161  return LW_TRUE;
162 }
163 
164 int gbox_same(const GBOX *g1, const GBOX *g2)
165 {
166  if (FLAGS_GET_ZM(g1->flags) != FLAGS_GET_ZM(g2->flags))
167  return LW_FALSE;
168 
169  if (!gbox_same_2d(g1, g2)) return LW_FALSE;
170 
171  if (FLAGS_GET_Z(g1->flags) && (g1->zmin != g2->zmin || g1->zmax != g2->zmax))
172  return LW_FALSE;
173  if (FLAGS_GET_M(g1->flags) && (g1->mmin != g2->mmin || g1->mmax != g2->mmax))
174  return LW_FALSE;
175 
176  return LW_TRUE;
177 }
178 
179 int gbox_same_2d(const GBOX *g1, const GBOX *g2)
180 {
181  if (g1->xmin == g2->xmin && g1->ymin == g2->ymin &&
182  g1->xmax == g2->xmax && g1->ymax == g2->ymax)
183  return LW_TRUE;
184  return LW_FALSE;
185 }
186 
187 int gbox_same_2d_float(const GBOX *g1, const GBOX *g2)
188 {
189  if ((g1->xmax == g2->xmax || next_float_up(g1->xmax) == next_float_up(g2->xmax)) &&
190  (g1->ymax == g2->ymax || next_float_up(g1->ymax) == next_float_up(g2->ymax)) &&
191  (g1->xmin == g2->xmin || next_float_down(g1->xmin) == next_float_down(g1->xmin)) &&
192  (g1->ymin == g2->ymin || next_float_down(g2->ymin) == next_float_down(g2->ymin)))
193  return LW_TRUE;
194  return LW_FALSE;
195 }
196 
197 int gbox_is_valid(const GBOX *gbox)
198 {
199  /* X */
200  if ( ! isfinite(gbox->xmin) || isnan(gbox->xmin) ||
201  ! isfinite(gbox->xmax) || isnan(gbox->xmax) )
202  return LW_FALSE;
203 
204  /* Y */
205  if ( ! isfinite(gbox->ymin) || isnan(gbox->ymin) ||
206  ! isfinite(gbox->ymax) || isnan(gbox->ymax) )
207  return LW_FALSE;
208 
209  /* Z */
210  if ( FLAGS_GET_GEODETIC(gbox->flags) || FLAGS_GET_Z(gbox->flags) )
211  {
212  if ( ! isfinite(gbox->zmin) || isnan(gbox->zmin) ||
213  ! isfinite(gbox->zmax) || isnan(gbox->zmax) )
214  return LW_FALSE;
215  }
216 
217  /* M */
218  if ( FLAGS_GET_M(gbox->flags) )
219  {
220  if ( ! isfinite(gbox->mmin) || isnan(gbox->mmin) ||
221  ! isfinite(gbox->mmax) || isnan(gbox->mmax) )
222  return LW_FALSE;
223  }
224 
225  return LW_TRUE;
226 }
227 
228 int gbox_merge_point3d(const POINT3D *p, GBOX *gbox)
229 {
230  if ( gbox->xmin > p->x ) gbox->xmin = p->x;
231  if ( gbox->ymin > p->y ) gbox->ymin = p->y;
232  if ( gbox->zmin > p->z ) gbox->zmin = p->z;
233  if ( gbox->xmax < p->x ) gbox->xmax = p->x;
234  if ( gbox->ymax < p->y ) gbox->ymax = p->y;
235  if ( gbox->zmax < p->z ) gbox->zmax = p->z;
236  return LW_SUCCESS;
237 }
238 
239 int gbox_init_point3d(const POINT3D *p, GBOX *gbox)
240 {
241  gbox->xmin = gbox->xmax = p->x;
242  gbox->ymin = gbox->ymax = p->y;
243  gbox->zmin = gbox->zmax = p->z;
244  return LW_SUCCESS;
245 }
246 
247 int gbox_contains_point3d(const GBOX *gbox, const POINT3D *pt)
248 {
249  if ( gbox->xmin > pt->x || gbox->ymin > pt->y || gbox->zmin > pt->z ||
250  gbox->xmax < pt->x || gbox->ymax < pt->y || gbox->zmax < pt->z )
251  {
252  return LW_FALSE;
253  }
254  return LW_TRUE;
255 }
256 
257 int gbox_merge(const GBOX *new_box, GBOX *merge_box)
258 {
259  assert(merge_box);
260 
261  if ( FLAGS_GET_ZM(merge_box->flags) != FLAGS_GET_ZM(new_box->flags) )
262  return LW_FAILURE;
263 
264  if ( new_box->xmin < merge_box->xmin) merge_box->xmin = new_box->xmin;
265  if ( new_box->ymin < merge_box->ymin) merge_box->ymin = new_box->ymin;
266  if ( new_box->xmax > merge_box->xmax) merge_box->xmax = new_box->xmax;
267  if ( new_box->ymax > merge_box->ymax) merge_box->ymax = new_box->ymax;
268 
269  if ( FLAGS_GET_Z(merge_box->flags) || FLAGS_GET_GEODETIC(merge_box->flags) )
270  {
271  if ( new_box->zmin < merge_box->zmin) merge_box->zmin = new_box->zmin;
272  if ( new_box->zmax > merge_box->zmax) merge_box->zmax = new_box->zmax;
273  }
274  if ( FLAGS_GET_M(merge_box->flags) )
275  {
276  if ( new_box->mmin < merge_box->mmin) merge_box->mmin = new_box->mmin;
277  if ( new_box->mmax > merge_box->mmax) merge_box->mmax = new_box->mmax;
278  }
279 
280  return LW_SUCCESS;
281 }
282 
283 int gbox_overlaps(const GBOX *g1, const GBOX *g2)
284 {
285 
286  /* Make sure our boxes are consistent */
288  lwerror("gbox_overlaps: cannot compare geodetic and non-geodetic boxes");
289 
290  /* Check X/Y first */
291  if ( g1->xmax < g2->xmin || g1->ymax < g2->ymin ||
292  g1->xmin > g2->xmax || g1->ymin > g2->ymax )
293  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 */
298  {
299  if ( g1->zmax < g2->zmin || g1->zmin > g2->zmax )
300  return LW_FALSE;
301  else
302  return LW_TRUE;
303  }
304 
305  /* If both geodetic or both have Z, check Z */
306  if ( FLAGS_GET_Z(g1->flags) && FLAGS_GET_Z(g2->flags) )
307  {
308  if ( g1->zmax < g2->zmin || g1->zmin > g2->zmax )
309  return LW_FALSE;
310  }
311 
312  /* If both have M, check M */
313  if ( FLAGS_GET_M(g1->flags) && FLAGS_GET_M(g2->flags) )
314  {
315  if ( g1->mmax < g2->mmin || g1->mmin > g2->mmax )
316  return LW_FALSE;
317  }
318 
319  return LW_TRUE;
320 }
321 
322 int
323 gbox_overlaps_2d(const GBOX *g1, const GBOX *g2)
324 {
325 
326  /* Make sure our boxes are consistent */
328  lwerror("gbox_overlaps: cannot compare geodetic and non-geodetic boxes");
329 
330  /* Check X/Y first */
331  if ( g1->xmax < g2->xmin || g1->ymax < g2->ymin ||
332  g1->xmin > g2->xmax || g1->ymin > g2->ymax )
333  return LW_FALSE;
334 
335  return LW_TRUE;
336 }
337 
338 int
339 gbox_contains_2d(const GBOX *g1, const GBOX *g2)
340 {
341  if ( ( g2->xmin < g1->xmin ) || ( g2->xmax > g1->xmax ) ||
342  ( g2->ymin < g1->ymin ) || ( g2->ymax > g1->ymax ) )
343  {
344  return LW_FALSE;
345  }
346  return LW_TRUE;
347 }
348 
349 int
350 gbox_contains_point2d(const GBOX *g, const POINT2D *p)
351 {
352  if ( ( g->xmin <= p->x ) && ( g->xmax >= p->x ) &&
353  ( g->ymin <= p->y ) && ( g->ymax >= p->y ) )
354  {
355  return LW_TRUE;
356  }
357  return LW_FALSE;
358 }
359 
364 GBOX* gbox_from_string(const char *str)
365 {
366  const char *ptr = str;
367  char *nextptr;
368  char *gbox_start = strstr(str, "GBOX((");
369  GBOX *gbox = gbox_new(lwflags(0,0,1));
370  if ( ! gbox_start ) return NULL; /* No header found */
371  ptr += 6;
372  gbox->xmin = strtod(ptr, &nextptr);
373  if ( ptr == nextptr ) return NULL; /* No double found */
374  ptr = nextptr + 1;
375  gbox->ymin = strtod(ptr, &nextptr);
376  if ( ptr == nextptr ) return NULL; /* No double found */
377  ptr = nextptr + 1;
378  gbox->zmin = strtod(ptr, &nextptr);
379  if ( ptr == nextptr ) return NULL; /* No double found */
380  ptr = nextptr + 3;
381  gbox->xmax = strtod(ptr, &nextptr);
382  if ( ptr == nextptr ) return NULL; /* No double found */
383  ptr = nextptr + 1;
384  gbox->ymax = strtod(ptr, &nextptr);
385  if ( ptr == nextptr ) return NULL; /* No double found */
386  ptr = nextptr + 1;
387  gbox->zmax = strtod(ptr, &nextptr);
388  if ( ptr == nextptr ) return NULL; /* No double found */
389  return gbox;
390 }
391 
392 char* gbox_to_string(const GBOX *gbox)
393 {
394  const size_t sz = 138;
395  char *str = NULL;
396 
397  if ( ! gbox )
398  return lwstrdup("NULL POINTER");
399 
400  str = (char*)lwalloc(sz);
401 
402  if ( FLAGS_GET_GEODETIC(gbox->flags) )
403  {
404  snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->xmax, gbox->ymax, gbox->zmax);
405  return str;
406  }
407  if ( FLAGS_GET_Z(gbox->flags) && FLAGS_GET_M(gbox->flags) )
408  {
409  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);
410  return str;
411  }
412  if ( FLAGS_GET_Z(gbox->flags) )
413  {
414  snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->xmax, gbox->ymax, gbox->zmax);
415  return str;
416  }
417  if ( FLAGS_GET_M(gbox->flags) )
418  {
419  snprintf(str, sz, "GBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->mmin, gbox->xmax, gbox->ymax, gbox->mmax);
420  return str;
421  }
422  snprintf(str, sz, "GBOX((%.8g,%.8g),(%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->xmax, gbox->ymax);
423  return str;
424 }
425 
426 GBOX* gbox_copy(const GBOX *box)
427 {
428  GBOX *copy = (GBOX*)lwalloc(sizeof(GBOX));
429  memcpy(copy, box, sizeof(GBOX));
430  return copy;
431 }
432 
433 void gbox_duplicate(const GBOX *original, GBOX *duplicate)
434 {
435  assert(duplicate);
436  assert(original);
437  memcpy(duplicate, original, sizeof(GBOX));
438 }
439 
441 {
442  if (FLAGS_GET_GEODETIC(flags))
443  return 6 * sizeof(float);
444  else
445  return 2 * FLAGS_NDIMS(flags) * sizeof(float);
446 }
447 
448 
449 /* ********************************************************************************
450 ** Compute cartesian bounding GBOX boxes from LWGEOM.
451 */
452 
453 int lw_arc_calculate_gbox_cartesian_2d(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, GBOX *gbox)
454 {
455  POINT2D xmin, ymin, xmax, ymax;
456  POINT2D C;
457  int A2_side;
458  double radius_A;
459 
460  LWDEBUG(2, "lw_arc_calculate_gbox_cartesian_2d called.");
461 
462  radius_A = lw_arc_center(A1, A2, A3, &C);
463 
464  /* Negative radius signals straight line, p1/p2/p3 are collinear */
465  if (radius_A < 0.0)
466  {
467  gbox->xmin = FP_MIN(A1->x, A3->x);
468  gbox->ymin = FP_MIN(A1->y, A3->y);
469  gbox->xmax = FP_MAX(A1->x, A3->x);
470  gbox->ymax = FP_MAX(A1->y, A3->y);
471  return LW_SUCCESS;
472  }
473 
474  /* Matched start/end points imply circle */
475  if ( A1->x == A3->x && A1->y == A3->y )
476  {
477  gbox->xmin = C.x - radius_A;
478  gbox->ymin = C.y - radius_A;
479  gbox->xmax = C.x + radius_A;
480  gbox->ymax = C.y + radius_A;
481  return LW_SUCCESS;
482  }
483 
484  /* First approximation, bounds of start/end points */
485  gbox->xmin = FP_MIN(A1->x, A3->x);
486  gbox->ymin = FP_MIN(A1->y, A3->y);
487  gbox->xmax = FP_MAX(A1->x, A3->x);
488  gbox->ymax = FP_MAX(A1->y, A3->y);
489 
490  /* Create points for the possible extrema */
491  xmin.x = C.x - radius_A;
492  xmin.y = C.y;
493  ymin.x = C.x;
494  ymin.y = C.y - radius_A;
495  xmax.x = C.x + radius_A;
496  xmax.y = C.y;
497  ymax.x = C.x;
498  ymax.y = C.y + radius_A;
499 
500  /* Divide the circle into two parts, one on each side of a line
501  joining p1 and p3. The circle extrema on the same side of that line
502  as p2 is on, are also the extrema of the bbox. */
503 
504  A2_side = lw_segment_side(A1, A3, A2);
505 
506  if ( A2_side == lw_segment_side(A1, A3, &xmin) )
507  gbox->xmin = xmin.x;
508 
509  if ( A2_side == lw_segment_side(A1, A3, &ymin) )
510  gbox->ymin = ymin.y;
511 
512  if ( A2_side == lw_segment_side(A1, A3, &xmax) )
513  gbox->xmax = xmax.x;
514 
515  if ( A2_side == lw_segment_side(A1, A3, &ymax) )
516  gbox->ymax = ymax.y;
517 
518  return LW_SUCCESS;
519 }
520 
521 
522 static int lw_arc_calculate_gbox_cartesian(const POINT4D *p1, const POINT4D *p2, const POINT4D *p3, GBOX *gbox)
523 {
524  int rv;
525 
526  LWDEBUG(2, "lw_arc_calculate_gbox_cartesian called.");
527 
528  rv = lw_arc_calculate_gbox_cartesian_2d((POINT2D*)p1, (POINT2D*)p2, (POINT2D*)p3, gbox);
529  gbox->zmin = FP_MIN(p1->z, p3->z);
530  gbox->mmin = FP_MIN(p1->m, p3->m);
531  gbox->zmax = FP_MAX(p1->z, p3->z);
532  gbox->mmax = FP_MAX(p1->m, p3->m);
533  return rv;
534 }
535 
536 static void
538 {
539  const POINT2D *p = getPoint2d_cp(pa, 0);
540 
541  gbox->xmax = gbox->xmin = p->x;
542  gbox->ymax = gbox->ymin = p->y;
543 
544  for (uint32_t i = 1; i < pa->npoints; i++)
545  {
546  p = getPoint2d_cp(pa, i);
547  gbox->xmin = FP_MIN(gbox->xmin, p->x);
548  gbox->xmax = FP_MAX(gbox->xmax, p->x);
549  gbox->ymin = FP_MIN(gbox->ymin, p->y);
550  gbox->ymax = FP_MAX(gbox->ymax, p->y);
551  }
552 }
553 
554 /* Works with X/Y/Z. Needs to be adjusted after if X/Y/M was required */
555 static void
557 {
558  const POINT3D *p = getPoint3d_cp(pa, 0);
559 
560  gbox->xmax = gbox->xmin = p->x;
561  gbox->ymax = gbox->ymin = p->y;
562  gbox->zmax = gbox->zmin = p->z;
563 
564  for (uint32_t i = 1; i < pa->npoints; i++)
565  {
566  p = getPoint3d_cp(pa, i);
567  gbox->xmin = FP_MIN(gbox->xmin, p->x);
568  gbox->xmax = FP_MAX(gbox->xmax, p->x);
569  gbox->ymin = FP_MIN(gbox->ymin, p->y);
570  gbox->ymax = FP_MAX(gbox->ymax, p->y);
571  gbox->zmin = FP_MIN(gbox->zmin, p->z);
572  gbox->zmax = FP_MAX(gbox->zmax, p->z);
573  }
574 }
575 
576 static void
578 {
579  const POINT4D *p = getPoint4d_cp(pa, 0);
580 
581  gbox->xmax = gbox->xmin = p->x;
582  gbox->ymax = gbox->ymin = p->y;
583  gbox->zmax = gbox->zmin = p->z;
584  gbox->mmax = gbox->mmin = p->m;
585 
586  for (uint32_t i = 1; i < pa->npoints; i++)
587  {
588  p = getPoint4d_cp(pa, i);
589  gbox->xmin = FP_MIN(gbox->xmin, p->x);
590  gbox->xmax = FP_MAX(gbox->xmax, p->x);
591  gbox->ymin = FP_MIN(gbox->ymin, p->y);
592  gbox->ymax = FP_MAX(gbox->ymax, p->y);
593  gbox->zmin = FP_MIN(gbox->zmin, p->z);
594  gbox->zmax = FP_MAX(gbox->zmax, p->z);
595  gbox->mmin = FP_MIN(gbox->mmin, p->m);
596  gbox->mmax = FP_MAX(gbox->mmax, p->m);
597  }
598 }
599 
600 int
602 {
603  if (!pa || pa->npoints == 0)
604  return LW_FAILURE;
605  if (!gbox)
606  return LW_FAILURE;
607 
608  int has_z = FLAGS_GET_Z(pa->flags);
609  int has_m = FLAGS_GET_M(pa->flags);
610  gbox->flags = lwflags(has_z, has_m, 0);
611  LWDEBUGF(4, "ptarray_calculate_gbox Z: %d M: %d", has_z, has_m);
612  int coordinates = 2 + has_z + has_m;
613 
614  switch (coordinates)
615  {
616  case 2:
617  {
619  break;
620  }
621  case 3:
622  {
623  if (has_z)
624  {
626  }
627  else
628  {
629  double zmin = gbox->zmin;
630  double zmax = gbox->zmax;
632  gbox->mmin = gbox->zmin;
633  gbox->mmax = gbox->zmax;
634  gbox->zmin = zmin;
635  gbox->zmax = zmax;
636  }
637  break;
638  }
639  default:
640  {
642  break;
643  }
644  }
645  return LW_SUCCESS;
646 }
647 
649 {
650  GBOX tmp;
651  POINT4D p1, p2, p3;
652  uint32_t i;
653 
654  if (!curve) return LW_FAILURE;
655  if (curve->points->npoints < 3) return LW_FAILURE;
656 
657  tmp.flags =
658  lwflags(FLAGS_GET_Z(curve->flags), FLAGS_GET_M(curve->flags), 0);
659 
660  /* Initialize */
661  gbox->xmin = gbox->ymin = gbox->zmin = gbox->mmin = FLT_MAX;
662  gbox->xmax = gbox->ymax = gbox->zmax = gbox->mmax = -1*FLT_MAX;
663 
664  for ( i = 2; i < curve->points->npoints; i += 2 )
665  {
666  getPoint4d_p(curve->points, i-2, &p1);
667  getPoint4d_p(curve->points, i-1, &p2);
668  getPoint4d_p(curve->points, i, &p3);
669 
670  if (lw_arc_calculate_gbox_cartesian(&p1, &p2, &p3, &tmp) == LW_FAILURE)
671  continue;
672 
673  gbox_merge(&tmp, gbox);
674  }
675 
676  return LW_SUCCESS;
677 }
678 
680 {
681  if ( ! point ) return LW_FAILURE;
682  return ptarray_calculate_gbox_cartesian( point->point, gbox );
683 }
684 
686 {
687  if ( ! line ) return LW_FAILURE;
688  return ptarray_calculate_gbox_cartesian( line->points, gbox );
689 }
690 
692 {
693  if ( ! triangle ) return LW_FAILURE;
694  return ptarray_calculate_gbox_cartesian( triangle->points, gbox );
695 }
696 
698 {
699  if ( ! poly ) return LW_FAILURE;
700  if ( poly->nrings == 0 ) return LW_FAILURE;
701  /* Just need to check outer ring */
702  return ptarray_calculate_gbox_cartesian( poly->rings[0], gbox );
703 }
704 
706 {
707  GBOX subbox;
708  uint32_t i;
709  int result = LW_FAILURE;
710  int first = LW_TRUE;
711  assert(coll);
712  if ( (coll->ngeoms == 0) || !gbox)
713  return LW_FAILURE;
714 
715  subbox.flags = coll->flags;
716 
717  for ( i = 0; i < coll->ngeoms; i++ )
718  {
719  if ( lwgeom_calculate_gbox_cartesian((LWGEOM*)(coll->geoms[i]), &subbox) == LW_SUCCESS )
720  {
721  /* Keep a copy of the sub-bounding box for later
722  if ( coll->geoms[i]->bbox )
723  lwfree(coll->geoms[i]->bbox);
724  coll->geoms[i]->bbox = gbox_copy(&subbox); */
725  if ( first )
726  {
727  gbox_duplicate(&subbox, gbox);
728  first = LW_FALSE;
729  }
730  else
731  {
732  gbox_merge(&subbox, gbox);
733  }
734  result = LW_SUCCESS;
735  }
736  }
737  return result;
738 }
739 
740 int lwgeom_calculate_gbox_cartesian(const LWGEOM *lwgeom, GBOX *gbox)
741 {
742  if ( ! lwgeom ) return LW_FAILURE;
743  LWDEBUGF(4, "lwgeom_calculate_gbox got type (%d) - %s", lwgeom->type, lwtype_name(lwgeom->type));
744 
745  switch (lwgeom->type)
746  {
747  case POINTTYPE:
748  return lwpoint_calculate_gbox_cartesian((LWPOINT *)lwgeom, gbox);
749  case LINETYPE:
750  return lwline_calculate_gbox_cartesian((LWLINE *)lwgeom, gbox);
751  case CIRCSTRINGTYPE:
752  return lwcircstring_calculate_gbox_cartesian((LWCIRCSTRING *)lwgeom, gbox);
753  case POLYGONTYPE:
754  return lwpoly_calculate_gbox_cartesian((LWPOLY *)lwgeom, gbox);
755  case TRIANGLETYPE:
756  return lwtriangle_calculate_gbox_cartesian((LWTRIANGLE *)lwgeom, gbox);
757  case COMPOUNDTYPE:
758  case CURVEPOLYTYPE:
759  case MULTIPOINTTYPE:
760  case MULTILINETYPE:
761  case MULTICURVETYPE:
762  case MULTIPOLYGONTYPE:
763  case MULTISURFACETYPE:
765  case TINTYPE:
766  case COLLECTIONTYPE:
767  return lwcollection_calculate_gbox_cartesian((LWCOLLECTION *)lwgeom, gbox);
768  }
769  /* Never get here, please. */
770  lwerror("unsupported type (%d) - %s", lwgeom->type, lwtype_name(lwgeom->type));
771  return LW_FAILURE;
772 }
773 
775 {
776  gbox->xmin = next_float_down(gbox->xmin);
777  gbox->xmax = next_float_up(gbox->xmax);
778 
779  gbox->ymin = next_float_down(gbox->ymin);
780  gbox->ymax = next_float_up(gbox->ymax);
781 
782  if ( FLAGS_GET_M(gbox->flags) )
783  {
784  gbox->mmin = next_float_down(gbox->mmin);
785  gbox->mmax = next_float_up(gbox->mmax);
786  }
787 
788  if ( FLAGS_GET_Z(gbox->flags) )
789  {
790  gbox->zmin = next_float_down(gbox->zmin);
791  gbox->zmax = next_float_up(gbox->zmax);
792  }
793 }
794 
795 inline static uint64_t
796 uint64_interleave_2(uint64_t x, uint64_t y)
797 {
798  x = (x | (x << 16)) & 0x0000FFFF0000FFFFULL;
799  x = (x | (x << 8)) & 0x00FF00FF00FF00FFULL;
800  x = (x | (x << 4)) & 0x0F0F0F0F0F0F0F0FULL;
801  x = (x | (x << 2)) & 0x3333333333333333ULL;
802  x = (x | (x << 1)) & 0x5555555555555555ULL;
803 
804  y = (y | (y << 16)) & 0x0000FFFF0000FFFFULL;
805  y = (y | (y << 8)) & 0x00FF00FF00FF00FFULL;
806  y = (y | (y << 4)) & 0x0F0F0F0F0F0F0F0FULL;
807  y = (y | (y << 2)) & 0x3333333333333333ULL;
808  y = (y | (y << 1)) & 0x5555555555555555ULL;
809 
810  return x | (y << 1);
811 }
812 
813 /* Based on https://github.com/rawrunprotected/hilbert_curves Public Domain code */
814 inline static uint64_t
815 uint32_hilbert(uint32_t px, uint32_t py)
816 {
817  uint64_t x = px;
818  uint64_t y = py;
819 
820  uint64_t A, B, C, D;
821 
822  // Initial prefix scan round, prime with x and y
823  {
824  uint64_t a = x ^ y;
825  uint64_t b = 0xFFFFFFFFULL ^ a;
826  uint64_t c = 0xFFFFFFFFULL ^ (x | y);
827  uint64_t d = x & (y ^ 0xFFFFFFFFULL);
828 
829  A = a | (b >> 1);
830  B = (a >> 1) ^ a;
831  C = ((c >> 1) ^ (b & (d >> 1))) ^ c;
832  D = ((a & (c >> 1)) ^ (d >> 1)) ^ d;
833  }
834 
835  {
836  uint64_t a = A;
837  uint64_t b = B;
838  uint64_t c = C;
839  uint64_t d = D;
840 
841  A = ((a & (a >> 2)) ^ (b & (b >> 2)));
842  B = ((a & (b >> 2)) ^ (b & ((a ^ b) >> 2)));
843  C ^= ((a & (c >> 2)) ^ (b & (d >> 2)));
844  D ^= ((b & (c >> 2)) ^ ((a ^ b) & (d >> 2)));
845  }
846 
847  {
848  uint64_t a = A;
849  uint64_t b = B;
850  uint64_t c = C;
851  uint64_t d = D;
852 
853  A = ((a & (a >> 4)) ^ (b & (b >> 4)));
854  B = ((a & (b >> 4)) ^ (b & ((a ^ b) >> 4)));
855  C ^= ((a & (c >> 4)) ^ (b & (d >> 4)));
856  D ^= ((b & (c >> 4)) ^ ((a ^ b) & (d >> 4)));
857  }
858 
859  {
860  uint64_t a = A;
861  uint64_t b = B;
862  uint64_t c = C;
863  uint64_t d = D;
864 
865  A = ((a & (a >> 8)) ^ (b & (b >> 8)));
866  B = ((a & (b >> 8)) ^ (b & ((a ^ b) >> 8)));
867  C ^= ((a & (c >> 8)) ^ (b & (d >> 8)));
868  D ^= ((b & (c >> 8)) ^ ((a ^ b) & (d >> 8)));
869  }
870 
871  {
872  uint64_t a = A;
873  uint64_t b = B;
874  uint64_t c = C;
875  uint64_t d = D;
876 
877  C ^= ((a & (c >> 16)) ^ (b & (d >> 16)));
878  D ^= ((b & (c >> 16)) ^ ((a ^ b) & (d >> 16)));
879  }
880 
881  // Undo transformation prefix scan
882  uint64_t a = C ^ (C >> 1);
883  uint64_t b = D ^ (D >> 1);
884 
885  // Recover index bits
886  uint64_t i0 = x ^ y;
887  uint64_t i1 = b | (0xFFFFFFFFULL ^ (i0 | a));
888 
889  return uint64_interleave_2(i0, i1);
890 }
891 
892 uint64_t
893 gbox_get_sortable_hash(const GBOX *g, const int32_t srid)
894 {
895  union floatuint {
896  uint32_t u;
897  float f;
898  };
899 
900  union floatuint x, y;
901 
902  /*
903  * Since in theory the bitwise representation of an IEEE
904  * float is sortable (exponents come before mantissa, etc)
905  * we just copy the bits directly into an int and then
906  * interleave those ints.
907  */
908  if (FLAGS_GET_GEODETIC(g->flags))
909  {
910  GEOGRAPHIC_POINT gpt;
911  POINT3D p;
912  p.x = (g->xmax + g->xmin) / 2.0;
913  p.y = (g->ymax + g->ymin) / 2.0;
914  p.z = (g->zmax + g->zmin) / 2.0;
915  normalize(&p);
916  cart2geog(&p, &gpt);
917  /* We know range for geography, so build the curve taking it into account */
918  x.f = 1.5 + gpt.lon / 512.0;
919  y.f = 1.5 + gpt.lat / 256.0;
920  }
921  else
922  {
923  x.f = (g->xmax + g->xmin) / 2;
924  y.f = (g->ymax + g->ymin) / 2;
925  /*
926  * Tweak for popular SRID values: push floating point values into 1..2 range,
927  * a region where exponent is constant and thus Hilbert curve
928  * doesn't have compression artifact when X or Y value is close to 0.
929  * If someone has out of bounds value it will still expose the arifact but not crash.
930  * TODO: reconsider when we will have machinery to properly get bounds by SRID.
931  */
932  if (srid == 3857 || srid == 3395)
933  {
934  x.f = 1.5 + x.f / 67108864.0;
935  y.f = 1.5 + y.f / 67108864.0;
936  }
937  else if (srid == 4326)
938  {
939  x.f = 1.5 + x.f / 512.0;
940  y.f = 1.5 + y.f / 256.0;
941  }
942  }
943 
944  return uint32_hilbert(y.u, x.u);
945 }
int gbox_merge(const GBOX *new_box, GBOX *merge_box)
Update the merged GBOX to be large enough to include itself and the new box.
Definition: gbox.c:257
int gbox_same(const GBOX *g1, const GBOX *g2)
Check if 2 given Gbox are the same.
Definition: gbox.c:164
int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout)
Update the output GBOX to be large enough to include both inputs.
Definition: gbox.c:135
size_t gbox_serialized_size(lwflags_t flags)
Return the number of bytes necessary to hold a GBOX of this dimension in serialized form.
Definition: gbox.c:440
void gbox_duplicate(const GBOX *original, GBOX *duplicate)
Copy the values of original GBOX into duplicate.
Definition: gbox.c:433
int gbox_same_2d(const GBOX *g1, const GBOX *g2)
Check if 2 given GBOX are the same in x and y.
Definition: gbox.c:179
GBOX * box3d_to_gbox(const BOX3D *b3d)
Definition: gbox.c:80
void gbox_expand(GBOX *g, double d)
Move the box minimums down and the maximums up by the distance provided.
Definition: gbox.c:97
int gbox_contains_point3d(const GBOX *gbox, const POINT3D *pt)
Return true if the point is inside the gbox.
Definition: gbox.c:247
static void ptarray_calculate_gbox_cartesian_2d(const POINTARRAY *pa, GBOX *gbox)
Definition: gbox.c:537
void gbox_float_round(GBOX *gbox)
Round given GBOX to float boundaries.
Definition: gbox.c:774
static uint64_t uint32_hilbert(uint32_t px, uint32_t py)
Definition: gbox.c:815
static int lwpoint_calculate_gbox_cartesian(LWPOINT *point, GBOX *gbox)
Definition: gbox.c:679
static int lwpoly_calculate_gbox_cartesian(LWPOLY *poly, GBOX *gbox)
Definition: gbox.c:697
static int lwtriangle_calculate_gbox_cartesian(LWTRIANGLE *triangle, GBOX *gbox)
Definition: gbox.c:691
int gbox_merge_point3d(const POINT3D *p, GBOX *gbox)
Update the GBOX to be large enough to include itself and the new point.
Definition: gbox.c:228
BOX3D * box3d_from_gbox(const GBOX *gbox)
Definition: gbox.c:53
int lwgeom_calculate_gbox_cartesian(const LWGEOM *lwgeom, GBOX *gbox)
Calculate the 2-4D bounding box of a geometry.
Definition: gbox.c:740
int gbox_overlaps(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the GBOX overlaps, LW_FALSE otherwise.
Definition: gbox.c:283
GBOX * gbox_new(lwflags_t flags)
Create a new gbox with the dimensionality indicated by the flags.
Definition: gbox.c:32
int gbox_overlaps_2d(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the GBOX overlaps on the 2d plane, LW_FALSE otherwise.
Definition: gbox.c:323
int gbox_contains_point2d(const GBOX *g, const POINT2D *p)
Definition: gbox.c:350
void gbox_init(GBOX *gbox)
Zero out all the entries in the GBOX.
Definition: gbox.c:40
void gbox_expand_xyzm(GBOX *g, double dx, double dy, double dz, double dm)
Move the box minimums down and the maximums up by the distances provided.
Definition: gbox.c:115
static void ptarray_calculate_gbox_cartesian_3d(const POINTARRAY *pa, GBOX *gbox)
Definition: gbox.c:556
GBOX * gbox_copy(const GBOX *box)
Return a copy of the GBOX, based on dimensionality of flags.
Definition: gbox.c:426
static int lwline_calculate_gbox_cartesian(LWLINE *line, GBOX *gbox)
Definition: gbox.c:685
int gbox_init_point3d(const POINT3D *p, GBOX *gbox)
Initialize a GBOX using the values of the point.
Definition: gbox.c:239
int ptarray_calculate_gbox_cartesian(const POINTARRAY *pa, GBOX *gbox)
Calculate box (x/y) and add values to gbox.
Definition: gbox.c:601
uint64_t gbox_get_sortable_hash(const GBOX *g, const int32_t srid)
Return a sortable key based on the center point of the GBOX.
Definition: gbox.c:893
static int lwcircstring_calculate_gbox_cartesian(LWCIRCSTRING *curve, GBOX *gbox)
Definition: gbox.c:648
GBOX * gbox_from_string(const char *str)
Warning, this function is only good for x/y/z boxes, used in unit testing of geodetic box generation.
Definition: gbox.c:364
static int lwcollection_calculate_gbox_cartesian(LWCOLLECTION *coll, GBOX *gbox)
Definition: gbox.c:705
static uint64_t uint64_interleave_2(uint64_t x, uint64_t y)
Definition: gbox.c:796
static void ptarray_calculate_gbox_cartesian_4d(const POINTARRAY *pa, GBOX *gbox)
Definition: gbox.c:577
GBOX * gbox_clone(const GBOX *gbox)
Definition: gbox.c:45
int gbox_same_2d_float(const GBOX *g1, const GBOX *g2)
Check if two given GBOX are the same in x and y, or would round to the same GBOX in x and if serializ...
Definition: gbox.c:187
int gbox_is_valid(const GBOX *gbox)
Return false if any of the dimensions is NaN or infinite.
Definition: gbox.c:197
int gbox_contains_2d(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the first GBOX contains the second on the 2d plane, LW_FALSE otherwise.
Definition: gbox.c:339
char * gbox_to_string(const GBOX *gbox)
Allocate a string representation of the GBOX, based on dimensionality of flags.
Definition: gbox.c:392
int lw_arc_calculate_gbox_cartesian_2d(const POINT2D *A1, const POINT2D *A2, const POINT2D *A3, GBOX *gbox)
Definition: gbox.c:453
static int lw_arc_calculate_gbox_cartesian(const POINT4D *p1, const POINT4D *p2, const POINT4D *p3, GBOX *gbox)
Definition: gbox.c:522
#define LW_FALSE
Definition: liblwgeom.h:108
#define COLLECTIONTYPE
Definition: liblwgeom.h:122
#define COMPOUNDTYPE
Definition: liblwgeom.h:124
#define LW_FAILURE
Definition: liblwgeom.h:110
#define CURVEPOLYTYPE
Definition: liblwgeom.h:125
#define MULTILINETYPE
Definition: liblwgeom.h:120
#define MULTISURFACETYPE
Definition: liblwgeom.h:127
#define LINETYPE
Definition: liblwgeom.h:117
#define LW_SUCCESS
Definition: liblwgeom.h:111
uint16_t lwflags_t
Definition: liblwgeom.h:313
#define MULTIPOINTTYPE
Definition: liblwgeom.h:119
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:116
#define FLAGS_GET_Z(flags)
Definition: liblwgeom.h:179
#define TINTYPE
Definition: liblwgeom.h:130
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:121
#define FLAGS_NDIMS(flags)
Definition: liblwgeom.h:193
#define POLYGONTYPE
Definition: liblwgeom.h:118
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:128
#define CIRCSTRINGTYPE
Definition: liblwgeom.h:123
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
#define FLAGS_GET_M(flags)
Definition: liblwgeom.h:180
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition: lwgeom_api.c:125
#define FLAGS_GET_ZM(flags)
Definition: liblwgeom.h:194
#define MULTICURVETYPE
Definition: liblwgeom.h:126
#define TRIANGLETYPE
Definition: liblwgeom.h:129
void * lwalloc(size_t size)
Definition: lwutil.c:227
float next_float_up(double d)
Definition: lwgeom_api.c:75
lwflags_t lwflags(int hasz, int hasm, int geodetic)
Construct a new flags bitmask.
Definition: lwutil.c:471
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:107
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:229
float next_float_down(double d)
Definition: lwgeom_api.c:54
#define FLAGS_GET_GEODETIC(flags)
Definition: liblwgeom.h:182
char * lwstrdup(const char *a)
Definition: lwutil.c:248
#define FP_MAX(A, B)
#define FP_MIN(A, B)
double lw_arc_center(const POINT2D *p1, const POINT2D *p2, const POINT2D *p3, POINT2D *result)
Determines the center of the circle defined by the three given points.
Definition: lwalgorithm.c:229
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
Definition: lwalgorithm.c:65
void normalize(POINT3D *p)
Normalize to a unit vector.
Definition: lwgeodetic.c:615
void cart2geog(const POINT3D *p, GEOGRAPHIC_POINT *g)
Convert cartesian coordinates on unit sphere to spherical coordinates.
Definition: lwgeodetic.c:414
#define str(s)
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:83
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
static const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition: lwinline.h:91
static const POINT3D * getPoint3d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition: lwinline.h:103
static const POINT4D * getPoint4d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition: lwinline.h:115
double xmax
Definition: liblwgeom.h:326
double zmin
Definition: liblwgeom.h:325
double ymax
Definition: liblwgeom.h:326
double ymin
Definition: liblwgeom.h:325
double zmax
Definition: liblwgeom.h:326
double xmin
Definition: liblwgeom.h:325
int32_t srid
Definition: liblwgeom.h:327
double ymax
Definition: liblwgeom.h:343
double zmax
Definition: liblwgeom.h:345
double xmax
Definition: liblwgeom.h:341
double zmin
Definition: liblwgeom.h:344
double mmax
Definition: liblwgeom.h:347
double ymin
Definition: liblwgeom.h:342
double xmin
Definition: liblwgeom.h:340
double mmin
Definition: liblwgeom.h:346
lwflags_t flags
Definition: liblwgeom.h:339
Point in spherical coordinates on the world.
Definition: lwgeodetic.h:54
lwflags_t flags
Definition: liblwgeom.h:495
POINTARRAY * points
Definition: liblwgeom.h:493
lwflags_t flags
Definition: liblwgeom.h:563
uint32_t ngeoms
Definition: liblwgeom.h:566
LWGEOM ** geoms
Definition: liblwgeom.h:561
uint8_t type
Definition: liblwgeom.h:448
POINTARRAY * points
Definition: liblwgeom.h:469
POINTARRAY * point
Definition: liblwgeom.h:457
POINTARRAY ** rings
Definition: liblwgeom.h:505
uint32_t nrings
Definition: liblwgeom.h:510
POINTARRAY * points
Definition: liblwgeom.h:481
double y
Definition: liblwgeom.h:376
double x
Definition: liblwgeom.h:376
double z
Definition: liblwgeom.h:388
double x
Definition: liblwgeom.h:388
double y
Definition: liblwgeom.h:388
double m
Definition: liblwgeom.h:400
double x
Definition: liblwgeom.h:400
double z
Definition: liblwgeom.h:400
double y
Definition: liblwgeom.h:400
lwflags_t flags
Definition: liblwgeom.h:417
uint32_t npoints
Definition: liblwgeom.h:413