Skip to content

Commit

Permalink
Implement ST_NewEdgesSplit in C
Browse files Browse the repository at this point in the history
Involved adding a new "deleteEdges" callback.

Funded by Tuscany Region (Italy) - SITA (CIG: 60351023B8)

git-svn-id: http://svn.osgeo.org/postgis/trunk@13738 b70326c6-7e19-0410-871a-916f4a2858ee
  • Loading branch information
Sandro Santilli committed Jun 29, 2015
1 parent 193aac3 commit aacf8e6
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 289 deletions.
22 changes: 20 additions & 2 deletions liblwgeom/liblwgeom_topo.h
Expand Up @@ -398,6 +398,22 @@ typedef struct LWT_BE_CALLBACKS_T {
LWT_ELEMID split_edge, LWT_ELEMID new_edge1, LWT_ELEMID new_edge2
);

/**
* Delete edges
*
* @param topo the topology to act upon
* @param sel_edge an LWT_ISO_EDGE object with selecting fields set.
* @param sel_fields fields used to select edges to be deleted,
* see LWT_COL_EDGE_* macros
*
* @return number of edges being deleted or -1 on error
* (@see lastErroMessage)
*/
int (*deleteEdges) (
const LWT_BE_TOPOLOGY* topo,
const LWT_ISO_EDGE* sel_edge, int sel_fields
);

} LWT_BE_CALLBACKS;


Expand Down Expand Up @@ -815,7 +831,7 @@ void lwt_ChangeEdgeGeom(LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWGEOM* geom);
* @param edge identifier of the edge to be split
* @param pt geometry of the new node
* @param skipChecks if non-zero skips consistency checks
* (coincident node)
* (coincident node, point not on edge...)
* @return the id of newly created node, or -1 on error
* (liblwgeom error handler will be invoked with error message)
*
Expand All @@ -830,10 +846,12 @@ LWT_ELEMID lwt_ModEdgeSplit(LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWPOINT* pt, in
* @param topo the topology to operate on
* @param edge identifier of the edge to be split
* @param pt geometry of the new node
* @param skipChecks if non-zero skips consistency checks
* (coincident node, point not on edge...)
* @return the id of newly created node
*
*/
LWT_ELEMID lwt_NewEdgesSplit(LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWPOINT* pt);
LWT_ELEMID lwt_NewEdgesSplit(LWT_TOPOLOGY* topo, LWT_ELEMID edge, LWPOINT* pt, int skipChecks);

/**
* Merge two edges, modifying the first and deleting the second
Expand Down
2 changes: 2 additions & 0 deletions liblwgeom/liblwgeom_topo_internal.h
Expand Up @@ -53,6 +53,8 @@ int
lwt_be_insertEdges(LWT_TOPOLOGY* topo, LWT_ISO_EDGE* edge, int numelems);
int
lwt_be_updateEdges(LWT_TOPOLOGY* topo, const LWT_ISO_EDGE* sel_edge, int sel_fields, const LWT_ISO_EDGE* upd_edge, int upd_fields, const LWT_ISO_EDGE* exc_edge, int exc_fields);
int
lwt_be_deleteEdges(LWT_TOPOLOGY* topo, const LWT_ISO_EDGE* sel_edge, int sel_fields);

LWT_ELEMID lwt_be_getFaceContainingPoint(LWT_TOPOLOGY* topo, LWPOINT* pt);

Expand Down
182 changes: 182 additions & 0 deletions liblwgeom/lwgeom_topo.c
Expand Up @@ -160,6 +160,14 @@ lwt_be_updateEdges(LWT_TOPOLOGY* topo,
exc_edge, exc_fields);
}

int
lwt_be_deleteEdges(LWT_TOPOLOGY* topo,
const LWT_ISO_EDGE* sel_edge, int sel_fields
)
{
CBT2(topo, deleteEdges, sel_edge, sel_fields);
}

LWT_ELEMID
lwt_be_getFaceContainingPoint(LWT_TOPOLOGY* topo, LWPOINT* pt)
{
Expand Down Expand Up @@ -496,3 +504,177 @@ lwt_ModEdgeSplit( LWT_TOPOLOGY* topo, LWT_ELEMID edge,
/* return new node id */
return node.node_id;
}

LWT_ELEMID
lwt_NewEdgesSplit( LWT_TOPOLOGY* topo, LWT_ELEMID edge,
LWPOINT* pt, int skipISOChecks )
{
LWT_ISO_NODE node;
LWT_ISO_EDGE* oldedge = NULL;
LWCOLLECTION *split_col;
const LWGEOM *oldedge_geom;
const LWGEOM *newedge_geom;
LWT_ISO_EDGE newedges[2];
//LWT_ISO_EDGE newedge1, newedge2;
LWT_ISO_EDGE seledge, updedge;
int ret;

split_col = _lwt_EdgeSplit( topo, edge, pt, skipISOChecks, &oldedge );
if ( ! split_col ) return -1; /* should have raised an exception */
oldedge_geom = split_col->geoms[0];
newedge_geom = split_col->geoms[1];

/* Add new node, getting new id back */
node.node_id = -1;
node.containing_face = -1; /* means not-isolated */
node.geom = pt;
if ( ! lwt_be_insertNodes(topo, &node, 1) )
{
lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
return -1;
}
if (node.node_id == -1) {
/* should have been set by backend */
lwerror("Backend coding error: "
"insertNodes callback did not return node_id");
return -1;
}

/* Delete the old edge */
seledge.edge_id = edge;
ret = lwt_be_deleteEdges(topo, &seledge, LWT_COL_EDGE_EDGE_ID);
if ( ret == -1 ) {
lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
return -1;
}

/* Get new edges identifiers */
newedges[0].edge_id = lwt_be_getNextEdgeId(topo);
if ( newedges[0].edge_id == -1 ) {
lwcollection_release(split_col);
lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
return -1;
}
newedges[1].edge_id = lwt_be_getNextEdgeId(topo);
if ( newedges[1].edge_id == -1 ) {
lwcollection_release(split_col);
lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
return -1;
}

/* Define the first new edge (to new node) */
newedges[0].start_node = oldedge->start_node;
newedges[0].end_node = node.node_id;
newedges[0].face_left = oldedge->face_left;
newedges[0].face_right = oldedge->face_right;
newedges[0].next_left = newedges[1].edge_id;
if ( oldedge->next_right == edge )
newedges[0].next_right = newedges[0].edge_id;
else if ( oldedge->next_right == -edge )
newedges[0].next_right = -newedges[1].edge_id;
else
newedges[0].next_right = oldedge->next_right;
newedges[0].geom = lwgeom_as_lwline(oldedge_geom);
/* lwgeom_split of a line should only return lines ... */
if ( ! newedges[0].geom ) {
lwcollection_release(split_col);
lwerror("first geometry in lwgeom_split output is not a line");
return -1;
}

/* Define the second new edge (from new node) */
newedges[1].start_node = node.node_id;
newedges[1].end_node = oldedge->end_node;
newedges[1].face_left = oldedge->face_left;
newedges[1].face_right = oldedge->face_right;
newedges[1].next_right = -newedges[0].edge_id;
if ( oldedge->next_left == -edge )
newedges[1].next_left = -newedges[1].edge_id;
else if ( oldedge->next_left == edge )
newedges[1].next_left = newedges[0].edge_id;
else
newedges[1].next_left = oldedge->next_left;
newedges[1].geom = lwgeom_as_lwline(newedge_geom);
/* lwgeom_split of a line should only return lines ... */
if ( ! newedges[1].geom ) {
lwcollection_release(split_col);
lwerror("second geometry in lwgeom_split output is not a line");
return -1;
}

/* Insert both new edges */
ret = lwt_be_insertEdges(topo, newedges, 2);
if ( ret == -1 ) {
lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
return -1;
} else if ( ret == 0 ) {
lwcollection_release(split_col);
lwerror("Insertion of split edge failed (no reason)");
return -1;
}

/* Update all next edge references pointing to old edge id */

updedge.next_right = newedges[1].edge_id;
seledge.next_right = edge;
seledge.start_node = oldedge->start_node;
ret = lwt_be_updateEdges(topo,
&seledge, LWT_COL_EDGE_NEXT_RIGHT|LWT_COL_EDGE_START_NODE,
&updedge, LWT_COL_EDGE_NEXT_RIGHT,
NULL, 0);
if ( ret == -1 ) {
lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
return -1;
}

updedge.next_right = -newedges[0].edge_id;
seledge.next_right = -edge;
seledge.start_node = oldedge->end_node;
ret = lwt_be_updateEdges(topo,
&seledge, LWT_COL_EDGE_NEXT_RIGHT|LWT_COL_EDGE_START_NODE,
&updedge, LWT_COL_EDGE_NEXT_RIGHT,
NULL, 0);
if ( ret == -1 ) {
lwcollection_release(split_col);
lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
return -1;
}

updedge.next_left = newedges[0].edge_id;
seledge.next_left = edge;
seledge.end_node = oldedge->start_node;
ret = lwt_be_updateEdges(topo,
&seledge, LWT_COL_EDGE_NEXT_LEFT|LWT_COL_EDGE_END_NODE,
&updedge, LWT_COL_EDGE_NEXT_LEFT,
NULL, 0);
if ( ret == -1 ) {
lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
return -1;
}

updedge.next_left = -newedges[1].edge_id;
seledge.next_left = -edge;
seledge.end_node = oldedge->end_node;
ret = lwt_be_updateEdges(topo,
&seledge, LWT_COL_EDGE_NEXT_LEFT|LWT_COL_EDGE_END_NODE,
&updedge, LWT_COL_EDGE_NEXT_LEFT,
NULL, 0);
if ( ret == -1 ) {
lwcollection_release(split_col);
lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
return -1;
}

/* Update TopoGeometries composition -- TODO */
ret = lwt_be_updateTopoGeomEdgeSplit(topo, oldedge->edge_id, newedges[0].edge_id, newedges[1].edge_id);
if ( ! ret ) {
lwcollection_release(split_col);
lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
return -1;
}

lwcollection_release(split_col);

/* return new node id */
return node.node_id;
}
102 changes: 98 additions & 4 deletions topology/postgis_topology.c
Expand Up @@ -767,6 +767,34 @@ cb_updateEdges( const LWT_BE_TOPOLOGY* topo,
return SPI_processed;
}

static int
cb_deleteEdges( const LWT_BE_TOPOLOGY* topo,
const LWT_ISO_EDGE* sel_edge, int sel_fields )
{
int spi_result;
StringInfoData sqldata;
StringInfo sql = &sqldata;

initStringInfo(sql);
appendStringInfo(sql, "DELETE FROM \"%s\".edge_data WHERE ", topo->name);
addEdgeUpdate( sql, sel_edge, sel_fields, 0, updSel );

/* lwpgnotice("cb_deleteEdges: %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);

lwpgnotice("cb_deleteEdges: delete query processed %d rows", SPI_processed);

return SPI_processed;
}

static LWT_ELEMID
cb_getNextEdgeId( const LWT_BE_TOPOLOGY* topo )
{
Expand Down Expand Up @@ -819,15 +847,15 @@ cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo,
} else {
appendStringInfoString(sql, "DELETE");
}
appendStringInfo(sql, " FROM \"%s\".relation r, topology.layer l WHERE "
appendStringInfo( sql, " FROM \"%s\".relation r %s topology.layer l WHERE "
"l.topology_id = %d AND l.level = 0 AND l.layer_id = r.layer_id "
"AND abs(r.element_id) = %lld AND r.element_type = 2",
topo->name, topo->id, split_edge);
topo->name, (new_edge2 == -1 ? "," : "USING" ), topo->id, split_edge );
if ( new_edge2 != -1 ) {
appendStringInfo(sql, " RETURNING %s", proj);
}

spi_result = SPI_execute(sql->data, true, 0);
spi_result = SPI_execute(sql->data, new_edge2 == -1, 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",
spi_result, sql->data);
Expand Down Expand Up @@ -964,7 +992,8 @@ LWT_BE_CALLBACKS be_callbacks = {
cb_updateEdges,
NULL, /* getFacesById */
cb_getFaceContainingPoint,
cb_updateTopoGeomEdgeSplit
cb_updateTopoGeomEdgeSplit,
cb_deleteEdges
};


Expand Down Expand Up @@ -1074,6 +1103,71 @@ Datum ST_ModEdgeSplit(PG_FUNCTION_ARGS)
PG_RETURN_INT32(node_id);
}

/* ST_NewEdgesSplit(atopology, anedge, apoint) */
Datum ST_NewEdgesSplit(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(ST_NewEdgesSplit);
Datum ST_NewEdgesSplit(PG_FUNCTION_ARGS)
{
text* toponame_text;
char* toponame;
LWT_ELEMID edge_id;
LWT_ELEMID node_id;
GSERIALIZED *geom;
LWGEOM *lwgeom;
LWPOINT *pt;
LWT_TOPOLOGY *topo;

if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) ) {
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);

edge_id = PG_GETARG_INT32(1) ;

geom = PG_GETARG_GSERIALIZED_P(2);
lwgeom = lwgeom_from_gserialized(geom);
pt = lwgeom_as_lwpoint(lwgeom);
if ( ! pt ) {
lwgeom_free(lwgeom);
PG_FREE_IF_COPY(geom, 2);
lwpgerror("ST_NewEdgesSplit third argument must be a point geometry");
PG_RETURN_NULL();
}

if ( SPI_OK_CONNECT != SPI_connect() ) {
lwpgerror("Could not connect to SPI");
PG_RETURN_NULL();
}

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_NewEdgesSplit");
node_id = lwt_NewEdgesSplit(topo, edge_id, pt, 0);
POSTGIS_DEBUG(1, "lwt_NewEdgesSplit returned");
lwgeom_free(lwgeom);
PG_FREE_IF_COPY(geom, 3);
lwt_FreeTopology(topo);

if ( node_id == -1 ) {
/* should never reach this point, as lwerror would raise an exception */
SPI_finish();
PG_RETURN_NULL();
}

SPI_finish();
PG_RETURN_INT32(node_id);
}

/* ST_AddIsoNode(atopology, aface, apoint) */
Datum ST_AddIsoNode(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(ST_AddIsoNode);
Expand Down

0 comments on commit aacf8e6

Please sign in to comment.