From: Sandro Santilli Date: Tue, 4 Aug 2015 21:08:22 +0000 (+0000) Subject: Implement ST_GetFaceEdges in C X-Git-Tag: 2.2.0rc1~188 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d1fc6cabb7291dbc79146219ea9332905f2e5500;p=postgis Implement ST_GetFaceEdges in C Wraps SPI_exec calls in callbacks to not force a memory context switch Funded by Tuscany Region (Italy) - SITA (CIG: 60351023B8) git-svn-id: http://svn.osgeo.org/postgis/trunk@13877 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/liblwgeom/TODO b/liblwgeom/TODO index 06f069963..773d677df 100644 --- a/liblwgeom/TODO +++ b/liblwgeom/TODO @@ -8,7 +8,7 @@ lwt_NewEdgesSplit X lwt_AddIsoEdge X lwt_GetFaceGeometry X -lwt_GetFaceEdges +lwt_GetFaceEdges X lwt_ChangeEdgeGeom lwt_MoveIsoNode diff --git a/liblwgeom/lwgeom_topo.c b/liblwgeom/lwgeom_topo.c index 87c1eaef1..ac971f053 100644 --- a/liblwgeom/lwgeom_topo.c +++ b/liblwgeom/lwgeom_topo.c @@ -2408,6 +2408,53 @@ lwt_AddEdgeNewFaces( LWT_TOPOLOGY* topo, return _lwt_AddEdge( topo, start_node, end_node, geom, skipChecks, 0 ); } +static LWGEOM * +_lwt_FaceByEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edges, int numfaceedges) +{ + LWGEOM *outg; + LWCOLLECTION *bounds; + LWGEOM **geoms = lwalloc( sizeof(LWGEOM*) * numfaceedges ); + int i, validedges = 0; + + for ( i=0; isrid, topo->hasZ, 0) + ); + } + bounds = lwcollection_construct(MULTILINETYPE, + topo->srid, + NULL, /* gbox */ + validedges, + geoms); + outg = lwgeom_buildarea( lwcollection_as_lwgeom(bounds) ); + lwcollection_release(bounds); + lwfree(geoms); +#if 0 + { + size_t sz; + char *wkt = lwgeom_to_wkt(outg, WKT_ISO, 2, &sz); + LWDEBUGF(1, "_lwt_FaceByEdges returning area: %s", wkt); + lwfree(wkt); + } +#endif + return outg; +} + LWGEOM* lwt_GetFaceGeometry(LWT_TOPOLOGY* topo, LWT_ELEMID faceid) { @@ -2461,51 +2508,306 @@ lwt_GetFaceGeometry(LWT_TOPOLOGY* topo, LWT_ELEMID faceid) return lwpoly_as_lwgeom(out); } - /* Linemerge polygon boundaries */ + outg = _lwt_FaceByEdges( topo, edges, numfaceedges ); + _lwt_release_edges(edges, numfaceedges); - LWGEOM **geoms = lwalloc( sizeof(LWGEOM*) * numfaceedges ); - int validedges = 0; - for ( i=0; isrid, topo->hasZ, 0); - return lwpoly_as_lwgeom(out); - } - LWCOLLECTION *bounds = lwcollection_construct(MULTILINETYPE, - topo->srid, - NULL, /* gbox */ - validedges, - geoms); -#if 0 /* debugging (a leaking one) */ - lwnotice("Collected face bounds: %s", lwgeom_to_wkt((LWGEOM*)bounds, - WKT_ISO, 2, NULL)); + const LWT_ISO_EDGE *isoe = &(edges[i]); + LWLINE *edge = isoe->geom; + POINTARRAY *epa = edge->points; + POINT2D p2, pt; + int match = 0; + int j; + + /* Skip if the edge is a dangling one */ + if ( isoe->face_left == isoe->face_right ) + { + LWDEBUGF(3, "_lwt_FindNextRingEdge: edge %" PRId64 + " has same face (%" PRId64 + ") on both sides, skipping", + isoe->edge_id, isoe->face_left); + continue; + } + +#if 0 + size_t sz; + LWDEBUGF(1, "Edge %" PRId64 " is %s", + isoe->edge_id, + lwgeom_to_wkt(lwline_as_lwgeom(edge), WKT_ISO, 2, &sz)); #endif - outg = lwgeom_buildarea( lwcollection_as_lwgeom(bounds) ); + /* ptarray_remove_repeated_points ? */ - _lwt_release_edges(edges, numfaceedges); - lwcollection_release(bounds); + getPoint2d_p(epa, 0, &p2); + LWDEBUGF(1, "Edges's 'first' point is %g,%g", p2.x, p2.y); + LWDEBUGF(1, "Rings's 'from' point is still %g,%g", p1.x, p1.y); + if ( p2d_same(&p1, &p2) ) + { + LWDEBUG(1, "p2d_same(p1,p2) returned true"); + LWDEBUGF(1, "First point of edge %" PRId64 + " matches ring vertex %d", isoe->edge_id, from); + /* first point matches, let's check next non-equal one */ + for ( j=1; jnpoints; ++j ) + { + getPoint2d_p(epa, j, &p2); + /* we won't check duplicated edge points */ + if ( p2d_same(&p1, &p2) ) continue; + /* we assume there are no duplicated points in ring */ + getPoint2d_p(ring, from+1, &pt); + if ( p2d_same(&pt, &p2) ) + { + match = 1; + break; /* no need to check more */ + } + } + } + else + { + LWDEBUG(1, "p2d_same(p1,p2) returned false"); + getPoint2d_p(epa, epa->npoints-1, &p2); + LWDEBUGF(1, "Edges's 'last' point is %g,%g", p2.x, p2.y); + if ( p2d_same(&p1, &p2) ) + { + LWDEBUGF(1, "Last point of edge %" PRId64 + " matches ring vertex %d", isoe->edge_id, from); + /* last point matches, let's check next non-equal one */ + for ( j=epa->npoints-2; j>=0; --j ) + { + getPoint2d_p(epa, j, &p2); + /* we won't check duplicated edge points */ + if ( p2d_same(&p1, &p2) ) continue; + /* we assume there are no duplicated points in ring */ + getPoint2d_p(ring, from+1, &pt); + if ( p2d_same(&pt, &p2) ) + { + match = 1; + break; /* no need to check more */ + } + } + } + } - return outg; + if ( match ) return i; + + } + + return -1; } +/* Reverse values in array between "from" (inclusive) + * and "to" (exclusive) indexes */ +static void +_lwt_ReverseElemidArray(LWT_ELEMID *ary, int from, int to) +{ + LWT_ELEMID t; + while (from < to) + { + t = ary[from]; + ary[from++] = ary[to]; + ary[to--] = t; + } +} + +/* Rotate values in array between "from" (inclusive) + * and "to" (exclusive) indexes, so that "rotidx" is + * the new value at "from" */ +static void +_lwt_RotateElemidArray(LWT_ELEMID *ary, int from, int to, int rotidx) +{ + _lwt_ReverseElemidArray(ary, from, rotidx-1); + _lwt_ReverseElemidArray(ary, rotidx, to-1); + _lwt_ReverseElemidArray(ary, from, to-1); +} + + int -lwt_GetFaceEdges(LWT_TOPOLOGY* topo, LWT_ELEMID face, LWT_ELEMID **edges) +lwt_GetFaceEdges(LWT_TOPOLOGY* topo, LWT_ELEMID face_id, LWT_ELEMID **out ) { - /* TODO: Construct the face geometry */ - /* TODO: Then for each ring of the face polygon... */ - lwerror("lwt_GetFaceEdges not implemented yet"); - return -1; + LWGEOM *face; + LWPOLY *facepoly; + LWT_ISO_EDGE *edges; + int numfaceedges; + int fields, i; + int nseid = 0; /* number of signed edge ids */ + int prevseid; + LWT_ELEMID *seid; /* signed edge ids */ + + /* Get list of face edges */ + numfaceedges = 1; + fields = LWT_COL_EDGE_EDGE_ID | + LWT_COL_EDGE_GEOM | + LWT_COL_EDGE_FACE_LEFT | + LWT_COL_EDGE_FACE_RIGHT + ; + edges = lwt_be_getEdgeByFace( topo, &face_id, &numfaceedges, fields ); + if ( numfaceedges == -1 ) { + lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface)); + return -1; + } + if ( ! numfaceedges ) return 0; /* no edges in output */ + + /* order edges by occurrence in face */ + + face = _lwt_FaceByEdges(topo, edges, numfaceedges); + if ( ! face ) + { + /* _lwt_FaceByEdges should have already invoked lwerror in this case */ + _lwt_release_edges(edges, numfaceedges); + return -1; + } + + if ( lwgeom_is_empty(face) ) + { + /* no edges in output */ + _lwt_release_edges(edges, numfaceedges); + lwgeom_free(face); + return 0; + } + + /* force_lhr, if the face is not the universe */ + /* _lwt_FaceByEdges seems to guaranteed RHR */ + /* lwgeom_force_clockwise(face); */ + if ( face_id ) lwgeom_reverse(face); + +#if 0 + { + size_t sz; + char *wkt = lwgeom_to_wkt(face, WKT_ISO, 2, &sz); + LWDEBUGF(1, "Geometry of face %" PRId64 " is: %s", + face_id, wkt); + lwfree(wkt); + } +#endif + + facepoly = lwgeom_as_lwpoly(face); + if ( ! facepoly ) + { + _lwt_release_edges(edges, numfaceedges); + lwgeom_free(face); + lwerror("Geometry of face %" PRId64 " is not a polygon", face_id); + return -1; + } + + nseid = prevseid = 0; + seid = lwalloc( sizeof(LWT_ELEMID) * numfaceedges ); + + /* for each ring of the face polygon... */ + for ( i=0; inrings; ++i ) + { + const POINTARRAY *ring = facepoly->rings[i]; + int j = 0; + LWT_ISO_EDGE *nextedge; + LWLINE *nextline; + + LWDEBUGF(1, "Ring %d has %d points", i, ring->npoints); + + while ( j < ring->npoints-1 ) + { + LWDEBUGF(1, "Looking for edge covering ring %d from vertex %d", + i, j); + + int edgeno = _lwt_FindNextRingEdge(ring, j, edges, numfaceedges); + if ( edgeno == -1 ) + { + /* should never happen */ + _lwt_release_edges(edges, numfaceedges); + lwgeom_free(face); + lwfree(seid); + lwerror("No edge (among %d) found to be defining geometry of face %" + PRId64, face_id); + return -1; + } + + nextedge = &(edges[edgeno]); + nextline = nextedge->geom; + + LWDEBUGF(1, "Edge %" PRId64 + " covers ring %d from vertex %d to %d", + nextedge->edge_id, i, j, j + nextline->points->npoints - 1); + +#if 0 + size_t sz; + LWDEBUGF(1, "Edge %" PRId64 " is %s", + nextedge->edge_id, +lwgeom_to_wkt(lwline_as_lwgeom(nextline), WKT_ISO, 2, &sz)); +#endif + + j += nextline->points->npoints - 1; + + /* Add next edge to the output array */ + seid[nseid++] = nextedge->face_left == face_id ? + nextedge->edge_id : + -nextedge->edge_id; + + /* avoid checking again on next time turn */ + nextedge->face_left = nextedge->face_right = -1; + } + + /* TODO: now "scroll" the list of edges so that the one + * with smaller absolute edge_id is first */ + /* Range is: [prevseid, nseid) -- [inclusive, exclusive) */ + if ( (nseid - prevseid) > 1 ) + {{ + LWT_ELEMID minid = 0; + int minidx = 0; + LWDEBUGF(1, "Looking for smallest id among the %d edges " + "composing ring %d", (nseid-prevseid), i); + for ( j=prevseid; jdata, !be->data_changed, 0); + MemoryContextSwitchTo( oldcontext ); /* switch back */ if ( spi_result != SPI_OK_SELECT ) { pfree(sqldata.data); cberror(be, "unexpected return (%d) from query execution: %s", spi_result, sql->data); @@ -718,7 +720,7 @@ cb_getEdgeById(const LWT_BE_TOPOLOGY* topo, { LWT_ISO_EDGE *edges; int spi_result; - + MemoryContext oldcontext = CurrentMemoryContext; StringInfoData sqldata; StringInfo sql = &sqldata; int i; @@ -736,6 +738,7 @@ cb_getEdgeById(const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_getEdgeById query: %s", sql->data); spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, *numelems); + 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); *numelems = -1; return NULL; @@ -768,6 +771,7 @@ cb_getEdgeByNode(const LWT_BE_TOPOLOGY* topo, StringInfoData sqldata; StringInfo sql = &sqldata; int i; + MemoryContext oldcontext = CurrentMemoryContext; initStringInfo(sql); appendStringInfoString(sql, "SELECT "); @@ -789,6 +793,7 @@ cb_getEdgeByNode(const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "data_changed is %d", topo->be_data->data_changed); 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); *numelems = -1; return NULL; @@ -817,6 +822,7 @@ cb_getEdgeByFace(const LWT_BE_TOPOLOGY* topo, { LWT_ISO_EDGE *edges; int spi_result; + MemoryContext oldcontext = CurrentMemoryContext; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -842,6 +848,7 @@ cb_getEdgeByFace(const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "data_changed is %d", topo->be_data->data_changed); 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); *numelems = -1; return NULL; @@ -870,10 +877,10 @@ cb_getFacesById(const LWT_BE_TOPOLOGY* topo, { LWT_ISO_FACE *faces; int spi_result; - StringInfoData sqldata; StringInfo sql = &sqldata; int i; + MemoryContext oldcontext = CurrentMemoryContext; initStringInfo(sql); appendStringInfoString(sql, "SELECT "); @@ -890,6 +897,7 @@ cb_getFacesById(const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "data_changed is %d", topo->be_data->data_changed); 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); *numelems = -1; return NULL; @@ -922,6 +930,7 @@ cb_getRingEdges(const LWT_BE_TOPOLOGY* topo, StringInfoData sqldata; StringInfo sql = &sqldata; int i; + MemoryContext oldcontext = CurrentMemoryContext; initStringInfo(sql); appendStringInfo(sql, "WITH RECURSIVE edgering AS ( " @@ -943,6 +952,7 @@ cb_getRingEdges(const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_getRingEdges query (limit %d): %s", limit, sql->data); spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit); + 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); *numelems = -1; return NULL; @@ -976,8 +986,8 @@ cb_getRingEdges(const LWT_BE_TOPOLOGY* topo, } val = DatumGetInt32(dat); edges[i] = val; - POSTGIS_DEBUGF(1, "Component %d in ring of edge %" PRId64 " is edge %d", - i, edge, val); + POSTGIS_DEBUGF(1, "Component %d in ring of edge " INT64_FORMAT + " is edge %d", i, edge, val); } return edges; @@ -993,6 +1003,7 @@ cb_getNodeById(const LWT_BE_TOPOLOGY* topo, StringInfoData sqldata; StringInfo sql = &sqldata; int i; + MemoryContext oldcontext = CurrentMemoryContext; initStringInfo(sql); appendStringInfoString(sql, "SELECT "); @@ -1006,6 +1017,7 @@ cb_getNodeById(const LWT_BE_TOPOLOGY* topo, appendStringInfoString(sql, ")"); POSTGIS_DEBUGF(1, "cb_getNodeById query: %s", sql->data); spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, *numelems); + 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); *numelems = -1; return NULL; @@ -1034,7 +1046,7 @@ cb_getNodeByFace(const LWT_BE_TOPOLOGY* topo, { LWT_ISO_NODE *nodes; int spi_result; - + MemoryContext oldcontext = CurrentMemoryContext; StringInfoData sqldata; StringInfo sql = &sqldata; int i; @@ -1052,6 +1064,7 @@ cb_getNodeByFace(const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_getNodeByFace query: %s", sql->data); POSTGIS_DEBUGF(1, "data_changed is %d", topo->be_data->data_changed); 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); *numelems = -1; return NULL; @@ -1084,7 +1097,7 @@ cb_getEdgeWithinDistance2D(const LWT_BE_TOPOLOGY* topo, int elems_requested = limit; size_t hexewkb_size; char *hexewkb; - + MemoryContext oldcontext = CurrentMemoryContext; StringInfoData sqldata; StringInfo sql = &sqldata; int i; @@ -1112,6 +1125,7 @@ cb_getEdgeWithinDistance2D(const LWT_BE_TOPOLOGY* topo, } lwpgnotice("cb_getEdgeWithinDistance2D: query is: %s", sql->data); spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 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); *numelems = -1; return NULL; @@ -1155,6 +1169,7 @@ cb_getNodeWithinDistance2D(const LWT_BE_TOPOLOGY* topo, const LWPOINT* pt, double dist, int* numelems, int fields, int limit) { + MemoryContext oldcontext = CurrentMemoryContext; LWT_ISO_NODE *nodes; int spi_result; size_t hexewkb_size; @@ -1193,6 +1208,7 @@ cb_getNodeWithinDistance2D(const LWT_BE_TOPOLOGY* topo, appendStringInfo(sql, " LIMIT %d", elems_requested); } spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 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); @@ -1236,6 +1252,7 @@ static int cb_insertNodes( const LWT_BE_TOPOLOGY* topo, LWT_ISO_NODE* nodes, int numelems ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -1255,6 +1272,7 @@ cb_insertNodes( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_insertNodes query: %s", sql->data); spi_result = SPI_execute(sql->data, false, numelems); + MemoryContextSwitchTo( oldcontext ); /* switch back */ if ( spi_result != SPI_OK_INSERT_RETURNING ) { cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data); @@ -1286,6 +1304,7 @@ static int cb_insertEdges( const LWT_BE_TOPOLOGY* topo, LWT_ISO_EDGE* edges, int numelems ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -1307,6 +1326,7 @@ cb_insertEdges( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_insertEdges query (%d elems): %s", numelems, sql->data); spi_result = SPI_execute(sql->data, false, numelems); + MemoryContextSwitchTo( oldcontext ); /* switch back */ if ( spi_result != ( needsEdgeIdReturn ? SPI_OK_INSERT_RETURNING : SPI_OK_INSERT ) ) { cberror(topo->be_data, "unexpected return (%d) from query execution: %s", @@ -1340,6 +1360,7 @@ static int cb_insertFaces( const LWT_BE_TOPOLOGY* topo, LWT_ISO_FACE* faces, int numelems ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -1360,6 +1381,7 @@ cb_insertFaces( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_insertFaces query (%d elems): %s", numelems, sql->data); spi_result = SPI_execute(sql->data, false, numelems); + MemoryContextSwitchTo( oldcontext ); /* switch back */ if ( spi_result != ( needsFaceIdReturn ? SPI_OK_INSERT_RETURNING : SPI_OK_INSERT ) ) { cberror(topo->be_data, "unexpected return (%d) from query execution: %s", @@ -1395,6 +1417,7 @@ cb_updateEdges( const LWT_BE_TOPOLOGY* topo, const LWT_ISO_EDGE* upd_edge, int upd_fields, const LWT_ISO_EDGE* exc_edge, int exc_fields ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -1414,6 +1437,7 @@ cb_updateEdges( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_updateEdges query: %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", @@ -1435,6 +1459,7 @@ cb_updateNodes( const LWT_BE_TOPOLOGY* topo, const LWT_ISO_NODE* upd_node, int upd_fields, const LWT_ISO_NODE* exc_node, int exc_fields ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -1454,6 +1479,7 @@ cb_updateNodes( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_updateNodes: %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", @@ -1473,6 +1499,7 @@ static int cb_updateNodesById( const LWT_BE_TOPOLOGY* topo, const LWT_ISO_NODE* nodes, int numnodes, int fields ) { + MemoryContext oldcontext = CurrentMemoryContext; int i; int spi_result; StringInfoData sqldata; @@ -1519,6 +1546,7 @@ cb_updateNodesById( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_updateNodesById query: %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", @@ -1538,6 +1566,7 @@ static int cb_updateFacesById( const LWT_BE_TOPOLOGY* topo, const LWT_ISO_FACE* faces, int numfaces ) { + MemoryContext oldcontext = CurrentMemoryContext; int i; int spi_result; StringInfoData sqldata; @@ -1559,6 +1588,7 @@ cb_updateFacesById( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_updateFacesById query: %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", @@ -1578,6 +1608,7 @@ static int cb_updateEdgesById( const LWT_BE_TOPOLOGY* topo, const LWT_ISO_EDGE* edges, int numedges, int fields ) { + MemoryContext oldcontext = CurrentMemoryContext; int i; int spi_result; StringInfoData sqldata; @@ -1640,6 +1671,7 @@ cb_updateEdgesById( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_updateEdgesById query: %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", @@ -1659,6 +1691,7 @@ static int cb_deleteEdges( const LWT_BE_TOPOLOGY* topo, const LWT_ISO_EDGE* sel_edge, int sel_fields ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -1670,6 +1703,7 @@ cb_deleteEdges( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_deleteEdges: %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", @@ -1688,6 +1722,7 @@ cb_deleteEdges( const LWT_BE_TOPOLOGY* topo, static LWT_ELEMID cb_getNextEdgeId( const LWT_BE_TOPOLOGY* topo ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -1699,6 +1734,7 @@ cb_getNextEdgeId( const LWT_BE_TOPOLOGY* topo ) appendStringInfo(sql, "SELECT nextval('\"%s\".edge_data_edge_id_seq')", topo->name); spi_result = SPI_execute(sql->data, false, 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); @@ -1727,6 +1763,7 @@ static int cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo, LWT_ELEMID split_edge, LWT_ELEMID new_edge1, LWT_ELEMID new_edge2 ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -1750,6 +1787,7 @@ cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeSplit query: %s", sql->data); spi_result = SPI_execute(sql->data, new_edge2 == -1 ? !topo->be_data->data_changed : false, 0); + MemoryContextSwitchTo( oldcontext ); /* switch back */ 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); @@ -1808,6 +1846,7 @@ cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo, "%d,%d," INT64_FORMAT ",%d)", topo->name, topogeo_id, layer_id, negate ? -new_edge1 : new_edge1, element_type); spi_result = SPI_execute(sql->data, false, 0); + MemoryContextSwitchTo( oldcontext ); /* switch back */ if ( spi_result != SPI_OK_INSERT ) { cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data); @@ -1821,6 +1860,7 @@ cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo, "%d,%d," INT64_FORMAT ",%d", topo->name, topogeo_id, layer_id, negate ? -new_edge2 : new_edge2, element_type); spi_result = SPI_execute(sql->data, false, 0); + MemoryContextSwitchTo( oldcontext ); /* switch back */ if ( spi_result != SPI_OK_INSERT ) { cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data); @@ -1830,6 +1870,8 @@ cb_updateTopoGeomEdgeSplit ( const LWT_BE_TOPOLOGY* topo, } } + /* TODO: release string info ! */ + POSTGIS_DEBUGF(1, "cb_updateTopoGeomEdgeSplit: updated %d topogeoms", ntopogeoms); return 1; @@ -1839,6 +1881,7 @@ static int cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo, LWT_ELEMID split_face, LWT_ELEMID new_face1, LWT_ELEMID new_face2 ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -1846,8 +1889,8 @@ cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo, const char *proj = "r.element_id, r.topogeo_id, r.layer_id, r.element_type"; POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit signalled " - "split of face %" PRId64 " into %" PRId64 - " and %" PRId64, + "split of face " INT64_FORMAT " into " + INT64_FORMAT " and " INT64_FORMAT, split_face, new_face1, new_face2); initStringInfo(sql); @@ -1867,6 +1910,7 @@ cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit query: %s", sql->data); spi_result = SPI_execute(sql->data, new_face2 == -1 ? !topo->be_data->data_changed : false, 0); + MemoryContextSwitchTo( oldcontext ); /* switch back */ if ( spi_result != ( new_face2 == -1 ? SPI_OK_SELECT : SPI_OK_DELETE_RETURNING ) ) { cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data); @@ -1927,6 +1971,7 @@ cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit query: %s", sql->data); spi_result = SPI_execute(sql->data, false, 0); + MemoryContextSwitchTo( oldcontext ); /* switch back */ if ( spi_result != SPI_OK_INSERT ) { cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data); @@ -1943,6 +1988,7 @@ cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit query: %s", sql->data); spi_result = SPI_execute(sql->data, false, 0); + MemoryContextSwitchTo( oldcontext ); /* switch back */ if ( spi_result != SPI_OK_INSERT ) { cberror(topo->be_data, "unexpected return (%d) from query execution: %s", spi_result, sql->data); @@ -1952,6 +1998,8 @@ cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo, } } + /* TODO: release string info */ + POSTGIS_DEBUGF(1, "cb_updateTopoGeomFaceSplit: updated %d topogeoms", ntopogeoms); return 1; @@ -1960,6 +2008,7 @@ cb_updateTopoGeomFaceSplit ( const LWT_BE_TOPOLOGY* topo, static LWT_ELEMID cb_getFaceContainingPoint( const LWT_BE_TOPOLOGY* topo, const LWPOINT* pt ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -1981,6 +2030,7 @@ cb_getFaceContainingPoint( const LWT_BE_TOPOLOGY* topo, const LWPOINT* pt ) lwfree(hexewkb); spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, 1); + 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); @@ -2006,6 +2056,7 @@ static int cb_deleteFacesById( const LWT_BE_TOPOLOGY* topo, const LWT_ELEMID* ids, int numelems ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result, i; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -2020,6 +2071,7 @@ cb_deleteFacesById( const LWT_BE_TOPOLOGY* topo, POSTGIS_DEBUGF(1, "cb_deleteFacesById query: %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", @@ -2040,6 +2092,7 @@ static LWT_ISO_NODE* cb_getNodeWithinBox2D ( const LWT_BE_TOPOLOGY* topo, const GBOX* box, int* numelems, int fields, int limit ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -2066,6 +2119,7 @@ cb_getNodeWithinBox2D ( const LWT_BE_TOPOLOGY* topo, const GBOX* box, } lwpgnotice("cb_getNodeWithinBox2D: query is: %s", sql->data); spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 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); *numelems = -1; return NULL; @@ -2108,6 +2162,7 @@ static LWT_ISO_EDGE* cb_getEdgeWithinBox2D ( const LWT_BE_TOPOLOGY* topo, const GBOX* box, int* numelems, int fields, int limit ) { + MemoryContext oldcontext = CurrentMemoryContext; int spi_result; StringInfoData sqldata; StringInfo sql = &sqldata; @@ -2134,6 +2189,7 @@ cb_getEdgeWithinBox2D ( const LWT_BE_TOPOLOGY* topo, const GBOX* box, } lwpgnotice("cb_getEdgeWithinBox2D: query is: %s", sql->data); spi_result = SPI_execute(sql->data, !topo->be_data->data_changed, limit >= 0 ? limit : 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); *numelems = -1; return NULL; @@ -2726,3 +2782,138 @@ Datum ST_GetFaceGeometry(PG_FUNCTION_ARGS) PG_RETURN_POINTER(geom); } + +typedef struct FACEEDGESSTATE +{ + LWT_ELEMID *elems; + int nelems; + int curr; +} +FACEEDGESSTATE; + +/* ST_GetFaceEdges(atopology, aface) */ +Datum ST_GetFaceEdges(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(ST_GetFaceEdges); +Datum ST_GetFaceEdges(PG_FUNCTION_ARGS) +{ + text* toponame_text; + char* toponame; + LWT_ELEMID face_id; + int nelems; + LWT_ELEMID *elems; + LWT_TOPOLOGY *topo; + FuncCallContext *funcctx; + MemoryContext oldcontext, newcontext; + TupleDesc tupdesc; + HeapTuple tuple; + AttInMetadata *attinmeta; + FACEEDGESSTATE *state; + char buf[64]; + char *values[2]; + Datum result; + + values[0] = buf; + values[1] = &(buf[32]); + + if (SRF_IS_FIRSTCALL()) + { + + POSTGIS_DEBUG(1, "ST_GetFaceEdges first call"); + funcctx = SRF_FIRSTCALL_INIT(); + newcontext = funcctx->multi_call_memory_ctx; + + 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); + + face_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); + oldcontext = MemoryContextSwitchTo( newcontext ); + 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_GetFaceEdges"); + nelems = lwt_GetFaceEdges(topo, face_id, &elems); + POSTGIS_DEBUGF(1, "lwt_GetFaceEdges returned %d", nelems); + lwt_FreeTopology(topo); + + if ( nelems < 0 ) { + /* should never reach this point, as lwerror would raise an exception */ + SPI_finish(); + PG_RETURN_NULL(); + } + + state = lwalloc(sizeof(FACEEDGESSTATE)); + state->elems = elems; + state->nelems = nelems; + state->curr = 0; + funcctx->user_fctx = state; + + /* + * Build a tuple description for an + * geometry_dump tuple + */ + tupdesc = RelationNameGetTupleDesc("topology.getfaceedges_returntype"); + + /* + * generate attribute metadata needed later to produce + * tuples from raw C strings + */ + attinmeta = TupleDescGetAttInMetadata(tupdesc); + funcctx->attinmeta = attinmeta; + + POSTGIS_DEBUG(1, "lwt_GetFaceEdges calling SPI_finish"); + + MemoryContextSwitchTo(oldcontext); + + SPI_finish(); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + + /* get state */ + state = funcctx->user_fctx; + + if ( state->curr == state->nelems ) + { + SRF_RETURN_DONE(funcctx); + } + + if ( snprintf(values[0], 32, "%d", state->curr+1) >= 32 ) + { + lwerror("Face edge sequence number does not fit 32 chars ?!: %d", + state->curr+1); + } + if ( snprintf(values[1], 32, INT64_FORMAT, + state->elems[state->curr]) >= 32 ) + { + lwerror("Signed edge identifier does not fit 32 chars ?!: " + INT64_FORMAT, state->elems[state->curr]); + } + + POSTGIS_DEBUGF(1, "ST_GetFaceEdges: cur:%d, val0:%s, val1:%s", + state->curr, values[0], values[1]); + + tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); + result = HeapTupleGetDatum(tuple); + state->curr++; + + SRF_RETURN_NEXT(funcctx, result); +} diff --git a/topology/sql/sqlmm.sql.in b/topology/sql/sqlmm.sql.in index c426f931a..5d7236d70 100644 --- a/topology/sql/sqlmm.sql.in +++ b/topology/sql/sqlmm.sql.in @@ -40,104 +40,9 @@ CREATE TYPE topology.GetFaceEdges_ReturnType AS ( -- -- CREATE OR REPLACE FUNCTION topology.ST_GetFaceEdges(toponame varchar, face_id integer) - RETURNS SETOF topology.GetFaceEdges_ReturnType -AS -$$ -DECLARE - rec RECORD; - bounds geometry; - retrec topology.GetFaceEdges_ReturnType; - n int; - sql TEXT; -BEGIN - -- - -- toponame and face_id are required - -- - IF toponame IS NULL OR face_id IS NULL THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - null argument'; - END IF; - - IF NOT EXISTS(SELECT name FROM topology.topology WHERE name = toponame) THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name'; - END IF; - - n := 1; - - -- Construct the face geometry, then for each ring of each polygon: - sql := 'SELECT (ST_DumpRings((ST_Dump(ST_ForceRHR(' - || 'ST_BuildArea(ST_Collect(geom))))).geom)).geom FROM ' - || quote_ident(toponame) || '.edge_data WHERE left_face = ' - || face_id || ' OR right_face = ' || face_id; - FOR rec IN EXECUTE sql - LOOP -- { - - -- Find the edges constituting its boundary - bounds = ST_Boundary(rec.geom); - - - sql := 'WITH er2 AS ( ' - || 'WITH er AS ( SELECT ' - || 'min(e.edge_id) over (), count(*) over () as cnt, e.edge_id, ' - || 'ST_LineLocatePoint($1' - || ', ST_LineInterpolatePoint(e.geom, 0.2)) as pos' - || ', ST_LineLocatePoint($1' - || ', ST_LineInterpolatePoint(e.geom, 0.8)) as pos2 FROM ' - || quote_ident(toponame) - || '.edge e WHERE ( e.left_face = ' || face_id - || ' OR e.right_face = ' || face_id - || ') AND ST_Covers($1, e.geom)'; - IF face_id = 0 THEN - sql := sql || ' ORDER BY POS ASC) '; - ELSE - sql := sql || ' ORDER BY POS DESC) '; - END IF; - - -- Reorder rows so to start with the one with smaller edge_id - sql := sql || 'SELECT row_number() over () - 1 as rn, * FROM er ) ' - || 'SELECT *, ( rn + cnt - ( select rn FROM er2 WHERE edge_id = min ) ) % cnt AS reord FROM er2 ORDER BY reord'; - - - --RAISE DEBUG 'SQL: %', sql; - - FOR rec IN EXECUTE sql USING bounds - LOOP - -#ifdef POSTGIS_TOPOLOGY_DEBUG - RAISE DEBUG 'rn:%, n:%, edg:%, cnt:%, min:%, reord:%', - rec.rn, n, rec.edge_id, rec.cnt, rec.min, rec.reord; -#endif - - retrec.sequence = n; - retrec.edge = rec.edge_id; - - IF face_id = 0 THEN - -- if this edge goes in opposite direction to the - -- ring bounds, make it with negative orientation - IF rec.pos2 < rec.pos THEN -- edge goes in opposite direction - retrec.edge = -retrec.edge; - END IF; - ELSE - -- if this edge goes in same direction to the - -- ring bounds, make it with negative orientation - IF rec.pos2 > rec.pos THEN -- edge goes in same direction - retrec.edge = -retrec.edge; - END IF; - END IF; - - RETURN NEXT retrec; - - n = n+1; - - END LOOP; - END LOOP; -- } - - RETURN; -EXCEPTION - WHEN INVALID_SCHEMA_NAME THEN - RAISE EXCEPTION 'SQL/MM Spatial exception - invalid topology name'; -END -$$ -LANGUAGE 'plpgsql' STABLE; + RETURNS SETOF topology.GetFaceEdges_ReturnType AS + 'MODULE_PATHNAME', 'ST_GetFaceEdges' + LANGUAGE 'c' STABLE; --} ST_GetFaceEdges --{