PostGIS  3.2.2dev-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 1895 of file lwgeom_topo.c.

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