PostGIS  3.1.6dev-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 1887 of file lwgeom_topo.c.

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