From ad80f72f391050a209f71cac5c5dc86e8e8418f8 Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Thu, 30 Jul 2015 14:23:55 +0000 Subject: [PATCH] Implement ST_AddIsoEdge in C Funded by Tuscany Region (Italy) - SITA (CIG: 60351023B8) git-svn-id: http://svn.osgeo.org/postgis/trunk@13858 b70326c6-7e19-0410-871a-916f4a2858ee --- liblwgeom/TODO | 2 +- liblwgeom/liblwgeom_topo.h | 5 +- liblwgeom/lwgeom_topo.c | 524 +++++++++++++------ topology/postgis_topology.c | 82 ++- topology/sql/sqlmm.sql.in | 182 +------ topology/test/regress/st_addisoedge_expected | 2 +- 6 files changed, 439 insertions(+), 358 deletions(-) diff --git a/liblwgeom/TODO b/liblwgeom/TODO index 0f7610b04..78309c67d 100644 --- a/liblwgeom/TODO +++ b/liblwgeom/TODO @@ -6,7 +6,7 @@ lwt_AddEdgeNewFaces X lwt_ModEdgeSplit X lwt_NewEdgesSplit X -lwt_AddIsoEdge +lwt_AddIsoEdge X lwt_GetFaceEdges lwt_GetFaceGeometry diff --git a/liblwgeom/liblwgeom_topo.h b/liblwgeom/liblwgeom_topo.h index aea9a2a85..7368c2ebe 100644 --- a/liblwgeom/liblwgeom_topo.h +++ b/liblwgeom/liblwgeom_topo.h @@ -958,12 +958,13 @@ void lwt_RemoveIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID node); * @param start_node identifier of the starting node * @param end_node identifier of the ending node * @param geom the edge geometry - * @return ID of the newly added edge + * @return ID of the newly added edge, or -1 on error + * (liblwgeom error handler will be invoked with error message) * */ LWT_ELEMID lwt_AddIsoEdge(LWT_TOPOLOGY* topo, LWT_ELEMID startNode, LWT_ELEMID endNode, - LWLINE *geom); + const LWLINE *geom); /** * Add a new edge possibly splitting a face (modifying it) diff --git a/liblwgeom/lwgeom_topo.c b/liblwgeom/lwgeom_topo.c index 35da652a7..bcc799d08 100644 --- a/liblwgeom/lwgeom_topo.c +++ b/liblwgeom/lwgeom_topo.c @@ -348,6 +348,16 @@ _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges) lwfree(edges); } +static void +_lwt_release_nodes(LWT_ISO_NODE *nodes, int num_nodes) +{ + int i; + for ( i=0; ibe_iface)); + return -1; + } + for ( i=0; inode_id == start_node ) continue; + if ( node->node_id == end_node ) continue; + /* check if the edge contains this node (not on boundary) */ + nodegg = LWGEOM2GEOS( lwpoint_as_lwgeom(node->geom) , 0); + /* ST_RelateMatch(rec.relate, 'T********') */ + contains = GEOSPreparedContains( prepared_edge, nodegg ); + GEOSGeom_destroy(nodegg); + if (contains == 2) + { + GEOSPreparedGeom_destroy(prepared_edge); + GEOSGeom_destroy(edgegg); + lwfree(nodes); + lwerror("GEOS exception on PreparedContains: %s", lwgeom_geos_errmsg); + return -1; + } + if ( contains ) + { + GEOSPreparedGeom_destroy(prepared_edge); + GEOSGeom_destroy(edgegg); + lwfree(nodes); + lwerror("SQL/MM Spatial exception - geometry crosses a node"); + return -1; + } + } + if ( nodes ) lwfree(nodes); /* may be NULL if num_nodes == 0 */ + + /* loop over each edge within the edge's gbox */ + edges = lwt_be_getEdgeWithinBox2D( topo, edgebox, &num_edges, LWT_COL_EDGE_ALL, 0 ); + LWDEBUGF(1, "lwt_be_getEdgeWithinBox2D returned %d edges", num_edges); + if ( num_edges == -1 ) { + GEOSPreparedGeom_destroy(prepared_edge); + GEOSGeom_destroy(edgegg); + lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); + return -1; + } + for ( i=0; igeom ) { + lwerror("Edge %d has NULL geometry!", edge->edge_id); + return -1; + } + + eegg = LWGEOM2GEOS( lwline_as_lwgeom(edge->geom), 0 ); + if ( ! eegg ) { + GEOSPreparedGeom_destroy(prepared_edge); + GEOSGeom_destroy(edgegg); + lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg); + return -1; + } + + LWDEBUGF(2, "Edge %d converted to GEOS", edge->edge_id); + + /* check if the edge crosses our edge (not boundary-boundary) */ + + relate = GEOSRelateBoundaryNodeRule(eegg, edgegg, 2); + if ( ! relate ) { + GEOSGeom_destroy(eegg); + GEOSPreparedGeom_destroy(prepared_edge); + GEOSGeom_destroy(edgegg); + lwerror("GEOSRelateBoundaryNodeRule error: %s", lwgeom_geos_errmsg); + return -1; + } + + LWDEBUGF(2, "Edge %d relate pattern is %s", edge->edge_id, relate); + + match = GEOSRelatePatternMatch(relate, "F********"); + if ( match ) { + if ( match == 2 ) { + GEOSPreparedGeom_destroy(prepared_edge); + GEOSGeom_destroy(edgegg); + GEOSGeom_destroy(eegg); + GEOSFree(relate); + lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg); + return -1; + } + else continue; /* no interior intersection */ + } + + match = GEOSRelatePatternMatch(relate, "1FFF*FFF2"); + if ( match ) { + GEOSPreparedGeom_destroy(prepared_edge); + GEOSGeom_destroy(edgegg); + GEOSGeom_destroy(eegg); + GEOSFree(relate); + if ( match == 2 ) { + lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg); + } else { + lwerror("SQL/MM Spatial exception - coincident edge %" PRId64, + edge->edge_id); + } + return -1; + } + + match = GEOSRelatePatternMatch(relate, "1********"); + if ( match ) { + GEOSPreparedGeom_destroy(prepared_edge); + GEOSGeom_destroy(edgegg); + GEOSGeom_destroy(eegg); + GEOSFree(relate); + if ( match == 2 ) { + lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg); + } else { + lwerror("Spatial exception - geometry intersects edge %" PRId64, + edge->edge_id); + } + return -1; + } + + match = GEOSRelatePatternMatch(relate, "T********"); + if ( match ) { + GEOSPreparedGeom_destroy(prepared_edge); + GEOSGeom_destroy(edgegg); + GEOSGeom_destroy(eegg); + GEOSFree(relate); + if ( match == 2 ) { + lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg); + } else { + lwerror("SQL/MM Spatial exception - geometry crosses edge %" + PRId64, edge->edge_id); + } + return -1; + } + + LWDEBUGF(2, "Edge %d analisys completed, it does no harm", edge->edge_id); + + GEOSFree(relate); + GEOSGeom_destroy(eegg); + } + if ( edges ) lwfree(edges); /* would be NULL if num_edges was 0 */ + + GEOSPreparedGeom_destroy(prepared_edge); + GEOSGeom_destroy(edgegg); + + return 0; +} + + +LWT_ELEMID +lwt_AddIsoEdge( LWT_TOPOLOGY* topo, LWT_ELEMID startNode, + LWT_ELEMID endNode, const LWLINE* geom ) +{ + int num_nodes; + int i; + LWT_ISO_EDGE newedge; + LWT_ISO_NODE *endpoints; + LWT_ELEMID containing_face = -1; + LWT_ELEMID *node_ids; + int skipISOChecks = 0; + POINT2D p1, p2; + + /* NOT IN THE SPECS: + * A closed edge is never isolated (as it forms a face) + */ + if ( startNode == endNode ) + { + lwerror("Closed edges would not be isolated, try lwt_AddEdgeNewFaces"); + return -1; + } + + if ( ! skipISOChecks ) + { + /* Acurve must be simple */ + if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) ) + { + lwerror("SQL/MM Spatial exception - curve not simple"); + return -1; + } + } + + /* + * Check for: + * existence of nodes + * nodes faces match + * Extract: + * nodes face id + * nodes geoms + */ + num_nodes = 2; + node_ids = lwalloc(sizeof(LWT_ELEMID) * num_nodes); + node_ids[0] = startNode; + node_ids[1] = endNode; + endpoints = lwt_be_getNodeById( topo, node_ids, &num_nodes, + LWT_COL_NODE_ALL ); + if ( ! endpoints ) { + lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); + return -1; + } + if ( num_nodes < 2 ) + { + _lwt_release_nodes(endpoints, num_nodes); + lwerror("SQL/MM Spatial exception - non-existent node"); + return -1; + } + for ( i=0; icontaining_face == -1 ) + { + _lwt_release_nodes(endpoints, num_nodes); + lwerror("SQL/MM Spatial exception - not isolated node"); + return -1; + } + if ( containing_face == -1 ) containing_face = n->containing_face; + else if ( containing_face != n->containing_face ) + { + _lwt_release_nodes(endpoints, num_nodes); + lwerror("SQL/MM Spatial exception - nodes in different faces"); + return -1; + } + + if ( ! skipISOChecks ) + { + if ( n->node_id == startNode ) + { + /* l) Check that start point of acurve match start node geoms. */ + getPoint2d_p(geom->points, 0, &p1); + getPoint2d_p(n->geom->point, 0, &p2); + if ( ! p2d_same(&p1, &p2) ) + { + _lwt_release_nodes(endpoints, num_nodes); + lwerror("SQL/MM Spatial exception - " + "start node not geometry start point."); + return -1; + } + } + else + { + /* m) Check that end point of acurve match end node geoms. */ + getPoint2d_p(geom->points, geom->points->npoints-1, &p1); + getPoint2d_p(n->geom->point, 0, &p2); + if ( ! p2d_same(&p1, &p2) ) + { + _lwt_release_nodes(endpoints, num_nodes); + lwerror("SQL/MM Spatial exception - " + "end node not geometry end point."); + return -1; + } + } + } + } + + if ( ! skipISOChecks ) + _lwt_CheckEdgeCrossing( topo, startNode, endNode, geom ); + + /* + * All checks passed, time to prepare the new edge + */ + + newedge.edge_id = lwt_be_getNextEdgeId( topo ); + if ( newedge.edge_id == -1 ) { + lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); + return -1; + } + + /* TODO: this should likely be an exception instead ! */ + if ( containing_face == -1 ) containing_face = 0; + + newedge.start_node = startNode; + newedge.end_node = endNode; + newedge.face_left = newedge.face_right = containing_face; + newedge.next_left = -newedge.edge_id; + newedge.next_right = newedge.edge_id; + newedge.geom = (LWLINE *)geom; /* const cast.. */ + + int ret = lwt_be_insertEdges(topo, &newedge, 1); + if ( ret == -1 ) { + lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); + return -1; + } else if ( ret == 0 ) { + lwerror("Insertion of split edge failed (no reason)"); + return -1; + } + + /* + * Update Node containing_face values + * + * the nodes anode and anothernode are no more isolated + * because now there is an edge connecting them + */ + LWT_ISO_NODE *updated_nodes = lwalloc(sizeof(LWT_ISO_NODE) * 2); + updated_nodes[0].node_id = startNode; + updated_nodes[0].containing_face = -1; + updated_nodes[1].node_id = endNode; + updated_nodes[1].containing_face = -1; + ret = lwt_be_updateNodesById(topo, updated_nodes, 2, + LWT_COL_NODE_CONTAINING_FACE); + if ( ret == -1 ) { + lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); + return -1; + } + + return newedge.edge_id; +} + static LWCOLLECTION * _lwt_EdgeSplit( LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWPOINT* pt, int skipISOChecks, LWT_ISO_EDGE** oldedge ) { @@ -1617,7 +1970,6 @@ _lwt_AddEdge( LWT_TOPOLOGY* topo, const LWPOINT *start_node_geom = NULL; const LWPOINT *end_node_geom = NULL; int num_nodes; - int num_edges; LWT_ISO_NODE *endpoints; int i; int prev_left; @@ -1767,175 +2119,7 @@ _lwt_AddEdge( LWT_TOPOLOGY* topo, } } - LWT_ISO_EDGE *edges; - LWT_ISO_NODE *nodes; - const GBOX *edgebox; - GEOSGeometry *edgegg; - const GEOSPreparedGeometry* prepared_edge; - - edgegg = LWGEOM2GEOS( lwline_as_lwgeom(geom), 0); - if ( ! edgegg ) { - lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg); - return -1; - } - prepared_edge = GEOSPrepare( edgegg ); - if ( ! prepared_edge ) { - lwerror("Could not prepare edge geometry: %s", lwgeom_geos_errmsg); - return -1; - } - edgebox = lwgeom_get_bbox( lwline_as_lwgeom(geom) ); - - /* loop over each node within the edge's gbox */ - nodes = lwt_be_getNodeWithinBox2D( topo, edgebox, &num_nodes, LWT_COL_NODE_ALL, 0 ); - lwnotice("lwt_be_getNodeWithinBox2D returned %d nodes", num_nodes); - if ( num_nodes == -1 ) { - GEOSPreparedGeom_destroy(prepared_edge); - GEOSGeom_destroy(edgegg); - lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); - return -1; - } - for ( i=0; inode_id == start_node ) continue; - if ( node->node_id == end_node ) continue; - /* check if the edge contains this node (not on boundary) */ - nodegg = LWGEOM2GEOS( lwpoint_as_lwgeom(node->geom) , 0); - /* ST_RelateMatch(rec.relate, 'T********') */ - contains = GEOSPreparedContains( prepared_edge, nodegg ); - GEOSGeom_destroy(nodegg); - if (contains == 2) - { - GEOSPreparedGeom_destroy(prepared_edge); - GEOSGeom_destroy(edgegg); - lwfree(nodes); - lwerror("GEOS exception on PreparedContains: %s", lwgeom_geos_errmsg); - return -1; - } - if ( contains ) - { - GEOSPreparedGeom_destroy(prepared_edge); - GEOSGeom_destroy(edgegg); - lwfree(nodes); - lwerror("SQL/MM Spatial exception - geometry crosses a node"); - return -1; - } - } - if ( nodes ) lwfree(nodes); /* may be NULL if num_nodes == 0 */ - - /* loop over each edge within the edge's gbox */ - edges = lwt_be_getEdgeWithinBox2D( topo, edgebox, &num_edges, LWT_COL_EDGE_ALL, 0 ); - LWDEBUGF(1, "lwt_be_getEdgeWithinBox2D returned %d edges", num_edges); - if ( num_edges == -1 ) { - GEOSPreparedGeom_destroy(prepared_edge); - GEOSGeom_destroy(edgegg); - lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); - return -1; - } - for ( i=0; igeom ) { - lwerror("Edge %d has NULL geometry!", edge->edge_id); - return -1; - } - - eegg = LWGEOM2GEOS( lwline_as_lwgeom(edge->geom), 0 ); - if ( ! eegg ) { - GEOSPreparedGeom_destroy(prepared_edge); - GEOSGeom_destroy(edgegg); - lwerror("Could not convert edge geometry to GEOS: %s", lwgeom_geos_errmsg); - return -1; - } - - LWDEBUGF(2, "Edge %d converted to GEOS", edge->edge_id); - - /* check if the edge crosses our edge (not boundary-boundary) */ - - relate = GEOSRelateBoundaryNodeRule(eegg, edgegg, 2); - if ( ! relate ) { - GEOSGeom_destroy(eegg); - GEOSPreparedGeom_destroy(prepared_edge); - GEOSGeom_destroy(edgegg); - lwerror("GEOSRelateBoundaryNodeRule error: %s", lwgeom_geos_errmsg); - return -1; - } - - LWDEBUGF(2, "Edge %d relate pattern is %s", edge->edge_id, relate); - - match = GEOSRelatePatternMatch(relate, "F********"); - if ( match ) { - if ( match == 2 ) { - GEOSPreparedGeom_destroy(prepared_edge); - GEOSGeom_destroy(edgegg); - GEOSGeom_destroy(eegg); - GEOSFree(relate); - lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg); - return -1; - } - else continue; /* no interior intersection */ - } - - match = GEOSRelatePatternMatch(relate, "1FFF*FFF2"); - if ( match ) { - GEOSPreparedGeom_destroy(prepared_edge); - GEOSGeom_destroy(edgegg); - GEOSGeom_destroy(eegg); - GEOSFree(relate); - if ( match == 2 ) { - lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg); - } else { - lwerror("SQL/MM Spatial exception - coincident edge %" PRId64, - edge->edge_id); - } - return -1; - } - - match = GEOSRelatePatternMatch(relate, "1********"); - if ( match ) { - GEOSPreparedGeom_destroy(prepared_edge); - GEOSGeom_destroy(edgegg); - GEOSGeom_destroy(eegg); - GEOSFree(relate); - if ( match == 2 ) { - lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg); - } else { - lwerror("Spatial exception - geometry intersects edge %" PRId64, - edge->edge_id); - } - return -1; - } - - match = GEOSRelatePatternMatch(relate, "T********"); - if ( match ) { - GEOSPreparedGeom_destroy(prepared_edge); - GEOSGeom_destroy(edgegg); - GEOSGeom_destroy(eegg); - GEOSFree(relate); - if ( match == 2 ) { - lwerror("GEOSRelatePatternMatch error: %s", lwgeom_geos_errmsg); - } else { - lwerror("SQL/MM Spatial exception - geometry crosses edge %" - PRId64, edge->edge_id); - } - return -1; - } - - LWDEBUGF(2, "Edge %d analisys completed, it does no harm", edge->edge_id); - - GEOSFree(relate); - GEOSGeom_destroy(eegg); - } - if ( edges ) lwfree(edges); /* would be NULL if num_edges was 0 */ - - GEOSPreparedGeom_destroy(prepared_edge); - GEOSGeom_destroy(edgegg); + _lwt_CheckEdgeCrossing( topo, start_node, end_node, geom ); } /* ! skipChecks */ diff --git a/topology/postgis_topology.c b/topology/postgis_topology.c index dc731b88d..fbc55a152 100644 --- a/topology/postgis_topology.c +++ b/topology/postgis_topology.c @@ -386,7 +386,7 @@ addNodeUpdate(StringInfo str, const LWT_ISO_NODE* node, int fields, if ( node->containing_face != -1 ) { appendStringInfo(str, "%" PRId64, node->containing_face); } else { - appendStringInfoString(str, "NULL"); + appendStringInfoString(str, "null::int"); } sep = sep1; } @@ -453,7 +453,7 @@ addNodeValues(StringInfo str, const LWT_ISO_NODE *node, int fields) if ( fields & LWT_COL_NODE_CONTAINING_FACE ) { if ( node->containing_face != -1 ) appendStringInfo(str, "%s%" PRId64, sep, node->containing_face); - else appendStringInfo(str, "%snull", sep); + else appendStringInfo(str, "%snull::int", sep); } if ( fields & LWT_COL_NODE_GEOM ) { @@ -463,7 +463,7 @@ addNodeValues(StringInfo str, const LWT_ISO_NODE *node, int fields) appendStringInfo(str, "%s'%s'::geometry", sep, hexewkb); lwfree(hexewkb); } else { - appendStringInfo(str, "%snull", sep); + appendStringInfo(str, "%snull::geometry", sep); } } @@ -484,7 +484,7 @@ addFaceValues(StringInfo str, LWT_ISO_FACE *face, int srid) face->mbr->xmin, face->mbr->ymin, face->mbr->xmax, face->mbr->ymax, srid); } else { - appendStringInfoString(str, ",null)"); + appendStringInfoString(str, ",null::geometry)"); } } @@ -2437,6 +2437,80 @@ Datum ST_AddIsoNode(PG_FUNCTION_ARGS) PG_RETURN_INT32(node_id); } +/* ST_AddIsoEdge(atopology, anode, anothernode, acurve) */ +Datum ST_AddIsoEdge(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(ST_AddIsoEdge); +Datum ST_AddIsoEdge(PG_FUNCTION_ARGS) +{ + text* toponame_text; + char* toponame; + LWT_ELEMID edge_id; + LWT_ELEMID start_node, end_node; + GSERIALIZED *geom; + LWGEOM *lwgeom; + LWLINE *curve; + LWT_TOPOLOGY *topo; + + if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || + PG_ARGISNULL(2) || PG_ARGISNULL(3) ) + { + 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); + + start_node = PG_GETARG_INT32(1); + end_node = PG_GETARG_INT32(2); + + if ( start_node == end_node ) { + lwpgerror("Closed edges would not be isolated, try ST_AddEdgeNewFaces"); + PG_RETURN_NULL(); + } + + geom = PG_GETARG_GSERIALIZED_P(3); + lwgeom = lwgeom_from_gserialized(geom); + curve = lwgeom_as_lwline(lwgeom); + if ( ! curve ) { + lwgeom_free(lwgeom); + PG_FREE_IF_COPY(geom, 3); + lwpgerror("SQL/MM Spatial exception - invalid curve"); + PG_RETURN_NULL(); + } + + 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_AddIsoEdge"); + edge_id = lwt_AddIsoEdge(topo, start_node, end_node, curve); + POSTGIS_DEBUG(1, "lwt_AddIsoNode returned"); + lwgeom_free(lwgeom); + PG_FREE_IF_COPY(geom, 3); + lwt_FreeTopology(topo); + + if ( edge_id == -1 ) { + /* should never reach this point, as lwerror would raise an exception */ + SPI_finish(); + PG_RETURN_NULL(); + } + + SPI_finish(); + PG_RETURN_INT32(edge_id); +} + /* ST_AddEdgeModFace(atopology, snode, enode, line) */ Datum ST_AddEdgeModFace(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(ST_AddEdgeModFace); diff --git a/topology/sql/sqlmm.sql.in b/topology/sql/sqlmm.sql.in index 01b83159e..fbf9a95b8 100644 --- a/topology/sql/sqlmm.sql.in +++ b/topology/sql/sqlmm.sql.in @@ -1733,186 +1733,8 @@ CREATE OR REPLACE FUNCTION topology.ST_ModEdgeSplit(atopology varchar, anedge in -- CREATE OR REPLACE FUNCTION topology.ST_AddIsoEdge(atopology varchar, anode integer, anothernode integer, acurve geometry) RETURNS INTEGER AS -$$ -DECLARE - aface INTEGER; - face GEOMETRY; - snodegeom GEOMETRY; - enodegeom GEOMETRY; - count INTEGER; - rec RECORD; - edgeid INTEGER; -BEGIN - - -- - -- All arguments required - -- - IF atopology IS NULL - OR anode IS NULL - OR anothernode IS NULL - OR acurve IS NULL - THEN - RAISE EXCEPTION - 'SQL/MM Spatial exception - null argument'; - END IF; - - -- NOT IN THE SPECS: - -- A closed edge is never isolated (as it forms a face) - IF anode = anothernode THEN - RAISE EXCEPTION - 'Closed edges would not be isolated, try ST_AddEdgeNewFaces'; - END IF; - - -- - -- Acurve must be a LINESTRING - -- - IF substring(geometrytype(acurve), 1, 4) != 'LINE' - THEN - RAISE EXCEPTION - 'SQL/MM Spatial exception - invalid curve'; - END IF; - - -- - -- Acurve must be simple - -- - IF NOT ST_IsSimple(acurve) - THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - curve not simple'; - END IF; - - -- - -- Check for: - -- existence of nodes - -- nodes faces match - -- Extract: - -- nodes face id - -- nodes geoms - -- - aface := NULL; - count := 0; - FOR rec IN EXECUTE 'SELECT geom, containing_face, node_id FROM ' - || quote_ident(atopology) || '.node - WHERE node_id = ' || anode || - ' OR node_id = ' || anothernode - LOOP - - IF rec.containing_face IS NULL THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - not isolated node'; - END IF; - - IF aface IS NULL THEN - aface := rec.containing_face; - ELSE - IF aface != rec.containing_face THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - nodes in different faces'; - END IF; - END IF; - - -- Get nodes geom - IF rec.node_id = anode THEN - snodegeom = rec.geom; - ELSE - enodegeom = rec.geom; - END IF; - - count = count+1; - - END LOOP; - - -- TODO: don't need count, can do with snodegeom/enodegeom instead.. - IF count < 2 THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - non-existent node'; - END IF; - - - -- - -- l) Check that start point of acurve match start node - -- geoms. - -- - IF ST_X(snodegeom) != ST_X(ST_StartPoint(acurve)) OR - ST_Y(snodegeom) != ST_Y(ST_StartPoint(acurve)) - THEN - RAISE EXCEPTION - 'SQL/MM Spatial exception - start node not geometry start point.'; - END IF; - - -- - -- m) Check that end point of acurve match end node - -- geoms. - -- - IF ST_X(enodegeom) != ST_X(ST_EndPoint(acurve)) OR - ST_Y(enodegeom) != ST_Y(ST_EndPoint(acurve)) - THEN - RAISE EXCEPTION - 'SQL/MM Spatial exception - end node not geometry end point.'; - END IF; - - -- - -- n) Check if curve crosses (contains) any node - -- I used _contains_ here to leave endpoints out - -- - FOR rec IN EXECUTE 'SELECT node_id FROM ' - || quote_ident(atopology) || '.node ' - || ' WHERE ST_Contains($1, geom)' - USING acurve - LOOP - RAISE EXCEPTION - 'SQL/MM Spatial exception - geometry crosses a node'; - END LOOP; - - -- - -- o) Check if curve intersects any other edge - -- - FOR rec IN EXECUTE 'SELECT * FROM ' - || quote_ident(atopology) || '.edge_data - WHERE ST_Intersects(geom, $1)' - USING acurve - LOOP - RAISE EXCEPTION 'SQL/MM Spatial exception - geometry intersects an edge'; - END LOOP; - - -- - -- Get new edge id from sequence - -- - FOR rec IN EXECUTE 'SELECT nextval(''' || - atopology || '.edge_data_edge_id_seq'')' - LOOP - edgeid = rec.nextval; - END LOOP; - - -- TODO: this should likely be an exception instead ! - IF aface IS NULL THEN - aface := 0; - END IF; - - -- - -- Insert the new row - -- - EXECUTE 'INSERT INTO ' || quote_ident(atopology) - || '.edge VALUES(' || edgeid || ',' || anode - || ',' || anothernode || ',' || (-edgeid) - || ',' || edgeid || ',' - || aface || ',' || aface || ',$1)' - USING acurve; - - -- - -- Update Node containing_face values - -- - -- the nodes anode and anothernode are no more isolated - -- because now there is an edge connecting them - -- - EXECUTE 'UPDATE ' || quote_ident(atopology) - || '.node SET containing_face = NULL where (node_id =' - || anode - || ' OR node_id=' - || anothernode - || ')'; - - RETURN edgeid; - -END -$$ -LANGUAGE 'plpgsql' VOLATILE; + 'MODULE_PATHNAME','ST_AddIsoEdge' + LANGUAGE 'c' VOLATILE; --} ST_AddIsoEdge -- Internal function used by ST_ChangeEdgeGeom to compare diff --git a/topology/test/regress/st_addisoedge_expected b/topology/test/regress/st_addisoedge_expected index 182dc6ae7..f1ae4f023 100644 --- a/topology/test/regress/st_addisoedge_expected +++ b/topology/test/regress/st_addisoedge_expected @@ -29,5 +29,5 @@ ERROR: SQL/MM Spatial exception - not isolated node ERROR: SQL/MM Spatial exception - not isolated node ERROR: Closed edges would not be isolated, try ST_AddEdgeNewFaces ERROR: SQL/MM Spatial exception - not isolated node -ERROR: SQL/MM Spatial exception - geometry intersects an edge +ERROR: SQL/MM Spatial exception - geometry crosses edge 2 Topology 'tt' dropped -- 2.40.0