Datum gserialized_overright_2d(PG_FUNCTION_ARGS);
Datum gserialized_overabove_2d(PG_FUNCTION_ARGS);
Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS);
-Datum gserialized_boxdistance_2d(PG_FUNCTION_ARGS);
+Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS);
+Datum gserialized_distance_centroid_2d(PG_FUNCTION_ARGS);
/*
** true/false test function type
*/
-typedef bool (*box2df_predicate)(BOX2DF *a, BOX2DF *b);
+typedef bool (*box2df_predicate)(const BOX2DF *a, const BOX2DF *b);
#if POSTGIS_DEBUG_LEVEL > 0
return;
}
-static bool box2df_overlaps(BOX2DF *a, BOX2DF *b)
+static bool box2df_overlaps(const BOX2DF *a, const BOX2DF *b)
{
if ( (a->xmin > b->xmax) || (b->xmin > a->xmax) ||
(a->ymin > b->ymax) || (b->ymin > a->ymax) )
return TRUE;
}
-static bool box2df_contains(BOX2DF *a, BOX2DF *b)
+static bool box2df_contains(const BOX2DF *a, const BOX2DF *b)
{
if ( (a->xmin > b->xmin) || (a->xmax < b->xmax) ||
(a->ymin > b->ymin) || (a->ymax < b->ymax) )
return TRUE;
}
-static bool box2df_within(BOX2DF *a, BOX2DF *b)
+static bool box2df_within(const BOX2DF *a, const BOX2DF *b)
{
POSTGIS_DEBUG(5, "entered function");
return box2df_contains(b,a);
}
-static bool box2df_equals(BOX2DF *a, BOX2DF *b)
+static bool box2df_equals(const BOX2DF *a, const BOX2DF *b)
{
if ( (a->xmin != b->xmin) || (a->xmax != b->xmax) ||
(a->ymin != b->ymin) || (a->ymax != b->ymax) )
return TRUE;
}
-static bool box2df_overleft(BOX2DF *a, BOX2DF *b)
+static bool box2df_overleft(const BOX2DF *a, const BOX2DF *b)
{
/* a.xmax <= b.xmax */
return a->xmax <= b->xmax;
}
-static bool box2df_left(BOX2DF *a, BOX2DF *b)
+static bool box2df_left(const BOX2DF *a, const BOX2DF *b)
{
/* a.xmax < b.xmin */
return a->xmax < b->xmin;
}
-static bool box2df_right(BOX2DF *a, BOX2DF *b)
+static bool box2df_right(const BOX2DF *a, const BOX2DF *b)
{
/* a.xmin > b.xmax */
return a->xmin > b->xmax;
}
-static bool box2df_overright(BOX2DF *a, BOX2DF *b)
+static bool box2df_overright(const BOX2DF *a, const BOX2DF *b)
{
/* a.xmin >= b.xmin */
return a->xmin >= b->xmin;
}
-static bool box2df_overbelow(BOX2DF *a, BOX2DF *b)
+static bool box2df_overbelow(const BOX2DF *a, const BOX2DF *b)
{
/* a.ymax <= b.ymax */
return a->ymax <= b->ymax;
}
-static bool box2df_below(BOX2DF *a, BOX2DF *b)
+static bool box2df_below(const BOX2DF *a, const BOX2DF *b)
{
/* a.ymax < b.ymin */
return a->ymax < b->ymin;
}
-static bool box2df_above(BOX2DF *a, BOX2DF *b)
+static bool box2df_above(const BOX2DF *a, const BOX2DF *b)
{
/* a.ymin > b.ymax */
return a->ymin > b->ymax;
}
-static bool box2df_overabove(BOX2DF *a, BOX2DF *b)
+static bool box2df_overabove(const BOX2DF *a, const BOX2DF *b)
{
/* a.ymin >= b.ymin */
return a->ymin >= b->ymin;
* Calculate the centroid->centroid distance between the boxes.
* We return the square distance to avoid a call to sqrt.
*/
-static double box2df_distance_leaf(BOX2DF *a, BOX2DF *b)
+static double box2df_distance_leaf_centroid(const BOX2DF *a, const BOX2DF *b)
{
/* The centroid->centroid distance between the boxes */
double a_x = (a->xmax + a->xmin) / 2.0;
* between the boxes.
* We return the square distance to avoid a call to sqrt.
*/
-static double box2df_distance_node(BOX2DF *node, BOX2DF *query)
+static double box2df_distance_node_centroid(const BOX2DF *node, const BOX2DF *query)
{
BOX2DF q;
double qx, qy;
return d;
}
+/* Quick distance function */
+static inline double pt_distance(double ax, double ay, double bx, double by)
+{
+ return sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by));
+}
+
+/**
+* Calculate the box->box distance.
+*/
+static double box2df_distance(const BOX2DF *a, const BOX2DF *b)
+{
+ /* Check for overlap */
+ if ( box2df_overlaps(a, b) )
+ return 0.0;
+
+ if ( box2df_left(a, b) )
+ {
+ if ( box2df_above(a, b) )
+ return pt_distance(a->xmax, a->ymin, b->xmin, b->ymax);
+ if ( box2df_below(a, b) )
+ return pt_distance(a->xmax, a->ymax, b->xmin, b->ymin);
+ else
+ return b->xmin - a->xmax;
+ }
+ if ( box2df_right(a, b) )
+ {
+ if ( box2df_above(a, b) )
+ return pt_distance(a->xmin, a->ymin, b->xmax, b->ymax);
+ if ( box2df_below(a, b) )
+ return pt_distance(a->xmin, a->ymax, b->xmax, b->ymin);
+ else
+ return a->xmin - b->xmax;
+ }
+ if ( box2df_above(a, b) )
+ {
+ if ( box2df_left(a, b) )
+ return pt_distance(a->xmax, a->ymin, b->xmin, b->ymax);
+ if ( box2df_right(a, b) )
+ return pt_distance(a->xmin, a->ymin, b->xmax, b->ymax);
+ else
+ return a->ymin - b->ymax;
+ }
+ if ( box2df_below(a, b) )
+ {
+ if ( box2df_left(a, b) )
+ return pt_distance(a->xmax, a->ymax, b->xmin, b->ymin);
+ if ( box2df_right(a, b) )
+ return pt_distance(a->xmin, a->ymax, b->xmax, b->ymin);
+ else
+ return b->ymin - a->ymax;
+ }
+
+ return INFINITY;
+}
+
/**
* Peak into a #GSERIALIZED datum to find the bounding box. If the
* GiST 2-D Index Operator Functions
*/
-PG_FUNCTION_INFO_V1(gserialized_boxdistance_2d);
-Datum gserialized_boxdistance_2d(PG_FUNCTION_ARGS)
+PG_FUNCTION_INFO_V1(gserialized_distance_centroid_2d);
+Datum gserialized_distance_centroid_2d(PG_FUNCTION_ARGS)
+{
+ BOX2DF b1, b2;
+ Datum gs1 = PG_GETARG_DATUM(0);
+ Datum gs2 = PG_GETARG_DATUM(1);
+
+ POSTGIS_DEBUG(3, "entered function");
+
+ /* Must be able to build box for each argument (ie, not empty geometry). */
+ if ( (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) &&
+ (gserialized_datum_get_box2df_p(gs2, &b2) == LW_SUCCESS) )
+ {
+ double distance = box2df_distance_leaf_centroid(&b1, &b2);
+ POSTGIS_DEBUGF(3, "got boxes %s and %s", box2df_to_string(&b1), box2df_to_string(&b2));
+ PG_RETURN_FLOAT8(distance);
+ }
+ PG_RETURN_FLOAT8(INFINITY);
+}
+
+PG_FUNCTION_INFO_V1(gserialized_distance_box_2d);
+Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS)
{
BOX2DF b1, b2;
- Datum gs1 = PG_GETARG_DATUM(0);
- Datum gs2 = PG_GETARG_DATUM(1);
+ Datum gs1 = PG_GETARG_DATUM(0);
+ Datum gs2 = PG_GETARG_DATUM(1);
POSTGIS_DEBUG(3, "entered function");
if ( (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) &&
(gserialized_datum_get_box2df_p(gs2, &b2) == LW_SUCCESS) )
{
- double distance = box2df_distance_leaf(&b1, &b2);
+ double distance = box2df_distance(&b1, &b2);
POSTGIS_DEBUGF(3, "got boxes %s and %s", box2df_to_string(&b1), box2df_to_string(&b2));
- PG_RETURN_FLOAT8(distance);
+ PG_RETURN_FLOAT8(distance);
}
- PG_RETURN_FLOAT8(INFINITY);
+ PG_RETURN_FLOAT8(INFINITY);
}
PG_FUNCTION_INFO_V1(gserialized_same_2d);
** 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
*/
PG_FUNCTION_INFO_V1(gserialized_gist_distance_2d);
Datum gserialized_gist_distance_2d(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
BOX2DF query_box;
+ BOX2DF *entry_box;
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
double distance;
POSTGIS_DEBUG(4, "[GIST] 'distance' function called");
- /* We are using '13' as the gist distance strategy number */
- if ( strategy != 13 ) {
+ /* 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(INFINITY);
}
PG_RETURN_FLOAT8(INFINITY);
}
+ /* Get the entry box */
+ entry_box = (BOX2DF*)DatumGetPointer(entry->key);
+
+ /* Box-style distance test */
+ if ( strategy == 14 )
+ {
+ distance = (double)box2df_distance(entry_box, &query_box);
+ PG_RETURN_FLOAT8(distance);
+ }
+
/* Treat leaf node tests different from internal nodes */
if (GIST_LEAF(entry))
{
- /* Calculate distance to centroid for leaves */
- BOX2DF *leaf_box = (BOX2DF*)DatumGetPointer(entry->key);
- distance = (double)box2df_distance_leaf(leaf_box, &query_box);
+ /* Calculate distance to leaves */
+ distance = (double)box2df_distance_leaf_centroid(entry_box, &query_box);
}
else
{
- /* Calculate distance to nearest corner for internal nodes */
- BOX2DF *node_box = (BOX2DF*)DatumGetPointer(entry->key);
- distance = (double)box2df_distance_node(node_box, &query_box);
+ /* Calculate distance for internal nodes */
+ distance = (double)box2df_distance_node_centroid(entry_box, &query_box);
}
PG_RETURN_FLOAT8(distance);