From: Sandro Santilli Date: Mon, 20 Jul 2015 15:51:01 +0000 (+0000) Subject: Implement ST_AddEdgeNewFaces in C X-Git-Tag: 2.2.0rc1~241 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fb3e2cad85c1ef2ff807b4722ca78f17c2250fbe;p=postgis Implement ST_AddEdgeNewFaces in C Adds BE callback to delete faces by id Funded by Tuscany Region (Italy) - SITA (CIG: 60351023B8) git-svn-id: http://svn.osgeo.org/postgis/trunk@13814 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/liblwgeom/liblwgeom_topo.h b/liblwgeom/liblwgeom_topo.h index 75843f28a..d6438b342 100644 --- a/liblwgeom/liblwgeom_topo.h +++ b/liblwgeom/liblwgeom_topo.h @@ -520,7 +520,7 @@ typedef struct LWT_BE_CALLBACKS_T { * as a result of face splitting, or -1 if the old face was * modified rather than replaced. * - * @return 1 on success, 0 on error + * @return 1 on success, 0 on error (@see lastErroMessage) * */ int (*updateTopoGeomFaceSplit) ( @@ -674,6 +674,22 @@ typedef struct LWT_BE_CALLBACKS_T { int upd_fields ); + /** + * Delete faces by id + * + * @param topo the topology to act upon + * @param ids an array of face identifiers + * @param numelems number of face identifiers in the ids array + * + * @return number of faces being deleted or -1 on error + * (@see lastErroMessage) + */ + int (*deleteFacesById) ( + const LWT_BE_TOPOLOGY* topo, + const LWT_ELEMID* ids, + int numelems + ); + } LWT_BE_CALLBACKS; @@ -1056,12 +1072,15 @@ LWT_ELEMID lwt_AddEdgeModFace(LWT_TOPOLOGY* topo, * @param start_node identifier of the starting node * @param end_node identifier of the ending node * @param geom the edge geometry + * @param skipChecks if non-zero skips consistency checks + * (curve being simple and valid, start/end nodes + * consistency actual face containement) * @return ID of the newly added edge * */ LWT_ELEMID lwt_AddEdgeNewFaces(LWT_TOPOLOGY* topo, LWT_ELEMID start_node, LWT_ELEMID end_node, - LWLINE *geom); + LWLINE *geom, int skipChecks); /** * Remove an edge, possibly merging two faces (replacing both with a new one) diff --git a/liblwgeom/lwgeom_topo.c b/liblwgeom/lwgeom_topo.c index 361bc809a..2ea2e2a20 100644 --- a/liblwgeom/lwgeom_topo.c +++ b/liblwgeom/lwgeom_topo.c @@ -162,6 +162,12 @@ lwt_be_insertFaces(LWT_TOPOLOGY* topo, LWT_ISO_FACE* face, int numelems) CBT2(topo, insertFaces, face, numelems); } +static int +lwt_be_deleteFacesById(const LWT_TOPOLOGY* topo, const LWT_ELEMID* ids, int numelems) +{ + CBT2(topo, deleteFacesById, ids, numelems); +} + LWT_ELEMID lwt_be_getNextEdgeId(LWT_TOPOLOGY* topo) { @@ -1596,10 +1602,10 @@ _lwt_AddFaceSplit( LWT_TOPOLOGY* topo, return newface.face_id; } -LWT_ELEMID -lwt_AddEdgeModFace( LWT_TOPOLOGY* topo, - LWT_ELEMID start_node, LWT_ELEMID end_node, - LWLINE *geom, int skipChecks ) +static LWT_ELEMID +_lwt_AddEdge( LWT_TOPOLOGY* topo, + LWT_ELEMID start_node, LWT_ELEMID end_node, + LWLINE *geom, int skipChecks, int modFace ) { LWT_ISO_EDGE newedge; LWGEOM *cleangeom; @@ -2118,29 +2124,81 @@ lwt_AddEdgeModFace( LWT_TOPOLOGY* topo, } } - /* Check face splitting */ - int newface = _lwt_AddFaceSplit( topo, newedge.edge_id, - newedge.face_left, 0 ); - if ( newface == 0 ) return newedge.edge_id; /* no split */ + int newface1 = -1; - if ( newface < 0 ) + if ( ! modFace ) { - /* face on the left is the universe face */ - /* must be forming a maximal ring in universal face */ - newface = _lwt_AddFaceSplit( topo, -newedge.edge_id, - newedge.face_left, 0 ); - if ( newface < 0 ) return newedge.edge_id; /* no split */ + newface1 = _lwt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 0 ); + if ( newface1 == 0 ) { + LWDEBUG(1, "New edge does not split any face"); + return newedge.edge_id; /* no split */ + } } - else + + /* Check face splitting */ + int newface = _lwt_AddFaceSplit( topo, newedge.edge_id, + newedge.face_left, 0 ); + if ( modFace ) { - _lwt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 1 ); + if ( newface == 0 ) { + LWDEBUG(1, "New edge does not split any face"); + return newedge.edge_id; /* no split */ + } + + if ( newface < 0 ) + { + /* face on the left is the universe face */ + /* must be forming a maximal ring in universal face */ + newface = _lwt_AddFaceSplit( topo, -newedge.edge_id, + newedge.face_left, 0 ); + if ( newface < 0 ) return newedge.edge_id; /* no split */ + } + else + { + _lwt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 1 ); + } } + 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 */ - ret = lwt_be_updateTopoGeomFaceSplit(topo, newedge.face_left, - newface, -1); + if ( newedge.face_left != 0 ) + { + ret = lwt_be_updateTopoGeomFaceSplit(topo, newedge.face_left, + newface, newface1); + if ( ret == 0 ) { + lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); + return -1; + } + + if ( ! modFace ) + { + /* drop old face from the face table */ + ret = lwt_be_deleteFacesById(topo, &(newedge.face_left), 1); + if ( ret == -1 ) { + lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); + return -1; + } + } + } return newedge.edge_id; } + +LWT_ELEMID +lwt_AddEdgeModFace( LWT_TOPOLOGY* topo, + LWT_ELEMID start_node, LWT_ELEMID end_node, + LWLINE *geom, int skipChecks ) +{ + return _lwt_AddEdge( topo, start_node, end_node, geom, skipChecks, 1 ); +} + +LWT_ELEMID +lwt_AddEdgeNewFaces( LWT_TOPOLOGY* topo, + LWT_ELEMID start_node, LWT_ELEMID end_node, + LWLINE *geom, int skipChecks ) +{ + return _lwt_AddEdge( topo, start_node, end_node, geom, skipChecks, 0 ); +} diff --git a/topology/postgis_topology.c b/topology/postgis_topology.c index 90e640c66..73a6dcd26 100644 --- a/topology/postgis_topology.c +++ b/topology/postgis_topology.c @@ -1720,6 +1720,8 @@ cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo, appendStringInfo(sql, " RETURNING %s", proj); } + POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeSplit query: %s", sql->data); + spi_result = SPI_execute(sql->data, new_edge2 == -1 ? !topo->be_data->data_changed : false, 0); if ( spi_result != ( new_edge2 == -1 ? SPI_OK_SELECT : SPI_OK_DELETE_RETURNING ) ) { cberror(topo->be_data, "unexpected return (%d) from query execution: %s", @@ -1729,6 +1731,7 @@ cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo, if ( spi_result == SPI_OK_DELETE_RETURNING && SPI_processed ) { + POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeSplit: deleted %d faces", SPI_processed); topo->be_data->data_changed = true; } @@ -1972,6 +1975,40 @@ cb_getFaceContainingPoint( const LWT_BE_TOPOLOGY* topo, const LWPOINT* pt ) return face_id; } +static int +cb_deleteFacesById( const LWT_BE_TOPOLOGY* topo, + const LWT_ELEMID* ids, int numelems ) +{ + int spi_result, i; + StringInfoData sqldata; + StringInfo sql = &sqldata; + + initStringInfo(sql); + appendStringInfo(sql, "DELETE FROM \"%s\".face WHERE face_id IN (", topo->name); + for (i=0; idata); + + spi_result = SPI_execute( sql->data, false, 0 ); + if ( spi_result != SPI_OK_DELETE ) + { + cberror(topo->be_data, "unexpected return (%d) from query execution: %s", + spi_result, sql->data); + return -1; + } + pfree(sqldata.data); + + if ( SPI_processed ) topo->be_data->data_changed = true; + + POSTGIS_DEBUGF(1, "cb_deleteFacesById: delete query processed %d rows", + SPI_processed); + + return SPI_processed; +} + static LWT_ISO_NODE* cb_getNodeWithinBox2D ( const LWT_BE_TOPOLOGY* topo, const GBOX* box, int* numelems, int fields, int limit ) @@ -2137,7 +2174,8 @@ static LWT_BE_CALLBACKS be_callbacks = { cb_updateEdgesById, cb_getEdgeByFace, cb_getNodeByFace, - cb_updateNodesById + cb_updateNodesById, + cb_deleteFacesById }; @@ -2457,3 +2495,70 @@ Datum ST_AddEdgeModFace(PG_FUNCTION_ARGS) SPI_finish(); PG_RETURN_INT32(edge_id); } + +/* ST_AddEdgeNewFaces(atopology, snode, enode, line) */ +Datum ST_AddEdgeNewFaces(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(ST_AddEdgeNewFaces); +Datum ST_AddEdgeNewFaces(PG_FUNCTION_ARGS) +{ + text* toponame_text; + char* toponame; + LWT_ELEMID startnode_id, endnode_id; + int edge_id; + GSERIALIZED *geom; + LWGEOM *lwgeom; + LWLINE *line; + 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); + + startnode_id = PG_GETARG_INT32(1) ; + endnode_id = PG_GETARG_INT32(2) ; + + geom = PG_GETARG_GSERIALIZED_P(3); + lwgeom = lwgeom_from_gserialized(geom); + line = lwgeom_as_lwline(lwgeom); + if ( ! line ) { + lwgeom_free(lwgeom); + PG_FREE_IF_COPY(geom, 2); + lwpgerror("ST_AddEdgeModFace fourth argument must be a line geometry"); + 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_AddEdgeNewFaces"); + edge_id = lwt_AddEdgeNewFaces(topo, startnode_id, endnode_id, line, 0); + POSTGIS_DEBUG(1, "lwt_AddEdgeNewFaces 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); +} diff --git a/topology/sql/sqlmm.sql.in b/topology/sql/sqlmm.sql.in index b6f3f2d64..01b83159e 100644 --- a/topology/sql/sqlmm.sql.in +++ b/topology/sql/sqlmm.sql.in @@ -2491,616 +2491,8 @@ LANGUAGE 'plpgsql' VOLATILE; -- CREATE OR REPLACE FUNCTION topology.ST_AddEdgeNewFaces(atopology varchar, anode integer, anothernode integer, acurve geometry) RETURNS INTEGER AS -$$ -DECLARE - rec RECORD; - i INTEGER; - topoid INTEGER; - az FLOAT8; - span RECORD; -- start point analysis data - epan RECORD; -- end point analysis data - fan RECORD; -- face analisys - newedge RECORD; -- informations about new edge - sql TEXT; - newfaces INTEGER[]; - newface INTEGER; -BEGIN - - -- - -- All args 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; - - -- - -- Acurve must be a LINESTRING - -- - IF substring(geometrytype(acurve), 1, 4) != 'LINE' - THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - invalid curve'; - END IF; - - -- - -- Curve must be simple - -- - IF NOT ST_IsSimple(acurve) THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - curve not simple'; - END IF; - - -- - -- Get topology id - -- - BEGIN - SELECT id FROM topology.topology - INTO STRICT topoid WHERE name = atopology; - EXCEPTION - WHEN NO_DATA_FOUND THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name'; - END; - - -- Initialize new edge info (will be filled up more later) - SELECT anode as start_node, anothernode as end_node, acurve as geom, - NULL::int as next_left_edge, NULL::int as next_right_edge, - NULL::int as left_face, NULL::int as right_face, NULL::int as edge_id, - NULL::int as prev_left_edge, NULL::int as prev_right_edge, -- convenience - anode = anothernode as isclosed, -- convenience - false as start_node_isolated, -- convenience - false as end_node_isolated, -- convenience - NULL::geometry as start_node_geom, -- convenience - NULL::geometry as end_node_geom, -- convenience - ST_RemoveRepeatedPoints(acurve) as cleangeom -- convenience - INTO newedge; - - -- Compute azimuth of first edge end on start node - SELECT null::int AS nextCW, null::int AS nextCCW, - null::float8 AS minaz, null::float8 AS maxaz, - false AS was_isolated, - ST_Azimuth(ST_StartPoint(newedge.cleangeom), - ST_PointN(newedge.cleangeom, 2)) AS myaz - INTO span; - IF span.myaz IS NULL THEN - RAISE EXCEPTION 'Invalid edge (no two distinct vertices exist)'; - END IF; - - -- Compute azimuth of last edge end on end node - SELECT null::int AS nextCW, null::int AS nextCCW, - null::float8 AS minaz, null::float8 AS maxaz, - false AS was_isolated, - ST_Azimuth(ST_EndPoint(newedge.cleangeom), - ST_PointN(newedge.cleangeom, - ST_NumPoints(newedge.cleangeom)-1)) AS myaz - INTO epan; - IF epan.myaz IS NULL THEN - RAISE EXCEPTION 'Invalid edge (no two distinct vertices exist)'; - END IF; - - - -- - -- Check endpoints existance, match with Curve geometry - -- and get face information (if any) - -- - i := 0; - FOR rec IN EXECUTE 'SELECT node_id, containing_face, geom FROM ' - || quote_ident(atopology) - || '.node WHERE node_id IN ( ' - || anode || ',' || anothernode - || ')' - LOOP - IF rec.containing_face IS NOT NULL THEN -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'containing_face for node %:%', - rec.node_id, rec.containing_face; -#endif - IF newedge.left_face IS NULL THEN - newedge.left_face := rec.containing_face; - newedge.right_face := rec.containing_face; - ELSE - IF newedge.left_face != rec.containing_face THEN - RAISE EXCEPTION - 'SQL/MM Spatial exception - geometry crosses an edge (endnodes in faces % and %)', newedge.left_face, rec.containing_face; - END IF; - END IF; - END IF; - - IF rec.node_id = anode THEN - newedge.start_node_geom = rec.geom; - END IF; - - IF rec.node_id = anothernode THEN - newedge.end_node_geom = rec.geom; - END IF; - - i := i + 1; - END LOOP; - - IF newedge.start_node_geom IS NULL - THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - non-existent node'; - ELSIF NOT ST_Equals(newedge.start_node_geom, ST_StartPoint(acurve)) - THEN - RAISE EXCEPTION - 'SQL/MM Spatial exception - start node not geometry start point.'; - END IF; - - IF newedge.end_node_geom IS NULL - THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - non-existent node'; - ELSIF NOT ST_Equals(newedge.end_node_geom, ST_EndPoint(acurve)) - THEN - RAISE EXCEPTION - 'SQL/MM Spatial exception - end node not geometry end point.'; - END IF; - - RAISE DEBUG 'All Checked !'; - - -- - -- Check if this geometry crosses any node - -- - FOR rec IN EXECUTE - 'SELECT node_id, ST_Relate(geom, $1, 2) as relate FROM ' - || quote_ident(atopology) - || '.node WHERE geom && $1' - USING acurve - LOOP - IF ST_RelateMatch(rec.relate, 'T********') THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - geometry crosses a node'; - END IF; - END LOOP; - - -- - -- Check if this geometry has any interaction with any existing edge - -- - FOR rec IN EXECUTE 'SELECT edge_id, ST_Relate(geom, $1, 2) as im FROM ' - || quote_ident(atopology) - || '.edge_data WHERE geom && $1' - USING acurve - LOOP - - --RAISE DEBUG 'IM=%',rec.im; - - IF ST_RelateMatch(rec.im, 'F********') THEN - CONTINUE; -- no interior intersection - END IF; - - IF ST_RelateMatch(rec.im, '1FFF*FFF2') THEN - RAISE EXCEPTION - 'SQL/MM Spatial exception - coincident edge %', rec.edge_id; - END IF; - - -- NOT IN THE SPECS: geometry touches an edge - IF ST_RelateMatch(rec.im, '1********') THEN - RAISE EXCEPTION - 'Spatial exception - geometry intersects edge %', rec.edge_id; - END IF; - - IF ST_RelateMatch(rec.im, 'T********') THEN - RAISE EXCEPTION - 'SQL/MM Spatial exception - geometry crosses edge %', rec.edge_id; - END IF; - - END LOOP; - - --------------------------------------------------------------- - -- - -- All checks passed, time to prepare the new edge - -- - --------------------------------------------------------------- - - EXECUTE 'SELECT nextval(' || quote_literal( - quote_ident(atopology) || '.edge_data_edge_id_seq') || ')' - INTO STRICT newedge.edge_id; - - - -- Find links on start node -- { - -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'My start-segment azimuth: %', span.myaz; -#endif - - sql := - 'SELECT edge_id, -1 AS end_node, start_node, left_face, right_face, ' - || 'ST_RemoveRepeatedPoints(geom) as geom FROM ' - || quote_ident(atopology) - || '.edge_data WHERE start_node = ' || anode - || ' UNION SELECT edge_id, end_node, -1, left_face, right_face, ' - || 'ST_RemoveRepeatedPoints(geom) FROM ' - || quote_ident(atopology) - || '.edge_data WHERE end_node = ' || anode; - IF newedge.isclosed THEN - sql := sql || ' UNION SELECT ' - || newedge.edge_id || ',' || newedge.end_node - || ',-1,0,0,$1'; -- pretend we start elsewhere - END IF; - i := 0; - FOR rec IN EXECUTE sql USING newedge.cleangeom - LOOP -- incident edges { - - i := i + 1; - - IF rec.start_node = anode THEN - -- - -- Edge starts at our node, we compute - -- azimuth from node to its second point - -- - az := ST_Azimuth(ST_StartPoint(rec.geom), ST_PointN(rec.geom, 2)); - - ELSE - -- - -- Edge ends at our node, we compute - -- azimuth from node to its second-last point - -- - az := ST_Azimuth(ST_EndPoint(rec.geom), - ST_PointN(rec.geom, ST_NumPoints(rec.geom)-1)); - rec.edge_id := -rec.edge_id; - - END IF; - - IF az IS NULL THEN - RAISE EXCEPTION 'Invalid edge % found (no two distinct nodes exist)', - rec.edge_id; - END IF; - -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'Edge % - az % (%) - fl:% fr:%', - rec.edge_id, az, az - span.myaz, rec.left_face, rec.right_face; -#endif - - az = az - span.myaz; - IF az < 0 THEN - az := az + 2*PI(); - END IF; - - -- RAISE DEBUG ' normalized az %', az; - - IF span.maxaz IS NULL OR az > span.maxaz THEN - span.maxaz := az; - span.nextCCW := rec.edge_id; - IF abs(rec.edge_id) != newedge.edge_id THEN - IF rec.edge_id < 0 THEN - -- TODO: check for mismatch ? - newedge.left_face := rec.left_face; - ELSE - -- TODO: check for mismatch ? - newedge.left_face := rec.right_face; - END IF; - END IF; - END IF; - - IF span.minaz IS NULL OR az < span.minaz THEN - span.minaz := az; - span.nextCW := rec.edge_id; - IF abs(rec.edge_id) != newedge.edge_id THEN - IF rec.edge_id < 0 THEN - -- TODO: check for mismatch ? - newedge.right_face := rec.right_face; - ELSE - -- TODO: check for mismatch ? - newedge.right_face := rec.left_face; - END IF; - END IF; - END IF; - - --RAISE DEBUG 'Closest edges: CW:%(%) CCW:%(%)', span.nextCW, span.minaz, span.nextCCW, span.maxaz; - - END LOOP; -- incident edges } - -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'span ROW_COUNT: %', i; -#endif - IF newedge.isclosed THEN - IF i < 2 THEN span.was_isolated = true; END IF; - ELSE - IF i < 1 THEN span.was_isolated = true; END IF; - END IF; - - IF span.nextCW IS NULL THEN - -- This happens if the destination node is isolated - newedge.next_right_edge := newedge.edge_id; - newedge.prev_left_edge := -newedge.edge_id; - ELSE - newedge.next_right_edge := span.nextCW; - newedge.prev_left_edge := -span.nextCCW; - END IF; - -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'edge:%', newedge.edge_id; -#endif -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG ' left:%, next:%, prev:%', - newedge.left_face, newedge.next_left_edge, newedge.prev_left_edge; -#endif -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG ' right:%, next:%, prev:%', - newedge.right_face, newedge.next_right_edge, newedge.prev_right_edge; -#endif - - -- } start_node analysis - - - -- Find links on end_node { - -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'My end-segment azimuth: %', epan.myaz; -#endif - - sql := - 'SELECT edge_id, -1 as end_node, start_node, left_face, right_face, ' - || 'ST_RemoveRepeatedPoints(geom) as geom FROM ' - || quote_ident(atopology) - || '.edge_data WHERE start_node = ' || anothernode - || 'UNION SELECT edge_id, end_node, -1, left_face, right_face, ' - || 'ST_RemoveRepeatedPoints(geom) FROM ' - || quote_ident(atopology) - || '.edge_data WHERE end_node = ' || anothernode; - IF newedge.isclosed THEN - sql := sql || ' UNION SELECT ' - || newedge.edge_id || ',' || -1 -- pretend we end elsewhere - || ',' || newedge.start_node || ',0,0,$1'; - END IF; - i := 0; - FOR rec IN EXECUTE sql USING newedge.cleangeom - LOOP -- incident edges { - - i := i + 1; - - IF rec.start_node = anothernode THEN - -- - -- Edge starts at our node, we compute - -- azimuth from node to its second point - -- - az := ST_Azimuth(ST_StartPoint(rec.geom), - ST_PointN(rec.geom, 2)); - - ELSE - -- - -- Edge ends at our node, we compute - -- azimuth from node to its second-last point - -- - az := ST_Azimuth(ST_EndPoint(rec.geom), - ST_PointN(rec.geom, ST_NumPoints(rec.geom)-1)); - rec.edge_id := -rec.edge_id; - - END IF; - -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'Edge % - az % (%)', rec.edge_id, az, az - epan.myaz; -#endif - - az := az - epan.myaz; - IF az < 0 THEN - az := az + 2*PI(); - END IF; - - -- RAISE DEBUG ' normalized az %', az; - - IF epan.maxaz IS NULL OR az > epan.maxaz THEN - epan.maxaz := az; - epan.nextCCW := rec.edge_id; - IF abs(rec.edge_id) != newedge.edge_id THEN - IF rec.edge_id < 0 THEN - -- TODO: check for mismatch ? - newedge.right_face := rec.left_face; - ELSE - -- TODO: check for mismatch ? - newedge.right_face := rec.right_face; - END IF; - END IF; - END IF; - - IF epan.minaz IS NULL OR az < epan.minaz THEN - epan.minaz := az; - epan.nextCW := rec.edge_id; - IF abs(rec.edge_id) != newedge.edge_id THEN - IF rec.edge_id < 0 THEN - -- TODO: check for mismatch ? - newedge.left_face := rec.right_face; - ELSE - -- TODO: check for mismatch ? - newedge.left_face := rec.left_face; - END IF; - END IF; - END IF; - - --RAISE DEBUG 'Closest edges: CW:%(%) CCW:%(%)', epan.nextCW, epan.minaz, epan.nextCCW, epan.maxaz; - - END LOOP; -- incident edges } - -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'epan ROW_COUNT: %', i; -#endif - IF newedge.isclosed THEN - IF i < 2 THEN epan.was_isolated = true; END IF; - ELSE - IF i < 1 THEN epan.was_isolated = true; END IF; - END IF; - - IF epan.nextCW IS NULL THEN - -- This happens if the destination node is isolated - newedge.next_left_edge := -newedge.edge_id; - newedge.prev_right_edge := newedge.edge_id; - ELSE - newedge.next_left_edge := epan.nextCW; - newedge.prev_right_edge := -epan.nextCCW; - END IF; - - -- } end_node analysis - -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'edge:%', newedge.edge_id; -#endif -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG ' left:%, next:%, prev:%', - newedge.left_face, newedge.next_left_edge, newedge.prev_left_edge; -#endif -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG ' right:%, next:%, prev:%', - newedge.right_face, newedge.next_right_edge, newedge.prev_right_edge; -#endif - - ---------------------------------------------------------------------- - -- - -- If we don't have faces setup by now we must have encountered - -- a malformed topology (no containing_face on isolated nodes, no - -- left/right faces on adjacent edges or mismatching values) - -- - ---------------------------------------------------------------------- - IF newedge.left_face != newedge.right_face THEN - RAISE EXCEPTION 'Left(%)/right(%) faces mismatch: invalid topology ?', - newedge.left_face, newedge.right_face; - END IF; - IF newedge.left_face IS NULL THEN - RAISE EXCEPTION 'Could not derive edge face from linked primitives: invalid topology ?'; - END IF; - - ---------------------------------------------------------------------- - -- - -- Insert the new edge, and update all linking - -- - ---------------------------------------------------------------------- - - -- Insert the new edge with what we have so far - EXECUTE 'INSERT INTO ' || quote_ident(atopology) - || '.edge VALUES(' || newedge.edge_id - || ',' || newedge.start_node - || ',' || newedge.end_node - || ',' || newedge.next_left_edge - || ',' || newedge.next_right_edge - || ',' || newedge.left_face - || ',' || newedge.right_face - || ',$1)' - USING newedge.geom - ; - - -- Link prev_left_edge to us - -- (if it's not us already) - IF abs(newedge.prev_left_edge) != newedge.edge_id THEN - IF newedge.prev_left_edge > 0 THEN - -- its next_left_edge is us - EXECUTE 'UPDATE ' || quote_ident(atopology) - || '.edge_data SET next_left_edge = ' - || newedge.edge_id - || ', abs_next_left_edge = ' - || newedge.edge_id - || ' WHERE edge_id = ' - || newedge.prev_left_edge; - ELSE - -- its next_right_edge is us - EXECUTE 'UPDATE ' || quote_ident(atopology) - || '.edge_data SET next_right_edge = ' - || newedge.edge_id - || ', abs_next_right_edge = ' - || newedge.edge_id - || ' WHERE edge_id = ' - || -newedge.prev_left_edge; - END IF; - END IF; - - -- Link prev_right_edge to us - -- (if it's not us already) - IF abs(newedge.prev_right_edge) != newedge.edge_id THEN - IF newedge.prev_right_edge > 0 THEN - -- its next_left_edge is -us - EXECUTE 'UPDATE ' || quote_ident(atopology) - || '.edge_data SET next_left_edge = ' - || -newedge.edge_id - || ', abs_next_left_edge = ' - || newedge.edge_id - || ' WHERE edge_id = ' - || newedge.prev_right_edge; - ELSE - -- its next_right_edge is -us - EXECUTE 'UPDATE ' || quote_ident(atopology) - || '.edge_data SET next_right_edge = ' - || -newedge.edge_id - || ', abs_next_right_edge = ' - || newedge.edge_id - || ' WHERE edge_id = ' - || -newedge.prev_right_edge; - END IF; - END IF; - - -- NOT IN THE SPECS... - -- set containing_face = null for start_node and end_node - -- if they where isolated - IF span.was_isolated OR epan.was_isolated THEN - EXECUTE 'UPDATE ' || quote_ident(atopology) - || '.node SET containing_face = null WHERE node_id IN (' - || anode || ',' || anothernode || ')'; - END IF; - - -------------------------------------------- - -- Check face splitting - -------------------------------------------- - -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'Checking right face'; -#endif - - SELECT topology._ST_AddFaceSplit(atopology, -newedge.edge_id, newedge.left_face, false) - INTO newface; - - IF newface = 0 THEN -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG ' No split'; -#endif - RETURN newedge.edge_id; - END IF; - - newfaces[1] := newface; - -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'Checking left face'; -#endif - SELECT topology._ST_AddFaceSplit(atopology, newedge.edge_id, newedge.left_face, false) - INTO newface; - - newfaces[2] := newface; - - IF newedge.left_face != 0 THEN -- { - - -- NOT IN THE SPECS: - -- update TopoGeometry compositions to substitute oldface with newfaces - sql := 'UPDATE ' - || quote_ident(atopology) - || '.relation r set element_id = ' || newfaces[1] - || ' FROM topology.layer l ' - || ' WHERE l.topology_id = ' || topoid - || ' AND l.level = 0 ' - || ' AND l.layer_id = r.layer_id ' - || ' AND r.element_id = ' || newedge.left_face - || ' AND r.element_type = 3 RETURNING r.topogeo_id, r.layer_id'; - --RAISE DEBUG 'SQL: %', sql; - FOR rec IN EXECUTE sql - LOOP -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'TopoGeometry % in layer % contained the face being split (%) - updating to contain both new faces %', rec.topogeo_id, rec.layer_id, newedge.left_face, newfaces; -#endif - - -- Add reference to the other face - sql := 'INSERT INTO ' || quote_ident(atopology) - || '.relation VALUES( ' || rec.topogeo_id - || ',' || rec.layer_id || ',' || newfaces[2] || ', 3)'; - --RAISE DEBUG 'SQL: %', sql; - EXECUTE sql; - - END LOOP; - - -- drop old face from faces table - sql := 'DELETE FROM ' || quote_ident(atopology) - || '.face WHERE face_id = ' || newedge.left_face; - EXECUTE sql; - - END IF; -- } - - RETURN newedge.edge_id; -END -$$ -LANGUAGE 'plpgsql' VOLATILE; + 'MODULE_PATHNAME','ST_AddEdgeNewFaces' + LANGUAGE 'c' VOLATILE; --} ST_AddEdgeNewFaces --{