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