/********************************************************************** * * PostGIS - Spatial Types for PostgreSQL * http://postgis.net * * PostGIS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * PostGIS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PostGIS. If not, see . * ********************************************************************** * * Copyright 2001-2003 Refractions Research Inc. * Copyright 2009-2010 Olivier Courtin * **********************************************************************/ #include "liblwgeom_internal.h" #include /* strlen */ #include static lwvarlena_t *asgeojson_point(const LWPOINT *point, const char *srs, GBOX *bbox, int precision); static lwvarlena_t *asgeojson_line(const LWLINE *line, const char *srs, GBOX *bbox, int precision); static lwvarlena_t *asgeojson_triangle(const LWTRIANGLE *tri, const char *srs, GBOX *bbox, int precision); static lwvarlena_t *asgeojson_poly(const LWPOLY *poly, const char *srs, GBOX *bbox, int precision); static lwvarlena_t *asgeojson_multipoint(const LWMPOINT *mpoint, const char *srs, GBOX *bbox, int precision); static lwvarlena_t *asgeojson_multiline(const LWMLINE *mline, const char *srs, GBOX *bbox, int precision); static lwvarlena_t *asgeojson_multipolygon(const LWMPOLY *mpoly, const char *srs, GBOX *bbox, int precision); static lwvarlena_t *asgeojson_collection(const LWCOLLECTION *col, const char *srs, GBOX *bbox, int precision); static size_t asgeojson_geom_size(const LWGEOM *geom, GBOX *bbox, int precision); static size_t asgeojson_geom_buf(const LWGEOM *geom, char *output, GBOX *bbox, int precision); static size_t pointArray_to_geojson(POINTARRAY *pa, char *buf, int precision); static size_t pointArray_geojson_size(POINTARRAY *pa, int precision); /** * Takes a GEOMETRY and returns a GeoJson representation */ lwvarlena_t * lwgeom_to_geojson(const LWGEOM *geom, const char *srs, int precision, int has_bbox) { int type = geom->type; GBOX *bbox = NULL; GBOX tmp = {0}; if (has_bbox) { /* Whether these are geography or geometry, the GeoJSON expects a cartesian bounding box */ lwgeom_calculate_gbox_cartesian(geom, &tmp); bbox = &tmp; } switch (type) { case POINTTYPE: return asgeojson_point((LWPOINT*)geom, srs, bbox, precision); case LINETYPE: return asgeojson_line((LWLINE*)geom, srs, bbox, precision); case POLYGONTYPE: return asgeojson_poly((LWPOLY*)geom, srs, bbox, precision); case MULTIPOINTTYPE: return asgeojson_multipoint((LWMPOINT*)geom, srs, bbox, precision); case MULTILINETYPE: return asgeojson_multiline((LWMLINE*)geom, srs, bbox, precision); case MULTIPOLYGONTYPE: return asgeojson_multipolygon((LWMPOLY*)geom, srs, bbox, precision); case TRIANGLETYPE: return asgeojson_triangle((LWTRIANGLE *)geom, srs, bbox, precision); case TINTYPE: case COLLECTIONTYPE: return asgeojson_collection((LWCOLLECTION*)geom, srs, bbox, precision); default: lwerror("lwgeom_to_geojson: '%s' geometry type not supported", lwtype_name(type)); } /* Never get here */ return NULL; } /** * Handle SRS */ static size_t asgeojson_srs_size(const char *srs) { int size; size = sizeof("'crs':{'type':'name',"); size += sizeof("'properties':{'name':''}},"); size += strlen(srs) * sizeof(char); return size; } static size_t asgeojson_srs_buf(char *output, const char *srs) { char *ptr = output; ptr += sprintf(ptr, "\"crs\":{\"type\":\"name\","); ptr += sprintf(ptr, "\"properties\":{\"name\":\"%s\"}},", srs); return (ptr-output); } /** * Handle Bbox */ static size_t asgeojson_bbox_size(int hasz, int precision) { int size; if (!hasz) { size = sizeof("\"bbox\":[,,,],"); size += 2 * 2 * (OUT_MAX_BYTES_DOUBLE + precision); } else { size = sizeof("\"bbox\":[,,,,,],"); size += 2 * 3 * (OUT_MAX_BYTES_DOUBLE + precision); } return size; } static size_t asgeojson_bbox_buf(char *output, GBOX *bbox, int hasz, int precision) { char *ptr = output; if (!hasz) ptr += sprintf(ptr, "\"bbox\":[%.*f,%.*f,%.*f,%.*f],", precision, bbox->xmin, precision, bbox->ymin, precision, bbox->xmax, precision, bbox->ymax); else ptr += sprintf(ptr, "\"bbox\":[%.*f,%.*f,%.*f,%.*f,%.*f,%.*f],", precision, bbox->xmin, precision, bbox->ymin, precision, bbox->zmin, precision, bbox->xmax, precision, bbox->ymax, precision, bbox->zmax); return (ptr-output); } /** * Point Geometry */ static size_t asgeojson_point_size(const LWPOINT *point, const char *srs, GBOX *bbox, int precision) { int size; size = pointArray_geojson_size(point->point, precision); size += sizeof("{'type':'Point',"); size += sizeof("'coordinates':}"); if ( lwpoint_is_empty(point) ) size += 2; /* [] */ if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(point->flags), precision); return size; } static size_t asgeojson_point_buf(const LWPOINT *point, const char *srs, char *output, GBOX *bbox, int precision) { char *ptr = output; ptr += sprintf(ptr, "{\"type\":\"Point\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(point->flags), precision); ptr += sprintf(ptr, "\"coordinates\":"); if ( lwpoint_is_empty(point) ) ptr += sprintf(ptr, "[]"); ptr += pointArray_to_geojson(point->point, ptr, precision); ptr += sprintf(ptr, "}"); return (ptr-output); } static lwvarlena_t * asgeojson_point(const LWPOINT *point, const char *srs, GBOX *bbox, int precision) { uint32_t size = asgeojson_point_size(point, srs, bbox, precision); lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ); size = asgeojson_point_buf(point, srs, output->data, bbox, precision); LWSIZE_SET(output->size, size + LWVARHDRSZ); return output; } /** * Triangle Geometry */ static size_t asgeojson_triangle_size(const LWTRIANGLE *tri, const char *srs, GBOX *bbox, int precision) { int size; size = sizeof("{'type':'Polygon',"); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(tri->flags), precision); size += sizeof("'coordinates':[[]]}"); size += pointArray_geojson_size(tri->points, precision); return size; } static size_t asgeojson_triangle_buf(const LWTRIANGLE *tri, const char *srs, char *output, GBOX *bbox, int precision) { char *ptr = output; ptr += sprintf(ptr, "{\"type\":\"Polygon\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(tri->flags), precision); ptr += sprintf(ptr, "\"coordinates\":[["); ptr += pointArray_to_geojson(tri->points, ptr, precision); ptr += sprintf(ptr, "]]}"); return (ptr - output); } static lwvarlena_t * asgeojson_triangle(const LWTRIANGLE *tri, const char *srs, GBOX *bbox, int precision) { uint32_t size = asgeojson_triangle_size(tri, srs, bbox, precision); lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ); size = asgeojson_triangle_buf(tri, srs, output->data, bbox, precision); LWSIZE_SET(output->size, size + LWVARHDRSZ); return output; } /** * Line Geometry */ static size_t asgeojson_line_size(const LWLINE *line, const char *srs, GBOX *bbox, int precision) { int size; size = sizeof("{'type':'LineString',"); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(line->flags), precision); size += sizeof("'coordinates':[]}"); size += pointArray_geojson_size(line->points, precision); return size; } static size_t asgeojson_line_buf(const LWLINE *line, const char *srs, char *output, GBOX *bbox, int precision) { char *ptr=output; ptr += sprintf(ptr, "{\"type\":\"LineString\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(line->flags), precision); ptr += sprintf(ptr, "\"coordinates\":["); ptr += pointArray_to_geojson(line->points, ptr, precision); ptr += sprintf(ptr, "]}"); return (ptr-output); } static lwvarlena_t * asgeojson_line(const LWLINE *line, const char *srs, GBOX *bbox, int precision) { uint32_t size = asgeojson_line_size(line, srs, bbox, precision); lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ); size = asgeojson_line_buf(line, srs, output->data, bbox, precision); LWSIZE_SET(output->size, size + LWVARHDRSZ); return output; } /** * Polygon Geometry */ static size_t asgeojson_poly_size(const LWPOLY *poly, const char *srs, GBOX *bbox, int precision) { size_t size; uint32_t i; size = sizeof("{\"type\":\"Polygon\","); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(poly->flags), precision); size += sizeof("\"coordinates\":["); for (i=0; inrings; i++) { size += pointArray_geojson_size(poly->rings[i], precision); size += sizeof("[]"); } size += sizeof(",") * i; size += sizeof("]}"); return size; } static size_t asgeojson_poly_buf(const LWPOLY *poly, const char *srs, char *output, GBOX *bbox, int precision) { uint32_t i; char *ptr=output; ptr += sprintf(ptr, "{\"type\":\"Polygon\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(poly->flags), precision); ptr += sprintf(ptr, "\"coordinates\":["); for (i=0; inrings; i++) { if (i) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "["); ptr += pointArray_to_geojson(poly->rings[i], ptr, precision); ptr += sprintf(ptr, "]"); } ptr += sprintf(ptr, "]}"); return (ptr-output); } static lwvarlena_t * asgeojson_poly(const LWPOLY *poly, const char *srs, GBOX *bbox, int precision) { uint32_t size = asgeojson_poly_size(poly, srs, bbox, precision); lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ); size = asgeojson_poly_buf(poly, srs, output->data, bbox, precision); LWSIZE_SET(output->size, size + LWVARHDRSZ); return output; } /** * Multipoint Geometry */ static size_t asgeojson_multipoint_size(const LWMPOINT *mpoint, const char *srs, GBOX *bbox, int precision) { LWPOINT * point; int size; uint32_t i, ngeoms = mpoint->ngeoms; size = sizeof("{'type':'MultiPoint',"); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(mpoint->flags), precision); size += sizeof("'coordinates':[]}"); if (lwgeom_is_empty((LWGEOM*)mpoint)) ngeoms = 0; for (i=0; igeoms[i]; size += pointArray_geojson_size(point->point, precision); } size += sizeof(",") * i; return size; } static size_t asgeojson_multipoint_buf(const LWMPOINT *mpoint, const char *srs, char *output, GBOX *bbox, int precision) { LWPOINT *point; uint32_t i, ngeoms = mpoint->ngeoms; char *ptr=output; ptr += sprintf(ptr, "{\"type\":\"MultiPoint\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(mpoint->flags), precision); ptr += sprintf(ptr, "\"coordinates\":["); if (lwgeom_is_empty((LWGEOM*)mpoint)) ngeoms = 0; for (i=0; igeoms[i]; ptr += pointArray_to_geojson(point->point, ptr, precision); } ptr += sprintf(ptr, "]}"); return (ptr - output); } static lwvarlena_t * asgeojson_multipoint(const LWMPOINT *mpoint, const char *srs, GBOX *bbox, int precision) { uint32_t size = asgeojson_multipoint_size(mpoint, srs, bbox, precision); lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ); size = asgeojson_multipoint_buf(mpoint, srs, output->data, bbox, precision); LWSIZE_SET(output->size, size + LWVARHDRSZ); return output; } /** * Multiline Geometry */ static size_t asgeojson_multiline_size(const LWMLINE *mline, const char *srs, GBOX *bbox, int precision) { LWLINE * line; int size; uint32_t i, ngeoms = mline->ngeoms; size = sizeof("{'type':'MultiLineString',"); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(mline->flags), precision); size += sizeof("'coordinates':[]}"); if (lwgeom_is_empty((LWGEOM*)mline)) ngeoms = 0; for (i=0 ; igeoms[i]; size += pointArray_geojson_size(line->points, precision); size += sizeof("[]"); } size += sizeof(",") * i; return size; } static size_t asgeojson_multiline_buf(const LWMLINE *mline, const char *srs, char *output, GBOX *bbox, int precision) { LWLINE *line; uint32_t i, ngeoms = mline->ngeoms; char *ptr=output; ptr += sprintf(ptr, "{\"type\":\"MultiLineString\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(mline->flags), precision); ptr += sprintf(ptr, "\"coordinates\":["); if (lwgeom_is_empty((LWGEOM*)mline)) ngeoms = 0; for (i=0; igeoms[i]; ptr += pointArray_to_geojson(line->points, ptr, precision); ptr += sprintf(ptr, "]"); } ptr += sprintf(ptr, "]}"); return (ptr - output); } static lwvarlena_t * asgeojson_multiline(const LWMLINE *mline, const char *srs, GBOX *bbox, int precision) { uint32_t size = asgeojson_multiline_size(mline, srs, bbox, precision); lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ); size = asgeojson_multiline_buf(mline, srs, output->data, bbox, precision); LWSIZE_SET(output->size, size + LWVARHDRSZ); return output; } /** * MultiPolygon Geometry */ static size_t asgeojson_multipolygon_size(const LWMPOLY *mpoly, const char *srs, GBOX *bbox, int precision) { LWPOLY *poly; int size; uint32_t i, j, ngeoms = mpoly->ngeoms; size = sizeof("{'type':'MultiPolygon',"); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(mpoly->flags), precision); size += sizeof("'coordinates':[]}"); if (lwgeom_is_empty((LWGEOM*)mpoly)) ngeoms = 0; for (i=0; i < ngeoms; i++) { poly = mpoly->geoms[i]; for (j=0 ; j nrings ; j++) { size += pointArray_geojson_size(poly->rings[j], precision); size += sizeof("[]"); } size += sizeof("[]"); } size += sizeof(",") * i; size += sizeof("]}"); return size; } static size_t asgeojson_multipolygon_buf(const LWMPOLY *mpoly, const char *srs, char *output, GBOX *bbox, int precision) { LWPOLY *poly; uint32_t i, j, ngeoms = mpoly->ngeoms; char *ptr=output; ptr += sprintf(ptr, "{\"type\":\"MultiPolygon\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(mpoly->flags), precision); ptr += sprintf(ptr, "\"coordinates\":["); if (lwgeom_is_empty((LWGEOM*)mpoly)) ngeoms = 0; for (i=0; i < ngeoms; i++) { if (i) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "["); poly = mpoly->geoms[i]; for (j=0 ; j < poly->nrings ; j++) { if (j) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "["); ptr += pointArray_to_geojson(poly->rings[j], ptr, precision); ptr += sprintf(ptr, "]"); } ptr += sprintf(ptr, "]"); } ptr += sprintf(ptr, "]}"); return (ptr - output); } static lwvarlena_t * asgeojson_multipolygon(const LWMPOLY *mpoly, const char *srs, GBOX *bbox, int precision) { uint32_t size = asgeojson_multipolygon_size(mpoly, srs, bbox, precision); lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ); size = asgeojson_multipolygon_buf(mpoly, srs, output->data, bbox, precision); LWSIZE_SET(output->size, size + LWVARHDRSZ); return output; } /** * Collection Geometry */ static size_t asgeojson_collection_size(const LWCOLLECTION *col, const char *srs, GBOX *bbox, int precision) { uint32_t i, ngeoms = col->ngeoms; size_t size; LWGEOM *subgeom; size = sizeof("{'type':'GeometryCollection',"); if (srs) size += asgeojson_srs_size(srs); if (bbox) size += asgeojson_bbox_size(FLAGS_GET_Z(col->flags), precision); size += sizeof("'geometries':"); if (lwgeom_is_empty((LWGEOM*)col)) ngeoms = 0; for (i=0; igeoms[i]; size += asgeojson_geom_size(subgeom, NULL, precision); } size += sizeof(",") * i; size += sizeof("]}"); return size; } static size_t asgeojson_collection_buf(const LWCOLLECTION *col, const char *srs, char *output, GBOX *bbox, int precision) { uint32_t i, ngeoms = col->ngeoms; char *ptr=output; LWGEOM *subgeom; ptr += sprintf(ptr, "{\"type\":\"GeometryCollection\","); if (srs) ptr += asgeojson_srs_buf(ptr, srs); if (col->ngeoms && bbox) ptr += asgeojson_bbox_buf(ptr, bbox, FLAGS_GET_Z(col->flags), precision); ptr += sprintf(ptr, "\"geometries\":["); if (lwgeom_is_empty((LWGEOM*)col)) ngeoms = 0; for (i=0; igeoms[i]; ptr += asgeojson_geom_buf(subgeom, ptr, NULL, precision); } ptr += sprintf(ptr, "]}"); return (ptr - output); } static lwvarlena_t * asgeojson_collection(const LWCOLLECTION *col, const char *srs, GBOX *bbox, int precision) { uint32_t size = asgeojson_collection_size(col, srs, bbox, precision); lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ); size = asgeojson_collection_buf(col, srs, output->data, bbox, precision); LWSIZE_SET(output->size, size + LWVARHDRSZ); return output; } static size_t asgeojson_geom_size(const LWGEOM *geom, GBOX *bbox, int precision) { switch (geom->type) { case POINTTYPE: return asgeojson_point_size((LWPOINT *)geom, NULL, bbox, precision); case LINETYPE: return asgeojson_line_size((LWLINE *)geom, NULL, bbox, precision); case TRIANGLETYPE: return asgeojson_triangle_size((LWTRIANGLE *)geom, NULL, bbox, precision); case POLYGONTYPE: return asgeojson_poly_size((LWPOLY *)geom, NULL, bbox, precision); case MULTIPOINTTYPE: return asgeojson_multipoint_size((LWMPOINT *)geom, NULL, bbox, precision); case MULTILINETYPE: return asgeojson_multiline_size((LWMLINE *)geom, NULL, bbox, precision); case MULTIPOLYGONTYPE: return asgeojson_multipolygon_size((LWMPOLY *)geom, NULL, bbox, precision); default: lwerror("GeoJson: geometry not supported."); return 0; } } static size_t asgeojson_geom_buf(const LWGEOM *geom, char *output, GBOX *bbox, int precision) { int type = geom->type; char *ptr=output; switch (type) { case POINTTYPE: ptr += asgeojson_point_buf((LWPOINT*)geom, NULL, ptr, bbox, precision); break; case LINETYPE: ptr += asgeojson_line_buf((LWLINE*)geom, NULL, ptr, bbox, precision); break; case POLYGONTYPE: ptr += asgeojson_poly_buf((LWPOLY*)geom, NULL, ptr, bbox, precision); break; case TRIANGLETYPE: ptr += asgeojson_triangle_buf((LWTRIANGLE *)geom, NULL, ptr, bbox, precision); break; case MULTIPOINTTYPE: ptr += asgeojson_multipoint_buf((LWMPOINT*)geom, NULL, ptr, bbox, precision); break; case MULTILINETYPE: ptr += asgeojson_multiline_buf((LWMLINE*)geom, NULL, ptr, bbox, precision); break; case MULTIPOLYGONTYPE: ptr += asgeojson_multipolygon_buf((LWMPOLY*)geom, NULL, ptr, bbox, precision); break; default: if (bbox) lwfree(bbox); lwerror("GeoJson: geometry not supported."); } return (ptr-output); } static size_t pointArray_to_geojson(POINTARRAY *pa, char *output, int precision) { char *ptr = output; if (!FLAGS_GET_Z(pa->flags)) { for (uint32_t i = 0; i < pa->npoints; i++) { if (i) { *ptr = ','; ptr++; } const POINT2D *pt = getPoint2d_cp(pa, i); *ptr = '['; ptr++; ptr += lwprint_double(pt->x, precision, ptr); *ptr = ','; ptr++; ptr += lwprint_double(pt->y, precision, ptr); *ptr = ']'; ptr++; } } else { for (uint32_t i = 0; i < pa->npoints; i++) { if (i) { *ptr = ','; ptr++; } const POINT3D *pt = getPoint3d_cp(pa, i); *ptr = '['; ptr++; ptr += lwprint_double(pt->x, precision, ptr); *ptr = ','; ptr++; ptr += lwprint_double(pt->y, precision, ptr); *ptr = ','; ptr++; ptr += lwprint_double(pt->z, precision, ptr); *ptr = ']'; ptr++; } } *ptr = '\0'; return (ptr-output); } /** * Returns maximum size of rendered pointarray in bytes. */ static size_t pointArray_geojson_size(POINTARRAY *pa, int precision) { if (FLAGS_NDIMS(pa->flags) == 2) return (OUT_MAX_BYTES_DOUBLE + precision + sizeof(",")) * 2 * pa->npoints + sizeof(",[]"); return (OUT_MAX_BYTES_DOUBLE + precision + sizeof(",,")) * 3 * pa->npoints + sizeof(",[]"); }