PostGIS  3.0.6dev-r@@SVN_REVISION@@
gserialized_spgist_2d.c
Go to the documentation of this file.
1 /*****************************************************************************
2  *
3  * gserialized_spgist_2d.c
4  * SP-GiST implementation of 4-dimensional quad-tree over box2df
5  *
6  * This module provides SP-GiST implementation for boxes using quad tree
7  * analogy in 4-dimensional space. SP-GiST doesn't allow indexing of
8  * overlapping objects. We are making objects never-overlapping in
9  * 4D 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 quad-tree, we are splitting the tree into 16
15  * quadrants in 4D space. It is easier to imagine it as splitting space
16  * two times into 2:
17  *
18  * | |
19  * | |
20  * | -----+-----
21  * | |
22  * | |
23  * -------------+-------------
24  * |
25  * |
26  * |
27  * |
28  * |
29  * FRONT
30  *
31  * We are using box data type as the prefix, but we are treating them
32  * as points in 4-dimensional space, because boxes are not enough
33  * to represent the quadrant boundaries in 4D space. They however are
34  * sufficient to point out the additional boundaries of the next
35  * quadrant.
36  *
37  * We are using traversal values provided by SP-GiST to calculate and
38  * to store the bounds of the quadrants, while traversing into the tree.
39  * Traversal value has all the boundaries in the 4D 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 quadrant 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 quadrants in
55  * the current node will be checked for overlapping. The boundaries
56  * will be re-calculated for all quadrants. Overlap check answers
57  * the question: can any box from this quadrant overlap with the given
58  * box? If yes, then this quadrant will be walked. If no, then this
59  * quadrant 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 <postgres.h>
74 #include <utils/builtins.h> /* For float manipulation */
75 #include "access/spgist.h" /* For SP-GiST */
76 #include "catalog/pg_type_d.h" /* For VOIDOID */
77 
78 #include "../postgis_config.h"
79 
80 #include "liblwgeom.h" /* For standard geometry types. */
81 #include "lwgeom_pg.h" /* For debugging macros. */
82 #include <gserialized_gist.h> /* For utility functions. */
83 #include <lwgeom_pg.h> /* For debugging macros. */
84 #include <math.h>
85 
86 /*
87 ** SP-GiST 2D index function prototypes
88 */
89 
90 Datum gserialized_spgist_config_2d(PG_FUNCTION_ARGS);
91 Datum gserialized_spgist_choose_2d(PG_FUNCTION_ARGS);
92 Datum gserialized_spgist_picksplit_2d(PG_FUNCTION_ARGS);
93 Datum gserialized_spgist_inner_consistent_2d(PG_FUNCTION_ARGS);
94 Datum gserialized_spgist_leaf_consistent_2d(PG_FUNCTION_ARGS);
95 Datum gserialized_spgist_compress_2d(PG_FUNCTION_ARGS);
96 
97 /*
98  * Comparator for qsort
99  *
100  * We don't need to use the floating point macros in here, because this
101  * is only going to be used in a place to effect the performance
102  * of the index, not the correctness.
103  */
104 static int
105 compareDoubles(const void *a, const void *b)
106 {
107  double x = *(double *)a;
108  double y = *(double *)b;
109 
110  if (x == y)
111  return 0;
112  return (x > y) ? 1 : -1;
113 }
114 
115 typedef struct
116 {
117  BOX2DF left;
118  BOX2DF right;
119 } RectBox;
120 
121 /*
122  * Calculate the quadrant
123  *
124  * The quadrant is 4 bit unsigned integer with 4 least bits in use.
125  * This function accepts 2 BOX2DF as input. All 4 bits are set by comparing
126  * a corner of the box. This makes 16 quadrants in total.
127  */
128 static uint8
129 getQuadrant4D(BOX2DF *centroid, BOX2DF *inBox)
130 {
131  uint8 quadrant = 0;
132 
133  if (inBox->xmin > centroid->xmin)
134  quadrant |= 0x8;
135 
136  if (inBox->xmax > centroid->xmax)
137  quadrant |= 0x4;
138 
139  if (inBox->ymin > centroid->ymin)
140  quadrant |= 0x2;
141 
142  if (inBox->ymax > centroid->ymax)
143  quadrant |= 0x1;
144 
145  return quadrant;
146 }
147 
148 /*
149  * Initialize the traversal value
150  *
151  * In the beginning, we don't have any restrictions. We have to
152  * initialize the struct to cover the whole 4D space.
153  */
154 static RectBox *
156 {
157  RectBox *rect_box = (RectBox *)palloc(sizeof(RectBox));
158  float infinity = FLT_MAX;
159 
160  rect_box->left.xmin = -infinity;
161  rect_box->left.xmax = infinity;
162 
163  rect_box->left.ymin = -infinity;
164  rect_box->left.ymax = infinity;
165 
166  rect_box->right.xmin = -infinity;
167  rect_box->right.xmax = infinity;
168 
169  rect_box->right.ymin = -infinity;
170  rect_box->right.ymax = infinity;
171 
172  return rect_box;
173 }
174 
175 /*
176  * Calculate the next traversal value
177  *
178  * All centroids are bounded by RectBox, but SP-GiST only keeps
179  * boxes. When we are traversing the tree, we must calculate RectBox,
180  * using centroid and quadrant.
181  */
182 static RectBox *
183 nextRectBox(RectBox *rect_box, BOX2DF *centroid, uint8 quadrant)
184 {
185  RectBox *next_rect_box = (RectBox *)palloc(sizeof(RectBox));
186 
187  memcpy(next_rect_box, rect_box, sizeof(RectBox));
188 
189  if (quadrant & 0x8)
190  next_rect_box->left.xmin = centroid->xmin;
191  else
192  next_rect_box->left.xmax = centroid->xmin;
193 
194  if (quadrant & 0x4)
195  next_rect_box->right.xmin = centroid->xmax;
196  else
197  next_rect_box->right.xmax = centroid->xmax;
198 
199  if (quadrant & 0x2)
200  next_rect_box->left.ymin = centroid->ymin;
201  else
202  next_rect_box->left.ymax = centroid->ymin;
203 
204  if (quadrant & 0x1)
205  next_rect_box->right.ymin = centroid->ymax;
206  else
207  next_rect_box->right.ymax = centroid->ymax;
208 
209  return next_rect_box;
210 }
211 
212 /* Can any cube from rect_box overlap with query? */
213 static bool
214 overlap4D(RectBox *rect_box, BOX2DF *query)
215 {
216  return (rect_box->left.xmin <= query->xmax && rect_box->right.xmax >= query->xmin) &&
217  (rect_box->left.ymin <= query->ymax && rect_box->right.ymax >= query->ymin);
218 }
219 
220 /* Can any cube from rect_box contain query? */
221 static bool
222 contain4D(RectBox *rect_box, BOX2DF *query)
223 {
224  return (rect_box->right.xmax >= query->xmax && rect_box->left.xmin <= query->xmin) &&
225  (rect_box->right.ymax >= query->ymax && rect_box->left.ymin <= query->ymin);
226 }
227 
228 /* Can any cube from rect_box be left of query? */
229 static bool
230 left4D(RectBox *rect_box, BOX2DF *query)
231 {
232  return rect_box->right.xmax <= query->xmin;
233 }
234 
235 /* Can any cube from rect_box does not extend the right of query? */
236 static bool
237 overLeft4D(RectBox *rect_box, BOX2DF *query)
238 {
239  return rect_box->right.xmax <= query->xmax;
240 }
241 
242 /* Can any cube from rect_box be right of query? */
243 static bool
244 right4D(RectBox *rect_box, BOX2DF *query)
245 {
246  return rect_box->left.xmin >= query->xmax;
247 }
248 
249 /* Can any cube from rect_box does not extend the left of query? */
250 static bool
251 overRight4D(RectBox *rect_box, BOX2DF *query)
252 {
253  return rect_box->left.xmin >= query->xmin;
254 }
255 
256 /* Can any cube from rect_box be below of query? */
257 static bool
258 below4D(RectBox *rect_box, BOX2DF *query)
259 {
260  return rect_box->right.ymax <= query->ymin;
261 }
262 
263 /* Can any cube from rect_box does not extend above query? */
264 static bool
265 overBelow4D(RectBox *rect_box, BOX2DF *query)
266 {
267  return rect_box->right.ymax <= query->ymax;
268 }
269 
270 /* Can any cube from rect_box be above of query? */
271 static bool
272 above4D(RectBox *rect_box, BOX2DF *query)
273 {
274  return rect_box->left.ymin >= query->ymax;
275 }
276 
277 /* Can any cube from rect_box does not extend below of query? */
278 static bool
279 overAbove4D(RectBox *rect_box, BOX2DF *query)
280 {
281  return rect_box->left.ymin >= query->ymin;
282 }
283 
284 /*
285  * SP-GiST config function
286  */
287 
289 
290 PGDLLEXPORT Datum gserialized_spgist_config_2d(PG_FUNCTION_ARGS)
291 {
292  spgConfigOut *cfg = (spgConfigOut *)PG_GETARG_POINTER(1);
293  Oid boxoid = InvalidOid;
294  /* We need to initialize the internal cache to access it later via postgis_oid() */
295  postgis_initialize_cache(fcinfo);
296  boxoid = postgis_oid(BOX2DFOID);
297 
298  cfg->prefixType = boxoid;
299  cfg->labelType = VOIDOID; /* We don't need node labels. */
300  cfg->leafType = boxoid;
301  cfg->canReturnData = false;
302  cfg->longValuesOK = false;
303 
304  PG_RETURN_VOID();
305 }
306 
307 /*
308  * SP-GiST choose function
309  */
310 
312 
313 PGDLLEXPORT Datum gserialized_spgist_choose_2d(PG_FUNCTION_ARGS)
314 {
315  spgChooseIn *in = (spgChooseIn *)PG_GETARG_POINTER(0);
316  spgChooseOut *out = (spgChooseOut *)PG_GETARG_POINTER(1);
317  BOX2DF *centroid = (BOX2DF *)DatumGetPointer(in->prefixDatum), *box = (BOX2DF *)DatumGetPointer(in->leafDatum);
318 
319  out->resultType = spgMatchNode;
320  out->result.matchNode.restDatum = PointerGetDatum(box);
321 
322  /* nodeN will be set by core, when allTheSame. */
323  if (!in->allTheSame)
324  out->result.matchNode.nodeN = getQuadrant4D(centroid, box);
325 
326  PG_RETURN_VOID();
327 }
328 
329 /*
330  * SP-GiST pick-split function
331  *
332  * It splits a list of boxes into quadrants by choosing a central 4D
333  * point as the median of the coordinates of the boxes.
334  */
336 
337 PGDLLEXPORT Datum gserialized_spgist_picksplit_2d(PG_FUNCTION_ARGS)
338 {
339  spgPickSplitIn *in = (spgPickSplitIn *)PG_GETARG_POINTER(0);
340  spgPickSplitOut *out = (spgPickSplitOut *)PG_GETARG_POINTER(1);
341  BOX2DF *centroid;
342  int median, i;
343  double *lowXs = palloc(sizeof(double) * in->nTuples);
344  double *highXs = palloc(sizeof(double) * in->nTuples);
345  double *lowYs = palloc(sizeof(double) * in->nTuples);
346  double *highYs = palloc(sizeof(double) * in->nTuples);
347 
348  /* Calculate median of all 4D coordinates */
349  for (i = 0; i < in->nTuples; i++)
350  {
351  BOX2DF *box = (BOX2DF *)DatumGetPointer(in->datums[i]);
352 
353  lowXs[i] = (double)box->xmin;
354  highXs[i] = (double)box->xmax;
355  lowYs[i] = (double)box->ymin;
356  highYs[i] = (double)box->ymax;
357  }
358 
359  qsort(lowXs, in->nTuples, sizeof(double), compareDoubles);
360  qsort(highXs, in->nTuples, sizeof(double), compareDoubles);
361  qsort(lowYs, in->nTuples, sizeof(double), compareDoubles);
362  qsort(highYs, in->nTuples, sizeof(double), compareDoubles);
363 
364  median = in->nTuples / 2;
365 
366  centroid = palloc(sizeof(BOX2DF));
367 
368  centroid->xmin = (float)lowXs[median];
369  centroid->xmax = (float)highXs[median];
370  centroid->ymin = (float)lowYs[median];
371  centroid->ymax = (float)highYs[median];
372 
373  /* Fill the output */
374  out->hasPrefix = true;
375  out->prefixDatum = BoxPGetDatum(centroid);
376 
377  out->nNodes = 16;
378  out->nodeLabels = NULL; /* We don't need node labels. */
379 
380  out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples);
381  out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples);
382 
383  /*
384  * Assign ranges to corresponding nodes according to quadrants relative to the "centroid" range
385  */
386  for (i = 0; i < in->nTuples; i++)
387  {
388  BOX2DF *box = (BOX2DF *)DatumGetPointer(in->datums[i]);
389  uint8 quadrant = getQuadrant4D(centroid, box);
390 
391  out->leafTupleDatums[i] = PointerGetDatum(box);
392  out->mapTuplesToNodes[i] = quadrant;
393  }
394 
395  pfree(lowXs);
396  pfree(highXs);
397  pfree(lowYs);
398  pfree(highYs);
399 
400  PG_RETURN_VOID();
401 }
402 
403 /*
404  * SP-GiST inner consistent function
405  */
407 
408 PGDLLEXPORT Datum gserialized_spgist_inner_consistent_2d(PG_FUNCTION_ARGS)
409 {
410  spgInnerConsistentIn *in = (spgInnerConsistentIn *)PG_GETARG_POINTER(0);
411  spgInnerConsistentOut *out = (spgInnerConsistentOut *)PG_GETARG_POINTER(1);
412  int i;
413  MemoryContext old_ctx;
414  RectBox *rect_box;
415  uint8 quadrant;
416  BOX2DF *centroid;
417 
418  if (in->allTheSame)
419  {
420  /* Report that all nodes should be visited */
421  out->nNodes = in->nNodes;
422  out->nodeNumbers = (int *)palloc(sizeof(int) * in->nNodes);
423  for (i = 0; i < in->nNodes; i++)
424  out->nodeNumbers[i] = i;
425 
426  PG_RETURN_VOID();
427  }
428 
429  /*
430  * We are saving the traversal value or initialize it an unbounded one, if
431  * we have just begun to walk the tree.
432  */
433  if (in->traversalValue)
434  rect_box = in->traversalValue;
435  else
436  rect_box = initRectBox();
437 
438  centroid = (BOX2DF *)DatumGetPointer(in->prefixDatum);
439 
440  /* Allocate enough memory for nodes */
441  out->nNodes = 0;
442  out->nodeNumbers = (int *)palloc(sizeof(int) * in->nNodes);
443  out->traversalValues = (void **)palloc(sizeof(void *) * in->nNodes);
444 
445  /*
446  * We switch memory context, because we want to allocate memory for new
447  * traversal values (next_rect_box) and pass these pieces of memory to
448  * further call of this function.
449  */
450  old_ctx = MemoryContextSwitchTo(in->traversalMemoryContext);
451 
452  for (quadrant = 0; quadrant < in->nNodes; quadrant++)
453  {
454  RectBox *next_rect_box = nextRectBox(rect_box, centroid, quadrant);
455  bool flag = true;
456 
457  for (i = 0; i < in->nkeys; i++)
458  {
459  StrategyNumber strategy = in->scankeys[i].sk_strategy;
460 
461  Datum query = in->scankeys[i].sk_argument;
462  BOX2DF query_gbox_index;
463 
464  /* Quick sanity check on query argument. */
465  if (DatumGetPointer(query) == NULL)
466  {
467  POSTGIS_DEBUG(4, "[SPGIST] null query pointer (!?!), returning false");
468  PG_RETURN_BOOL(false); /* NULL query! This is screwy! */
469  }
470 
471  if (gserialized_datum_get_box2df_p(query, &query_gbox_index) == LW_FAILURE)
472  {
473  POSTGIS_DEBUG(4, "[SPGIST] null query_gbox_index!");
474  PG_RETURN_BOOL(false);
475  }
476 
477  switch (strategy)
478  {
479  case RTOverlapStrategyNumber:
480  case RTContainedByStrategyNumber:
481  case RTOldContainedByStrategyNumber:
482  flag = overlap4D(next_rect_box, &query_gbox_index);
483  break;
484 
485  case RTContainsStrategyNumber:
486  case RTSameStrategyNumber:
487  flag = contain4D(next_rect_box, &query_gbox_index);
488  break;
489 
490  case RTLeftStrategyNumber:
491  flag = !overRight4D(next_rect_box, &query_gbox_index);
492  break;
493 
494  case RTOverLeftStrategyNumber:
495  flag = !right4D(next_rect_box, &query_gbox_index);
496  break;
497 
498  case RTRightStrategyNumber:
499  flag = !overLeft4D(next_rect_box, &query_gbox_index);
500  break;
501 
502  case RTOverRightStrategyNumber:
503  flag = !left4D(next_rect_box, &query_gbox_index);
504  break;
505 
506  case RTAboveStrategyNumber:
507  flag = !overBelow4D(next_rect_box, &query_gbox_index);
508  break;
509 
510  case RTOverAboveStrategyNumber:
511  flag = !below4D(next_rect_box, &query_gbox_index);
512  break;
513 
514  case RTBelowStrategyNumber:
515  flag = !overAbove4D(next_rect_box, &query_gbox_index);
516  break;
517 
518  case RTOverBelowStrategyNumber:
519  flag = !above4D(next_rect_box, &query_gbox_index);
520  break;
521 
522  default:
523  elog(ERROR, "unrecognized strategy: %d", strategy);
524  }
525 
526  /* If any check is failed, we have found our answer. */
527  if (!flag)
528  break;
529  }
530 
531  if (flag)
532  {
533  out->traversalValues[out->nNodes] = next_rect_box;
534  out->nodeNumbers[out->nNodes] = quadrant;
535  out->nNodes++;
536  }
537  else
538  {
539  /*
540  * If this node is not selected, we don't need to keep the next
541  * traversal value in the memory context.
542  */
543  pfree(next_rect_box);
544  }
545  }
546 
547  /* Switch after */
548  MemoryContextSwitchTo(old_ctx);
549 
550  PG_RETURN_VOID();
551 }
552 
553 /*
554  * SP-GiST inner consistent function
555  */
557 
558 PGDLLEXPORT Datum gserialized_spgist_leaf_consistent_2d(PG_FUNCTION_ARGS)
559 {
560  spgLeafConsistentIn *in = (spgLeafConsistentIn *)PG_GETARG_POINTER(0);
561  spgLeafConsistentOut *out = (spgLeafConsistentOut *)PG_GETARG_POINTER(1);
562  BOX2DF *key = (BOX2DF *)DatumGetPointer(in->leafDatum);
563  bool flag = true;
564  int i;
565 
566  /* Quick sanity check on entry key. */
567  if (DatumGetPointer(key) == NULL)
568  {
569  POSTGIS_DEBUG(4, "[SPGIST] null index entry, returning false");
570  PG_RETURN_BOOL(false); /* NULL entry! */
571  }
572 
573  /* All tests are exact. */
574  out->recheck = false;
575 
576  /* leafDatum is what it is... */
577  out->leafValue = in->leafDatum;
578 
579  /* Perform the required comparison(s) */
580  for (i = 0; i < in->nkeys; i++)
581  {
582  StrategyNumber strategy = in->scankeys[i].sk_strategy;
583  Datum query = in->scankeys[i].sk_argument;
584  BOX2DF query_gbox_index;
585 
586  /* Quick sanity check on query argument. */
587  if (DatumGetPointer(query) == NULL)
588  {
589  POSTGIS_DEBUG(4, "[SPGIST] null query pointer (!?!), returning false");
590  PG_RETURN_BOOL(false); /* NULL query! This is screwy! */
591  }
592 
593  if (gserialized_datum_get_box2df_p(query, &query_gbox_index) == LW_FAILURE)
594  {
595  POSTGIS_DEBUG(4, "[SPGIST] null query_gbox_index!");
596  PG_RETURN_BOOL(false);
597  }
598 
599  switch (strategy)
600  {
601  case RTOverlapStrategyNumber:
602  flag = box2df_overlaps(key, &query_gbox_index);
603  break;
604 
605  case RTContainsStrategyNumber:
606  case RTOldContainsStrategyNumber:
607  flag = box2df_contains(key, &query_gbox_index);
608  break;
609 
610  case RTContainedByStrategyNumber:
611  case RTOldContainedByStrategyNumber:
612  flag = box2df_contains(&query_gbox_index, key);
613  break;
614 
615  case RTSameStrategyNumber:
616  flag = box2df_equals(key, &query_gbox_index);
617  break;
618 
619  case RTLeftStrategyNumber:
620  flag = box2df_left(key, &query_gbox_index);
621  break;
622 
623  case RTOverLeftStrategyNumber:
624  flag = box2df_overleft(key, &query_gbox_index);
625  break;
626 
627  case RTRightStrategyNumber:
628  flag = box2df_right(key, &query_gbox_index);
629  break;
630 
631  case RTOverRightStrategyNumber:
632  flag = box2df_overright(key, &query_gbox_index);
633  break;
634 
635  case RTAboveStrategyNumber:
636  flag = box2df_above(key, &query_gbox_index);
637  break;
638 
639  case RTOverAboveStrategyNumber:
640  flag = box2df_overabove(key, &query_gbox_index);
641  break;
642 
643  case RTBelowStrategyNumber:
644  flag = box2df_below(key, &query_gbox_index);
645  break;
646 
647  case RTOverBelowStrategyNumber:
648  flag = box2df_overbelow(key, &query_gbox_index);
649  break;
650 
651  default:
652  elog(ERROR, "unrecognized strategy: %d", strategy);
653  }
654 
655  /* If any check is failed, we have found our answer. */
656  if (!flag)
657  break;
658  }
659 
660  PG_RETURN_BOOL(flag);
661 }
662 
664 
665 PGDLLEXPORT Datum gserialized_spgist_compress_2d(PG_FUNCTION_ARGS)
666 {
667  Datum gsdatum = PG_GETARG_DATUM(0);
668  BOX2DF *bbox_out = palloc(sizeof(BOX2DF));
669  int result = LW_SUCCESS;
670 
671  POSTGIS_DEBUG(4, "[SPGIST] 'compress' function called");
672 
673  /* Extract the index key from the argument. */
674  result = gserialized_datum_get_box2df_p(gsdatum, bbox_out);
675 
676  /* Is the bounding box valid (non-empty, non-infinite)? If not, return input uncompressed. */
677  if (result == LW_FAILURE)
678  {
679  box2df_set_empty(bbox_out);
680 
681  POSTGIS_DEBUG(4, "[SPGIST] empty geometry!");
682  PG_RETURN_POINTER(bbox_out);
683  }
684 
685  /* Check all the dimensions for finite values */
686  if ((!isfinite(bbox_out->xmax) || !isfinite(bbox_out->xmin)) ||
687  (!isfinite(bbox_out->ymax) || !isfinite(bbox_out->ymin)))
688  {
689  box2df_set_finite(bbox_out);
690 
691  POSTGIS_DEBUG(4, "[SPGIST] infinite geometry!");
692  PG_RETURN_POINTER(bbox_out);
693  }
694 
695  /* Ensure bounding box has minimums below maximums. */
696  box2df_validate(bbox_out);
697 
698  /* Return BOX2DF. */
699  POSTGIS_DEBUG(4, "[SPGIST] 'compress' function complete");
700  PG_RETURN_POINTER(bbox_out);
701 }
702 
703 /*****************************************************************************/
bool box2df_left(const BOX2DF *a, const BOX2DF *b)
void box2df_set_empty(BOX2DF *a)
bool box2df_equals(const BOX2DF *a, const BOX2DF *b)
bool box2df_contains(const BOX2DF *a, const BOX2DF *b)
bool box2df_right(const BOX2DF *a, const BOX2DF *b)
bool box2df_overlaps(const BOX2DF *a, const BOX2DF *b)
void box2df_set_finite(BOX2DF *a)
bool box2df_above(const BOX2DF *a, const BOX2DF *b)
bool box2df_overbelow(const BOX2DF *a, const BOX2DF *b)
bool box2df_overright(const BOX2DF *a, const BOX2DF *b)
int gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df)
Peak into a GSERIALIZED datum to find the bounding box.
bool box2df_overabove(const BOX2DF *a, const BOX2DF *b)
void box2df_validate(BOX2DF *b)
bool box2df_below(const BOX2DF *a, const BOX2DF *b)
bool box2df_overleft(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_spgist_compress_2d(PG_FUNCTION_ARGS)
Datum gserialized_spgist_choose_2d(PG_FUNCTION_ARGS)
static bool right4D(RectBox *rect_box, BOX2DF *query)
static bool overRight4D(RectBox *rect_box, BOX2DF *query)
static bool overlap4D(RectBox *rect_box, BOX2DF *query)
static bool overAbove4D(RectBox *rect_box, BOX2DF *query)
static bool above4D(RectBox *rect_box, BOX2DF *query)
static int compareDoubles(const void *a, const void *b)
Datum gserialized_spgist_config_2d(PG_FUNCTION_ARGS)
static uint8 getQuadrant4D(BOX2DF *centroid, BOX2DF *inBox)
static RectBox * nextRectBox(RectBox *rect_box, BOX2DF *centroid, uint8 quadrant)
PG_FUNCTION_INFO_V1(gserialized_spgist_config_2d)
static RectBox * initRectBox(void)
static bool contain4D(RectBox *rect_box, BOX2DF *query)
static bool left4D(RectBox *rect_box, BOX2DF *query)
Datum gserialized_spgist_picksplit_2d(PG_FUNCTION_ARGS)
Datum gserialized_spgist_leaf_consistent_2d(PG_FUNCTION_ARGS)
static bool below4D(RectBox *rect_box, BOX2DF *query)
static bool overBelow4D(RectBox *rect_box, BOX2DF *query)
static bool overLeft4D(RectBox *rect_box, BOX2DF *query)
Datum gserialized_spgist_inner_consistent_2d(PG_FUNCTION_ARGS)
#define LW_FAILURE
Definition: liblwgeom.h:110
#define LW_SUCCESS
Definition: liblwgeom.h:111
This library is the generic geometry handling section of PostGIS.
Datum centroid(PG_FUNCTION_ARGS)