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
-- 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),
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. */
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);
{
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;
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);
{
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. */
}
/* 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);
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. */
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);
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
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);
}
/**
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.
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 ) {
/* 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);