PostGIS  2.5.7dev-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 1856 of file lwgeom_topo.c.

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