PostGIS  3.4.0dev-r@@SVN_REVISION@@
gserialized_spgist_3d.c
Go to the documentation of this file.
1 /*****************************************************************************
2  *
3  * gserialized_spgist_3d.c
4  * SP-GiST implementation of 6-dimensional oct-tree over 3D boxes
5  *
6  * This module provides SP-GiST implementation for boxes using oct tree
7  * analogy in 4-dimensional space. SP-GiST doesn't allow indexing of
8  * overlapping objects. We are making 3D objects never-overlapping in
9  * 6D space. This technique has some benefits compared to traditional
10  * R-Tree which is implemented as GiST. The performance tests reveal
11  * that this technique especially beneficial with too much overlapping
12  * objects, so called "spaghetti data".
13  *
14  * Unlike the original oct-tree, we are splitting the tree into 64
15  * octants in 6D space. It is easier to imagine it as splitting space
16  * three times into 3:
17  *
18  * | | | |
19  * | | | |
20  * | -----+----- | -----+-----
21  * | | | |
22  * | | | |
23  * -------------+------------- -+- -------------+-------------
24  * | |
25  * | |
26  * | |
27  * | |
28  * | |
29  * FRONT BACK
30  *
31  * We are using box3D data type as the prefix, but we are treating them
32  * as points in 6-dimensional space, because 3D boxes are not enough
33  * to represent the octant boundaries in 6D space. They however are
34  * sufficient to point out the additional boundaries of the next
35  * octant.
36  *
37  * We are using traversal values provided by SP-GiST to calculate and
38  * to store the bounds of the octants, while traversing into the tree.
39  * Traversal value has all the boundaries in the 6D space, and is is
40  * capable of transferring the required boundaries to the following
41  * traversal values. In conclusion, three things are necessary
42  * to calculate the next traversal value:
43  *
44  * (1) the traversal value of the parent
45  * (2) the octant of the current node
46  * (3) the prefix of the current node
47  *
48  * If we visualize them on our simplified drawing (see the drawing above);
49  * transferred boundaries of (1) would be the outer axis, relevant part
50  * of (2) would be the up range_y part of the other axis, and (3) would be
51  * the inner axis.
52  *
53  * For example, consider the case of overlapping. When recursion
54  * descends deeper and deeper down the tree, all octants in
55  * the current node will be checked for overlapping. The boundaries
56  * will be re-calculated for all octants. Overlap check answers
57  * the question: can any box from this octant overlap with the given
58  * box? If yes, then this octant will be walked. If no, then this
59  * octant will be skipped.
60  *
61  * This method provides restrictions for minimum and maximum values of
62  * every dimension of every corner of the box on every level of the tree
63  * except the root. For the root node, we are setting the boundaries
64  * that we don't yet have as infinity.
65  *
66  * Portions Copyright (c) 2018, Esteban Zimanyi, Arthur Lesuisse,
67  * Université Libre de Bruxelles
68  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
69  * Portions Copyright (c) 1994, Regents of the University of California
70  *
71  *****************************************************************************/
72 
73 #include "gserialized_spgist_3d.h"
74 #include "lwgeom_box3d.h"
75 #include "lwgeom_pg.h"
76 
77 #include <float.h>
78 
80 Datum gserialized_overlaps_3d(PG_FUNCTION_ARGS)
81 {
82  BOX3D *box1 = DatumGetBox3DP(DirectFunctionCall1(LWGEOM_to_BOX3D, PG_GETARG_DATUM(0)));
83  BOX3D *box2 = DatumGetBox3DP(DirectFunctionCall1(LWGEOM_to_BOX3D, PG_GETARG_DATUM(1)));
84  bool resut = BOX3D_overlaps_internal(box1, box2);
85  pfree(box1);
86  pfree(box2);
87 
88  PG_RETURN_BOOL(resut);
89 }
90 
92 Datum gserialized_contains_3d(PG_FUNCTION_ARGS)
93 {
94  BOX3D *box1 = DatumGetBox3DP(DirectFunctionCall1(LWGEOM_to_BOX3D, PG_GETARG_DATUM(0)));
95  BOX3D *box2 = DatumGetBox3DP(DirectFunctionCall1(LWGEOM_to_BOX3D, PG_GETARG_DATUM(1)));
96  bool resut = BOX3D_contains_internal(box1, box2);
97  pfree(box1);
98  pfree(box2);
99 
100  PG_RETURN_BOOL(resut);
101 }
102 
104 Datum gserialized_contained_3d(PG_FUNCTION_ARGS)
105 {
106  BOX3D *box1 = DatumGetBox3DP(DirectFunctionCall1(LWGEOM_to_BOX3D, PG_GETARG_DATUM(0)));
107  BOX3D *box2 = DatumGetBox3DP(DirectFunctionCall1(LWGEOM_to_BOX3D, PG_GETARG_DATUM(1)));
108  bool resut = BOX3D_contained_internal(box1, box2);
109  pfree(box1);
110  pfree(box2);
111 
112  PG_RETURN_BOOL(resut);
113 }
114 
116 Datum gserialized_same_3d(PG_FUNCTION_ARGS)
117 {
118  BOX3D *box1 = DatumGetBox3DP(DirectFunctionCall1(LWGEOM_to_BOX3D, PG_GETARG_DATUM(0)));
119  BOX3D *box2 = DatumGetBox3DP(DirectFunctionCall1(LWGEOM_to_BOX3D, PG_GETARG_DATUM(1)));
120  bool resut = BOX3D_same_internal(box1, box2);
121  pfree(box1);
122  pfree(box2);
123 
124  PG_RETURN_BOOL(resut);
125 }
126 
127 /*
128  * Comparator for qsort
129  *
130  * We don't need to use the floating point macros in here, because this
131  * is only going to be used in a place to effect the performance
132  * of the index, not the correctness.
133  */
134 static int
135 compareDoubles(const void *a, const void *b)
136 {
137  double x = *(double *)a;
138  double y = *(double *)b;
139 
140  if (x == y)
141  return 0;
142  return (x > y) ? 1 : -1;
143 }
144 
145 typedef struct
146 {
149 } CubeBox3D;
150 
151 /*
152  * Calculate the octant
153  *
154  * The octant is 8 bit unsigned integer with 6 least bits in use.
155  * This function accepts 2 BOX3D as input. All 6 bits are set by comparing
156  * a corner of the box. This makes 64 octants in total.
157  */
158 static uint8
159 getOctant(const BOX3D *centroid, const BOX3D *inBox)
160 {
161  uint8 octant = 0;
162 
163  if (inBox->xmin > centroid->xmin)
164  octant |= 0x20;
165 
166  if (inBox->xmax > centroid->xmax)
167  octant |= 0x10;
168 
169  if (inBox->ymin > centroid->ymin)
170  octant |= 0x08;
171 
172  if (inBox->ymax > centroid->ymax)
173  octant |= 0x04;
174 
175  if (inBox->zmin > centroid->zmin)
176  octant |= 0x02;
177 
178  if (inBox->zmax > centroid->zmax)
179  octant |= 0x01;
180 
181  return octant;
182 }
183 
184 /*
185  * Initialize the traversal value
186  *
187  * In the beginning, we don't have any restrictions. We have to
188  * initialize the struct to cover the whole 6D space.
189  */
190 static CubeBox3D *
192 {
193  CubeBox3D *cube_box = (CubeBox3D *)palloc(sizeof(CubeBox3D));
194  double infinity = DBL_MAX;
195 
196  cube_box->left.xmin = -infinity;
197  cube_box->left.xmax = infinity;
198 
199  cube_box->left.ymin = -infinity;
200  cube_box->left.ymax = infinity;
201 
202  cube_box->left.zmin = -infinity;
203  cube_box->left.zmax = infinity;
204 
205  cube_box->right.xmin = -infinity;
206  cube_box->right.xmax = infinity;
207 
208  cube_box->right.ymin = -infinity;
209  cube_box->right.ymax = infinity;
210 
211  cube_box->right.zmin = -infinity;
212  cube_box->right.zmax = infinity;
213 
214  return cube_box;
215 }
216 
217 /*
218  * Calculate the next traversal value
219  *
220  * All centroids are bounded by CubeBox3D, but SP-GiST only keeps boxes.
221  * When we are traversing the tree, we must calculate CubeBox3D,
222  * using centroid and octant.
223  */
224 static CubeBox3D *
225 nextCubeBox3D(CubeBox3D *cube_box, BOX3D *centroid, uint8 octant)
226 {
227  CubeBox3D *next_cube_box = (CubeBox3D *)palloc(sizeof(CubeBox3D));
228 
229  memcpy(next_cube_box, cube_box, sizeof(CubeBox3D));
230 
231  if (octant & 0x20)
232  next_cube_box->left.xmin = centroid->xmin;
233  else
234  next_cube_box->left.xmax = centroid->xmin;
235 
236  if (octant & 0x10)
237  next_cube_box->right.xmin = centroid->xmax;
238  else
239  next_cube_box->right.xmax = centroid->xmax;
240 
241  if (octant & 0x08)
242  next_cube_box->left.ymin = centroid->ymin;
243  else
244  next_cube_box->left.ymax = centroid->ymin;
245 
246  if (octant & 0x04)
247  next_cube_box->right.ymin = centroid->ymax;
248  else
249  next_cube_box->right.ymax = centroid->ymax;
250 
251  if (octant & 0x02)
252  next_cube_box->left.zmin = centroid->zmin;
253  else
254  next_cube_box->left.zmax = centroid->zmin;
255 
256  if (octant & 0x01)
257  next_cube_box->right.zmin = centroid->zmax;
258  else
259  next_cube_box->right.zmax = centroid->zmax;
260 
261  return next_cube_box;
262 }
263 
264 /* Can any cube from cube_box overlap with query? */
265 static bool
266 overlap6D(CubeBox3D *cube_box, BOX3D *query)
267 {
268  return (cube_box->left.xmin <= query->xmax) && (cube_box->right.xmax >= query->xmin) &&
269  (cube_box->left.ymin <= query->ymax) && (cube_box->right.ymax >= query->ymin) &&
270  (cube_box->left.zmin <= query->zmax) && (cube_box->right.zmax >= query->zmin);
271 }
272 
273 /* Can any cube from cube_box contain query? */
274 static bool
275 contain6D(CubeBox3D *cube_box, BOX3D *query)
276 {
277  return (cube_box->right.xmax >= query->xmax) && (cube_box->left.xmin <= query->xmin) &&
278  (cube_box->right.ymax >= query->ymax) && (cube_box->left.ymin <= query->ymin) &&
279  (cube_box->right.zmax >= query->zmax) && (cube_box->left.zmin <= query->zmin);
280 }
281 
282 /* Can any cube from cube_box be left of query? */
283 static bool
284 left6D(CubeBox3D *cube_box, BOX3D *query)
285 {
286  return (cube_box->right.xmax < query->xmin);
287 }
288 
289 /* Can any cube from cube_box does not extend the right of query? */
290 static bool
291 overLeft6D(CubeBox3D *cube_box, BOX3D *query)
292 {
293  return (cube_box->right.xmax <= query->xmax);
294 }
295 
296 /* Can any cube from cube_box be right of query? */
297 static bool
298 right6D(CubeBox3D *cube_box, BOX3D *query)
299 {
300  return (cube_box->left.xmin > query->xmax);
301 }
302 
303 /* Can any cube from cube_box does not extend the left of query? */
304 static bool
305 overRight6D(CubeBox3D *cube_box, BOX3D *query)
306 {
307  return (cube_box->left.xmin >= query->xmin);
308 }
309 
310 /* Can any cube from cube_box be below of query? */
311 static bool
312 below6D(CubeBox3D *cube_box, BOX3D *query)
313 {
314  return (cube_box->right.ymax < query->ymin);
315 }
316 
317 /* Can any cube from cube_box does not extend above query? */
318 static bool
319 overBelow6D(CubeBox3D *cube_box, BOX3D *query)
320 {
321  return (cube_box->right.ymax <= query->ymax);
322 }
323 
324 /* Can any cube from cube_box be above of query? */
325 static bool
326 above6D(CubeBox3D *cube_box, BOX3D *query)
327 {
328  return (cube_box->left.ymin > query->ymax);
329 }
330 
331 /* Can any cube from cube_box does not extend below of query? */
332 static bool
333 overAbove6D(CubeBox3D *cube_box, BOX3D *query)
334 {
335  return (cube_box->left.ymin >= query->ymin);
336 }
337 
338 /* Can any cube from cube_box be before of query? */
339 static bool
340 front6D(CubeBox3D *cube_box, BOX3D *query)
341 {
342  return (cube_box->right.zmax < query->zmin);
343 }
344 
345 /* Can any cube from cube_box does not extend the after of query? */
346 static bool
347 overFront6D(CubeBox3D *cube_box, BOX3D *query)
348 {
349  return (cube_box->right.zmax <= query->zmax);
350 }
351 
352 /* Can any cube from cube_box be after of query? */
353 static bool
354 back6D(CubeBox3D *cube_box, BOX3D *query)
355 {
356  return (cube_box->left.zmin > query->zmax);
357 }
358 
359 /* Can any cube from cube_box does not extend the before of query? */
360 static bool
361 overBack6D(CubeBox3D *cube_box, BOX3D *query)
362 {
363  return (cube_box->left.zmin >= query->zmin);
364 }
365 
366 /*
367  * SP-GiST config function
368  */
369 
371 
372 PGDLLEXPORT Datum gserialized_spgist_config_3d(PG_FUNCTION_ARGS)
373 {
374  spgConfigOut *cfg = (spgConfigOut *)PG_GETARG_POINTER(1);
375 
376  Oid boxoid = InvalidOid;
377  /* We need to initialize the internal cache to access it later via postgis_oid() */
378  postgis_initialize_cache();
379  boxoid = postgis_oid(BOX3DOID);
380 
381  cfg->prefixType = boxoid;
382  cfg->labelType = VOIDOID; /* We don't need node labels. */
383  cfg->leafType = boxoid;
384  cfg->canReturnData = false;
385  cfg->longValuesOK = false;
386 
387  PG_RETURN_VOID();
388 }
389 
390 /*
391  * SP-GiST choose function
392  */
393 
395 
396 PGDLLEXPORT Datum gserialized_spgist_choose_3d(PG_FUNCTION_ARGS)
397 {
398  spgChooseIn *in = (spgChooseIn *)PG_GETARG_POINTER(0);
399  spgChooseOut *out = (spgChooseOut *)PG_GETARG_POINTER(1);
400  BOX3D *centroid = DatumGetBox3DP(in->prefixDatum);
401  BOX3D *box = DatumGetBox3DP(in->leafDatum);
402 
403  out->resultType = spgMatchNode;
404  out->result.matchNode.restDatum = Box3DPGetDatum(box);
405 
406  /* nodeN will be set by core, when allTheSame. */
407  if (!in->allTheSame)
408  out->result.matchNode.nodeN = getOctant(centroid, box);
409 
410  PG_RETURN_VOID();
411 }
412 
413 /*
414  * SP-GiST pick-split function
415  *
416  * It splits a list of boxes into octants by choosing a central 6D
417  * point as the median of the coordinates of the boxes.
418  */
420 
421 PGDLLEXPORT Datum gserialized_spgist_picksplit_3d(PG_FUNCTION_ARGS)
422 {
423  spgPickSplitIn *in = (spgPickSplitIn *)PG_GETARG_POINTER(0);
424  spgPickSplitOut *out = (spgPickSplitOut *)PG_GETARG_POINTER(1);
425  BOX3D *centroid;
426  int median, i;
427  double *lowXs = palloc(sizeof(double) * in->nTuples);
428  double *highXs = palloc(sizeof(double) * in->nTuples);
429  double *lowYs = palloc(sizeof(double) * in->nTuples);
430  double *highYs = palloc(sizeof(double) * in->nTuples);
431  double *lowZs = palloc(sizeof(double) * in->nTuples);
432  double *highZs = palloc(sizeof(double) * in->nTuples);
433  int32_t srid = SRID_UNKNOWN;
434 
435  /* Calculate median of all 6D coordinates */
436  for (i = 0; i < in->nTuples; i++)
437  {
438  BOX3D* box_in = DatumGetBox3DP(in->datums[i]);
439  lowXs[i] = box_in->xmin;
440  highXs[i] = box_in->xmax;
441  lowYs[i] = box_in->ymin;
442  highYs[i] = box_in->ymax;
443  lowZs[i] = box_in->zmin;
444  highZs[i] = box_in->zmax;
445 
446  if (i == 0)
447  srid = box_in->srid;
448  }
449 
450  qsort(lowXs, in->nTuples, sizeof(double), compareDoubles);
451  qsort(highXs, in->nTuples, sizeof(double), compareDoubles);
452  qsort(lowYs, in->nTuples, sizeof(double), compareDoubles);
453  qsort(highYs, in->nTuples, sizeof(double), compareDoubles);
454  qsort(lowZs, in->nTuples, sizeof(double), compareDoubles);
455  qsort(highZs, in->nTuples, sizeof(double), compareDoubles);
456 
457  median = in->nTuples / 2;
458 
459  centroid = palloc(sizeof(BOX3D));
460 
461  centroid->xmin = lowXs[median];
462  centroid->xmax = highXs[median];
463  centroid->ymin = lowYs[median];
464  centroid->ymax = highYs[median];
465  centroid->zmin = lowZs[median];
466  centroid->zmax = highZs[median];
467  centroid->srid = srid;
468 
469  /* Fill the output */
470  out->hasPrefix = true;
471  out->prefixDatum = Box3DPGetDatum(centroid);
472 
473  out->nNodes = 64;
474  out->nodeLabels = NULL; /* We don't need node labels. */
475 
476  out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples);
477  out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples);
478 
479  /*
480  * Assign ranges to corresponding nodes according to octants relative to
481  * the "centroid" range
482  */
483  for (i = 0; i < in->nTuples; i++)
484  {
485  BOX3D* box_in = DatumGetBox3DP(in->datums[i]);
486  uint8 octant = getOctant(centroid, box_in);
487 
488  out->leafTupleDatums[i] = Box3DPGetDatum(box_in);
489  out->mapTuplesToNodes[i] = octant;
490  }
491 
492  pfree(lowXs);
493  pfree(highXs);
494  pfree(lowYs);
495  pfree(highYs);
496  pfree(lowZs);
497  pfree(highZs);
498 
499  PG_RETURN_VOID();
500 }
501 
502 /*
503  * SP-GiST inner consistent function
504  */
506 
507 PGDLLEXPORT Datum gserialized_spgist_inner_consistent_3d(PG_FUNCTION_ARGS)
508 {
509  spgInnerConsistentIn *in = (spgInnerConsistentIn *)PG_GETARG_POINTER(0);
510  spgInnerConsistentOut *out = (spgInnerConsistentOut *)PG_GETARG_POINTER(1);
511  int i;
512  MemoryContext old_ctx;
513  CubeBox3D *cube_box;
514  uint8 octant;
515  BOX3D *centroid;
516  int *nodeNumbers;
517  void **traversalValues;
518 
519  if (in->allTheSame)
520  {
521  /* Report that all nodes should be visited */
522  out->nNodes = in->nNodes;
523  out->nodeNumbers = (int *)palloc(sizeof(int) * in->nNodes);
524  for (i = 0; i < in->nNodes; i++)
525  out->nodeNumbers[i] = i;
526 
527  PG_RETURN_VOID();
528  }
529 
530  /*
531  * We are saving the traversal value or initialize it an unbounded one, if
532  * we have just begun to walk the tree.
533  */
534  if (in->traversalValue)
535  cube_box = in->traversalValue;
536  else
537  cube_box = initCubeBox();
538 
539  centroid = DatumGetBox3DP(in->prefixDatum);
540 
541  /* Allocate enough memory for nodes */
542  out->nNodes = 0;
543  nodeNumbers = (int *)palloc(sizeof(int) * in->nNodes);
544  traversalValues = (void **)palloc(sizeof(void *) * in->nNodes);
545 
546  /*
547  * We switch memory context, because we want to allocate memory for new
548  * traversal values (next_cube_box) and pass these pieces of memory to
549  * further call of this function.
550  */
551  old_ctx = MemoryContextSwitchTo(in->traversalMemoryContext);
552 
553  for (octant = 0; octant < in->nNodes; octant++)
554  {
555  CubeBox3D *next_cube_box = nextCubeBox3D(cube_box, centroid, octant);
556  bool flag = true;
557 
558  for (i = 0; i < in->nkeys; i++)
559  {
560  StrategyNumber strategy = in->scankeys[i].sk_strategy;
561  Datum query = in->scankeys[i].sk_argument;
562  BOX3D *box = DatumGetBox3DP(DirectFunctionCall1(LWGEOM_to_BOX3D, query));
563 
564  switch (strategy)
565  {
568  flag = overlap6D(next_cube_box, box);
569  break;
570 
573  flag = contain6D(next_cube_box, box);
574  break;
575 
577  flag = !overRight6D(next_cube_box, box);
578  break;
579 
581  flag = !right6D(next_cube_box, box);
582  break;
583 
585  flag = !overLeft6D(next_cube_box, box);
586  break;
587 
589  flag = !left6D(next_cube_box, box);
590  break;
591 
593  flag = !overBelow6D(next_cube_box, box);
594  break;
595 
597  flag = !below6D(next_cube_box, box);
598  break;
599 
601  flag = !overAbove6D(next_cube_box, box);
602  break;
603 
605  flag = !above6D(next_cube_box, box);
606  break;
607 
609  flag = !overFront6D(next_cube_box, box);
610  break;
611 
613  flag = !front6D(next_cube_box, box);
614  break;
615 
617  flag = !overBack6D(next_cube_box, box);
618  break;
619 
621  flag = !back6D(next_cube_box, box);
622  break;
623 
624  default:
625  elog(ERROR, "unrecognized strategy: %d", strategy);
626  }
627 
628  /* If any check is failed, we have found our answer. */
629  if (!flag)
630  break;
631  }
632 
633  if (flag)
634  {
635  traversalValues[out->nNodes] = next_cube_box;
636  nodeNumbers[out->nNodes] = octant;
637  out->nNodes++;
638  }
639  else
640  {
641  /*
642  * If this node is not selected, we don't need to keep the next
643  * traversal value in the memory context.
644  */
645  pfree(next_cube_box);
646  }
647  }
648 
649  /* Pass to the next level only the values that need to be traversed */
650  out->nodeNumbers = (int *)palloc(sizeof(int) * out->nNodes);
651  out->traversalValues = (void **)palloc(sizeof(void *) * out->nNodes);
652  for (i = 0; i < out->nNodes; i++)
653  {
654  out->nodeNumbers[i] = nodeNumbers[i];
655  out->traversalValues[i] = traversalValues[i];
656  }
657  pfree(nodeNumbers);
658  pfree(traversalValues);
659 
660  /* Switch after */
661  MemoryContextSwitchTo(old_ctx);
662 
663  PG_RETURN_VOID();
664 }
665 
666 /*
667  * SP-GiST inner consistent function
668  */
670 
671 PGDLLEXPORT Datum gserialized_spgist_leaf_consistent_3d(PG_FUNCTION_ARGS)
672 {
673  spgLeafConsistentIn *in = (spgLeafConsistentIn *)PG_GETARG_POINTER(0);
674  spgLeafConsistentOut *out = (spgLeafConsistentOut *)PG_GETARG_POINTER(1);
675  BOX3D *leaf = DatumGetBox3DP(in->leafDatum);
676  bool flag = true;
677  int i;
678 
679  /* All tests are exact. */
680  out->recheck = false;
681 
682  /* leafDatum is what it is... */
683  out->leafValue = in->leafDatum;
684 
685  /* Perform the required comparison(s) */
686  for (i = 0; i < in->nkeys; i++)
687  {
688  StrategyNumber strategy = in->scankeys[i].sk_strategy;
689  Datum query = in->scankeys[i].sk_argument;
690  BOX3D *box = DatumGetBox3DP(DirectFunctionCall1(LWGEOM_to_BOX3D, query));
691 
692  switch (strategy)
693  {
695  flag = BOX3D_overlaps_internal(leaf, box);
696  break;
697 
699  flag = BOX3D_contains_internal(leaf, box);
700  break;
701 
703  flag = BOX3D_contained_internal(leaf, box);
704  break;
705 
707  flag = BOX3D_same_internal(leaf, box);
708  break;
709 
711  flag = BOX3D_left_internal(leaf, box);
712  break;
713 
715  flag = BOX3D_overleft_internal(leaf, box);
716  break;
717 
719  flag = BOX3D_right_internal(leaf, box);
720  break;
721 
723  flag = BOX3D_overright_internal(leaf, box);
724  break;
725 
727  flag = BOX3D_above_internal(leaf, box);
728  break;
729 
731  flag = BOX3D_overabove_internal(leaf, box);
732  break;
733 
735  flag = BOX3D_below_internal(leaf, box);
736  break;
737 
739  flag = BOX3D_overbelow_internal(leaf, box);
740  break;
741 
743  flag = BOX3D_back_internal(leaf, box);
744  break;
745 
747  flag = BOX3D_overback_internal(leaf, box);
748  break;
749 
751  flag = BOX3D_front_internal(leaf, box);
752  break;
753 
755  flag = BOX3D_overfront_internal(leaf, box);
756  break;
757 
758  default:
759  elog(ERROR, "unrecognized strategy: %d", strategy);
760  }
761 
762  /* If any check is failed, we have found our answer. */
763  if (!flag)
764  break;
765  }
766 
767  PG_RETURN_BOOL(flag);
768 }
769 
771 
772 PGDLLEXPORT Datum gserialized_spgist_compress_3d(PG_FUNCTION_ARGS)
773 {
774  BOX3D *result = DatumGetBox3DP(DirectFunctionCall1(LWGEOM_to_BOX3D, PG_GETARG_DATUM(0)));
775 
776  /* Is the bounding box is null */
777  if (result == LW_FAILURE)
778  {
779  PG_RETURN_NULL();
780  }
781 
782  /* Return BOX3D. */
783  PG_RETURN_POINTER(result);
784 }
785 
786 /*****************************************************************************/
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:262
static bool front6D(CubeBox3D *cube_box, BOX3D *query)
static bool back6D(CubeBox3D *cube_box, BOX3D *query)
static bool right6D(CubeBox3D *cube_box, BOX3D *query)
Datum gserialized_overlaps_3d(PG_FUNCTION_ARGS)
static bool overBack6D(CubeBox3D *cube_box, BOX3D *query)
static bool overAbove6D(CubeBox3D *cube_box, BOX3D *query)
PG_FUNCTION_INFO_V1(gserialized_overlaps_3d)
static bool overlap6D(CubeBox3D *cube_box, BOX3D *query)
Datum gserialized_contains_3d(PG_FUNCTION_ARGS)
static uint8 getOctant(const BOX3D *centroid, const BOX3D *inBox)
static int compareDoubles(const void *a, const void *b)
static CubeBox3D * nextCubeBox3D(CubeBox3D *cube_box, BOX3D *centroid, uint8 octant)
PGDLLEXPORT Datum gserialized_spgist_picksplit_3d(PG_FUNCTION_ARGS)
static bool overRight6D(CubeBox3D *cube_box, BOX3D *query)
static bool overFront6D(CubeBox3D *cube_box, BOX3D *query)
PGDLLEXPORT Datum gserialized_spgist_config_3d(PG_FUNCTION_ARGS)
static CubeBox3D * initCubeBox(void)
PGDLLEXPORT Datum gserialized_spgist_inner_consistent_3d(PG_FUNCTION_ARGS)
Datum gserialized_same_3d(PG_FUNCTION_ARGS)
PGDLLEXPORT Datum gserialized_spgist_compress_3d(PG_FUNCTION_ARGS)
static bool contain6D(CubeBox3D *cube_box, BOX3D *query)
static bool overBelow6D(CubeBox3D *cube_box, BOX3D *query)
static bool above6D(CubeBox3D *cube_box, BOX3D *query)
PGDLLEXPORT Datum gserialized_spgist_choose_3d(PG_FUNCTION_ARGS)
Datum gserialized_contained_3d(PG_FUNCTION_ARGS)
static bool below6D(CubeBox3D *cube_box, BOX3D *query)
static bool overLeft6D(CubeBox3D *cube_box, BOX3D *query)
PGDLLEXPORT Datum gserialized_spgist_leaf_consistent_3d(PG_FUNCTION_ARGS)
static bool left6D(CubeBox3D *cube_box, BOX3D *query)
#define SPGOverlapStrategyNumber
#define SPGOverLeftStrategyNumber
#define SPGLeftStrategyNumber
#define SPGAboveStrategyNumber
#define SPGSameStrategyNumber
#define SPGOverBackStrategyNumber
#define SPGContainedByStrategyNumber
#define SPGOverRightStrategyNumber
#define SPGFrontStrategyNumber
#define SPGBelowStrategyNumber
#define SPGRightStrategyNumber
#define SPGOverFrontStrategyNumber
#define SPGContainsStrategyNumber
#define SPGOverBelowStrategyNumber
#define SPGBackStrategyNumber
#define SPGOverAboveStrategyNumber
#define LW_FAILURE
Definition: liblwgeom.h:96
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:215
bool BOX3D_above_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:782
bool BOX3D_back_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:850
bool BOX3D_overlaps_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:642
bool BOX3D_contains_internal(BOX3D *box1, BOX3D *box2)
needed for sp-gist support PostgreSQL 11+
Definition: lwgeom_box3d.c:606
bool BOX3D_overabove_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:799
bool BOX3D_right_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:714
bool BOX3D_overback_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:867
bool BOX3D_overleft_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:697
bool BOX3D_below_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:748
bool BOX3D_left_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:680
bool BOX3D_overright_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:731
bool BOX3D_same_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:661
bool BOX3D_contained_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:625
Datum LWGEOM_to_BOX3D(PG_FUNCTION_ARGS)
Definition: lwgeom_box3d.c:398
bool BOX3D_front_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:816
bool BOX3D_overfront_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:833
bool BOX3D_overbelow_internal(BOX3D *box1, BOX3D *box2)
Definition: lwgeom_box3d.c:765
Datum centroid(PG_FUNCTION_ARGS)
double xmax
Definition: liblwgeom.h:340
double zmin
Definition: liblwgeom.h:339
double ymax
Definition: liblwgeom.h:340
double ymin
Definition: liblwgeom.h:339
double zmax
Definition: liblwgeom.h:340
double xmin
Definition: liblwgeom.h:339
int32_t srid
Definition: liblwgeom.h:341