]> granicus.if.org Git - postgis/commitdiff
Implement ST_AddEdgeNewFaces in C
authorSandro Santilli <strk@keybit.net>
Mon, 20 Jul 2015 15:51:01 +0000 (15:51 +0000)
committerSandro Santilli <strk@keybit.net>
Mon, 20 Jul 2015 15:51:01 +0000 (15:51 +0000)
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

liblwgeom/liblwgeom_topo.h
liblwgeom/lwgeom_topo.c
topology/postgis_topology.c
topology/sql/sqlmm.sql.in

index 75843f28a4ed516805adf7f8d30563768ca75550..d6438b3424db72a95a8e8c0469fed12988d099be 100644 (file)
@@ -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)
index 361bc809a4c253a10c42c33f57d1d2a9fd01f225..2ea2e2a2060bc166c0e5c440fcd627d26ccc2dd8 100644 (file)
@@ -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 );
+}
index 90e640c6669f6626b4a5becaf92a517a77898130..73a6dcd26f61d3781c1b8abe3ab1a478e9b638a3 100644 (file)
@@ -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; i<numelems; ++i) {
+    appendStringInfo(sql, "%s%" PRId64, (i?",":""), ids[i]);
+  }
+  appendStringInfoString(sql, ")");
+
+  POSTGIS_DEBUGF(1, "cb_deleteFacesById query: %s", sql->data);
+
+  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);
+}
index b6f3f2d64b6ce1063239004fda84484f61682813..01b83159eee162dcd575c9f1a2025040cc1da852 100644 (file)
@@ -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
 
 --{