PostGIS  2.4.9dev-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 Darafei Praliaskouski <me@komzpa.net>
23  *
24  **********************************************************************/
25 
26 
27 /*
28 ** R-Tree Bibliography
29 **
30 ** [1] A. Guttman. R-tree: a dynamic index structure for spatial searching.
31 ** Proceedings of the ACM SIGMOD Conference, pp 47-57, June 1984.
32 ** [2] C.-H. Ang and T. C. Tan. New linear node splitting algorithm for
33 ** R-Trees. Advances in Spatial Databases - 5th International Symposium,
34 ** 1997
35 ** [3] N. Beckmann, H.-P. Kriegel, R. Schneider, B. Seeger. The R*tree: an
36 ** efficient and robust access method for points and rectangles.
37 ** Proceedings of the ACM SIGMOD Conference. June 1990.
38 ** [4] A. Korotkov, "A new double sorting-based node splitting algorithm for R-tree",
39 ** http://syrcose.ispras.ru/2011/files/SYRCoSE2011_Proceedings.pdf#page=36
40 */
41 
42 #include "postgres.h"
43 #include "access/gist.h" /* For GiST */
44 #include "access/itup.h"
45 #include "access/skey.h"
46 
47 #include "../postgis_config.h"
48 
49 #include "liblwgeom.h" /* For standard geometry types. */
50 #include "lwgeom_pg.h" /* For debugging macros. */
51 #include "gserialized_gist.h" /* For utility functions. */
52 
53 /* Fall back to older finite() if necessary */
54 #ifndef HAVE_ISFINITE
55 # ifdef HAVE_GNU_ISFINITE
56 # define _GNU_SOURCE
57 # else
58 # define isfinite finite
59 # endif
60 #endif
61 
62 #include <float.h> /* For FLT_MAX */
63 
64 /*
65 ** When is a node split not so good? If more than 90% of the entries
66 ** end up in one of the children.
67 */
68 #define LIMIT_RATIO 0.1
69 
70 /*
71 ** 0 == don't use it
72 ** 1 == use it
73 */
74 #define KOROTKOV_SPLIT 1
75 
76 /*
77 ** For debugging
78 */
79 #if POSTGIS_DEBUG_LEVEL > 0
80 static int g2d_counter_leaf = 0;
81 static int g2d_counter_internal = 0;
82 #endif
83 
84 /*
85 ** GiST 2D key stubs
86 */
87 Datum box2df_out(PG_FUNCTION_ARGS);
88 Datum box2df_in(PG_FUNCTION_ARGS);
89 
90 /*
91 ** GiST 2D index function prototypes
92 */
93 Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS);
94 Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS);
95 Datum gserialized_gist_decompress_2d(PG_FUNCTION_ARGS);
96 Datum gserialized_gist_penalty_2d(PG_FUNCTION_ARGS);
97 Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS);
98 Datum gserialized_gist_union_2d(PG_FUNCTION_ARGS);
99 Datum gserialized_gist_same_2d(PG_FUNCTION_ARGS);
100 Datum gserialized_gist_distance_2d(PG_FUNCTION_ARGS);
101 
102 /*
103 ** GiST 2D operator prototypes
104 */
105 Datum gserialized_same_2d(PG_FUNCTION_ARGS);
106 Datum gserialized_within_2d(PG_FUNCTION_ARGS);
107 Datum gserialized_contains_2d(PG_FUNCTION_ARGS);
108 Datum gserialized_overlaps_2d(PG_FUNCTION_ARGS);
109 Datum gserialized_left_2d(PG_FUNCTION_ARGS);
110 Datum gserialized_right_2d(PG_FUNCTION_ARGS);
111 Datum gserialized_above_2d(PG_FUNCTION_ARGS);
112 Datum gserialized_below_2d(PG_FUNCTION_ARGS);
113 Datum gserialized_overleft_2d(PG_FUNCTION_ARGS);
114 Datum gserialized_overright_2d(PG_FUNCTION_ARGS);
115 Datum gserialized_overabove_2d(PG_FUNCTION_ARGS);
116 Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS);
117 Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS);
118 Datum gserialized_distance_centroid_2d(PG_FUNCTION_ARGS);
119 
120 #if POSTGIS_PGSQL_VERSION > 94
121 Datum gserialized_contains_box2df_geom_2d(PG_FUNCTION_ARGS);
122 Datum gserialized_contains_box2df_box2df_2d(PG_FUNCTION_ARGS);
123 Datum gserialized_within_box2df_geom_2d(PG_FUNCTION_ARGS);
124 Datum gserialized_within_box2df_box2df_2d(PG_FUNCTION_ARGS);
125 Datum gserialized_overlaps_box2df_geom_2d(PG_FUNCTION_ARGS);
126 Datum gserialized_overlaps_box2df_box2df_2d(PG_FUNCTION_ARGS);
127 #endif
128 
129 /*
130 ** true/false test function type
131 */
132 typedef bool (*box2df_predicate)(const BOX2DF *a, const BOX2DF *b);
133 
134 
135 static char* box2df_to_string(const BOX2DF *a)
136 {
137  char *rv = NULL;
138 
139  if ( a == NULL )
140  return pstrdup("<NULLPTR>");
141 
142  rv = palloc(128);
143  sprintf(rv, "BOX2DF(%.12g %.12g, %.12g %.12g)", a->xmin, a->ymin, a->xmax, a->ymax);
144  return rv;
145 }
146 
147 /* Allocate a new copy of BOX2DF */
148 static BOX2DF* box2df_copy(BOX2DF *b)
149 {
150  BOX2DF *c = (BOX2DF*)palloc(sizeof(BOX2DF));
151  memcpy((void*)c, (void*)b, sizeof(BOX2DF));
152  POSTGIS_DEBUGF(5, "copied box2df (%p) to box2df (%p)", b, c);
153  return c;
154 }
155 
156 
157 
158 /* Enlarge b_union to contain b_new. If b_new contains more
159  dimensions than b_union, expand b_union to contain those dimensions. */
160 static void box2df_merge(BOX2DF *b_union, BOX2DF *b_new)
161 {
162 
163  POSTGIS_DEBUGF(5, "merging %s with %s", box2df_to_string(b_union), box2df_to_string(b_new));
164  /* Adjust minimums */
165  if (b_union->xmin > b_new->xmin || isnan(b_union->xmin))
166  b_union->xmin = b_new->xmin;
167  if (b_union->ymin > b_new->ymin || isnan(b_union->ymin))
168  b_union->ymin = b_new->ymin;
169  /* Adjust maximums */
170  if (b_union->xmax < b_new->xmax || isnan(b_union->xmax))
171  b_union->xmax = b_new->xmax;
172  if (b_union->ymax < b_new->ymax || isnan(b_union->ymax))
173  b_union->ymax = b_new->ymax;
174 
175  POSTGIS_DEBUGF(5, "merge complete %s", box2df_to_string(b_union));
176  return;
177 }
178 
179 #if KOROTKOV_SPLIT < 1
180 static bool box2df_intersection(const BOX2DF *a, const BOX2DF *b, BOX2DF *n)
181 {
182  POSTGIS_DEBUGF(5, "calculating intersection of %s with %s", box2df_to_string(a), box2df_to_string(b));
183 
184  if( a == NULL || b == NULL || n == NULL )
185  return FALSE;
186 
187  n->xmax = Min(a->xmax, b->xmax);
188  n->ymax = Min(a->ymax, b->ymax);
189  n->xmin = Max(a->xmin, b->xmin);
190  n->ymin = Max(a->ymin, b->ymin);
191 
192  POSTGIS_DEBUGF(5, "intersection is %s", box2df_to_string(n));
193 
194  if ( (n->xmax < n->xmin) || (n->ymax < n->ymin) )
195  return FALSE;
196 
197  return TRUE;
198 }
199 #endif
200 
201 static float box2df_size(const BOX2DF *a)
202 {
203  float result;
204 
205  if ( a == NULL )
206  return (float)0.0;
207 
208  if ( (a->xmax <= a->xmin) || (a->ymax <= a->ymin) )
209  {
210  result = (float) 0.0;
211  }
212  else
213  {
214  result = (((double) a->xmax)-((double) a->xmin)) * (((double) a->ymax)-((double) a->ymin));
215  }
216 
217  return result;
218 }
219 
220 static float box2df_edge(const BOX2DF *a)
221 {
222  if ( a == NULL )
223  return (float)0.0;
224 
225  return ((a->xmax) - (a->xmin)) + ((a->ymax) - (a->ymin));
226 }
227 
228 static float box2df_union_size(const BOX2DF *a, const BOX2DF *b)
229 {
230  float result;
231 
232  POSTGIS_DEBUG(5,"entered function");
233 
234  if ( a == NULL && b == NULL )
235  {
236  elog(ERROR, "box2df_union_size received two null arguments");
237  return 0.0;
238  }
239 
240  if ( a == NULL )
241  return box2df_size(b);
242 
243  if ( b == NULL )
244  return box2df_size(a);
245 
246  result = ((double)Max(a->xmax,b->xmax) - (double)Min(a->xmin,b->xmin)) *
247  ((double)Max(a->ymax,b->ymax) - (double)Min(a->ymin,b->ymin));
248 
249  POSTGIS_DEBUGF(5, "union size of %s and %s is %.8g", box2df_to_string(a), box2df_to_string(b), result);
250 
251  return result;
252 }
253 
254 
255 static float box2df_union_edge(const BOX2DF *a, const BOX2DF *b)
256 {
257  float result;
258 
259  POSTGIS_DEBUG(5,"entered function");
260 
261  if ( a == NULL && b == NULL )
262  {
263  elog(ERROR, "box2df_union_edge received two null arguments");
264  return 0.0;
265  }
266 
267  if ( a == NULL )
268  return box2df_edge(b);
269 
270  if ( b == NULL )
271  return box2df_edge(a);
272 
273  result = (Max(a->xmax,b->xmax) - Min(a->xmin,b->xmin)) +
274  (Max(a->ymax,b->ymax) - Min(a->ymin,b->ymin));
275 
276  POSTGIS_DEBUGF(5, "union edge of %s and %s is %.8g", box2df_to_string(a), box2df_to_string(b), result);
277 
278  return result;
279 }
280 
281 /* Convert a double-based GBOX into a float-based BOX2DF,
282  ensuring the float box is larger than the double box */
283 static inline int box2df_from_gbox_p(GBOX *box, BOX2DF *a)
284 {
285  a->xmin = next_float_down(box->xmin);
286  a->xmax = next_float_up(box->xmax);
287  a->ymin = next_float_down(box->ymin);
288  a->ymax = next_float_up(box->ymax);
289  return LW_SUCCESS;
290 }
291 
292 /***********************************************************************
293 ** BOX3DF tests for 2D index operators.
294 */
295 
296 /* Ensure all minimums are below maximums. */
297 static inline void box2df_validate(BOX2DF *b)
298 {
299  float tmp;
300  POSTGIS_DEBUGF(5,"validating box2df (%s)", box2df_to_string(b));
301  if ( b->xmax < b->xmin )
302  {
303  tmp = b->xmin;
304  b->xmin = b->xmax;
305  b->xmax = tmp;
306  }
307  if ( b->ymax < b->ymin )
308  {
309  tmp = b->ymin;
310  b->ymin = b->ymax;
311  b->ymax = tmp;
312  }
313  return;
314 }
315 
316 static bool box2df_overlaps(const BOX2DF *a, const BOX2DF *b)
317 {
318  if ( ! a || ! b ) return FALSE; /* TODO: might be smarter for EMPTY */
319 
320  if ( (a->xmin > b->xmax) || (b->xmin > a->xmax) ||
321  (a->ymin > b->ymax) || (b->ymin > a->ymax) )
322  {
323  return FALSE;
324  }
325 
326  return TRUE;
327 }
328 
329 bool box2df_contains(const BOX2DF *a, const BOX2DF *b)
330 {
331  if ( ! a || ! b ) return FALSE; /* TODO: might be smarter for EMPTY */
332 
333  if ( (a->xmin > b->xmin) || (a->xmax < b->xmax) ||
334  (a->ymin > b->ymin) || (a->ymax < b->ymax) )
335  {
336  return FALSE;
337  }
338 
339  return TRUE;
340 }
341 
342 static bool box2df_within(const BOX2DF *a, const BOX2DF *b)
343 {
344  if ( ! a || ! b ) return FALSE; /* TODO: might be smarter for EMPTY */
345 
346  POSTGIS_DEBUG(5, "entered function");
347  return box2df_contains(b,a);
348 }
349 
350 static bool box2df_equals(const BOX2DF *a, const BOX2DF *b)
351 {
352  if ( a && b ) {
353  if ( (a->xmin != b->xmin) || (a->xmax != b->xmax) ||
354  (a->ymin != b->ymin) || (a->ymax != b->ymax) )
355  {
356  return FALSE;
357  }
358  return TRUE;
359  } else if ( a || b ) {
360  /* one empty, one not */
361  return FALSE;
362  } else {
363  /* both empty */
364  return TRUE;
365  }
366 }
367 
368 static bool box2df_overleft(const BOX2DF *a, const BOX2DF *b)
369 {
370  if ( ! a || ! b ) return FALSE; /* TODO: might be smarter for EMPTY */
371 
372  /* a.xmax <= b.xmax */
373  return a->xmax <= b->xmax;
374 }
375 
376 static bool box2df_left(const BOX2DF *a, const BOX2DF *b)
377 {
378  if ( ! a || ! b ) return FALSE; /* TODO: might be smarter for EMPTY */
379 
380  /* a.xmax < b.xmin */
381  return a->xmax < b->xmin;
382 }
383 
384 static bool box2df_right(const BOX2DF *a, const BOX2DF *b)
385 {
386  if ( ! a || ! b ) return FALSE; /* TODO: might be smarter for EMPTY */
387 
388  /* a.xmin > b.xmax */
389  return a->xmin > b->xmax;
390 }
391 
392 static bool box2df_overright(const BOX2DF *a, const BOX2DF *b)
393 {
394  if ( ! a || ! b ) return FALSE; /* TODO: might be smarter for EMPTY */
395 
396  /* a.xmin >= b.xmin */
397  return a->xmin >= b->xmin;
398 }
399 
400 static bool box2df_overbelow(const BOX2DF *a, const BOX2DF *b)
401 {
402  if ( ! a || ! b ) return FALSE; /* TODO: might be smarter for EMPTY */
403 
404  /* a.ymax <= b.ymax */
405  return a->ymax <= b->ymax;
406 }
407 
408 static bool box2df_below(const BOX2DF *a, const BOX2DF *b)
409 {
410  if ( ! a || ! b ) return FALSE; /* TODO: might be smarter for EMPTY */
411 
412  /* a.ymax < b.ymin */
413  return a->ymax < b->ymin;
414 }
415 
416 static bool box2df_above(const BOX2DF *a, const BOX2DF *b)
417 {
418  if ( ! a || ! b ) return FALSE; /* TODO: might be smarter for EMPTY */
419 
420  /* a.ymin > b.ymax */
421  return a->ymin > b->ymax;
422 }
423 
424 static bool box2df_overabove(const BOX2DF *a, const BOX2DF *b)
425 {
426  if ( ! a || ! b ) return FALSE; /* TODO: might be smarter for EMPTY */
427 
428  /* a.ymin >= b.ymin */
429  return a->ymin >= b->ymin;
430 }
431 
435 static double box2df_distance_leaf_centroid(const BOX2DF *a, const BOX2DF *b)
436 {
437  /* The centroid->centroid distance between the boxes */
438  double a_x = (a->xmax + a->xmin) / 2.0;
439  double a_y = (a->ymax + a->ymin) / 2.0;
440  double b_x = (b->xmax + b->xmin) / 2.0;
441  double b_y = (b->ymax + b->ymin) / 2.0;
442 
443  /* This "distance" is only used for comparisons, */
444  return sqrt((a_x - b_x) * (a_x - b_x) + (a_y - b_y) * (a_y - b_y));
445 }
446 
447 #if POSTGIS_PGSQL_VERSION < 95
448 
452 static double box2df_distance_node_centroid(const BOX2DF *node, const BOX2DF *query)
453 {
454  BOX2DF q;
455  double qx, qy;
456  double d = 0.0;
457 
458  /* Turn query into point */
459  q.xmin = q.xmax = (query->xmin + query->xmax) / 2.0;
460  q.ymin = q.ymax = (query->ymin + query->ymax) / 2.0;
461  qx = q.xmin;
462  qy = q.ymin;
463 
464  /* Check for overlap */
465  if ( box2df_overlaps(node, &q) == LW_TRUE )
466  return 0.0;
467 
468  /* Above or below */
469  if ( qx >= node->xmin && qx <= node->xmax )
470  {
471  if( qy > node->ymax )
472  d = qy - node->ymax;
473  else if ( qy < node->ymin )
474  d = node->ymin - qy;
475  return d;
476  }
477  /* Left or right */
478  else if ( qy >= node->ymin && qy <= node->ymax )
479  {
480  if ( qx > node->xmax )
481  d = qx - node->xmax;
482  else if ( qx < node->xmin )
483  d = node->xmin - qx;
484  return d;
485  }
486  /* Corner quadrants */
487  else
488  {
489  /* below/left of xmin/ymin */
490  if ( qx < node->xmin && qy < node->ymin )
491  {
492  d = (node->xmin - qx) * (node->xmin - qx) +
493  (node->ymin - qy) * (node->ymin - qy);
494  }
495  /* above/left of xmin/ymax */
496  else if ( qx < node->xmin && qy > node->ymax )
497  {
498  d = (node->xmin - qx) * (node->xmin - qx) +
499  (node->ymax - qy) * (node->ymax - qy);
500  }
501  /* above/right of xmax/ymax */
502  else if ( qx > node->xmax && qy > node->ymax )
503  {
504  d = (node->xmax - qx) * (node->xmax - qx) +
505  (node->ymax - qy) * (node->ymax - qy);
506  }
507  /* below/right of xmax/ymin */
508  else if ( qx > node->xmin && qy < node->ymin )
509  {
510  d = (node->xmax - qx) * (node->xmax - qx) +
511  (node->ymin - qy) * (node->ymin - qy);
512  }
513  else
514  {
515  /*ERROR*/
516  elog(ERROR, "%s: reached unreachable code", __func__);
517  }
518  }
519 
520  return sqrt(d);
521 }
522 #endif
523 
524 /* Quick distance function */
525 static inline double pt_distance(double ax, double ay, double bx, double by)
526 {
527  return sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by));
528 }
529 
533 static double box2df_distance(const BOX2DF *a, const BOX2DF *b)
534 {
535  /* Check for overlap */
536  if ( box2df_overlaps(a, b) )
537  return 0.0;
538 
539  if ( box2df_left(a, b) )
540  {
541  if ( box2df_above(a, b) )
542  return pt_distance(a->xmax, a->ymin, b->xmin, b->ymax);
543  if ( box2df_below(a, b) )
544  return pt_distance(a->xmax, a->ymax, b->xmin, b->ymin);
545  else
546  return (double)b->xmin - (double)a->xmax;
547  }
548  if ( box2df_right(a, b) )
549  {
550  if ( box2df_above(a, b) )
551  return pt_distance(a->xmin, a->ymin, b->xmax, b->ymax);
552  if ( box2df_below(a, b) )
553  return pt_distance(a->xmin, a->ymax, b->xmax, b->ymin);
554  else
555  return (double)a->xmin - (double)b->xmax;
556  }
557  if ( box2df_above(a, b) )
558  {
559  if ( box2df_left(a, b) )
560  return pt_distance(a->xmax, a->ymin, b->xmin, b->ymax);
561  if ( box2df_right(a, b) )
562  return pt_distance(a->xmin, a->ymin, b->xmax, b->ymax);
563  else
564  return (double)a->ymin - (double)b->ymax;
565  }
566  if ( box2df_below(a, b) )
567  {
568  if ( box2df_left(a, b) )
569  return pt_distance(a->xmax, a->ymax, b->xmin, b->ymin);
570  if ( box2df_right(a, b) )
571  return pt_distance(a->xmin, a->ymax, b->xmax, b->ymin);
572  else
573  return (double)b->ymin - (double)a->ymax;
574  }
575 
576  return FLT_MAX;
577 }
578 
579 
586 int
587 gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df)
588 {
589  GSERIALIZED *gpart;
590  uint8_t flags;
591  int result = LW_SUCCESS;
592 
593  POSTGIS_DEBUG(4, "entered function");
594 
595  /*
596  ** The most info we need is the 8 bytes of serialized header plus the
597  ** of floats necessary to hold the bounding box.
598  */
599  if (VARATT_IS_EXTENDED(gsdatum))
600  {
601  gpart = (GSERIALIZED*)PG_DETOAST_DATUM_SLICE(gsdatum, 0, 8 + sizeof(BOX2DF));
602  }
603  else
604  {
605  gpart = (GSERIALIZED*)PG_DETOAST_DATUM(gsdatum);
606  }
607 
608  flags = gpart->flags;
609 
610  POSTGIS_DEBUGF(4, "got flags %d", gpart->flags);
611 
612  /* Do we even have a serialized bounding box? */
613  if ( FLAGS_GET_BBOX(flags) )
614  {
615  /* Yes! Copy it out into the box! */
616  POSTGIS_DEBUG(4, "copying box out of serialization");
617  memcpy(box2df, gpart->data, sizeof(BOX2DF));
618  result = LW_SUCCESS;
619  }
620  else
621  {
622  /* No, we need to calculate it from the full object. */
623  GBOX gbox;
624  GSERIALIZED *g = (GSERIALIZED*)PG_DETOAST_DATUM(gsdatum);
625 
626  gbox_init(&gbox);
627 
628  if (gserialized_get_gbox_p(g, &gbox) == LW_FAILURE)
629  {
630  POSTGIS_DEBUG(4, "could not calculate bbox, returning failure");
631  POSTGIS_FREE_IF_COPY_P(gpart, gsdatum);
632  POSTGIS_FREE_IF_COPY_P(g, gsdatum);
633  return LW_FAILURE;
634  }
635  POSTGIS_FREE_IF_COPY_P(g, gsdatum);
636  result = box2df_from_gbox_p(&gbox, box2df);
637  }
638 
639  POSTGIS_FREE_IF_COPY_P(gpart, gsdatum);
640 
641  if ( result == LW_SUCCESS )
642  {
643  POSTGIS_DEBUGF(4, "got box2df %s", box2df_to_string(box2df));
644  }
645 
646  return result;
647 }
648 
649 
654 static int
655 gserialized_datum_predicate_2d(Datum gs1, Datum gs2, box2df_predicate predicate)
656 {
657  BOX2DF b1, b2, *br1=NULL, *br2=NULL;
658  POSTGIS_DEBUG(3, "entered function");
659 
660  if (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) br1 = &b1;
661  if (gserialized_datum_get_box2df_p(gs2, &b2) == LW_SUCCESS) br2 = &b2;
662 
663  if ( predicate(br1, br2) )
664  {
665  POSTGIS_DEBUGF(3, "got boxes %s and %s", br1 ? box2df_to_string(&b1) : "(null)", br2 ? box2df_to_string(&b2) : "(null)");
666  return LW_TRUE;
667  }
668  return LW_FALSE;
669 }
670 
671 #if POSTGIS_PGSQL_VERSION > 94
672 static int
673 gserialized_datum_predicate_box2df_geom_2d(const BOX2DF *br1, Datum gs2, box2df_predicate predicate)
674 {
675  BOX2DF b2, *br2=NULL;
676  POSTGIS_DEBUG(3, "entered function");
677 
678  if (gserialized_datum_get_box2df_p(gs2, &b2) == LW_SUCCESS) br2 = &b2;
679 
680  if ( predicate(br1, br2) )
681  {
682  POSTGIS_DEBUGF(3, "got boxes %s", br2 ? box2df_to_string(&b2) : "(null)");
683  return LW_TRUE;
684  }
685  return LW_FALSE;
686 }
687 
688 /***********************************************************************
689 * BRIN 2-D Index Operator Functions
690 */
691 
693 Datum gserialized_contains_box2df_geom_2d(PG_FUNCTION_ARGS)
694 {
695  POSTGIS_DEBUG(3, "entered function");
696  if ( gserialized_datum_predicate_box2df_geom_2d((BOX2DF*)PG_GETARG_POINTER(0), PG_GETARG_DATUM(1), box2df_contains) == LW_TRUE )
697  PG_RETURN_BOOL(TRUE);
698 
699  PG_RETURN_BOOL(FALSE);
700 }
701 
704 {
705  if ( box2df_contains((BOX2DF *)PG_GETARG_POINTER(0), (BOX2DF *)PG_GETARG_POINTER(1)))
706  PG_RETURN_BOOL(TRUE);
707 
708  PG_RETURN_BOOL(FALSE);
709 }
710 
712 Datum gserialized_within_box2df_geom_2d(PG_FUNCTION_ARGS)
713 {
714  POSTGIS_DEBUG(3, "entered function");
715  if ( gserialized_datum_predicate_box2df_geom_2d((BOX2DF*)PG_GETARG_POINTER(0), PG_GETARG_DATUM(1), box2df_within) == LW_TRUE )
716  PG_RETURN_BOOL(TRUE);
717 
718  PG_RETURN_BOOL(FALSE);
719 }
720 
722 Datum gserialized_within_box2df_box2df_2d(PG_FUNCTION_ARGS)
723 {
724  if ( box2df_within((BOX2DF *)PG_GETARG_POINTER(0), (BOX2DF *)PG_GETARG_POINTER(1)))
725  PG_RETURN_BOOL(TRUE);
726 
727  PG_RETURN_BOOL(FALSE);
728 }
729 
731 Datum gserialized_overlaps_box2df_geom_2d(PG_FUNCTION_ARGS)
732 {
733  POSTGIS_DEBUG(3, "entered function");
734  if ( gserialized_datum_predicate_box2df_geom_2d((BOX2DF*)PG_GETARG_POINTER(0), PG_GETARG_DATUM(1), box2df_overlaps) == LW_TRUE )
735  PG_RETURN_BOOL(TRUE);
736 
737  PG_RETURN_BOOL(FALSE);
738 }
739 
742 {
743  if ( box2df_overlaps((BOX2DF *)PG_GETARG_POINTER(0), (BOX2DF *)PG_GETARG_POINTER(1)))
744  PG_RETURN_BOOL(TRUE);
745 
746  PG_RETURN_BOOL(FALSE);
747 }
748 #endif
749 
750 /***********************************************************************
751 * GiST 2-D Index Operator Functions
752 */
753 
755 Datum gserialized_distance_centroid_2d(PG_FUNCTION_ARGS)
756 {
757  BOX2DF b1, b2;
758  Datum gs1 = PG_GETARG_DATUM(0);
759  Datum gs2 = PG_GETARG_DATUM(1);
760 
761  POSTGIS_DEBUG(3, "entered function");
762 
763  /* Must be able to build box for each argument (ie, not empty geometry). */
764  if ( (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) &&
766  {
767  double distance = box2df_distance_leaf_centroid(&b1, &b2);
768  POSTGIS_DEBUGF(3, "got boxes %s and %s", box2df_to_string(&b1), box2df_to_string(&b2));
769  PG_RETURN_FLOAT8(distance);
770  }
771  PG_RETURN_FLOAT8(FLT_MAX);
772 }
773 
775 Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS)
776 {
777  BOX2DF b1, b2;
778  Datum gs1 = PG_GETARG_DATUM(0);
779  Datum gs2 = PG_GETARG_DATUM(1);
780 
781  POSTGIS_DEBUG(3, "entered function");
782 
783  /* Must be able to build box for each argument (ie, not empty geometry). */
784  if ( (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) &&
786  {
787  double distance = box2df_distance(&b1, &b2);
788  POSTGIS_DEBUGF(3, "got boxes %s and %s", box2df_to_string(&b1), box2df_to_string(&b2));
789  PG_RETURN_FLOAT8(distance);
790  }
791  PG_RETURN_FLOAT8(FLT_MAX);
792 }
793 
795 Datum gserialized_same_2d(PG_FUNCTION_ARGS)
796 {
797  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_equals) == LW_TRUE )
798  PG_RETURN_BOOL(TRUE);
799 
800  PG_RETURN_BOOL(FALSE);
801 }
802 
804 Datum gserialized_within_2d(PG_FUNCTION_ARGS)
805 {
806  POSTGIS_DEBUG(3, "entered function");
807  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_within) == LW_TRUE )
808  PG_RETURN_BOOL(TRUE);
809 
810  PG_RETURN_BOOL(FALSE);
811 }
812 
814 Datum gserialized_contains_2d(PG_FUNCTION_ARGS)
815 {
816  POSTGIS_DEBUG(3, "entered function");
817  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_contains) == LW_TRUE )
818  PG_RETURN_BOOL(TRUE);
819 
820  PG_RETURN_BOOL(FALSE);
821 }
822 
824 Datum gserialized_overlaps_2d(PG_FUNCTION_ARGS)
825 {
826  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overlaps) == LW_TRUE )
827  PG_RETURN_BOOL(TRUE);
828 
829  PG_RETURN_BOOL(FALSE);
830 }
831 
833 Datum gserialized_left_2d(PG_FUNCTION_ARGS)
834 {
835  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_left) == LW_TRUE )
836  PG_RETURN_BOOL(TRUE);
837 
838  PG_RETURN_BOOL(FALSE);
839 }
840 
842 Datum gserialized_right_2d(PG_FUNCTION_ARGS)
843 {
844  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_right) == LW_TRUE )
845  PG_RETURN_BOOL(TRUE);
846 
847  PG_RETURN_BOOL(FALSE);
848 }
849 
851 Datum gserialized_above_2d(PG_FUNCTION_ARGS)
852 {
853  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_above) == LW_TRUE )
854  PG_RETURN_BOOL(TRUE);
855 
856  PG_RETURN_BOOL(FALSE);
857 }
858 
860 Datum gserialized_below_2d(PG_FUNCTION_ARGS)
861 {
862  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_below) == LW_TRUE )
863  PG_RETURN_BOOL(TRUE);
864 
865  PG_RETURN_BOOL(FALSE);
866 }
867 
869 Datum gserialized_overleft_2d(PG_FUNCTION_ARGS)
870 {
871  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overleft) == LW_TRUE )
872  PG_RETURN_BOOL(TRUE);
873 
874  PG_RETURN_BOOL(FALSE);
875 }
876 
878 Datum gserialized_overright_2d(PG_FUNCTION_ARGS)
879 {
880  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overright) == LW_TRUE )
881  PG_RETURN_BOOL(TRUE);
882 
883  PG_RETURN_BOOL(FALSE);
884 }
885 
887 Datum gserialized_overabove_2d(PG_FUNCTION_ARGS)
888 {
889  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overabove) == LW_TRUE )
890  PG_RETURN_BOOL(TRUE);
891 
892  PG_RETURN_BOOL(FALSE);
893 }
894 
896 Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS)
897 {
898  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overbelow) == LW_TRUE )
899  PG_RETURN_BOOL(TRUE);
900 
901  PG_RETURN_BOOL(FALSE);
902 }
903 
904 
905 /***********************************************************************
906 * GiST Index Support Functions
907 */
908 
909 /*
910 ** GiST support function. Given a geography, return a "compressed"
911 ** version. In this case, we convert the geography into a geocentric
912 ** bounding box. If the geography already has the box embedded in it
913 ** we pull that out and hand it back.
914 */
916 Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS)
917 {
918  GISTENTRY *entry_in = (GISTENTRY*)PG_GETARG_POINTER(0);
919  GISTENTRY *entry_out = NULL;
920  BOX2DF bbox_out;
921  int result = LW_SUCCESS;
922 
923  POSTGIS_DEBUG(4, "[GIST] 'compress' function called");
924 
925  /*
926  ** Not a leaf key? There's nothing to do.
927  ** Return the input unchanged.
928  */
929  if ( ! entry_in->leafkey )
930  {
931  POSTGIS_DEBUG(4, "[GIST] non-leafkey entry, returning input unaltered");
932  PG_RETURN_POINTER(entry_in);
933  }
934 
935  POSTGIS_DEBUG(4, "[GIST] processing leafkey input");
936  entry_out = palloc(sizeof(GISTENTRY));
937 
938  /*
939  ** Null key? Make a copy of the input entry and
940  ** return.
941  */
942  if ( DatumGetPointer(entry_in->key) == NULL )
943  {
944  POSTGIS_DEBUG(4, "[GIST] leafkey is null");
945  gistentryinit(*entry_out, (Datum) 0, entry_in->rel,
946  entry_in->page, entry_in->offset, FALSE);
947  POSTGIS_DEBUG(4, "[GIST] returning copy of input");
948  PG_RETURN_POINTER(entry_out);
949  }
950 
951  /* Extract our index key from the GiST entry. */
952  result = gserialized_datum_get_box2df_p(entry_in->key, &bbox_out);
953 
954  /* Is the bounding box valid (non-empty, non-infinite)? If not, return input uncompressed. */
955  if ( result == LW_FAILURE )
956  {
957  POSTGIS_DEBUG(4, "[GIST] empty geometry!");
958  PG_RETURN_POINTER(entry_in);
959  }
960 
961  POSTGIS_DEBUGF(4, "[GIST] got entry_in->key: %s", box2df_to_string(&bbox_out));
962 
963  /* Check all the dimensions for finite values */
964  if ( ! isfinite(bbox_out.xmax) || ! isfinite(bbox_out.xmin) ||
965  ! isfinite(bbox_out.ymax) || ! isfinite(bbox_out.ymin) )
966  {
967  POSTGIS_DEBUG(4, "[GIST] infinite geometry!");
968  PG_RETURN_POINTER(entry_in);
969  }
970 
971  /* Enure bounding box has minimums below maximums. */
972  box2df_validate(&bbox_out);
973 
974  /* Prepare GISTENTRY for return. */
975  gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)),
976  entry_in->rel, entry_in->page, entry_in->offset, FALSE);
977 
978  /* Return GISTENTRY. */
979  POSTGIS_DEBUG(4, "[GIST] 'compress' function complete");
980  PG_RETURN_POINTER(entry_out);
981 }
982 
983 
984 /*
985 ** GiST support function.
986 ** Decompress an entry. Unused for geography, so we return.
987 */
989 Datum gserialized_gist_decompress_2d(PG_FUNCTION_ARGS)
990 {
991  POSTGIS_DEBUG(5, "[GIST] 'decompress' function called");
992  /* We don't decompress. Just return the input. */
993  PG_RETURN_POINTER(PG_GETARG_POINTER(0));
994 }
995 
996 
997 
998 /*
999 ** GiST support function. Called from gserialized_gist_consistent below.
1000 */
1001 static inline bool gserialized_gist_consistent_leaf_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
1002 {
1003  bool retval;
1004 
1005  POSTGIS_DEBUGF(4, "[GIST] leaf consistent, strategy [%d], count[%i]",
1006  strategy, g2d_counter_leaf++);
1007 
1008  switch (strategy)
1009  {
1010 
1011  /* Basic overlaps */
1012  case RTOverlapStrategyNumber:
1013  retval = (bool) box2df_overlaps(key, query);
1014  break;
1015  case RTSameStrategyNumber:
1016  retval = (bool) box2df_equals(key, query);
1017  break;
1018  case RTContainsStrategyNumber:
1019  case RTOldContainsStrategyNumber:
1020  retval = (bool) box2df_contains(key, query);
1021  break;
1022  case RTContainedByStrategyNumber:
1023  case RTOldContainedByStrategyNumber:
1024  retval = (bool) box2df_contains(query, key);
1025  break;
1026 
1027  /* To one side */
1028  case RTAboveStrategyNumber:
1029  retval = (bool) box2df_above(key, query);
1030  break;
1031  case RTBelowStrategyNumber:
1032  retval = (bool) box2df_below(key, query);
1033  break;
1034  case RTRightStrategyNumber:
1035  retval = (bool) box2df_right(key, query);
1036  break;
1037  case RTLeftStrategyNumber:
1038  retval = (bool) box2df_left(key, query);
1039  break;
1040 
1041  /* Overlapping to one side */
1042  case RTOverAboveStrategyNumber:
1043  retval = (bool) box2df_overabove(key, query);
1044  break;
1045  case RTOverBelowStrategyNumber:
1046  retval = (bool) box2df_overbelow(key, query);
1047  break;
1048  case RTOverRightStrategyNumber:
1049  retval = (bool) box2df_overright(key, query);
1050  break;
1051  case RTOverLeftStrategyNumber:
1052  retval = (bool) box2df_overleft(key, query);
1053  break;
1054 
1055  default:
1056  retval = FALSE;
1057  }
1058 
1059  return (retval);
1060 }
1061 
1062 /*
1063 ** GiST support function. Called from gserialized_gist_consistent below.
1064 */
1065 static inline bool gserialized_gist_consistent_internal_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
1066 {
1067  bool retval;
1068 
1069  POSTGIS_DEBUGF(4, "[GIST] internal consistent, strategy [%d], count[%i], query[%s], key[%s]",
1070  strategy, g2d_counter_internal++, box2df_to_string(query), box2df_to_string(key) );
1071 
1072  switch (strategy)
1073  {
1074 
1075  /* Basic overlaps */
1076  case RTOverlapStrategyNumber:
1077  retval = (bool) box2df_overlaps(key, query);
1078  break;
1079  case RTSameStrategyNumber:
1080  case RTContainsStrategyNumber:
1081  case RTOldContainsStrategyNumber:
1082  retval = (bool) box2df_contains(key, query);
1083  break;
1084  case RTContainedByStrategyNumber:
1085  case RTOldContainedByStrategyNumber:
1086  retval = (bool) box2df_overlaps(key, query);
1087  break;
1088 
1089  /* To one side */
1090  case RTAboveStrategyNumber:
1091  retval = (bool)(!box2df_overbelow(key, query));
1092  break;
1093  case RTBelowStrategyNumber:
1094  retval = (bool)(!box2df_overabove(key, query));
1095  break;
1096  case RTRightStrategyNumber:
1097  retval = (bool)(!box2df_overleft(key, query));
1098  break;
1099  case RTLeftStrategyNumber:
1100  retval = (bool)(!box2df_overright(key, query));
1101  break;
1102 
1103  /* Overlapping to one side */
1104  case RTOverAboveStrategyNumber:
1105  retval = (bool)(!box2df_below(key, query));
1106  break;
1107  case RTOverBelowStrategyNumber:
1108  retval = (bool)(!box2df_above(key, query));
1109  break;
1110  case RTOverRightStrategyNumber:
1111  retval = (bool)(!box2df_left(key, query));
1112  break;
1113  case RTOverLeftStrategyNumber:
1114  retval = (bool)(!box2df_right(key, query));
1115  break;
1116 
1117  default:
1118  retval = FALSE;
1119  }
1120 
1121  return (retval);
1122 }
1123 
1124 /*
1125 ** GiST support function. Take in a query and an entry and see what the
1126 ** relationship is, based on the query strategy.
1127 */
1129 Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS)
1130 {
1131  GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
1132  StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1133  bool result;
1134  BOX2DF query_gbox_index;
1135 
1136  /* PostgreSQL 8.4 and later require the RECHECK flag to be set here,
1137  rather than being supplied as part of the operator class definition */
1138  bool *recheck = (bool *) PG_GETARG_POINTER(4);
1139 
1140  /* We set recheck to false to avoid repeatedly pulling every "possibly matched" geometry
1141  out during index scans. For cases when the geometries are large, rechecking
1142  can make things twice as slow. */
1143  *recheck = false;
1144 
1145  POSTGIS_DEBUG(4, "[GIST] 'consistent' function called");
1146 
1147  /* Quick sanity check on query argument. */
1148  if ( DatumGetPointer(PG_GETARG_DATUM(1)) == NULL )
1149  {
1150  POSTGIS_DEBUG(4, "[GIST] null query pointer (!?!), returning false");
1151  PG_RETURN_BOOL(FALSE); /* NULL query! This is screwy! */
1152  }
1153 
1154  /* Quick sanity check on entry key. */
1155  if ( DatumGetPointer(entry->key) == NULL )
1156  {
1157  POSTGIS_DEBUG(4, "[GIST] null index entry, returning false");
1158  PG_RETURN_BOOL(FALSE); /* NULL entry! */
1159  }
1160 
1161  /* Null box should never make this far. */
1162  if ( gserialized_datum_get_box2df_p(PG_GETARG_DATUM(1), &query_gbox_index) == LW_FAILURE )
1163  {
1164  POSTGIS_DEBUG(4, "[GIST] null query_gbox_index!");
1165  PG_RETURN_BOOL(FALSE);
1166  }
1167 
1168  /* Treat leaf node tests different from internal nodes */
1169  if (GIST_LEAF(entry))
1170  {
1172  (BOX2DF*)DatumGetPointer(entry->key),
1173  &query_gbox_index, strategy);
1174  }
1175  else
1176  {
1178  (BOX2DF*)DatumGetPointer(entry->key),
1179  &query_gbox_index, strategy);
1180  }
1181 
1182  PG_RETURN_BOOL(result);
1183 }
1184 
1185 
1186 /*
1187 ** GiST support function. Take in a query and an entry and return the "distance"
1188 ** between them.
1189 **
1190 ** Given an index entry p and a query value q, this function determines the
1191 ** index entry's "distance" from the query value. This function must be
1192 ** supplied if the operator class contains any ordering operators. A query
1193 ** using the ordering operator will be implemented by returning index entries
1194 ** with the smallest "distance" values first, so the results must be consistent
1195 ** with the operator's semantics. For a leaf index entry the result just
1196 ** represents the distance to the index entry; for an internal tree node, the
1197 ** result must be the smallest distance that any child entry could have.
1198 **
1199 ** Strategy 13 = true distance tests <->
1200 ** Strategy 14 = box-based distance tests <#>
1201 */
1203 Datum gserialized_gist_distance_2d(PG_FUNCTION_ARGS)
1204 {
1205  GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
1206  BOX2DF query_box;
1207  BOX2DF *entry_box;
1208  StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1209  double distance;
1210 #if POSTGIS_PGSQL_VERSION >= 95
1211  bool *recheck = (bool *) PG_GETARG_POINTER(4);
1212 #endif
1213 
1214  POSTGIS_DEBUG(4, "[GIST] 'distance' function called");
1215 
1216  /* We are using '13' as the gist true-distance <-> strategy number
1217  * and '14' as the gist distance-between-boxes <#> strategy number */
1218  if ( strategy != 13 && strategy != 14 ) {
1219  elog(ERROR, "unrecognized strategy number: %d", strategy);
1220  PG_RETURN_FLOAT8(FLT_MAX);
1221  }
1222 
1223  /* Null box should never make this far. */
1224  if ( gserialized_datum_get_box2df_p(PG_GETARG_DATUM(1), &query_box) == LW_FAILURE )
1225  {
1226  POSTGIS_DEBUG(4, "[GIST] null query_gbox_index!");
1227  PG_RETURN_FLOAT8(FLT_MAX);
1228  }
1229 
1230  /* Get the entry box */
1231  entry_box = (BOX2DF*)DatumGetPointer(entry->key);
1232 
1233 #if POSTGIS_PGSQL_VERSION >= 95
1234 
1235  /* Box-style distance test */
1236  if ( strategy == 14 ) /* operator <#> */
1237  {
1238  distance = box2df_distance(entry_box, &query_box);
1239  }
1240  /* True distance test (formerly centroid distance) */
1241  else if ( strategy == 13 ) /* operator <-> */
1242  {
1243  /* In all cases, since we only have keys (boxes) we'll return */
1244  /* the minimum possible distance, which is the box2df_distance */
1245  /* and let the recheck sort things out in the case of leaves */
1246  distance = box2df_distance(entry_box, &query_box);
1247 
1248  if (GIST_LEAF(entry))
1249  *recheck = true;
1250  }
1251  else
1252  {
1253  elog(ERROR, "%s: reached unreachable code", __func__);
1254  PG_RETURN_NULL();
1255  }
1256 #else
1257  /* Box-style distance test */
1258  if ( strategy == 14 )
1259  {
1260  distance = (double)box2df_distance(entry_box, &query_box);
1261  PG_RETURN_FLOAT8(distance);
1262  }
1263 
1264  /* Treat leaf node tests different from internal nodes */
1265  if (GIST_LEAF(entry))
1266  {
1267  /* Calculate distance to leaves */
1268  distance = (double)box2df_distance_leaf_centroid(entry_box, &query_box);
1269  }
1270  else
1271  {
1272  /* Calculate distance for internal nodes */
1273  distance = (double)box2df_distance_node_centroid(entry_box, &query_box);
1274  }
1275 #endif
1276 
1277  PG_RETURN_FLOAT8(distance);
1278 }
1279 
1280 /*
1281 ** Function to pack floats of different realms
1282 ** This function serves to pack bit flags inside float type
1283 ** Resulted value represent can be from four different "realms"
1284 ** Every value from realm 3 is greater than any value from realms 2, 1 and 0.
1285 ** Every value from realm 2 is less than every value from realm 3 and greater
1286 ** than any value from realm 1 and 0, and so on. Values from the same realm
1287 ** loose two bits of precision. This technique is possible due to floating
1288 ** point numbers specification according to IEEE 754: exponent bits are highest
1289 ** (excluding sign bits, but here penalty is always positive). If float a is
1290 ** greater than float b, integer A with same bit representation as a is greater
1291 ** than integer B with same bits as b.
1292 */
1293 static float pack_float(const float value, const int realm)
1294 {
1295  union {
1296  float f;
1297  struct { unsigned value:31, sign:1; } vbits;
1298  struct { unsigned value:29, realm:2, sign:1; } rbits;
1299  } a;
1300 
1301  a.f = value;
1302  a.rbits.value = a.vbits.value >> 2;
1303  a.rbits.realm = realm;
1304 
1305  return a.f;
1306 }
1307 
1308 /*
1309 ** GiST support function. Calculate the "penalty" cost of adding this entry into an existing entry.
1310 ** Calculate the change in volume of the old entry once the new entry is added.
1311 */
1313 Datum gserialized_gist_penalty_2d(PG_FUNCTION_ARGS)
1314 {
1315  GISTENTRY *origentry = (GISTENTRY*) PG_GETARG_POINTER(0);
1316  GISTENTRY *newentry = (GISTENTRY*) PG_GETARG_POINTER(1);
1317  float *result = (float*) PG_GETARG_POINTER(2);
1318  BOX2DF *gbox_index_orig, *gbox_index_new;
1319  float size_union, size_orig, edge_union, edge_orig;
1320 
1321  POSTGIS_DEBUG(4, "[GIST] 'penalty' function called");
1322 
1323  gbox_index_orig = (BOX2DF*)DatumGetPointer(origentry->key);
1324  gbox_index_new = (BOX2DF*)DatumGetPointer(newentry->key);
1325 
1326  /* Drop out if we're dealing with null inputs. Shouldn't happen. */
1327  if ( (gbox_index_orig == NULL) && (gbox_index_new == NULL) )
1328  {
1329  POSTGIS_DEBUG(4, "[GIST] both inputs NULL! returning penalty of zero");
1330  *result = 0.0;
1331  PG_RETURN_FLOAT8(*result);
1332  }
1333 
1334  /* Calculate the size difference of the boxes. */
1335  size_union = box2df_union_size(gbox_index_orig, gbox_index_new);
1336  size_orig = box2df_size(gbox_index_orig);
1337  *result = size_union - size_orig;
1338 
1339  /* REALM 0: No extension is required, volume is zero, return edge */
1340  /* REALM 1: No extension is required, return nonzero area */
1341  /* REALM 2: Area extension is zero, return nonzero edge extension */
1342  /* REALM 3: Area extension is nonzero, return it */
1343 
1344  if( *result == 0 )
1345  {
1346  if (size_orig > 0)
1347  {
1348  *result = pack_float(size_orig, 1); /* REALM 1 */
1349  }
1350  else
1351  {
1352  edge_union = box2df_union_edge(gbox_index_orig, gbox_index_new);
1353  edge_orig = box2df_edge(gbox_index_orig);
1354  *result = edge_union - edge_orig;
1355  if( *result == 0 )
1356  {
1357  *result = pack_float(edge_orig, 0); /* REALM 0 */
1358  }
1359  else
1360  {
1361  *result = pack_float(*result, 2); /* REALM 2 */
1362  }
1363  }
1364  }
1365  else
1366  {
1367  *result = pack_float(*result, 3); /* REALM 3 */
1368  }
1369 
1370  POSTGIS_DEBUGF(4, "[GIST] 'penalty', union size (%.12f), original size (%.12f), penalty (%.12f)", size_union, size_orig, *result);
1371 
1372  PG_RETURN_POINTER(result);
1373 }
1374 
1375 /*
1376 ** GiST support function. Merge all the boxes in a page into one master box.
1377 */
1379 Datum gserialized_gist_union_2d(PG_FUNCTION_ARGS)
1380 {
1381  GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
1382  int *sizep = (int *) PG_GETARG_POINTER(1); /* Size of the return value */
1383  int numranges, i;
1384  BOX2DF *box_cur, *box_union;
1385 
1386  POSTGIS_DEBUG(4, "[GIST] 'union' function called");
1387 
1388  numranges = entryvec->n;
1389 
1390  box_cur = (BOX2DF*) DatumGetPointer(entryvec->vector[0].key);
1391 
1392  box_union = box2df_copy(box_cur);
1393 
1394  for ( i = 1; i < numranges; i++ )
1395  {
1396  box_cur = (BOX2DF*) DatumGetPointer(entryvec->vector[i].key);
1397  box2df_merge(box_union, box_cur);
1398  }
1399 
1400  *sizep = sizeof(BOX2DF);
1401 
1402  POSTGIS_DEBUGF(4, "[GIST] 'union', numranges(%i), pageunion %s", numranges, box2df_to_string(box_union));
1403 
1404  PG_RETURN_POINTER(box_union);
1405 
1406 }
1407 
1408 /*
1409 ** GiST support function. Test equality of keys.
1410 */
1412 Datum gserialized_gist_same_2d(PG_FUNCTION_ARGS)
1413 {
1414  BOX2DF *b1 = (BOX2DF*)PG_GETARG_POINTER(0);
1415  BOX2DF *b2 = (BOX2DF*)PG_GETARG_POINTER(1);
1416  bool *result = (bool*)PG_GETARG_POINTER(2);
1417 
1418  POSTGIS_DEBUG(4, "[GIST] 'same' function called");
1419 
1420  *result = box2df_equals(b1, b2);
1421 
1422  PG_RETURN_POINTER(result);
1423 }
1424 
1425 #if KOROTKOV_SPLIT > 0
1426 /*
1427  * Adjust BOX2DF b boundaries with insertion of addon.
1428  */
1429 static void
1430 adjustBox(BOX2DF *b, BOX2DF *addon)
1431 {
1432  if (b->xmax < addon->xmax || isnan(b->xmax))
1433  b->xmax = addon->xmax;
1434  if (b->xmin > addon->xmin || isnan(b->xmin))
1435  b->xmin = addon->xmin;
1436  if (b->ymax < addon->ymax || isnan(b->ymax))
1437  b->ymax = addon->ymax;
1438  if (b->ymin > addon->ymin || isnan(b->ymin))
1439  b->ymin = addon->ymin;
1440 }
1441 
1442 /*
1443  * Trivial split: half of entries will be placed on one page
1444  * and another half - to another
1445  */
1446 static void
1447 fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
1448 {
1449  OffsetNumber i,
1450  maxoff;
1451  BOX2DF *unionL = NULL,
1452  *unionR = NULL;
1453  int nbytes;
1454 
1455  maxoff = entryvec->n - 1;
1456 
1457  nbytes = (maxoff + 2) * sizeof(OffsetNumber);
1458  v->spl_left = (OffsetNumber *) palloc(nbytes);
1459  v->spl_right = (OffsetNumber *) palloc(nbytes);
1460  v->spl_nleft = v->spl_nright = 0;
1461 
1462  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1463  {
1464  BOX2DF *cur = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1465 
1466  if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
1467  {
1468  v->spl_left[v->spl_nleft] = i;
1469  if (unionL == NULL)
1470  {
1471  unionL = (BOX2DF *) palloc(sizeof(BOX2DF));
1472  *unionL = *cur;
1473  }
1474  else
1475  adjustBox(unionL, cur);
1476 
1477  v->spl_nleft++;
1478  }
1479  else
1480  {
1481  v->spl_right[v->spl_nright] = i;
1482  if (unionR == NULL)
1483  {
1484  unionR = (BOX2DF *) palloc(sizeof(BOX2DF));
1485  *unionR = *cur;
1486  }
1487  else
1488  adjustBox(unionR, cur);
1489 
1490  v->spl_nright++;
1491  }
1492  }
1493 
1494  if (v->spl_ldatum_exists)
1495  adjustBox(unionL, (BOX2DF *) DatumGetPointer(v->spl_ldatum));
1496  v->spl_ldatum = BoxPGetDatum(unionL);
1497 
1498  if (v->spl_rdatum_exists)
1499  adjustBox(unionR, (BOX2DF *) DatumGetPointer(v->spl_rdatum));
1500  v->spl_rdatum = BoxPGetDatum(unionR);
1501 
1502  v->spl_ldatum_exists = v->spl_rdatum_exists = false;
1503 }
1504 
1505 /*
1506  * Represents information about an entry that can be placed to either group
1507  * without affecting overlap over selected axis ("common entry").
1508  */
1509 typedef struct
1510 {
1511  /* Index of entry in the initial array */
1512  int index;
1513  /* Delta between penalties of entry insertion into different groups */
1514  float delta;
1515 } CommonEntry;
1516 
1517 /*
1518  * Context for g_box_consider_split. Contains information about currently
1519  * selected split and some general information.
1520  */
1521 typedef struct
1522 {
1523  int entriesCount; /* total number of entries being split */
1524  BOX2DF boundingBox; /* minimum bounding box across all entries */
1525 
1526  /* Information about currently selected split follows */
1527 
1528  bool first; /* true if no split was selected yet */
1529 
1530  float leftUpper; /* upper bound of left interval */
1531  float rightLower; /* lower bound of right interval */
1532 
1533  float4 ratio;
1534  float4 overlap;
1535  int dim; /* axis of this split */
1536  float range; /* width of general MBR projection to the
1537  * selected axis */
1539 
1540 /*
1541  * Interval represents projection of box to axis.
1542  */
1543 typedef struct
1544 {
1545  float lower,
1546  upper;
1547 } SplitInterval;
1548 
1549 /*
1550  * Interval comparison function by lower bound of the interval;
1551  */
1552 static int
1553 interval_cmp_lower(const void *i1, const void *i2)
1554 {
1555  float lower1 = ((const SplitInterval *) i1)->lower,
1556  lower2 = ((const SplitInterval *) i2)->lower;
1557 
1558  if (isnan(lower1))
1559  {
1560  if (isnan(lower2))
1561  return 0;
1562  else
1563  return 1;
1564  }
1565  else if (isnan(lower2))
1566  {
1567  return -1;
1568  }
1569  else
1570  {
1571  if (lower1 < lower2)
1572  return -1;
1573  else if (lower1 > lower2)
1574  return 1;
1575  else
1576  return 0;
1577  }
1578 }
1579 
1580 
1581 /*
1582  * Interval comparison function by upper bound of the interval;
1583  */
1584 static int
1585 interval_cmp_upper(const void *i1, const void *i2)
1586 {
1587  float upper1 = ((const SplitInterval *) i1)->upper,
1588  upper2 = ((const SplitInterval *) i2)->upper;
1589 
1590  if (isnan(upper1))
1591  {
1592  if (isnan(upper2))
1593  return 0;
1594  else
1595  return -1;
1596  }
1597  else if (isnan(upper2))
1598  {
1599  return 1;
1600  }
1601  else
1602  {
1603  if (upper1 < upper2)
1604  return -1;
1605  else if (upper1 > upper2)
1606  return 1;
1607  else
1608  return 0;
1609  }
1610 }
1611 
1612 /*
1613  * Replace negative value with zero.
1614  */
1615 static inline float
1616 non_negative(float val)
1617 {
1618  if (val >= 0.0f)
1619  return val;
1620  else
1621  return 0.0f;
1622 }
1623 
1624 /*
1625  * Consider replacement of currently selected split with the better one.
1626  */
1627 static inline void
1629  float rightLower, int minLeftCount,
1630  float leftUpper, int maxLeftCount)
1631 {
1632  int leftCount,
1633  rightCount;
1634  float4 ratio,
1635  overlap;
1636  float range;
1637 
1638  POSTGIS_DEBUGF(5, "consider split: dimNum = %d, rightLower = %f, "
1639  "minLeftCount = %d, leftUpper = %f, maxLeftCount = %d ",
1640  dimNum, rightLower, minLeftCount, leftUpper, maxLeftCount);
1641 
1642  /*
1643  * Calculate entries distribution ratio assuming most uniform distribution
1644  * of common entries.
1645  */
1646  if (minLeftCount >= (context->entriesCount + 1) / 2)
1647  {
1648  leftCount = minLeftCount;
1649  }
1650  else
1651  {
1652  if (maxLeftCount <= context->entriesCount / 2)
1653  leftCount = maxLeftCount;
1654  else
1655  leftCount = context->entriesCount / 2;
1656  }
1657  rightCount = context->entriesCount - leftCount;
1658 
1659  /*
1660  * Ratio of split - quotient between size of lesser group and total
1661  * entries count.
1662  */
1663  ratio = ((float4) Min(leftCount, rightCount)) /
1664  ((float4) context->entriesCount);
1665 
1666  if (ratio > LIMIT_RATIO)
1667  {
1668  bool selectthis = false;
1669 
1670  /*
1671  * The ratio is acceptable, so compare current split with previously
1672  * selected one. Between splits of one dimension we search for minimal
1673  * overlap (allowing negative values) and minimal ration (between same
1674  * overlaps. We switch dimension if find less overlap (non-negative)
1675  * or less range with same overlap.
1676  */
1677  if (dimNum == 0)
1678  range = context->boundingBox.xmax - context->boundingBox.xmin;
1679  else
1680  range = context->boundingBox.ymax - context->boundingBox.ymin;
1681 
1682  overlap = (leftUpper - rightLower) / range;
1683 
1684  /* If there is no previous selection, select this */
1685  if (context->first)
1686  selectthis = true;
1687  else if (context->dim == dimNum)
1688  {
1689  /*
1690  * Within the same dimension, choose the new split if it has a
1691  * smaller overlap, or same overlap but better ratio.
1692  */
1693  if (overlap < context->overlap ||
1694  (overlap == context->overlap && ratio > context->ratio))
1695  selectthis = true;
1696  }
1697  else
1698  {
1699  /*
1700  * Across dimensions, choose the new split if it has a smaller
1701  * *non-negative* overlap, or same *non-negative* overlap but
1702  * bigger range. This condition differs from the one described in
1703  * the article. On the datasets where leaf MBRs don't overlap
1704  * themselves, non-overlapping splits (i.e. splits which have zero
1705  * *non-negative* overlap) are frequently possible. In this case
1706  * splits tends to be along one dimension, because most distant
1707  * non-overlapping splits (i.e. having lowest negative overlap)
1708  * appears to be in the same dimension as in the previous split.
1709  * Therefore MBRs appear to be very prolonged along another
1710  * dimension, which leads to bad search performance. Using range
1711  * as the second split criteria makes MBRs more quadratic. Using
1712  * *non-negative* overlap instead of overlap as the first split
1713  * criteria gives to range criteria a chance to matter, because
1714  * non-overlapping splits are equivalent in this criteria.
1715  */
1716  if (non_negative(overlap) < non_negative(context->overlap) ||
1717  (range > context->range &&
1718  non_negative(overlap) <= non_negative(context->overlap)))
1719  selectthis = true;
1720  }
1721 
1722  if (selectthis)
1723  {
1724  /* save information about selected split */
1725  context->first = false;
1726  context->ratio = ratio;
1727  context->range = range;
1728  context->overlap = overlap;
1729  context->rightLower = rightLower;
1730  context->leftUpper = leftUpper;
1731  context->dim = dimNum;
1732  POSTGIS_DEBUG(5, "split selected");
1733  }
1734  }
1735 }
1736 
1737 /*
1738  * Return increase of original BOX2DF area by new BOX2DF area insertion.
1739  */
1740 static float
1741 box_penalty(BOX2DF *original, BOX2DF *new)
1742 {
1743  float union_width,
1744  union_height;
1745 
1746  union_width = Max(original->xmax, new->xmax) -
1747  Min(original->xmin, new->xmin);
1748  union_height = Max(original->ymax, new->ymax) -
1749  Min(original->ymin, new->ymin);
1750  return union_width * union_height - (original->xmax - original->xmin) *
1751  (original->ymax - original->ymin);
1752 }
1753 
1754 /*
1755  * Compare common entries by their deltas.
1756  */
1757 static int
1758 common_entry_cmp(const void *i1, const void *i2)
1759 {
1760  float delta1 = ((const CommonEntry *) i1)->delta,
1761  delta2 = ((const CommonEntry *) i2)->delta;
1762 
1763  if (delta1 < delta2)
1764  return -1;
1765  else if (delta1 > delta2)
1766  return 1;
1767  else
1768  return 0;
1769 }
1770 
1771 /*
1772  * --------------------------------------------------------------------------
1773  * Double sorting split algorithm. This is used for both boxes and points.
1774  *
1775  * The algorithm finds split of boxes by considering splits along each axis.
1776  * Each entry is first projected as an interval on the X-axis, and different
1777  * ways to split the intervals into two groups are considered, trying to
1778  * minimize the overlap of the groups. Then the same is repeated for the
1779  * Y-axis, and the overall best split is chosen. The quality of a split is
1780  * determined by overlap along that axis and some other criteria (see
1781  * g_box_consider_split).
1782  *
1783  * After that, all the entries are divided into three groups:
1784  *
1785  * 1) Entries which should be placed to the left group
1786  * 2) Entries which should be placed to the right group
1787  * 3) "Common entries" which can be placed to any of groups without affecting
1788  * of overlap along selected axis.
1789  *
1790  * The common entries are distributed by minimizing penalty.
1791  *
1792  * For details see:
1793  * "A new double sorting-based node splitting algorithm for R-tree", A. Korotkov
1794  * http://syrcose.ispras.ru/2011/files/SYRCoSE2011_Proceedings.pdf#page=36
1795  * --------------------------------------------------------------------------
1796  */
1798 Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS)
1799 {
1800  GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
1801  GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
1802  OffsetNumber i,
1803  maxoff;
1804  ConsiderSplitContext context;
1805  BOX2DF *box,
1806  *leftBox,
1807  *rightBox;
1808  int dim,
1809  commonEntriesCount;
1810  SplitInterval *intervalsLower,
1811  *intervalsUpper;
1812  CommonEntry *commonEntries;
1813  int nentries;
1814 
1815  POSTGIS_DEBUG(3, "[GIST] 'picksplit' entered");
1816 
1817  memset(&context, 0, sizeof(ConsiderSplitContext));
1818 
1819  maxoff = entryvec->n - 1;
1820  nentries = context.entriesCount = maxoff - FirstOffsetNumber + 1;
1821 
1822  /* Allocate arrays for intervals along axes */
1823  intervalsLower = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
1824  intervalsUpper = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
1825 
1826  /*
1827  * Calculate the overall minimum bounding box over all the entries.
1828  */
1829  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1830  {
1831  box = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1832  if (i == FirstOffsetNumber)
1833  context.boundingBox = *box;
1834  else
1835  adjustBox(&context.boundingBox, box);
1836  }
1837 
1838  POSTGIS_DEBUGF(4, "boundingBox is %s", box2df_to_string(
1839  &context.boundingBox));
1840 
1841  /*
1842  * Iterate over axes for optimal split searching.
1843  */
1844  context.first = true; /* nothing selected yet */
1845  for (dim = 0; dim < 2; dim++)
1846  {
1847  float leftUpper,
1848  rightLower;
1849  int i1,
1850  i2;
1851 
1852  /* Project each entry as an interval on the selected axis. */
1853  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1854  {
1855  box = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1856  if (dim == 0)
1857  {
1858  intervalsLower[i - FirstOffsetNumber].lower = box->xmin;
1859  intervalsLower[i - FirstOffsetNumber].upper = box->xmax;
1860  }
1861  else
1862  {
1863  intervalsLower[i - FirstOffsetNumber].lower = box->ymin;
1864  intervalsLower[i - FirstOffsetNumber].upper = box->ymax;
1865  }
1866  }
1867 
1868  /*
1869  * Make two arrays of intervals: one sorted by lower bound and another
1870  * sorted by upper bound.
1871  */
1872  memcpy(intervalsUpper, intervalsLower,
1873  sizeof(SplitInterval) * nentries);
1874  qsort(intervalsLower, nentries, sizeof(SplitInterval),
1876  qsort(intervalsUpper, nentries, sizeof(SplitInterval),
1878 
1879  /*----
1880  * The goal is to form a left and right interval, so that every entry
1881  * interval is contained by either left or right interval (or both).
1882  *
1883  * For example, with the intervals (0,1), (1,3), (2,3), (2,4):
1884  *
1885  * 0 1 2 3 4
1886  * +-+
1887  * +---+
1888  * +-+
1889  * +---+
1890  *
1891  * The left and right intervals are of the form (0,a) and (b,4).
1892  * We first consider splits where b is the lower bound of an entry.
1893  * We iterate through all entries, and for each b, calculate the
1894  * smallest possible a. Then we consider splits where a is the
1895  * uppper bound of an entry, and for each a, calculate the greatest
1896  * possible b.
1897  *
1898  * In the above example, the first loop would consider splits:
1899  * b=0: (0,1)-(0,4)
1900  * b=1: (0,1)-(1,4)
1901  * b=2: (0,3)-(2,4)
1902  *
1903  * And the second loop:
1904  * a=1: (0,1)-(1,4)
1905  * a=3: (0,3)-(2,4)
1906  * a=4: (0,4)-(2,4)
1907  */
1908 
1909  /*
1910  * Iterate over lower bound of right group, finding smallest possible
1911  * upper bound of left group.
1912  */
1913  i1 = 0;
1914  i2 = 0;
1915  rightLower = intervalsLower[i1].lower;
1916  leftUpper = intervalsUpper[i2].lower;
1917  while (true)
1918  {
1919  /*
1920  * Find next lower bound of right group.
1921  */
1922  while (i1 < nentries && (rightLower == intervalsLower[i1].lower ||
1923  isnan(intervalsLower[i1].lower)))
1924  {
1925  leftUpper = Max(leftUpper, intervalsLower[i1].upper);
1926  i1++;
1927  }
1928  if (i1 >= nentries)
1929  break;
1930  rightLower = intervalsLower[i1].lower;
1931 
1932  /*
1933  * Find count of intervals which anyway should be placed to the
1934  * left group.
1935  */
1936  while (i2 < nentries && intervalsUpper[i2].upper <= leftUpper)
1937  i2++;
1938 
1939  /*
1940  * Consider found split.
1941  */
1942  g_box_consider_split(&context, dim, rightLower, i1, leftUpper, i2);
1943  }
1944 
1945  /*
1946  * Iterate over upper bound of left group finding greates possible
1947  * lower bound of right group.
1948  */
1949  i1 = nentries - 1;
1950  i2 = nentries - 1;
1951  rightLower = intervalsLower[i1].upper;
1952  leftUpper = intervalsUpper[i2].upper;
1953  while (true)
1954  {
1955  /*
1956  * Find next upper bound of left group.
1957  */
1958  while (i2 >= 0 && (leftUpper == intervalsUpper[i2].upper ||
1959  isnan(intervalsUpper[i2].upper)))
1960  {
1961  rightLower = Min(rightLower, intervalsUpper[i2].lower);
1962  i2--;
1963  }
1964  if (i2 < 0)
1965  break;
1966  leftUpper = intervalsUpper[i2].upper;
1967 
1968  /*
1969  * Find count of intervals which anyway should be placed to the
1970  * right group.
1971  */
1972  while (i1 >= 0 && intervalsLower[i1].lower >= rightLower)
1973  i1--;
1974 
1975  /*
1976  * Consider found split.
1977  */
1978  g_box_consider_split(&context, dim,
1979  rightLower, i1 + 1, leftUpper, i2 + 1);
1980  }
1981  }
1982 
1983  /*
1984  * If we failed to find any acceptable splits, use trivial split.
1985  */
1986  if (context.first)
1987  {
1988  POSTGIS_DEBUG(4, "no acceptable splits, trivial split");
1989  fallbackSplit(entryvec, v);
1990  PG_RETURN_POINTER(v);
1991  }
1992 
1993  /*
1994  * Ok, we have now selected the split across one axis.
1995  *
1996  * While considering the splits, we already determined that there will be
1997  * enough entries in both groups to reach the desired ratio, but we did
1998  * not memorize which entries go to which group. So determine that now.
1999  */
2000 
2001  POSTGIS_DEBUGF(4, "split direction: %d", context.dim);
2002 
2003  /* Allocate vectors for results */
2004  v->spl_left = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
2005  v->spl_right = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
2006  v->spl_nleft = 0;
2007  v->spl_nright = 0;
2008 
2009  /* Allocate bounding boxes of left and right groups */
2010  leftBox = palloc0(sizeof(BOX2DF));
2011  rightBox = palloc0(sizeof(BOX2DF));
2012 
2013  /*
2014  * Allocate an array for "common entries" - entries which can be placed to
2015  * either group without affecting overlap along selected axis.
2016  */
2017  commonEntriesCount = 0;
2018  commonEntries = (CommonEntry *) palloc(nentries * sizeof(CommonEntry));
2019 
2020  /* Helper macros to place an entry in the left or right group */
2021 #define PLACE_LEFT(box, off) \
2022  do { \
2023  if (v->spl_nleft > 0) \
2024  adjustBox(leftBox, box); \
2025  else \
2026  *leftBox = *(box); \
2027  v->spl_left[v->spl_nleft++] = off; \
2028  } while(0)
2029 
2030 #define PLACE_RIGHT(box, off) \
2031  do { \
2032  if (v->spl_nright > 0) \
2033  adjustBox(rightBox, box); \
2034  else \
2035  *rightBox = *(box); \
2036  v->spl_right[v->spl_nright++] = off; \
2037  } while(0)
2038 
2039  /*
2040  * Distribute entries which can be distributed unambiguously, and collect
2041  * common entries.
2042  */
2043  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
2044  {
2045  float lower,
2046  upper;
2047 
2048  /*
2049  * Get upper and lower bounds along selected axis.
2050  */
2051  box = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
2052  if (context.dim == 0)
2053  {
2054  lower = box->xmin;
2055  upper = box->xmax;
2056  }
2057  else
2058  {
2059  lower = box->ymin;
2060  upper = box->ymax;
2061  }
2062 
2063  if (upper <= context.leftUpper || isnan(upper))
2064  {
2065  /* Fits to the left group */
2066  if (lower >= context.rightLower || isnan(lower))
2067  {
2068  /* Fits also to the right group, so "common entry" */
2069  commonEntries[commonEntriesCount++].index = i;
2070  }
2071  else
2072  {
2073  /* Doesn't fit to the right group, so join to the left group */
2074  PLACE_LEFT(box, i);
2075  }
2076  }
2077  else
2078  {
2079  /*
2080  * Each entry should fit on either left or right group. Since this
2081  * entry didn't fit on the left group, it better fit in the right
2082  * group.
2083  */
2084  Assert(lower >= context.rightLower);
2085 
2086  /* Doesn't fit to the left group, so join to the right group */
2087  PLACE_RIGHT(box, i);
2088  }
2089  }
2090 
2091  POSTGIS_DEBUGF(4, "leftBox is %s", box2df_to_string(leftBox));
2092  POSTGIS_DEBUGF(4, "rightBox is %s", box2df_to_string(rightBox));
2093 
2094  /*
2095  * Distribute "common entries", if any.
2096  */
2097  if (commonEntriesCount > 0)
2098  {
2099  /*
2100  * Calculate minimum number of entries that must be placed in both
2101  * groups, to reach LIMIT_RATIO.
2102  */
2103  int m = ceil(LIMIT_RATIO * (double) nentries);
2104 
2105  /*
2106  * Calculate delta between penalties of join "common entries" to
2107  * different groups.
2108  */
2109  for (i = 0; i < commonEntriesCount; i++)
2110  {
2111  box = (BOX2DF *) DatumGetPointer(entryvec->vector[
2112  commonEntries[i].index].key);
2113  commonEntries[i].delta = Abs(box_penalty(leftBox, box) -
2114  box_penalty(rightBox, box));
2115  }
2116 
2117  /*
2118  * Sort "common entries" by calculated deltas in order to distribute
2119  * the most ambiguous entries first.
2120  */
2121  qsort(commonEntries, commonEntriesCount, sizeof(CommonEntry), common_entry_cmp);
2122 
2123  /*
2124  * Distribute "common entries" between groups.
2125  */
2126  for (i = 0; i < commonEntriesCount; i++)
2127  {
2128  box = (BOX2DF *) DatumGetPointer(entryvec->vector[
2129  commonEntries[i].index].key);
2130 
2131  /*
2132  * Check if we have to place this entry in either group to achieve
2133  * LIMIT_RATIO.
2134  */
2135  if (v->spl_nleft + (commonEntriesCount - i) <= m)
2136  PLACE_LEFT(box, commonEntries[i].index);
2137  else if (v->spl_nright + (commonEntriesCount - i) <= m)
2138  PLACE_RIGHT(box, commonEntries[i].index);
2139  else
2140  {
2141  /* Otherwise select the group by minimal penalty */
2142  if (box_penalty(leftBox, box) < box_penalty(rightBox, box))
2143  PLACE_LEFT(box, commonEntries[i].index);
2144  else
2145  PLACE_RIGHT(box, commonEntries[i].index);
2146  }
2147  }
2148  }
2149  v->spl_ldatum = PointerGetDatum(leftBox);
2150  v->spl_rdatum = PointerGetDatum(rightBox);
2151 
2152  POSTGIS_DEBUG(4, "[GIST] 'picksplit' completed");
2153 
2154  PG_RETURN_POINTER(v);
2155 }
2156 
2157 #else /* !KOROTOV_SPLIT */
2158 
2159 typedef struct
2160 {
2161  BOX2DF *key;
2162  int pos;
2163 }
2164 KBsort;
2165 
2166 static int
2167 compare_KB(const void* a, const void* b)
2168 {
2169  BOX2DF *abox = ((KBsort*)a)->key;
2170  BOX2DF *bbox = ((KBsort*)b)->key;
2171  float sa = (abox->xmax - abox->xmin) * (abox->ymax - abox->ymin);
2172  float sb = (bbox->xmax - bbox->xmin) * (bbox->ymax - bbox->ymin);
2173 
2174  if ( sa==sb ) return 0;
2175  return ( sa>sb ) ? 1 : -1;
2176 }
2177 
2184 Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS)
2185 {
2186  GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
2187 
2188  GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
2189  OffsetNumber i;
2190  OffsetNumber *listL, *listR, *listB, *listT;
2191  BOX2DF *unionL, *unionR, *unionB, *unionT;
2192  int posL, posR, posB, posT;
2193  BOX2DF pageunion;
2194  BOX2DF *cur;
2195  char direction = ' ';
2196  bool allisequal = true;
2197  OffsetNumber maxoff;
2198  int nbytes;
2199 
2200  POSTGIS_DEBUG(3, "[GIST] 'picksplit' entered");
2201 
2202  posL = posR = posB = posT = 0;
2203 
2204  maxoff = entryvec->n - 1;
2205  cur = (BOX2DF*) DatumGetPointer(entryvec->vector[FirstOffsetNumber].key);
2206 
2207  memcpy((void *) &pageunion, (void *) cur, sizeof(BOX2DF));
2208 
2209  /* find MBR */
2210  for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i))
2211  {
2212  cur = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
2213 
2214  if ( allisequal == true && (
2215  pageunion.xmax != cur->xmax ||
2216  pageunion.ymax != cur->ymax ||
2217  pageunion.xmin != cur->xmin ||
2218  pageunion.ymin != cur->ymin
2219  ) )
2220  allisequal = false;
2221 
2222  if (pageunion.xmax < cur->xmax)
2223  pageunion.xmax = cur->xmax;
2224  if (pageunion.xmin > cur->xmin)
2225  pageunion.xmin = cur->xmin;
2226  if (pageunion.ymax < cur->ymax)
2227  pageunion.ymax = cur->ymax;
2228  if (pageunion.ymin > cur->ymin)
2229  pageunion.ymin = cur->ymin;
2230  }
2231 
2232  POSTGIS_DEBUGF(4, "pageunion is %s", box2df_to_string(&pageunion));
2233 
2234  nbytes = (maxoff + 2) * sizeof(OffsetNumber);
2235  listL = (OffsetNumber *) palloc(nbytes);
2236  listR = (OffsetNumber *) palloc(nbytes);
2237  unionL = (BOX2DF *) palloc(sizeof(BOX2DF));
2238  unionR = (BOX2DF *) palloc(sizeof(BOX2DF));
2239 
2240  if (allisequal)
2241  {
2242  POSTGIS_DEBUG(4, " AllIsEqual!");
2243 
2244  cur = (BOX2DF*) DatumGetPointer(entryvec->vector[OffsetNumberNext(FirstOffsetNumber)].key);
2245 
2246 
2247  if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX2DF)) == 0)
2248  {
2249  v->spl_left = listL;
2250  v->spl_right = listR;
2251  v->spl_nleft = v->spl_nright = 0;
2252  memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX2DF));
2253  memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX2DF));
2254 
2255  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
2256  {
2257  if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
2258  {
2259  v->spl_left[v->spl_nleft] = i;
2260  v->spl_nleft++;
2261  }
2262  else
2263  {
2264  v->spl_right[v->spl_nright] = i;
2265  v->spl_nright++;
2266  }
2267  }
2268  v->spl_ldatum = PointerGetDatum(unionL);
2269  v->spl_rdatum = PointerGetDatum(unionR);
2270 
2271  PG_RETURN_POINTER(v);
2272  }
2273  }
2274 
2275  listB = (OffsetNumber *) palloc(nbytes);
2276  listT = (OffsetNumber *) palloc(nbytes);
2277  unionB = (BOX2DF *) palloc(sizeof(BOX2DF));
2278  unionT = (BOX2DF *) palloc(sizeof(BOX2DF));
2279 
2280 #define ADDLIST( list, unionD, pos, num ) do { \
2281  if ( pos ) { \
2282  if ( unionD->xmax < cur->xmax ) unionD->xmax = cur->xmax; \
2283  if ( unionD->xmin > cur->xmin ) unionD->xmin = cur->xmin; \
2284  if ( unionD->ymax < cur->ymax ) unionD->ymax = cur->ymax; \
2285  if ( unionD->ymin > cur->ymin ) unionD->ymin = cur->ymin; \
2286  } else { \
2287  memcpy( (void*)unionD, (void*) cur, sizeof( BOX2DF ) ); \
2288  } \
2289  list[pos] = num; \
2290  (pos)++; \
2291 } while(0)
2292 
2293  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
2294  {
2295  cur = (BOX2DF*) DatumGetPointer(entryvec->vector[i].key);
2296 
2297  if (cur->xmin - pageunion.xmin < pageunion.xmax - cur->xmax)
2298  ADDLIST(listL, unionL, posL,i);
2299  else
2300  ADDLIST(listR, unionR, posR,i);
2301  if (cur->ymin - pageunion.ymin < pageunion.ymax - cur->ymax)
2302  ADDLIST(listB, unionB, posB,i);
2303  else
2304  ADDLIST(listT, unionT, posT,i);
2305  }
2306 
2307  POSTGIS_DEBUGF(4, "unionL is %s", box2df_to_string(unionL));
2308  POSTGIS_DEBUGF(4, "unionR is %s", box2df_to_string(unionR));
2309  POSTGIS_DEBUGF(4, "unionT is %s", box2df_to_string(unionT));
2310  POSTGIS_DEBUGF(4, "unionB is %s", box2df_to_string(unionB));
2311 
2312  /* bad disposition, sort by ascending and resplit */
2313  if ( (posR==0 || posL==0) && (posT==0 || posB==0) )
2314  {
2315  KBsort *arr = (KBsort*)palloc( sizeof(KBsort) * maxoff );
2316  posL = posR = posB = posT = 0;
2317  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
2318  {
2319  arr[i-1].key = (BOX2DF*) DatumGetPointer(entryvec->vector[i].key);
2320  arr[i-1].pos = i;
2321  }
2322  qsort( arr, maxoff, sizeof(KBsort), compare_KB );
2323  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
2324  {
2325  cur = arr[i-1].key;
2326  if (cur->xmin - pageunion.xmin < pageunion.xmax - cur->xmax)
2327  ADDLIST(listL, unionL, posL,arr[i-1].pos);
2328  else if ( cur->xmin - pageunion.xmin == pageunion.xmax - cur->xmax )
2329  {
2330  if ( posL>posR )
2331  ADDLIST(listR, unionR, posR,arr[i-1].pos);
2332  else
2333  ADDLIST(listL, unionL, posL,arr[i-1].pos);
2334  }
2335  else
2336  ADDLIST(listR, unionR, posR,arr[i-1].pos);
2337 
2338  if (cur->ymin - pageunion.ymin < pageunion.ymax - cur->ymax)
2339  ADDLIST(listB, unionB, posB,arr[i-1].pos);
2340  else if ( cur->ymin - pageunion.ymin == pageunion.ymax - cur->ymax )
2341  {
2342  if ( posB>posT )
2343  ADDLIST(listT, unionT, posT,arr[i-1].pos);
2344  else
2345  ADDLIST(listB, unionB, posB,arr[i-1].pos);
2346  }
2347  else
2348  ADDLIST(listT, unionT, posT,arr[i-1].pos);
2349  }
2350  pfree(arr);
2351  }
2352 
2353  /* which split more optimal? */
2354  if (Max(posL, posR) < Max(posB, posT))
2355  direction = 'x';
2356  else if (Max(posL, posR) > Max(posB, posT))
2357  direction = 'y';
2358  else
2359  {
2360  float sizeLR, sizeBT;
2361  BOX2DF interLR, interBT;
2362 
2363  if ( box2df_intersection(unionL, unionR, &interLR) == FALSE )
2364  sizeLR = 0.0;
2365  else
2366  sizeLR = box2df_size(&interLR);
2367 
2368  if ( box2df_intersection(unionB, unionT, &interBT) == FALSE )
2369  sizeBT = 0.0;
2370  else
2371  sizeBT = box2df_size(&interBT);
2372 
2373  if (sizeLR < sizeBT)
2374  direction = 'x';
2375  else
2376  direction = 'y';
2377  }
2378 
2379  POSTGIS_DEBUGF(4, "split direction '%c'", direction);
2380 
2381  if (direction == 'x')
2382  {
2383  pfree(unionB);
2384  pfree(listB);
2385  pfree(unionT);
2386  pfree(listT);
2387 
2388  v->spl_left = listL;
2389  v->spl_right = listR;
2390  v->spl_nleft = posL;
2391  v->spl_nright = posR;
2392  v->spl_ldatum = PointerGetDatum(unionL);
2393  v->spl_rdatum = PointerGetDatum(unionR);
2394  }
2395  else
2396  {
2397  pfree(unionR);
2398  pfree(listR);
2399  pfree(unionL);
2400  pfree(listL);
2401 
2402  v->spl_left = listB;
2403  v->spl_right = listT;
2404  v->spl_nleft = posB;
2405  v->spl_nright = posT;
2406  v->spl_ldatum = PointerGetDatum(unionB);
2407  v->spl_rdatum = PointerGetDatum(unionT);
2408  }
2409 
2410  POSTGIS_DEBUG(4, "[GIST] 'picksplit' completed");
2411 
2412  PG_RETURN_POINTER(v);
2413 }
2414 
2415 #endif
2416 
2417 
2418 /*
2419 ** The BOX32DF key must be defined as a PostgreSQL type, even though it is only
2420 ** ever used internally. These no-op stubs are used to bind the type.
2421 */
2423 Datum box2df_in(PG_FUNCTION_ARGS)
2424 {
2425  ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2426  errmsg("function box2df_in not implemented")));
2427  PG_RETURN_POINTER(NULL);
2428 }
2429 
2431 Datum box2df_out(PG_FUNCTION_ARGS)
2432 {
2433  BOX2DF *box = (BOX2DF *) PG_GETARG_POINTER(0);
2434  char *result = box2df_to_string(box);
2435  PG_RETURN_CSTRING(result);
2436 }
int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *box)
Read the bounding box off a serialization and calculate one if it is not already there.
Definition: g_serialized.c:642
static float non_negative(float val)
Datum gserialized_above_2d(PG_FUNCTION_ARGS)
Datum gserialized_overabove_2d(PG_FUNCTION_ARGS)
static float box2df_union_edge(const BOX2DF *a, const BOX2DF *b)
static void box2df_validate(BOX2DF *b)
uint8_t data[1]
Definition: liblwgeom.h:384
#define LIMIT_RATIO
Datum gserialized_gist_penalty_2d(PG_FUNCTION_ARGS)
static BOX2DF * box2df_copy(BOX2DF *b)
static bool box2df_overlaps(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS)
Datum gserialized_contains_box2df_box2df_2d(PG_FUNCTION_ARGS)
static bool box2df_left(const BOX2DF *a, const BOX2DF *b)
double xmax
Definition: liblwgeom.h:293
static void fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
#define LW_SUCCESS
Definition: liblwgeom.h:80
static bool box2df_below(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_left_2d(PG_FUNCTION_ARGS)
static bool gserialized_gist_consistent_leaf_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
static void g_box_consider_split(ConsiderSplitContext *context, int dimNum, float rightLower, int minLeftCount, float leftUpper, int maxLeftCount)
static int interval_cmp_lower(const void *i1, const void *i2)
static void adjustBox(BOX2DF *b, BOX2DF *addon)
static bool box2df_right(const BOX2DF *a, const BOX2DF *b)
float next_float_down(double d)
Definition: lwgeom_api.c:51
Datum gserialized_contains_box2df_geom_2d(PG_FUNCTION_ARGS)
static bool box2df_above(const BOX2DF *a, const BOX2DF *b)
static float box2df_size(const BOX2DF *a)
bool(* box2df_predicate)(const BOX2DF *a, const BOX2DF *b)
static bool box2df_within(const BOX2DF *a, const BOX2DF *b)
#define LW_FAILURE
Definition: liblwgeom.h:79
float next_float_up(double d)
Definition: lwgeom_api.c:72
static int box2df_from_gbox_p(GBOX *box, BOX2DF *a)
double ymin
Definition: liblwgeom.h:294
Datum gserialized_overlaps_2d(PG_FUNCTION_ARGS)
Datum gserialized_distance_centroid_2d(PG_FUNCTION_ARGS)
Datum gserialized_within_box2df_geom_2d(PG_FUNCTION_ARGS)
Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS)
void gbox_init(GBOX *gbox)
Zero out all the entries in the GBOX.
Definition: g_box.c:51
Datum gserialized_within_box2df_box2df_2d(PG_FUNCTION_ARGS)
double xmin
Definition: liblwgeom.h:292
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.
#define FLAGS_GET_BBOX(flags)
Definition: liblwgeom.h:142
static char * box2df_to_string(const BOX2DF *a)
#define LW_FALSE
Definition: liblwgeom.h:77
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:76
Datum gserialized_below_2d(PG_FUNCTION_ARGS)
Datum gserialized_gist_same_2d(PG_FUNCTION_ARGS)
static void box2df_merge(BOX2DF *b_union, BOX2DF *b_new)
Datum gserialized_within_2d(PG_FUNCTION_ARGS)
int gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df)
Peak into a GSERIALIZED datum to find the bounding box.
Datum gserialized_overleft_2d(PG_FUNCTION_ARGS)
Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS)
Datum box2df_out(PG_FUNCTION_ARGS)
double ymax
Definition: liblwgeom.h:295
Datum gserialized_overlaps_box2df_box2df_2d(PG_FUNCTION_ARGS)
static float pack_float(const float value, const int realm)
Datum gserialized_same_2d(PG_FUNCTION_ARGS)
static int interval_cmp_upper(const void *i1, const void *i2)
static double pt_distance(double ax, double ay, double bx, double by)
Datum distance(PG_FUNCTION_ARGS)
static bool box2df_overright(const BOX2DF *a, const BOX2DF *b)
static int common_entry_cmp(const void *i1, const void *i2)
Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS)
static bool box2df_overabove(const BOX2DF *a, const BOX2DF *b)
static bool box2df_overbelow(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_gist_decompress_2d(PG_FUNCTION_ARGS)
static bool box2df_overleft(const BOX2DF *a, const BOX2DF *b)
#define FALSE
Definition: dbfopen.c:168
Datum gserialized_overright_2d(PG_FUNCTION_ARGS)
bool box2df_contains(const BOX2DF *a, const BOX2DF *b)
#define PLACE_RIGHT(box, off)
static float box2df_union_size(const BOX2DF *a, const BOX2DF *b)
static int gserialized_datum_predicate_2d(Datum gs1, Datum gs2, box2df_predicate predicate)
Support function.
int value
Definition: genraster.py:61
static int gserialized_datum_predicate_box2df_geom_2d(const BOX2DF *br1, Datum gs2, box2df_predicate predicate)
Datum gserialized_overlaps_box2df_geom_2d(PG_FUNCTION_ARGS)
static bool box2df_equals(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_right_2d(PG_FUNCTION_ARGS)
unsigned char uint8_t
Definition: uthash.h:79
Datum box2df_in(PG_FUNCTION_ARGS)
#define TRUE
Definition: dbfopen.c:169
Datum gserialized_contains_2d(PG_FUNCTION_ARGS)
Datum gserialized_gist_union_2d(PG_FUNCTION_ARGS)
Datum gserialized_gist_distance_2d(PG_FUNCTION_ARGS)
#define PLACE_LEFT(box, off)
static bool gserialized_gist_consistent_internal_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
static float box_penalty(BOX2DF *original, BOX2DF *new)
Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(gserialized_contains_box2df_geom_2d)
This library is the generic geometry handling section of PostGIS.
uint8_t flags
Definition: liblwgeom.h:383
static float box2df_edge(const BOX2DF *a)