lwt_ChangeEdgeGeom X
lwt_RemoveIsoNode X
lwt_MoveIsoNode X
+lwt_RemEdgeModFace X
lwt_RemEdgeNewFace
-lwt_RemEdgeModFace
lwt_ModEdgeHeal
lwt_NewEdgeHeal
int numelems
);
+ /**
+ * Update TopoGeometry objects after an edge removal event
+ *
+ * @param topo the topology to act upon
+ * @param rem_edge identifier of the edge that's been removed
+ *
+ * @return 1 on success, 0 on error (@see lastErrorMessage)
+ *
+ */
+ int (*updateTopoGeomRemEdge) (
+ const LWT_BE_TOPOLOGY* topo,
+ LWT_ELEMID rem_edge
+ );
+
+ /**
+ * Update TopoGeometry objects after healing two faces
+ *
+ * @param topo the topology to act upon
+ * @param face1 identifier of the first face
+ * @param face2 identifier of the second face
+ * @param newface identifier of the new face
+ *
+ * @note that newface may or may not be equal to face1 or face2,
+ * while face1 should never be the same as face2.
+ *
+ * @return 1 on success, 0 on error (@see lastErrorMessage)
+ *
+ */
+ int (*updateTopoGeomFaceHeal) (
+ const LWT_BE_TOPOLOGY* topo,
+ LWT_ELEMID face1, LWT_ELEMID face2, LWT_ELEMID newface
+ );
} LWT_BE_CALLBACKS;
*
* @param topo the topology to operate on
* @param edge identifier of the edge to be removed
- * @return the id of newly created face or -1 if no new face was created
+ * @return the id of newly created face, 0 if no new face was created
+ * or -1 on error
*
*/
LWT_ELEMID lwt_RemEdgeNewFace(LWT_TOPOLOGY* topo, LWT_ELEMID edge);
*
* @param topo the topology to operate on
* @param edge identifier of the edge to be removed
- * @return the id of newly created face or -1 if no new face was created
+ * @return the id of newly created face, 0 if no new face was created
+ * or -1 on error
*
*/
LWT_ELEMID lwt_RemEdgeModFace(LWT_TOPOLOGY* topo, LWT_ELEMID edge);
}
static int
-lwt_be_updateTopoGeomFaceSplit(LWT_TOPOLOGY* topo, LWT_ELEMID split_face, LWT_ELEMID new_face1, LWT_ELEMID new_face2)
+lwt_be_updateTopoGeomFaceSplit(LWT_TOPOLOGY* topo, LWT_ELEMID split_face,
+ LWT_ELEMID new_face1, LWT_ELEMID new_face2)
{
CBT3(topo, updateTopoGeomFaceSplit, split_face, new_face1, new_face2);
}
+static int
+lwt_be_updateTopoGeomRemEdge(LWT_TOPOLOGY* topo, LWT_ELEMID edge_id)
+{
+ CBT1(topo, updateTopoGeomRemEdge, edge_id);
+}
+
+static int
+lwt_be_updateTopoGeomFaceHeal(LWT_TOPOLOGY* topo,
+ LWT_ELEMID face1, LWT_ELEMID face2,
+ LWT_ELEMID newface)
+{
+ CBT3(topo, updateTopoGeomFaceHeal, face1, face2, newface);
+}
+
static LWT_ELEMID*
lwt_be_getRingEdges( LWT_TOPOLOGY* topo,
LWT_ELEMID edge, int *numedges, int limit )
*
************************************************************************/
+static void
+_lwt_release_faces(LWT_ISO_FACE *faces, int num_faces)
+{
+ int i;
+ for ( i=0; i<num_faces; ++i ) {
+ if ( faces[i].mbr ) lwfree(faces[i].mbr);
+ }
+ lwfree(faces);
+}
+
static void
_lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
{
int i;
for ( i=0; i<num_edges; ++i ) {
- lwline_release(edges[i].geom);
+ if ( edges[i].geom ) lwline_release(edges[i].geom);
}
lwfree(edges);
}
{
int i;
for ( i=0; i<num_nodes; ++i ) {
- lwpoint_release(nodes[i].geom);
+ if ( nodes[i].geom ) lwpoint_release(nodes[i].geom);
}
lwfree(nodes);
}
lwerror("Backend coding error: getEdgeById callback returned NULL "
"but numelements output parameter has value %d "
"(expected 0 or 1)", i);
+ return NULL;
}
}
lwerror("Backend coding error: getEdgeById callback returned NULL "
"but numelements output parameter has value %d "
"(expected 0 or 1)", i);
+ return -1;
}
}
return 0; /* success */
}
+
+/* Used by _lwt_RemEdge to update edge face ref on healing
+ *
+ * @param of old face id (never 0 as you cannot remove face 0)
+ * @param nf new face id
+ * @return 0 on success, -1 on backend error
+ */
+static int
+_lwt_UpdateEdgeFaceRef( LWT_TOPOLOGY *topo, LWT_ELEMID of, LWT_ELEMID nf)
+{
+ LWT_ISO_EDGE sel_edge, upd_edge;
+ int ret;
+
+ assert( of != 0 );
+
+ /* Update face_left for all edges still referencing old face */
+ sel_edge.face_left = of;
+ upd_edge.face_left = nf;
+ ret = lwt_be_updateEdges(topo, &sel_edge, LWT_COL_EDGE_FACE_LEFT,
+ &upd_edge, LWT_COL_EDGE_FACE_LEFT,
+ NULL, 0);
+ if ( ret == -1 ) return -1;
+
+ /* Update face_right for all edges still referencing old face */
+ sel_edge.face_right = of;
+ upd_edge.face_right = nf;
+ ret = lwt_be_updateEdges(topo, &sel_edge, LWT_COL_EDGE_FACE_RIGHT,
+ &upd_edge, LWT_COL_EDGE_FACE_RIGHT,
+ NULL, 0);
+ if ( ret == -1 ) return -1;
+
+ return 0;
+}
+
+/* Used by _lwt_RemEdge to update node face ref on healing
+ *
+ * @param of old face id (never 0 as you cannot remove face 0)
+ * @param nf new face id
+ * @return 0 on success, -1 on backend error
+ */
+static int
+_lwt_UpdateNodeFaceRef( LWT_TOPOLOGY *topo, LWT_ELEMID of, LWT_ELEMID nf)
+{
+ LWT_ISO_NODE sel, upd;
+ int ret;
+
+ assert( of != 0 );
+
+ /* Update face_left for all edges still referencing old face */
+ sel.containing_face = of;
+ upd.containing_face = nf;
+ ret = lwt_be_updateNodes(topo, &sel, LWT_COL_NODE_CONTAINING_FACE,
+ &upd, LWT_COL_NODE_CONTAINING_FACE,
+ NULL, 0);
+ if ( ret == -1 ) return -1;
+
+ return 0;
+}
+
+/* Used by lwt_RemEdgeModFace and lwt_RemEdgeNewFaces */
+static LWT_ELEMID
+_lwt_RemEdge( LWT_TOPOLOGY* topo, LWT_ELEMID edge_id, int modFace )
+{
+ int i, nedges, nfaces, fields;
+ LWT_ISO_EDGE *edge = NULL;
+ LWT_ISO_EDGE *upd_edge = NULL;
+ LWT_ISO_EDGE upd_edge_left[2];
+ int nedge_left = 0;
+ LWT_ISO_EDGE upd_edge_right[2];
+ int nedge_right = 0;
+ LWT_ISO_NODE upd_node[2];
+ int nnode = 0;
+ LWT_ISO_FACE *faces = NULL;
+ LWT_ISO_FACE newface;
+ LWT_ELEMID node_ids[2];
+ LWT_ELEMID face_ids[2];
+ int fnode_edges = 0; /* number of edges on the first node (excluded
+ * the one being removed ) */
+ int lnode_edges = 0; /* number of edges on the last node (excluded
+ * the one being removed ) */
+
+ i = 1;
+ edge = lwt_be_getEdgeById(topo, &edge_id, &i, LWT_COL_EDGE_ALL);
+ if ( ! edge )
+ {
+ LWDEBUGF(1, "lwt_be_getEdgeById returned NULL and set i=%d", i);
+ if ( i == -1 )
+ {
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ else if ( i == 0 )
+ {
+ lwerror("SQL/MM Spatial exception - non-existent edge %"
+ LWTFMT_ELEMID, edge_id);
+ return -1;
+ }
+ else
+ {
+ lwerror("Backend coding error: getEdgeById callback returned NULL "
+ "but numelements output parameter has value %d "
+ "(expected 0 or 1)", i);
+ return -1;
+ }
+ }
+
+ if ( ! lwt_be_updateTopoGeomRemEdge(topo, edge_id) )
+ {
+ lwerror("%s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+
+ lwnotice("Updating next_{right,left}_face of ring edges...");
+
+ /* Update edge linking */
+
+ nedges = 0;
+ node_ids[nedges++] = edge->start_node;
+ if ( edge->end_node != edge->start_node )
+ {
+ node_ids[nedges++] = edge->end_node;
+ }
+ fields = LWT_COL_EDGE_EDGE_ID | LWT_COL_EDGE_START_NODE |
+ LWT_COL_EDGE_END_NODE | LWT_COL_EDGE_NEXT_LEFT |
+ LWT_COL_EDGE_NEXT_RIGHT;
+ upd_edge = lwt_be_getEdgeByNode( topo, &(node_ids[0]), &nedges, fields );
+ if ( nedges == -1 ) {
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ nedge_left = nedge_right = 0;
+ for ( i=0; i<nedges; ++i )
+ {
+ LWT_ISO_EDGE *e = &(upd_edge[i]);
+ if ( e->edge_id == edge_id ) continue;
+ if ( e->start_node == edge->start_node || e->end_node == edge->start_node )
+ {
+ ++fnode_edges;
+ }
+ if ( e->start_node == edge->end_node || e->end_node == edge->end_node )
+ {
+ ++lnode_edges;
+ }
+ if ( e->next_left == -edge_id )
+ {
+ upd_edge_left[nedge_left].edge_id = e->edge_id;
+ upd_edge_left[nedge_left++].next_left =
+ edge->next_left != edge_id ? edge->next_left : edge->next_right;
+ }
+ else if ( e->next_left == edge_id )
+ {
+ upd_edge_left[nedge_left].edge_id = e->edge_id;
+ upd_edge_left[nedge_left++].next_left =
+ edge->next_right != -edge_id ? edge->next_right : edge->next_left;
+ }
+
+ if ( e->next_right == -edge_id )
+ {
+ upd_edge_right[nedge_right].edge_id = e->edge_id;
+ upd_edge_right[nedge_right++].next_right =
+ edge->next_left != edge_id ? edge->next_left : edge->next_right;
+ }
+ else if ( e->next_right == edge_id )
+ {
+ upd_edge_right[nedge_right].edge_id = e->edge_id;
+ upd_edge_right[nedge_right++].next_right =
+ edge->next_right != -edge_id ? edge->next_right : edge->next_left;
+ }
+ }
+
+ if ( nedge_left )
+ {
+ LWDEBUGF(1, "updating %d 'next_left' edges", nedge_left);
+ /* update edges in upd_edge_left set next_left */
+ i = lwt_be_updateEdgesById(topo, &(upd_edge_left[0]), nedge_left,
+ LWT_COL_EDGE_NEXT_LEFT);
+ if ( i == -1 )
+ {
+ _lwt_release_edges(edge, 1);
+ lwfree(upd_edge);
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ }
+ if ( nedge_right )
+ {
+ LWDEBUGF(1, "updating %d 'next_right' edges", nedge_right);
+ /* update edges in upd_edge_right set next_right */
+ i = lwt_be_updateEdgesById(topo, &(upd_edge_right[0]), nedge_right,
+ LWT_COL_EDGE_NEXT_RIGHT);
+ if ( i == -1 )
+ {
+ _lwt_release_edges(edge, 1);
+ lwfree(upd_edge);
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ }
+ LWDEBUGF(1, "releasing %d updateable edges in %p", nedges, upd_edge);
+ lwfree(upd_edge);
+
+ /* Id of face that will take up all the space previously
+ * taken by left and right faces of the edge */
+ LWT_ELEMID floodface;
+
+ /* Find floodface, and update its mbr if != 0 */
+ if ( edge->face_left == edge->face_right )
+ {
+ floodface = edge->face_right;
+ }
+ else
+ {
+ /* Two faces healed */
+ if ( edge->face_left == 0 || edge->face_right == 0 )
+ {
+ floodface = 0;
+ LWDEBUG(1, "floodface is universe");
+ }
+ else
+ {
+ /* we choose right face as the face that will remain
+ * to be symmetric with ST_AddEdgeModFace */
+ floodface = edge->face_right;
+ LWDEBUGF(1, "floodface is %" LWTFMT_ELEMID, floodface);
+ if ( modFace )
+ {
+ /* update mbr of floodface as union of mbr of both faces */
+ face_ids[0] = edge->face_left;
+ face_ids[1] = edge->face_right;
+ nfaces = 2;
+ fields = LWT_COL_FACE_ALL;
+ faces = lwt_be_getFaceById(topo, face_ids, &nfaces, fields);
+ if ( nfaces == -1 ) {
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ GBOX *box1=NULL;
+ GBOX *box2=NULL;
+ for ( i=0; i<nfaces; ++i )
+ {
+ if ( faces[i].face_id == edge->face_left )
+ {
+ if ( ! box1 ) box1 = faces[i].mbr;
+ else
+ {
+ i = edge->face_left;
+ _lwt_release_edges(edge, 1);
+ _lwt_release_faces(faces, nfaces);
+ lwerror("corrupted topology: more than 1 face have face_id=%"
+ LWTFMT_ELEMID, i);
+ return -1;
+ }
+ }
+ else if ( faces[i].face_id == edge->face_right )
+ {
+ if ( ! box2 ) box2 = faces[i].mbr;
+ else
+ {
+ i = edge->face_right;
+ _lwt_release_edges(edge, 1);
+ _lwt_release_faces(faces, nfaces);
+ lwerror("corrupted topology: more than 1 face have face_id=%"
+ LWTFMT_ELEMID, i);
+ return -1;
+ }
+ }
+ else
+ {
+ i = faces[i].face_id;
+ _lwt_release_edges(edge, 1);
+ _lwt_release_faces(faces, nfaces);
+ lwerror("Backend coding error: getFaceById returned face "
+ "with non-requested id %" LWTFMT_ELEMID, i);
+ return -1;
+ }
+ }
+ if ( ! box1 ) {
+ i = edge->face_left;
+ _lwt_release_edges(edge, 1);
+ _lwt_release_faces(faces, nfaces);
+ lwerror("corrupted topology: no face have face_id=%"
+ LWTFMT_ELEMID " (left face for edge %"
+ LWTFMT_ELEMID ")", i, edge_id);
+ return -1;
+ }
+ if ( ! box2 ) {
+ i = edge->face_right;
+ _lwt_release_edges(edge, 1);
+ _lwt_release_faces(faces, nfaces);
+ lwerror("corrupted topology: no face have face_id=%"
+ LWTFMT_ELEMID " (right face for edge %"
+ LWTFMT_ELEMID ")", i, edge_id);
+ return -1;
+ }
+ gbox_merge(box2, box1); /* box1 is now the union of the two */
+ newface.face_id = edge->face_right;
+ newface.mbr = box1;
+ i = lwt_be_updateFacesById( topo, &newface, 1 );
+ _lwt_release_faces(faces, 2);
+ if ( i == -1 )
+ {
+ _lwt_release_edges(edge, 1);
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ if ( i != 1 )
+ {
+ _lwt_release_edges(edge, 1);
+ lwerror("Unexpected error: %d faces updated when expecting 1", i);
+ return -1;
+ }
+ }
+ }
+
+ /* Update face references for edges and nodes still referencing
+ * the removed face(s) */
+
+ if ( edge->face_left != floodface )
+ {
+ if ( -1 == _lwt_UpdateEdgeFaceRef(topo, edge->face_left, floodface) )
+ {
+ _lwt_release_edges(edge, 1);
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ if ( -1 == _lwt_UpdateNodeFaceRef(topo, edge->face_left, floodface) )
+ {
+ _lwt_release_edges(edge, 1);
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ }
+
+ if ( edge->face_right != floodface )
+ {
+ if ( -1 == _lwt_UpdateEdgeFaceRef(topo, edge->face_right, floodface) )
+ {
+ _lwt_release_edges(edge, 1);
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ if ( -1 == _lwt_UpdateNodeFaceRef(topo, edge->face_right, floodface) )
+ {
+ _lwt_release_edges(edge, 1);
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ }
+
+ /* Update topogeoms on heal */
+ if ( ! lwt_be_updateTopoGeomFaceHeal(topo,
+ edge->face_right, edge->face_left,
+ floodface) )
+ {
+ _lwt_release_edges(edge, 1);
+ lwerror("%s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ } /* two faces healed */
+
+ /* Delete the edge */
+ i = lwt_be_deleteEdges(topo, edge, LWT_COL_EDGE_EDGE_ID);
+ if ( i == -1 ) {
+ _lwt_release_edges(edge, 1);
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+
+ /* If any of the edge nodes remained isolated, set
+ * containing_face = floodface
+ */
+ if ( ! fnode_edges )
+ {
+ upd_node[nnode].node_id = edge->start_node;
+ upd_node[nnode].containing_face = floodface;
+ ++nnode;
+ }
+ if ( edge->end_node != edge->start_node && ! lnode_edges )
+ {
+ upd_node[nnode].node_id = edge->end_node;
+ upd_node[nnode].containing_face = floodface;
+ ++nnode;
+ }
+ if ( nnode )
+ {
+ i = lwt_be_updateNodesById(topo, upd_node, nnode,
+ LWT_COL_NODE_CONTAINING_FACE);
+ if ( i == -1 ) {
+ _lwt_release_edges(edge, 1);
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ }
+
+ if ( edge->face_left != edge->face_right )
+ /* or there'd be no face to remove */
+ {
+ LWT_ELEMID ids[2];
+ int nids = 0;
+ if ( edge->face_right != floodface )
+ ids[nids++] = edge->face_right;
+ if ( edge->face_left != floodface )
+ ids[nids++] = edge->face_left;
+ i = lwt_be_deleteFacesById(topo, ids, nids);
+ if ( i == -1 ) {
+ _lwt_release_edges(edge, 1);
+ lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
+ return -1;
+ }
+ }
+
+ _lwt_release_edges(edge, 1);
+ return floodface;
+}
+
+LWT_ELEMID
+lwt_RemEdgeModFace( LWT_TOPOLOGY* topo, LWT_ELEMID edge_id )
+{
+ return _lwt_RemEdge( topo, edge_id, 1 );
+}
+
+LWT_ELEMID
+lwt_RemEdgeNewFace( LWT_TOPOLOGY* topo, LWT_ELEMID edge_id )
+{
+ return _lwt_RemEdge( topo, edge_id, 0 );
+}
return 1;
}
+static int
+cb_updateTopoGeomRemEdge ( const LWT_BE_TOPOLOGY* topo,
+ LWT_ELEMID rem_edge )
+{
+ MemoryContext oldcontext = CurrentMemoryContext;
+ int spi_result;
+ StringInfoData sqldata;
+ StringInfo sql = &sqldata;
+
+ POSTGIS_DEBUG(1, "cb_updateTopoGeomRemEdge enter ");
+
+ initStringInfo(sql);
+ appendStringInfo( sql, "SELECT r.topogeo_id, r.layer_id, "
+ "l.schema_name, l.table_name, l.feature_column FROM "
+ "topology.layer l INNER JOIN \"%s\".relation r "
+ "ON (l.layer_id = r.layer_id) WHERE l.level = 0 AND "
+ "l.feature_type = 2 AND l.topology_id = %d"
+ " AND abs(r.element_id) = " INT64_FORMAT,
+ topo->name, topo->id, rem_edge );
+
+ POSTGIS_DEBUGF(1, "cb_updateTopoGeomRemEdge query: %s", sql->data);
+
+ spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, 0);
+ MemoryContextSwitchTo( oldcontext ); /* switch back */
+ if ( spi_result != SPI_OK_SELECT ) {
+ cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
+ spi_result, sql->data);
+ return 0;
+ }
+
+ if ( SPI_processed )
+ {{
+ const char *tg_id, *layer_id;
+ const char *schema_name, *table_name, *col_name;
+ HeapTuple row = SPI_tuptable->vals[0];
+ TupleDesc tdesc = SPI_tuptable->tupdesc;
+
+ tg_id = SPI_getvalue(row, tdesc, 1);
+ layer_id = SPI_getvalue(row, tdesc, 2);
+ schema_name = SPI_getvalue(row, tdesc, 3);
+ table_name = SPI_getvalue(row, tdesc, 4);
+ col_name = SPI_getvalue(row, tdesc, 5);
+
+ cberror(topo->be_data, "TopoGeom %s in layer %s "
+ "(%s.%s.%s) cannot be represented "
+ "dropping edge " INT64_FORMAT,
+ tg_id, layer_id, schema_name, table_name,
+ col_name, rem_edge);
+ return 0;
+ }}
+
+ return 1;
+}
+
+static int
+cb_updateTopoGeomFaceHeal ( const LWT_BE_TOPOLOGY* topo,
+ LWT_ELEMID face1, LWT_ELEMID face2, LWT_ELEMID newface )
+{
+ MemoryContext oldcontext = CurrentMemoryContext;
+ int spi_result;
+ StringInfoData sqldata;
+ StringInfo sql = &sqldata;
+
+ POSTGIS_DEBUG(1, "cb_updateTopoGeomFaceHeal enter ");
+
+ /* 1. check if any basic TopoGeometry is defined by one but not
+ * the other face, return 0 in that case */
+
+ initStringInfo(sql);
+ appendStringInfo( sql, "SELECT t.* FROM ( SELECT r.topogeo_id, "
+ "r.layer_id, l.schema_name, l.table_name, l.feature_column, "
+ "array_agg(r.element_id) as elems FROM topology.layer l "
+ " INNER JOIN \"%s\".relation r ON (l.layer_id = r.layer_id) "
+ "WHERE l.level = 0 and l.feature_type = 3 "
+ "AND l.topology_id = %d"
+ " AND r.element_id = ANY (ARRAY[" INT64_FORMAT "," INT64_FORMAT
+ "]::int4[]) group by r.topogeo_id, r.layer_id, l.schema_name, "
+ "l.table_name, l.feature_column ) t WHERE NOT t.elems @> ARRAY["
+ INT64_FORMAT "," INT64_FORMAT "]::int4[]",
+ topo->name, topo->id, face1, face2, face1, face2 );
+
+ POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceHeal query 1: %s", sql->data);
+
+ spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, 0);
+ MemoryContextSwitchTo( oldcontext ); /* switch back */
+ if ( spi_result != SPI_OK_SELECT ) {
+ cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
+ spi_result, sql->data);
+ return 0;
+ }
+
+ if ( SPI_processed )
+ {{
+ const char *tg_id, *layer_id;
+ const char *schema_name, *table_name, *col_name;
+ HeapTuple row = SPI_tuptable->vals[0];
+ TupleDesc tdesc = SPI_tuptable->tupdesc;
+
+ tg_id = SPI_getvalue(row, tdesc, 1);
+ layer_id = SPI_getvalue(row, tdesc, 2);
+ schema_name = SPI_getvalue(row, tdesc, 3);
+ table_name = SPI_getvalue(row, tdesc, 4);
+ col_name = SPI_getvalue(row, tdesc, 5);
+
+ cberror(topo->be_data, "TopoGeom %s in layer %s "
+ "(%s.%s.%s) cannot be represented "
+ "healing faces " INT64_FORMAT
+ " and " INT64_FORMAT,
+ tg_id, layer_id, schema_name, table_name,
+ col_name, face1, face2);
+ return 0;
+ }}
+
+ /* 2. delete oldfaces (not equal to newface) from the
+ * set of primitives defining the TopoGeometries found before */
+
+ if ( newface == face1 || newface == face2 )
+ {
+ initStringInfo(sql);
+ /* this query can be optimized */
+ appendStringInfo( sql, "DELETE FROM \"%s\".relation r "
+ "USING topology.layer l WHERE l.level = 0 AND l.feature_type = 3"
+ " AND l.topology_id = %d AND l.layer_id = r.layer_id "
+ " AND abs(r.element_id) IN ( " INT64_FORMAT "," INT64_FORMAT ")"
+ " AND abs(r.element_id) != " INT64_FORMAT,
+ topo->name, topo->id, face1, face2, newface );
+ POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceHeal query 2a: %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 0;
+ }
+ if ( SPI_processed ) topo->be_data->data_changed = true;
+ }
+ else
+ {
+ initStringInfo(sql);
+ /* delete face1 */
+ appendStringInfo( sql, "DELETE FROM \"%s\".relation r "
+ "USING topology.layer l WHERE l.level = 0 AND l.feature_type = 3"
+ " AND l.topology_id = %d AND l.layer_id = r.layer_id "
+ " AND abs(r.element_id) = " INT64_FORMAT,
+ topo->name, topo->id, face1 );
+ POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceHeal query 2b: %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 0;
+ }
+ if ( SPI_processed ) topo->be_data->data_changed = true;
+
+ initStringInfo(sql);
+ /* update face2 to newface */
+ appendStringInfo( sql, "UPDATE \"%s\".relation r "
+ "SET element_id = " INT64_FORMAT " FROM topology.layer l "
+ "WHERE l.level = 0 AND l.feature_type = 3 AND l.topology_id = %d"
+ " AND l.layer_id = r.layer_id AND r.element_id = " INT64_FORMAT,
+ topo->name, newface, topo->id, face2 );
+ POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceHeal query 3: %s", sql->data);
+
+ spi_result = SPI_execute(sql->data, false, 0);
+ MemoryContextSwitchTo( oldcontext ); /* switch back */
+ if ( spi_result != SPI_OK_UPDATE ) {
+ cberror(topo->be_data, "unexpected return (%d) from query execution: %s",
+ spi_result, sql->data);
+ return 0;
+ }
+ if ( SPI_processed ) topo->be_data->data_changed = true;
+ }
+
+ return 1;
+}
+
static LWT_ELEMID
cb_getFaceContainingPoint( const LWT_BE_TOPOLOGY* topo, const LWPOINT* pt )
{
cb_topoGetSRID,
cb_topoGetPrecision,
cb_topoHasZ,
- cb_deleteNodesById
+ cb_deleteNodesById,
+ cb_updateTopoGeomRemEdge,
+ cb_updateTopoGeomFaceHeal
};
}
PG_RETURN_TEXT_P(cstring2text(buf));
}
+
+/* ST_RemEdgeModFace(atopology, anedge) */
+Datum ST_RemEdgeModFace(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(ST_RemEdgeModFace);
+Datum ST_RemEdgeModFace(PG_FUNCTION_ARGS)
+{
+ text* toponame_text;
+ 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_RemEdgeModFace");
+ ret = lwt_RemEdgeModFace(topo, node_id);
+ POSTGIS_DEBUG(1, "lwt_RemEdgeModFace returned");
+ lwt_FreeTopology(topo);
+
+ if ( ret == -1 ) {
+ /* should never reach this point, as lwerror would raise an exception */
+ SPI_finish();
+ PG_RETURN_NULL();
+ }
+
+ SPI_finish();
+
+ PG_RETURN_INT32(ret);
+}
--
-- }{
CREATE OR REPLACE FUNCTION topology.ST_RemEdgeModFace(toponame varchar, e1id integer)
- RETURNS int
-AS
-$$
-DECLARE
- e1rec RECORD;
- rec RECORD;
- fidary int[];
- topoid int;
- sql text;
- floodfaceid int;
- elink int;
-BEGIN
- --
- -- toponame and face_id are required
- --
- IF toponame IS NULL OR e1id IS NULL THEN
- RAISE EXCEPTION 'SQL/MM Spatial exception - null argument';
- END IF;
-
- -- Get topology id
- BEGIN
- SELECT id FROM topology.topology
- INTO STRICT topoid WHERE name = toponame;
- EXCEPTION
- WHEN NO_DATA_FOUND THEN
- RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name';
- END;
-
- BEGIN
- EXECUTE 'SELECT * FROM ' || quote_ident(toponame)
- || '.edge_data WHERE edge_id = ' || e1id
- INTO STRICT e1rec;
- EXCEPTION
- WHEN NO_DATA_FOUND THEN
- RAISE EXCEPTION 'SQL/MM Spatial exception - non-existent edge %', e1id;
- WHEN INVALID_SCHEMA_NAME THEN
- RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name';
- WHEN UNDEFINED_TABLE THEN
- RAISE EXCEPTION 'corrupted topology "%" (missing edge_data table)',
- toponame;
- END;
-
- -- NOT IN THE SPECS:
- -- Check that no TopoGeometry references the edge being removed
- PERFORM topology._ST_RemEdgeCheck(toponame, topoid, e1id, e1rec.left_face, e1rec.right_face);
-
- -- Update next_left_edge and next_right_edge face
- -- for all edges bounding the new face
- RAISE NOTICE 'Updating next_{right,left}_face of ring edges...';
-
- -- TODO: reduce the following to 2 UPDATE rather than 4
-
- -- Update next_left_edge of previous edges in left face -- {
-
- elink := e1rec.next_left_edge;
-
- sql := 'UPDATE ' || quote_ident(toponame)
- || '.edge_data SET next_left_edge = '
- || elink
- || ', abs_next_left_edge = '
- || abs(elink)
- || ' WHERE next_left_edge < 0 AND abs(next_left_edge) = '
- || e1id;
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'next_left_edge update: %', sql;
-#endif
- EXECUTE sql;
-
- -- If the edge being removed links to self,
- -- we use the other face
- IF e1rec.abs_next_right_edge = e1rec.edge_id THEN
- elink := e1rec.next_left_edge;
- ELSE
- elink := e1rec.next_right_edge;
- END IF;
-
- sql := 'UPDATE ' || quote_ident(toponame)
- || '.edge_data SET next_left_edge = '
- || elink
- || ', abs_next_left_edge = '
- || abs(elink)
- || ' WHERE next_left_edge > 0 AND abs(next_left_edge) = '
- || e1id;
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'next_left_edge update: %', sql;
-#endif
- EXECUTE sql;
-
- -- }
-
- -- Update next_right_edge of previous edges in right face -- {
-
- elink := e1rec.next_left_edge;
-
- sql := 'UPDATE ' || quote_ident(toponame)
- || '.edge_data SET next_right_edge = '
- || elink
- || ', abs_next_right_edge = '
- || abs(elink)
- || ' WHERE next_right_edge < 0 AND abs(next_right_edge) = '
- || e1id;
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'next_right_edge update: %', sql;
-#endif
- EXECUTE sql;
-
- -- If the edge being removed links to self,
- -- we use the other face
- IF e1rec.abs_next_right_edge = e1rec.edge_id THEN
- elink := e1rec.next_left_edge;
- ELSE
- elink := e1rec.next_right_edge;
- END IF;
-
- sql := 'UPDATE ' || quote_ident(toponame)
- || '.edge_data SET next_right_edge = '
- || elink
- || ', abs_next_right_edge = '
- || abs(elink)
- || ' WHERE next_right_edge > 0 AND abs(next_right_edge) = '
- || e1id;
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'next_right_edge update: %', sql;
-#endif
- EXECUTE sql;
-
- -- }
-
- IF e1rec.left_face = e1rec.right_face THEN -- {
-
- floodfaceid = e1rec.left_face;
-
- ELSE -- }{
-
- IF e1rec.left_face = 0 OR e1rec.right_face = 0 THEN -- {
-
- --
- -- We won't add any new face, but rather let the universe
- -- flood the removed face.
- --
-
- floodfaceid = 0;
-
- ELSE -- }{
-
- -- we choose right face as the face that will remain
- -- to be symmetric with ST_AddEdgeModFace
- floodfaceid = e1rec.right_face;
-
- sql := 'UPDATE '
- || quote_ident(toponame)
- || '.face SET mbr = (SELECT '
- -- minimum bounding rectangle is the union of the old faces mbr
- -- (doing this without GEOS would be faster)
- || 'ST_Envelope(ST_Union(mbr)) FROM '
- || quote_ident(toponame)
- || '.face WHERE face_id IN ('
- || e1rec.left_face || ',' || e1rec.right_face
- || ') ) WHERE face_id = ' || floodfaceid ;
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'SQL: %', sql;
-#endif
- EXECUTE sql;
-
- END IF; -- }
-
- -- Update left_face for all edges still referencing old faces
- sql := 'UPDATE ' || quote_ident(toponame)
- || '.edge_data SET left_face = ' || floodfaceid
- || ' WHERE left_face IN ('
- || e1rec.left_face || ',' || e1rec.right_face
- || ')';
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'left_face update: %', sql;
-#endif
- EXECUTE sql;
-
- -- Update right_face for all edges still referencing old faces
- sql := 'UPDATE ' || quote_ident(toponame)
- || '.edge_data SET right_face = ' || floodfaceid
- || ' WHERE right_face IN ('
- || e1rec.left_face || ',' || e1rec.right_face
- || ')';
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'right_face update: %', sql;
-#endif
- EXECUTE sql;
-
- -- Update containing_face for all nodes still referencing old faces
- sql := 'UPDATE ' || quote_ident(toponame)
- || '.node SET containing_face = ' || floodfaceid
- || ' WHERE containing_face IN ('
- || e1rec.left_face || ',' || e1rec.right_face
- || ')';
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'Isolated nodes update: %', sql;
-#endif
- EXECUTE sql;
-
- -- NOT IN THE SPECS:
- -- Replace composition rows involving the two
- -- faces as one involving the new face.
- -- It takes a single DELETE to do that.
- sql := 'DELETE FROM ' || quote_ident(toponame)
- || '.relation r USING topology.layer l '
- || 'WHERE l.level = 0 AND l.feature_type = 3'
- || ' AND l.topology_id = ' || topoid
- || ' AND l.layer_id = r.layer_id AND abs(r.element_id) IN ('
- || e1rec.left_face || ',' || e1rec.right_face
- || ') AND abs(r.element_id) != '
- || floodfaceid; -- could be optimized..
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'SQL: %', sql;
-#endif
- EXECUTE sql;
-
- END IF; -- } two faces healed...
-
- -- Delete the edge
- sql := 'DELETE FROM ' || quote_ident(toponame)
- || '.edge_data WHERE edge_id = ' || e1id;
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'Edge deletion: %', sql;
-#endif
- EXECUTE sql;
-
- -- Check if any of the edge nodes remains isolated,
- -- set containing_face = floodfaceid in that case
- sql := 'UPDATE ' || quote_ident(toponame)
- || '.node n SET containing_face = ' || floodfaceid
- || ' WHERE node_id IN ('
- || e1rec.start_node || ','
- || e1rec.end_node || ') AND NOT EXISTS (SELECT edge_id FROM '
- || quote_ident(toponame)
- || '.edge_data WHERE start_node = n.node_id OR end_node = n.node_id)';
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'Checking for nodes left isolated: %', sql;
-#endif
- EXECUTE sql;
-
- IF e1rec.right_face != e1rec.left_face THEN -- {
-
- -- Delete left face, if not universe and not "flood" face
- IF e1rec.left_face != 0 AND e1rec.left_face != floodfaceid
- THEN
- sql := 'DELETE FROM ' || quote_ident(toponame)
- || '.face WHERE face_id = ' || e1rec.left_face;
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'Left face deletion: %', sql;
-#endif
- EXECUTE sql;
- END IF;
-
- -- Delete right face, if not universe and not "flood" face
- IF e1rec.right_face != 0 AND e1rec.right_face != floodfaceid
- THEN
- sql := 'DELETE FROM ' || quote_ident(toponame)
- || '.face WHERE face_id = ' || e1rec.right_face;
-#ifdef POSTGIS_TOPOLOGY_DEBUG
- RAISE DEBUG 'Right face deletion: %', sql;
-#endif
- EXECUTE sql;
- END IF;
-
- END IF; -- }
-
- RETURN floodfaceid;
-END
-$$
-LANGUAGE 'plpgsql' VOLATILE;
+ RETURNS int AS
+ 'MODULE_PATHNAME','ST_RemEdgeModFace'
+ LANGUAGE 'c' VOLATILE;
--} ST_RemEdgeModFace