PostGIS  3.3.9dev-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 1901 of file lwgeom_topo.c.

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

Referenced by _lwt_AddEdge().

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