/********************************************************************** * $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 "liblwgeom_internal.h" #include "lwgeom_log.h" /*********************************************************************** * GSERIALIZED metadata utility functions. */ int gserialized_has_bbox(const GSERIALIZED *gser) { return FLAGS_GET_BBOX(gser->flags); } int gserialized_has_z(const GSERIALIZED *gser) { return FLAGS_GET_Z(gser->flags); } int gserialized_has_m(const GSERIALIZED *gser) { return FLAGS_GET_M(gser->flags); } int gserialized_get_zm(const GSERIALIZED *gser) { return 2 * FLAGS_GET_Z(gser->flags) + FLAGS_GET_M(gser->flags); } int gserialized_ndims(const GSERIALIZED *gser) { return FLAGS_NDIMS(gser->flags); } uint32_t gserialized_get_type(const GSERIALIZED *s) { uint32_t *ptr; assert(s); ptr = (uint32_t*)(s->data); LWDEBUG(4,"entered"); if ( FLAGS_GET_BBOX(s->flags) ) { LWDEBUGF(4,"skipping forward past bbox (%d bytes)",gbox_serialized_size(s->flags)); ptr += (gbox_serialized_size(s->flags) / sizeof(uint32_t)); } return *ptr; } int32_t gserialized_get_srid(const GSERIALIZED *s) { int32_t srid = 0; srid = srid | (s->srid[0] << 16); srid = srid | (s->srid[1] << 8); srid = srid | s->srid[2]; /* Only the first 21 bits are set. Slide up and back to pull the negative bits down, if we need them. */ srid = (srid<<11)>>11; /* 0 is our internal unknown value. We'll map back and forth here for now */ if ( srid == 0 ) return SRID_UNKNOWN; else return clamp_srid(srid); } void gserialized_set_srid(GSERIALIZED *s, int32_t srid) { LWDEBUGF(3, "Called with srid = %d", srid); srid = clamp_srid(srid); /* 0 is our internal unknown value. * We'll map back and forth here for now */ if ( srid == SRID_UNKNOWN ) srid = 0; s->srid[0] = (srid & 0x001F0000) >> 16; s->srid[1] = (srid & 0x0000FF00) >> 8; s->srid[2] = (srid & 0x000000FF); } GSERIALIZED* gserialized_copy(const GSERIALIZED *g) { GSERIALIZED *g_out = NULL; assert(g); g_out = (GSERIALIZED*)lwalloc(SIZE_GET(g->size)); memcpy((uint8_t*)g_out,(uint8_t*)g,SIZE_GET(g->size)); return g_out; } int gserialized_is_empty(const GSERIALIZED *g) { uint8_t *p = (uint8_t*)g; int i; assert(g); p += 8; /* Skip varhdr and srid/flags */ if( FLAGS_GET_BBOX(g->flags) ) p += gbox_serialized_size(g->flags); /* Skip the box */ p += 4; /* Skip type number */ /* For point/line/circstring this is npoints */ /* For polygons this is nrings */ /* For collections this is ngeoms */ memcpy(&i, p, sizeof(int)); /* If it is non-zero, it's not empty */ if ( i > 0 ) return LW_FALSE; else return LW_TRUE; } char* gserialized_to_string(const GSERIALIZED *g) { return lwgeom_to_wkt(lwgeom_from_gserialized(g), WKT_ISO, 12, 0); } int gserialized_read_gbox_p(const GSERIALIZED *g, GBOX *gbox) { /* Null input! */ if ( ! ( g && gbox ) ) return LW_FAILURE; /* Initialize the flags on the box */ gbox->flags = g->flags; /* Has pre-calculated box */ if ( FLAGS_GET_BBOX(g->flags) ) { int i = 0; float *fbox = (float*)(g->data); gbox->xmin = fbox[i++]; gbox->xmax = fbox[i++]; gbox->ymin = fbox[i++]; gbox->ymax = fbox[i++]; /* Geodetic? Read next dimension (geocentric Z) and return */ if ( FLAGS_GET_GEODETIC(g->flags) ) { gbox->zmin = fbox[i++]; gbox->zmax = fbox[i++]; return LW_SUCCESS; } /* Cartesian? Read extra dimensions (if there) and return */ if ( FLAGS_GET_Z(g->flags) ) { gbox->zmin = fbox[i++]; gbox->zmax = fbox[i++]; } if ( FLAGS_GET_M(g->flags) ) { gbox->mmin = fbox[i++]; gbox->mmax = fbox[i++]; } return LW_SUCCESS; } /* No pre-calculated box, but for cartesian entries we can do some magic */ if ( ! FLAGS_GET_GEODETIC(g->flags) ) { 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) ) { 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; } /* 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 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 */ 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->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->zmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]); } gbox_float_round(gbox); return LW_SUCCESS; } /* We could also do single-entry multi-points */ else if ( type == MULTIPOINTTYPE ) { /* TODO: Make this actually happen */ return LW_FAILURE; } } 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) { 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); gbox_float_round(box); lwgeom_free(lwgeom); } return ret; } /*********************************************************************** * 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 * FLAGS_NDIMS(point->flags) * 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 * FLAGS_NDIMS(line->flags) * sizeof(double); LWDEBUGF(3, "linestring size = %d", size); return size; } static size_t gserialized_from_lwtriangle_size(const LWTRIANGLE *triangle) { size_t size = 4; /* Type number. */ assert(triangle); size += 4; /* Number of points (zero => empty). */ size += triangle->points->npoints * FLAGS_NDIMS(triangle->flags) * sizeof(double); LWDEBUGF(3, "triangle 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 * FLAGS_NDIMS(poly->flags) * 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 * FLAGS_NDIMS(curve->flags) * 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) { LWDEBUGF(2, "Input type: %s", lwtype_name(geom->type)); switch (geom->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 TRIANGLETYPE: return gserialized_from_lwtriangle_size((LWTRIANGLE *)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 TINTYPE: case COLLECTIONTYPE: return gserialized_from_lwcollection_size((LWCOLLECTION *)geom); default: lwerror("Unknown geometry type: %d - %s", geom->type, lwtype_name(geom->type)); return 0; } } /* Public function */ size_t gserialized_from_lwgeom_size(const LWGEOM *geom) { size_t size = 8; /* Header overhead. */ assert(geom); if ( geom->bbox ) size += gbox_serialized_size(geom->flags); 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, uint8_t *buf); static size_t gserialized_from_lwpoint(const LWPOINT *point, uint8_t *buf) { uint8_t *loc; int ptsize = ptarray_point_size(point->point); int type = POINTTYPE; assert(point); assert(buf); if ( FLAGS_GET_ZM(point->flags) != FLAGS_GET_ZM(point->point->flags) ) 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_t)); loc += sizeof(uint32_t); /* Write in the number of points (0 => empty). */ memcpy(loc, &(point->point->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); /* 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, uint8_t *buf) { uint8_t *loc; int ptsize; size_t size; int type = LINETYPE; assert(line); assert(buf); LWDEBUGF(2, "lwline_to_gserialized(%p, %p) called", line, buf); if ( FLAGS_GET_Z(line->flags) != FLAGS_GET_Z(line->points->flags) ) lwerror("Dimensions mismatch in lwline"); ptsize = ptarray_point_size(line->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints. */ memcpy(loc, &(line->points->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); 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, uint8_t *buf) { int i; uint8_t *loc; int ptsize; int type = POLYGONTYPE; assert(poly); assert(buf); LWDEBUG(2, "lwpoly_to_gserialized called"); ptsize = sizeof(double) * FLAGS_NDIMS(poly->flags); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the nrings. */ memcpy(loc, &(poly->nrings), sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints per ring. */ for ( i = 0; i < poly->nrings; i++ ) { memcpy(loc, &(poly->rings[i]->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); } /* Add in padding if necessary to remain double aligned. */ if ( poly->nrings % 2 ) { memset(loc, 0, sizeof(uint32_t)); loc += sizeof(uint32_t); } /* Copy in the ordinates. */ for ( i = 0; i < poly->nrings; i++ ) { POINTARRAY *pa = poly->rings[i]; size_t pasize; if ( FLAGS_GET_ZM(poly->flags) != FLAGS_GET_ZM(pa->flags) ) 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_lwtriangle(const LWTRIANGLE *triangle, uint8_t *buf) { uint8_t *loc; int ptsize; size_t size; int type = TRIANGLETYPE; assert(triangle); assert(buf); LWDEBUGF(2, "lwtriangle_to_gserialized(%p, %p) called", triangle, buf); if ( FLAGS_GET_ZM(triangle->flags) != FLAGS_GET_ZM(triangle->points->flags) ) lwerror("Dimensions mismatch in lwtriangle"); ptsize = ptarray_point_size(triangle->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints. */ memcpy(loc, &(triangle->points->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); LWDEBUGF(3, "lwtriangle_to_gserialized added npoints (%d)", triangle->points->npoints); /* Copy in the ordinates. */ if ( triangle->points->npoints > 0 ) { size = triangle->points->npoints * ptsize; memcpy(loc, getPoint_internal(triangle->points, 0), size); loc += size; } LWDEBUGF(3, "lwtriangle_to_gserialized copied serialized_pointlist (%d bytes)", ptsize * triangle->points->npoints); return (size_t)(loc - buf); } static size_t gserialized_from_lwcircstring(const LWCIRCSTRING *curve, uint8_t *buf) { uint8_t *loc; int ptsize; size_t size; int type = CIRCSTRINGTYPE; assert(curve); assert(buf); if (FLAGS_GET_ZM(curve->flags) != FLAGS_GET_ZM(curve->points->flags)) lwerror("Dimensions mismatch in lwcircstring"); ptsize = ptarray_point_size(curve->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints. */ memcpy(loc, &curve->points->npoints, sizeof(uint32_t)); loc += sizeof(uint32_t); /* 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, uint8_t *buf) { size_t subsize = 0; uint8_t *loc; int i; int type; assert(coll); assert(buf); type = coll->type; loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the number of subgeoms. */ memcpy(loc, &coll->ngeoms, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Serialize subgeoms. */ for ( i=0; ingeoms; i++ ) { if (FLAGS_GET_ZM(coll->flags) != FLAGS_GET_ZM(coll->geoms[i]->flags)) 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, uint8_t *buf) { assert(geom); assert(buf); LWDEBUGF(2, "Input type (%d) %s, hasz: %d hasm: %d", geom->type, lwtype_name(geom->type), FLAGS_GET_Z(geom->flags), FLAGS_GET_M(geom->flags)); LWDEBUGF(2, "LWGEOM(%p) uint8_t(%p)", geom, buf); switch (geom->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 TRIANGLETYPE: return gserialized_from_lwtriangle((LWTRIANGLE *)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 TINTYPE: case COLLECTIONTYPE: return gserialized_from_lwcollection((LWCOLLECTION *)geom, buf); default: lwerror("Unknown geometry type: %d - %s", geom->type, lwtype_name(geom->type)); return 0; } return 0; } static size_t gserialized_from_gbox(const GBOX *gbox, uint8_t *buf) { uint8_t *loc = buf; float f; size_t return_size; assert(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(LWGEOM *geom, int is_geodetic, size_t *size) { size_t expected_size = 0; size_t return_size = 0; uint8_t *serialized = NULL; uint8_t *ptr = NULL; GSERIALIZED *g = NULL; assert(geom); /* ** See if we need a bounding box, add one if we don't have one. */ if ( (! geom->bbox) && lwgeom_needs_bbox(geom) && (!lwgeom_is_empty(geom)) ) { lwgeom_add_bbox(geom); } /* ** Harmonize the flags to the state of the lwgeom */ if ( geom->bbox ) FLAGS_SET_BBOX(geom->flags, 1); /* Set up the uint8_t buffer into which we are going to write the serialized geometry. */ expected_size = gserialized_from_lwgeom_size(geom); 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 ( geom->bbox ) ptr += gserialized_from_gbox(geom->bbox, 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; /* Set the SRID! */ gserialized_set_srid(g, geom->srid); g->flags = geom->flags; return g; } /*********************************************************************** * De-serialize GSERIALIZED into an LWGEOM. */ static LWGEOM* lwgeom_from_gserialized_buffer(uint8_t *data_ptr, uint8_t g_flags, size_t *g_size); static LWPOINT* lwpoint_from_gserialized_buffer(uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint8_t *start_ptr = data_ptr; LWPOINT *point; uint32_t npoints = 0; assert(data_ptr); point = (LWPOINT*)lwalloc(sizeof(LWPOINT)); point->srid = SRID_UNKNOWN; /* Default */ point->bbox = NULL; point->type = POINTTYPE; point->flags = g_flags; data_ptr += 4; /* Skip past the type. */ npoints = lw_get_uint32_t(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) point->point = ptarray_construct_reference_data(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), 1, data_ptr); 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(uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint8_t *start_ptr = data_ptr; LWLINE *line; uint32_t npoints = 0; assert(data_ptr); line = (LWLINE*)lwalloc(sizeof(LWLINE)); line->srid = SRID_UNKNOWN; /* Default */ line->bbox = NULL; line->type = LINETYPE; line->flags = g_flags; data_ptr += 4; /* Skip past the type. */ npoints = lw_get_uint32_t(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) line->points = ptarray_construct_reference_data(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), npoints, data_ptr); 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(uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint8_t *start_ptr = data_ptr; LWPOLY *poly; uint8_t *ordinate_ptr; uint32_t nrings = 0; int i = 0; assert(data_ptr); poly = (LWPOLY*)lwalloc(sizeof(LWPOLY)); poly->srid = SRID_UNKNOWN; /* Default */ poly->bbox = NULL; poly->type = POLYGONTYPE; poly->flags = g_flags; data_ptr += 4; /* Skip past the polygontype. */ nrings = lw_get_uint32_t(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_t npoints = 0; /* Read in the number of points. */ npoints = lw_get_uint32_t(data_ptr); data_ptr += 4; /* Make a point array for the ring, and move the ordinate pointer past the ring ordinates. */ poly->rings[i] = ptarray_construct_reference_data(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), npoints, ordinate_ptr); ordinate_ptr += sizeof(double) * FLAGS_NDIMS(g_flags) * npoints; } if ( g_size ) *g_size = ordinate_ptr - start_ptr; return poly; } static LWTRIANGLE* lwtriangle_from_gserialized_buffer(uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint8_t *start_ptr = data_ptr; LWTRIANGLE *triangle; uint32_t npoints = 0; assert(data_ptr); triangle = (LWTRIANGLE*)lwalloc(sizeof(LWTRIANGLE)); triangle->srid = SRID_UNKNOWN; /* Default */ triangle->bbox = NULL; triangle->type = TRIANGLETYPE; triangle->flags = g_flags; data_ptr += 4; /* Skip past the type. */ npoints = lw_get_uint32_t(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) triangle->points = ptarray_construct_reference_data(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), npoints, data_ptr); else triangle->points = ptarray_construct(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), 0); /* Empty triangle */ data_ptr += FLAGS_NDIMS(g_flags) * npoints * sizeof(double); if ( g_size ) *g_size = data_ptr - start_ptr; return triangle; } static LWCIRCSTRING* lwcircstring_from_gserialized_buffer(uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint8_t *start_ptr = data_ptr; LWCIRCSTRING *circstring; uint32_t npoints = 0; assert(data_ptr); circstring = (LWCIRCSTRING*)lwalloc(sizeof(LWCIRCSTRING)); circstring->srid = SRID_UNKNOWN; /* Default */ circstring->bbox = NULL; circstring->type = CIRCSTRINGTYPE; circstring->flags = g_flags; data_ptr += 4; /* Skip past the circstringtype. */ npoints = lw_get_uint32_t(data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) circstring->points = ptarray_construct_reference_data(FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), npoints, data_ptr); 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 LWCOLLECTION* lwcollection_from_gserialized_buffer(uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint32_t type; uint8_t *start_ptr = data_ptr; LWCOLLECTION *collection; uint32_t ngeoms = 0; int i = 0; assert(data_ptr); type = lw_get_uint32_t(data_ptr); data_ptr += 4; /* Skip past the type. */ collection = (LWCOLLECTION*)lwalloc(sizeof(LWCOLLECTION)); collection->srid = SRID_UNKNOWN; /* Default */ collection->bbox = NULL; collection->type = type; collection->flags = g_flags; ngeoms = lw_get_uint32_t(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; /* Sub-geometries are never de-serialized with boxes (#1254) */ FLAGS_SET_BBOX(g_flags, 0); for ( i = 0; i < ngeoms; i++ ) { uint32_t subtype = lw_get_uint32_t(data_ptr); size_t subsize = 0; if ( ! lwcollection_allows_subtype(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(uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint32_t type; assert(data_ptr); type = lw_get_uint32_t(data_ptr); LWDEBUGF(2, "Got type %d (%s), hasz=%d hasm=%d geodetic=%d hasbox=%d", type, lwtype_name(type), FLAGS_GET_Z(g_flags), FLAGS_GET_M(g_flags), FLAGS_GET_GEODETIC(g_flags), FLAGS_GET_BBOX(g_flags)); 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 TRIANGLETYPE: return (LWGEOM *)lwtriangle_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 TINTYPE: case COLLECTIONTYPE: return (LWGEOM *)lwcollection_from_gserialized_buffer(data_ptr, g_flags, g_size); default: lwerror("Unknown geometry type: %d - %s", type, lwtype_name(type)); return NULL; } } LWGEOM* lwgeom_from_gserialized(const GSERIALIZED *g) { uint8_t g_flags = 0; int32_t g_srid = 0; uint32_t g_type = 0; uint8_t *data_ptr = NULL; LWGEOM *lwgeom = NULL; GBOX bbox; size_t g_size = 0; assert(g); g_srid = gserialized_get_srid(g); g_flags = g->flags; g_type = gserialized_get_type(g); LWDEBUGF(4, "Got type %d (%s), srid=%d", g_type, lwtype_name(g_type), g_srid); data_ptr = (uint8_t*)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 ) lwerror("lwgeom_from_gserialized: unable create geometry"); /* Ooops! */ lwgeom->type = g_type; lwgeom->flags = g_flags; if ( gserialized_read_gbox_p(g, &bbox) == LW_SUCCESS ) { lwgeom->bbox = gbox_copy(&bbox); } else if ( lwgeom_needs_bbox(lwgeom) && (lwgeom_calculate_gbox(lwgeom, &bbox) == LW_SUCCESS) ) { lwgeom->bbox = gbox_copy(&bbox); } else { lwgeom->bbox = NULL; } lwgeom_set_srid(lwgeom, g_srid); return lwgeom; }