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
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 )
{
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;
}
}
- 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 */
if ( node->containing_face != -1 ) {
appendStringInfo(str, "%" PRId64, node->containing_face);
} else {
- appendStringInfoString(str, "NULL");
+ appendStringInfoString(str, "null::int");
}
sep = sep1;
}
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 ) {
appendStringInfo(str, "%s'%s'::geometry", sep, hexewkb);
lwfree(hexewkb);
} else {
- appendStringInfo(str, "%snull", sep);
+ appendStringInfo(str, "%snull::geometry", sep);
}
}
face->mbr->xmin, face->mbr->ymin,
face->mbr->xmax, face->mbr->ymax, srid);
} else {
- appendStringInfoString(str, ",null)");
+ appendStringInfoString(str, ",null::geometry)");
}
}
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);
--
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