PostGIS  3.0.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 1854 of file lwgeom_topo.c.

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