]> granicus.if.org Git - postgis/commitdiff
Implement ST_AddIsoEdge in C
authorSandro Santilli <strk@keybit.net>
Thu, 30 Jul 2015 14:23:55 +0000 (14:23 +0000)
committerSandro Santilli <strk@keybit.net>
Thu, 30 Jul 2015 14:23:55 +0000 (14:23 +0000)
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
liblwgeom/liblwgeom_topo.h
liblwgeom/lwgeom_topo.c
topology/postgis_topology.c
topology/sql/sqlmm.sql.in
topology/test/regress/st_addisoedge_expected

index 0f7610b0427c80dc1a7f35bcf99dc274669736bf..78309c67d4c4612d3d0e87a1b64d7e7f4706ad14 100644 (file)
@@ -6,7 +6,7 @@ lwt_AddEdgeNewFaces   X
 lwt_ModEdgeSplit      X
 lwt_NewEdgesSplit     X
 
-lwt_AddIsoEdge
+lwt_AddIsoEdge        X
 lwt_GetFaceEdges
 lwt_GetFaceGeometry
 
index aea9a2a85a4c82c173f3a5fed2bf173abd7f4efe..7368c2ebef45a9f263b5221ad4558c6a83b4ed98 100644 (file)
@@ -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)
index 35da652a748219e6c3e59d7083563bae557b824d..bcc799d083e3d6cdbb8db37a58357b48cf5fd085 100644 (file)
@@ -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; i<num_nodes; ++i ) {
+    lwpoint_release(nodes[i].geom);
+  }
+  lwfree(nodes);
+}
+
 /************************************************************************
  *
  * API implementation
@@ -442,6 +452,349 @@ lwt_AddIsoNode( LWT_TOPOLOGY* topo, LWT_ELEMID face,
   return node.node_id;
 }
 
+/* Check that an edge does not cross an existing node or edge
+ *
+ * Return -1 on cross, 0 if everything is fine.
+ * Note that before returning -1, lwerror is invoked...
+ */
+static int
+_lwt_CheckEdgeCrossing( LWT_TOPOLOGY* topo,
+                        LWT_ELEMID start_node, LWT_ELEMID end_node,
+                        const LWLINE *geom )
+{
+  int i, num_nodes, num_edges;
+  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; i<num_nodes; ++i )
+  {
+    LWT_ISO_NODE* node = &(nodes[i]);
+    GEOSGeometry *nodegg;
+    int contains;
+    if ( node->node_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; i<num_edges; ++i )
+  {
+    LWT_ISO_EDGE* edge = &(edges[i]);
+    GEOSGeometry *eegg;
+    char *relate;
+    int match;
+
+    if ( ! edge->geom ) {
+      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; i<num_nodes; ++i )
+  {
+    const LWT_ISO_NODE *n = &(endpoints[i]);
+    if ( n->containing_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; i<num_nodes; ++i )
-    {
-      LWT_ISO_NODE* node = &(nodes[i]);
-      GEOSGeometry *nodegg;
-      int contains;
-      if ( node->node_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; i<num_edges; ++i )
-    {
-      LWT_ISO_EDGE* edge = &(edges[i]);
-      GEOSGeometry *eegg;
-      char *relate;
-      int match;
-
-      if ( ! edge->geom ) {
-        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 */
 
index dc731b88d433edee270a72db7597aa3bcd6e5531..fbc55a15246720049b647923538de77c3830258a 100644 (file)
@@ -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);
index 01b83159eee162dcd575c9f1a2025040cc1da852..fbf9a95b84647038f62038c48b679b8b418992a3 100644 (file)
@@ -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
index 182dc6ae76bc4dd0fdf8b6a70c35a7459ee5ebc3..f1ae4f0231d37362db9f5a1990446b77dd2ec871 100644 (file)
@@ -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