]> granicus.if.org Git - postgis/commitdiff
Implement ST_RemIsoNode/ST_RemoveIsoNode in C
authorSandro Santilli <strk@keybit.net>
Thu, 6 Aug 2015 17:28:13 +0000 (17:28 +0000)
committerSandro Santilli <strk@keybit.net>
Thu, 6 Aug 2015 17:28:13 +0000 (17:28 +0000)
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
liblwgeom/liblwgeom_topo.h
liblwgeom/lwgeom_topo.c
topology/postgis_topology.c
topology/sql/sqlmm.sql.in

index 7b6e7684037f44bddf8a02fc451efbdf97b30537..6c08d94864006cf53f1224aad2703aca1a99ee65 100644 (file)
@@ -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
 
index a85c88b49fe7c4c3a94cb65d17a975e6bfbc4756..a8aa2d5ca52b5f9c247d30d0b4cf102069ce528e 100644 (file)
@@ -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
index d8479c9aad9ad42cd6453167cd8f0fe23555d750..30f1caac5a5aea7c1e18e1e062ff405cba72d2fc 100644 (file)
@@ -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 */
+}
index a0ececc528081844b592c3a15ad5c21eabf8db2b..bee3824d16a368ffc5bcf630bfea7e7f1cea4a2c 100644 (file)
@@ -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; i<numelems; ++i) {
+    appendStringInfo(sql, "%s" INT64_FORMAT, (i?",":""), ids[i]);
+  }
+  appendStringInfoString(sql, ")");
+
+  POSTGIS_DEBUGF(1, "cb_deleteNodesById query: %s", sql->data);
+
+  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));
+}
index 81b1566e2c8f6c48a37b3779f20445d3a14bc84f..0e744d3484f603b539ab2b3a4990d91a0b990a1a 100644 (file)
@@ -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