/********************************************************************** * $Id$ * * PostGIS - Spatial Types for PostgreSQL * Copyright 2009 Paul Ramsey * * This is free software; you can redistribute and/or modify it under * the terms of the GNU General Public Licence. See the COPYING file. * **********************************************************************/ #include "libgeom.h" /*********************************************************************** * GSERIALIZED metadata utility functions. */ uint32 gserialized_get_type(const GSERIALIZED *s) { uint32 *ptr; assert(s); ptr = (uint32*)(s->data); ptr += (gbox_serialized_size(s->flags) / sizeof(uint32)); return *ptr; } uint32 gserialized_get_srid(const GSERIALIZED *s) { uint32 srid = 0; srid = srid | (s->srid[0] << 16); srid = srid | (s->srid[1] << 8); srid = srid | s->srid[2]; return srid; } void gserialized_set_srid(GSERIALIZED *s, uint32 srid) { LWDEBUGF(3, "Called with srid = %d", srid); s->srid[0] = (srid & 0x000F0000) >> 16; s->srid[1] = (srid & 0x0000FF00) >> 8; s->srid[2] = (srid & 0x000000FF); } /*********************************************************************** * Calculate the GSERIALIZED size for an LWGEOM. */ /* Private functions */ static size_t gserialized_from_any_size(const LWGEOM *geom); /* Local prototype */ static size_t gserialized_from_lwpoint_size(const LWPOINT *point) { size_t size = 4; /* Type number. */ assert(point); size += 4; /* Number of points (one or zero (empty)). */ size += point->point->npoints * TYPE_NDIMS(point->type) * sizeof(double); LWDEBUGF(3, "point size = %d", size); return size; } static size_t gserialized_from_lwline_size(const LWLINE *line) { size_t size = 4; /* Type number. */ assert(line); size += 4; /* Number of points (zero => empty). */ size += line->points->npoints * TYPE_NDIMS(line->type) * sizeof(double); LWDEBUGF(3, "linestring size = %d", size); return size; } static size_t gserialized_from_lwpoly_size(const LWPOLY *poly) { size_t size = 4; /* Type number. */ int i = 0; assert(poly); size += 4; /* Number of rings (zero => empty). */ if ( poly->nrings % 2 ) size += 4; /* Padding to double alignment. */ for ( i = 0; i < poly->nrings; i++ ) { size += 4; /* Number of points in ring. */ size += poly->rings[i]->npoints * TYPE_NDIMS(poly->type) * sizeof(double); } LWDEBUGF(3, "polygon size = %d", size); return size; } static size_t gserialized_from_lwcircstring_size(const LWCIRCSTRING *curve) { size_t size = 4; /* Type number. */ assert(curve); size += 4; /* Number of points (zero => empty). */ size += curve->points->npoints * TYPE_NDIMS(curve->type) * sizeof(double); LWDEBUGF(3, "circstring size = %d", size); return size; } static size_t gserialized_from_lwcollection_size(const LWCOLLECTION *col) { size_t size = 4; /* Type number. */ int i = 0; assert(col); size += 4; /* Number of sub-geometries (zero => empty). */ for ( i = 0; i < col->ngeoms; i++ ) { size_t subsize = gserialized_from_any_size(col->geoms[i]); size += subsize; LWDEBUGF(3, "lwcollection subgeom(%d) size = %d", i, subsize); } LWDEBUGF(3, "lwcollection size = %d", size); return size; } static size_t gserialized_from_any_size(const LWGEOM *geom) { int type = TYPE_GETTYPE(geom->type); LWDEBUGF(2, "Input type: %s", lwtype_name(type)); switch (type) { case POINTTYPE: return gserialized_from_lwpoint_size((LWPOINT *)geom); case LINETYPE: return gserialized_from_lwline_size((LWLINE *)geom); case POLYGONTYPE: return gserialized_from_lwpoly_size((LWPOLY *)geom); case CIRCSTRINGTYPE: return gserialized_from_lwcircstring_size((LWCIRCSTRING *)geom); case CURVEPOLYTYPE: case COMPOUNDTYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTICURVETYPE: case MULTIPOLYGONTYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case COLLECTIONTYPE: return gserialized_from_lwcollection_size((LWCOLLECTION *)geom); default: lwerror("Unknown geometry type: %d", type); return 0; } } /* Public function */ size_t gserialized_from_lwgeom_size(const LWGEOM *geom) { size_t size = 8; /* Header overhead. */ assert(geom); size += gserialized_from_any_size(geom); LWDEBUGF(3, "g_serialize size = %d", size); return size; } /*********************************************************************** * Serialize an LWGEOM into GSERIALIZED. */ /* Private functions */ static size_t gserialized_from_lwgeom_any(const LWGEOM *geom, uchar *buf); static size_t gserialized_from_lwpoint(const LWPOINT *point, uchar *buf) { uchar *loc; int ptsize = pointArray_ptsize(point->point); int type = POINTTYPE; assert(point); assert(buf); if ( TYPE_GETZM(point->type) != TYPE_GETZM(point->point->dims) ) lwerror("Dimensions mismatch in lwpoint"); LWDEBUGF(2, "lwpoint_to_gserialized(%p, %p) called", point, buf); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32)); loc += sizeof(uint32); /* Write in the number of points (0 => empty). */ memcpy(loc, &(point->point->npoints), sizeof(uint32)); loc += sizeof(uint32); /* Copy in the ordinates. */ if ( point->point->npoints > 0 ) { memcpy(loc, getPoint_internal(point->point, 0), ptsize); loc += ptsize; } return (size_t)(loc - buf); } static size_t gserialized_from_lwline(const LWLINE *line, uchar *buf) { uchar *loc; int ptsize; size_t size; int type = LINETYPE; assert(line); assert(buf); LWDEBUGF(2, "lwline_to_gserialized(%p, %p) called", line, buf); if ( TYPE_GETZM(line->type) != TYPE_GETZM(line->points->dims) ) lwerror("Dimensions mismatch in lwline"); ptsize = pointArray_ptsize(line->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32)); loc += sizeof(uint32); /* Write in the npoints. */ memcpy(loc, &(line->points->npoints), sizeof(uint32)); loc += sizeof(uint32); LWDEBUGF(3, "lwline_to_gserialized added npoints (%d)", line->points->npoints); /* Copy in the ordinates. */ if ( line->points->npoints > 0 ) { size = line->points->npoints * ptsize; memcpy(loc, getPoint_internal(line->points, 0), size); loc += size; } LWDEBUGF(3, "lwline_to_gserialized copied serialized_pointlist (%d bytes)", ptsize * line->points->npoints); return (size_t)(loc - buf); } static size_t gserialized_from_lwpoly(const LWPOLY *poly, uchar *buf) { int i; uchar *loc; int ptsize; int type = POLYGONTYPE; assert(poly); assert(buf); LWDEBUG(2, "lwpoly_to_gserialized called"); ptsize = sizeof(double) * TYPE_NDIMS(poly->type); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32)); loc += sizeof(uint32); /* Write in the nrings. */ memcpy(loc, &(poly->nrings), sizeof(uint32)); loc += sizeof(uint32); /* Write in the npoints per ring. */ for ( i = 0; i < poly->nrings; i++ ) { memcpy(loc, &(poly->rings[i]->npoints), sizeof(uint32)); loc += sizeof(uint32); } /* Add in padding if necessary to remain double aligned. */ if ( poly->nrings % 2 ) { loc += sizeof(uint32); } /* Copy in the ordinates. */ for ( i = 0; i < poly->nrings; i++ ) { POINTARRAY *pa = poly->rings[i]; size_t pasize; if ( TYPE_GETZM(poly->type) != TYPE_GETZM(pa->dims) ) lwerror("Dimensions mismatch in lwpoly"); pasize = pa->npoints * ptsize; memcpy(loc, getPoint_internal(pa, 0), pasize); loc += pasize; } return (size_t)(loc - buf); } static size_t gserialized_from_lwcircstring(const LWCIRCSTRING *curve, uchar *buf) { uchar *loc; int ptsize; size_t size; int type = CIRCSTRINGTYPE; assert(curve); assert(buf); if (TYPE_GETZM(curve->type) != TYPE_GETZM(curve->points->dims)) lwerror("Dimensions mismatch in lwcircstring"); ptsize = pointArray_ptsize(curve->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32)); loc += sizeof(uint32); /* Write in the npoints. */ memcpy(loc, &curve->points->npoints, sizeof(uint32)); loc += sizeof(uint32); /* Copy in the ordinates. */ if ( curve->points->npoints > 0 ) { size = curve->points->npoints * ptsize; memcpy(loc, getPoint_internal(curve->points, 0), size); loc += size; } return (size_t)(loc - buf); } static size_t gserialized_from_lwcollection(const LWCOLLECTION *coll, uchar *buf) { size_t subsize = 0; uchar *loc; int i; int type; assert(coll); assert(buf); type = TYPE_GETTYPE(coll->type); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32)); loc += sizeof(uint32); /* Write in the number of subgeoms. */ memcpy(loc, &coll->ngeoms, sizeof(uint32)); loc += sizeof(uint32); /* Serialize subgeoms. */ for ( i=0; ingeoms; i++ ) { if (TYPE_GETZM(coll->type) != TYPE_GETZM(coll->geoms[i]->type)) lwerror("Dimensions mismatch in lwcollection"); subsize = gserialized_from_lwgeom_any(coll->geoms[i], loc); loc += subsize; } return (size_t)(loc - buf); } static size_t gserialized_from_lwgeom_any(const LWGEOM *geom, uchar *buf) { int type = 0; assert(geom); assert(buf); type = TYPE_GETTYPE(geom->type); LWDEBUGF(2, "Input type (%d) %s", type, lwtype_name(type)); LWDEBUGF(2, "LWGEOM(%p) uchar(%p)", geom, buf); switch (type) { case POINTTYPE: return gserialized_from_lwpoint((LWPOINT *)geom, buf); case LINETYPE: return gserialized_from_lwline((LWLINE *)geom, buf); case POLYGONTYPE: return gserialized_from_lwpoly((LWPOLY *)geom, buf); case CIRCSTRINGTYPE: return gserialized_from_lwcircstring((LWCIRCSTRING *)geom, buf); case CURVEPOLYTYPE: case COMPOUNDTYPE: case MULTIPOINTTYPE: case MULTILINETYPE: case MULTICURVETYPE: case MULTIPOLYGONTYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case COLLECTIONTYPE: return gserialized_from_lwcollection((LWCOLLECTION *)geom, buf); default: lwerror("Unknown geometry type: %d", geom->type); return 0; } return 0; } static size_t gserialized_from_gbox(const GBOX *gbox, uchar *buf) { uchar *loc; float f; size_t return_size; assert(buf); loc = buf; f = next_float_down(gbox->xmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->xmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_down(gbox->ymin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->ymax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); if ( FLAGS_GET_GEODETIC(gbox->flags) ) { f = next_float_down(gbox->zmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->zmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); return_size = (size_t)(loc - buf); LWDEBUGF(4, "returning size %d", return_size); return return_size; } if ( FLAGS_GET_Z(gbox->flags) ) { f = next_float_down(gbox->zmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->zmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); } if ( FLAGS_GET_M(gbox->flags) ) { f = next_float_down(gbox->mmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(gbox->mmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); } return_size = (size_t)(loc - buf); LWDEBUGF(4, "returning size %d", return_size); return return_size; } /* Public function */ GSERIALIZED* gserialized_from_lwgeom(const LWGEOM *geom, int is_geodetic, size_t *size) { size_t expected_box_size = 0; size_t expected_size = 0; size_t return_size = 0; uchar *serialized = NULL; uchar *ptr = NULL; GSERIALIZED *g = NULL; GBOX gbox; assert(geom); gbox.flags = gflags(TYPE_HASZ(geom->type), TYPE_HASM(geom->type), is_geodetic); /* ** We need room for a bounding box in the serialized form. ** Calculate the box and allocate enough size for it. */ if ( ! lwgeom_is_empty(geom) && lwgeom_needs_bbox(geom) ) { int result = G_SUCCESS; LWDEBUG(3, "calculating bbox"); if ( is_geodetic ) result = lwgeom_calculate_gbox_geodetic(geom, &gbox); else result = lwgeom_calculate_gbox(geom, &gbox); if ( result == G_SUCCESS ) { FLAGS_SET_BBOX(gbox.flags, 1); expected_box_size = gbox_serialized_size(gbox.flags); } } /* Set up the uchar buffer into which we are going to write the serialized geometry. */ expected_size = gserialized_from_lwgeom_size(geom) + expected_box_size; serialized = lwalloc(expected_size); ptr = serialized; /* Move past size, srid and flags. */ ptr += 8; /* Write in the serialized form of the gbox, if necessary. */ if ( FLAGS_GET_BBOX(gbox.flags) ) ptr += gserialized_from_gbox(&gbox, ptr); /* Write in the serialized form of the geometry. */ ptr += gserialized_from_lwgeom_any(geom, ptr); /* Calculate size as returned by data processing functions. */ return_size = ptr - serialized; if ( expected_size != return_size ) /* Uh oh! */ { lwerror("Return size (%d) not equal to expected size (%d)!", return_size, expected_size); return NULL; } if ( size ) /* Return the output size to the caller if necessary. */ *size = return_size; g = (GSERIALIZED*)serialized; /* ** We are aping PgSQL code here, PostGIS code should use ** VARSIZE to set this for real. */ g->size = return_size << 2; if ( geom->SRID == 0 || geom->SRID == (uint32)(-1) ) /* Zero is the no-SRID value now. */ gserialized_set_srid(g, 0); else gserialized_set_srid(g, geom->SRID); g->flags = gbox.flags; return g; } /*********************************************************************** * De-serialize GSERIALIZED into an LWGEOM. */ static LWGEOM* lwgeom_from_gserialized_buffer(uchar *data_ptr, uchar g_flags, size_t *g_size); static LWPOINT* lwpoint_from_gserialized_buffer(uchar *data_ptr, uchar g_flags, size_t *g_size) { static uint32 type = POINTTYPE; uchar *start_ptr = data_ptr; LWPOINT *point; uint32 npoints = 0; assert(data_ptr); point = (LWPOINT*)lwalloc(sizeof(LWPOINT)); point->SRID = -1; /* Default */ point->bbox = NULL; point->type = lwgeom_makeType_full(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), 0, type, 0); data_ptr += 4; /* Skip past the type. */ npoints = lw_get_uint32(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) point->point = pointArray_construct(data_ptr, FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), 1); else point->point = ptarray_construct(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), 0); /* Empty point */ data_ptr += npoints * FLAGS_NDIMS(g_flags) * sizeof(double); if ( g_size ) *g_size = data_ptr - start_ptr; return point; } static LWLINE* lwline_from_gserialized_buffer(uchar *data_ptr, uchar g_flags, size_t *g_size) { static uint32 type = LINETYPE; uchar *start_ptr = data_ptr; LWLINE *line; uint32 npoints = 0; assert(data_ptr); line = (LWLINE*)lwalloc(sizeof(LWLINE)); line->SRID = -1; /* Default */ line->bbox = NULL; line->type = lwgeom_makeType_full(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), 0, type, 0); data_ptr += 4; /* Skip past the type. */ npoints = lw_get_uint32(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) line->points = pointArray_construct(data_ptr, FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), npoints); else line->points = ptarray_construct(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), 0); /* Empty linestring */ data_ptr += FLAGS_NDIMS(g_flags) * npoints * sizeof(double); if ( g_size ) *g_size = data_ptr - start_ptr; return line; } static LWPOLY* lwpoly_from_gserialized_buffer(uchar *data_ptr, uchar g_flags, size_t *g_size) { static uint32 type = POLYGONTYPE; uchar *start_ptr = data_ptr; LWPOLY *poly; uchar *ordinate_ptr; uint32 nrings = 0; int i = 0; assert(data_ptr); poly = (LWPOLY*)lwalloc(sizeof(LWPOLY)); poly->SRID = -1; /* Default */ poly->bbox = NULL; poly->type = lwgeom_makeType_full(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), 0, type, 0); data_ptr += 4; /* Skip past the polygontype. */ nrings = lw_get_uint32(data_ptr); /* Zero => empty geometry */ poly->nrings = nrings; LWDEBUGF(4, "nrings = %d", nrings); data_ptr += 4; /* Skip past the nrings. */ ordinate_ptr = data_ptr; /* Start the ordinate pointer. */ if ( nrings > 0) { poly->rings = (POINTARRAY**)lwalloc( sizeof(POINTARRAY*) * nrings ); ordinate_ptr += nrings * 4; /* Move past all the npoints values. */ if ( nrings % 2 ) /* If there is padding, move past that too. */ ordinate_ptr += 4; } else /* Empty polygon */ { poly->rings = NULL; } for ( i = 0; i < nrings; i++ ) { uint32 npoints = 0; /* Read in the number of points. */ npoints = lw_get_uint32(data_ptr); data_ptr += 4; /* Make a point array for the ring, and move the ordinate pointer past the ring ordinates. */ poly->rings[i] = pointArray_construct(ordinate_ptr, FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), npoints); ordinate_ptr += sizeof(double) * FLAGS_NDIMS(g_flags) * npoints; } if ( g_size ) *g_size = ordinate_ptr - start_ptr; return poly; } static LWCIRCSTRING* lwcircstring_from_gserialized_buffer(uchar *data_ptr, uchar g_flags, size_t *g_size) { static uint32 type = CIRCSTRINGTYPE; uchar *start_ptr = data_ptr; LWCIRCSTRING *circstring; uint32 npoints = 0; assert(data_ptr); circstring = (LWCIRCSTRING*)lwalloc(sizeof(LWCIRCSTRING)); circstring->SRID = -1; /* Default */ circstring->bbox = NULL; circstring->type = lwgeom_makeType_full(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), 0, type, 0); data_ptr += 4; /* Skip past the circstringtype. */ npoints = lw_get_uint32(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) circstring->points = pointArray_construct(data_ptr, FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), npoints); else circstring->points = ptarray_construct(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), 0); /* Empty circularstring */ data_ptr += FLAGS_NDIMS(g_flags) * npoints * sizeof(double); if ( g_size ) *g_size = data_ptr - start_ptr; return circstring; } static int lwcollection_from_gserialized_allowed_types(int collectiontype, int subtype) { if ( collectiontype == COLLECTIONTYPE ) return LW_TRUE; if ( collectiontype == MULTIPOINTTYPE && subtype == POINTTYPE ) return LW_TRUE; if ( collectiontype == MULTILINETYPE && subtype == LINETYPE ) return LW_TRUE; if ( collectiontype == MULTIPOLYGONTYPE && subtype == POLYGONTYPE ) return LW_TRUE; if ( collectiontype == COMPOUNDTYPE && (subtype == LINETYPE || subtype == CIRCSTRINGTYPE) ) return LW_TRUE; if ( collectiontype == CURVEPOLYTYPE && (subtype == CIRCSTRINGTYPE || subtype == LINETYPE || subtype == COMPOUNDTYPE) ) return LW_TRUE; if ( collectiontype == MULTICURVETYPE && (subtype == CIRCSTRINGTYPE || subtype == LINETYPE || subtype == COMPOUNDTYPE) ) return LW_TRUE; if ( collectiontype == MULTISURFACETYPE && (subtype == POLYGONTYPE || subtype == CURVEPOLYTYPE) ) return LW_TRUE; if ( collectiontype == POLYHEDRALSURFACETYPE && subtype == POLYGONTYPE ) return LW_TRUE; /* Must be a bad combination! */ return LW_FALSE; } static LWCOLLECTION* lwcollection_from_gserialized_buffer(uchar *data_ptr, uchar g_flags, size_t *g_size) { uint32 type; uchar *start_ptr = data_ptr; LWCOLLECTION *collection; uint32 ngeoms = 0; int i = 0; assert(data_ptr); type = lw_get_uint32(data_ptr); data_ptr += 4; /* Skip past the type. */ collection = (LWCOLLECTION*)lwalloc(sizeof(LWCOLLECTION)); collection->SRID = -1; /* Default */ collection->bbox = NULL; collection->type = lwgeom_makeType_full(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), 0, type, 0); ngeoms = lw_get_uint32(data_ptr); collection->ngeoms = ngeoms; /* Zero => empty geometry */ data_ptr += 4; /* Skip past the ngeoms. */ if ( ngeoms > 0 ) collection->geoms = lwalloc(sizeof(LWGEOM*) * ngeoms); else collection->geoms = NULL; for ( i = 0; i < ngeoms; i++ ) { uint32 subtype = lw_get_uint32(data_ptr); size_t subsize = 0; if ( ! lwcollection_from_gserialized_allowed_types(type, subtype) ) { lwerror("Invalid subtype (%s) for collection type (%s)", lwtype_name(subtype), lwtype_name(type)); lwfree(collection); return NULL; } collection->geoms[i] = lwgeom_from_gserialized_buffer(data_ptr, g_flags, &subsize); data_ptr += subsize; } if ( g_size ) *g_size = data_ptr - start_ptr; return collection; } LWGEOM* lwgeom_from_gserialized_buffer(uchar *data_ptr, uchar g_flags, size_t *g_size) { uint32 type; assert(data_ptr); type = lw_get_uint32(data_ptr); LWDEBUGF(2, "Got type %d (%s)", type, lwtype_name(type)); switch (type) { case POINTTYPE: return (LWGEOM *)lwpoint_from_gserialized_buffer(data_ptr, g_flags, g_size); case LINETYPE: return (LWGEOM *)lwline_from_gserialized_buffer(data_ptr, g_flags, g_size); case CIRCSTRINGTYPE: return (LWGEOM *)lwcircstring_from_gserialized_buffer(data_ptr, g_flags, g_size); case POLYGONTYPE: return (LWGEOM *)lwpoly_from_gserialized_buffer(data_ptr, g_flags, g_size); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case COLLECTIONTYPE: return (LWGEOM *)lwcollection_from_gserialized_buffer(data_ptr, g_flags, g_size); default: lwerror("Unknown geometry type: %d", type); return NULL; } } LWGEOM* lwgeom_from_gserialized(const GSERIALIZED *g) { uchar g_flags = 0; uchar has_srid = 0; uchar *data_ptr = NULL; uint32 g_srid = 0; uint32 g_type = 0; LWGEOM *lwgeom = NULL; size_t g_size = 0; assert(g); g_srid = gserialized_get_srid(g); LWDEBUGF(4, "Got srid %d", g_srid); g_flags = g->flags; if ( g_srid > 0 ) has_srid = 1; g_type = gserialized_get_type(g); LWDEBUGF(4, "Got type %d", g_type); data_ptr = (uchar*)g->data; if ( FLAGS_GET_BBOX(g_flags) ) data_ptr += gbox_serialized_size(g_flags); lwgeom = lwgeom_from_gserialized_buffer(data_ptr, g_flags, &g_size); if ( ! lwgeom ) return NULL; /* Ooops! */ lwgeom->type = lwgeom_makeType_full(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), has_srid, g_type, FLAGS_GET_BBOX(g_flags)); if ( FLAGS_GET_BBOX(g_flags) && ! FLAGS_GET_GEODETIC(g_flags) ) { float *fptr = (float*)g->data; BOX2DFLOAT4 *bbox = lwalloc(sizeof(BOX2DFLOAT4)); bbox->xmin = fptr[0]; bbox->xmax = fptr[1]; bbox->ymin = fptr[2]; bbox->ymax = fptr[3]; lwgeom->bbox = bbox; } else { lwgeom->bbox = NULL; } if ( has_srid ) lwgeom->SRID = g_srid; else lwgeom->SRID = -1; return lwgeom; } /*********************************************************************** * Calculate geocentric bounding box from geodetic coordinates * of GSERIALIZED. To be used in index calculations to get the box * of smaller features on the fly, and in feature creation, to * calculate the box that will be added to the feature. */ static int gserialized_calculate_gbox_geocentric_from_any(uchar *data_ptr, size_t *g_size, GBOX *gbox); static int gserialized_calculate_gbox_geocentric_from_point(uchar *data_ptr, size_t *g_size, GBOX *gbox) { uchar *start_ptr = data_ptr; int npoints = 0; POINTARRAY *pa; assert(data_ptr); data_ptr += 4; /* Move past type integer. */ npoints = lw_get_uint32(data_ptr); data_ptr += 4; /* Move past npoints. */ if ( npoints == 0 ) /* Empty point */ { if (g_size) *g_size = data_ptr - start_ptr; return G_FAILURE; } pa = pointArray_construct(data_ptr, FLAGS_GET_Z(gbox->flags), FLAGS_GET_M(gbox->flags), npoints); if ( ptarray_calculate_gbox_geodetic(pa, gbox) == G_FAILURE ) return G_FAILURE; /* Move past all the double ordinates. */ data_ptr += sizeof(double) * FLAGS_NDIMS(gbox->flags); if (g_size) *g_size = data_ptr - start_ptr; lwfree(pa); return G_SUCCESS; } static int gserialized_calculate_gbox_geocentric_from_line(uchar *data_ptr, size_t *g_size, GBOX *gbox) { uchar *start_ptr = data_ptr; int npoints = 0; POINTARRAY *pa; assert(data_ptr); data_ptr += 4; /* Move past type integer. */ npoints = lw_get_uint32(data_ptr); data_ptr += 4; /* Move past npoints. */ if ( npoints == 0 ) /* Empty linestring */ { if (g_size) *g_size = data_ptr - start_ptr; return G_FAILURE; } pa = pointArray_construct(data_ptr, FLAGS_GET_Z(gbox->flags), FLAGS_GET_M(gbox->flags), npoints); if ( ptarray_calculate_gbox_geodetic(pa, gbox) == G_FAILURE ) return G_FAILURE; /* Move past all the double ordinates. */ data_ptr += sizeof(double) * FLAGS_NDIMS(gbox->flags) * npoints; if (g_size) *g_size = data_ptr - start_ptr; lwfree(pa); return G_SUCCESS; } static int gserialized_calculate_gbox_geocentric_from_polygon(uchar *data_ptr, size_t *g_size, GBOX *gbox) { uchar *start_ptr = data_ptr; int npoints0 = 0; /* Points in exterior ring. */ int npoints = 0; /* Points in all rings. */ int nrings = 0; POINTARRAY *pa; int i; assert(data_ptr); data_ptr += 4; /* Move past type integer. */ nrings = lw_get_uint32(data_ptr); data_ptr += 4; /* Move past nrings. */ if ( nrings <= 0 ) { if (g_size) *g_size = data_ptr - start_ptr; return G_FAILURE; /* Empty polygon */ } npoints0 = lw_get_uint32(data_ptr); /* NPoints in first (exterior) ring. */ for ( i = 0; i < nrings; i++ ) { npoints += lw_get_uint32(data_ptr); data_ptr += 4; /* Move past this npoints value. */ } if ( nrings % 2 ) /* Move past optional padding. */ data_ptr += 4; pa = pointArray_construct(data_ptr, FLAGS_GET_Z(gbox->flags), FLAGS_GET_M(gbox->flags), npoints); /* Bounds of exterior ring is bounds of whole polygon. */ if ( ptarray_calculate_gbox_geodetic(pa, gbox) == G_FAILURE ) return G_FAILURE; /* Move past all the double ordinates. */ data_ptr += sizeof(double) * FLAGS_NDIMS(gbox->flags) * npoints; if (g_size) *g_size = data_ptr - start_ptr; lwfree(pa); return G_SUCCESS; } static int gserialized_calculate_gbox_geocentric_from_collection(uchar *data_ptr, size_t *g_size, GBOX *gbox) { uchar *start_ptr = data_ptr; int ngeoms = 0; int i; int first = LW_TRUE; int result = G_FAILURE; assert(data_ptr); data_ptr += 4; /* Move past type integer. */ ngeoms = lw_get_uint32(data_ptr); data_ptr += 4; /* Move past ngeoms. */ if ( ngeoms <= 0 ) return G_FAILURE; /* Empty collection */ for ( i = 0; i < ngeoms; i++ ) { size_t subgeom_size = 0; GBOX subbox; subbox.flags = gbox->flags; if ( gserialized_calculate_gbox_geocentric_from_any(data_ptr, &subgeom_size, &subbox) != G_FAILURE ) { if ( first ) { gbox_duplicate(&subbox, gbox); first = LW_FALSE; } else { gbox_merge(&subbox, gbox); } result = G_SUCCESS; } data_ptr += subgeom_size; } if (g_size) *g_size = data_ptr - start_ptr; return result; } static int gserialized_calculate_gbox_geocentric_from_any(uchar *data_ptr, size_t *g_size, GBOX *gbox) { uint32 type; assert(data_ptr); type = lw_get_uint32(data_ptr); LWDEBUGF(2, "Got type %d (%s)", type, lwtype_name(type)); LWDEBUGF(3, "Got gbox pointer (%p)", gbox); switch (type) { case POINTTYPE: return gserialized_calculate_gbox_geocentric_from_point(data_ptr, g_size, gbox); case LINETYPE: return gserialized_calculate_gbox_geocentric_from_line(data_ptr, g_size, gbox); case POLYGONTYPE: return gserialized_calculate_gbox_geocentric_from_polygon(data_ptr, g_size, gbox); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case COLLECTIONTYPE: return gserialized_calculate_gbox_geocentric_from_collection(data_ptr, g_size, gbox); default: lwerror("Unsupported geometry type: %d", type); return G_FAILURE; } } int gserialized_calculate_gbox_geocentric_p(const GSERIALIZED *g, GBOX *g_box) { uchar *data_ptr = NULL; size_t g_size = 0; int result = G_SUCCESS; assert(g); /* This function only works for geodetics. */ if ( ! FLAGS_GET_GEODETIC(g->flags) ) { lwerror("Function only accepts geodetic inputs."); return G_FAILURE; } LWDEBUGF(4, "Got input %s", gserialized_to_string(g)); data_ptr = (uchar*)g->data; g_box->flags = g->flags; /* If the serialized form already has a box, skip past it. */ if ( FLAGS_GET_BBOX(g->flags) ) { int ndims = FLAGS_GET_GEODETIC(g->flags) ? 3 : FLAGS_NDIMS(g->flags); data_ptr += 2 * ndims * sizeof(float); /* Copy the bounding box and return. */ LWDEBUG(3,"Serialized form has box already, skipping past..."); } LWDEBUG(3,"Calculating box..."); /* Calculate the bounding box from the geometry. */ result = gserialized_calculate_gbox_geocentric_from_any(data_ptr, &g_size, g_box); if ( result == G_FAILURE ) { LWDEBUG(3,"Unable to calculate geocentric bounding box."); return G_FAILURE; /* Ooops! */ } LWDEBUGF(3,"Returning box: %s", gbox_to_string(g_box)); return result; } GBOX* gserialized_calculate_gbox_geocentric(const GSERIALIZED *g) { GBOX g_box; int result = G_SUCCESS; result = gserialized_calculate_gbox_geocentric_p(g, &g_box); if ( result == G_FAILURE ) return NULL; /* Ooops! */ LWDEBUGF(3,"Returning box: %s", gbox_to_string(&g_box)); return gbox_copy(&g_box); } GSERIALIZED* gserialized_copy(const GSERIALIZED *g) { GSERIALIZED *g_out = NULL; assert(g); g_out = (GSERIALIZED*)lwalloc(SIZE_GET(g->size)); memcpy((uchar*)g_out,(uchar*)g,SIZE_GET(g->size)); return g_out; } char* gserialized_to_string(const GSERIALIZED *g) { LWGEOM_UNPARSER_RESULT lwg_unparser_result; LWGEOM *lwgeom = lwgeom_from_gserialized(g); uchar *serialized_lwgeom; int result; assert(g); if ( ! lwgeom ) { lwerror("Unable to create lwgeom from gserialized"); return NULL; } serialized_lwgeom = lwgeom_serialize(lwgeom); lwgeom_release(lwgeom); result = serialized_lwgeom_to_ewkt(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_NONE); lwfree(serialized_lwgeom); return lwg_unparser_result.wkoutput; }