PostGIS  2.5.7dev-r@@SVN_REVISION@@
gserialized_gist_nd.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 */
39 
40 #include "postgres.h"
41 #include "access/gist.h" /* For GiST */
42 #include "access/itup.h"
43 #include "access/skey.h"
44 
45 #include "../postgis_config.h"
46 
47 /*#define POSTGIS_DEBUG_LEVEL 4*/
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 #include "geography.h"
53 
54 #include <assert.h>
55 
56 
57 /* Fall back to older finite() if necessary */
58 #ifndef HAVE_ISFINITE
59 # ifdef HAVE_GNU_ISFINITE
60 # define _GNU_SOURCE
61 # else
62 # define isfinite finite
63 # endif
64 #endif
65 
66 /*
67 ** When is a node split not so good? If more than 90% of the entries
68 ** end up in one of the children.
69 */
70 #define LIMIT_RATIO 0.1
71 
72 /*
73 ** For debugging
74 */
75 #if POSTGIS_DEBUG_LEVEL > 0
76 static int geog_counter_leaf = 0;
77 static int geog_counter_internal = 0;
78 #endif
79 
80 /*
81 ** ND Index key type stub prototypes
82 */
83 Datum gidx_out(PG_FUNCTION_ARGS);
84 Datum gidx_in(PG_FUNCTION_ARGS);
85 
86 /*
87 ** ND GiST prototypes
88 */
89 Datum gserialized_gist_consistent(PG_FUNCTION_ARGS);
90 Datum gserialized_gist_compress(PG_FUNCTION_ARGS);
91 Datum gserialized_gist_decompress(PG_FUNCTION_ARGS);
92 Datum gserialized_gist_penalty(PG_FUNCTION_ARGS);
93 Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS);
94 Datum gserialized_gist_union(PG_FUNCTION_ARGS);
95 Datum gserialized_gist_same(PG_FUNCTION_ARGS);
96 Datum gserialized_gist_distance(PG_FUNCTION_ARGS);
97 Datum gserialized_gist_geog_distance(PG_FUNCTION_ARGS);
98 
99 /*
100 ** ND Operator prototypes
101 */
102 Datum gserialized_overlaps(PG_FUNCTION_ARGS);
103 Datum gserialized_contains(PG_FUNCTION_ARGS);
104 #if POSTGIS_PGSQL_VERSION > 94
105 Datum gserialized_gidx_geom_contains(PG_FUNCTION_ARGS);
106 Datum gserialized_gidx_gidx_contains(PG_FUNCTION_ARGS);
107 #endif
108 Datum gserialized_within(PG_FUNCTION_ARGS);
109 #if POSTGIS_PGSQL_VERSION > 94
110 Datum gserialized_gidx_geom_within(PG_FUNCTION_ARGS);
111 Datum gserialized_gidx_gidx_within(PG_FUNCTION_ARGS);
112 #endif
113 Datum gserialized_distance_nd(PG_FUNCTION_ARGS);
114 #if POSTGIS_PGSQL_VERSION > 94
115 Datum gserialized_gidx_geom_same(PG_FUNCTION_ARGS);
116 Datum gserialized_gidx_gidx_same(PG_FUNCTION_ARGS);
117 #endif
118 
119 /*
120 ** GIDX true/false test function type
121 */
122 typedef bool (*gidx_predicate)(GIDX *a, GIDX *b);
123 
124 
125 /* Allocate a new copy of GIDX */
126 GIDX* gidx_copy(GIDX *b)
127 {
128  GIDX *c = (GIDX*)palloc(VARSIZE(b));
129  POSTGIS_DEBUGF(5, "copied gidx (%p) to gidx (%p)", b, c);
130  memcpy((void*)c, (void*)b, VARSIZE(b));
131  return c;
132 }
133 
134 
135 /* Ensure all minimums are below maximums. */
136 void
138 {
139  uint32_t i;
140  Assert(b);
141  POSTGIS_DEBUGF(5, "validating gidx (%s)", gidx_to_string(b));
142  for (i = 0; i < GIDX_NDIMS(b); i++)
143  {
144  if (GIDX_GET_MIN(b, i) > GIDX_GET_MAX(b, i))
145  {
146  float tmp = GIDX_GET_MIN(b, i);
147  GIDX_SET_MIN(b, i, GIDX_GET_MAX(b, i));
148  GIDX_SET_MAX(b, i, tmp);
149  }
150  }
151  return;
152 }
153 
154 /* An "unknown" GIDX is used to represent the bounds of an EMPTY
155  geometry or other-wise unindexable geometry (like one with NaN
156  or Inf bounds) */
157 inline bool gidx_is_unknown(const GIDX *a)
158 {
159  size_t size = VARSIZE(a) - VARHDRSZ;
160  /* "unknown" gidx objects have a too-small size of one float */
161  if ( size <= 0.0 )
162  return true;
163  return false;
164 }
165 
166 static inline void gidx_set_unknown(GIDX *a)
167 {
168  SET_VARSIZE(a, VARHDRSZ);
169 }
170 
171 /* Enlarge b_union to contain b_new. If b_new contains more
172  dimensions than b_union, expand b_union to contain those dimensions. */
173 void gidx_merge(GIDX **b_union, GIDX *b_new)
174 {
175  int i, dims_union, dims_new;
176  Assert(b_union);
177  Assert(*b_union);
178  Assert(b_new);
179 
180  /* Can't merge an unknown into any thing */
181  if( gidx_is_unknown(b_new) )
182  return;
183 
184  /* Merge of unknown and known is known */
185  if( gidx_is_unknown(*b_union) )
186  {
187  pfree(*b_union);
188  *b_union = gidx_copy(b_new);
189  return;
190  }
191 
192  dims_union = GIDX_NDIMS(*b_union);
193  dims_new = GIDX_NDIMS(b_new);
194 
195  POSTGIS_DEBUGF(4, "merging gidx (%s) into gidx (%s)", gidx_to_string(b_new), gidx_to_string(*b_union));
196 
197  if ( dims_new < dims_union )
198  {
199  POSTGIS_DEBUGF(5, "reallocating b_union from %d dims to %d dims", dims_union, dims_new);
200  *b_union = (GIDX*)repalloc(*b_union, GIDX_SIZE(dims_new));
201  SET_VARSIZE(*b_union, VARSIZE(b_new));
202  dims_union = dims_new;
203  }
204 
205  for ( i = 0; i < dims_union; i++ )
206  {
207  /* Adjust minimums */
208  GIDX_SET_MIN(*b_union, i, Min(GIDX_GET_MIN(*b_union,i),GIDX_GET_MIN(b_new,i)));
209  /* Adjust maximums */
210  GIDX_SET_MAX(*b_union, i, Max(GIDX_GET_MAX(*b_union,i),GIDX_GET_MAX(b_new,i)));
211  }
212 
213  POSTGIS_DEBUGF(5, "merge complete (%s)", gidx_to_string(*b_union));
214  return;
215 }
216 
217 /* Calculate the volume (in n-d units) of the GIDX */
218 static float gidx_volume(GIDX *a)
219 {
220  float result;
221  uint32_t i;
222  if ( a == NULL || gidx_is_unknown(a) )
223  {
224  /* elog(ERROR, "gidx_volume received a null argument"); */
225  return 0.0;
226  }
227  result = GIDX_GET_MAX(a,0) - GIDX_GET_MIN(a,0);
228  for ( i = 1; i < GIDX_NDIMS(a); i++ )
229  result *= (GIDX_GET_MAX(a,i) - GIDX_GET_MIN(a,i));
230  POSTGIS_DEBUGF(5, "calculated volume of %s as %.8g", gidx_to_string(a), result);
231  return result;
232 }
233 
234 /* Calculate the edge of the GIDX */
235 static float gidx_edge(GIDX *a)
236 {
237  float result;
238  uint32_t i;
239  if ( a == NULL || gidx_is_unknown(a) )
240  {
241  return 0.0;
242  }
243  result = GIDX_GET_MAX(a,0) - GIDX_GET_MIN(a,0);
244  for ( i = 1; i < GIDX_NDIMS(a); i++ )
245  result += (GIDX_GET_MAX(a,i) - GIDX_GET_MIN(a,i));
246  POSTGIS_DEBUGF(5, "calculated edge of %s as %.8g", gidx_to_string(a), result);
247  return result;
248 }
249 
250 /* Ensure the first argument has the higher dimensionality. */
251 static void gidx_dimensionality_check(GIDX **a, GIDX **b)
252 {
253  if ( GIDX_NDIMS(*a) < GIDX_NDIMS(*b) )
254  {
255  GIDX *tmp = *b;
256  *b = *a;
257  *a = tmp;
258  }
259 }
260 
261 /* Calculate the volume of the union of the boxes. Avoids creating an intermediate box. */
262 static float gidx_union_volume(GIDX *a, GIDX *b)
263 {
264  float result;
265  int i;
266  int ndims_a, ndims_b;
267 
268  POSTGIS_DEBUG(5,"entered function");
269 
270  if ( a == NULL && b == NULL )
271  {
272  elog(ERROR, "gidx_union_volume received two null arguments");
273  return 0.0;
274  }
275 
276  if ( a == NULL || gidx_is_unknown(a) )
277  return gidx_volume(b);
278 
279  if ( b == NULL || gidx_is_unknown(b) )
280  return gidx_volume(a);
281 
282  if ( gidx_is_unknown(a) && gidx_is_unknown(b) )
283  {
284  return 0.0;
285  }
286 
287  /* Ensure 'a' has the most dimensions. */
289 
290  ndims_a = GIDX_NDIMS(a);
291  ndims_b = GIDX_NDIMS(b);
292 
293  /* Initialize with maximal length of first dimension. */
294  result = Max(GIDX_GET_MAX(a,0),GIDX_GET_MAX(b,0)) - Min(GIDX_GET_MIN(a,0),GIDX_GET_MIN(b,0));
295 
296  /* Multiply by maximal length of remaining dimensions. */
297  for ( i = 1; i < ndims_b; i++ )
298  {
299  result *= (Max(GIDX_GET_MAX(a,i),GIDX_GET_MAX(b,i)) - Min(GIDX_GET_MIN(a,i),GIDX_GET_MIN(b,i)));
300  }
301 
302  /* Add in dimensions of higher dimensional box. */
303  for ( i = ndims_b; i < ndims_a; i++ )
304  {
305  result *= (GIDX_GET_MAX(a,i) - GIDX_GET_MIN(a,i));
306  }
307 
308  POSTGIS_DEBUGF(5, "volume( %s union %s ) = %.12g", gidx_to_string(a), gidx_to_string(b), result);
309 
310  return result;
311 }
312 
313 /* Calculate the edge of the union of the boxes. Avoids creating an intermediate box. */
314 static float gidx_union_edge(GIDX *a, GIDX *b)
315 {
316  float result;
317  int i;
318  int ndims_a, ndims_b;
319 
320  POSTGIS_DEBUG(5,"entered function");
321 
322  if ( a == NULL && b == NULL )
323  {
324  elog(ERROR, "gidx_union_edge received two null arguments");
325  return 0.0;
326  }
327 
328  if ( a == NULL || gidx_is_unknown(a) )
329  return gidx_volume(b);
330 
331  if ( b == NULL || gidx_is_unknown(b) )
332  return gidx_volume(a);
333 
334  if ( gidx_is_unknown(a) && gidx_is_unknown(b) )
335  {
336  return 0.0;
337  }
338 
339  /* Ensure 'a' has the most dimensions. */
341 
342  ndims_a = GIDX_NDIMS(a);
343  ndims_b = GIDX_NDIMS(b);
344 
345  /* Initialize with maximal length of first dimension. */
346  result = Max(GIDX_GET_MAX(a,0),GIDX_GET_MAX(b,0)) - Min(GIDX_GET_MIN(a,0),GIDX_GET_MIN(b,0));
347 
348  /* Add maximal length of remaining dimensions. */
349  for ( i = 1; i < ndims_b; i++ )
350  {
351  result += (Max(GIDX_GET_MAX(a,i),GIDX_GET_MAX(b,i)) - Min(GIDX_GET_MIN(a,i),GIDX_GET_MIN(b,i)));
352  }
353 
354  /* Add in dimensions of higher dimensional box. */
355  for ( i = ndims_b; i < ndims_a; i++ )
356  {
357  result += (GIDX_GET_MAX(a,i) - GIDX_GET_MIN(a,i));
358  }
359 
360  POSTGIS_DEBUGF(5, "edge( %s union %s ) = %.12g", gidx_to_string(a), gidx_to_string(b), result);
361 
362  return result;
363 }
364 
365 /* Calculate the volume of the intersection of the boxes. */
366 static float gidx_inter_volume(GIDX *a, GIDX *b)
367 {
368  uint32_t i;
369  float result;
370 
371  POSTGIS_DEBUG(5,"entered function");
372 
373  if ( a == NULL || b == NULL )
374  {
375  elog(ERROR, "gidx_inter_volume received a null argument");
376  return 0.0;
377  }
378 
379  if ( gidx_is_unknown(a) || gidx_is_unknown(b) )
380  {
381  return 0.0;
382  }
383 
384  /* Ensure 'a' has the most dimensions. */
386 
387  /* Initialize with minimal length of first dimension. */
388  result = Min(GIDX_GET_MAX(a,0),GIDX_GET_MAX(b,0)) - Max(GIDX_GET_MIN(a,0),GIDX_GET_MIN(b,0));
389 
390  /* If they are disjoint (max < min) then return zero. */
391  if ( result < 0.0 ) return 0.0;
392 
393  /* Continue for remaining dimensions. */
394  for ( i = 1; i < GIDX_NDIMS(b); i++ )
395  {
396  float width = Min(GIDX_GET_MAX(a,i),GIDX_GET_MAX(b,i)) - Max(GIDX_GET_MIN(a,i),GIDX_GET_MIN(b,i));
397  if ( width < 0.0 ) return 0.0;
398  /* Multiply by minimal length of remaining dimensions. */
399  result *= width;
400  }
401  POSTGIS_DEBUGF(5, "volume( %s intersection %s ) = %.12g", gidx_to_string(a), gidx_to_string(b), result);
402  return result;
403 }
404 
405 /*
406 ** Overlapping GIDX box test.
407 **
408 ** Box(A) Overlaps Box(B) IFF for every dimension d:
409 ** min(A,d) <= max(B,d) && max(A,d) => min(B,d)
410 **
411 ** Any missing dimension is assumed by convention to
412 ** overlap whatever finite range available on the
413 ** other operand. See
414 ** http://lists.osgeo.org/pipermail/postgis-devel/2015-February/024757.html
415 **
416 ** Empty boxes never overlap.
417 */
418 static bool gidx_overlaps(GIDX *a, GIDX *b)
419 {
420  int i;
421  int ndims_b;
422  POSTGIS_DEBUG(5, "entered function");
423 
424  if ( (a == NULL) || (b == NULL) ) return false;
425 
426  if ( gidx_is_unknown(a) || gidx_is_unknown(b) )
427  return false;
428 
429  /* Ensure 'a' has the most dimensions. */
431 
432  ndims_b = GIDX_NDIMS(b);
433 
434  /* compare only up to dimensions of (b), missing dimensions always overlap */
435  for ( i = 0; i < ndims_b; i++ )
436  {
437  if ( GIDX_GET_MIN(a,i) > GIDX_GET_MAX(b,i) )
438  return false;
439  if ( GIDX_GET_MIN(b,i) > GIDX_GET_MAX(a,i) )
440  return false;
441  }
442 
443  return true;
444 }
445 
446 /*
447 ** Containment GIDX test.
448 **
449 ** Box(A) CONTAINS Box(B) IFF (pt(A)LL < pt(B)LL) && (pt(A)UR > pt(B)UR)
450 */
451 bool gidx_contains(GIDX *a, GIDX *b)
452 {
453  int i, dims_a, dims_b;
454 
455  POSTGIS_DEBUG(5, "entered function");
456 
457  if ( (a == NULL) || (b == NULL) ) return false;
458 
459  if ( gidx_is_unknown(a) || gidx_is_unknown(b) )
460  return false;
461 
462  dims_a = GIDX_NDIMS(a);
463  dims_b = GIDX_NDIMS(b);
464 
465  if ( dims_a < dims_b )
466  {
467  /*
468  ** If (b) is of higher dimensionality than (a) it can only be contained
469  ** if those higher dimensions are zeroes.
470  */
471  for (i = dims_a; i < dims_b; i++)
472  {
473  if ( GIDX_GET_MIN(b,i) != 0 )
474  return false;
475  if ( GIDX_GET_MAX(b,i) != 0 )
476  return false;
477  }
478  }
479 
480  /* Excess dimensions of (a), don't matter, it just has to contain (b) in (b)'s dimensions */
481  for (i = 0; i < Min(dims_a, dims_b); i++)
482  {
483  if ( GIDX_GET_MIN(a,i) > GIDX_GET_MIN(b,i) )
484  return false;
485  if ( GIDX_GET_MAX(a,i) < GIDX_GET_MAX(b,i) )
486  return false;
487  }
488 
489  return true;
490 }
491 
492 /*
493 ** Equality GIDX test.
494 **
495 ** Box(A) EQUALS Box(B) IFF (pt(A)LL == pt(B)LL) && (pt(A)UR == pt(B)UR)
496 */
497 static bool gidx_equals(GIDX *a, GIDX *b)
498 {
499  uint32_t i;
500 
501  POSTGIS_DEBUG(5, "entered function");
502 
503  if ( (a == NULL) && (b == NULL) ) return true;
504  if ( (a == NULL) || (b == NULL) ) return false;
505 
506  if ( gidx_is_unknown(a) && gidx_is_unknown(b) )
507  return true;
508 
509  if ( gidx_is_unknown(a) || gidx_is_unknown(b) )
510  return false;
511 
512  /* Ensure 'a' has the most dimensions. */
514 
515  /* For all shared dimensions min(a) == min(b), max(a) == max(b) */
516  for (i = 0; i < GIDX_NDIMS(b); i++)
517  {
518  if ( GIDX_GET_MIN(a,i) != GIDX_GET_MIN(b,i) )
519  return false;
520  if ( GIDX_GET_MAX(a,i) != GIDX_GET_MAX(b,i) )
521  return false;
522  }
523  /* For all unshared dimensions min(a) == 0.0, max(a) == 0.0 */
524  for (i = GIDX_NDIMS(b); i < GIDX_NDIMS(a); i++)
525  {
526  if ( GIDX_GET_MIN(a,i) != 0.0 )
527  return false;
528  if ( GIDX_GET_MAX(a,i) != 0.0 )
529  return false;
530  }
531  return true;
532 }
533 
538 static int
539 gserialized_datum_predicate(Datum gs1, Datum gs2, gidx_predicate predicate)
540 {
541  /* Put aside some stack memory and use it for GIDX pointers. */
542  char boxmem1[GIDX_MAX_SIZE];
543  char boxmem2[GIDX_MAX_SIZE];
544  GIDX *gidx1 = (GIDX*)boxmem1;
545  GIDX *gidx2 = (GIDX*)boxmem2;
546 
547  POSTGIS_DEBUG(3, "entered function");
548 
549  /* Must be able to build box for each arguement (ie, not empty geometry)
550  and predicate function to return true. */
551  if ( (gserialized_datum_get_gidx_p(gs1, gidx1) == LW_SUCCESS) &&
552  (gserialized_datum_get_gidx_p(gs2, gidx2) == LW_SUCCESS) &&
553  predicate(gidx1, gidx2) )
554  {
555  POSTGIS_DEBUGF(3, "got boxes %s and %s", gidx_to_string(gidx1), gidx_to_string(gidx2));
556  return LW_TRUE;
557  }
558  return LW_FALSE;
559 }
560 
561 #if POSTGIS_PGSQL_VERSION > 94
562 static int
563 gserialized_datum_predicate_gidx_geom(GIDX *gidx1, Datum gs2, gidx_predicate predicate)
564 {
565  /* Put aside some stack memory and use it for GIDX pointers. */
566  char boxmem2[GIDX_MAX_SIZE];
567  GIDX *gidx2 = (GIDX*)boxmem2;
568 
569  POSTGIS_DEBUG(3, "entered function");
570 
571  /* Must be able to build box for gs2 arguement (ie, not empty geometry)
572  and predicate function to return true. */
573  if ( (gserialized_datum_get_gidx_p(gs2, gidx2) == LW_SUCCESS) &&
574  predicate(gidx1, gidx2) )
575  {
576  POSTGIS_DEBUGF(3, "got boxes %s and %s", gidx_to_string(gidx1), gidx_to_string(gidx2));
577  return LW_TRUE;
578  }
579  return LW_FALSE;
580 }
581 
582 static int
583 gserialized_datum_predicate_geom_gidx(Datum gs1, GIDX *gidx2, gidx_predicate predicate)
584 {
585  /* Put aside some stack memory and use it for GIDX pointers. */
586  char boxmem2[GIDX_MAX_SIZE];
587  GIDX *gidx1 = (GIDX*)boxmem2;
588 
589  POSTGIS_DEBUG(3, "entered function");
590 
591  /* Must be able to build box for gs2 arguement (ie, not empty geometry)
592  and predicate function to return true. */
593  if ( (gserialized_datum_get_gidx_p(gs1, gidx1) == LW_SUCCESS) &&
594  predicate(gidx1, gidx2) )
595  {
596  POSTGIS_DEBUGF(3, "got boxes %s and %s", gidx_to_string(gidx1), gidx_to_string(gidx2));
597  return LW_TRUE;
598  }
599  return LW_FALSE;
600 }
601 #endif
602 
603 
607 #if POSTGIS_PGSQL_VERSION < 95
608 static double gidx_distance_leaf_centroid(const GIDX *a, const GIDX *b)
609 {
610  int ndims, i;
611  double sum = 0;
612 
613  /* Base computation on least available dimensions */
614  ndims = Min(GIDX_NDIMS(b), GIDX_NDIMS(a));
615  for ( i = 0; i < ndims; ++i )
616  {
617  double ca, cb, d;
618  double amin = GIDX_GET_MIN(a,i);
619  double amax = GIDX_GET_MAX(a,i);
620  double bmin = GIDX_GET_MIN(b,i);
621  double bmax = GIDX_GET_MAX(b,i);
622  ca = amin + ( ( amax - amin ) / 2.0 );
623  cb = bmin + ( ( bmax - bmin ) / 2.0 );
624  d = ca - cb;
625  if ( ! isfinite(d) )
626  {
627  /* Can happen if a dimension was padded with FLT_MAX,
628  * effectively meaning "infinite range". In that case
629  * we take that dimension as adding 0 to the total
630  * distance.
631  */
632  continue;
633  }
634  sum += d * d;
635 /*
636  POSTGIS_DEBUGF(3, " centroid of A for dimension %d is %g", i, ca);
637  POSTGIS_DEBUGF(3, " centroid of B for dimension %d is %g", i, cb);
638  POSTGIS_DEBUGF(3, " distance on dimension %d is %g, squared as %g, grows sum to %g", i, d, d*d, sum);
639 */
640  }
641  return sqrt(sum);
642 }
643 #endif
644 
648 static double gidx_distance(const GIDX *a, const GIDX *b, int m_is_time)
649 {
650  int ndims, i;
651  double sum = 0;
652 
653  /* Base computation on least available dimensions */
654  ndims = Min(GIDX_NDIMS(b), GIDX_NDIMS(a));
655  for ( i = 0; i < ndims; ++i )
656  {
657  double d;
658  double amin = GIDX_GET_MIN(a,i);
659  double amax = GIDX_GET_MAX(a,i);
660  double bmin = GIDX_GET_MIN(b,i);
661  double bmax = GIDX_GET_MAX(b,i);
662  POSTGIS_DEBUGF(3, "A %g - %g", amin, amax);
663  POSTGIS_DEBUGF(3, "B %g - %g", bmin, bmax);
664 
665  if ( ( amin <= bmax && amax >= bmin ) )
666  {
667  /* overlaps */
668  d = 0;
669  }
670  else if ( i == 4 && m_is_time )
671  {
672  return FLT_MAX;
673  }
674  else if ( bmax < amin )
675  {
676  /* is "left" */
677  d = amin - bmax;
678  }
679  else
680  {
681  /* is "right" */
682  assert( bmin > amax );
683  d = bmin - amax;
684  }
685  if ( ! isfinite(d) )
686  {
687  /* Can happen if coordinates are corrupted/NaN */
688  continue;
689  }
690  sum += d * d;
691  POSTGIS_DEBUGF(3, "dist %g, squared %g, grows sum to %g", d, d*d, sum);
692  }
693  return sqrt(sum);
694 }
695 
696 #if POSTGIS_PGSQL_VERSION < 95
697 static double gidx_distance_node_centroid(const GIDX *node, const GIDX *query)
698 {
699  int i;
700  double sum = 0;
701 
702  /* Base computation on least available dimensions */
703  int ndims = Min(GIDX_NDIMS(node), GIDX_NDIMS(query));
704 
705  for ( i = 0; i < ndims; ++i )
706  {
707  double d;
708  double amin = GIDX_GET_MIN(query,i);
709  double amax = GIDX_GET_MAX(query,i);
710  double bmin = GIDX_GET_MIN(node,i);
711  double bmax = GIDX_GET_MAX(node,i);
712  double ca = amin + ( ( amax - amin ) / 2.0 );
713 
714  if ( ( ca <= bmax && ca >= bmin ) )
715  {
716  /* overlaps */
717  d = 0;
718  }
719  else if ( bmax < ca )
720  {
721  /* is "left" */
722  d = ca - bmax;
723  }
724  else
725  {
726  /* is "right" */
727  assert( bmin > ca );
728  d = bmin - ca;
729  }
730  if ( ! isfinite(d) )
731  {
732  /* Can happen if coordinates are corrupted/NaN */
733  continue;
734  }
735  sum += d * d;
736  POSTGIS_DEBUGF(3, "dist %g, squared %g, grows sum to %g", d, d*d, sum);
737  }
738  return sqrt(sum);
739 }
740 #else /* POSTGIS_PGSQL_VERSION >= 95 */
741 static double gidx_distance_m(const GIDX *a, const GIDX *b)
742 {
743  int mdim_a, mdim_b;
744  double d, amin, amax, bmin, bmax;
745 
746  /* Base computation on least available dimensions */
747  mdim_a = GIDX_NDIMS(a) - 1;
748  mdim_b = GIDX_NDIMS(b) - 1;
749 
750  amin = GIDX_GET_MIN(a,mdim_a);
751  amax = GIDX_GET_MAX(a,mdim_a);
752  bmin = GIDX_GET_MIN(b,mdim_b);
753  bmax = GIDX_GET_MAX(b,mdim_b);
754 
755  if ( ( amin <= bmax && amax >= bmin ) )
756  {
757  /* overlaps */
758  d = 0;
759  }
760  else if ( bmax < amin )
761  {
762  /* is "left" */
763  d = amin - bmax;
764  }
765  else
766  {
767  /* is "right" */
768  assert( bmin > amax );
769  d = bmin - amax;
770  }
771 
772  return d;
773 }
774 #endif /* POSTGIS_PGSQL_VERSION >= 96 */
775 
781 {
782  char boxmem[GIDX_MAX_SIZE];
783  GIDX *gidx = (GIDX*)boxmem;
784  float fdistance = (float)distance;
785 
786  /* Get our bounding box out of the geography, return right away if
787  input is an EMPTY geometry. */
788  if ( gserialized_get_gidx_p(g, gidx) == LW_FAILURE )
789  {
790  return g;
791  }
792 
793  gidx_expand(gidx, fdistance);
794 
795  return gserialized_set_gidx(g, gidx);
796 }
797 
798 /***********************************************************************
799 * GiST N-D Index Operator Functions
800 */
801 
802 /*
803 * Do centroid to centroid n-d distance if you don't have
804 * re-check available (PgSQL 9.5+), do "real" n-d distance
805 * if you do
806 */
808 Datum gserialized_distance_nd(PG_FUNCTION_ARGS)
809 {
810  char b1mem[GIDX_MAX_SIZE];
811  GIDX *b1 = (GIDX*)b1mem;
812  char b2mem[GIDX_MAX_SIZE];
813  GIDX *b2 = (GIDX*)b2mem;
814 
815 #if POSTGIS_PGSQL_VERSION < 95
816 
817  /* Centroid-to-centroid distance */
818  Datum gs1 = PG_GETARG_DATUM(0);
819  Datum gs2 = PG_GETARG_DATUM(1);
820  double box_distance = FLT_MAX;
821 
822  /* Must be able to build box for each argument (ie, not empty geometry). */
823  if ( (gserialized_datum_get_gidx_p(gs1, b1) == LW_SUCCESS) &&
824  (gserialized_datum_get_gidx_p(gs2, b2) == LW_SUCCESS) )
825  {
826  box_distance = gidx_distance_leaf_centroid(b1, b2);
827  POSTGIS_DEBUGF(3, "got boxes %s and %s", gidx_to_string(b1), gidx_to_string(b2));
828  }
829  PG_RETURN_FLOAT8(box_distance);
830 
831 #else /* POSTGIS_PGSQL_VERSION >= 96 */
832 
833  /* Feature-to-feature distance */
834  GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
835  GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
836  LWGEOM *lw1 = lwgeom_from_gserialized(geom1);
837  LWGEOM *lw2 = lwgeom_from_gserialized(geom2);
838  LWGEOM *closest;
839  double distance;
840 
841 
842  /* Find an exact shortest line w/ the dimensions we support */
843  if ( lwgeom_has_z(lw1) && lwgeom_has_z(lw2) )
844  {
845  closest = lwgeom_closest_line_3d(lw1, lw2);
846  distance = lwgeom_length(closest);
847  }
848  else
849  {
850  closest = lwgeom_closest_line(lw1, lw2);
851  distance = lwgeom_length_2d(closest);
852  }
853 
854  /* Un-sqrt the distance so we can add extra terms */
856 
857  /* Can only add the M term if both objects have M */
858  if ( lwgeom_has_m(lw1) && lwgeom_has_m(lw2) )
859  {
860  double m1 = 0, m2 = 0;
861  int usebox = false;
862 
863  if ( lwgeom_get_type(lw1) == POINTTYPE )
864  {
865  POINT4D p;
866  lwpoint_getPoint4d_p((LWPOINT*)lw1, &p);
867  m1 = p.m;
868  }
869  else if ( lwgeom_get_type(lw1) == LINETYPE )
870  {
871  LWPOINT *lwp1 = lwline_get_lwpoint(lwgeom_as_lwline(closest), 0);
872  m1 = lwgeom_interpolate_point(lw1, lwp1);
873  lwpoint_free(lwp1);
874  }
875  else
876  {
877  usebox = true;
878  }
879 
880  if ( lwgeom_get_type(lw2) == POINTTYPE )
881  {
882  POINT4D p;
883  lwpoint_getPoint4d_p((LWPOINT*)lw2, &p);
884  m2 = p.m;
885  }
886  else if ( lwgeom_get_type(lw2) == LINETYPE )
887  {
888  LWPOINT *lwp2 = lwline_get_lwpoint(lwgeom_as_lwline(closest), 1);
889  m2 = lwgeom_interpolate_point(lw2, lwp2);
890  lwpoint_free(lwp2);
891  }
892  else
893  {
894  usebox = true;
895  }
896 
897  if ( usebox )
898  {
899  double d;
900  gserialized_get_gidx_p(geom1, b1);
901  gserialized_get_gidx_p(geom2, b2);
902  d = gidx_distance_m(b1, b2);
903  distance += d*d;
904  }
905  else
906  {
907  distance += (m2-m1)*(m2-m1);
908  }
909  }
910 
911  lwgeom_free(closest);
912 
913  PG_FREE_IF_COPY(geom1, 0);
914  PG_FREE_IF_COPY(geom2, 1);
915  PG_RETURN_FLOAT8(sqrt(distance));
916 #endif /* POSTGIS_PGSQL_VERSION >= 96 */
917 }
918 
919 /*
920 ** '~' and operator function. Based on two serialized return true if
921 ** the first is contained by the second.
922 */
924 Datum gserialized_within(PG_FUNCTION_ARGS)
925 {
926  if ( gserialized_datum_predicate(PG_GETARG_DATUM(1), PG_GETARG_DATUM(0), gidx_contains) == LW_TRUE )
927  {
928  PG_RETURN_BOOL(true);
929  }
930 
931  PG_RETURN_BOOL(false);
932 }
933 
934 #if POSTGIS_PGSQL_VERSION > 94
935 /*
936 ** '~' and operator function. Based on a GIDX and a serialized return true if
937 ** the first is contained by the second.
938 */
940 Datum gserialized_gidx_geom_within(PG_FUNCTION_ARGS)
941 {
942  GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0);
943 
944  if ( gserialized_datum_predicate_geom_gidx(PG_GETARG_DATUM(1), gidx, gidx_contains) == LW_TRUE )
945  PG_RETURN_BOOL(true);
946 
947  PG_RETURN_BOOL(false);
948 }
949 
950 /*
951 ** '~' and operator function. Based on two GIDX return true if
952 ** the first is contained by the second.
953 */
955 Datum gserialized_gidx_gidx_within(PG_FUNCTION_ARGS)
956 {
957  if ( gidx_contains((GIDX *)PG_GETARG_POINTER(1), (GIDX *)PG_GETARG_POINTER(0)))
958  PG_RETURN_BOOL(true);
959 
960  PG_RETURN_BOOL(false);
961 }
962 #endif
963 
964 /*
965 ** '@' and operator function. Based on two serialized return true if
966 ** the first contains the second.
967 */
969 Datum gserialized_contains(PG_FUNCTION_ARGS)
970 {
971  if ( gserialized_datum_predicate(PG_GETARG_DATUM(0),PG_GETARG_DATUM(1), gidx_contains) == LW_TRUE )
972  {
973  PG_RETURN_BOOL(true);
974  }
975 
976  PG_RETURN_BOOL(false);
977 }
978 
979 #if POSTGIS_PGSQL_VERSION > 94
980 /*
981 ** '@' and operator function. Based on a GIDX and a serialized return true if
982 ** the first contains the second.
983 */
985 Datum gserialized_gidx_geom_contains(PG_FUNCTION_ARGS)
986 {
987  GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0);
988 
989  if ( gserialized_datum_predicate_gidx_geom(gidx, PG_GETARG_DATUM(1), gidx_contains) == LW_TRUE )
990  PG_RETURN_BOOL(true);
991 
992  PG_RETURN_BOOL(false);
993 }
994 
995 /*
996 ** '@' and operator function. Based on two GIDX return true if
997 ** the first contains the second.
998 */
1000 Datum gserialized_gidx_gidx_contains(PG_FUNCTION_ARGS)
1001 {
1002  if ( gidx_contains((GIDX *)PG_GETARG_POINTER(0), (GIDX *)PG_GETARG_POINTER(1)))
1003  PG_RETURN_BOOL(true);
1004 
1005  PG_RETURN_BOOL(false);
1006 }
1007 
1009 Datum gserialized_gidx_geom_same(PG_FUNCTION_ARGS)
1010 {
1011  GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0);
1012 
1013  if ( gserialized_datum_predicate_gidx_geom(gidx, PG_GETARG_DATUM(1), gidx_equals) == LW_TRUE )
1014  PG_RETURN_BOOL(true);
1015 
1016  PG_RETURN_BOOL(false);
1017 }
1018 
1020 Datum gserialized_gidx_gidx_same(PG_FUNCTION_ARGS)
1021 {
1022  if ( gidx_equals((GIDX *)PG_GETARG_POINTER(0), (GIDX *)PG_GETARG_POINTER(1)) )
1023  PG_RETURN_BOOL(true);
1024 
1025  PG_RETURN_BOOL(false);
1026 }
1027 #endif
1028 
1029 /*
1030 ** '&&' operator function. Based on two serialized return true if
1031 ** they overlap and false otherwise.
1032 */
1034 Datum gserialized_overlaps(PG_FUNCTION_ARGS)
1035 {
1036  if ( gserialized_datum_predicate(PG_GETARG_DATUM(0),PG_GETARG_DATUM(1), gidx_overlaps) == LW_TRUE )
1037  {
1038  PG_RETURN_BOOL(true);
1039  }
1040 
1041  PG_RETURN_BOOL(false);
1042 }
1043 
1044 #if POSTGIS_PGSQL_VERSION > 94
1045 /*
1046  * This is the cross-operator for the geographies
1047  */
1049 Datum gserialized_gidx_geog_overlaps(PG_FUNCTION_ARGS)
1050 {
1051  GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0);
1052 
1053  if ( gserialized_datum_predicate_gidx_geom(gidx, PG_GETARG_DATUM(1), gidx_overlaps) == LW_TRUE )
1054  PG_RETURN_BOOL(true);
1055 
1056  PG_RETURN_BOOL(false);
1057 }
1058 
1060 Datum gserialized_gidx_geom_overlaps(PG_FUNCTION_ARGS)
1061 {
1062  GIDX *gidx = (GIDX *)PG_GETARG_POINTER(0);
1063 
1064  if ( gserialized_datum_predicate_gidx_geom(gidx, PG_GETARG_DATUM(1), gidx_overlaps) == LW_TRUE )
1065  PG_RETURN_BOOL(true);
1066 
1067  PG_RETURN_BOOL(false);
1068 }
1069 
1071 Datum gserialized_gidx_gidx_overlaps(PG_FUNCTION_ARGS)
1072 {
1073  if ( gidx_overlaps((GIDX *)PG_GETARG_POINTER(0), (GIDX *)PG_GETARG_POINTER(1)) )
1074  PG_RETURN_BOOL(true);
1075 
1076  PG_RETURN_BOOL(false);
1077 }
1078 #endif
1079 
1080 /***********************************************************************
1081 * GiST Index Support Functions
1082 */
1083 
1084 /*
1085 ** GiST support function. Given a geography, return a "compressed"
1086 ** version. In this case, we convert the geography into a geocentric
1087 ** bounding box. If the geography already has the box embedded in it
1088 ** we pull that out and hand it back.
1089 */
1091 Datum gserialized_gist_compress(PG_FUNCTION_ARGS)
1092 {
1093  GISTENTRY *entry_in = (GISTENTRY*)PG_GETARG_POINTER(0);
1094  GISTENTRY *entry_out = NULL;
1095  char gidxmem[GIDX_MAX_SIZE];
1096  GIDX *bbox_out = (GIDX*)gidxmem;
1097  int result = LW_SUCCESS;
1098  uint32_t i;
1099 
1100  POSTGIS_DEBUG(4, "[GIST] 'compress' function called");
1101 
1102  /*
1103  ** Not a leaf key? There's nothing to do.
1104  ** Return the input unchanged.
1105  */
1106  if ( ! entry_in->leafkey )
1107  {
1108  POSTGIS_DEBUG(4, "[GIST] non-leafkey entry, returning input unaltered");
1109  PG_RETURN_POINTER(entry_in);
1110  }
1111 
1112  POSTGIS_DEBUG(4, "[GIST] processing leafkey input");
1113  entry_out = palloc(sizeof(GISTENTRY));
1114 
1115  /*
1116  ** Null key? Make a copy of the input entry and
1117  ** return.
1118  */
1119  if ( DatumGetPointer(entry_in->key) == NULL )
1120  {
1121  POSTGIS_DEBUG(4, "[GIST] leafkey is null");
1122  gistentryinit(*entry_out, (Datum) 0, entry_in->rel,
1123  entry_in->page, entry_in->offset, false);
1124  POSTGIS_DEBUG(4, "[GIST] returning copy of input");
1125  PG_RETURN_POINTER(entry_out);
1126  }
1127 
1128  /* Extract our index key from the GiST entry. */
1129  result = gserialized_datum_get_gidx_p(entry_in->key, bbox_out);
1130 
1131  /* Is the bounding box valid (non-empty, non-infinite) ?
1132  * If not, use the "unknown" GIDX. */
1133  if ( result == LW_FAILURE )
1134  {
1135  POSTGIS_DEBUG(4, "[GIST] empty geometry!");
1136  gidx_set_unknown(bbox_out);
1137  gistentryinit(*entry_out, PointerGetDatum(gidx_copy(bbox_out)),
1138  entry_in->rel, entry_in->page,
1139  entry_in->offset, false);
1140  PG_RETURN_POINTER(entry_out);
1141  }
1142 
1143  POSTGIS_DEBUGF(4, "[GIST] got entry_in->key: %s", gidx_to_string(bbox_out));
1144 
1145  /* Check all the dimensions for finite values.
1146  * If not, use the "unknown" GIDX as a key */
1147  for ( i = 0; i < GIDX_NDIMS(bbox_out); i++ )
1148  {
1149  if ( ! isfinite(GIDX_GET_MAX(bbox_out, i))
1150  || ! isfinite(GIDX_GET_MIN(bbox_out, i)) )
1151  {
1152  gidx_set_unknown(bbox_out);
1153  gistentryinit(*entry_out,
1154  PointerGetDatum(gidx_copy(bbox_out)),
1155  entry_in->rel, entry_in->page,
1156  entry_in->offset, false);
1157  PG_RETURN_POINTER(entry_out);
1158  }
1159  }
1160 
1161  /* Enure bounding box has minimums below maximums. */
1162  gidx_validate(bbox_out);
1163 
1164  /* Prepare GISTENTRY for return. */
1165  gistentryinit(*entry_out, PointerGetDatum(gidx_copy(bbox_out)),
1166  entry_in->rel, entry_in->page, entry_in->offset, false);
1167 
1168  /* Return GISTENTRY. */
1169  POSTGIS_DEBUG(4, "[GIST] 'compress' function complete");
1170  PG_RETURN_POINTER(entry_out);
1171 }
1172 
1173 /*
1174 ** GiST support function.
1175 ** Decompress an entry. Unused for geography, so we return.
1176 */
1178 Datum gserialized_gist_decompress(PG_FUNCTION_ARGS)
1179 {
1180  POSTGIS_DEBUG(5, "[GIST] 'decompress' function called");
1181  /* We don't decompress. Just return the input. */
1182  PG_RETURN_POINTER(PG_GETARG_POINTER(0));
1183 }
1184 
1185 /*
1186 ** GiST support function. Called from gserialized_gist_consistent below.
1187 */
1188 static inline bool gserialized_gist_consistent_leaf(GIDX *key, GIDX *query, StrategyNumber strategy)
1189 {
1190  bool retval;
1191 
1192  POSTGIS_DEBUGF(4, "[GIST] leaf consistent, strategy [%d], count[%i]",
1193  strategy, geog_counter_leaf++);
1194 
1195  switch (strategy)
1196  {
1197  case RTOverlapStrategyNumber:
1198  retval = (bool) gidx_overlaps(key, query);
1199  break;
1200  case RTSameStrategyNumber:
1201  retval = (bool) gidx_equals(key, query);
1202  break;
1203  case RTContainsStrategyNumber:
1204  case RTOldContainsStrategyNumber:
1205  retval = (bool) gidx_contains(key, query);
1206  break;
1207  case RTContainedByStrategyNumber:
1208  case RTOldContainedByStrategyNumber:
1209  retval = (bool) gidx_contains(query, key);
1210  break;
1211  default:
1212  retval = false;
1213  }
1214 
1215  return (retval);
1216 }
1217 
1218 /*
1219 ** GiST support function. Called from gserialized_gist_consistent below.
1220 */
1221 static inline bool gserialized_gist_consistent_internal(GIDX *key, GIDX *query, StrategyNumber strategy)
1222 {
1223  bool retval;
1224 
1225  POSTGIS_DEBUGF(4, "[GIST] internal consistent, strategy [%d], count[%i], query[%s], key[%s]",
1226  strategy, geog_counter_internal++, gidx_to_string(query), gidx_to_string(key) );
1227 
1228  switch (strategy)
1229  {
1230  case RTOverlapStrategyNumber:
1231  retval = (bool) gidx_overlaps(key, query);
1232  break;
1233  case RTSameStrategyNumber:
1234  case RTContainsStrategyNumber:
1235  case RTOldContainsStrategyNumber:
1236  retval = (bool) gidx_contains(key, query);
1237  break;
1238  case RTContainedByStrategyNumber:
1239  case RTOldContainedByStrategyNumber:
1240  retval = (bool) gidx_overlaps(key, query);
1241  break;
1242  default:
1243  retval = false;
1244  }
1245 
1246  return (retval);
1247 }
1248 
1249 /*
1250 ** GiST support function. Take in a query and an entry and see what the
1251 ** relationship is, based on the query strategy.
1252 */
1254 Datum gserialized_gist_consistent(PG_FUNCTION_ARGS)
1255 {
1256  GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
1257  StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1258  bool result;
1259  char gidxmem[GIDX_MAX_SIZE];
1260  GIDX *query_gbox_index = (GIDX*)gidxmem;
1261 
1262  /* PostgreSQL 8.4 and later require the RECHECK flag to be set here,
1263  rather than being supplied as part of the operator class definition */
1264  bool *recheck = (bool *) PG_GETARG_POINTER(4);
1265 
1266  /* We set recheck to false to avoid repeatedly pulling every "possibly matched" geometry
1267  out during index scans. For cases when the geometries are large, rechecking
1268  can make things twice as slow. */
1269  *recheck = false;
1270 
1271  POSTGIS_DEBUG(4, "[GIST] 'consistent' function called");
1272 
1273  /* Quick sanity check on query argument. */
1274  if ( DatumGetPointer(PG_GETARG_DATUM(1)) == NULL )
1275  {
1276  POSTGIS_DEBUG(4, "[GIST] null query pointer (!?!), returning false");
1277  PG_RETURN_BOOL(false); /* NULL query! This is screwy! */
1278  }
1279 
1280  /* Quick sanity check on entry key. */
1281  if ( DatumGetPointer(entry->key) == NULL )
1282  {
1283  POSTGIS_DEBUG(4, "[GIST] null index entry, returning false");
1284  PG_RETURN_BOOL(false); /* NULL entry! */
1285  }
1286 
1287  /* Null box should never make this far. */
1288  if ( gserialized_datum_get_gidx_p(PG_GETARG_DATUM(1), query_gbox_index) == LW_FAILURE )
1289  {
1290  POSTGIS_DEBUG(4, "[GIST] null query_gbox_index!");
1291  PG_RETURN_BOOL(false);
1292  }
1293 
1294  /* Treat leaf node tests different from internal nodes */
1295  if (GIST_LEAF(entry))
1296  {
1298  (GIDX*)DatumGetPointer(entry->key),
1299  query_gbox_index, strategy);
1300  }
1301  else
1302  {
1304  (GIDX*)DatumGetPointer(entry->key),
1305  query_gbox_index, strategy);
1306  }
1307 
1308  PG_RETURN_BOOL(result);
1309 }
1310 
1311 /*
1312 ** Function to pack floats of different realms
1313 ** This function serves to pack bit flags inside float type
1314 ** Resulted value represent can be from four different "realms"
1315 ** Every value from realm 3 is greater than any value from realms 2, 1 and 0.
1316 ** Every value from realm 2 is less than every value from realm 3 and greater
1317 ** than any value from realm 1 and 0, and so on. Values from the same realm
1318 ** loose two bits of precision. This technique is possible due to floating
1319 ** point numbers specification according to IEEE 754: exponent bits are highest
1320 ** (excluding sign bits, but here penalty is always positive). If float a is
1321 ** greater than float b, integer A with same bit representation as a is greater
1322 ** than integer B with same bits as b.
1323 */
1324 static float pack_float(const float value, const int realm)
1325 {
1326  union {
1327  float f;
1328  struct { unsigned value:31, sign:1; } vbits;
1329  struct { unsigned value:29, realm:2, sign:1; } rbits;
1330  } a;
1331 
1332  a.f = value;
1333  a.rbits.value = a.vbits.value >> 2;
1334  a.rbits.realm = realm;
1335 
1336  return a.f;
1337 }
1338 
1339 /*
1340 ** GiST support function. Calculate the "penalty" cost of adding this entry into an existing entry.
1341 ** Calculate the change in volume of the old entry once the new entry is added.
1342 */
1344 Datum gserialized_gist_penalty(PG_FUNCTION_ARGS)
1345 {
1346  GISTENTRY *origentry = (GISTENTRY*) PG_GETARG_POINTER(0);
1347  GISTENTRY *newentry = (GISTENTRY*) PG_GETARG_POINTER(1);
1348  float *result = (float*) PG_GETARG_POINTER(2);
1349  GIDX *gbox_index_orig, *gbox_index_new;
1350  float size_union, size_orig, edge_union, edge_orig;
1351 
1352  POSTGIS_DEBUG(4, "[GIST] 'penalty' function called");
1353 
1354  gbox_index_orig = (GIDX*)DatumGetPointer(origentry->key);
1355  gbox_index_new = (GIDX*)DatumGetPointer(newentry->key);
1356 
1357  /* Drop out if we're dealing with null inputs. Shouldn't happen. */
1358  if ( (gbox_index_orig == NULL) && (gbox_index_new == NULL) )
1359  {
1360  POSTGIS_DEBUG(4, "[GIST] both inputs NULL! returning penalty of zero");
1361  *result = 0.0;
1362  PG_RETURN_FLOAT8(*result);
1363  }
1364 
1365  /* Calculate the size difference of the boxes (volume difference in this case). */
1366  size_union = gidx_union_volume(gbox_index_orig, gbox_index_new);
1367  size_orig = gidx_volume(gbox_index_orig);
1368  *result = size_union - size_orig;
1369 
1370  /* REALM 0: No extension is required, volume is zero, return edge */
1371  /* REALM 1: No extension is required, return nonzero area */
1372  /* REALM 2: Area extension is zero, return nonzero edge extension */
1373  /* REALM 3: Area extension is nonzero, return it */
1374 
1375  if( *result == 0 )
1376  {
1377  if (size_orig > 0)
1378  {
1379  *result = pack_float(size_orig, 1); /* REALM 1 */
1380  }
1381  else
1382  {
1383  edge_union = gidx_union_edge(gbox_index_orig, gbox_index_new);
1384  edge_orig = gidx_edge(gbox_index_orig);
1385  *result = edge_union - edge_orig;
1386  if( *result == 0 )
1387  {
1388  *result = pack_float(edge_orig, 0); /* REALM 0 */
1389  }
1390  else
1391  {
1392  *result = pack_float(*result, 2); /* REALM 2 */
1393  }
1394  }
1395  }
1396  else
1397  {
1398  *result = pack_float(*result, 3); /* REALM 3 */
1399  }
1400 
1401  POSTGIS_DEBUGF(4, "[GIST] union size (%.12f), original size (%.12f), penalty (%.12f)", size_union, size_orig, *result);
1402 
1403  PG_RETURN_POINTER(result);
1404 }
1405 
1406 /*
1407 ** GiST support function. Merge all the boxes in a page into one master box.
1408 */
1410 Datum gserialized_gist_union(PG_FUNCTION_ARGS)
1411 {
1412  GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
1413  int *sizep = (int *) PG_GETARG_POINTER(1); /* Size of the return value */
1414  int numranges, i;
1415  GIDX *box_cur, *box_union;
1416 
1417  POSTGIS_DEBUG(4, "[GIST] 'union' function called");
1418 
1419  numranges = entryvec->n;
1420 
1421  box_cur = (GIDX*) DatumGetPointer(entryvec->vector[0].key);
1422 
1423  box_union = gidx_copy(box_cur);
1424 
1425  for ( i = 1; i < numranges; i++ )
1426  {
1427  box_cur = (GIDX*) DatumGetPointer(entryvec->vector[i].key);
1428  gidx_merge(&box_union, box_cur);
1429  }
1430 
1431  *sizep = VARSIZE(box_union);
1432 
1433  POSTGIS_DEBUGF(4, "[GIST] union called, numranges(%i), pageunion %s", numranges, gidx_to_string(box_union));
1434 
1435  PG_RETURN_POINTER(box_union);
1436 
1437 }
1438 
1439 /*
1440 ** GiST support function. Test equality of keys.
1441 */
1443 Datum gserialized_gist_same(PG_FUNCTION_ARGS)
1444 {
1445  GIDX *b1 = (GIDX*)PG_GETARG_POINTER(0);
1446  GIDX *b2 = (GIDX*)PG_GETARG_POINTER(1);
1447  bool *result = (bool*)PG_GETARG_POINTER(2);
1448 
1449  POSTGIS_DEBUG(4, "[GIST] 'same' function called");
1450 
1451  *result = gidx_equals(b1, b2);
1452 
1453  PG_RETURN_POINTER(result);
1454 }
1455 
1456 
1457 
1458 
1460 Datum gserialized_gist_geog_distance(PG_FUNCTION_ARGS)
1461 {
1462  GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
1463  Datum query_datum = PG_GETARG_DATUM(1);
1464  StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1465 #if POSTGIS_PGSQL_VERSION >= 95
1466  bool *recheck = (bool *) PG_GETARG_POINTER(4);
1467 #endif
1468  char query_box_mem[GIDX_MAX_SIZE];
1469  GIDX *query_box = (GIDX*)query_box_mem;
1470  GIDX *entry_box;
1471  double distance;
1472 
1473  POSTGIS_DEBUGF(3, "[GIST] '%s' function called", __func__);
1474 
1475  /* We are using '13' as the gist geography distance <-> strategy number */
1476  if ( strategy != 13 )
1477  {
1478  elog(ERROR, "unrecognized strategy number: %d", strategy);
1479  PG_RETURN_FLOAT8(FLT_MAX);
1480  }
1481 
1482  /* Null box should never make this far. */
1483  if ( gserialized_datum_get_gidx_p(query_datum, query_box) == LW_FAILURE )
1484  {
1485  POSTGIS_DEBUG(2, "[GIST] null query_gbox_index!");
1486  PG_RETURN_FLOAT8(FLT_MAX);
1487  }
1488 
1489 #if POSTGIS_PGSQL_VERSION >= 95
1490  /* When we hit leaf nodes, it's time to turn on recheck */
1491  if (GIST_LEAF(entry))
1492  {
1493  *recheck = true;
1494  }
1495 #endif
1496 
1497  /* Get the entry box */
1498  entry_box = (GIDX*)DatumGetPointer(entry->key);
1499 
1500  /* Return distances from key-based tests should always be */
1501  /* the minimum possible distance, box-to-box */
1502  /* We scale up to "world units" so that the box-to-box distances */
1503  /* compare reasonably with the over-the-spheroid distances that */
1504  /* the recheck process will turn up */
1505  distance = WGS84_RADIUS * gidx_distance(entry_box, query_box, 0);
1506  POSTGIS_DEBUGF(2, "[GIST] '%s' got distance %g", __func__, distance);
1507 
1508  PG_RETURN_FLOAT8(distance);
1509 }
1510 
1511 
1512 /*
1513 ** GiST support function.
1514 ** Take in a query and an entry and return the "distance" between them.
1515 **
1516 ** Given an index entry p and a query value q, this function determines the
1517 ** index entry's "distance" from the query value. This function must be
1518 ** supplied if the operator class contains any ordering operators. A query
1519 ** using the ordering operator will be implemented by returning index entries
1520 ** with the smallest "distance" values first, so the results must be consistent
1521 ** with the operator's semantics. For a leaf index entry the result just
1522 ** represents the distance to the index entry; for an internal tree node, the
1523 ** result must be the smallest distance that any child entry could have.
1524 **
1525 ** Strategy 13 is centroid-based distance tests <<->>
1526 ** Strategy 20 is cpa based distance tests |=|
1527 */
1529 Datum gserialized_gist_distance(PG_FUNCTION_ARGS)
1530 {
1531  GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
1532  StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1533  char query_box_mem[GIDX_MAX_SIZE];
1534  GIDX *query_box = (GIDX*)query_box_mem;
1535  GIDX *entry_box;
1536 #if POSTGIS_PGSQL_VERSION >= 95
1537  bool *recheck = (bool *) PG_GETARG_POINTER(4);
1538 #endif
1539 
1540  double distance;
1541 
1542  POSTGIS_DEBUG(4, "[GIST] 'distance' function called");
1543 
1544  /* Strategy 13 is <<->> */
1545  /* Strategy 20 is |=| */
1546  if ( strategy != 13 && strategy != 20 ) {
1547  elog(ERROR, "unrecognized strategy number: %d", strategy);
1548  PG_RETURN_FLOAT8(FLT_MAX);
1549  }
1550 
1551  /* Null box should never make this far. */
1552  if ( gserialized_datum_get_gidx_p(PG_GETARG_DATUM(1), query_box) == LW_FAILURE )
1553  {
1554  POSTGIS_DEBUG(4, "[GIST] null query_gbox_index!");
1555  PG_RETURN_FLOAT8(FLT_MAX);
1556  }
1557 
1558  /* Get the entry box */
1559  entry_box = (GIDX*)DatumGetPointer(entry->key);
1560 
1561 #if POSTGIS_PGSQL_VERSION >= 95
1562 
1563  /* Strategy 20 is |=| */
1564  distance = gidx_distance(entry_box, query_box, strategy == 20);
1565 
1566  /* Treat leaf node tests different from internal nodes */
1567  if (GIST_LEAF(entry))
1568  *recheck = true;
1569 #else
1570 
1571  if ( strategy == 20 )
1572  {
1573  elog(ERROR, "You need PostgreSQL 9.5.0 or higher in order to use |=| with index");
1574  PG_RETURN_FLOAT8(FLT_MAX);
1575  }
1576 
1577  /* Treat leaf node tests different from internal nodes */
1578  if (GIST_LEAF(entry))
1579  {
1580  /* Calculate distance to leaves */
1581  distance = (double)gidx_distance_leaf_centroid(entry_box, query_box);
1582  }
1583  else
1584  {
1585  /* Calculate distance for internal nodes */
1586  distance = (double)gidx_distance_node_centroid(entry_box, query_box);
1587  }
1588 #endif
1589  PG_RETURN_FLOAT8(distance);
1590 }
1591 
1592 
1593 /*
1594 ** Utility function to add entries to the axis partition lists in the
1595 ** picksplit function.
1596 */
1597 static void gserialized_gist_picksplit_addlist(OffsetNumber *list, GIDX **box_union, GIDX *box_current, int *pos, int num)
1598 {
1599  if ( *pos )
1600  gidx_merge(box_union, box_current);
1601  else
1602  {
1603  pfree(*box_union);
1604  *box_union = gidx_copy(box_current);
1605  }
1606  list[*pos] = num;
1607  (*pos)++;
1608 }
1609 
1610 /*
1611 ** Utility function check whether the number of entries two halves of the
1612 ** space constitute a "bad ratio" (poor balance).
1613 */
1615 {
1616  POSTGIS_DEBUGF(4, "[GIST] checking split ratio (%d, %d)", x, y);
1617  if ( (y == 0) || (((float)x / (float)y) < LIMIT_RATIO) ||
1618  (x == 0) || (((float)y / (float)x) < LIMIT_RATIO) )
1619  return true;
1620 
1621  return false;
1622 }
1623 
1624 static bool gserialized_gist_picksplit_badratios(int *pos, int dims)
1625 {
1626  int i;
1627  for ( i = 0; i < dims; i++ )
1628  {
1629  if ( gserialized_gist_picksplit_badratio(pos[2*i],pos[2*i+1]) == false )
1630  return false;
1631  }
1632  return true;
1633 }
1634 
1635 
1636 /*
1637 ** Where the picksplit algorithm cannot find any basis for splitting one way
1638 ** or another, we simply split the overflowing node in half.
1639 */
1640 static void gserialized_gist_picksplit_fallback(GistEntryVector *entryvec, GIST_SPLITVEC *v)
1641 {
1642  OffsetNumber i, maxoff;
1643  GIDX *unionL = NULL;
1644  GIDX *unionR = NULL;
1645  int nbytes;
1646 
1647  POSTGIS_DEBUG(4, "[GIST] in fallback picksplit function");
1648 
1649  maxoff = entryvec->n - 1;
1650 
1651  nbytes = (maxoff + 2) * sizeof(OffsetNumber);
1652  v->spl_left = (OffsetNumber*) palloc(nbytes);
1653  v->spl_right = (OffsetNumber*) palloc(nbytes);
1654  v->spl_nleft = v->spl_nright = 0;
1655 
1656  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1657  {
1658  GIDX *cur = (GIDX*)DatumGetPointer(entryvec->vector[i].key);
1659 
1660  if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
1661  {
1662  v->spl_left[v->spl_nleft] = i;
1663  if (unionL == NULL)
1664  {
1665  unionL = gidx_copy(cur);
1666  }
1667  else
1668  {
1669  gidx_merge(&unionL, cur);
1670  }
1671  v->spl_nleft++;
1672  }
1673  else
1674  {
1675  v->spl_right[v->spl_nright] = i;
1676  if (unionR == NULL)
1677  {
1678  unionR = gidx_copy(cur);
1679  }
1680  else
1681  {
1682  gidx_merge(&unionR, cur);
1683  }
1684  v->spl_nright++;
1685  }
1686  }
1687 
1688  if (v->spl_ldatum_exists)
1689  gidx_merge(&unionL, (GIDX*)DatumGetPointer(v->spl_ldatum));
1690 
1691  v->spl_ldatum = PointerGetDatum(unionL);
1692 
1693  if (v->spl_rdatum_exists)
1694  gidx_merge(&unionR, (GIDX*)DatumGetPointer(v->spl_rdatum));
1695 
1696  v->spl_rdatum = PointerGetDatum(unionR);
1697  v->spl_ldatum_exists = v->spl_rdatum_exists = false;
1698 }
1699 
1700 
1701 
1702 static void gserialized_gist_picksplit_constructsplit(GIST_SPLITVEC *v, OffsetNumber *list1, int nlist1, GIDX **union1, OffsetNumber *list2, int nlist2, GIDX **union2)
1703 {
1704  bool firstToLeft = true;
1705 
1706  POSTGIS_DEBUG(4, "[GIST] picksplit in constructsplit function");
1707 
1708  if (v->spl_ldatum_exists || v->spl_rdatum_exists)
1709  {
1710  if (v->spl_ldatum_exists && v->spl_rdatum_exists)
1711  {
1712  GIDX *LRl = gidx_copy(*union1);
1713  GIDX *LRr = gidx_copy(*union2);
1714  GIDX *RLl = gidx_copy(*union2);
1715  GIDX *RLr = gidx_copy(*union1);
1716  double sizeLR, sizeRL;
1717 
1718  gidx_merge(&LRl, (GIDX*)DatumGetPointer(v->spl_ldatum));
1719  gidx_merge(&LRr, (GIDX*)DatumGetPointer(v->spl_rdatum));
1720  gidx_merge(&RLl, (GIDX*)DatumGetPointer(v->spl_ldatum));
1721  gidx_merge(&RLr, (GIDX*)DatumGetPointer(v->spl_rdatum));
1722 
1723  sizeLR = gidx_inter_volume(LRl,LRr);
1724  sizeRL = gidx_inter_volume(RLl,RLr);
1725 
1726  POSTGIS_DEBUGF(4, "[GIST] sizeLR / sizeRL == %.12g / %.12g", sizeLR, sizeRL);
1727 
1728  if (sizeLR > sizeRL)
1729  firstToLeft = false;
1730 
1731  }
1732  else
1733  {
1734  float p1, p2;
1735  GISTENTRY oldUnion, addon;
1736 
1737  gistentryinit(oldUnion, (v->spl_ldatum_exists) ? v->spl_ldatum : v->spl_rdatum,
1738  NULL, NULL, InvalidOffsetNumber, false);
1739 
1740  gistentryinit(addon, PointerGetDatum(*union1), NULL, NULL, InvalidOffsetNumber, false);
1741  DirectFunctionCall3(gserialized_gist_penalty, PointerGetDatum(&oldUnion), PointerGetDatum(&addon), PointerGetDatum(&p1));
1742  gistentryinit(addon, PointerGetDatum(*union2), NULL, NULL, InvalidOffsetNumber, false);
1743  DirectFunctionCall3(gserialized_gist_penalty, PointerGetDatum(&oldUnion), PointerGetDatum(&addon), PointerGetDatum(&p2));
1744 
1745  POSTGIS_DEBUGF(4, "[GIST] p1 / p2 == %.12g / %.12g", p1, p2);
1746 
1747  if ((v->spl_ldatum_exists && p1 > p2) || (v->spl_rdatum_exists && p1 < p2))
1748  firstToLeft = false;
1749  }
1750  }
1751 
1752  POSTGIS_DEBUGF(4, "[GIST] firstToLeft == %d", firstToLeft);
1753 
1754  if (firstToLeft)
1755  {
1756  v->spl_left = list1;
1757  v->spl_right = list2;
1758  v->spl_nleft = nlist1;
1759  v->spl_nright = nlist2;
1760  if (v->spl_ldatum_exists)
1761  gidx_merge(union1, (GIDX*)DatumGetPointer(v->spl_ldatum));
1762  v->spl_ldatum = PointerGetDatum(*union1);
1763  if (v->spl_rdatum_exists)
1764  gidx_merge(union2, (GIDX*)DatumGetPointer(v->spl_rdatum));
1765  v->spl_rdatum = PointerGetDatum(*union2);
1766  }
1767  else
1768  {
1769  v->spl_left = list2;
1770  v->spl_right = list1;
1771  v->spl_nleft = nlist2;
1772  v->spl_nright = nlist1;
1773  if (v->spl_ldatum_exists)
1774  gidx_merge(union2, (GIDX*)DatumGetPointer(v->spl_ldatum));
1775  v->spl_ldatum = PointerGetDatum(*union2);
1776  if (v->spl_rdatum_exists)
1777  gidx_merge(union1, (GIDX*)DatumGetPointer(v->spl_rdatum));
1778  v->spl_rdatum = PointerGetDatum(*union1);
1779  }
1780 
1781  v->spl_ldatum_exists = v->spl_rdatum_exists = false;
1782 }
1783 
1784 
1785 #define BELOW(d) (2*(d))
1786 #define ABOVE(d) ((2*(d))+1)
1787 
1788 /*
1789 ** GiST support function. Split an overflowing node into two new nodes.
1790 ** Uses linear algorithm from Ang & Tan [2], dividing node extent into
1791 ** four quadrants, and splitting along the axis that most evenly distributes
1792 ** entries between the new nodes.
1793 ** TODO: Re-evaluate this in light of R*Tree picksplit approaches.
1794 */
1796 Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS)
1797 {
1798 
1799  GistEntryVector *entryvec = (GistEntryVector*) PG_GETARG_POINTER(0);
1800 
1801  GIST_SPLITVEC *v = (GIST_SPLITVEC*) PG_GETARG_POINTER(1);
1802  OffsetNumber i;
1803  /* One union box for each half of the space. */
1804  GIDX **box_union;
1805  /* One offset number list for each half of the space. */
1806  OffsetNumber **list;
1807  /* One position index for each half of the space. */
1808  int *pos;
1809  GIDX *box_pageunion;
1810  GIDX *box_current;
1811  int direction = -1;
1812  bool all_entries_equal = true;
1813  OffsetNumber max_offset;
1814  int nbytes, ndims_pageunion, d;
1815  int posmin = entryvec->n;
1816 
1817  POSTGIS_DEBUG(4, "[GIST] 'picksplit' function called");
1818 
1819  /*
1820  ** First calculate the bounding box and maximum number of dimensions in this page.
1821  */
1822 
1823  max_offset = entryvec->n - 1;
1824  box_current = (GIDX*) DatumGetPointer(entryvec->vector[FirstOffsetNumber].key);
1825  box_pageunion = gidx_copy(box_current);
1826 
1827  /* Calculate the containing box (box_pageunion) for the whole page we are going to split. */
1828  for ( i = OffsetNumberNext(FirstOffsetNumber); i <= max_offset; i = OffsetNumberNext(i) )
1829  {
1830  box_current = (GIDX*) DatumGetPointer(entryvec->vector[i].key);
1831 
1832  if ( all_entries_equal == true && ! gidx_equals (box_pageunion, box_current) )
1833  all_entries_equal = false;
1834 
1835  gidx_merge( &box_pageunion, box_current );
1836  }
1837 
1838  POSTGIS_DEBUGF(3, "[GIST] box_pageunion: %s", gidx_to_string(box_pageunion));
1839 
1840  /* Every box in the page is the same! So, we split and just put half the boxes in each child. */
1841  if ( all_entries_equal )
1842  {
1843  POSTGIS_DEBUG(4, "[GIST] picksplit finds all entries equal!");
1845  PG_RETURN_POINTER(v);
1846  }
1847 
1848  /* Initialize memory structures. */
1849  nbytes = (max_offset + 2) * sizeof(OffsetNumber);
1850  ndims_pageunion = GIDX_NDIMS(box_pageunion);
1851  POSTGIS_DEBUGF(4, "[GIST] ndims_pageunion == %d", ndims_pageunion);
1852  pos = palloc(2*ndims_pageunion * sizeof(int));
1853  list = palloc(2*ndims_pageunion * sizeof(OffsetNumber*));
1854  box_union = palloc(2*ndims_pageunion * sizeof(GIDX*));
1855  for ( d = 0; d < ndims_pageunion; d++ )
1856  {
1857  list[BELOW(d)] = (OffsetNumber*) palloc(nbytes);
1858  list[ABOVE(d)] = (OffsetNumber*) palloc(nbytes);
1859  box_union[BELOW(d)] = gidx_new(ndims_pageunion);
1860  box_union[ABOVE(d)] = gidx_new(ndims_pageunion);
1861  pos[BELOW(d)] = 0;
1862  pos[ABOVE(d)] = 0;
1863  }
1864 
1865  /*
1866  ** Assign each entry in the node to the volume partitions it belongs to,
1867  ** such as "above the x/y plane, left of the y/z plane, below the x/z plane".
1868  ** Each entry thereby ends up in three of the six partitions.
1869  */
1870  POSTGIS_DEBUG(4, "[GIST] 'picksplit' calculating best split axis");
1871  for ( i = FirstOffsetNumber; i <= max_offset; i = OffsetNumberNext(i) )
1872  {
1873  box_current = (GIDX*) DatumGetPointer(entryvec->vector[i].key);
1874 
1875  for ( d = 0; d < ndims_pageunion; d++ )
1876  {
1877  if ( GIDX_GET_MIN(box_current,d)-GIDX_GET_MIN(box_pageunion,d) < GIDX_GET_MAX(box_pageunion,d)-GIDX_GET_MAX(box_current,d) )
1878  {
1879  gserialized_gist_picksplit_addlist(list[BELOW(d)], &(box_union[BELOW(d)]), box_current, &(pos[BELOW(d)]), i);
1880  }
1881  else
1882  {
1883  gserialized_gist_picksplit_addlist(list[ABOVE(d)], &(box_union[ABOVE(d)]), box_current, &(pos[ABOVE(d)]), i);
1884  }
1885 
1886  }
1887 
1888  }
1889 
1890  /*
1891  ** "Bad disposition", too many entries fell into one octant of the space, so no matter which
1892  ** plane we choose to split on, we're going to end up with a mostly full node. Where the
1893  ** data is pretty homogeneous (lots of duplicates) entries that are equidistant from the
1894  ** sides of the page union box can occasionally all end up in one place, leading
1895  ** to this condition.
1896  */
1897  if ( gserialized_gist_picksplit_badratios(pos,ndims_pageunion) == true )
1898  {
1899  /*
1900  ** Instead we split on center points and see if we do better.
1901  ** First calculate the average center point for each axis.
1902  */
1903  double *avgCenter = palloc(ndims_pageunion * sizeof(double));
1904 
1905  for ( d = 0; d < ndims_pageunion; d++ )
1906  {
1907  avgCenter[d] = 0.0;
1908  }
1909 
1910  POSTGIS_DEBUG(4, "[GIST] picksplit can't find good split axis, trying center point method");
1911 
1912  for ( i = FirstOffsetNumber; i <= max_offset; i = OffsetNumberNext(i) )
1913  {
1914  box_current = (GIDX*) DatumGetPointer(entryvec->vector[i].key);
1915  for ( d = 0; d < ndims_pageunion; d++ )
1916  {
1917  avgCenter[d] += (GIDX_GET_MAX(box_current,d) + GIDX_GET_MIN(box_current,d)) / 2.0;
1918  }
1919  }
1920  for ( d = 0; d < ndims_pageunion; d++ )
1921  {
1922  avgCenter[d] /= max_offset;
1923  pos[BELOW(d)] = pos[ABOVE(d)] = 0; /* Re-initialize our counters. */
1924  POSTGIS_DEBUGF(4, "[GIST] picksplit average center point[%d] = %.12g", d, avgCenter[d]);
1925  }
1926 
1927  /* For each of our entries... */
1928  for ( i = FirstOffsetNumber; i <= max_offset; i = OffsetNumberNext(i) )
1929  {
1930  double center;
1931  box_current = (GIDX*) DatumGetPointer(entryvec->vector[i].key);
1932 
1933  for ( d = 0; d < ndims_pageunion; d++ )
1934  {
1935  center = (GIDX_GET_MIN(box_current,d)+GIDX_GET_MAX(box_current,d))/2.0;
1936  if ( center < avgCenter[d] )
1937  gserialized_gist_picksplit_addlist(list[BELOW(d)], &(box_union[BELOW(d)]), box_current, &(pos[BELOW(d)]), i);
1938  else if ( FPeq(center, avgCenter[d]) )
1939  if ( pos[BELOW(d)] > pos[ABOVE(d)] )
1940  gserialized_gist_picksplit_addlist(list[ABOVE(d)], &(box_union[ABOVE(d)]), box_current, &(pos[ABOVE(d)]), i);
1941  else
1942  gserialized_gist_picksplit_addlist(list[BELOW(d)], &(box_union[BELOW(d)]), box_current, &(pos[BELOW(d)]), i);
1943  else
1944  gserialized_gist_picksplit_addlist(list[ABOVE(d)], &(box_union[ABOVE(d)]), box_current, &(pos[ABOVE(d)]), i);
1945  }
1946 
1947  }
1948 
1949  /* Do we have a good disposition now? If not, screw it, just cut the node in half. */
1950  if ( gserialized_gist_picksplit_badratios(pos,ndims_pageunion) == true )
1951  {
1952  POSTGIS_DEBUG(4, "[GIST] picksplit still cannot find a good split! just cutting the node in half");
1954  PG_RETURN_POINTER(v);
1955  }
1956 
1957  }
1958 
1959  /*
1960  ** Now, what splitting plane gives us the most even ratio of
1961  ** entries in our child pages? Since each split region has been apportioned entries
1962  ** against the same number of total entries, the axis that has the smallest maximum
1963  ** number of entries in its regions is the most evenly distributed.
1964  ** TODO: what if the distributions are equal in two or more axes?
1965  */
1966  for ( d = 0; d < ndims_pageunion; d++ )
1967  {
1968  int posd = Max(pos[ABOVE(d)],pos[BELOW(d)]);
1969  if ( posd < posmin )
1970  {
1971  direction = d;
1972  posmin = posd;
1973  }
1974  }
1975  if ( direction == -1 || posmin == entryvec->n )
1976  {
1977  /* ERROR OUT HERE */
1978  elog(ERROR, "Error in building split, unable to determine split direction.");
1979  }
1980 
1981  POSTGIS_DEBUGF(3, "[GIST] 'picksplit' splitting on axis %d", direction);
1982 
1984  pos[BELOW(direction)],
1985  &(box_union[BELOW(direction)]),
1986  list[ABOVE(direction)],
1987  pos[ABOVE(direction)],
1988  &(box_union[ABOVE(direction)]) );
1989 
1990  POSTGIS_DEBUGF(4, "[GIST] spl_ldatum: %s", gidx_to_string((GIDX*)v->spl_ldatum));
1991  POSTGIS_DEBUGF(4, "[GIST] spl_rdatum: %s", gidx_to_string((GIDX*)v->spl_rdatum));
1992 
1993  POSTGIS_DEBUGF(4, "[GIST] axis %d: parent range (%.12g, %.12g) left range (%.12g, %.12g), right range (%.12g, %.12g)",
1994  direction,
1995  GIDX_GET_MIN(box_pageunion, direction), GIDX_GET_MAX(box_pageunion, direction),
1996  GIDX_GET_MIN((GIDX*)v->spl_ldatum, direction), GIDX_GET_MAX((GIDX*)v->spl_ldatum, direction),
1997  GIDX_GET_MIN((GIDX*)v->spl_rdatum, direction), GIDX_GET_MAX((GIDX*)v->spl_rdatum, direction) );
1998 
1999  PG_RETURN_POINTER(v);
2000 
2001 }
2002 
2003 /*
2004 ** The GIDX key must be defined as a PostgreSQL type, even though it is only
2005 ** ever used internally. These no-op stubs are used to bind the type.
2006 */
2008 Datum gidx_in(PG_FUNCTION_ARGS)
2009 {
2010  ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2011  errmsg("function gidx_in not implemented")));
2012  PG_RETURN_POINTER(NULL);
2013 }
2014 
2016 Datum gidx_out(PG_FUNCTION_ARGS)
2017 {
2018  GIDX *box = (GIDX *) PG_GETARG_POINTER(0);
2019  char *result = gidx_to_string(box);
2020  PG_RETURN_CSTRING(result);
2021 }
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
static void gidx_set_unknown(GIDX *a)
static float gidx_union_volume(GIDX *a, GIDX *b)
static bool gserialized_gist_consistent_internal(GIDX *key, GIDX *query, StrategyNumber strategy)
Datum gserialized_distance_nd(PG_FUNCTION_ARGS)
bool(* gidx_predicate)(GIDX *a, GIDX *b)
static void gserialized_gist_picksplit_constructsplit(GIST_SPLITVEC *v, OffsetNumber *list1, int nlist1, GIDX **union1, OffsetNumber *list2, int nlist2, GIDX **union2)
Datum gserialized_gidx_gidx_overlaps(PG_FUNCTION_ARGS)
Datum gserialized_gist_union(PG_FUNCTION_ARGS)
static bool gserialized_gist_picksplit_badratios(int *pos, int dims)
bool gidx_is_unknown(const GIDX *a)
static float pack_float(const float value, const int realm)
Datum gserialized_within(PG_FUNCTION_ARGS)
Datum gserialized_gist_same(PG_FUNCTION_ARGS)
#define LIMIT_RATIO
static float gidx_inter_volume(GIDX *a, GIDX *b)
Datum gserialized_contains(PG_FUNCTION_ARGS)
static float gidx_union_edge(GIDX *a, GIDX *b)
static void gserialized_gist_picksplit_addlist(OffsetNumber *list, GIDX **box_union, GIDX *box_current, int *pos, int num)
Datum gserialized_gidx_geom_overlaps(PG_FUNCTION_ARGS)
Datum gserialized_gidx_gidx_within(PG_FUNCTION_ARGS)
static float gidx_volume(GIDX *a)
GIDX * gidx_copy(GIDX *b)
Datum gserialized_gist_consistent(PG_FUNCTION_ARGS)
Datum gserialized_gidx_geom_contains(PG_FUNCTION_ARGS)
static void gidx_dimensionality_check(GIDX **a, GIDX **b)
bool gidx_contains(GIDX *a, GIDX *b)
Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS)
#define ABOVE(d)
static int gserialized_datum_predicate_gidx_geom(GIDX *gidx1, Datum gs2, gidx_predicate predicate)
static int gserialized_datum_predicate(Datum gs1, Datum gs2, gidx_predicate predicate)
Support function.
Datum gserialized_gidx_geog_overlaps(PG_FUNCTION_ARGS)
static int gserialized_gist_picksplit_badratio(int x, int y)
#define BELOW(d)
static bool gserialized_gist_consistent_leaf(GIDX *key, GIDX *query, StrategyNumber strategy)
static double gidx_distance_m(const GIDX *a, const GIDX *b)
static float gidx_edge(GIDX *a)
PG_FUNCTION_INFO_V1(gserialized_distance_nd)
Datum gserialized_gist_compress(PG_FUNCTION_ARGS)
Datum gserialized_gidx_gidx_same(PG_FUNCTION_ARGS)
static int gserialized_datum_predicate_geom_gidx(Datum gs1, GIDX *gidx2, gidx_predicate predicate)
Datum gserialized_overlaps(PG_FUNCTION_ARGS)
Datum gserialized_gidx_gidx_contains(PG_FUNCTION_ARGS)
Datum gserialized_gidx_geom_within(PG_FUNCTION_ARGS)
Datum gidx_out(PG_FUNCTION_ARGS)
Datum gserialized_gist_penalty(PG_FUNCTION_ARGS)
Datum gserialized_gist_distance(PG_FUNCTION_ARGS)
Datum gserialized_gist_decompress(PG_FUNCTION_ARGS)
Datum gserialized_gidx_geom_same(PG_FUNCTION_ARGS)
Datum gidx_in(PG_FUNCTION_ARGS)
Datum gserialized_gist_geog_distance(PG_FUNCTION_ARGS)
static double gidx_distance(const GIDX *a, const GIDX *b, int m_is_time)
Calculate the centroid->centroid distance between the boxes.
static bool gidx_equals(GIDX *a, GIDX *b)
static void gserialized_gist_picksplit_fallback(GistEntryVector *entryvec, GIST_SPLITVEC *v)
static bool gidx_overlaps(GIDX *a, GIDX *b)
void gidx_validate(GIDX *b)
void gidx_merge(GIDX **b_union, GIDX *b_new)
GSERIALIZED * gserialized_expand(GSERIALIZED *g, double distance)
Return a GSERIALIZED with an expanded bounding box.
int lwpoint_getPoint4d_p(const LWPOINT *point, POINT4D *out)
Definition: lwpoint.c:57
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:170
#define LW_FALSE
Definition: liblwgeom.h:77
void lwpoint_free(LWPOINT *pt)
Definition: lwpoint.c:213
#define LW_FAILURE
Definition: liblwgeom.h:79
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1144
#define LINETYPE
Definition: liblwgeom.h:86
double lwgeom_interpolate_point(const LWGEOM *lwin, const LWPOINT *lwpt)
Find the measure value at the location on the line closest to the point.
#define WGS84_RADIUS
Definition: liblwgeom.h:131
#define LW_SUCCESS
Definition: liblwgeom.h:80
uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwgeom.c:923
double lwgeom_length(const LWGEOM *geom)
Definition: lwgeom.c:1939
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:930
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:85
LWGEOM * lwgeom_closest_line(const LWGEOM *lw1, const LWGEOM *lw2)
Definition: measures.c:42
double lwgeom_length_2d(const LWGEOM *geom)
Definition: lwgeom.c:1961
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:76
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:937
LWGEOM * lwgeom_closest_line_3d(const LWGEOM *lw1, const LWGEOM *lw2)
Definition: measures3d.c:88
LWPOINT * lwline_get_lwpoint(const LWLINE *line, uint32_t where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition: lwline.c:318
This library is the generic geometry handling section of PostGIS.
Datum distance(PG_FUNCTION_ARGS)
int value
Definition: genraster.py:61
double m
Definition: liblwgeom.h:355
unsigned int uint32_t
Definition: uthash.h:78