PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
gserialized_gist_2d.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 * Copyright 2017-2019 Darafei Praliaskouski <me@komzpa.net>
23 *
24 **********************************************************************/
25
26/*
27** R-Tree Bibliography
28**
29** [1] A. Guttman. R-tree: a dynamic index structure for spatial searching.
30** Proceedings of the ACM SIGMOD Conference, pp 47-57, June 1984.
31** [2] C.-H. Ang and T. C. Tan. New linear node splitting algorithm for
32** R-Trees. Advances in Spatial Databases - 5th International Symposium,
33** 1997
34** [3] N. Beckmann, H.-P. Kriegel, R. Schneider, B. Seeger. The R*tree: an
35** efficient and robust access method for points and rectangles.
36** Proceedings of the ACM SIGMOD Conference. June 1990.
37** [4] A. Korotkov, "A new double sorting-based node splitting algorithm for R-tree",
38** http://syrcose.ispras.ru/2011/files/SYRCoSE2011_Proceedings.pdf#page=36
39*/
40
41#include "postgres.h"
42#include "access/gist.h" /* For GiST */
43#include "access/itup.h"
44#include "access/skey.h"
45
46#include "../postgis_config.h"
47
48#include "liblwgeom.h" /* For standard geometry types. */
49#include "lwgeom_pg.h" /* For debugging macros. */
50#include "gserialized_gist.h" /* For utility functions. */
51
52#include <float.h> /* For FLT_MAX */
53#include <math.h>
54
55/*
56** When is a node split not so good? If more than 90% of the entries
57** end up in one of the children.
58*/
59#define LIMIT_RATIO 0.1
60
61/*
62** For debugging
63*/
64#if POSTGIS_DEBUG_LEVEL > 0
65static int g2d_counter_leaf = 0;
66static int g2d_counter_internal = 0;
67#endif
68
69/*
70** GiST 2D key stubs
71*/
72Datum box2df_out(PG_FUNCTION_ARGS);
73Datum box2df_in(PG_FUNCTION_ARGS);
74
75/*
76** GiST 2D index function prototypes
77*/
78Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS);
79Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS);
80Datum gserialized_gist_decompress_2d(PG_FUNCTION_ARGS);
81Datum gserialized_gist_penalty_2d(PG_FUNCTION_ARGS);
82Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS);
83Datum gserialized_gist_union_2d(PG_FUNCTION_ARGS);
84Datum gserialized_gist_same_2d(PG_FUNCTION_ARGS);
85Datum gserialized_gist_distance_2d(PG_FUNCTION_ARGS);
86
87/*
88** GiST 2D operator prototypes
89*/
90Datum gserialized_same_2d(PG_FUNCTION_ARGS);
91Datum gserialized_within_2d(PG_FUNCTION_ARGS);
92Datum gserialized_contains_2d(PG_FUNCTION_ARGS);
93Datum gserialized_overlaps_2d(PG_FUNCTION_ARGS);
94Datum gserialized_left_2d(PG_FUNCTION_ARGS);
95Datum gserialized_right_2d(PG_FUNCTION_ARGS);
96Datum gserialized_above_2d(PG_FUNCTION_ARGS);
97Datum gserialized_below_2d(PG_FUNCTION_ARGS);
98Datum gserialized_overleft_2d(PG_FUNCTION_ARGS);
99Datum gserialized_overright_2d(PG_FUNCTION_ARGS);
100Datum gserialized_overabove_2d(PG_FUNCTION_ARGS);
101Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS);
102Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS);
103Datum gserialized_distance_centroid_2d(PG_FUNCTION_ARGS);
104Datum gserialized_contains_box2df_geom_2d(PG_FUNCTION_ARGS);
105Datum gserialized_contains_box2df_box2df_2d(PG_FUNCTION_ARGS);
106Datum gserialized_within_box2df_geom_2d(PG_FUNCTION_ARGS);
107Datum gserialized_within_box2df_box2df_2d(PG_FUNCTION_ARGS);
108Datum gserialized_overlaps_box2df_geom_2d(PG_FUNCTION_ARGS);
109Datum gserialized_overlaps_box2df_box2df_2d(PG_FUNCTION_ARGS);
110
111/*
112** true/false test function type
113*/
114typedef bool (*box2df_predicate)(const BOX2DF *a, const BOX2DF *b);
115
116
117static char* box2df_to_string(const BOX2DF *a)
118{
119 char *rv = NULL;
120
121 if ( a == NULL )
122 return pstrdup("<NULLPTR>");
123
124 rv = palloc(128);
125 sprintf(rv, "BOX2DF(%.12g %.12g, %.12g %.12g)", a->xmin, a->ymin, a->xmax, a->ymax);
126 return rv;
127}
128
129/* Allocate a new copy of BOX2DF */
130BOX2DF* box2df_copy(BOX2DF *b)
131{
132 BOX2DF *c = (BOX2DF*)palloc(sizeof(BOX2DF));
133 memcpy((void*)c, (void*)b, sizeof(BOX2DF));
134 POSTGIS_DEBUGF(5, "copied box2df (%p) to box2df (%p)", b, c);
135 return c;
136}
137
138inline bool box2df_is_empty(const BOX2DF *a)
139{
140 if (isnan(a->xmin))
141 return true;
142 else
143 return false;
144}
145
146inline void box2df_set_empty(BOX2DF *a)
147{
148 a->xmin = a->xmax = a->ymin = a->ymax = NAN;
149 return;
150}
151
152inline void box2df_set_finite(BOX2DF *a)
153{
154 if ( ! isfinite(a->xmax) )
155 a->xmax = FLT_MAX;
156 if ( ! isfinite(a->ymax) )
157 a->ymax = FLT_MAX;
158 if ( ! isfinite(a->ymin) )
159 a->ymin = -1*FLT_MAX;
160 if ( ! isfinite(a->xmin) )
161 a->xmin = -1*FLT_MAX;
162 return;
163}
164
165/* Enlarge b_union to contain b_new. */
166void box2df_merge(BOX2DF *b_union, BOX2DF *b_new)
167{
168
169 POSTGIS_DEBUGF(5, "merging %s with %s", box2df_to_string(b_union), box2df_to_string(b_new));
170 /* Adjust minimums */
171 if (b_union->xmin > b_new->xmin || isnan(b_union->xmin))
172 b_union->xmin = b_new->xmin;
173 if (b_union->ymin > b_new->ymin || isnan(b_union->ymin))
174 b_union->ymin = b_new->ymin;
175 /* Adjust maximums */
176 if (b_union->xmax < b_new->xmax || isnan(b_union->xmax))
177 b_union->xmax = b_new->xmax;
178 if (b_union->ymax < b_new->ymax || isnan(b_union->ymax))
179 b_union->ymax = b_new->ymax;
180
181 POSTGIS_DEBUGF(5, "merge complete %s", box2df_to_string(b_union));
182 return;
183}
184
185
186/* Convert a double-based GBOX into a float-based BOX2DF,
187 ensuring the float box is larger than the double box */
188static inline int box2df_from_gbox_p(GBOX *box, BOX2DF *a)
189{
190 a->xmin = next_float_down(box->xmin);
191 a->xmax = next_float_up(box->xmax);
192 a->ymin = next_float_down(box->ymin);
193 a->ymax = next_float_up(box->ymax);
194 return LW_SUCCESS;
195}
196
197int box2df_to_gbox_p(BOX2DF *a, GBOX *box)
198{
199 memset(box, 0, sizeof(GBOX));
200 box->xmin = a->xmin;
201 box->xmax = a->xmax;
202 box->ymin = a->ymin;
203 box->ymax = a->ymax;
204 return LW_SUCCESS;
205}
206
207/***********************************************************************
208** BOX2DF tests for 2D index operators.
209*/
210
211/* Ensure all minimums are below maximums. */
212inline void box2df_validate(BOX2DF *b)
213{
214 float tmp;
215
216 if ( box2df_is_empty(b) )
217 return;
218
219 if ( b->xmax < b->xmin )
220 {
221 tmp = b->xmin;
222 b->xmin = b->xmax;
223 b->xmax = tmp;
224 }
225 if ( b->ymax < b->ymin )
226 {
227 tmp = b->ymin;
228 b->ymin = b->ymax;
229 b->ymax = tmp;
230 }
231 return;
232}
233
234bool box2df_overlaps(const BOX2DF *a, const BOX2DF *b)
235{
236 if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
237 return false;
238
239 if ( (a->xmin > b->xmax) || (b->xmin > a->xmax) ||
240 (a->ymin > b->ymax) || (b->ymin > a->ymax) )
241 {
242 return false;
243 }
244
245 return true;
246}
247
248bool box2df_contains(const BOX2DF *a, const BOX2DF *b)
249{
250 if ( !a || !b )
251 return false;
252
253 /* All things can contain EMPTY (except EMPTY) */
254 if ( box2df_is_empty(b) && ! box2df_is_empty(a) )
255 return true;
256
257 if ( (a->xmin > b->xmin) || (a->xmax < b->xmax) ||
258 (a->ymin > b->ymin) || (a->ymax < b->ymax) )
259 {
260 return false;
261 }
262
263 return true;
264}
265
266static bool box2df_within(const BOX2DF *a, const BOX2DF *b)
267{
268 if ( !a || !b )
269 return false;
270
271 /* EMPTY is within all other things (except EMPTY) */
272 if ( box2df_is_empty(a) && ! box2df_is_empty(b) )
273 return true;
274
275 if ( (a->xmin < b->xmin) || (a->xmax > b->xmax) ||
276 (a->ymin < b->ymin) || (a->ymax > b->ymax) )
277 {
278 return false;
279 }
280
281 return true;
282}
283
284bool box2df_equals(const BOX2DF *a, const BOX2DF *b)
285{
286 if ( !a && !b )
287 return true;
288 else if ( !a || !b )
289 return false;
290 else if ( box2df_is_empty(a) && box2df_is_empty(b) )
291 return true;
292 else if ( box2df_is_empty(a) || box2df_is_empty(b) )
293 return false;
294 else if ((a->xmin == b->xmin) && (a->xmax == b->xmax) && (a->ymin == b->ymin) && (a->ymax == b->ymax))
295 return true;
296 else
297 return false;
298}
299
300bool box2df_overleft(const BOX2DF *a, const BOX2DF *b)
301{
302 if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
303 return false;
304
305 /* a.xmax <= b.xmax */
306 return a->xmax <= b->xmax;
307}
308
309bool box2df_left(const BOX2DF *a, const BOX2DF *b)
310{
311 if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
312 return false;
313
314 /* a.xmax < b.xmin */
315 return a->xmax < b->xmin;
316}
317
318bool box2df_right(const BOX2DF *a, const BOX2DF *b)
319{
320 if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
321 return false;
322
323 /* a.xmin > b.xmax */
324 return a->xmin > b->xmax;
325}
326
327bool box2df_overright(const BOX2DF *a, const BOX2DF *b)
328{
329 if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
330 return false;
331
332 /* a.xmin >= b.xmin */
333 return a->xmin >= b->xmin;
334}
335
336bool box2df_overbelow(const BOX2DF *a, const BOX2DF *b)
337{
338 if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
339 return false;
340
341 /* a.ymax <= b.ymax */
342 return a->ymax <= b->ymax;
343}
344
345bool box2df_below(const BOX2DF *a, const BOX2DF *b)
346{
347 if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
348 return false;
349
350 /* a.ymax < b.ymin */
351 return a->ymax < b->ymin;
352}
353
354bool box2df_above(const BOX2DF *a, const BOX2DF *b)
355{
356 if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
357 return false;
358
359 /* a.ymin > b.ymax */
360 return a->ymin > b->ymax;
361}
362
363bool box2df_overabove(const BOX2DF *a, const BOX2DF *b)
364{
365 if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
366 return false;
367
368 /* a.ymin >= b.ymin */
369 return a->ymin >= b->ymin;
370}
371
375static double box2df_distance_leaf_centroid(const BOX2DF *a, const BOX2DF *b)
376{
377 /* The centroid->centroid distance between the boxes */
378 double a_x = (a->xmax + a->xmin) / 2.0;
379 double a_y = (a->ymax + a->ymin) / 2.0;
380 double b_x = (b->xmax + b->xmin) / 2.0;
381 double b_y = (b->ymax + b->ymin) / 2.0;
382
383 /* This "distance" is only used for comparisons, */
384 return sqrt((a_x - b_x) * (a_x - b_x) + (a_y - b_y) * (a_y - b_y));
385}
386
387/* Quick distance function */
388static inline double pt_distance(double ax, double ay, double bx, double by)
389{
390 return sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by));
391}
392
396static double box2df_distance(const BOX2DF *a, const BOX2DF *b)
397{
398 /* Check for overlap */
399 if ( box2df_overlaps(a, b) )
400 return 0.0;
401
402 if ( box2df_left(a, b) )
403 {
404 if ( box2df_above(a, b) )
405 return pt_distance(a->xmax, a->ymin, b->xmin, b->ymax);
406 if ( box2df_below(a, b) )
407 return pt_distance(a->xmax, a->ymax, b->xmin, b->ymin);
408 else
409 return (double)b->xmin - (double)a->xmax;
410 }
411 if ( box2df_right(a, b) )
412 {
413 if ( box2df_above(a, b) )
414 return pt_distance(a->xmin, a->ymin, b->xmax, b->ymax);
415 if ( box2df_below(a, b) )
416 return pt_distance(a->xmin, a->ymax, b->xmax, b->ymin);
417 else
418 return (double)a->xmin - (double)b->xmax;
419 }
420 if ( box2df_above(a, b) )
421 {
422 if ( box2df_left(a, b) )
423 return pt_distance(a->xmax, a->ymin, b->xmin, b->ymax);
424 if ( box2df_right(a, b) )
425 return pt_distance(a->xmin, a->ymin, b->xmax, b->ymax);
426 else
427 return (double)a->ymin - (double)b->ymax;
428 }
429 if ( box2df_below(a, b) )
430 {
431 if ( box2df_left(a, b) )
432 return pt_distance(a->xmax, a->ymax, b->xmin, b->ymin);
433 if ( box2df_right(a, b) )
434 return pt_distance(a->xmin, a->ymax, b->xmax, b->ymin);
435 else
436 return (double)b->ymin - (double)a->ymax;
437 }
438
439 return FLT_MAX;
440}
441
442
449int
450gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df)
451{
452 GSERIALIZED *gpart;
453 int result = LW_SUCCESS;
454
455 POSTGIS_DEBUG(4, "entered function");
456
457 /*
458 ** Because geometry is declared as "storage = main" anything large
459 ** enough to take serious advantage of PG_DETOAST_DATUM_SLICE will have
460 ** already been compressed, which means the entire object will be
461 ** fetched and decompressed before a slice is taken, thus removing
462 ** any efficiencies gained from slicing.
463 ** As of Pg12 we can partially decompress a toasted object
464 ** (though we still need to fully retrieve it from TOAST)
465 ** which makes slicing worthwhile.
466 */
467 gpart = (GSERIALIZED*)PG_DETOAST_DATUM(gsdatum);
468
469 POSTGIS_DEBUGF(4, "got flags %d", gpart->gflags);
470
471 /* Do we even have a serialized bounding box? */
472 if (gserialized_has_bbox(gpart))
473 {
474 /* Yes! Copy it out into the box! */
475 size_t box_ndims;
476 const float *f = gserialized_get_float_box_p(gpart, &box_ndims);
477
478 POSTGIS_DEBUG(4, "copying box out of serialization");
479 memcpy(box2df, f, sizeof(BOX2DF));
480 result = LW_SUCCESS;
481 }
482 else
483 {
484 /* No, we need to calculate it from the full object. */
485 GBOX gbox;
486 gbox_init(&gbox);
487
488 result = gserialized_get_gbox_p(gpart, &gbox);
489 if ( result == LW_SUCCESS )
490 {
491 result = box2df_from_gbox_p(&gbox, box2df);
492 }
493 else
494 {
495 POSTGIS_DEBUG(4, "could not calculate bbox");
496 }
497 }
498
499 POSTGIS_FREE_IF_COPY_P(gpart, gsdatum);
500 POSTGIS_DEBUGF(4, "result = %d, got box2df %s", result, result == LW_SUCCESS ? box2df_to_string(box2df) : "NONE");
501
502 return result;
503}
504
505
510static int
511gserialized_datum_predicate_2d(Datum gs1, Datum gs2, box2df_predicate predicate)
512{
513 BOX2DF b1, b2, *br1=NULL, *br2=NULL;
514 POSTGIS_DEBUG(3, "entered function");
515
516 if (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) br1 = &b1;
517 if (gserialized_datum_get_box2df_p(gs2, &b2) == LW_SUCCESS) br2 = &b2;
518
519 if ( predicate(br1, br2) )
520 {
521 POSTGIS_DEBUGF(3, "got boxes %s and %s", br1 ? box2df_to_string(&b1) : "(null)", br2 ? box2df_to_string(&b2) : "(null)");
522 return LW_TRUE;
523 }
524 return LW_FALSE;
525}
526
527static int
528gserialized_datum_predicate_box2df_geom_2d(const BOX2DF *br1, Datum gs2, box2df_predicate predicate)
529{
530 BOX2DF b2, *br2=NULL;
531 POSTGIS_DEBUG(3, "entered function");
532
533 if (gserialized_datum_get_box2df_p(gs2, &b2) == LW_SUCCESS) br2 = &b2;
534
535 if ( predicate(br1, br2) )
536 {
537 POSTGIS_DEBUGF(3, "got boxes %s", br2 ? box2df_to_string(&b2) : "(null)");
538 return LW_TRUE;
539 }
540 return LW_FALSE;
541}
542
543/***********************************************************************
544* BRIN 2-D Index Operator Functions
545*/
546
549{
550 POSTGIS_DEBUG(3, "entered function");
551 if ( gserialized_datum_predicate_box2df_geom_2d((BOX2DF*)PG_GETARG_POINTER(0), PG_GETARG_DATUM(1), box2df_contains) == LW_TRUE )
552 PG_RETURN_BOOL(true);
553
554 PG_RETURN_BOOL(false);
555}
556
559{
560 if ( box2df_contains((BOX2DF *)PG_GETARG_POINTER(0), (BOX2DF *)PG_GETARG_POINTER(1)))
561 PG_RETURN_BOOL(true);
562
563 PG_RETURN_BOOL(false);
564}
565
568{
569 POSTGIS_DEBUG(3, "entered function");
570 if ( gserialized_datum_predicate_box2df_geom_2d((BOX2DF*)PG_GETARG_POINTER(0), PG_GETARG_DATUM(1), box2df_within) == LW_TRUE )
571 PG_RETURN_BOOL(true);
572
573 PG_RETURN_BOOL(false);
574}
575
578{
579 if ( box2df_within((BOX2DF *)PG_GETARG_POINTER(0), (BOX2DF *)PG_GETARG_POINTER(1)))
580 PG_RETURN_BOOL(true);
581
582 PG_RETURN_BOOL(false);
583}
584
587{
588 POSTGIS_DEBUG(3, "entered function");
589 if ( gserialized_datum_predicate_box2df_geom_2d((BOX2DF*)PG_GETARG_POINTER(0), PG_GETARG_DATUM(1), box2df_overlaps) == LW_TRUE )
590 PG_RETURN_BOOL(true);
591
592 PG_RETURN_BOOL(false);
593}
594
597{
598 if ( box2df_overlaps((BOX2DF *)PG_GETARG_POINTER(0), (BOX2DF *)PG_GETARG_POINTER(1)))
599 PG_RETURN_BOOL(true);
600
601 PG_RETURN_BOOL(false);
602}
603
604/***********************************************************************
605* GiST 2-D Index Operator Functions
606*/
607
609Datum gserialized_distance_centroid_2d(PG_FUNCTION_ARGS)
610{
611 BOX2DF b1, b2;
612 Datum gs1 = PG_GETARG_DATUM(0);
613 Datum gs2 = PG_GETARG_DATUM(1);
614
615 POSTGIS_DEBUG(3, "entered function");
616
617 /* Must be able to build box for each argument (ie, not empty geometry). */
618 if ( (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) &&
620 {
621 double distance = box2df_distance_leaf_centroid(&b1, &b2);
622 POSTGIS_DEBUGF(3, "got boxes %s and %s", box2df_to_string(&b1), box2df_to_string(&b2));
623 PG_RETURN_FLOAT8(distance);
624 }
625 PG_RETURN_FLOAT8(FLT_MAX);
626}
627
629Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS)
630{
631 BOX2DF b1, b2;
632 Datum gs1 = PG_GETARG_DATUM(0);
633 Datum gs2 = PG_GETARG_DATUM(1);
634
635 POSTGIS_DEBUG(3, "entered function");
636
637 /* Must be able to build box for each argument (ie, not empty geometry). */
638 if ( (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) &&
640 {
641 double distance = box2df_distance(&b1, &b2);
642 POSTGIS_DEBUGF(3, "got boxes %s and %s", box2df_to_string(&b1), box2df_to_string(&b2));
643 PG_RETURN_FLOAT8(distance);
644 }
645 PG_RETURN_FLOAT8(FLT_MAX);
646}
647
649Datum gserialized_same_2d(PG_FUNCTION_ARGS)
650{
651 if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_equals) == LW_TRUE )
652 PG_RETURN_BOOL(true);
653
654 PG_RETURN_BOOL(false);
655}
656
658Datum gserialized_within_2d(PG_FUNCTION_ARGS)
659{
660 POSTGIS_DEBUG(3, "entered function");
661 if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_within) == LW_TRUE )
662 PG_RETURN_BOOL(true);
663
664 PG_RETURN_BOOL(false);
665}
666
668Datum gserialized_contains_2d(PG_FUNCTION_ARGS)
669{
670 POSTGIS_DEBUG(3, "entered function");
671 if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_contains) == LW_TRUE )
672 PG_RETURN_BOOL(true);
673
674 PG_RETURN_BOOL(false);
675}
676
678Datum gserialized_overlaps_2d(PG_FUNCTION_ARGS)
679{
680 if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overlaps) == LW_TRUE )
681 PG_RETURN_BOOL(true);
682
683 PG_RETURN_BOOL(false);
684}
685
687Datum gserialized_left_2d(PG_FUNCTION_ARGS)
688{
689 if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_left) == LW_TRUE )
690 PG_RETURN_BOOL(true);
691
692 PG_RETURN_BOOL(false);
693}
694
696Datum gserialized_right_2d(PG_FUNCTION_ARGS)
697{
698 if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_right) == LW_TRUE )
699 PG_RETURN_BOOL(true);
700
701 PG_RETURN_BOOL(false);
702}
703
705Datum gserialized_above_2d(PG_FUNCTION_ARGS)
706{
707 if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_above) == LW_TRUE )
708 PG_RETURN_BOOL(true);
709
710 PG_RETURN_BOOL(false);
711}
712
714Datum gserialized_below_2d(PG_FUNCTION_ARGS)
715{
716 if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_below) == LW_TRUE )
717 PG_RETURN_BOOL(true);
718
719 PG_RETURN_BOOL(false);
720}
721
723Datum gserialized_overleft_2d(PG_FUNCTION_ARGS)
724{
725 if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overleft) == LW_TRUE )
726 PG_RETURN_BOOL(true);
727
728 PG_RETURN_BOOL(false);
729}
730
732Datum gserialized_overright_2d(PG_FUNCTION_ARGS)
733{
734 if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overright) == LW_TRUE )
735 PG_RETURN_BOOL(true);
736
737 PG_RETURN_BOOL(false);
738}
739
741Datum gserialized_overabove_2d(PG_FUNCTION_ARGS)
742{
743 if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overabove) == LW_TRUE )
744 PG_RETURN_BOOL(true);
745
746 PG_RETURN_BOOL(false);
747}
748
750Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS)
751{
752 if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overbelow) == LW_TRUE )
753 PG_RETURN_BOOL(true);
754
755 PG_RETURN_BOOL(false);
756}
757
758
759/***********************************************************************
760* GiST Index Support Functions
761*/
762
763/*
764** GiST support function. Given a geography, return a "compressed"
765** version. In this case, we convert the geography into a geocentric
766** bounding box. If the geography already has the box embedded in it
767** we pull that out and hand it back.
768*/
770Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS)
771{
772 GISTENTRY *entry_in = (GISTENTRY*)PG_GETARG_POINTER(0);
773 GISTENTRY *entry_out = NULL;
774 BOX2DF bbox_out;
775 int result = LW_SUCCESS;
776
777 POSTGIS_DEBUG(4, "[GIST] 'compress' function called");
778
779 /*
780 ** Not a leaf key? There's nothing to do.
781 ** Return the input unchanged.
782 */
783 if ( ! entry_in->leafkey )
784 {
785 POSTGIS_DEBUG(4, "[GIST] non-leafkey entry, returning input unaltered");
786 PG_RETURN_POINTER(entry_in);
787 }
788
789 POSTGIS_DEBUG(4, "[GIST] processing leafkey input");
790 entry_out = palloc(sizeof(GISTENTRY));
791
792 /*
793 ** Null key? Make a copy of the input entry and
794 ** return.
795 */
796 if ( DatumGetPointer(entry_in->key) == NULL )
797 {
798 POSTGIS_DEBUG(4, "[GIST] leafkey is null");
799 gistentryinit(*entry_out, (Datum) 0, entry_in->rel,
800 entry_in->page, entry_in->offset, false);
801 POSTGIS_DEBUG(4, "[GIST] returning copy of input");
802 PG_RETURN_POINTER(entry_out);
803 }
804
805 /* Extract our index key from the GiST entry. */
806 result = gserialized_datum_get_box2df_p(entry_in->key, &bbox_out);
807
808 /* Is the bounding box valid (non-empty, non-infinite)? If not, return input uncompressed. */
809 if ( result == LW_FAILURE )
810 {
811 box2df_set_empty(&bbox_out);
812 gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)),
813 entry_in->rel, entry_in->page, entry_in->offset, false);
814
815 POSTGIS_DEBUG(4, "[GIST] empty geometry!");
816 PG_RETURN_POINTER(entry_out);
817 }
818
819 POSTGIS_DEBUGF(4, "[GIST] got entry_in->key: %s", box2df_to_string(&bbox_out));
820
821 /* Check all the dimensions for finite values */
822 if ( ! isfinite(bbox_out.xmax) || ! isfinite(bbox_out.xmin) ||
823 ! isfinite(bbox_out.ymax) || ! isfinite(bbox_out.ymin) )
824 {
825 box2df_set_finite(&bbox_out);
826 gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)),
827 entry_in->rel, entry_in->page, entry_in->offset, false);
828
829 POSTGIS_DEBUG(4, "[GIST] infinite geometry!");
830 PG_RETURN_POINTER(entry_out);
831 }
832
833 /* Enure bounding box has minimums below maximums. */
834 box2df_validate(&bbox_out);
835
836 /* Prepare GISTENTRY for return. */
837 gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)),
838 entry_in->rel, entry_in->page, entry_in->offset, false);
839
840 /* Return GISTENTRY. */
841 POSTGIS_DEBUG(4, "[GIST] 'compress' function complete");
842 PG_RETURN_POINTER(entry_out);
843}
844
845
846/*
847** GiST support function.
848** Decompress an entry. Unused for geography, so we return.
849*/
851Datum gserialized_gist_decompress_2d(PG_FUNCTION_ARGS)
852{
853 POSTGIS_DEBUG(5, "[GIST] 'decompress' function called");
854 /* We don't decompress. Just return the input. */
855 PG_RETURN_POINTER(PG_GETARG_POINTER(0));
856}
857
858
859
860/*
861** GiST support function. Called from gserialized_gist_consistent below.
862*/
863static inline bool gserialized_gist_consistent_leaf_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
864{
865 bool retval;
866
867 POSTGIS_DEBUGF(4, "[GIST] leaf consistent, strategy [%d], count[%i]",
868 strategy, g2d_counter_leaf++);
869
870 switch (strategy)
871 {
872
873 /* Basic overlaps */
874 case RTOverlapStrategyNumber:
875 retval = (bool) box2df_overlaps(key, query);
876 break;
877 case RTSameStrategyNumber:
878 retval = (bool) box2df_equals(key, query);
879 break;
880 case RTContainsStrategyNumber:
881 case RTOldContainsStrategyNumber:
882 retval = (bool) box2df_contains(key, query);
883 break;
884 case RTContainedByStrategyNumber:
885 case RTOldContainedByStrategyNumber:
886 retval = (bool) box2df_within(key, query);
887 break;
888
889 /* To one side */
890 case RTAboveStrategyNumber:
891 retval = (bool) box2df_above(key, query);
892 break;
893 case RTBelowStrategyNumber:
894 retval = (bool) box2df_below(key, query);
895 break;
896 case RTRightStrategyNumber:
897 retval = (bool) box2df_right(key, query);
898 break;
899 case RTLeftStrategyNumber:
900 retval = (bool) box2df_left(key, query);
901 break;
902
903 /* Overlapping to one side */
904 case RTOverAboveStrategyNumber:
905 retval = (bool) box2df_overabove(key, query);
906 break;
907 case RTOverBelowStrategyNumber:
908 retval = (bool) box2df_overbelow(key, query);
909 break;
910 case RTOverRightStrategyNumber:
911 retval = (bool) box2df_overright(key, query);
912 break;
913 case RTOverLeftStrategyNumber:
914 retval = (bool) box2df_overleft(key, query);
915 break;
916
917 default:
918 retval = false;
919 }
920
921 return (retval);
922}
923
924/*
925** GiST support function. Called from gserialized_gist_consistent below.
926*/
927static inline bool gserialized_gist_consistent_internal_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
928{
929 bool retval;
930
931 POSTGIS_DEBUGF(4, "[GIST] internal consistent, strategy [%d], count[%i], query[%s], key[%s]",
932 strategy, g2d_counter_internal++, box2df_to_string(query), box2df_to_string(key) );
933
934 switch (strategy)
935 {
936
937 /* Basic overlaps */
938 case RTOverlapStrategyNumber:
939 retval = (bool) box2df_overlaps(key, query);
940 break;
941 case RTSameStrategyNumber:
942 case RTContainsStrategyNumber:
943 case RTOldContainsStrategyNumber:
944 retval = (bool) box2df_contains(key, query);
945 break;
946 case RTContainedByStrategyNumber:
947 case RTOldContainedByStrategyNumber:
948 retval = (bool) box2df_overlaps(key, query);
949 break;
950
951 /* To one side */
952 case RTAboveStrategyNumber:
953 retval = (bool)(!box2df_overbelow(key, query));
954 break;
955 case RTBelowStrategyNumber:
956 retval = (bool)(!box2df_overabove(key, query));
957 break;
958 case RTRightStrategyNumber:
959 retval = (bool)(!box2df_overleft(key, query));
960 break;
961 case RTLeftStrategyNumber:
962 retval = (bool)(!box2df_overright(key, query));
963 break;
964
965 /* Overlapping to one side */
966 case RTOverAboveStrategyNumber:
967 retval = (bool)(!box2df_below(key, query));
968 break;
969 case RTOverBelowStrategyNumber:
970 retval = (bool)(!box2df_above(key, query));
971 break;
972 case RTOverRightStrategyNumber:
973 retval = (bool)(!box2df_left(key, query));
974 break;
975 case RTOverLeftStrategyNumber:
976 retval = (bool)(!box2df_right(key, query));
977 break;
978
979 default:
980 retval = false;
981 }
982
983 return (retval);
984}
985
986/*
987** GiST support function. Take in a query and an entry and see what the
988** relationship is, based on the query strategy.
989*/
991Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS)
992{
993 GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
994 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
995 bool result;
996 BOX2DF query_gbox_index;
997
998 /* PostgreSQL 8.4 and later require the RECHECK flag to be set here,
999 rather than being supplied as part of the operator class definition */
1000 bool *recheck = (bool *) PG_GETARG_POINTER(4);
1001
1002 /* We set recheck to false to avoid repeatedly pulling every "possibly matched" geometry
1003 out during index scans. For cases when the geometries are large, rechecking
1004 can make things twice as slow. */
1005 *recheck = false;
1006
1007 POSTGIS_DEBUG(4, "[GIST] 'consistent' function called");
1008
1009 /* Quick sanity check on query argument. */
1010 if ( DatumGetPointer(PG_GETARG_DATUM(1)) == NULL )
1011 {
1012 POSTGIS_DEBUG(4, "[GIST] null query pointer (!?!), returning false");
1013 PG_RETURN_BOOL(false); /* NULL query! This is screwy! */
1014 }
1015
1016 /* Quick sanity check on entry key. */
1017 if ( DatumGetPointer(entry->key) == NULL )
1018 {
1019 POSTGIS_DEBUG(4, "[GIST] null index entry, returning false");
1020 PG_RETURN_BOOL(false); /* NULL entry! */
1021 }
1022
1023 /* Null box should never make this far. */
1024 if ( gserialized_datum_get_box2df_p(PG_GETARG_DATUM(1), &query_gbox_index) == LW_FAILURE )
1025 {
1026 POSTGIS_DEBUG(4, "[GIST] null query_gbox_index!");
1027 PG_RETURN_BOOL(false);
1028 }
1029
1030 /* Treat leaf node tests different from internal nodes */
1031 if (GIST_LEAF(entry))
1032 {
1034 (BOX2DF*)DatumGetPointer(entry->key),
1035 &query_gbox_index, strategy);
1036 }
1037 else
1038 {
1040 (BOX2DF*)DatumGetPointer(entry->key),
1041 &query_gbox_index, strategy);
1042 }
1043
1044 PG_RETURN_BOOL(result);
1045}
1046
1047
1048/*
1049** GiST support function. Take in a query and an entry and return the "distance"
1050** between them.
1051**
1052** Given an index entry p and a query value q, this function determines the
1053** index entry's "distance" from the query value. This function must be
1054** supplied if the operator class contains any ordering operators. A query
1055** using the ordering operator will be implemented by returning index entries
1056** with the smallest "distance" values first, so the results must be consistent
1057** with the operator's semantics. For a leaf index entry the result just
1058** represents the distance to the index entry; for an internal tree node, the
1059** result must be the smallest distance that any child entry could have.
1060**
1061** Strategy 13 = true distance tests <->
1062** Strategy 14 = box-based distance tests <#>
1063*/
1065Datum gserialized_gist_distance_2d(PG_FUNCTION_ARGS)
1066{
1067 GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
1068 BOX2DF query_box;
1069 BOX2DF *entry_box;
1070 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1071 double distance;
1072 bool *recheck = (bool *) PG_GETARG_POINTER(4);
1073
1074 POSTGIS_DEBUG(4, "[GIST] 'distance' function called");
1075
1076 /* We are using '13' as the gist true-distance <-> strategy number
1077 * and '14' as the gist distance-between-boxes <#> strategy number */
1078 if ( strategy != 13 && strategy != 14 ) {
1079 elog(ERROR, "unrecognized strategy number: %d", strategy);
1080 PG_RETURN_FLOAT8(FLT_MAX);
1081 }
1082
1083 /* Null box should never make this far. */
1084 if ( gserialized_datum_get_box2df_p(PG_GETARG_DATUM(1), &query_box) == LW_FAILURE )
1085 {
1086 POSTGIS_DEBUG(4, "[GIST] null query_gbox_index!");
1087 PG_RETURN_FLOAT8(FLT_MAX);
1088 }
1089
1090 /* Get the entry box */
1091 entry_box = (BOX2DF*)DatumGetPointer(entry->key);
1092
1093 /* Box-style distance test */
1094 if ( strategy == 14 ) /* operator <#> */
1095 {
1096 distance = box2df_distance(entry_box, &query_box);
1097 }
1098 /* True distance test (formerly centroid distance) */
1099 else if ( strategy == 13 ) /* operator <-> */
1100 {
1101 /* In all cases, since we only have keys (boxes) we'll return */
1102 /* the minimum possible distance, which is the box2df_distance */
1103 /* and let the recheck sort things out in the case of leaves */
1104 distance = box2df_distance(entry_box, &query_box);
1105
1106 if (GIST_LEAF(entry))
1107 *recheck = true;
1108 }
1109 else
1110 {
1111 elog(ERROR, "%s: reached unreachable code", __func__);
1112 PG_RETURN_NULL();
1113 }
1114
1115 PG_RETURN_FLOAT8(distance);
1116}
1117
1118/*
1119** Function to pack floats of different realms.
1120** This function serves to pack bit flags inside float type.
1121** Result value represent can be from two different "realms".
1122** Every value from realm 1 is greater than any value from realm 0.
1123** Values from the same realm loose one bit of precision.
1124**
1125** This technique is possible due to floating point numbers specification
1126** according to IEEE 754: exponent bits are highest
1127** (excluding sign bits, but here penalty is always positive). If float a is
1128** greater than float b, integer A with same bit representation as a is greater
1129** than integer B with same bits as b.
1130*/
1131static inline float
1132pack_float(const float value, const uint8_t realm)
1133{
1134 union {
1135 float f;
1136 struct {
1137 unsigned value : 31, sign : 1;
1138 } vbits;
1139 struct {
1140 unsigned value : 30, realm : 1, sign : 1;
1141 } rbits;
1142 } a;
1143
1144 a.f = value;
1145 a.rbits.value = a.vbits.value >> 1;
1146 a.rbits.realm = realm;
1147
1148 return a.f;
1149}
1150
1151static inline float
1152box2df_penalty(const BOX2DF *b1, const BOX2DF *b2)
1153{
1154 float b1xmin = b1->xmin, b1xmax = b1->xmax;
1155 float b1ymin = b1->ymin, b1ymax = b1->ymax;
1156 float b2xmin = b2->xmin, b2xmax = b2->xmax;
1157 float b2ymin = b2->ymin, b2ymax = b2->ymax;
1158
1159 float box_union_xmin = Min(b1xmin, b2xmin), box_union_xmax = Max(b1xmax, b2xmax);
1160 float box_union_ymin = Min(b1ymin, b2ymin), box_union_ymax = Max(b1ymax, b2ymax);
1161
1162 float b1dx = b1xmax - b1xmin, b1dy = b1ymax - b1ymin;
1163 float box_union_dx = box_union_xmax - box_union_xmin, box_union_dy = box_union_ymax - box_union_ymin;
1164
1165 float box_union_area = box_union_dx * box_union_dy, box1area = b1dx * b1dy;
1166 float box_union_edge = box_union_dx + box_union_dy, box1edge = b1dx + b1dy;
1167
1168 float area_extension = box_union_area - box1area;
1169 float edge_extension = box_union_edge - box1edge;
1170
1171 /* REALM 1: Area extension is nonzero, return it */
1172 if (area_extension > FLT_EPSILON)
1173 return pack_float(area_extension, 1);
1174 /* REALM 0: Area extension is zero, return nonzero edge extension */
1175 else if (edge_extension > FLT_EPSILON)
1176 return pack_float(edge_extension, 0);
1177
1178 return 0;
1179}
1180
1181/*
1182** GiST support function. Calculate the "penalty" cost of adding this entry into an existing entry.
1183** Calculate the change in volume of the old entry once the new entry is added.
1184*/
1186Datum gserialized_gist_penalty_2d(PG_FUNCTION_ARGS)
1187{
1188 GISTENTRY *origentry = (GISTENTRY*) PG_GETARG_POINTER(0);
1189 GISTENTRY *newentry = (GISTENTRY*) PG_GETARG_POINTER(1);
1190 float *result = (float*) PG_GETARG_POINTER(2);
1191 BOX2DF *b1, *b2;
1192
1193 b1 = (BOX2DF *)DatumGetPointer(origentry->key);
1194 b2 = (BOX2DF *)DatumGetPointer(newentry->key);
1195
1196 /* Penalty value of 0 has special code path in Postgres's gistchoose.
1197 * It is used as an early exit condition in subtree loop, allowing faster tree descend.
1198 * For multi-column index, it lets next column break the tie, possibly more confidently.
1199 */
1200 *result = 0;
1201
1202 if (b1 && b2 && !box2df_is_empty(b1) && !box2df_is_empty(b2))
1203 *result = box2df_penalty(b1, b2);
1204
1205 PG_RETURN_POINTER(result);
1206}
1207
1208/*
1209** GiST support function. Merge all the boxes in a page into one master box.
1210*/
1212Datum gserialized_gist_union_2d(PG_FUNCTION_ARGS)
1213{
1214 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
1215 int *sizep = (int *) PG_GETARG_POINTER(1); /* Size of the return value */
1216 int numranges, i;
1217 BOX2DF *box_cur, *box_union;
1218
1219 POSTGIS_DEBUG(4, "[GIST] 'union' function called");
1220
1221 numranges = entryvec->n;
1222
1223 box_cur = (BOX2DF*) DatumGetPointer(entryvec->vector[0].key);
1224
1225 box_union = box2df_copy(box_cur);
1226
1227 for ( i = 1; i < numranges; i++ )
1228 {
1229 box_cur = (BOX2DF*) DatumGetPointer(entryvec->vector[i].key);
1230 box2df_merge(box_union, box_cur);
1231 }
1232
1233 *sizep = sizeof(BOX2DF);
1234
1235 POSTGIS_DEBUGF(4, "[GIST] 'union', numranges(%i), pageunion %s", numranges, box2df_to_string(box_union));
1236
1237 PG_RETURN_POINTER(box_union);
1238
1239}
1240
1241/*
1242** GiST support function. Test equality of keys.
1243*/
1245Datum gserialized_gist_same_2d(PG_FUNCTION_ARGS)
1246{
1247 BOX2DF *b1 = (BOX2DF*)PG_GETARG_POINTER(0);
1248 BOX2DF *b2 = (BOX2DF*)PG_GETARG_POINTER(1);
1249 bool *result = (bool*)PG_GETARG_POINTER(2);
1250
1251 POSTGIS_DEBUG(4, "[GIST] 'same' function called");
1252
1253 *result = box2df_equals(b1, b2);
1254
1255 PG_RETURN_POINTER(result);
1256}
1257
1258/*
1259 * Adjust BOX2DF b boundaries with insertion of addon.
1260 */
1261static void
1262adjustBox(BOX2DF *b, BOX2DF *addon)
1263{
1264 if (b->xmax < addon->xmax || isnan(b->xmax))
1265 b->xmax = addon->xmax;
1266 if (b->xmin > addon->xmin || isnan(b->xmin))
1267 b->xmin = addon->xmin;
1268 if (b->ymax < addon->ymax || isnan(b->ymax))
1269 b->ymax = addon->ymax;
1270 if (b->ymin > addon->ymin || isnan(b->ymin))
1271 b->ymin = addon->ymin;
1272}
1273
1274/*
1275 * Trivial split: half of entries will be placed on one page
1276 * and another half - to another
1277 */
1278static void
1279fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
1280{
1281 OffsetNumber i,
1282 maxoff;
1283 BOX2DF *unionL = NULL,
1284 *unionR = NULL;
1285 int nbytes;
1286
1287 maxoff = entryvec->n - 1;
1288
1289 nbytes = (maxoff + 2) * sizeof(OffsetNumber);
1290 v->spl_left = (OffsetNumber *) palloc(nbytes);
1291 v->spl_right = (OffsetNumber *) palloc(nbytes);
1292 v->spl_nleft = v->spl_nright = 0;
1293
1294 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1295 {
1296 BOX2DF *cur = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1297
1298 if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
1299 {
1300 v->spl_left[v->spl_nleft] = i;
1301 if (unionL == NULL)
1302 {
1303 unionL = (BOX2DF *) palloc(sizeof(BOX2DF));
1304 *unionL = *cur;
1305 }
1306 else
1307 adjustBox(unionL, cur);
1308
1309 v->spl_nleft++;
1310 }
1311 else
1312 {
1313 v->spl_right[v->spl_nright] = i;
1314 if (unionR == NULL)
1315 {
1316 unionR = (BOX2DF *) palloc(sizeof(BOX2DF));
1317 *unionR = *cur;
1318 }
1319 else
1320 adjustBox(unionR, cur);
1321
1322 v->spl_nright++;
1323 }
1324 }
1325
1326 if (v->spl_ldatum_exists)
1327 adjustBox(unionL, (BOX2DF *) DatumGetPointer(v->spl_ldatum));
1328 v->spl_ldatum = BoxPGetDatum(unionL);
1329
1330 if (v->spl_rdatum_exists)
1331 adjustBox(unionR, (BOX2DF *) DatumGetPointer(v->spl_rdatum));
1332 v->spl_rdatum = BoxPGetDatum(unionR);
1333
1334 v->spl_ldatum_exists = v->spl_rdatum_exists = false;
1335}
1336
1337/*
1338 * Represents information about an entry that can be placed to either group
1339 * without affecting overlap over selected axis ("common entry").
1340 */
1341typedef struct
1342{
1343 /* Index of entry in the initial array */
1345 /* Delta between penalties of entry insertion into different groups */
1346 float delta;
1347} CommonEntry;
1348
1349/*
1350 * Context for g_box_consider_split. Contains information about currently
1351 * selected split and some general information.
1352 */
1353typedef struct
1354{
1355 int entriesCount; /* total number of entries being split */
1356 BOX2DF boundingBox; /* minimum bounding box across all entries */
1357
1358 /* Information about currently selected split follows */
1359
1360 bool first; /* true if no split was selected yet */
1361
1362 float leftUpper; /* upper bound of left interval */
1363 float rightLower; /* lower bound of right interval */
1364
1365 float4 ratio;
1366 float4 overlap;
1367 int dim; /* axis of this split */
1368 float range; /* width of general MBR projection to the
1369 * selected axis */
1371
1372/*
1373 * Interval represents projection of box to axis.
1374 */
1375typedef struct
1376{
1377 float lower,
1380
1381/*
1382 * Interval comparison function by lower bound of the interval;
1383 */
1384static int
1385interval_cmp_lower(const void *i1, const void *i2)
1386{
1387 float lower1 = ((const SplitInterval *) i1)->lower,
1388 lower2 = ((const SplitInterval *) i2)->lower;
1389
1390 if (isnan(lower1))
1391 {
1392 if (isnan(lower2))
1393 return 0;
1394 else
1395 return 1;
1396 }
1397 else if (isnan(lower2))
1398 {
1399 return -1;
1400 }
1401 else
1402 {
1403 if (lower1 < lower2)
1404 return -1;
1405 else if (lower1 > lower2)
1406 return 1;
1407 else
1408 return 0;
1409 }
1410}
1411
1412
1413/*
1414 * Interval comparison function by upper bound of the interval;
1415 */
1416static int
1417interval_cmp_upper(const void *i1, const void *i2)
1418{
1419 float upper1 = ((const SplitInterval *) i1)->upper,
1420 upper2 = ((const SplitInterval *) i2)->upper;
1421
1422 if (isnan(upper1))
1423 {
1424 if (isnan(upper2))
1425 return 0;
1426 else
1427 return -1;
1428 }
1429 else if (isnan(upper2))
1430 {
1431 return 1;
1432 }
1433 else
1434 {
1435 if (upper1 < upper2)
1436 return -1;
1437 else if (upper1 > upper2)
1438 return 1;
1439 else
1440 return 0;
1441 }
1442}
1443
1444/*
1445 * Replace negative value with zero.
1446 */
1447static inline float
1449{
1450 if (val >= 0.0f)
1451 return val;
1452 else
1453 return 0.0f;
1454}
1455
1456/*
1457 * Consider replacement of currently selected split with the better one.
1458 */
1459static inline void
1461 float rightLower, int minLeftCount,
1462 float leftUpper, int maxLeftCount)
1463{
1464 int leftCount,
1465 rightCount;
1466 float4 ratio,
1467 overlap;
1468 float range;
1469
1470 POSTGIS_DEBUGF(5, "consider split: dimNum = %d, rightLower = %f, "
1471 "minLeftCount = %d, leftUpper = %f, maxLeftCount = %d ",
1472 dimNum, rightLower, minLeftCount, leftUpper, maxLeftCount);
1473
1474 /*
1475 * Calculate entries distribution ratio assuming most uniform distribution
1476 * of common entries.
1477 */
1478 if (minLeftCount >= (context->entriesCount + 1) / 2)
1479 {
1480 leftCount = minLeftCount;
1481 }
1482 else
1483 {
1484 if (maxLeftCount <= context->entriesCount / 2)
1485 leftCount = maxLeftCount;
1486 else
1487 leftCount = context->entriesCount / 2;
1488 }
1489 rightCount = context->entriesCount - leftCount;
1490
1491 /*
1492 * Ratio of split - quotient between size of lesser group and total
1493 * entries count.
1494 */
1495 ratio = ((float4) Min(leftCount, rightCount)) /
1496 ((float4) context->entriesCount);
1497
1498 if (ratio > LIMIT_RATIO)
1499 {
1500 bool selectthis = false;
1501
1502 /*
1503 * The ratio is acceptable, so compare current split with previously
1504 * selected one. Between splits of one dimension we search for minimal
1505 * overlap (allowing negative values) and minimal ration (between same
1506 * overlaps. We switch dimension if find less overlap (non-negative)
1507 * or less range with same overlap.
1508 */
1509 if (dimNum == 0)
1510 range = context->boundingBox.xmax - context->boundingBox.xmin;
1511 else
1512 range = context->boundingBox.ymax - context->boundingBox.ymin;
1513
1514 overlap = (leftUpper - rightLower) / range;
1515
1516 /* If there is no previous selection, select this */
1517 if (context->first)
1518 selectthis = true;
1519 else if (context->dim == dimNum)
1520 {
1521 /*
1522 * Within the same dimension, choose the new split if it has a
1523 * smaller overlap, or same overlap but better ratio.
1524 */
1525 if (overlap < context->overlap ||
1526 (overlap == context->overlap && ratio > context->ratio))
1527 selectthis = true;
1528 }
1529 else
1530 {
1531 /*
1532 * Across dimensions, choose the new split if it has a smaller
1533 * *non-negative* overlap, or same *non-negative* overlap but
1534 * bigger range. This condition differs from the one described in
1535 * the article. On the datasets where leaf MBRs don't overlap
1536 * themselves, non-overlapping splits (i.e. splits which have zero
1537 * *non-negative* overlap) are frequently possible. In this case
1538 * splits tends to be along one dimension, because most distant
1539 * non-overlapping splits (i.e. having lowest negative overlap)
1540 * appears to be in the same dimension as in the previous split.
1541 * Therefore MBRs appear to be very prolonged along another
1542 * dimension, which leads to bad search performance. Using range
1543 * as the second split criteria makes MBRs more quadratic. Using
1544 * *non-negative* overlap instead of overlap as the first split
1545 * criteria gives to range criteria a chance to matter, because
1546 * non-overlapping splits are equivalent in this criteria.
1547 */
1548 if (non_negative(overlap) < non_negative(context->overlap) ||
1549 (range > context->range &&
1550 non_negative(overlap) <= non_negative(context->overlap)))
1551 selectthis = true;
1552 }
1553
1554 if (selectthis)
1555 {
1556 /* save information about selected split */
1557 context->first = false;
1558 context->ratio = ratio;
1559 context->range = range;
1560 context->overlap = overlap;
1561 context->rightLower = rightLower;
1562 context->leftUpper = leftUpper;
1563 context->dim = dimNum;
1564 POSTGIS_DEBUG(5, "split selected");
1565 }
1566 }
1567}
1568
1569/*
1570 * Compare common entries by their deltas.
1571 */
1572static int
1573common_entry_cmp(const void *i1, const void *i2)
1574{
1575 float delta1 = ((const CommonEntry *) i1)->delta,
1576 delta2 = ((const CommonEntry *) i2)->delta;
1577
1578 if (delta1 < delta2)
1579 return -1;
1580 else if (delta1 > delta2)
1581 return 1;
1582 else
1583 return 0;
1584}
1585
1586/*
1587 * --------------------------------------------------------------------------
1588 * Double sorting split algorithm. This is used for both boxes and points.
1589 *
1590 * The algorithm finds split of boxes by considering splits along each axis.
1591 * Each entry is first projected as an interval on the X-axis, and different
1592 * ways to split the intervals into two groups are considered, trying to
1593 * minimize the overlap of the groups. Then the same is repeated for the
1594 * Y-axis, and the overall best split is chosen. The quality of a split is
1595 * determined by overlap along that axis and some other criteria (see
1596 * g_box_consider_split).
1597 *
1598 * After that, all the entries are divided into three groups:
1599 *
1600 * 1) Entries which should be placed to the left group
1601 * 2) Entries which should be placed to the right group
1602 * 3) "Common entries" which can be placed to any of groups without affecting
1603 * of overlap along selected axis.
1604 *
1605 * The common entries are distributed by minimizing penalty.
1606 *
1607 * For details see:
1608 * "A new double sorting-based node splitting algorithm for R-tree", A. Korotkov
1609 * http://syrcose.ispras.ru/2011/files/SYRCoSE2011_Proceedings.pdf#page=36
1610 * --------------------------------------------------------------------------
1611 */
1613Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS)
1614{
1615 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
1616 GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
1617 OffsetNumber i,
1618 maxoff;
1619 ConsiderSplitContext context;
1620 BOX2DF *box,
1621 *leftBox,
1622 *rightBox;
1623 int dim,
1624 commonEntriesCount;
1625 SplitInterval *intervalsLower,
1626 *intervalsUpper;
1627 CommonEntry *commonEntries;
1628 int nentries;
1629
1630 POSTGIS_DEBUG(3, "[GIST] 'picksplit' entered");
1631
1632 memset(&context, 0, sizeof(ConsiderSplitContext));
1633
1634 maxoff = entryvec->n - 1;
1635 nentries = context.entriesCount = maxoff - FirstOffsetNumber + 1;
1636
1637 /* Allocate arrays for intervals along axes */
1638 intervalsLower = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
1639 intervalsUpper = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
1640
1641 /*
1642 * Calculate the overall minimum bounding box over all the entries.
1643 */
1644 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1645 {
1646 box = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1647 if (i == FirstOffsetNumber)
1648 context.boundingBox = *box;
1649 else
1650 adjustBox(&context.boundingBox, box);
1651 }
1652
1653 POSTGIS_DEBUGF(4, "boundingBox is %s", box2df_to_string(
1654 &context.boundingBox));
1655
1656 /*
1657 * Iterate over axes for optimal split searching.
1658 */
1659 context.first = true; /* nothing selected yet */
1660 for (dim = 0; dim < 2; dim++)
1661 {
1662 float leftUpper,
1663 rightLower;
1664 int i1,
1665 i2;
1666
1667 /* Project each entry as an interval on the selected axis. */
1668 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1669 {
1670 box = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1671 if (dim == 0)
1672 {
1673 intervalsLower[i - FirstOffsetNumber].lower = box->xmin;
1674 intervalsLower[i - FirstOffsetNumber].upper = box->xmax;
1675 }
1676 else
1677 {
1678 intervalsLower[i - FirstOffsetNumber].lower = box->ymin;
1679 intervalsLower[i - FirstOffsetNumber].upper = box->ymax;
1680 }
1681 }
1682
1683 /*
1684 * Make two arrays of intervals: one sorted by lower bound and another
1685 * sorted by upper bound.
1686 */
1687 memcpy(intervalsUpper, intervalsLower,
1688 sizeof(SplitInterval) * nentries);
1689 qsort(intervalsLower, nentries, sizeof(SplitInterval),
1691 qsort(intervalsUpper, nentries, sizeof(SplitInterval),
1693
1694 /*----
1695 * The goal is to form a left and right interval, so that every entry
1696 * interval is contained by either left or right interval (or both).
1697 *
1698 * For example, with the intervals (0,1), (1,3), (2,3), (2,4):
1699 *
1700 * 0 1 2 3 4
1701 * +-+
1702 * +---+
1703 * +-+
1704 * +---+
1705 *
1706 * The left and right intervals are of the form (0,a) and (b,4).
1707 * We first consider splits where b is the lower bound of an entry.
1708 * We iterate through all entries, and for each b, calculate the
1709 * smallest possible a. Then we consider splits where a is the
1710 * upper bound of an entry, and for each a, calculate the greatest
1711 * possible b.
1712 *
1713 * In the above example, the first loop would consider splits:
1714 * b=0: (0,1)-(0,4)
1715 * b=1: (0,1)-(1,4)
1716 * b=2: (0,3)-(2,4)
1717 *
1718 * And the second loop:
1719 * a=1: (0,1)-(1,4)
1720 * a=3: (0,3)-(2,4)
1721 * a=4: (0,4)-(2,4)
1722 */
1723
1724 /*
1725 * Iterate over lower bound of right group, finding smallest possible
1726 * upper bound of left group.
1727 */
1728 i1 = 0;
1729 i2 = 0;
1730 rightLower = intervalsLower[i1].lower;
1731 leftUpper = intervalsUpper[i2].lower;
1732 while (true)
1733 {
1734 /*
1735 * Find next lower bound of right group.
1736 */
1737 while (i1 < nentries && (rightLower == intervalsLower[i1].lower ||
1738 isnan(intervalsLower[i1].lower)))
1739 {
1740 leftUpper = Max(leftUpper, intervalsLower[i1].upper);
1741 i1++;
1742 }
1743 if (i1 >= nentries)
1744 break;
1745 rightLower = intervalsLower[i1].lower;
1746
1747 /*
1748 * Find count of intervals which anyway should be placed to the
1749 * left group.
1750 */
1751 while (i2 < nentries && intervalsUpper[i2].upper <= leftUpper)
1752 i2++;
1753
1754 /*
1755 * Consider found split.
1756 */
1757 g_box_consider_split(&context, dim, rightLower, i1, leftUpper, i2);
1758 }
1759
1760 /*
1761 * Iterate over upper bound of left group finding greatest possible
1762 * lower bound of right group.
1763 */
1764 i1 = nentries - 1;
1765 i2 = nentries - 1;
1766 rightLower = intervalsLower[i1].upper;
1767 leftUpper = intervalsUpper[i2].upper;
1768 while (true)
1769 {
1770 /*
1771 * Find next upper bound of left group.
1772 */
1773 while (i2 >= 0 && (leftUpper == intervalsUpper[i2].upper ||
1774 isnan(intervalsUpper[i2].upper)))
1775 {
1776 rightLower = Min(rightLower, intervalsUpper[i2].lower);
1777 i2--;
1778 }
1779 if (i2 < 0)
1780 break;
1781 leftUpper = intervalsUpper[i2].upper;
1782
1783 /*
1784 * Find count of intervals which anyway should be placed to the
1785 * right group.
1786 */
1787 while (i1 >= 0 && intervalsLower[i1].lower >= rightLower)
1788 i1--;
1789
1790 /*
1791 * Consider found split.
1792 */
1793 g_box_consider_split(&context, dim,
1794 rightLower, i1 + 1, leftUpper, i2 + 1);
1795 }
1796 }
1797
1798 /*
1799 * If we failed to find any acceptable splits, use trivial split.
1800 */
1801 if (context.first)
1802 {
1803 POSTGIS_DEBUG(4, "no acceptable splits, trivial split");
1804 fallbackSplit(entryvec, v);
1805 PG_RETURN_POINTER(v);
1806 }
1807
1808 /*
1809 * Ok, we have now selected the split across one axis.
1810 *
1811 * While considering the splits, we already determined that there will be
1812 * enough entries in both groups to reach the desired ratio, but we did
1813 * not memorize which entries go to which group. So determine that now.
1814 */
1815
1816 POSTGIS_DEBUGF(4, "split direction: %d", context.dim);
1817
1818 /* Allocate vectors for results */
1819 v->spl_left = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
1820 v->spl_right = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
1821 v->spl_nleft = 0;
1822 v->spl_nright = 0;
1823
1824 /* Allocate bounding boxes of left and right groups */
1825 leftBox = palloc0(sizeof(BOX2DF));
1826 rightBox = palloc0(sizeof(BOX2DF));
1827
1828 /*
1829 * Allocate an array for "common entries" - entries which can be placed to
1830 * either group without affecting overlap along selected axis.
1831 */
1832 commonEntriesCount = 0;
1833 commonEntries = (CommonEntry *) palloc(nentries * sizeof(CommonEntry));
1834
1835 /* Helper macros to place an entry in the left or right group */
1836#define PLACE_LEFT(box, off) \
1837 do { \
1838 if (v->spl_nleft > 0) \
1839 adjustBox(leftBox, box); \
1840 else \
1841 *leftBox = *(box); \
1842 v->spl_left[v->spl_nleft++] = off; \
1843 } while(0)
1844
1845#define PLACE_RIGHT(box, off) \
1846 do { \
1847 if (v->spl_nright > 0) \
1848 adjustBox(rightBox, box); \
1849 else \
1850 *rightBox = *(box); \
1851 v->spl_right[v->spl_nright++] = off; \
1852 } while(0)
1853
1854 /*
1855 * Distribute entries which can be distributed unambiguously, and collect
1856 * common entries.
1857 */
1858 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1859 {
1860 float lower,
1861 upper;
1862
1863 /*
1864 * Get upper and lower bounds along selected axis.
1865 */
1866 box = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1867 if (context.dim == 0)
1868 {
1869 lower = box->xmin;
1870 upper = box->xmax;
1871 }
1872 else
1873 {
1874 lower = box->ymin;
1875 upper = box->ymax;
1876 }
1877
1878 if (upper <= context.leftUpper || isnan(upper))
1879 {
1880 /* Fits to the left group */
1881 if (lower >= context.rightLower || isnan(lower))
1882 {
1883 /* Fits also to the right group, so "common entry" */
1884 commonEntries[commonEntriesCount++].index = i;
1885 }
1886 else
1887 {
1888 /* Doesn't fit to the right group, so join to the left group */
1889 PLACE_LEFT(box, i);
1890 }
1891 }
1892 else
1893 {
1894 /*
1895 * Each entry should fit on either left or right group. Since this
1896 * entry didn't fit on the left group, it better fit in the right
1897 * group.
1898 */
1899 Assert(lower >= context.rightLower);
1900
1901 /* Doesn't fit to the left group, so join to the right group */
1902 PLACE_RIGHT(box, i);
1903 }
1904 }
1905
1906 POSTGIS_DEBUGF(4, "leftBox is %s", box2df_to_string(leftBox));
1907 POSTGIS_DEBUGF(4, "rightBox is %s", box2df_to_string(rightBox));
1908
1909 /*
1910 * Distribute "common entries", if any.
1911 */
1912 if (commonEntriesCount > 0)
1913 {
1914 /*
1915 * Calculate minimum number of entries that must be placed in both
1916 * groups, to reach LIMIT_RATIO.
1917 */
1918 int m = ceil(LIMIT_RATIO * (double) nentries);
1919
1920 /*
1921 * Calculate delta between penalties of join "common entries" to
1922 * different groups.
1923 */
1924 for (i = 0; i < commonEntriesCount; i++)
1925 {
1926 box = (BOX2DF *) DatumGetPointer(entryvec->vector[
1927 commonEntries[i].index].key);
1928 commonEntries[i].delta = Abs(box2df_penalty(leftBox, box) - box2df_penalty(rightBox, box));
1929 }
1930
1931 /*
1932 * Sort "common entries" by calculated deltas in order to distribute
1933 * the most ambiguous entries first.
1934 */
1935 qsort(commonEntries, commonEntriesCount, sizeof(CommonEntry), common_entry_cmp);
1936
1937 /*
1938 * Distribute "common entries" between groups.
1939 */
1940 for (i = 0; i < commonEntriesCount; i++)
1941 {
1942 box = (BOX2DF *) DatumGetPointer(entryvec->vector[
1943 commonEntries[i].index].key);
1944
1945 /*
1946 * Check if we have to place this entry in either group to achieve
1947 * LIMIT_RATIO.
1948 */
1949 if (v->spl_nleft + (commonEntriesCount - i) <= m)
1950 PLACE_LEFT(box, commonEntries[i].index);
1951 else if (v->spl_nright + (commonEntriesCount - i) <= m)
1952 PLACE_RIGHT(box, commonEntries[i].index);
1953 else
1954 {
1955 /* Otherwise select the group by minimal penalty */
1956 if (box2df_penalty(leftBox, box) < box2df_penalty(rightBox, box))
1957 PLACE_LEFT(box, commonEntries[i].index);
1958 else
1959 PLACE_RIGHT(box, commonEntries[i].index);
1960 }
1961 }
1962 }
1963 v->spl_ldatum = PointerGetDatum(leftBox);
1964 v->spl_rdatum = PointerGetDatum(rightBox);
1965
1966 POSTGIS_DEBUG(4, "[GIST] 'picksplit' completed");
1967
1968 PG_RETURN_POINTER(v);
1969}
1970
1971/*
1972** The BOX2DF key must be defined as a PostgreSQL type, even though it is only
1973** ever used internally. These no-op stubs are used to bind the type.
1974*/
1976Datum box2df_in(PG_FUNCTION_ARGS)
1977{
1978 ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1979 errmsg("function box2df_in not implemented")));
1980 PG_RETURN_POINTER(NULL);
1981}
1982
1984Datum box2df_out(PG_FUNCTION_ARGS)
1985{
1986 BOX2DF *box = (BOX2DF *) PG_GETARG_POINTER(0);
1987 char *result = box2df_to_string(box);
1988 PG_RETURN_CSTRING(result);
1989}
void gbox_init(GBOX *gbox)
Zero out all the entries in the GBOX.
Definition gbox.c:40
const float * gserialized_get_float_box_p(const GSERIALIZED *g, size_t *ndims)
Access to the float bounding box, if there is one.
int gserialized_has_bbox(const GSERIALIZED *g)
Check if a GSERIALIZED has a bounding box without deserializing first.
int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *gbox)
Read the box from the GSERIALIZED or calculate it if necessary.
Definition gserialized.c:65
bool box2df_left(const BOX2DF *a, const BOX2DF *b)
void box2df_merge(BOX2DF *b_union, BOX2DF *b_new)
static void fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS)
void box2df_set_empty(BOX2DF *a)
Datum gserialized_gist_same_2d(PG_FUNCTION_ARGS)
Datum gserialized_contains_box2df_geom_2d(PG_FUNCTION_ARGS)
static char * box2df_to_string(const BOX2DF *a)
bool box2df_is_empty(const BOX2DF *a)
static int gserialized_datum_predicate_2d(Datum gs1, Datum gs2, box2df_predicate predicate)
Support function.
Datum gserialized_within_2d(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(gserialized_contains_box2df_geom_2d)
Datum box2df_out(PG_FUNCTION_ARGS)
Datum gserialized_within_box2df_geom_2d(PG_FUNCTION_ARGS)
Datum gserialized_gist_penalty_2d(PG_FUNCTION_ARGS)
Datum gserialized_left_2d(PG_FUNCTION_ARGS)
Datum gserialized_contains_2d(PG_FUNCTION_ARGS)
Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS)
static int interval_cmp_upper(const void *i1, const void *i2)
Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS)
bool box2df_equals(const BOX2DF *a, const BOX2DF *b)
#define LIMIT_RATIO
bool box2df_contains(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_gist_union_2d(PG_FUNCTION_ARGS)
#define PLACE_RIGHT(box, off)
Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS)
Datum gserialized_distance_centroid_2d(PG_FUNCTION_ARGS)
Datum gserialized_contains_box2df_box2df_2d(PG_FUNCTION_ARGS)
Datum gserialized_overlaps_2d(PG_FUNCTION_ARGS)
Datum gserialized_overlaps_box2df_geom_2d(PG_FUNCTION_ARGS)
Datum gserialized_within_box2df_box2df_2d(PG_FUNCTION_ARGS)
bool box2df_right(const BOX2DF *a, const BOX2DF *b)
bool box2df_overlaps(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_overleft_2d(PG_FUNCTION_ARGS)
void box2df_set_finite(BOX2DF *a)
static bool gserialized_gist_consistent_internal_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
Datum gserialized_overlaps_box2df_box2df_2d(PG_FUNCTION_ARGS)
Datum gserialized_gist_distance_2d(PG_FUNCTION_ARGS)
Datum gserialized_overright_2d(PG_FUNCTION_ARGS)
static bool box2df_within(const BOX2DF *a, const BOX2DF *b)
bool box2df_above(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_right_2d(PG_FUNCTION_ARGS)
bool box2df_overbelow(const BOX2DF *a, const BOX2DF *b)
bool(* box2df_predicate)(const BOX2DF *a, const BOX2DF *b)
static double box2df_distance(const BOX2DF *a, const BOX2DF *b)
Calculate the box->box distance.
static double box2df_distance_leaf_centroid(const BOX2DF *a, const BOX2DF *b)
Calculate the centroid->centroid distance between the boxes.
Datum gserialized_below_2d(PG_FUNCTION_ARGS)
Datum gserialized_overabove_2d(PG_FUNCTION_ARGS)
static void g_box_consider_split(ConsiderSplitContext *context, int dimNum, float rightLower, int minLeftCount, float leftUpper, int maxLeftCount)
int box2df_to_gbox_p(BOX2DF *a, GBOX *box)
static int interval_cmp_lower(const void *i1, const void *i2)
bool box2df_overright(const BOX2DF *a, const BOX2DF *b)
static double pt_distance(double ax, double ay, double bx, double by)
static float pack_float(const float value, const uint8_t realm)
static int common_entry_cmp(const void *i1, const void *i2)
Datum gserialized_gist_decompress_2d(PG_FUNCTION_ARGS)
int gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df)
Peak into a GSERIALIZED datum to find the bounding box.
static int box2df_from_gbox_p(GBOX *box, BOX2DF *a)
bool box2df_overabove(const BOX2DF *a, const BOX2DF *b)
BOX2DF * box2df_copy(BOX2DF *b)
Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS)
#define PLACE_LEFT(box, off)
void box2df_validate(BOX2DF *b)
static float box2df_penalty(const BOX2DF *b1, const BOX2DF *b2)
bool box2df_below(const BOX2DF *a, const BOX2DF *b)
static bool gserialized_gist_consistent_leaf_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
bool box2df_overleft(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_above_2d(PG_FUNCTION_ARGS)
static int gserialized_datum_predicate_box2df_geom_2d(const BOX2DF *br1, Datum gs2, box2df_predicate predicate)
Datum gserialized_same_2d(PG_FUNCTION_ARGS)
Datum box2df_in(PG_FUNCTION_ARGS)
static void adjustBox(BOX2DF *b, BOX2DF *addon)
static float non_negative(float val)
#define LW_FALSE
Definition liblwgeom.h:108
#define LW_FAILURE
Definition liblwgeom.h:110
#define LW_SUCCESS
Definition liblwgeom.h:111
float next_float_up(double d)
Definition lwgeom_api.c:75
#define LW_TRUE
Return types for functions with status returns.
Definition liblwgeom.h:107
float next_float_down(double d)
Definition lwgeom_api.c:54
This library is the generic geometry handling section of PostGIS.
#define NAN
Definition lwgeodetic.h:37
#define POSTGIS_FREE_IF_COPY_P(ptrsrc, ptrori)
Definition lwinline.h:235
static double distance(double x1, double y1, double x2, double y2)
Definition lwtree.c:1032
double ymax
Definition liblwgeom.h:343
double xmax
Definition liblwgeom.h:341
double ymin
Definition liblwgeom.h:342
double xmin
Definition liblwgeom.h:340
uint8_t gflags
Definition liblwgeom.h:432