From 9f6747990f99a3b8eae61bbbcba39d9941c48b3c Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Thu, 6 Aug 2015 17:28:13 +0000 Subject: [PATCH] Implement ST_RemIsoNode/ST_RemoveIsoNode in C Adds deleteNodesById callback Funded by Tuscany Region (Italy) - SITA (CIG: 60351023B8) git-svn-id: http://svn.osgeo.org/postgis/trunk@13896 b70326c6-7e19-0410-871a-916f4a2858ee --- liblwgeom/TODO | 2 +- liblwgeom/liblwgeom_topo.h | 32 ++++++++++-- liblwgeom/lwgeom_topo.c | 77 +++++++++++++++++++++++++-- topology/postgis_topology.c | 101 +++++++++++++++++++++++++++++++++++- topology/sql/sqlmm.sql.in | 40 ++------------ 5 files changed, 207 insertions(+), 45 deletions(-) diff --git a/liblwgeom/TODO b/liblwgeom/TODO index 7b6e76840..6c08d9486 100644 --- a/liblwgeom/TODO +++ b/liblwgeom/TODO @@ -15,10 +15,10 @@ lwt_AddIsoEdge X lwt_GetFaceGeometry X lwt_GetFaceEdges X lwt_ChangeEdgeGeom X +lwt_RemoveIsoNode X lwt_RemEdgeNewFace lwt_RemEdgeModFace lwt_ModEdgeHeal lwt_NewEdgeHeal lwt_MoveIsoNode -lwt_RemoveIsoNode diff --git a/liblwgeom/liblwgeom_topo.h b/liblwgeom/liblwgeom_topo.h index a85c88b49..a8aa2d5ca 100644 --- a/liblwgeom/liblwgeom_topo.h +++ b/liblwgeom/liblwgeom_topo.h @@ -179,7 +179,10 @@ typedef struct LWT_BE_CALLBACKS_T { * LWT_COL_NODE_* macros * * @return an array of nodes - * or NULL on error (@see lastErrorMessage) + * or NULL in the following cases: + * - no edge found ("numelems" is set to 0) + * - error ("numelems" is set to -1) + * (@see lastErrorMessage) * */ LWT_ISO_NODE* (*getNodeById) ( @@ -478,6 +481,7 @@ typedef struct LWT_BE_CALLBACKS_T { * or NULL in the following cases: * - no edge found ("numelems" is set to 0) * - error ("numelems" is set to -1) + * (@see lastErrorMessage) */ LWT_ISO_EDGE* (*getEdgeByNode) ( const LWT_BE_TOPOLOGY* topo, @@ -713,6 +717,22 @@ typedef struct LWT_BE_CALLBACKS_T { const LWT_BE_TOPOLOGY* topo ); + /** + * Delete nodes by id + * + * @param topo the topology to act upon + * @param ids an array of node identifiers + * @param numelems number of node identifiers in the ids array + * + * @return number of nodes being deleted or -1 on error + * (@see lastErroMessage) + */ + int (*deleteNodesById) ( + const LWT_BE_TOPOLOGY* topo, + const LWT_ELEMID* ids, + int numelems + ); + } LWT_BE_CALLBACKS; @@ -957,10 +977,12 @@ LWT_ELEMID lwt_AddIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID face, * @param topo the topology to operate on * @param node the identifier of the nod to be moved * @param pt the new node position + * @return 0 on success, -1 on error + * (liblwgeom error handler will be invoked with error message) * */ -void lwt_MoveIsoNode(LWT_TOPOLOGY* topo, - LWT_ELEMID node, LWPOINT* pt); +int lwt_MoveIsoNode(LWT_TOPOLOGY* topo, + LWT_ELEMID node, LWPOINT* pt); /** * Remove an isolated node @@ -969,9 +991,11 @@ void lwt_MoveIsoNode(LWT_TOPOLOGY* topo, * * @param topo the topology to operate on * @param node the identifier of the nod to be moved + * @return 0 on success, -1 on error + * (liblwgeom error handler will be invoked with error message) * */ -void lwt_RemoveIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID node); +int lwt_RemoveIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID node); /** * Add an isolated edge connecting two existing isolated nodes diff --git a/liblwgeom/lwgeom_topo.c b/liblwgeom/lwgeom_topo.c index d8479c9aa..30f1caac5 100644 --- a/liblwgeom/lwgeom_topo.c +++ b/liblwgeom/lwgeom_topo.c @@ -192,6 +192,12 @@ lwt_be_deleteFacesById(const LWT_TOPOLOGY* topo, const LWT_ELEMID* ids, int nume CBT2(topo, deleteFacesById, ids, numelems); } +static int +lwt_be_deleteNodesById(const LWT_TOPOLOGY* topo, const LWT_ELEMID* ids, int numelems) +{ + CBT2(topo, deleteNodesById, ids, numelems); +} + LWT_ELEMID lwt_be_getNextEdgeId(LWT_TOPOLOGY* topo) { @@ -716,13 +722,14 @@ lwt_AddIsoEdge( LWT_TOPOLOGY* topo, LWT_ELEMID startNode, node_ids[1] = endNode; endpoints = lwt_be_getNodeById( topo, node_ids, &num_nodes, LWT_COL_NODE_ALL ); - if ( ! endpoints ) { + if ( num_nodes < 0 ) + { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } - if ( num_nodes < 2 ) + else if ( num_nodes < 2 ) { - _lwt_release_nodes(endpoints, num_nodes); + if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes); lwerror("SQL/MM Spatial exception - non-existent node"); return -1; } @@ -2186,7 +2193,7 @@ _lwt_AddEdge( LWT_TOPOLOGY* topo, } endpoints = lwt_be_getNodeById( topo, node_ids, &num_nodes, LWT_COL_NODE_ALL ); - if ( ! endpoints ) { + if ( num_nodes < 0 ) { lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); return -1; } @@ -3347,3 +3354,65 @@ lwt_ChangeEdgeGeom(LWT_TOPOLOGY* topo, LWT_ELEMID edge_id, LWLINE *geom) _lwt_release_edges(oldedge, 1); return 0; /* success */ } + +static LWT_ISO_NODE * +_lwt_GetIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID nid) +{ + LWT_ISO_NODE *node; + int n = 1; + + node = lwt_be_getNodeById( topo, &nid, &n, LWT_COL_NODE_CONTAINING_FACE ); + if ( n < 0 ) { + lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); + return 0; + } + if ( n < 1 ) { + lwerror("SQL/MM Spatial exception - non-existent node"); + return 0; + } + if ( node->containing_face == -1 ) + { + lwfree(node); + lwerror("SQL/MM Spatial exception - not isolated node"); + return 0; + } + + return node; +} + +int +lwt_MoveIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID nid, LWPOINT *pt) +{ + LWT_ISO_NODE *node; + + node = _lwt_GetIsoNode( topo, nid ); + if ( ! node ) return -1; + + lwfree(node); + lwerror("lwt_MoveIsoNode not implemented yet"); + return -1; +} + +int +lwt_RemoveIsoNode(LWT_TOPOLOGY* topo, LWT_ELEMID nid) +{ + LWT_ISO_NODE *node; + int n = 1; + + node = _lwt_GetIsoNode( topo, nid ); + if ( ! node ) return -1; + + n = lwt_be_deleteNodesById( topo, &nid, n ); + if ( n == -1 ) + { + lwerror("SQL/MM Spatial exception - not isolated node"); + return -1; + } + if ( n != 1 ) + { + lwerror("Unexpected error: %d nodes deleted when expecting 1", n); + return -1; + } + + return 0; /* success */ +} diff --git a/topology/postgis_topology.c b/topology/postgis_topology.c index a0ececc52..bee3824d1 100644 --- a/topology/postgis_topology.c +++ b/topology/postgis_topology.c @@ -2089,6 +2089,43 @@ cb_deleteFacesById( const LWT_BE_TOPOLOGY* topo, return SPI_processed; } +static int +cb_deleteNodesById( const LWT_BE_TOPOLOGY* topo, + const LWT_ELEMID* ids, int numelems ) +{ + MemoryContext oldcontext = CurrentMemoryContext; + int spi_result, i; + StringInfoData sqldata; + StringInfo sql = &sqldata; + + initStringInfo(sql); + appendStringInfo(sql, "DELETE FROM \"%s\".node WHERE node_id IN (", + topo->name); + for (i=0; idata); + + spi_result = SPI_execute( sql->data, false, 0 ); + MemoryContextSwitchTo( oldcontext ); /* switch back */ + 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_deleteNodesById: 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 ) @@ -2262,7 +2299,8 @@ static LWT_BE_CALLBACKS be_callbacks = { cb_deleteFacesById, cb_topoGetSRID, cb_topoGetPrecision, - cb_topoHasZ + cb_topoHasZ, + cb_deleteNodesById }; @@ -2990,3 +3028,64 @@ Datum ST_ChangeEdgeGeom(PG_FUNCTION_ARGS) } PG_RETURN_TEXT_P(cstring2text(buf)); } + +/* ST_RemoveIsoNode(atopology, anode) */ +Datum ST_RemoveIsoNode(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(ST_RemoveIsoNode); +Datum ST_RemoveIsoNode(PG_FUNCTION_ARGS) +{ + text* toponame_text; + char buf[64]; + char* toponame; + int ret; + LWT_ELEMID node_id; + LWT_TOPOLOGY *topo; + + 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); + + node_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_RemoveIsoNode"); + ret = lwt_RemoveIsoNode(topo, node_id); + POSTGIS_DEBUG(1, "lwt_RemoveIsoNode returned"); + lwt_FreeTopology(topo); + + if ( ret == -1 ) { + /* should never reach this point, as lwerror would raise an exception */ + SPI_finish(); + PG_RETURN_NULL(); + } + + /* TODO: check if any TopoGeometry exists including this point in + * its definition ! */ + + SPI_finish(); + + if ( snprintf(buf, 64, "Isolated node " INT64_FORMAT + " removed", node_id) >= 64 ) + { + buf[63] = '\0'; + } + PG_RETURN_TEXT_P(cstring2text(buf)); +} diff --git a/topology/sql/sqlmm.sql.in b/topology/sql/sqlmm.sql.in index 81b1566e2..0e744d348 100644 --- a/topology/sql/sqlmm.sql.in +++ b/topology/sql/sqlmm.sql.in @@ -1421,38 +1421,8 @@ $$ -- CREATE OR REPLACE FUNCTION topology.ST_RemoveIsoNode(atopology varchar, anode integer) RETURNS TEXT AS -$$ -DECLARE - rec RECORD; -BEGIN - - -- - -- Atopology and apoint are required - -- - IF atopology IS NULL OR anode IS NULL THEN - RAISE EXCEPTION - 'SQL/MM Spatial exception - null argument'; - END IF; - - -- - -- Check node isolation. - -- - FOR rec IN EXECUTE 'SELECT edge_id FROM ' - || quote_ident(atopology) || '.edge_data ' || - ' WHERE start_node = ' || anode || - ' OR end_node = ' || anode - LOOP - RAISE EXCEPTION - 'SQL/MM Spatial exception - not isolated node'; - END LOOP; - - EXECUTE 'DELETE FROM ' || quote_ident(atopology) || '.node ' - || ' WHERE node_id = ' || anode; - - RETURN 'Isolated node ' || anode || ' removed'; -END -$$ -LANGUAGE 'plpgsql' VOLATILE; + 'MODULE_PATHNAME','ST_RemoveIsoNode' + LANGUAGE 'c' VOLATILE; --} ST_RemoveIsoNode --{ @@ -1462,9 +1432,9 @@ LANGUAGE 'plpgsql' VOLATILE; -- CREATE OR REPLACE FUNCTION topology.ST_RemIsoNode(varchar, integer) RETURNS TEXT AS -$$ - SELECT topology.ST_RemoveIsoNode($1, $2) -$$ LANGUAGE 'sql' VOLATILE; + 'MODULE_PATHNAME','ST_RemoveIsoNode' + LANGUAGE 'c' VOLATILE; +--} ST_RemIsoNode --{ -- Topo-Geo and Topo-Net 3: Routine Details -- 2.50.0