PostGIS  3.7.0dev-r@@SVN_REVISION@@

◆ _lwt_AddFaceSplit()

static LWT_ELEMID _lwt_AddFaceSplit ( LWT_TOPOLOGY topo,
LWT_ELEMID  sedge,
LWT_ELEMID  face,
int  mbr_only 
)
static

Definition at line 1893 of file lwgeom_topo.c.

1896 {
1897  uint64_t numfaceedges, i, j;
1898  int newface_outside;
1899  uint64_t num_signed_edge_ids;
1900  LWT_ELEMID *signed_edge_ids;
1901  LWT_ISO_EDGE *edges;
1902  LWT_ISO_EDGE *forward_edges = NULL;
1903  int forward_edges_count = 0;
1904  LWT_ISO_EDGE *backward_edges = NULL;
1905  int backward_edges_count = 0;
1906 
1907  signed_edge_ids = lwt_be_getRingEdges(topo, sedge, &num_signed_edge_ids, 0);
1908  if (!signed_edge_ids)
1909  {
1910  //PGTOPO_BE_ERRORF("no ring edges for edge %" LWTFMT_ELEMID, sedge);
1911  PGTOPO_BE_ERROR();
1912  return -2;
1913  }
1914  LWDEBUGF(1, "getRingEdges returned %llu edges", num_signed_edge_ids);
1915 
1916  /* You can't get to the other side of an edge forming a ring */
1917  for (i=0; i<num_signed_edge_ids; ++i) {
1918  if ( signed_edge_ids[i] == -sedge ) {
1919  /* No split here */
1920  LWDEBUG(1, "not a ring");
1921  lwfree( signed_edge_ids );
1922  return 0;
1923  }
1924  }
1925 
1926  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " split face %" LWTFMT_ELEMID " (mbr_only:%d)",
1927  sedge, face, mbr_only);
1928 
1929  /*
1930  * Construct a polygon using edges of the ring
1931  *
1932  * NOTE: this possibly includes dangling edges
1933  *
1934  */
1935  LWPOLY *shell = _lwt_MakeRingShell(topo, signed_edge_ids,
1936  num_signed_edge_ids);
1937  if ( ! shell ) {
1938  lwfree( signed_edge_ids );
1939  /* ring_edges should be NULL */
1940  lwerror("Could not create ring shell: %s", lwt_be_lastErrorMessage(topo->be_iface));
1941  return -2;
1942  }
1943  const POINTARRAY *pa = shell->rings[0];
1944  if ( ! ptarray_is_closed_2d(pa) )
1945  {
1946  lwpoly_free(shell);
1947  lwfree( signed_edge_ids );
1948  lwerror("Corrupted topology: ring of edge %" LWTFMT_ELEMID
1949  " is geometrically not-closed", sedge);
1950  return -2;
1951  }
1952 
1953  int isccw = ptarray_isccw(pa);
1954  LWDEBUGF(1, "Ring of edge %" LWTFMT_ELEMID " is %sclockwise",
1955  sedge, isccw ? "counter" : "");
1956  const GBOX* shellbox = lwgeom_get_bbox(lwpoly_as_lwgeom(shell));
1957 
1958  if ( face == 0 )
1959  {
1960  /* Edge split the universe face */
1961  if ( ! isccw )
1962  {
1963  lwpoly_free(shell);
1964  lwfree( signed_edge_ids );
1965  /* Face on the left side of this ring is the universe face.
1966  * Next call (for the other side) should create the split face
1967  */
1968  LWDEBUG(1, "The left face of this clockwise ring is the universe, "
1969  "won't create a new face there");
1970  return -1;
1971  }
1972  }
1973 
1974  if ( mbr_only && face != 0 )
1975  {
1976  if ( isccw )
1977  {{
1978  LWT_ISO_FACE updface;
1979  updface.face_id = face;
1980  updface.mbr = (GBOX *)shellbox; /* const cast, we won't free it, later */
1981  int ret = lwt_be_updateFacesById( topo, &updface, 1 );
1982  if ( ret == -1 )
1983  {
1984  lwfree( signed_edge_ids );
1985  lwpoly_free(shell); /* NOTE: owns shellbox above */
1986  PGTOPO_BE_ERROR();
1987  return -2;
1988  }
1989  if ( ret != 1 )
1990  {
1991  lwfree( signed_edge_ids );
1992  lwpoly_free(shell); /* NOTE: owns shellbox above */
1993  lwerror("Unexpected error: %d faces found when expecting 1", ret);
1994  return -2;
1995  }
1996  }}
1997  lwfree( signed_edge_ids );
1998  lwpoly_free(shell); /* NOTE: owns shellbox above */
1999  return -1; /* mbr only was requested */
2000  }
2001 
2002  LWT_ISO_FACE *oldface = NULL;
2003  LWT_ISO_FACE newface;
2004  newface.face_id = -1;
2005  if ( face != 0 && ! isccw)
2006  {{
2007  /* Face created an hole in an outer face */
2008  uint64_t nfaces = 1;
2009  oldface = lwt_be_getFaceById(topo, &face, &nfaces, LWT_COL_FACE_ALL);
2010  if (nfaces == UINT64_MAX)
2011  {
2012  lwfree( signed_edge_ids );
2013  lwpoly_free(shell); /* NOTE: owns shellbox */
2014  PGTOPO_BE_ERROR();
2015  return -2;
2016  }
2017  if ( nfaces != 1 )
2018  {
2019  lwfree( signed_edge_ids );
2020  lwpoly_free(shell); /* NOTE: owns shellbox */
2021  lwerror("Unexpected error: %" PRIu64 " faces found when expecting 1", nfaces);
2022  return -2;
2023  }
2024  newface.mbr = oldface->mbr;
2025  }}
2026  else
2027  {
2028  newface.mbr = (GBOX *)shellbox; /* const cast, we won't free it, later */
2029  }
2030 
2031  /* Insert the new face */
2032  int ret = lwt_be_insertFaces( topo, &newface, 1 );
2033  if ( ret == -1 )
2034  {
2035  lwfree( signed_edge_ids );
2036  lwpoly_free(shell); /* NOTE: owns shellbox */
2037  PGTOPO_BE_ERROR();
2038  return -2;
2039  }
2040  if ( ret != 1 )
2041  {
2042  lwfree( signed_edge_ids );
2043  lwpoly_free(shell); /* NOTE: owns shellbox */
2044  lwerror("Unexpected error: %d faces inserted when expecting 1", ret);
2045  return -2;
2046  }
2047  if ( oldface ) {
2048  newface.mbr = NULL; /* it is a reference to oldface mbr... */
2049  _lwt_release_faces(oldface, 1);
2050  }
2051 
2052  /* Update side location of new face edges */
2053 
2054  /* We want the new face to be on the left, if possible */
2055  if ( face != 0 && ! isccw ) { /* ring is clockwise in a real face */
2056  /* face shrunk, must update all non-contained edges and nodes */
2057  LWDEBUG(1, "New face is on the outside of the ring, updating rings in former shell");
2058  newface_outside = 1;
2059  /* newface is outside */
2060  } else {
2061  LWDEBUG(1, "New face is on the inside of the ring, updating forward edges in new ring");
2062  newface_outside = 0;
2063  /* newface is inside */
2064  }
2065 
2066  /* Update edges bounding the old face */
2067  /* (1) fetch all edges where left_face or right_face is = oldface */
2068  int fields = LWT_COL_EDGE_EDGE_ID |
2072  ;
2073  numfaceedges = 1;
2074  edges = lwt_be_getEdgeByFace( topo, &face, &numfaceedges, fields, newface.mbr );
2075  if (numfaceedges == UINT64_MAX)
2076  {
2077  lwfree(signed_edge_ids);
2078  PGTOPO_BE_ERROR();
2079  return -2;
2080  }
2081  LWDEBUGF(1, "_lwt_AddFaceSplit: lwt_be_getEdgeByFace(%" LWTFMT_ELEMID ") returned %llu edges", face, numfaceedges);
2082 
2083  if ( numfaceedges )
2084  {
2085  forward_edges = lwalloc(sizeof(LWT_ISO_EDGE)*numfaceedges);
2086  forward_edges_count = 0;
2087  backward_edges = lwalloc(sizeof(LWT_ISO_EDGE)*numfaceedges);
2088  backward_edges_count = 0;
2089 
2090  /* (2) loop over the results and: */
2091  for ( i=0; i<numfaceedges; ++i )
2092  {
2093  LWT_ISO_EDGE *e = &(edges[i]);
2094  int found = 0;
2095  int contains;
2096  POINT2D ep;
2097 
2098  /* (2.1) skip edges whose ID is in the list of boundary edges */
2099  for ( j=0; j<num_signed_edge_ids; ++j )
2100  {
2101  int seid = signed_edge_ids[j]; /* signed ring edge id */
2102  if ( seid == e->edge_id )
2103  {
2104  /* IDEA: remove entry from signed_edge_ids, to speed next loop ? */
2105  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is a known forward edge of the new ring", e->edge_id);
2106  forward_edges[forward_edges_count].edge_id = e->edge_id;
2107  forward_edges[forward_edges_count++].face_left = newface.face_id;
2108  found++;
2109  if ( found == 2 ) break; /* both edge sides are found on the ring */
2110  }
2111  else if ( -seid == e->edge_id )
2112  {
2113  /* IDEA: remove entry from signed_edge_ids, to speed next loop ? */
2114  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is a known backward edge of the new ring", e->edge_id);
2115  backward_edges[backward_edges_count].edge_id = e->edge_id;
2116  backward_edges[backward_edges_count++].face_right = newface.face_id;
2117  found++;
2118  if ( found == 2 ) break; /* both edge sides are found on the ring */
2119  }
2120  }
2121  if ( found ) continue;
2122  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " is not a known edge of the new ring", e->edge_id);
2123 
2124  /* Check if the edge is now binding a different face */
2125 
2126  if ( ! getPoint2d_p(e->geom->points, 0, &ep) )
2127  {
2128  lwfree(signed_edge_ids);
2129  lwpoly_free(shell);
2130  lwfree(forward_edges); /* contents owned by edges */
2131  lwfree(backward_edges); /* contents owned by edges */
2132  _lwt_release_edges(edges, numfaceedges);
2133  lwerror("Edge %" LWTFMT_ELEMID " is empty", e->edge_id);
2134  return -2;
2135  }
2136 
2137  contains = gbox_contains_point2d(shellbox, &ep) == LW_TRUE ? LW_INSIDE : LW_OUTSIDE;
2139 
2140  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " first point %s new ring",
2141  e->edge_id, (contains == LW_INSIDE ? "inside" :
2142  contains == LW_OUTSIDE ? "outside" : "on boundary of"));
2143 
2144  /* (2.2) skip edges (NOT, if newface_outside) contained in ring */
2145  if ( newface_outside )
2146  {
2147  if ( contains != LW_OUTSIDE )
2148  {
2149  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " not outside of the new ring, not updating it",
2150  e->edge_id);
2151  continue;
2152  }
2153  }
2154  else
2155  {
2156  if ( contains != LW_INSIDE )
2157  {
2158  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " not inside the new ring, not updating it",
2159  e->edge_id);
2160  continue;
2161  }
2162  }
2163 
2164  /* (2.3) push to forward_edges if left_face = oface */
2165  if ( e->face_left == face )
2166  {
2167  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " has new face on the left side", e->edge_id);
2168  forward_edges[forward_edges_count].edge_id = e->edge_id;
2169  forward_edges[forward_edges_count++].face_left = newface.face_id;
2170  }
2171 
2172  /* (2.4) push to backward_edges if right_face = oface */
2173  if ( e->face_right == face )
2174  {
2175  LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " has new face on the right side", e->edge_id);
2176  backward_edges[backward_edges_count].edge_id = e->edge_id;
2177  backward_edges[backward_edges_count++].face_right = newface.face_id;
2178  }
2179  }
2180 
2181  /* Update forward edges */
2182  if ( forward_edges_count )
2183  {
2184  ret = lwt_be_updateEdgesById(topo, forward_edges,
2185  forward_edges_count,
2187  if ( ret == -1 )
2188  {
2189  lwfree( signed_edge_ids );
2190  PGTOPO_BE_ERROR();
2191  return -2;
2192  }
2193  if ( ret != forward_edges_count )
2194  {
2195  lwfree( signed_edge_ids );
2196  lwerror("Unexpected error: %d edges updated when expecting %d",
2197  ret, forward_edges_count);
2198  return -2;
2199  }
2200  }
2201 
2202  /* Update backward edges */
2203  if ( backward_edges_count )
2204  {
2205  ret = lwt_be_updateEdgesById(topo, backward_edges,
2206  backward_edges_count,
2208  if ( ret == -1 )
2209  {
2210  lwfree( signed_edge_ids );
2211  PGTOPO_BE_ERROR();
2212  return -2;
2213  }
2214  if ( ret != backward_edges_count )
2215  {
2216  lwfree( signed_edge_ids );
2217  lwerror("Unexpected error: %d edges updated when expecting %d",
2218  ret, backward_edges_count);
2219  return -2;
2220  }
2221  }
2222 
2223  lwfree(forward_edges);
2224  lwfree(backward_edges);
2225 
2226  _lwt_release_edges(edges, numfaceedges);
2227  }
2228 
2229  /* Update isolated nodes which are now in new face */
2230  uint64_t numisonodes = 1;
2232  LWT_ISO_NODE *nodes = lwt_be_getNodeByFace(topo, &face,
2233  &numisonodes, fields, newface.mbr);
2234  if (numisonodes == UINT64_MAX)
2235  {
2236  lwfree(signed_edge_ids);
2237  PGTOPO_BE_ERROR();
2238  return -2;
2239  }
2240  if ( numisonodes ) {
2241  LWT_ISO_NODE *updated_nodes = lwalloc(sizeof(LWT_ISO_NODE)*numisonodes);
2242  int nodes_to_update = 0;
2243  for (i=0; i<numisonodes; ++i)
2244  {
2245  LWT_ISO_NODE *n = &(nodes[i]);
2246  const POINT2D *pt = getPoint2d_cp(n->geom->point, 0);
2247  int contains = ptarray_contains_point(pa, pt) == LW_INSIDE;
2248  LWDEBUGF(1, "Node %" LWTFMT_ELEMID " is %scontained in new ring, newface is %s",
2249  n->node_id, contains ? "" : "not ",
2250  newface_outside ? "outside" : "inside" );
2251  if ( newface_outside )
2252  {
2253  if ( contains )
2254  {
2255  LWDEBUGF(1, "Node %" LWTFMT_ELEMID " contained in an hole of the new face",
2256  n->node_id);
2257  continue;
2258  }
2259  }
2260  else
2261  {
2262  if ( ! contains )
2263  {
2264  LWDEBUGF(1, "Node %" LWTFMT_ELEMID " not contained in the face shell",
2265  n->node_id);
2266  continue;
2267  }
2268  }
2269  updated_nodes[nodes_to_update].node_id = n->node_id;
2270  updated_nodes[nodes_to_update++].containing_face =
2271  newface.face_id;
2272  LWDEBUGF(1, "Node %" LWTFMT_ELEMID " will be updated", n->node_id);
2273  }
2274  _lwt_release_nodes(nodes, numisonodes);
2275  if ( nodes_to_update )
2276  {
2277  int ret = lwt_be_updateNodesById(topo, updated_nodes,
2278  nodes_to_update,
2280  if ( ret == -1 ) {
2281  lwfree( signed_edge_ids );
2282  PGTOPO_BE_ERROR();
2283  return -2;
2284  }
2285  }
2286  lwfree(updated_nodes);
2287  }
2288 
2289  lwfree(signed_edge_ids);
2290  lwpoly_free(shell);
2291 
2292  return newface.face_id;
2293 }
int gbox_contains_point2d(const GBOX *g, const POINT2D *p)
Definition: gbox.c:362
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:329
int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point)
Definition: lwgeom_api.c:342
void lwfree(void *mem)
Definition: lwutil.c:248
const GBOX * lwgeom_get_bbox(const LWGEOM *lwgeom)
Get a non-empty geometry bounding box, computing and caching it if not already there.
Definition: lwgeom.c:743
void * lwalloc(size_t size)
Definition: lwutil.c:227
int ptarray_is_closed_2d(const POINTARRAY *pa)
Definition: ptarray.c:710
void lwpoly_free(LWPOLY *poly)
Definition: lwpoly.c:175
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
#define LW_INSIDE
Constants for point-in-polygon return values.
int ptarray_contains_point(const POINTARRAY *pa, const POINT2D *pt)
Return LW_INSIDE if the point is inside the POINTARRAY, LW_OUTSIDE if it is outside,...
Definition: ptarray.c:751
#define LW_OUTSIDE
int ptarray_isccw(const POINTARRAY *pa)
Definition: ptarray.c:1051
#define LWT_COL_EDGE_FACE_RIGHT
LWT_INT64 LWT_ELEMID
Identifier of topology element.
#define LWT_COL_FACE_ALL
#define LWT_COL_EDGE_FACE_LEFT
#define LWT_COL_NODE_CONTAINING_FACE
#define LWT_COL_EDGE_EDGE_ID
Edge fields.
#define LWT_COL_NODE_GEOM
#define LWT_COL_NODE_NODE_ID
Node fields.
#define LWT_COL_EDGE_GEOM
#define PGTOPO_BE_ERROR()
#define LWTFMT_ELEMID
Datum contains(PG_FUNCTION_ARGS)
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:101
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:106
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
static LWT_ELEMID * lwt_be_getRingEdges(LWT_TOPOLOGY *topo, LWT_ELEMID edge, uint64_t *numedges, uint64_t limit)
Definition: lwgeom_topo.c:373
static uint64_t lwt_be_updateFacesById(LWT_TOPOLOGY *topo, const LWT_ISO_FACE *faces, uint64_t numfaces)
Definition: lwgeom_topo.c:286
const char * lwt_be_lastErrorMessage(const LWT_BE_IFACE *be)
Definition: lwgeom_topo.c:114
void _lwt_release_faces(LWT_ISO_FACE *faces, int num_faces)
Definition: lwgeom_topo.c:450
static LWPOLY * _lwt_MakeRingShell(LWT_TOPOLOGY *topo, LWT_ELEMID *signed_edge_ids, uint64_t num_signed_edge_ids)
Definition: lwgeom_topo.c:1779
static int lwt_be_updateNodesById(LWT_TOPOLOGY *topo, const LWT_ISO_NODE *nodes, int numnodes, int upd_fields)
Definition: lwgeom_topo.c:302
static void _lwt_release_nodes(LWT_ISO_NODE *nodes, int num_nodes)
Definition: lwgeom_topo.c:470
static LWT_ISO_EDGE * lwt_be_getEdgeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box)
Definition: lwgeom_topo.c:233
void _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
Definition: lwgeom_topo.c:460
int lwt_be_updateEdgesById(LWT_TOPOLOGY *topo, const LWT_ISO_EDGE *edges, int numedges, int upd_fields)
Definition: lwgeom_topo.c:294
static LWT_ISO_FACE * lwt_be_getFaceById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
Definition: lwgeom_topo.c:221
static LWT_ISO_NODE * lwt_be_getNodeByFace(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields, const GBOX *box)
Definition: lwgeom_topo.c:239
int lwt_be_insertFaces(LWT_TOPOLOGY *topo, LWT_ISO_FACE *face, uint64_t numelems)
Definition: lwgeom_topo.c:191
static const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition: lwinline.h:97
POINTARRAY * points
Definition: liblwgeom.h:483
POINTARRAY * point
Definition: liblwgeom.h:471
POINTARRAY ** rings
Definition: liblwgeom.h:519
LWT_ELEMID face_right
LWT_ELEMID face_left
LWLINE * geom
LWT_ELEMID edge_id
LWT_ELEMID face_id
LWT_ELEMID node_id
LWT_ELEMID containing_face
LWPOINT * geom
const LWT_BE_IFACE * be_iface

References _lwt_MakeRingShell(), _lwt_release_edges(), _lwt_release_faces(), _lwt_release_nodes(), LWT_TOPOLOGY_T::be_iface, LWT_ISO_NODE::containing_face, contains(), LWT_ISO_EDGE::edge_id, LWT_ISO_FACE::face_id, LWT_ISO_EDGE::face_left, LWT_ISO_EDGE::face_right, gbox_contains_point2d(), LWT_ISO_NODE::geom, LWT_ISO_EDGE::geom, getPoint2d_cp(), getPoint2d_p(), LW_INSIDE, LW_OUTSIDE, LW_TRUE, lwalloc(), LWDEBUG, LWDEBUGF, lwerror(), lwfree(), lwgeom_get_bbox(), lwpoly_as_lwgeom(), lwpoly_free(), lwt_be_getEdgeByFace(), lwt_be_getFaceById(), lwt_be_getNodeByFace(), lwt_be_getRingEdges(), lwt_be_insertFaces(), lwt_be_lastErrorMessage(), lwt_be_updateEdgesById(), lwt_be_updateFacesById(), lwt_be_updateNodesById(), LWT_COL_EDGE_EDGE_ID, LWT_COL_EDGE_FACE_LEFT, LWT_COL_EDGE_FACE_RIGHT, LWT_COL_EDGE_GEOM, LWT_COL_FACE_ALL, LWT_COL_NODE_CONTAINING_FACE, LWT_COL_NODE_GEOM, LWT_COL_NODE_NODE_ID, LWTFMT_ELEMID, LWT_ISO_FACE::mbr, LWT_ISO_NODE::node_id, PGTOPO_BE_ERROR, LWPOINT::point, LWLINE::points, ptarray_contains_point(), ptarray_is_closed_2d(), ptarray_isccw(), and LWPOLY::rings.

Referenced by _lwt_AddEdge(), and _lwt_SnapEdgeToExistingNode().

Here is the call graph for this function:
Here is the caller graph for this function: