From: Paul Ramsey Date: Thu, 21 May 2015 22:48:40 +0000 (+0000) Subject: <-> for geography committed with recheck X-Git-Tag: 2.2.0rc1~476 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=dd58081608c555b049adcd1bb820763dadfbe50f;p=postgis <-> for geography committed with recheck git-svn-id: http://svn.osgeo.org/postgis/trunk@13538 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/postgis/geography.sql.in b/postgis/geography.sql.in index 3d3dbce0f..4b867556b 100644 --- a/postgis/geography.sql.in +++ b/postgis/geography.sql.in @@ -247,6 +247,28 @@ CREATE OPERATOR && ( JOIN = gserialized_gist_joinsel_nd ); +#if POSTGIS_PGSQL_VERSION >= 95 + +-- Availability: 2.2.0 +CREATE OR REPLACE FUNCTION geography_knn_distance(geography, geography) + RETURNS float8 + AS 'MODULE_PATHNAME','geography_distance' + LANGUAGE 'c' IMMUTABLE STRICT + COST 100; + +-- Availability: 2.2.0 +CREATE OPERATOR <-> ( + LEFTARG = geography, RIGHTARG = geography, PROCEDURE = geography_knn_distance, + COMMUTATOR = '<->' +); + +-- Availability: 2.2.0 +CREATE OR REPLACE FUNCTION geography_gist_distance(internal, geography, int4) + RETURNS float8 + AS 'MODULE_PATHNAME' ,'gserialized_gist_geog_distance' + LANGUAGE 'c'; + +#endif -- Availability: 1.5.0 CREATE OPERATOR CLASS gist_geography_ops @@ -256,6 +278,11 @@ CREATE OPERATOR CLASS gist_geography_ops -- OPERATOR 6 ~= , -- OPERATOR 7 ~ , -- OPERATOR 8 @ , +#if POSTGIS_PGSQL_VERSION >= 95 +-- Availability: 2.2.0 + OPERATOR 13 <-> FOR ORDER BY pg_catalog.float_ops, + FUNCTION 8 geography_gist_distance (internal, geography, int4), +#endif FUNCTION 1 geography_gist_consistent (internal, geography, int4), FUNCTION 2 geography_gist_union (bytea, internal), FUNCTION 3 geography_gist_compress (internal), diff --git a/postgis/geography_measurement.c b/postgis/geography_measurement.c index 0bf47ccc9..1390aef2b 100644 --- a/postgis/geography_measurement.c +++ b/postgis/geography_measurement.c @@ -126,8 +126,8 @@ Datum geography_distance(PG_FUNCTION_ARGS) GSERIALIZED* g1 = NULL; GSERIALIZED* g2 = NULL; double distance; - double tolerance; - bool use_spheroid; + double tolerance = 0.0; + bool use_spheroid = true; SPHEROID s; /* Get our geometry objects loaded into memory. */ @@ -135,10 +135,12 @@ Datum geography_distance(PG_FUNCTION_ARGS) g2 = PG_GETARG_GSERIALIZED_P(1); /* Read our tolerance value. */ - tolerance = PG_GETARG_FLOAT8(2); + if ( ! PG_ARGISNULL(2) ) + tolerance = PG_GETARG_FLOAT8(2); /* Read our calculation type. */ - use_spheroid = PG_GETARG_BOOL(3); + if ( ! PG_ARGISNULL(3) ) + use_spheroid = PG_GETARG_BOOL(3); /* Initialize spheroid */ spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s); @@ -192,9 +194,9 @@ Datum geography_dwithin(PG_FUNCTION_ARGS) { GSERIALIZED *g1 = NULL; GSERIALIZED *g2 = NULL; - double tolerance; + double tolerance = 0.0; double distance; - bool use_spheroid; + bool use_spheroid = true; SPHEROID s; int dwithin = LW_FALSE; @@ -203,10 +205,12 @@ Datum geography_dwithin(PG_FUNCTION_ARGS) g2 = PG_GETARG_GSERIALIZED_P(1); /* Read our tolerance value. */ - tolerance = PG_GETARG_FLOAT8(2); + if ( ! PG_ARGISNULL(2) ) + tolerance = PG_GETARG_FLOAT8(2); /* Read our calculation type. */ - use_spheroid = PG_GETARG_BOOL(3); + if ( ! PG_ARGISNULL(3) ) + use_spheroid = PG_GETARG_BOOL(3); /* Initialize spheroid */ spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s); @@ -254,9 +258,9 @@ Datum geography_distance_tree(PG_FUNCTION_ARGS) { GSERIALIZED *g1 = NULL; GSERIALIZED *g2 = NULL; - double tolerance; + double tolerance = 0.0; double distance; - bool use_spheroid; + bool use_spheroid = true; SPHEROID s; /* Get our geometry objects loaded into memory. */ @@ -272,10 +276,12 @@ Datum geography_distance_tree(PG_FUNCTION_ARGS) } /* Read our tolerance value. */ - tolerance = PG_GETARG_FLOAT8(2); + if ( ! PG_ARGISNULL(2) ) + tolerance = PG_GETARG_FLOAT8(2); /* Read our calculation type. */ - use_spheroid = PG_GETARG_BOOL(3); + if ( ! PG_ARGISNULL(3) ) + use_spheroid = PG_GETARG_BOOL(3); /* Initialize spheroid */ spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s); @@ -306,9 +312,9 @@ Datum geography_dwithin_uncached(PG_FUNCTION_ARGS) LWGEOM *lwgeom2 = NULL; GSERIALIZED *g1 = NULL; GSERIALIZED *g2 = NULL; - double tolerance; + double tolerance = 0.0; double distance; - bool use_spheroid; + bool use_spheroid = true; SPHEROID s; /* Get our geometry objects loaded into memory. */ @@ -316,10 +322,12 @@ Datum geography_dwithin_uncached(PG_FUNCTION_ARGS) g2 = PG_GETARG_GSERIALIZED_P(1); /* Read our tolerance value. */ - tolerance = PG_GETARG_FLOAT8(2); + if ( ! PG_ARGISNULL(2) ) + tolerance = PG_GETARG_FLOAT8(2); /* Read our calculation type. */ - use_spheroid = PG_GETARG_BOOL(3); + if ( ! PG_ARGISNULL(3) ) + use_spheroid = PG_GETARG_BOOL(3); /* Initialize spheroid */ spheroid_init_from_srid(fcinfo, gserialized_get_srid(g1), &s); diff --git a/postgis/gserialized_gist_nd.c b/postgis/gserialized_gist_nd.c index bda70715d..84d26db33 100644 --- a/postgis/gserialized_gist_nd.c +++ b/postgis/gserialized_gist_nd.c @@ -78,6 +78,9 @@ 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); +#if POSTGIS_PGSQL_VERSION >= 95 +Datum gserialized_gist_geog_distance(PG_FUNCTION_ARGS); +#endif /* ** ND Operator prototypes @@ -549,8 +552,46 @@ static double gidx_distance(const GIDX *a, const GIDX *b) static double gidx_distance_node_centroid(const GIDX *node, const GIDX *query) { - /* TODO: implement ! */ - return 0; + int i; + double sum = 0; + + /* Base computation on least available dimensions */ + int ndims = Min(GIDX_NDIMS(node), GIDX_NDIMS(query)); + + for ( i = 0; i < ndims; ++i ) + { + double d; + double amin = GIDX_GET_MIN(query,i); + double amax = GIDX_GET_MAX(query,i); + double bmin = GIDX_GET_MIN(node,i); + double bmax = GIDX_GET_MAX(node,i); + double ca = amin + ( ( amax - amin ) / 2.0 ); + + if ( ( ca <= bmax && ca >= bmin ) ) + { + /* overlaps */ + d = 0; + } + else if ( bmax < ca ) + { + /* is "left" */ + d = ca - bmax; + } + else + { + /* is "right" */ + assert( bmin > ca ); + d = bmin - ca; + } + 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); } /** @@ -1005,6 +1046,59 @@ Datum gserialized_gist_same(PG_FUNCTION_ARGS) PG_RETURN_POINTER(result); } + + +#if POSTGIS_PGSQL_VERSION >= 95 + +PG_FUNCTION_INFO_V1(gserialized_gist_geog_distance); +Datum gserialized_gist_geog_distance(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0); + Datum query_datum = PG_GETARG_DATUM(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + bool *recheck = (bool *) PG_GETARG_POINTER(4); + char query_box_mem[GIDX_MAX_SIZE]; + GIDX *query_box = (GIDX*)query_box_mem; + GIDX *entry_box; + double distance; + + POSTGIS_DEBUGF(4, "[GIST] '%s' function called", __func__); + + /* We are using '13' as the gist geography distance <-> strategy number */ + if ( strategy != 13 ) + { + 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(query_datum, 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); + + /* Return distances from key-based tests should always be */ + /* the minimum possible distance, box-to-box */ + /* We scale up to "world units" so that the box-to-box distances */ + /* compare reasonably with the over-the-spheroid distances that */ + /* the recheck process will turn up */ + distance = WGS84_RADIUS * gidx_distance(entry_box, query_box); + + /* When we hit leaf nodes, it's time to turn on recheck */ + if (GIST_LEAF(entry)) + { + *recheck = true; + } + + PG_RETURN_FLOAT8(distance); +} +#endif + + /* ** GiST support function. ** Take in a query and an entry and return the "distance" between them. @@ -1032,7 +1126,7 @@ Datum gserialized_gist_distance(PG_FUNCTION_ARGS) 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 ) { @@ -1050,23 +1144,25 @@ Datum gserialized_gist_distance(PG_FUNCTION_ARGS) /* Get the entry box */ entry_box = (GIDX*)DatumGetPointer(entry->key); - /* Box-style distance test */ + /* 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); } + /* Centroid-style distance test <<->> */ else { - /* Calculate distance for internal nodes */ - distance = (double)gidx_distance_node_centroid(entry_box, query_box); + /* 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);