PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
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
90Datum gserialized_spgist_config_2d(PG_FUNCTION_ARGS);
91Datum gserialized_spgist_choose_2d(PG_FUNCTION_ARGS);
92Datum gserialized_spgist_picksplit_2d(PG_FUNCTION_ARGS);
93Datum gserialized_spgist_inner_consistent_2d(PG_FUNCTION_ARGS);
94Datum gserialized_spgist_leaf_consistent_2d(PG_FUNCTION_ARGS);
95Datum 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 */
104static int
105compareDoubles(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
115typedef 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 */
128static uint8
129getQuadrant4D(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 */
154static 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 */
182static RectBox *
183nextRectBox(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? */
213static bool
214overlap4D(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? */
221static bool
222contain4D(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? */
229static bool
230left4D(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? */
236static bool
237overLeft4D(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? */
243static bool
244right4D(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? */
250static bool
251overRight4D(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? */
257static bool
258below4D(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? */
264static bool
265overBelow4D(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? */
271static bool
272above4D(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? */
278static bool
279overAbove4D(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
290PGDLLEXPORT 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();
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
313PGDLLEXPORT 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
337PGDLLEXPORT 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 = PointerGetDatum(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
408PGDLLEXPORT 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 < (uint8)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
558PGDLLEXPORT 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 (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
665PGDLLEXPORT 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/*****************************************************************************/
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition cu_print.c:267
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)
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)
static RectBox * nextRectBox(RectBox *rect_box, BOX2DF *centroid, uint8 quadrant)
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 RectBox * initRectBox(void)
static int compareDoubles(const void *a, const void *b)
Datum gserialized_spgist_config_2d(PG_FUNCTION_ARGS)
static uint8 getQuadrant4D(BOX2DF *centroid, BOX2DF *inBox)
PG_FUNCTION_INFO_V1(gserialized_spgist_config_2d)
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:96
#define LW_SUCCESS
Definition liblwgeom.h:97
This library is the generic geometry handling section of PostGIS.
Datum centroid(PG_FUNCTION_ARGS)