PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
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
64static int geog_counter_leaf = 0;
65static int geog_counter_internal = 0;
66#endif
67
68/*
69** ND Index key type stub prototypes
70*/
71Datum gidx_out(PG_FUNCTION_ARGS);
72Datum gidx_in(PG_FUNCTION_ARGS);
73
74/*
75** ND GiST prototypes
76*/
77Datum gserialized_gist_consistent(PG_FUNCTION_ARGS);
78Datum gserialized_gist_compress(PG_FUNCTION_ARGS);
79Datum gserialized_gist_decompress(PG_FUNCTION_ARGS);
80Datum gserialized_gist_penalty(PG_FUNCTION_ARGS);
81Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS);
82Datum gserialized_gist_union(PG_FUNCTION_ARGS);
83Datum gserialized_gist_same(PG_FUNCTION_ARGS);
84Datum gserialized_gist_distance(PG_FUNCTION_ARGS);
85Datum gserialized_gist_geog_distance(PG_FUNCTION_ARGS);
86
87/*
88** ND Operator prototypes
89*/
90Datum gserialized_overlaps(PG_FUNCTION_ARGS);
91Datum gserialized_gidx_geom_overlaps(PG_FUNCTION_ARGS);
92Datum gserialized_gidx_gidx_overlaps(PG_FUNCTION_ARGS);
93Datum gserialized_contains(PG_FUNCTION_ARGS);
94Datum gserialized_gidx_geom_contains(PG_FUNCTION_ARGS);
95Datum gserialized_gidx_gidx_contains(PG_FUNCTION_ARGS);
96Datum gserialized_within(PG_FUNCTION_ARGS);
97Datum gserialized_gidx_geom_within(PG_FUNCTION_ARGS);
98Datum gserialized_gidx_gidx_within(PG_FUNCTION_ARGS);
99Datum gserialized_same(PG_FUNCTION_ARGS);
100Datum gserialized_gidx_geom_same(PG_FUNCTION_ARGS);
101Datum gserialized_gidx_gidx_same(PG_FUNCTION_ARGS);
102Datum gserialized_distance_nd(PG_FUNCTION_ARGS);
103
104/*
105** GIDX true/false test function type
106*/
107typedef bool (*gidx_predicate)(GIDX *a, GIDX *b);
108
109/* Allocate a new copy of GIDX */
110GIDX *
111gidx_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. */
120void
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) */
142inline bool
143gidx_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
152void
154{
155 SET_VARSIZE(a, VARHDRSZ);
156}
157
158/* Enlarge b_union to contain b_new. */
159void
160gidx_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 */
203static float
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 */
218static float
219gidx_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. */
233static void
234gidx_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. */
245static float
246gidx_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. */
292static float
293gidx_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. */
339static float
340gidx_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*/
392bool
393gidx_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*/
430bool
431gidx_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*/
466bool
467gidx_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
505static int
506gserialized_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 argument (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
527static int
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 argument (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
546static int
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 argument (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
568static double
569gidx_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
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 */
644Datum 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*/
740Datum 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*/
753Datum 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*/
768Datum 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*/
781Datum 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*/
794Datum 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*/
809Datum 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*/
822Datum 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
831Datum 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*/
846Datum 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*/
859Datum 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 */
871Datum 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
882Datum 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
893Datum 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*/
912Datum 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*/
1003Datum 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*/
1013static inline bool
1014gserialized_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*/
1046static inline bool
1047gserialized_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*/
1082Datum 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 requires 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*/
1150static inline float
1151pack_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*/
1175Datum 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*/
1222Datum 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*/
1254Datum 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
1268Datum 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*/
1330Datum 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*/
1375static void
1376gserialized_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*/
1394static 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
1404static 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*/
1420static void
1421gserialized_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
1473static 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*/
1582Datum 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*/
1798Datum 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
1805Datum 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:267
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
GSERIALIZED * gserialized_set_gbox(GSERIALIZED *g, GBOX *gbox)
Copy a new bounding box into an existing gserialized.
Definition gserialized.c:60
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *gbox)
Read the box from the GSERIALIZED or calculate it if necessary.
Definition gserialized.c:94
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)
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)
GIDX * gidx_copy(GIDX *b)
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
#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:1246
LWGEOM * lwgeom_closest_line(const LWGEOM *lw1, const LWGEOM *lw2)
Definition measures.c:43
LWPOINT * lwline_get_lwpoint(const LWLINE *line, uint32_t where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition lwline.c:319
#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:2066
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition lwgeom.c:962
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:102
double lwgeom_length_2d(const LWGEOM *geom)
Definition lwgeom.c:2088
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition lwgeom.c:207
#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:969
LWGEOM * lwgeom_closest_line_3d(const LWGEOM *lw1, const LWGEOM *lw2)
Definition measures3d.c:110
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:141
static double distance(double x1, double y1, double x2, double y2)
Definition lwtree.c:1032
double mmax
Definition liblwgeom.h:361
double mmin
Definition liblwgeom.h:360
double m
Definition liblwgeom.h:414