]> granicus.if.org Git - postgis/commitdiff
Add N-dimensional distance operator with KNN support
authorSandro Santilli <strk@keybit.net>
Wed, 25 Feb 2015 08:46:08 +0000 (08:46 +0000)
committerSandro Santilli <strk@keybit.net>
Wed, 25 Feb 2015 08:46:08 +0000 (08:46 +0000)
Includes docs and tests

git-svn-id: http://svn.osgeo.org/postgis/trunk@13287 b70326c6-7e19-0410-871a-916f4a2858ee

NEWS
doc/reference_operator.xml
postgis/gserialized_gist_nd.c
postgis/postgis.sql.in
regress/knn.sql
regress/knn_expected
regress/operators.sql
regress/operators_expected

diff --git a/NEWS b/NEWS
index cd48d1e824c52c3196aa073a1eb4105455f54407..b9b4ae18dffb31d3fd69cab263710a575ed60652 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,8 @@ PostGIS 2.2.0
 
  * New Features *
 
+  - #3040, KNN GiST index based centroid (<<->>) and box (<<#>>)
+           n-D distance operators (Sandro Santilli / Boundless)
   - Interruptibility API for liblwgeom (Sandro Santilli / CartoDB)
   - #2939, ST_ClipByBox2D (Sandro Santilli / CartoDB)
   - #2247, ST_Retile and ST_CreateOverview: in-db raster overviews creation
index 9c2e63bea835fbd3ded7f3b1a6bd5c7b5de0a488..36bc7f7c65d84da20dea00e388ec019e50da107c 100644 (file)
@@ -1197,7 +1197,7 @@ Finally the hybrid:
                        <refname>&lt;#&gt;</refname>
 
                        <refpurpose>
-Returns the 2D distance between bounding boxes of 2 geometries.
+Returns the 2D distance between A and B bounding boxes.
                        </refpurpose>
                  </refnamediv>
 
@@ -1272,4 +1272,126 @@ SELECT b.tlid, b.mtfcc,
                  </refsection>
                </refentry>
 
+               <refentry id="geometry_distance_centroid_nd">
+                 <refnamediv>
+                       <refname>&lt;&lt;-&gt;&gt;</refname>
+
+                       <refpurpose>
+Returns the n-D distance between the centroids of A and B bounding
+boxes.
+                       </refpurpose>
+                 </refnamediv>
+
+                 <refsynopsisdiv>
+                       <funcsynopsis>
+                         <funcprototype>
+                               <funcdef>double precision <function>&lt;&lt;-&gt;&gt;</function></funcdef>
+
+                               <paramdef>
+                                 <type>geometry </type>
+
+                                 <parameter>A</parameter>
+                               </paramdef>
+
+                               <paramdef>
+                                 <type>geometry </type>
+
+                                 <parameter>B</parameter>
+                               </paramdef>
+                         </funcprototype>
+                       </funcsynopsis>
+                 </refsynopsisdiv>
+
+                 <refsection>
+                       <title>Description</title>
+
+                       <para>
+The <varname>&lt;&lt;-&gt;&gt;</varname> operator returns the n-D (euclidean)
+distance between the centroids of the bounding boxes of two geometries. 
+Useful for doing nearest neighbor
+<emphasis role="strong">approximate</emphasis> distance ordering.
+      </para>
+
+                       <note><para>
+This operand will make use of n-D GiST indexes that may be available on
+the geometries.  It is different from other operators that use spatial
+indexes in that the spatial index is only used when the operator is in
+the ORDER BY clause.
+      </para></note>
+                       <note><para>
+Index only kicks in if one of the geometries is a constant (not in a
+subquery/cte).  e.g. 'SRID=3005;POINT(1011102 450541)'::geometry instead
+of a.geom
+      </para></note>
+
+                        <para>Availability: 2.2.0 -- KNN only available for PostgreSQL 9.1+</para>
+                               
+               
+                 </refsection>
+
+                 <refsection>
+                       <title>See Also</title>
+                       <para>
+<xref linkend="geometry_distance_box_nd" />,
+<xref linkend="geometry_distance_centroid" />
+      </para>
+                 </refsection>
+               </refentry>
+
+               <refentry id="geometry_distance_box_nd">
+                 <refnamediv>
+                       <refname>&lt;&lt;#&gt;&gt;</refname>
+
+                       <refpurpose>
+Returns the n-D distance between A and B bounding boxes.
+                       </refpurpose>
+                 </refnamediv>
+
+                 <refsynopsisdiv>
+                       <funcsynopsis>
+                         <funcprototype>
+                               <funcdef>double precision <function>&lt;&lt;#&gt;&gt;</function></funcdef>
+
+                               <paramdef>
+                                 <type>geometry </type>
+
+                                 <parameter>A</parameter>
+                               </paramdef>
+
+                               <paramdef>
+                                 <type>geometry </type>
+
+                                 <parameter>B</parameter>
+                               </paramdef>
+                         </funcprototype>
+                       </funcsynopsis>
+                 </refsynopsisdiv>
+
+                 <refsection>
+                       <title>Description</title>
+
+                       <para>The <varname>&lt;&lt;#&gt;&gt;</varname> operator returns distance between two floating point bounding boxes, possibly reading them from a spatial index (PostgreSQL 9.1+ required).   Useful for doing nearest neighbor <emphasis role="strong">approximate</emphasis> distance ordering.</para>
+
+                       <note><para>This operand will make use of any indexes that may be available on the
+                         geometries.  It is different from other operators that use spatial indexes in that the spatial index is only used when the operator
+                         is in the ORDER BY clause.</para></note>
+                       <note><para>
+Index only kicks in if one of the geometries is a constant e.g. ORDER BY
+(ST_GeomFromText('POINT(1 2)') &lt;&lt;#&gt;&gt; geom)  instead of g1.geom
+&lt;&lt;#&gt;&gt;.
+      </para></note>
+
+                        <para>Availability: 2.2.0 -- KNN only available for PostgreSQL 9.1+</para>
+               
+                 </refsection>
+
+                 <refsection>
+                       <title>See Also</title>
+                       <para>
+<xref linkend="geometry_distance_centroid_nd" />,
+<xref linkend="geometry_distance_box" />
+      </para>
+                 </refsection>
+               </refentry>
+
        </sect1>
index e264cf374a85284e5c46524448903594fb2de9c6..066b780fed88501258bb09355c4f218e4c88ab96 100644 (file)
@@ -36,6 +36,9 @@
 #include "gserialized_gist.h"       /* For utility functions. */
 #include "geography.h"
 
+#include <assert.h>
+
+
 /* Fall back to older finite() if necessary */
 #ifndef HAVE_ISFINITE
 # ifdef HAVE_GNU_ISFINITE
@@ -75,6 +78,7 @@ Datum gserialized_gist_penalty(PG_FUNCTION_ARGS);
 Datum gserialized_gist_picksplit(PG_FUNCTION_ARGS);
 Datum gserialized_gist_union(PG_FUNCTION_ARGS);
 Datum gserialized_gist_same(PG_FUNCTION_ARGS);
+Datum gserialized_gist_distance(PG_FUNCTION_ARGS);
 
 /*
 ** ND Operator prototypes
@@ -82,6 +86,8 @@ Datum gserialized_gist_same(PG_FUNCTION_ARGS);
 Datum gserialized_overlaps(PG_FUNCTION_ARGS);
 Datum gserialized_contains(PG_FUNCTION_ARGS);
 Datum gserialized_within(PG_FUNCTION_ARGS);
+Datum gserialized_distance_box_nd(PG_FUNCTION_ARGS);
+Datum gserialized_distance_centroid_nd(PG_FUNCTION_ARGS);
 
 /*
 ** GIDX true/false test function type
@@ -456,6 +462,98 @@ gserialized_datum_predicate(Datum gs1, Datum gs2, gidx_predicate predicate)
        return LW_FALSE;
 }
 
+/**
+* Calculate the centroid->centroid distance between the boxes.
+*/
+static double gidx_distance_leaf_centroid(const GIDX *a, const GIDX *b)
+{
+  int ndims, i;
+  double sum = 0;
+
+  /* Base computation on least available dimensions */
+  ndims = Min(GIDX_NDIMS(b), GIDX_NDIMS(a));
+  for ( i = 0; i < ndims; ++i )
+  {
+    double ca, cb, d;
+    double amin = GIDX_GET_MIN(a,i);
+    double amax = GIDX_GET_MAX(a,i);
+    double bmin = GIDX_GET_MIN(b,i);
+    double bmax = GIDX_GET_MAX(b,i);
+    ca = amin + ( ( amax - amin ) / 2.0 );
+    cb = bmin + ( ( bmax - bmin ) / 2.0 );
+    d = ca - cb;
+    if ( ! isfinite(d) )
+    {
+      /* Can happen if a dimension was padded with FLT_MAX,
+       * effectively meaning "infinite range". In that case
+       * we take that dimension as adding 0 to the total
+       * distance.
+       */
+      continue;
+    }
+    sum += d * d;
+/*
+    POSTGIS_DEBUGF(3, " centroid of A for dimension %d is %g", i, ca);
+    POSTGIS_DEBUGF(3, " centroid of B for dimension %d is %g", i, cb);
+    POSTGIS_DEBUGF(3, " distance on dimension %d is %g, squared as %g, grows sum to %g", i, d, d*d, sum);
+*/
+  }
+  return sqrt(sum);
+}
+
+/**
+* Calculate the box->box distance.
+*/
+static double gidx_distance(const GIDX *a, const GIDX *b)
+{
+  int ndims, i;
+  double sum = 0;
+
+  /* Base computation on least available dimensions */
+  ndims = Min(GIDX_NDIMS(b), GIDX_NDIMS(a));
+  for ( i = 0; i < ndims; ++i )
+  {
+    double d;
+    double amin = GIDX_GET_MIN(a,i);
+    double amax = GIDX_GET_MAX(a,i);
+    double bmin = GIDX_GET_MIN(b,i);
+    double bmax = GIDX_GET_MAX(b,i);
+    POSTGIS_DEBUGF(3, "A %g - %g", amin, amax);
+    POSTGIS_DEBUGF(3, "B %g - %g", bmin, bmax);
+
+    if ( ( amin <= bmax && amax >= bmin ) )
+    {
+      /* overlaps */
+      d = 0;
+    }
+    else if ( bmax < amin )
+    {
+      /* is "left" */
+      d = amin - bmax;
+    }
+    else
+    {
+      /* is "right" */
+      assert( bmin > amax );
+      d = bmin - amax;
+    }
+    if ( ! isfinite(d) )
+    {
+      /* Can happen if coordinates are corrupted/NaN */
+      continue;
+    }
+    sum += d * d;
+    POSTGIS_DEBUGF(3, "dist %g, squared %g, grows sum to %g", d, d*d, sum);
+  }
+  return sqrt(sum);
+}
+
+static double gidx_distance_node_centroid(const GIDX *node, const GIDX *query)
+{
+  /* TODO: implement ! */
+  return 0;
+}
+
 /**
 * Return a #GSERIALIZED with an expanded bounding box.
 */
@@ -482,6 +580,55 @@ gserialized_expand(GSERIALIZED *g, double distance)
 * GiST N-D Index Operator Functions
 */
 
+PG_FUNCTION_INFO_V1(gserialized_distance_box_nd);
+Datum gserialized_distance_box_nd(PG_FUNCTION_ARGS)
+{
+       char bmem1[GIDX_MAX_SIZE];
+       GIDX *b1 = (GIDX*)bmem1;
+       char bmem2[GIDX_MAX_SIZE];
+       GIDX *b2 = (GIDX*)bmem2;
+       Datum gs1 = PG_GETARG_DATUM(0);
+       Datum gs2 = PG_GETARG_DATUM(1);
+       double distance;
+
+       POSTGIS_DEBUG(3, "entered function");
+
+       /* Must be able to build box for each argument (ie, not empty geometry). */
+       if ( (gserialized_datum_get_gidx_p(gs1, b1) == LW_SUCCESS) &&
+            (gserialized_datum_get_gidx_p(gs2, b2) == LW_SUCCESS) )
+       {
+               distance = gidx_distance(b1, b2);
+               POSTGIS_DEBUGF(3, "got boxes %s and %s", gidx_to_string(b1), gidx_to_string(b2));
+               PG_RETURN_FLOAT8(distance);
+       }
+       PG_RETURN_FLOAT8(FLT_MAX);
+}
+
+
+PG_FUNCTION_INFO_V1(gserialized_distance_centroid_nd);
+Datum gserialized_distance_centroid_nd(PG_FUNCTION_ARGS)
+{
+       char b1mem[GIDX_MAX_SIZE];
+       GIDX *b1 = (GIDX*)b1mem;
+       char b2mem[GIDX_MAX_SIZE];
+       GIDX *b2 = (GIDX*)b2mem;
+       Datum gs1 = PG_GETARG_DATUM(0);
+       Datum gs2 = PG_GETARG_DATUM(1);
+       double distance;
+
+       POSTGIS_DEBUG(3, "entered function");
+
+       /* Must be able to build box for each argument (ie, not empty geometry). */
+       if ( (gserialized_datum_get_gidx_p(gs1, b1) == LW_SUCCESS) &&
+                        (gserialized_datum_get_gidx_p(gs2, b2) == LW_SUCCESS) )
+       {
+               distance = gidx_distance_leaf_centroid(b1, b2);
+               POSTGIS_DEBUGF(3, "got boxes %s and %s", gidx_to_string(b1), gidx_to_string(b2));
+               PG_RETURN_FLOAT8(distance);
+       }
+       PG_RETURN_FLOAT8(FLT_MAX);
+}
+
 /*
 ** '~' and operator function. Based on two serialized return true if
 ** the first is contained by the second.
@@ -842,8 +989,6 @@ Datum gserialized_gist_union(PG_FUNCTION_ARGS)
 
 }
 
-
-
 /*
 ** GiST support function. Test equality of keys.
 */
@@ -861,6 +1006,72 @@ Datum gserialized_gist_same(PG_FUNCTION_ARGS)
        PG_RETURN_POINTER(result);
 }
 
+/*
+** GiST support function.
+** Take in a query and an entry and return the "distance" between them.
+**
+** Given an index entry p and a query value q, this function determines the
+** index entry's "distance" from the query value. This function must be
+** supplied if the operator class contains any ordering operators. A query
+** using the ordering operator will be implemented by returning index entries
+** with the smallest "distance" values first, so the results must be consistent
+** with the operator's semantics. For a leaf index entry the result just
+** represents the distance to the index entry; for an internal tree node, the
+** result must be the smallest distance that any child entry could have.
+**
+** Strategy 13 = centroid-based distance tests
+** Strategy 14 = box-based distance tests (not implemented)
+*/
+PG_FUNCTION_INFO_V1(gserialized_gist_distance);
+Datum gserialized_gist_distance(PG_FUNCTION_ARGS)
+{
+       GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
+       StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+       char query_box_mem[GIDX_MAX_SIZE];
+       GIDX *query_box = (GIDX*)query_box_mem;
+       GIDX *entry_box;
+       double distance;
+
+       POSTGIS_DEBUG(4, "[GIST] 'distance' function called");
+
+       /* We are using '13' as the gist distance-betweeen-centroids strategy number
+        *  and '14' as the gist distance-between-boxes strategy number */
+       if ( strategy != 13 && strategy != 14 ) {
+               elog(ERROR, "unrecognized strategy number: %d", strategy);
+               PG_RETURN_FLOAT8(FLT_MAX);
+       }
+
+       /* Null box should never make this far. */
+       if ( gserialized_datum_get_gidx_p(PG_GETARG_DATUM(1), query_box) == LW_FAILURE )
+       {
+               POSTGIS_DEBUG(4, "[GIST] null query_gbox_index!");
+               PG_RETURN_FLOAT8(FLT_MAX);
+       }
+
+       /* Get the entry box */
+       entry_box = (GIDX*)DatumGetPointer(entry->key);
+
+       /* Box-style distance test */
+       if ( strategy == 14 )
+       {
+               distance = gidx_distance(entry_box, query_box);
+               PG_RETURN_FLOAT8(distance);
+       }
+
+       /* Treat leaf node tests different from internal nodes */
+       if (GIST_LEAF(entry))
+       {
+               /* Calculate distance to leaves */
+               distance = (double)gidx_distance_leaf_centroid(entry_box, query_box);
+       }
+       else
+       {
+               /* Calculate distance for internal nodes */
+               distance = (double)gidx_distance_node_centroid(entry_box, query_box);
+       }
+
+       PG_RETURN_FLOAT8(distance);
+}
 
 
 /*
index dbac31b2c5f2337cb21822eed5c337f23e808209..654b7c010d0ede6b9d89822013d184eb1bd50762 100644 (file)
@@ -809,6 +809,39 @@ CREATE OPERATOR &&& (
        JOIN = gserialized_gist_joinsel_nd      
 );
 
+-- Availability: 2.2.0
+CREATE OR REPLACE FUNCTION geometry_distance_centroid_nd(geometry,geometry)
+       RETURNS float8
+       AS 'MODULE_PATHNAME', 'gserialized_distance_centroid_nd'
+       LANGUAGE 'c' IMMUTABLE STRICT;
+
+-- Availability: 2.2.0
+CREATE OPERATOR <<->> (
+    LEFTARG = geometry, RIGHTARG = geometry,
+    PROCEDURE = geometry_distance_centroid_nd,
+    COMMUTATOR = '<<->>'
+);
+
+-- Availability: 2.2.0
+CREATE OR REPLACE FUNCTION geometry_distance_box_nd(geom1 geometry, geom2 geometry)
+       RETURNS float8
+       AS 'MODULE_PATHNAME' ,'gserialized_distance_box_nd'
+       LANGUAGE 'c' IMMUTABLE STRICT;
+
+-- Availability: 2.2.0
+CREATE OPERATOR <<#>> (
+    LEFTARG = geometry, RIGHTARG = geometry,
+    PROCEDURE = geometry_distance_box_nd,
+    COMMUTATOR = '<<#>>'
+);
+
+-- Availability: 2.2.0
+CREATE OR REPLACE FUNCTION geometry_gist_distance_nd(internal,geometry,int4)
+       RETURNS float8
+       AS 'MODULE_PATHNAME', 'gserialized_gist_distance'
+       LANGUAGE 'c';
+
+
 -- Availability: 2.0.0
 CREATE OPERATOR CLASS gist_geometry_ops_nd
        FOR TYPE geometry USING GIST AS
@@ -817,6 +850,14 @@ CREATE OPERATOR CLASS gist_geometry_ops_nd
 --     OPERATOR        6        ~=     ,
 --     OPERATOR        7        ~      ,
 --     OPERATOR        8        @      ,
+#if POSTGIS_PGSQL_VERSION >= 91
+       -- Availability: 2.2.0
+       OPERATOR        13       <<->> FOR ORDER BY pg_catalog.float_ops,
+       -- Availability: 2.2.0
+       OPERATOR        14       <<#>> FOR ORDER BY pg_catalog.float_ops,
+       -- Availability: 2.2.0
+       FUNCTION        8        geometry_gist_distance_nd (internal, geometry, int4),
+#endif
        FUNCTION        1        geometry_gist_consistent_nd (internal, geometry, int4),
        FUNCTION        2        geometry_gist_union_nd (bytea, internal),
        FUNCTION        3        geometry_gist_compress_nd (internal),
index df0929b0cfb38046a8943ff848d67b8af84b9c47..9ef9fab851cddc281a8fb8c3457879f45025def9 100644 (file)
@@ -21,10 +21,11 @@ END;
 $$;
 
 \i regress_lots_of_points.sql
-CREATE INDEX on test using gist (the_geom);
 
 -- Index-supported KNN query
 
+CREATE INDEX test_gist_2d on test using gist (the_geom);
+
 SELECT '<-> idx', qnodes('select * from test order by the_geom <-> ST_MakePoint(0,0) LIMIT 1');
 SELECT '<-> res1',num,
   (the_geom <-> 'LINESTRING(0 0,5 5)'::geometry)::numeric(10,2),
@@ -38,6 +39,67 @@ SELECT '<#> res1',num,
   ST_astext(the_geom) from test
   order by the_geom <#> 'LINESTRING(1000 0,1005 5)'::geometry LIMIT 1;
 
+-- Index-supported nd-KNN query
+
+DROP INDEX test_gist_2d;
+
+UPDATE test set the_geom = ST_MakePoint(
+    ST_X(the_geom), ST_Y(the_geom),
+    num, -num);
+
+SELECT '<<->> seq', qnodes('select * from test order by the_geom <<->> ST_MakePoint(0,0)');
+SELECT '<<#>> seq', qnodes('select * from test order by the_geom <<#>> ST_MakePoint(0,0)');
+
+CREATE INDEX test_gist_nd on test using gist (the_geom gist_geometry_ops_nd);
+
+ANALYZE test;
+
+--  EXT       X                Y          Z        M
+-- min    0.0439142361 |   0.0197799355|     1| -50000
+-- max  999.955261     | 999.993652    | 50000|     -1
+--SELECT min(st_x(the_geom)) as minx, min(st_y(the_geom)) as miny,
+--       min(st_z(the_geom)) as minz, min(st_m(the_geom)) as minm,
+--       max(st_x(the_geom)) as maxx, max(st_y(the_geom)) as maxy,
+--       max(st_z(the_geom)) as maxz, max(st_m(the_geom)) as maxm
+--FROM test;
+
+
+SELECT '<<->> idx', qnodes('select * from test order by the_geom <<->> ST_MakePoint(0,0) LIMIT 1');
+SELECT '<<->> res1',num,
+  (the_geom <<->> 'LINESTRING(0 0,5 5)'::geometry)::numeric(10,2),
+  ST_astext(the_geom) from test
+  order by the_geom <<->> 'LINESTRING(0 0,5 5)'::geometry LIMIT 1;
+SELECT '<<->> res2',num,
+  (the_geom <<->> 'POINT(95 23 25024 -25025)'::geometry)::numeric(10,2),
+  ST_astext(the_geom) from test
+  order by the_geom <<->> 'POINT(95 23 25024 -25025)'::geometry LIMIT 1;
+SELECT '<<->> res3',num,
+  (the_geom <<->> 'POINT(631 729 25023 -25022)'::geometry)::numeric(10,2),
+  ST_astext(the_geom) from test
+  order by the_geom <<->> 'POINT(631 729 25023 -25022)'::geometry LIMIT 1;
+
+--  EXT       X                Y          Z        M
+-- min    0.0439142361 |   0.0197799355|     1| -50000
+-- max  999.955261     | 999.993652    | 50000|     -1
+SELECT '<<#>> idx', qnodes('select * from test order by the_geom <<#>> ST_MakePoint(0,0) LIMIT 1');
+SELECT '<<#>> res1',num,
+  (the_geom <<#>> 'LINESTRING(1000 0,1005 5)'::geometry)::numeric(10,2),
+  ST_astext(the_geom) from test
+  order by the_geom <<#>> 'LINESTRING(1000 0,1005 5)'::geometry LIMIT 1;
+-- <<#>> res2|1|2.00|POINT ZM (529.522339 509.260284 1 -1)
+SELECT '<<#>> res2',num,
+  (the_geom <<#>> 'LINESTRING ZM (0 0 -10 -10,1000 1000 -1 -1)'::geometry)::numeric(10,2),
+  ST_astext(the_geom) from test
+  order by the_geom <<#>> 'LINESTRING ZM (0 0 -10 -10,1000 1000 -1 -1)'::geometry LIMIT 1;
+-- <<#>> res3|50000|1.00|POINT ZM (912.12323 831.139587 50000 -50000)
+SELECT '<<#>> res3',num,
+  (the_geom <<#>> 'LINESTRING ZM (0 0 1 -60000,1000 1000 50000 -50001)'::geometry)::numeric(10,2),
+  ST_astext(the_geom) from test
+  order by the_geom <<#>> 'LINESTRING ZM (0 0 1 -60000,1000 1000 50000 -50001)'::geometry LIMIT 1;
+
+
+-- Cleanup
+
 DROP FUNCTION qnodes(text);
 
 DROP TABLE test;
index 5a63f278fdd7798c28f7ee6d60cac6c7f76f4cc2..209fcf014770ed98b027905288a7121efde31997 100644 (file)
@@ -2,3 +2,13 @@
 <-> res1|48589|0.17|POINT(2.33793712 2.44566727)
 <#> idx|Index Scan
 <#> res1|2057|0.83|POINT(999.173279 3.92185807)
+<<->> seq|Seq Scan
+<<#>> seq|Seq Scan
+<<->> idx|Index Scan
+<<->> res1|48589|0.17|POINT ZM (2.33793712 2.44566727 48589 -48589)
+<<->> res2|25025|1.20|POINT ZM (95.6546249 23.0995369 25025 -25025)
+<<->> res3|25023|1.27|POINT ZM (631.060242 729.787354 25023 -25023)
+<<#>> idx|Index Scan
+<<#>> res1|2057|0.83|POINT ZM (999.173279 3.92185807 2057 -2057)
+<<#>> res2|1|2.00|POINT ZM (529.522339 509.260284 1 -1)
+<<#>> res3|50000|1.00|POINT ZM (912.12323 831.139587 50000 -50000)
index 121ffc776d327605ea98fffb1d5a5c79d8621e9d..5ec6b2e9cb77cb83bfefd3b73da2f3c820bbee75 100644 (file)
@@ -141,3 +141,41 @@ WITH v(i,g) AS ( VALUES
  )
 SELECT 'ndovm2', array_agg(i) FROM v WHERE g &&& 'POINTZ(0 0 1)'::geometry
 ORDER BY 1;
+
+-- nd box centroid distance  <<->>
+
+select 'ndcd1', 'LINESTRING(0 0,0 10,10 10)'::geometry <<->>
+                'LINESTRING(6 2,6 8)'::geometry; -- 1
+select 'ndcd2', 'LINESTRING(0 0,0 10,10 10)'::geometry <<->>
+                'LINESTRING(11 0,19 10)'::geometry; -- 10
+select 'ndcd3', 'POINTM(0 0 0)'::geometry <<->>
+                'POINTM(0 0 5)'::geometry; -- 5
+select 'ndcd4', 'POINTZ(0 0 15)'::geometry <<->>
+                'POINTZ(0 0 10)'::geometry; -- 5
+select 'ndcd5', 'POINTZM(1 2 3 4)'::geometry <<->>
+                'POINTZM(2 3 4 5)'::geometry; -- 2
+select 'ndcd6', 'POINTZM(9 9 3 4)'::geometry <<->>
+                'POINT(9 8)'::geometry; -- 1, higher dimensions overlapping
+
+-- nd box distance  <<#>>
+
+select 'ndbd1', 'LINESTRING(0 0,0 10,10 10)'::geometry <<#>>
+                'LINESTRING(6 2,6 8)'::geometry; -- 0, overlap
+select 'ndbd2', 'LINESTRING(0 0,0 10,10 10)'::geometry <<#>>
+                'LINESTRING(11 0,19 10)'::geometry; -- 1 on the right
+select 'ndbd3', 'LINESTRING(0 0,10 10)'::geometry <<#>>
+                'LINESTRING(-11 0,-2 10)'::geometry; -- 2 on the left
+select 'ndbd4', 'LINESTRING(0 0,10 10)'::geometry <<#>>
+                'LINESTRING(0 13,5 14)'::geometry; -- 3 above
+select 'ndbd5', 'LINESTRING(0 0,10 10)'::geometry <<#>>
+                'LINESTRING(0 -20,5 -4)'::geometry; -- 4 below
+select 'ndbd6', 'LINESTRINGM(0 0 0,1 1 1)'::geometry <<#>>
+                'LINESTRING(0 0,1 1)'::geometry; -- 0 overlap, mixed
+select 'ndbd7', 'LINESTRINGM(0 0 0,1 1 1)'::geometry <<#>>
+                'LINESTRINGM(1 1 2,1 1 3)'::geometry; -- 1
+select 'ndbd8', 'LINESTRINGZ(0 0 0,1 1 1)'::geometry <<#>>
+                'LINESTRINGZ(1 1 3,1 1 5)'::geometry; -- 2
+select 'ndbd9', 'LINESTRINGZ(0 0 0,1 1 1)'::geometry <<#>>
+                'LINESTRINGM(1 1 3,1 1 5)'::geometry; -- 0, overlap, mixed
+select 'ndbd10', 'LINESTRINGZM(0 0 0 0,1 2 3 4)'::geometry <<#>>
+                 'LINESTRINGZM(3 4 5 6,4 5 6 7)'::geometry; -- 4
index f8b1dc8bf4467e39da89391bbef42ff6a2f61df8..c3f399861bf936c0c029c1e643f690f6477c519b 100644 (file)
@@ -57,3 +57,19 @@ ndov6|t
 ndov7|t
 ndovm1|{1,2,3,4,5,8}
 ndovm2|{1,2,4,6,7}
+ndcd1|1
+ndcd2|10
+ndcd3|5
+ndcd4|5
+ndcd5|2
+ndcd6|1
+ndbd1|0
+ndbd2|1
+ndbd3|2
+ndbd4|3
+ndbd5|4
+ndbd6|0
+ndbd7|1
+ndbd8|2
+ndbd9|0
+ndbd10|4