PostGIS  3.4.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 1874 of file lwgeom_topo.c.

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