PostGIS  3.0.6dev-r@@SVN_REVISION@@
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
65 static int g2d_counter_leaf = 0;
66 static int g2d_counter_internal = 0;
67 #endif
68 
69 /*
70 ** GiST 2D key stubs
71 */
72 Datum box2df_out(PG_FUNCTION_ARGS);
73 Datum box2df_in(PG_FUNCTION_ARGS);
74 
75 /*
76 ** GiST 2D index function prototypes
77 */
78 Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS);
79 Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS);
80 Datum gserialized_gist_decompress_2d(PG_FUNCTION_ARGS);
81 Datum gserialized_gist_penalty_2d(PG_FUNCTION_ARGS);
82 Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS);
83 Datum gserialized_gist_union_2d(PG_FUNCTION_ARGS);
84 Datum gserialized_gist_same_2d(PG_FUNCTION_ARGS);
85 Datum gserialized_gist_distance_2d(PG_FUNCTION_ARGS);
86 
87 /*
88 ** GiST 2D operator prototypes
89 */
90 Datum gserialized_same_2d(PG_FUNCTION_ARGS);
91 Datum gserialized_within_2d(PG_FUNCTION_ARGS);
92 Datum gserialized_contains_2d(PG_FUNCTION_ARGS);
93 Datum gserialized_overlaps_2d(PG_FUNCTION_ARGS);
94 Datum gserialized_left_2d(PG_FUNCTION_ARGS);
95 Datum gserialized_right_2d(PG_FUNCTION_ARGS);
96 Datum gserialized_above_2d(PG_FUNCTION_ARGS);
97 Datum gserialized_below_2d(PG_FUNCTION_ARGS);
98 Datum gserialized_overleft_2d(PG_FUNCTION_ARGS);
99 Datum gserialized_overright_2d(PG_FUNCTION_ARGS);
100 Datum gserialized_overabove_2d(PG_FUNCTION_ARGS);
101 Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS);
102 Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS);
103 Datum gserialized_distance_centroid_2d(PG_FUNCTION_ARGS);
104 Datum gserialized_contains_box2df_geom_2d(PG_FUNCTION_ARGS);
105 Datum gserialized_contains_box2df_box2df_2d(PG_FUNCTION_ARGS);
106 Datum gserialized_within_box2df_geom_2d(PG_FUNCTION_ARGS);
107 Datum gserialized_within_box2df_box2df_2d(PG_FUNCTION_ARGS);
108 Datum gserialized_overlaps_box2df_geom_2d(PG_FUNCTION_ARGS);
109 Datum gserialized_overlaps_box2df_box2df_2d(PG_FUNCTION_ARGS);
110 
111 /*
112 ** true/false test function type
113 */
114 typedef bool (*box2df_predicate)(const BOX2DF *a, const BOX2DF *b);
115 
116 
117 static 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 */
130 BOX2DF* 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 
138 inline bool box2df_is_empty(const BOX2DF *a)
139 {
140  if (isnan(a->xmin))
141  return true;
142  else
143  return false;
144 }
145 
146 inline void box2df_set_empty(BOX2DF *a)
147 {
148  a->xmin = a->xmax = a->ymin = a->ymax = NAN;
149  return;
150 }
151 
152 inline 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. */
166 void 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 */
188 static 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 
197 int 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. */
212 inline 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 
234 bool 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 
248 bool 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 
266 static 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 
284 bool 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 
300 bool 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 
309 bool 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 
318 bool 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 
327 bool 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 
336 bool 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 
345 bool 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 
354 bool 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 
363 bool 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 
375 static 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 */
388 static 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 
396 static 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 
449 int
450 gserialized_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 
510 static int
511 gserialized_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 
527 static int
528 gserialized_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 
548 Datum gserialized_contains_box2df_geom_2d(PG_FUNCTION_ARGS)
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 
567 Datum gserialized_within_box2df_geom_2d(PG_FUNCTION_ARGS)
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 
577 Datum gserialized_within_box2df_box2df_2d(PG_FUNCTION_ARGS)
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 
586 Datum gserialized_overlaps_box2df_geom_2d(PG_FUNCTION_ARGS)
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 
609 Datum 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 
629 Datum 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 
649 Datum 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 
658 Datum 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 
668 Datum 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 
678 Datum 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 
687 Datum 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 
696 Datum 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 
705 Datum 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 
714 Datum 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 
723 Datum 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 
732 Datum 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 
741 Datum 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 
750 Datum 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 */
770 Datum 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 */
851 Datum 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 */
863 static 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 */
927 static 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 */
991 Datum 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 */
1065 Datum 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 */
1131 static inline float
1132 pack_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 
1151 static inline float
1152 box2df_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 */
1186 Datum 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 */
1212 Datum 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 */
1245 Datum 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  */
1261 static void
1262 adjustBox(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  */
1278 static void
1279 fallbackSplit(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  */
1341 typedef struct
1342 {
1343  /* Index of entry in the initial array */
1344  int index;
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  */
1353 typedef 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  */
1375 typedef struct
1376 {
1377  float lower,
1379 } SplitInterval;
1380 
1381 /*
1382  * Interval comparison function by lower bound of the interval;
1383  */
1384 static int
1385 interval_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  */
1416 static int
1417 interval_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  */
1447 static inline float
1448 non_negative(float val)
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  */
1459 static 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  */
1572 static int
1573 common_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  */
1613 Datum 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 */
1976 Datum 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 
1984 Datum 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
int gserialized_has_bbox(const GSERIALIZED *g)
Check if a GSERIALIZED has a bounding box without deserializing first.
Definition: gserialized.c:163
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
const float * gserialized_get_float_box_p(const GSERIALIZED *g, size_t *ndims)
Access to the float bounding box, if there is one.
Definition: gserialized.c:248
bool box2df_left(const BOX2DF *a, const BOX2DF *b)
static char * box2df_to_string(const BOX2DF *a)
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)
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)
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)
BOX2DF * box2df_copy(BOX2DF *b)
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
int value
Definition: genraster.py:62
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