lwt_NewEdgesSplit X
lwt_AddIsoEdge X
+lwt_GetFaceGeometry X
lwt_GetFaceEdges
-lwt_GetFaceGeometry
lwt_ChangeEdgeGeom
lwt_MoveIsoNode
int numelems
);
+ /**
+ * Get topology SRID
+ * @return 0 for unknown
+ */
+ int (*topoGetSRID) (
+ const LWT_BE_TOPOLOGY* topo
+ );
+
+ /**
+ * Get topology precision
+ */
+ double (*topoGetPrecision) (
+ const LWT_BE_TOPOLOGY* topo
+ );
+
+ /**
+ * Get topology Z flag
+ * @return 1 if topology elements do have Z value, 0 otherwise
+ */
+ int (*topoHasZ) (
+ const LWT_BE_TOPOLOGY* topo
+ );
+
+
} LWT_BE_CALLBACKS;
* @param face identifier of the face
* @param edges will be set to an array of signed edge identifiers, will
* need to be released with lwfree
- * @return the number of edges in the edges array
+ * @return the number of edges in the edges array, or -1 on error
+ * (liblwgeom error handler will be invoked with error message)
*
*/
int lwt_GetFaceEdges(LWT_TOPOLOGY* topo, LWT_ELEMID face, LWT_ELEMID **edges);
* @param topo the topology to operate on
* @param face identifier of the face
* @return a polygon geometry representing the face, ownership to caller,
- * to be released with lwgeom_release
+ * to be released with lwgeom_release, or NULL on error
+ * (liblwgeom error handler will be invoked with error message)
*/
LWGEOM* lwt_GetFaceGeometry(LWT_TOPOLOGY* topo, LWT_ELEMID face);
{
const LWT_BE_IFACE *be_iface;
LWT_BE_TOPOLOGY *be_topo;
- char *name;
- char *table_prefix;
+ int srid;
+ double precision;
+ int hasZ;
};
#endif /* LIBLWGEOM_TOPO_INTERNAL_H */
CB1(be, loadTopologyByName, name);
}
+static int
+lwt_be_topoGetSRID(LWT_TOPOLOGY *topo)
+{
+ CBT0(topo, topoGetSRID);
+}
+
+static double
+lwt_be_topoGetPrecision(LWT_TOPOLOGY *topo)
+{
+ CBT0(topo, topoGetPrecision);
+}
+
+static int
+lwt_be_topoHasZ(LWT_TOPOLOGY *topo)
+{
+ CBT0(topo, topoHasZ);
+}
+
int
lwt_be_freeTopology(LWT_TOPOLOGY *topo)
{
topo = lwalloc(sizeof(LWT_TOPOLOGY));
topo->be_iface = iface;
topo->be_topo = be_topo;
- topo->name = NULL; /* don't want to think about it now.. */
- topo->table_prefix = NULL; /* don't want to think about it now */
+ topo->srid = lwt_be_topoGetSRID(topo);
+ topo->hasZ = lwt_be_topoHasZ(topo);
+ topo->precision = lwt_be_topoGetPrecision(topo);
return topo;
}
GEOSGeometry *edgegg;
const GEOSPreparedGeometry* prepared_edge;
+ initGEOS(lwnotice, lwgeom_geos_error);
+
edgegg = LWGEOM2GEOS( lwline_as_lwgeom(geom), 0);
if ( ! edgegg ) {
lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg);
LWT_ISO_EDGE seledge;
LWT_ISO_EDGE updedge;
- initGEOS(lwnotice, lwgeom_geos_error);
-
if ( ! skipChecks )
{
/* curve must be simple */
}
}
- lwnotice("XXXX end of adding an edge, newedge.face_left is %d, newface is %d, newface1 is %d", newedge.face_left, newface, newface1);
-
/*
* Update topogeometries, if needed
*/
{
return _lwt_AddEdge( topo, start_node, end_node, geom, skipChecks, 0 );
}
+
+LWGEOM*
+lwt_GetFaceGeometry(LWT_TOPOLOGY* topo, LWT_ELEMID faceid)
+{
+ int numfaceedges;
+ LWT_ISO_EDGE *edges;
+ LWT_ISO_FACE *face;
+ LWPOLY *out;
+ LWGEOM *outg;
+ int i;
+ int fields;
+
+ if ( faceid == 0 )
+ {
+ lwerror("SQL/MM Spatial exception - universal face has no geometry");
+ return NULL;
+ }
+
+ /* Construct the face geometry */
+ numfaceedges = 1;
+ fields = LWT_COL_EDGE_GEOM |
+ LWT_COL_EDGE_FACE_LEFT |
+ LWT_COL_EDGE_FACE_RIGHT
+ ;
+ edges = lwt_be_getEdgeByFace( topo, &faceid, &numfaceedges, fields );
+ if ( numfaceedges == -1 ) {
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return NULL;
+ }
+
+ if ( numfaceedges == 0 )
+ {
+ i = 1;
+ face = lwt_be_getFaceById(topo, &faceid, &i, LWT_COL_FACE_FACE_ID);
+ if ( i == -1 ) {
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return NULL;
+ }
+ if ( i == 0 ) {
+ lwerror("SQL/MM Spatial exception - non-existent face.");
+ return NULL;
+ }
+ lwfree( face );
+ if ( i > 1 ) {
+ lwerror("Corrupted topology: multiple face records have face_id=%"
+ PRId64, faceid);
+ return NULL;
+ }
+ /* Face has no boundary edges, we'll return EMPTY, see
+ * https://trac.osgeo.org/postgis/ticket/3221 */
+ out = lwpoly_construct_empty(topo->srid, topo->hasZ, 0);
+ return lwpoly_as_lwgeom(out);
+ }
+
+ /* Linemerge polygon boundaries */
+
+ LWGEOM **geoms = lwalloc( sizeof(LWGEOM*) * numfaceedges );
+ int validedges = 0;
+ for ( i=0; i<numfaceedges; ++i )
+ {
+ /* NOTE: skipping edges with same face on both sides, although
+ * correct, results in a failure to build faces from
+ * invalid topologies as expected by legacy tests.
+ * TODO: update legacy tests expectances/unleash this skipping ?
+ */
+ /* if ( edges[i].face_left == edges[i].face_right ) continue; */
+ geoms[validedges++] = lwline_as_lwgeom(edges[i].geom);
+ }
+ if ( ! validedges )
+ {
+ lwfree(geoms);
+ /* Face has no valid boundary edges, we'll return EMPTY, see
+ * https://trac.osgeo.org/postgis/ticket/3221 */
+ out = lwpoly_construct_empty(topo->srid, topo->hasZ, 0);
+ return lwpoly_as_lwgeom(out);
+ }
+ LWCOLLECTION *bounds = lwcollection_construct(MULTILINETYPE,
+ topo->srid,
+ NULL, /* gbox */
+ validedges,
+ geoms);
+#if 0 /* debugging (a leaking one) */
+ lwnotice("Collected face bounds: %s", lwgeom_to_wkt((LWGEOM*)bounds,
+ WKT_ISO, 2, NULL));
+#endif
+
+ outg = lwgeom_buildarea( lwcollection_as_lwgeom(bounds) );
+
+ _lwt_release_edges(edges, numfaceedges);
+ lwcollection_release(bounds);
+
+ return outg;
+}
+
+int
+lwt_GetFaceEdges(LWT_TOPOLOGY* topo, LWT_ELEMID face, LWT_ELEMID **edges)
+{
+ /* TODO: Construct the face geometry */
+ /* TODO: Then for each ring of the face polygon... */
+ lwerror("lwt_GetFaceEdges not implemented yet");
+ return -1;
+}
int id;
int srid;
int precision;
+ int hasZ;
};
/* utility funx */
return topo;
}
+static int
+cb_topoGetSRID(const LWT_BE_TOPOLOGY* topo)
+{
+ return topo->srid;
+}
+
+static int
+cb_topoHasZ(const LWT_BE_TOPOLOGY* topo)
+{
+ return topo->hasZ;
+}
+
+static double
+cb_topoGetPrecision(const LWT_BE_TOPOLOGY* topo)
+{
+ return topo->precision;
+}
+
static int
cb_freeTopology(LWT_BE_TOPOLOGY* topo)
{
cb_getEdgeByFace,
cb_getNodeByFace,
cb_updateNodesById,
- cb_deleteFacesById
+ cb_deleteFacesById,
+ cb_topoGetSRID,
+ cb_topoGetPrecision,
+ cb_topoHasZ
};
SPI_finish();
PG_RETURN_INT32(edge_id);
}
+
+/* ST_GetFaceGeometry(atopology, aface) */
+Datum ST_GetFaceGeometry(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(ST_GetFaceGeometry);
+Datum ST_GetFaceGeometry(PG_FUNCTION_ARGS)
+{
+ text* toponame_text;
+ char* toponame;
+ LWT_ELEMID face_id;
+ LWGEOM *lwgeom;
+ LWT_TOPOLOGY *topo;
+ GSERIALIZED *geom;
+ MemoryContext old_context;
+
+ if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) ) {
+ lwpgerror("SQL/MM Spatial exception - null argument");
+ PG_RETURN_NULL();
+ }
+
+ toponame_text = PG_GETARG_TEXT_P(0);
+ toponame = text2cstring(toponame_text);
+ PG_FREE_IF_COPY(toponame_text, 0);
+
+ face_id = PG_GETARG_INT32(1) ;
+
+ if ( SPI_OK_CONNECT != SPI_connect() ) {
+ lwpgerror("Could not connect to SPI");
+ PG_RETURN_NULL();
+ }
+ be_data.data_changed = false;
+
+ topo = lwt_LoadTopology(be_iface, toponame);
+ pfree(toponame);
+ if ( ! topo ) {
+ /* should never reach this point, as lwerror would raise an exception */
+ SPI_finish();
+ PG_RETURN_NULL();
+ }
+
+ POSTGIS_DEBUG(1, "Calling lwt_GetFaceGeometry");
+ lwgeom = lwt_GetFaceGeometry(topo, face_id);
+ POSTGIS_DEBUG(1, "lwt_GetFaceGeometry returned");
+ lwt_FreeTopology(topo);
+
+ if ( lwgeom == NULL ) {
+ /* should never reach this point, as lwerror would raise an exception */
+ SPI_finish();
+ PG_RETURN_NULL();
+ }
+
+ /* Serialize in upper memory context (outside of SPI) */
+ /* TODO: use a narrower context to switch to */
+ old_context = MemoryContextSwitchTo( TopMemoryContext );
+ geom = geometry_serialize(lwgeom);
+ MemoryContextSwitchTo(old_context);
+
+ SPI_finish();
+
+ PG_RETURN_POINTER(geom);
+}
--
CREATE OR REPLACE FUNCTION topology.ST_GetFaceGeometry(toponame varchar, aface integer)
RETURNS GEOMETRY AS
-$$
-DECLARE
- rec RECORD;
- sql TEXT;
-BEGIN
-
- --
- -- toponame and aface are required
- --
- IF toponame IS NULL OR aface IS NULL THEN
- RAISE EXCEPTION 'SQL/MM Spatial exception - null argument';
- END IF;
-
- IF NOT EXISTS(SELECT name FROM topology.topology WHERE name = toponame) THEN
- RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name';
- END IF;
-
- IF aface = 0 THEN
- RAISE EXCEPTION
- 'SQL/MM Spatial exception - universal face has no geometry';
- END IF;
-
- BEGIN
-
- -- No such face
- sql := 'SELECT NOT EXISTS (SELECT * from ' || quote_ident(toponame)
- || '.face WHERE face_id = ' || aface
- || ') as none';
- EXECUTE sql INTO rec;
- IF rec.none THEN
- RAISE EXCEPTION 'SQL/MM Spatial exception - non-existent face.';
- END IF;
-
- --
- -- Construct face
- --
- sql :=
- 'SELECT ST_BuildArea(ST_Collect(geom)) as geom FROM '
- || quote_ident(toponame)
- || '.edge_data WHERE left_face = ' || aface
- || ' OR right_face = ' || aface;
- FOR rec IN EXECUTE sql
- LOOP
- RETURN rec.geom;
- END LOOP;
-
- EXCEPTION
- WHEN INVALID_SCHEMA_NAME THEN
- RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name';
- WHEN UNDEFINED_TABLE THEN
- RAISE EXCEPTION 'corrupted topology "%"', toponame;
- END;
-
- RETURN NULL;
-END
-$$
-LANGUAGE 'plpgsql' STABLE;
+ 'MODULE_PATHNAME', 'ST_GetFaceGeometry'
+ LANGUAGE 'c' STABLE;
--} ST_GetFaceGeometry
-- Scan the table looking for NULL geometries
FOR rec IN EXECUTE
'SELECT f1.face_id FROM '
- 'face_check f1 WHERE f1.geom IS NULL'
+ 'face_check f1 WHERE f1.geom IS NULL OR ST_IsEmpty(f1.geom)'
LOOP
-- Face missing !
retrec.error := 'face has no rings';