From: Paul Ramsey Date: Wed, 4 Mar 2015 19:47:33 +0000 (+0000) Subject: #3069, bounding boxes added to simple objects on deserialization X-Git-Tag: 2.2.0rc1~625 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=90f6ba3ed4ffc217a10b28cd3a0fa00d656c3687;p=postgis #3069, bounding boxes added to simple objects on deserialization git-svn-id: http://svn.osgeo.org/postgis/trunk@13302 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/liblwgeom/g_serialized.c b/liblwgeom/g_serialized.c index fa36da795..502bf01b4 100644 --- a/liblwgeom/g_serialized.c +++ b/liblwgeom/g_serialized.c @@ -172,103 +172,202 @@ int gserialized_read_gbox_p(const GSERIALIZED *g, GBOX *gbox) return LW_SUCCESS; } - /* No pre-calculated box, but for cartesian entries we can do some magic */ - if ( ! FLAGS_GET_GEODETIC(g->flags) ) + return LW_FAILURE; +} + +/* +* Populate a bounding box *without* allocating an LWGEOM. Useful +* for some performance purposes. +*/ +static int gserialized_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox) +{ + uint32_t type = gserialized_get_type(g); + + /* Peeking doesn't help if you already have a box or are geodetic */ + if ( FLAGS_GET_GEODETIC(g->flags) || FLAGS_GET_BBOX(g->flags) ) + { + return LW_FAILURE; + } + + /* Boxes of points are easy peasy */ + if ( type == POINTTYPE ) { - uint32_t type = gserialized_get_type(g); - /* Boxes of points are easy peasy */ - if ( type == POINTTYPE ) + int i = 1; /* Start past */ + double *dptr = (double*)(g->data); + + /* Read the empty flag */ + int *iptr = (int*)(g->data); + int isempty = (iptr[1] == 0); + + /* EMPTY point has no box */ + if ( isempty ) return LW_FAILURE; + + gbox->xmin = gbox->xmax = dptr[i++]; + gbox->ymin = gbox->ymax = dptr[i++]; + if ( FLAGS_GET_Z(g->flags) ) { - int i = 1; /* Start past */ - double *dptr = (double*)(g->data); - - /* Read the empty flag */ - int *iptr = (int*)(g->data); - int isempty = (iptr[1] == 0); - - /* EMPTY point has no box */ - if ( isempty ) return LW_FAILURE; - - gbox->xmin = gbox->xmax = dptr[i++]; - gbox->ymin = gbox->ymax = dptr[i++]; - if ( FLAGS_GET_Z(g->flags) ) - { - gbox->zmin = gbox->zmax = dptr[i++]; - } - if ( FLAGS_GET_M(g->flags) ) - { - gbox->mmin = gbox->mmax = dptr[i++]; - } - gbox_float_round(gbox); - return LW_SUCCESS; + gbox->zmin = gbox->zmax = dptr[i++]; } - /* We can calculate the box of a two-point cartesian line trivially */ - else if ( type == LINETYPE ) + if ( FLAGS_GET_M(g->flags) ) { - int ndims = FLAGS_NDIMS(g->flags); - int i = 0; /* Start past */ - double *dptr = (double*)(g->data); - int *iptr = (int*)(g->data); - int npoints = iptr[1]; /* Read the npoints */ - - /* This only works with 2-point lines */ - if ( npoints != 2 ) - return LW_FAILURE; - - /* Advance to X */ + gbox->mmin = gbox->mmax = dptr[i++]; + } + gbox_float_round(gbox); + return LW_SUCCESS; + } + /* We can calculate the box of a two-point cartesian line trivially */ + else if ( type == LINETYPE ) + { + int ndims = FLAGS_NDIMS(g->flags); + int i = 0; /* Start at */ + double *dptr = (double*)(g->data); + int *iptr = (int*)(g->data); + int npoints = iptr[1]; /* Read the npoints */ + + /* This only works with 2-point lines */ + if ( npoints != 2 ) + return LW_FAILURE; + + /* Advance to X */ + /* Past */ + i++; + gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]); + gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]); + + /* Advance to Y */ + i++; + gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]); + gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]); + + if ( FLAGS_GET_Z(g->flags) ) + { + /* Advance to Z */ i++; - gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]); - gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]); - - /* Advance to Y */ + gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]); + gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]); + } + if ( FLAGS_GET_M(g->flags) ) + { + /* Advance to M */ i++; - gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]); - gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]); - - if ( FLAGS_GET_Z(g->flags) ) - { - /* Advance to Z */ - i++; - gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]); - gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]); - } - if ( FLAGS_GET_M(g->flags) ) - { - /* Advance to M */ - i++; - gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]); - gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]); - } - gbox_float_round(gbox); - return LW_SUCCESS; + gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]); + gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]); + } + gbox_float_round(gbox); + return LW_SUCCESS; + } + /* We can also do single-entry multi-points */ + else if ( type == MULTIPOINTTYPE ) + { + int i = 0; /* Start at */ + double *dptr = (double*)(g->data); + int *iptr = (int*)(g->data); + int ngeoms = iptr[1]; /* Read the ngeoms */ + + /* This only works with single-entry multipoints */ + if ( ngeoms != 1 ) + return LW_FAILURE; + + /* Move forward two doubles (four ints) */ + /* Past */ + /* Past */ + i += 2; + + /* Read the doubles from the one point */ + gbox->xmin = gbox->xmax = dptr[i++]; + gbox->ymin = gbox->ymax = dptr[i++]; + if ( FLAGS_GET_Z(g->flags) ) + { + gbox->zmin = gbox->zmax = dptr[i++]; } - /* We could also do single-entry multi-points */ - else if ( type == MULTIPOINTTYPE ) + if ( FLAGS_GET_M(g->flags) ) { - /* TODO: Make this actually happen */ + gbox->mmin = gbox->mmax = dptr[i++]; + } + gbox_float_round(gbox); + return LW_SUCCESS; + } + /* And we can do single-entry multi-lines with two vertices (!!!) */ + else if ( type == MULTILINETYPE ) + { + int ndims = FLAGS_NDIMS(g->flags); + int i = 0; /* Start at */ + double *dptr = (double*)(g->data); + int *iptr = (int*)(g->data); + int ngeoms = iptr[1]; /* Read the ngeoms */ + int npoints; + + /* This only works with 1-line multilines */ + if ( ngeoms != 1 ) + return LW_FAILURE; + + /* Npoints is at */ + npoints = iptr[3]; + + if ( npoints != 2 ) return LW_FAILURE; + + /* Advance to X */ + /* Move forward two doubles (four ints) */ + /* Past */ + /* Past */ + i += 2; + gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]); + gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]); + + /* Advance to Y */ + i++; + gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]); + gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]); + + if ( FLAGS_GET_Z(g->flags) ) + { + /* Advance to Z */ + i++; + gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]); + gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]); } + if ( FLAGS_GET_M(g->flags) ) + { + /* Advance to M */ + i++; + gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]); + gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]); + } + gbox_float_round(gbox); + return LW_SUCCESS; } + return LW_FAILURE; } - /** * Read the bounding box off a serialization and calculate one if * it is not already there. */ -int gserialized_get_gbox_p(const GSERIALIZED *geom, GBOX *box) +int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *box) { - LWGEOM *lwgeom; - int ret = gserialized_read_gbox_p(geom, box); - if ( LW_FAILURE == ret ) { - /* See http://trac.osgeo.org/postgis/ticket/1023 */ - lwgeom = lwgeom_from_gserialized(geom); - ret = lwgeom_calculate_gbox(lwgeom, box); + /* Try to just read the serialized box. */ + if ( gserialized_read_gbox_p(g, box) == LW_SUCCESS ) + { + return LW_SUCCESS; + } + /* No box? Try to peek into simpler geometries and */ + /* derive a box without creating an lwgeom */ + else if ( gserialized_peek_gbox_p(g, box) == LW_SUCCESS ) + { + return LW_SUCCESS; + } + /* Damn! Nothing for it but to create an lwgeom... */ + /* See http://trac.osgeo.org/postgis/ticket/1023 */ + else + { + LWGEOM *lwgeom = lwgeom_from_gserialized(g); + int ret = lwgeom_calculate_gbox(lwgeom, box); gbox_float_round(box); lwgeom_free(lwgeom); + return ret; } - return ret; } diff --git a/postgis/lwgeom_box.c b/postgis/lwgeom_box.c index 6ecae73b4..2cb98539c 100644 --- a/postgis/lwgeom_box.c +++ b/postgis/lwgeom_box.c @@ -33,6 +33,7 @@ Datum BOX2D_in(PG_FUNCTION_ARGS); Datum BOX2D_out(PG_FUNCTION_ARGS); Datum LWGEOM_to_BOX2D(PG_FUNCTION_ARGS); +Datum LWGEOM_to_BOX2DF(PG_FUNCTION_ARGS); Datum BOX2D_expand(PG_FUNCTION_ARGS); Datum BOX2D_to_BOX3D(PG_FUNCTION_ARGS); Datum BOX2D_combine(PG_FUNCTION_ARGS); @@ -123,9 +124,30 @@ Datum LWGEOM_to_BOX2D(PG_FUNCTION_ARGS) FLAGS_SET_Z(gbox.flags, 0); FLAGS_SET_M(gbox.flags, 0); + PG_FREE_IF_COPY(geom, 0); PG_RETURN_POINTER(gbox_copy(&gbox)); } + +/*convert a GSERIALIZED to BOX2D */ +PG_FUNCTION_INFO_V1(LWGEOM_to_BOX2DF); +Datum LWGEOM_to_BOX2DF(PG_FUNCTION_ARGS) +{ + GSERIALIZED *geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + GBOX gbox; + + if ( gserialized_get_gbox_p(geom, &gbox) == LW_FAILURE ) + PG_RETURN_NULL(); + + /* Strip out higher dimensions */ + FLAGS_SET_Z(gbox.flags, 0); + FLAGS_SET_M(gbox.flags, 0); + + PG_FREE_IF_COPY(geom, 0); + PG_RETURN_POINTER(gbox_copy(&gbox)); +} + + /*---------------------------------------------------------- * Relational operators for BOXes. * <, >, <=, >=, and == are based on box area. diff --git a/postgis/postgis.sql.in b/postgis/postgis.sql.in index 6d0a9e9ea..ec84fa19a 100644 --- a/postgis/postgis.sql.in +++ b/postgis/postgis.sql.in @@ -1018,7 +1018,7 @@ CREATE OR REPLACE FUNCTION ST_expand(box2d,float8) -- Availability: 1.5.0 CREATE OR REPLACE FUNCTION postgis_getbbox(geometry) RETURNS box2d - AS 'MODULE_PATHNAME','LWGEOM_to_BOX2D' + AS 'MODULE_PATHNAME','LWGEOM_to_BOX2DF' LANGUAGE 'c' IMMUTABLE STRICT; -- Availability: 1.2.2 diff --git a/regress/lwgeom_regress.sql b/regress/lwgeom_regress.sql index cd39be5aa..8df265cf7 100644 --- a/regress/lwgeom_regress.sql +++ b/regress/lwgeom_regress.sql @@ -101,3 +101,16 @@ SELECT ST_MemSize(ST_collect(ST_Force3dm(geometry(wkb_ndr)))) from test_data; SELECT ST_MemSize(ST_collect(ST_Force2d(ST_force4d(ST_force3dm(ST_force3dz(ST_force2d(geometry(wkb_ndr)))))))) from test_data; DROP TABLE test_data; + + +SELECT '#3069', ST_Summary(PostGIS_Noop('SRID=4326;POINT(1 1)')); +SELECT '#3069', ST_Summary(PostGIS_Noop('SRID=4326;LINESTRING(1 1,0 0)'::geometry)); +SELECT '#3069', replace(ST_Summary(PostGIS_Noop('SRID=4326;MULTIPOINT(1 1)')),E'\n',' '); +SELECT '#3069', replace(ST_Summary(PostGIS_Noop('SRID=4326;MULTILINESTRING((1 1,0 0))'::geometry)),E'\n',' '); +SELECT '#3069', replace(ST_Summary(PostGIS_Noop('SRID=4326;POLYGON((0 0, 0 1, 1 1 ,1 0,0 0))'::geometry)),E'\n',' '); + +SELECT '#3069', postgis_getbbox('SRID=0;POINT(1 1)'::geometry); +SELECT '#3069', postgis_getbbox('SRID=0;LINESTRING(0 0, 1 1)'::geometry); +SELECT '#3069', postgis_getbbox('SRID=0;MULTILINESTRING((0 0, 1 1))'::geometry); +SELECT '#3069', postgis_getbbox('SRID=0;MULTIPOINT(1 1)'::geometry); +SELECT '#3069', postgis_getbbox('SRID=0;MULTILINESTRING((0 0,1 1))'::geometry); diff --git a/regress/lwgeom_regress_expected b/regress/lwgeom_regress_expected index aafdbafde..fd19fcf12 100644 --- a/regress/lwgeom_regress_expected +++ b/regress/lwgeom_regress_expected @@ -5,3 +5,13 @@ BOX3D(0 0.1 -55,11 12 12) 20464 15824 11184 +#3069|Point[S] +#3069|LineString[BS] with 2 points +#3069|MultiPoint[BS] with 1 elements Point[S] +#3069|MultiLineString[BS] with 1 elements LineString[S] with 2 points +#3069|Polygon[BS] with 1 rings ring 0 has 5 points +#3069|BOX(1 1,1 1) +#3069|BOX(0 0,1 1) +#3069|BOX(0 0,1 1) +#3069|BOX(1 1,1 1) +#3069|BOX(0 0,1 1) diff --git a/regress/tickets_expected b/regress/tickets_expected index a95fc96e5..65f5229b4 100644 --- a/regress/tickets_expected +++ b/regress/tickets_expected @@ -203,9 +203,9 @@ ERROR: AddToPROJ4SRSCache: could not parse proj4 string '' #1398b|POINT(-160.137654 77.091608) #1543|MULTILINESTRING((0 0,10 0,10 10,0 0),(0 0))|POLYGON((0 0,10 10,10 0,0 0)) #1578|f|f -#1580.1|Point[BS] +#1580.1|Point[S] ERROR: transform: couldn't project point (180 90 0): tolerance condition error (-20) -#1580.3|Point[BS] +#1580.3|Point[S] #1596.1|public.road_pg.roads_geom SRID:3395 TYPE:POINT DIMS:2 ERROR: invalid SRID: 330000 not found in spatial_ref_sys #1596.3|3395