/********************************************************************** * * 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 2010 Olivier Courtin * **********************************************************************/ #include #include "liblwgeom_internal.h" #include "lwgeom_log.h" typedef struct { int cnt[NUMTYPES]; LWCOLLECTION* buf[NUMTYPES]; } HomogenizeBuffer; static void init_homogenizebuffer(HomogenizeBuffer *buffer) { int i; for ( i = 0; i < NUMTYPES; i++ ) { buffer->cnt[i] = 0; buffer->buf[i] = NULL; } } /* static void free_homogenizebuffer(HomogenizeBuffer *buffer) { int i; for ( i = 0; i < NUMTYPES; i++ ) { if ( buffer->buf[i] ) { lwcollection_free(buffer->buf[i]); } } } */ /* ** Given a generic collection, return the "simplest" form. ** ** eg: GEOMETRYCOLLECTION(MULTILINESTRING()) => MULTILINESTRING() ** ** GEOMETRYCOLLECTION(MULTILINESTRING(), MULTILINESTRING(), POINT()) ** => GEOMETRYCOLLECTION(MULTILINESTRING(), POINT()) ** ** In general, if the subcomponents are homogeneous, return a properly ** typed collection. ** Otherwise, return a generic collection, with the subtypes in minimal ** typed collections. */ static void lwcollection_build_buffer(const LWCOLLECTION *col, HomogenizeBuffer *buffer) { uint32_t i; if (!col || lwcollection_is_empty(col)) return; for (i = 0; i < col->ngeoms; i++) { LWGEOM *geom = col->geoms[i]; switch (geom->type) { case POINTTYPE: case LINETYPE: case CIRCSTRINGTYPE: case COMPOUNDTYPE: case TRIANGLETYPE: case CURVEPOLYTYPE: case POLYGONTYPE: /* Init if necessary */ if (!buffer->buf[geom->type]) { LWCOLLECTION *bufcol = lwcollection_construct_empty( COLLECTIONTYPE, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags)); bufcol->type = lwtype_get_collectiontype(geom->type); buffer->buf[geom->type] = bufcol; } /* Add sub-geom to buffer */ lwcollection_add_lwgeom(buffer->buf[geom->type], lwgeom_clone_deep(geom)); /* Increment count for this singleton type */ buffer->cnt[geom->type]++; break; default: lwcollection_build_buffer(lwgeom_as_lwcollection(geom), buffer); break; } } } static LWGEOM* lwcollection_homogenize(const LWCOLLECTION *col) { int i; int ntypes = 0; int type = 0; LWGEOM *outgeom = NULL; HomogenizeBuffer buffer; /* Sort all the parts into a buffer */ init_homogenizebuffer(&buffer); lwcollection_build_buffer(col, &buffer); /* Check for homogeneity */ for ( i = 0; i < NUMTYPES; i++ ) { if ( buffer.cnt[i] > 0 ) { ntypes++; type = i; } } /* No types? Huh. Return empty. */ if ( ntypes == 0 ) { LWCOLLECTION *outcol; outcol = lwcollection_construct_empty(COLLECTIONTYPE, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags)); outgeom = lwcollection_as_lwgeom(outcol); } /* One type, return homogeneous collection */ else if ( ntypes == 1 ) { LWCOLLECTION *outcol; outcol = buffer.buf[type]; if ( outcol->ngeoms == 1 ) { outgeom = outcol->geoms[0]; outcol->ngeoms=0; lwcollection_free(outcol); } else { outgeom = lwcollection_as_lwgeom(outcol); } outgeom->srid = col->srid; } /* Bah, more than out type, return anonymous collection */ else if ( ntypes > 1 ) { int j; LWCOLLECTION *outcol; outcol = lwcollection_construct_empty(COLLECTIONTYPE, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags)); for ( j = 0; j < NUMTYPES; j++ ) { if ( buffer.buf[j] ) { LWCOLLECTION *bcol = buffer.buf[j]; if ( bcol->ngeoms == 1 ) { lwcollection_add_lwgeom(outcol, bcol->geoms[0]); bcol->ngeoms=0; lwcollection_free(bcol); } else { lwcollection_add_lwgeom(outcol, lwcollection_as_lwgeom(bcol)); } } } outgeom = lwcollection_as_lwgeom(outcol); } return outgeom; } /* ** Given a generic geometry, return the "simplest" form. ** ** eg: ** LINESTRING() => LINESTRING() ** ** MULTILINESTRING(with a single line) => LINESTRING() ** ** GEOMETRYCOLLECTION(MULTILINESTRING()) => MULTILINESTRING() ** ** GEOMETRYCOLLECTION(MULTILINESTRING(), MULTILINESTRING(), POINT()) ** => GEOMETRYCOLLECTION(MULTILINESTRING(), POINT()) */ LWGEOM * lwgeom_homogenize(const LWGEOM *geom) { LWGEOM *hgeom; /* EMPTY Geometry */ if (lwgeom_is_empty(geom)) { if( lwgeom_is_collection(geom) ) { return lwcollection_as_lwgeom(lwcollection_construct_empty(geom->type, geom->srid, lwgeom_has_z(geom), lwgeom_has_m(geom))); } return lwgeom_clone_deep(geom); } switch (geom->type) { /* Return simple geometries untouched */ case POINTTYPE: case LINETYPE: case CIRCSTRINGTYPE: case COMPOUNDTYPE: case TRIANGLETYPE: case CURVEPOLYTYPE: case POLYGONTYPE: return lwgeom_clone_deep(geom); /* Process homogeneous geometries lightly */ case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: { LWCOLLECTION *col = (LWCOLLECTION*)geom; /* Strip single-entry multi-geometries down to singletons */ if ( col->ngeoms == 1 ) { hgeom = lwgeom_clone_deep((LWGEOM *)(col->geoms[0])); hgeom->srid = geom->srid; if (geom->bbox) hgeom->bbox = gbox_copy(geom->bbox); return hgeom; } /* Return proper multigeometry untouched */ return lwgeom_clone_deep(geom); } /* Work on anonymous collections separately */ case COLLECTIONTYPE: return lwcollection_homogenize((LWCOLLECTION *) geom); } /* Unknown type */ lwerror("lwgeom_homogenize: Geometry Type not supported (%i)", lwtype_name(geom->type)); return NULL; /* Never get here! */ }