]> granicus.if.org Git - postgis/commitdiff
#3069, bounding boxes added to simple objects on deserialization
authorPaul Ramsey <pramsey@cleverelephant.ca>
Wed, 4 Mar 2015 19:47:33 +0000 (19:47 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Wed, 4 Mar 2015 19:47:33 +0000 (19:47 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@13302 b70326c6-7e19-0410-871a-916f4a2858ee

liblwgeom/g_serialized.c
postgis/lwgeom_box.c
postgis/postgis.sql.in
regress/lwgeom_regress.sql
regress/lwgeom_regress_expected
regress/tickets_expected

index fa36da7950957256bc895178ba7d9004d6ded3a7..502bf01b48f40567258036ef1e051036de26557e 100644 (file)
@@ -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 <pointtype><padding> */
+               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 <pointtype><padding> */
-                       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 <linetype><npoints> */
-                       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 <linetype><npoints> */
+               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 <linetype><npoints> */
+               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 <multipointtype><ngeoms> */
+               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 <multipointtype><ngeoms> */
+               /* Past <pointtype><emtpyflat> */
+               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 <multilinetype><ngeoms> */
+               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 <multilinetype><ngeoms><linetype><npoints> */
+               npoints = iptr[3];
+
+               if ( npoints != 2 )
                        return LW_FAILURE;
+
+               /* Advance to X */
+               /* Move forward two doubles (four ints) */
+               /* Past <multilinetype><ngeoms> */
+               /* Past <linetype><npoints> */
+               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;
 }
 
 
index 6ecae73b47a262324ddeb79e39838fe58b938a23..2cb98539c25714daf515a522748119551faa59b8 100644 (file)
@@ -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.
index 6d0a9e9eac5a8e4f499d681e5dca3eb11babb72d..ec84fa19a773ab485e9e680b317618fcd1282824 100644 (file)
@@ -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
index cd39be5aa2bc5046750ce183a730f385fe6f45e9..8df265cf7f8066a30c6140ce0496e215d39f3b22 100644 (file)
@@ -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);
index aafdbafde90c3daa09df34a2f00f209b6c3df270..fd19fcf12ef01e8f9875c17660e732d0de064c60 100644 (file)
@@ -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)
index a95fc96e545b5ee6798a3f2e39d975cb03f9a167..65f5229b4e6e37c44b99e8e1efe5db6aa06bdfe6 100644 (file)
@@ -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