PostGIS  3.3.9dev-r@@SVN_REVISION@@

◆ _lwt_AddLine()

static LWT_ELEMID* _lwt_AddLine ( LWT_TOPOLOGY topo,
LWLINE line,
double  tol,
int *  nedges,
int  handleFaceSplit 
)
static

Definition at line 5750 of file lwgeom_topo.c.

5752 {
5753  LWGEOM *geomsbuf[1];
5754  LWGEOM **geoms;
5755  uint32_t ngeoms;
5756  LWGEOM *noded, *tmp;
5757  LWCOLLECTION *col;
5758  LWT_ELEMID *ids;
5759  LWT_ISO_EDGE *edges;
5760  LWT_ISO_NODE *nodes;
5761  uint64_t num, numedges = 0, numnodes = 0;
5762  uint64_t i;
5763  GBOX qbox;
5764  int forward;
5765  int input_was_closed = 0;
5766  POINT4D originalStartPoint;
5767 
5768  if ( lwline_is_empty(line) )
5769  {
5770  *nedges = 0;
5771  return NULL;
5772  }
5773 
5774  if ( lwline_is_closed(line) )
5775  {
5776  input_was_closed = 1;
5777  getPoint4d_p( line->points, 0, &originalStartPoint);
5778  LWDEBUGF(1, "Input line is closed, original point is %g,%g", originalStartPoint.x, originalStartPoint.y);
5779  }
5780 
5781  *nedges = -1; /* error condition, by default */
5782 
5783  /* Get tolerance, if 0 was given */
5784  if ( ! tol ) tol = _LWT_MINTOLERANCE( topo, (LWGEOM*)line );
5785  LWDEBUGF(1, "Working tolerance:%.15g", tol);
5786  LWDEBUGF(1, "Input line has srid=%d", line->srid);
5787 
5788  /* Remove consecutive vertices below given tolerance upfront */
5789  if ( tol )
5790  {{
5792  tmp = lwline_as_lwgeom(clean); /* NOTE: might collapse to non-simple */
5793  LWDEBUGG(1, tmp, "Repeated-point removed");
5794  }} else tmp=(LWGEOM*)line;
5795 
5796  /* 1. Self-node */
5797  noded = lwgeom_node((LWGEOM*)tmp);
5798  if ( tmp != (LWGEOM*)line ) lwgeom_free(tmp);
5799  if ( ! noded ) return NULL; /* should have called lwerror already */
5800  LWDEBUGG(1, noded, "Noded");
5801 
5802  qbox = *lwgeom_get_bbox( lwline_as_lwgeom(line) );
5803  LWDEBUGF(1, "Line BOX is %.15g %.15g, %.15g %.15g", qbox.xmin, qbox.ymin,
5804  qbox.xmax, qbox.ymax);
5805  gbox_expand(&qbox, tol);
5806  LWDEBUGF(1, "BOX expanded by %g is %.15g %.15g, %.15g %.15g",
5807  tol, qbox.xmin, qbox.ymin, qbox.xmax, qbox.ymax);
5808 
5809  LWGEOM **nearby = 0;
5810  int nearbyindex = 0;
5811  int nearbycount = 0;
5812 
5813  /* 2.0. Find edges falling within tol distance */
5814  edges = lwt_be_getEdgeWithinBox2D( topo, &qbox, &numedges, LWT_COL_EDGE_ALL, 0 );
5815  if (numedges == UINT64_MAX)
5816  {
5817  lwgeom_free(noded);
5818  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5819  return NULL;
5820  }
5821  LWDEBUGF(1, "Line has %d points, its bbox intersects %d edges bboxes",
5822  line->points->npoints, numedges);
5823  if ( numedges )
5824  {{
5825  /* collect those whose distance from us is < tol */
5826  nearbycount += numedges;
5827  nearby = lwalloc(numedges * sizeof(LWGEOM *));
5828  for (i=0; i<numedges; ++i)
5829  {
5830  LW_ON_INTERRUPT(return NULL);
5831  LWT_ISO_EDGE *e = &(edges[i]);
5832  LWGEOM *g = lwline_as_lwgeom(e->geom);
5833  LWDEBUGF(2, "Computing distance from edge %d having %d points", i, e->geom->points->npoints);
5834  double dist = lwgeom_mindistance2d(g, noded);
5835  /* must be closer than tolerated, unless distance is zero */
5836  if ( dist && dist >= tol ) continue;
5837  nearby[nearbyindex++] = g;
5838  }
5839  LWDEBUGF(1, "Found %d edges closer than tolerance (%g)", nearbyindex, tol);
5840  }}
5841  int nearbyedgecount = nearbyindex;
5842 
5843  /* 2.1. Find isolated nodes falling within tol distance
5844  *
5845  * TODO: add backend-interface support for only getting isolated nodes
5846  */
5847  nodes = lwt_be_getNodeWithinBox2D( topo, &qbox, &numnodes, LWT_COL_NODE_ALL, 0 );
5848  if (numnodes == UINT64_MAX)
5849  {
5850  lwgeom_free(noded);
5851  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5852  return NULL;
5853  }
5854  LWDEBUGF(1, "Line bbox intersects %d nodes bboxes", numnodes);
5855  if ( numnodes )
5856  {{
5857  /* collect those whose distance from us is < tol */
5858  nearbycount = nearbyedgecount + numnodes;
5859  nearby = nearby ?
5860  lwrealloc(nearby, nearbycount * sizeof(LWGEOM *))
5861  :
5862  lwalloc(nearbycount * sizeof(LWGEOM *))
5863  ;
5864  int nn = 0;
5865  for (i=0; i<numnodes; ++i)
5866  {
5867  LWT_ISO_NODE *n = &(nodes[i]);
5868  if ( n->containing_face == -1 ) continue; /* skip not-isolated nodes */
5869  LWGEOM *g = lwpoint_as_lwgeom(n->geom);
5870  double dist = lwgeom_mindistance2d(g, noded);
5871  /* must be closer than tolerated, unless distance is zero */
5872  if ( dist && dist >= tol )
5873  {
5874  LWDEBUGF(1, "Node %d is %g units away, we tolerate only %g", n->node_id, dist, tol);
5875  continue;
5876  }
5877  nearby[nearbyindex++] = g;
5878  nn = nn + 1;
5879  }
5880  LWDEBUGF(1, "Found %d isolated nodes closer than tolerance (%g)", nn, tol);
5881  }}
5882  int nearbynodecount = nearbyindex - nearbyedgecount;
5883  nearbycount = nearbyindex;
5884 
5885  LWDEBUGF(1, "Number of nearby elements is %d", nearbycount);
5886 
5887  /* 2.2. Snap to nearby elements */
5888  if ( nearbycount )
5889  {{
5890  LWCOLLECTION *col;
5891  LWGEOM *elems;
5892 
5894  NULL, nearbycount, nearby);
5895  elems = lwcollection_as_lwgeom(col);
5896 
5897  LWDEBUGG(1, elems, "Collected nearby elements");
5898 
5899  tmp = _lwt_toposnap(noded, elems, tol);
5900  lwgeom_free(noded);
5901  noded = tmp;
5902  LWDEBUGG(1, noded, "Elements-snapped");
5903  if ( input_was_closed )
5904  {{
5905  /* Recompute start point in case it moved */
5906  LWLINE *scrolled = lwgeom_as_lwline(noded);
5907  if (scrolled)
5908  {
5909  getPoint4d_p( scrolled->points, 0, &originalStartPoint);
5910  LWDEBUGF(1, "Closed input line start point after snap %g,%g", originalStartPoint.x, originalStartPoint.y);
5911  }
5912  }}
5913 
5914  /* will not release the geoms array */
5915  lwcollection_release(col);
5916 
5917  /*
5918  -- re-node to account for ST_Snap introduced self-intersections
5919  -- See http://trac.osgeo.org/postgis/ticket/1714
5920  -- TODO: consider running UnaryUnion once after all noding
5921  */
5922  tmp = lwgeom_unaryunion(noded);
5923  lwgeom_free(noded);
5924  noded = tmp;
5925  LWDEBUGG(1, noded, "Unary-unioned");
5926  }}
5927 
5928  /* 2.3. Node with nearby edges */
5929  if ( nearbyedgecount )
5930  {{
5931  LWCOLLECTION *col;
5932  LWGEOM *iedges; /* just an alias for col */
5933  LWGEOM *diff, *xset;
5934 
5935  LWDEBUGF(1, "Line intersects %d edges", nearbyedgecount);
5936 
5938  NULL, nearbyedgecount, nearby);
5939  iedges = lwcollection_as_lwgeom(col);
5940  LWDEBUGG(1, iedges, "Collected edges");
5941 
5942  LWDEBUGF(1, "Diffing noded, with srid=%d "
5943  "and interesecting edges, with srid=%d",
5944  noded->srid, iedges->srid);
5945  diff = lwgeom_difference(noded, iedges);
5946  LWDEBUGG(1, diff, "Differenced");
5947 
5948  LWDEBUGF(1, "Intersecting noded, with srid=%d "
5949  "and interesecting edges, with srid=%d",
5950  noded->srid, iedges->srid);
5951  xset = lwgeom_intersection(noded, iedges);
5952  LWDEBUGG(1, xset, "Intersected");
5953  lwgeom_free(noded);
5954 
5955  /* We linemerge here because INTERSECTION, as of GEOS 3.8,
5956  * will result in shared segments being output as multiple
5957  * lines rather than a single line. Example:
5958 
5959  INTERSECTION(
5960  'LINESTRING(0 0, 5 0, 8 0, 10 0,12 0)',
5961  'LINESTRING(5 0, 8 0, 10 0)'
5962  )
5963  ==
5964  MULTILINESTRING((5 0,8 0),(8 0,10 0))
5965 
5966  * We will re-split in a subsequent step, by splitting
5967  * the final line with pre-existing nodes
5968  */
5969  LWDEBUG(1, "Linemerging intersection");
5970  tmp = lwgeom_linemerge(xset);
5971  LWDEBUGG(1, tmp, "Linemerged");
5972  lwgeom_free(xset);
5973  xset = tmp;
5974 
5975  /*
5976  * Here we union the (linemerged) intersection with
5977  * the difference (new lines)
5978  */
5979  LWDEBUG(1, "Unioning difference and (linemerged) intersection");
5980  noded = lwgeom_union(diff, xset);
5981  LWDEBUGG(1, noded, "Diff-Xset Unioned");
5982  lwgeom_free(xset);
5983  lwgeom_free(diff);
5984 
5985  /* will not release the geoms array */
5986  lwcollection_release(col);
5987 
5988  if ( input_was_closed )
5989  {{
5990  LWLINE *scrolled = lwgeom_as_lwline(noded);
5991  if (scrolled) {
5992  if ( lwline_is_closed(scrolled) ) {
5993  ptarray_scroll_in_place(scrolled->points, &originalStartPoint);
5994  }
5995  else {
5996  LWDEBUGG(1, lwline_as_lwgeom(scrolled), "Closed input became non closed");
5997  }
5998  }
5999  else {
6000  LWDEBUGG(1, noded, "Diff-Xset Unioned cannot be scrolled");
6001  }
6002  }}
6003 
6004 
6005  }}
6006 
6007 
6008  /* 2.4. Split by pre-existing nodes
6009  *
6010  * Pre-existing nodes are isolated nodes AND endpoints
6011  * of intersecting edges
6012  */
6013  if ( nearbyedgecount )
6014  {
6015  nearbycount += nearbyedgecount * 2; /* make space for endpoints */
6016  nearby = lwrealloc(nearby, nearbycount * sizeof(LWGEOM *));
6017  for (int i=0; i<nearbyedgecount; i++)
6018  {
6019  LWLINE *edge = lwgeom_as_lwline(nearby[i]);
6020  LWPOINT *startNode = lwline_get_lwpoint(edge, 0);
6021  LWPOINT *endNode = lwline_get_lwpoint(edge, edge->points->npoints-1);
6022  /* TODO: only add if within distance to noded AND if not duplicated */
6023  nearby[nearbyindex++] = lwpoint_as_lwgeom(startNode);
6024  nearbynodecount++;
6025  nearby[nearbyindex++] = lwpoint_as_lwgeom(endNode);
6026  nearbynodecount++;
6027  }
6028  }
6029  if ( nearbynodecount )
6030  {
6032  NULL, nearbynodecount,
6033  nearby + nearbyedgecount);
6034  LWGEOM *inodes = lwcollection_as_lwgeom(col);
6035  /* TODO: use lwgeom_split of lwgeom_union ... */
6036  tmp = _lwt_split_by_nodes(noded, inodes);
6037  lwgeom_free(noded);
6038  noded = tmp;
6039  LWDEBUGG(1, noded, "Node-split");
6040  /* will not release the geoms array */
6041  lwcollection_release(col);
6042  }
6043 
6044 
6045  LWDEBUG(1, "Freeing up nearby elements");
6046 
6047  /* TODO: free up endpoints of nearbyedges */
6048  if ( nearby ) lwfree(nearby);
6049  if ( nodes ) _lwt_release_nodes(nodes, numnodes);
6050  if ( edges ) _lwt_release_edges(edges, numedges);
6051 
6052  LWDEBUGG(1, noded, "Finally-noded");
6053 
6054  /* 3. For each (now-noded) segment, insert an edge */
6055  col = lwgeom_as_lwcollection(noded);
6056  if ( col )
6057  {
6058  LWDEBUG(1, "Noded line was a collection");
6059  geoms = col->geoms;
6060  ngeoms = col->ngeoms;
6061  }
6062  else
6063  {
6064  LWDEBUG(1, "Noded line was a single geom");
6065  geomsbuf[0] = noded;
6066  geoms = geomsbuf;
6067  ngeoms = 1;
6068  }
6069 
6070  LWDEBUGF(1, "Line was split into %d edges", ngeoms);
6071 
6072  /* TODO: refactor to first add all nodes (re-snapping edges if
6073  * needed) and then check all edges for existing already
6074  * ( so to save a DB scan for each edge to be added )
6075  */
6076  ids = lwalloc(sizeof(LWT_ELEMID)*ngeoms);
6077  num = 0;
6078  for ( i=0; i<ngeoms; ++i )
6079  {
6080  LWT_ELEMID id;
6081  LWGEOM *g = geoms[i];
6082  g->srid = noded->srid;
6083 
6084 #if POSTGIS_DEBUG_LEVEL > 0
6085  {
6086  size_t sz;
6087  char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
6088  LWDEBUGF(1, "Component %d of split line is: %s", i, wkt1);
6089  lwfree(wkt1);
6090  }
6091 #endif
6092 
6093  id = _lwt_AddLineEdge( topo, lwgeom_as_lwline(g), tol, handleFaceSplit, &forward );
6094  LWDEBUGF(1, "_lwt_AddLineEdge returned %" LWTFMT_ELEMID, id);
6095  if ( id < 0 )
6096  {
6097  lwgeom_free(noded);
6098  lwfree(ids);
6099  return NULL;
6100  }
6101  if ( ! id )
6102  {
6103  LWDEBUGF(1, "Component %d of split line collapsed", i);
6104  continue;
6105  }
6106 
6107  LWDEBUGF(1, "Component %d of split line is %s edge %" LWTFMT_ELEMID,
6108  i, forward ? "forward" : "backward", id);
6109  ids[num++] = forward ? id : -id; /* TODO: skip duplicates */
6110  }
6111 
6112  LWDEBUGG(1, noded, "Noded before free");
6113  lwgeom_free(noded);
6114 
6115  /* TODO: XXX remove duplicated ids if not done before */
6116 
6117  *nedges = num;
6118  return ids;
6119 }
void gbox_expand(GBOX *g, double d)
Move the box minimums down and the maximums up by the distance provided.
Definition: gbox.c:97
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:179
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:339
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:309
#define COLLECTIONTYPE
Definition: liblwgeom.h:123
LWGEOM * lwgeom_node(const LWGEOM *lwgeom_in)
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1155
LWGEOM * lwgeom_intersection(const LWGEOM *geom1, const LWGEOM *geom2)
#define WKT_EXTENDED
Definition: liblwgeom.h:2168
#define MULTIPOINTTYPE
Definition: liblwgeom.h:120
LWGEOM * lwgeom_difference(const LWGEOM *geom1, const LWGEOM *geom2)
void * lwrealloc(void *mem, size_t size)
Definition: lwutil.c:235
void lwfree(void *mem)
Definition: lwutil.c:242
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:344
LWGEOM * lwgeom_unaryunion(const LWGEOM *geom1)
void lwcollection_release(LWCOLLECTION *lwcollection)
Definition: lwcollection.c:36
double lwgeom_mindistance2d(const LWGEOM *lw1, const LWGEOM *lw2)
Function initializing min distance calculation.
Definition: measures.c:200
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition: lwgeom_api.c:126
char * lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out)
WKT emitter function.
Definition: lwout_wkt.c:708
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
LWGEOM * lwgeom_linemerge(const LWGEOM *geom1)
void * lwalloc(size_t size)
Definition: lwutil.c:227
LWCOLLECTION * lwcollection_construct(uint8_t type, int32_t srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
Definition: lwcollection.c:42
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
Definition: lwgeom.c:233
LWGEOM * lwgeom_union(const LWGEOM *geom1, const LWGEOM *geom2)
LWPOINT * lwline_get_lwpoint(const LWLINE *line, uint32_t where)
Returns freshly allocated LWPOINT that corresponds to the index where.
Definition: lwline.c:309
LWGEOM * lwline_remove_repeated_points(const LWLINE *in, double tolerance)
Definition: lwline.c:439
int lwline_is_empty(const LWLINE *line)
#define LW_ON_INTERRUPT(x)
int lwline_is_closed(const LWLINE *line)
Definition: lwline.c:445
int ptarray_scroll_in_place(POINTARRAY *pa, const POINT4D *newbase)
Definition: ptarray.c:2163
LWT_INT64 LWT_ELEMID
Identifier of topology element.
#define LWT_COL_EDGE_ALL
#define LWT_COL_NODE_ALL
#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
#define LWDEBUGG(level, geom, msg)
Definition: lwgeom_log.h:93
const char * lwt_be_lastErrorMessage(const LWT_BE_IFACE *be)
Definition: lwgeom_topo.c:119
static LWGEOM * _lwt_split_by_nodes(const LWGEOM *g, const LWGEOM *nodes)
Definition: lwgeom_topo.c:5728
static void _lwt_release_nodes(LWT_ISO_NODE *nodes, int num_nodes)
Definition: lwgeom_topo.c:475
static LWT_ISO_NODE * lwt_be_getNodeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
Definition: lwgeom_topo.c:172
#define _LWT_MINTOLERANCE(topo, geom)
Definition: lwgeom_topo.c:5064
static LWT_ISO_EDGE * lwt_be_getEdgeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, uint64_t *numelems, int fields, uint64_t limit)
Definition: lwgeom_topo.c:178
static LWT_ELEMID _lwt_AddLineEdge(LWT_TOPOLOGY *topo, LWLINE *edge, double tol, int handleFaceSplit, int *forward)
Definition: lwgeom_topo.c:5535
static LWGEOM * _lwt_toposnap(LWGEOM *src, LWGEOM *tgt, double tol)
Definition: lwgeom_topo.c:428
#define LWTFMT_ELEMID
Definition: lwgeom_topo.c:43
static void _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
Definition: lwgeom_topo.c:465
double ymax
Definition: liblwgeom.h:372
double xmax
Definition: liblwgeom.h:370
double ymin
Definition: liblwgeom.h:371
double xmin
Definition: liblwgeom.h:369
uint32_t ngeoms
Definition: liblwgeom.h:595
LWGEOM ** geoms
Definition: liblwgeom.h:590
int32_t srid
Definition: liblwgeom.h:475
POINTARRAY * points
Definition: liblwgeom.h:498
int32_t srid
Definition: liblwgeom.h:499
LWLINE * geom
LWT_ELEMID node_id
LWT_ELEMID containing_face
LWPOINT * geom
const LWT_BE_IFACE * be_iface
double x
Definition: liblwgeom.h:429
double y
Definition: liblwgeom.h:429
uint32_t npoints
Definition: liblwgeom.h:442

References _lwt_AddLineEdge(), _LWT_MINTOLERANCE, _lwt_release_edges(), _lwt_release_nodes(), _lwt_split_by_nodes(), _lwt_toposnap(), LWT_TOPOLOGY_T::be_iface, COLLECTIONTYPE, LWT_ISO_NODE::containing_face, gbox_expand(), LWT_ISO_NODE::geom, LWT_ISO_EDGE::geom, LWCOLLECTION::geoms, getPoint4d_p(), LW_ON_INTERRUPT, lwalloc(), lwcollection_as_lwgeom(), lwcollection_construct(), lwcollection_release(), LWDEBUG, LWDEBUGF, LWDEBUGG, lwerror(), lwfree(), lwgeom_as_lwcollection(), lwgeom_as_lwline(), lwgeom_difference(), lwgeom_free(), lwgeom_get_bbox(), lwgeom_intersection(), lwgeom_linemerge(), lwgeom_mindistance2d(), lwgeom_node(), lwgeom_to_wkt(), lwgeom_unaryunion(), lwgeom_union(), lwline_as_lwgeom(), lwline_get_lwpoint(), lwline_is_closed(), lwline_is_empty(), lwline_remove_repeated_points(), lwpoint_as_lwgeom(), lwrealloc(), lwt_be_getEdgeWithinBox2D(), lwt_be_getNodeWithinBox2D(), lwt_be_lastErrorMessage(), LWT_COL_EDGE_ALL, LWT_COL_NODE_ALL, LWTFMT_ELEMID, MULTIPOINTTYPE, LWCOLLECTION::ngeoms, LWT_ISO_NODE::node_id, POINTARRAY::npoints, LWLINE::points, ptarray_scroll_in_place(), LWGEOM::srid, LWLINE::srid, LWT_TOPOLOGY_T::srid, WKT_EXTENDED, POINT4D::x, GBOX::xmax, GBOX::xmin, POINT4D::y, GBOX::ymax, and GBOX::ymin.

Referenced by lwt_AddLine(), and lwt_AddLineNoFace().

Here is the call graph for this function:
Here is the caller graph for this function: