return c;
}
+inline bool box2df_is_empty(const BOX2DF *a)
+{
+ if (isnan(a->xmin))
+ return true;
+ else
+ return false;
+}
+
+static inline void box2df_set_empty(BOX2DF *a)
+{
+ a->xmin = a->xmax = a->ymin = a->ymax = NAN;
+ return;
+}
+static inline void box2df_set_finite(BOX2DF *a)
+{
+ if ( ! isfinite(a->xmax) )
+ a->xmax = FLT_MAX;
+ if ( ! isfinite(a->ymax) )
+ a->ymax = FLT_MAX;
+ if ( ! isfinite(a->ymin) )
+ a->ymin = -1*FLT_MAX;
+ if ( ! isfinite(a->xmin) )
+ a->xmin = -1*FLT_MAX;
+ return;
+}
/* Enlarge b_union to contain b_new. If b_new contains more
dimensions than b_union, expand b_union to contain those dimensions. */
{
float result;
- if ( a == NULL )
+ if ( a == NULL || box2df_is_empty(a) )
return (float)0.0;
if ( (a->xmax <= a->xmin) || (a->ymax <= a->ymin) )
static float box2df_edge(const BOX2DF *a)
{
- if ( a == NULL )
+ if ( a == NULL || box2df_is_empty(a) )
return (float)0.0;
return ((a->xmax) - (a->xmin)) + ((a->ymax) - (a->ymin));
return 0.0;
}
- if ( a == NULL )
+ if ( a == NULL || box2df_is_empty(a) )
return box2df_size(b);
- if ( b == NULL )
+ if ( b == NULL || box2df_is_empty(b) )
return box2df_size(a);
result = ((double)Max(a->xmax,b->xmax) - (double)Min(a->xmin,b->xmin)) *
return 0.0;
}
- if ( a == NULL )
+ if ( a == NULL || box2df_is_empty(a) )
return box2df_edge(b);
- if ( b == NULL )
+ if ( b == NULL || box2df_is_empty(b) )
return box2df_edge(a);
result = (Max(a->xmax,b->xmax) - Min(a->xmin,b->xmin)) +
{
float tmp;
POSTGIS_DEBUGF(5,"validating box2df (%s)", box2df_to_string(b));
+
+ if ( box2df_is_empty(b) )
+ return;
+
if ( b->xmax < b->xmin )
{
tmp = b->xmin;
static bool box2df_overlaps(const BOX2DF *a, const BOX2DF *b)
{
- if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+ if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+ return false;
if ( (a->xmin > b->xmax) || (b->xmin > a->xmax) ||
(a->ymin > b->ymax) || (b->ymin > a->ymax) )
bool box2df_contains(const BOX2DF *a, const BOX2DF *b)
{
- if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+ if ( !a || !b )
+ return false;
+
+ /* All things can contain EMPTY (except EMPTY) */
+ if ( box2df_is_empty(b) && ! box2df_is_empty(a) )
+ return true;
if ( (a->xmin > b->xmin) || (a->xmax < b->xmax) ||
(a->ymin > b->ymin) || (a->ymax < b->ymax) )
static bool box2df_within(const BOX2DF *a, const BOX2DF *b)
{
- if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+ if ( !a || !b )
+ return false;
+
+ /* EMPTY is within all other things (except EMPTY) */
+ if ( box2df_is_empty(a) && ! box2df_is_empty(b) )
+ return true;
POSTGIS_DEBUG(5, "entered function");
return box2df_contains(b,a);
static bool box2df_equals(const BOX2DF *a, const BOX2DF *b)
{
- if ( a && b ) {
- if ( (a->xmin != b->xmin) || (a->xmax != b->xmax) ||
- (a->ymin != b->ymin) || (a->ymax != b->ymax) )
- {
- return false;
- }
+ if ( !a && !b )
return true;
- } else if ( a || b ) {
- /* one empty, one not */
+ else if ( !a || !b )
return false;
- } else {
- /* both empty */
+ else if ( box2df_is_empty(a) && box2df_is_empty(b) )
return true;
- }
+ else if ( box2df_is_empty(a) || box2df_is_empty(b) )
+ return false;
+ else if ((a->xmin == b->xmin) && (a->xmax == b->xmax) && (a->ymin == b->ymin) && (a->ymax == b->ymax))
+ return true;
+ else
+ return false;
}
static bool box2df_overleft(const BOX2DF *a, const BOX2DF *b)
{
- if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+ if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+ return false;
/* a.xmax <= b.xmax */
return a->xmax <= b->xmax;
static bool box2df_left(const BOX2DF *a, const BOX2DF *b)
{
- if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+ if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+ return false;
/* a.xmax < b.xmin */
return a->xmax < b->xmin;
static bool box2df_right(const BOX2DF *a, const BOX2DF *b)
{
- if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+ if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+ return false;
/* a.xmin > b.xmax */
return a->xmin > b->xmax;
static bool box2df_overright(const BOX2DF *a, const BOX2DF *b)
{
- if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+ if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+ return false;
/* a.xmin >= b.xmin */
return a->xmin >= b->xmin;
static bool box2df_overbelow(const BOX2DF *a, const BOX2DF *b)
{
- if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+ if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+ return false;
/* a.ymax <= b.ymax */
return a->ymax <= b->ymax;
static bool box2df_below(const BOX2DF *a, const BOX2DF *b)
{
- if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+ if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+ return false;
/* a.ymax < b.ymin */
return a->ymax < b->ymin;
static bool box2df_above(const BOX2DF *a, const BOX2DF *b)
{
- if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+ if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+ return false;
/* a.ymin > b.ymax */
return a->ymin > b->ymax;
static bool box2df_overabove(const BOX2DF *a, const BOX2DF *b)
{
- if ( ! a || ! b ) return false; /* TODO: might be smarter for EMPTY */
+ if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
+ return false;
/* a.ymin >= b.ymin */
return a->ymin >= b->ymin;
/* Is the bounding box valid (non-empty, non-infinite)? If not, return input uncompressed. */
if ( result == LW_FAILURE )
{
+ box2df_set_empty(&bbox_out);
+ gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)),
+ entry_in->rel, entry_in->page, entry_in->offset, false);
+
POSTGIS_DEBUG(4, "[GIST] empty geometry!");
- PG_RETURN_POINTER(entry_in);
+ PG_RETURN_POINTER(entry_out);
}
POSTGIS_DEBUGF(4, "[GIST] got entry_in->key: %s", box2df_to_string(&bbox_out));
if ( ! isfinite(bbox_out.xmax) || ! isfinite(bbox_out.xmin) ||
! isfinite(bbox_out.ymax) || ! isfinite(bbox_out.ymin) )
{
+ box2df_set_finite(&bbox_out);
+ gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)),
+ entry_in->rel, entry_in->page, entry_in->offset, false);
+
POSTGIS_DEBUG(4, "[GIST] infinite geometry!");
- PG_RETURN_POINTER(entry_in);
+ PG_RETURN_POINTER(entry_out);
}
/* Enure bounding box has minimums below maximums. */
drop table p cascade;
-
--- create table test (id serial primary key, geom1 geometry, geom2 geometry);
--- create index test_x1 on test using gist (geom1);
--- create index test_x2 on test using gist (geom2);
--- select _postgis_gserialized_index_extent('test', 'geom1');
--- select _postgis_gserialized_index_extent('test', 'geom2');
--- insert into test (geom1, geom2) select NULL, NULL;
--- insert into test (geom1, geom2) select 'POINT EMPTY', 'LINESTRING EMPTY';
--- insert into test (geom1, geom2) select 'POINT EMPTY', 'LINESTRING EMPTY';
--- select _postgis_gserialized_index_extent('test', 'geom1');
--- select _postgis_gserialized_index_extent('test', 'geom2');
--- insert into test (geom1, geom2) select st_makepoint(s, s), st_makepoint(2*s, 2*s) from generate_series(-100,100) s;
--- select _postgis_gserialized_index_extent('test', 'geom1');
--- select _postgis_gserialized_index_extent('test', 'geom2');
+--
+-- Index assisted extent generation
+--
+create table test (id serial primary key, geom1 geometry, geom2 geometry);
+create index test_x1 on test using gist (geom1);
+create index test_x2 on test using gist (geom2);
+select '1.a null', _postgis_index_extent('test', 'geom1');
+select '1.b null', _postgis_index_extent('test', 'geom2');
+insert into test (geom1, geom2) select NULL, NULL;
+insert into test (geom1, geom2) select 'POINT EMPTY', 'LINESTRING EMPTY';
+select '2.a null', _postgis_index_extent('test', 'geom1');
+select '2.b null', _postgis_index_extent('test', 'geom2');
+insert into test (geom1, geom2) select 'POINT EMPTY', 'LINESTRING EMPTY' from generate_series(0,1024);
+select '3.a null', _postgis_index_extent('test', 'geom1');
+select '3.b null', _postgis_index_extent('test', 'geom2');
+insert into test (geom1, geom2) select st_makepoint(s, s), st_makepoint(2*s, 2*s) from generate_series(-100,100) s;
+select '4.a box',_postgis_index_extent('test', 'geom1');
+select '4.b box',_postgis_index_extent('test', 'geom2');
+delete from test;
+select '5.a bad-box',_postgis_index_extent('test', 'geom1');
+select '5.b bad-box',_postgis_index_extent('test', 'geom2');
+vacuum full test;
+select '6.a null', _postgis_index_extent('test', 'geom1');
+select '6.b null', _postgis_index_extent('test', 'geom2');
+drop table test cascade;