PostGIS  3.0.6dev-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 5529 of file lwgeom_topo.c.

5531 {
5532  LWGEOM *geomsbuf[1];
5533  LWGEOM **geoms;
5534  uint32_t ngeoms;
5535  LWGEOM *noded, *tmp;
5536  LWCOLLECTION *col;
5537  LWT_ELEMID *ids;
5538  LWT_ISO_EDGE *edges;
5539  LWT_ISO_NODE *nodes;
5540  uint64_t num, numedges = 0, numnodes = 0;
5541  uint64_t i;
5542  GBOX qbox;
5543 
5544  *nedges = -1; /* error condition, by default */
5545 
5546  /* Get tolerance, if 0 was given */
5547  if ( ! tol ) tol = _LWT_MINTOLERANCE( topo, (LWGEOM*)line );
5548  LWDEBUGF(1, "Working tolerance:%.15g", tol);
5549  LWDEBUGF(1, "Input line has srid=%d", line->srid);
5550 
5551  /* Remove consecutive vertices below given tolerance upfront */
5552  if ( tol )
5553  {{
5555  tmp = lwline_as_lwgeom(clean); /* NOTE: might collapse to non-simple */
5556  LWDEBUGG(1, tmp, "Repeated-point removed");
5557  }} else tmp=(LWGEOM*)line;
5558 
5559  /* 1. Self-node */
5560  noded = lwgeom_node((LWGEOM*)tmp);
5561  if ( tmp != (LWGEOM*)line ) lwgeom_free(tmp);
5562  if ( ! noded ) return NULL; /* should have called lwerror already */
5563  LWDEBUGG(1, noded, "Noded");
5564 
5565  qbox = *lwgeom_get_bbox( lwline_as_lwgeom(line) );
5566  LWDEBUGF(1, "Line BOX is %.15g %.15g, %.15g %.15g", qbox.xmin, qbox.ymin,
5567  qbox.xmax, qbox.ymax);
5568  gbox_expand(&qbox, tol);
5569  LWDEBUGF(1, "BOX expanded by %g is %.15g %.15g, %.15g %.15g",
5570  tol, qbox.xmin, qbox.ymin, qbox.xmax, qbox.ymax);
5571 
5572  LWGEOM **nearby = 0;
5573  int nearbyindex = 0;
5574  int nearbycount = 0;
5575 
5576  /* 2.0. Find edges falling within tol distance */
5577  edges = lwt_be_getEdgeWithinBox2D( topo, &qbox, &numedges, LWT_COL_EDGE_ALL, 0 );
5578  if (numedges == UINT64_MAX)
5579  {
5580  lwgeom_free(noded);
5581  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5582  return NULL;
5583  }
5584  LWDEBUGF(1, "Line has %d points, its bbox intersects %d edges bboxes",
5585  line->points->npoints, numedges);
5586  if ( numedges )
5587  {{
5588  /* collect those whose distance from us is < tol */
5589  nearbycount += numedges;
5590  nearby = lwalloc(numedges * sizeof(LWGEOM *));
5591  for (i=0; i<numedges; ++i)
5592  {
5593  LW_ON_INTERRUPT(return NULL);
5594  LWT_ISO_EDGE *e = &(edges[i]);
5595  LWGEOM *g = lwline_as_lwgeom(e->geom);
5596  LWDEBUGF(2, "Computing distance from edge %d having %d points", i, e->geom->points->npoints);
5597  double dist = lwgeom_mindistance2d(g, noded);
5598  /* must be closer than tolerated, unless distance is zero */
5599  if ( dist && dist >= tol ) continue;
5600  nearby[nearbyindex++] = g;
5601  }
5602  LWDEBUGF(1, "Found %d edges closer than tolerance (%g)", nearbyindex, tol);
5603  }}
5604  int nearbyedgecount = nearbyindex;
5605 
5606  /* 2.1. Find nodes falling within tol distance *
5607  * TODO: check if we should be only considering _isolated_ nodes! */
5608  nodes = lwt_be_getNodeWithinBox2D( topo, &qbox, &numnodes, LWT_COL_NODE_ALL, 0 );
5609  if (numnodes == UINT64_MAX)
5610  {
5611  lwgeom_free(noded);
5612  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5613  return NULL;
5614  }
5615  LWDEBUGF(1, "Line bbox intersects %d nodes bboxes", numnodes);
5616  if ( numnodes )
5617  {{
5618  /* collect those whose distance from us is < tol */
5619  nearbycount = nearbyedgecount + numnodes;
5620  nearby = nearby ?
5621  lwrealloc(nearby, nearbycount * sizeof(LWGEOM *))
5622  :
5623  lwalloc(nearbycount * sizeof(LWGEOM *))
5624  ;
5625  int nn = 0;
5626  for (i=0; i<numnodes; ++i)
5627  {
5628  LWT_ISO_NODE *n = &(nodes[i]);
5629  LWGEOM *g = lwpoint_as_lwgeom(n->geom);
5630  double dist = lwgeom_mindistance2d(g, noded);
5631  /* must be closer than tolerated, unless distance is zero */
5632  if ( dist && dist >= tol )
5633  {
5634  LWDEBUGF(1, "Node %d is %g units away, we tolerate only %g", n->node_id, dist, tol);
5635  continue;
5636  }
5637  nearby[nearbyindex++] = g;
5638  ++nn;
5639  }
5640  LWDEBUGF(1, "Found %d nodes closer than tolerance (%g)", nn, tol);
5641  }}
5642  int nearbynodecount = nearbyindex - nearbyedgecount;
5643  nearbycount = nearbyindex;
5644 
5645  LWDEBUGF(1, "Number of nearby elements is %d", nearbycount);
5646 
5647  /* 2.2. Snap to nearby elements */
5648  if ( nearbycount )
5649  {{
5650  LWCOLLECTION *col;
5651  LWGEOM *elems;
5652 
5654  NULL, nearbycount, nearby);
5655  elems = lwcollection_as_lwgeom(col);
5656 
5657  LWDEBUGG(1, elems, "Collected nearby elements");
5658 
5659  tmp = _lwt_toposnap(noded, elems, tol);
5660  lwgeom_free(noded);
5661  noded = tmp;
5662  LWDEBUGG(1, noded, "Elements-snapped");
5663 
5664  /* will not release the geoms array */
5665  lwcollection_release(col);
5666 
5667  /*
5668  -- re-node to account for ST_Snap introduced self-intersections
5669  -- See http://trac.osgeo.org/postgis/ticket/1714
5670  -- TODO: consider running UnaryUnion once after all noding
5671  */
5672  tmp = lwgeom_unaryunion(noded);
5673  lwgeom_free(noded);
5674  noded = tmp;
5675  LWDEBUGG(1, noded, "Unary-unioned");
5676  }}
5677 
5678  /* 2.3. Node with nearby edges */
5679  if ( nearbyedgecount )
5680  {{
5681  LWCOLLECTION *col;
5682  LWGEOM *iedges; /* just an alias for col */
5683  LWGEOM *diff, *xset;
5684 
5685  LWDEBUGF(1, "Line intersects %d edges", nearbyedgecount);
5686 
5688  NULL, nearbyedgecount, nearby);
5689  iedges = lwcollection_as_lwgeom(col);
5690  LWDEBUGG(1, iedges, "Collected edges");
5691 
5692  LWDEBUGF(1, "Diffing noded, with srid=%d "
5693  "and interesecting edges, with srid=%d",
5694  noded->srid, iedges->srid);
5695  diff = lwgeom_difference(noded, iedges);
5696  LWDEBUGG(1, diff, "Differenced");
5697 
5698  LWDEBUGF(1, "Intersecting noded, with srid=%d "
5699  "and interesecting edges, with srid=%d",
5700  noded->srid, iedges->srid);
5701  xset = lwgeom_intersection(noded, iedges);
5702  LWDEBUGG(1, xset, "Intersected");
5703  lwgeom_free(noded);
5704 
5705  /* We linemerge here because INTERSECTION, as of GEOS 3.8,
5706  * will result in shared segments being output as multiple
5707  * lines rather than a single line. Example:
5708 
5709  INTERSECTION(
5710  'LINESTRING(0 0, 5 0, 8 0, 10 0,12 0)',
5711  'LINESTRING(5 0, 8 0, 10 0)'
5712  )
5713  ==
5714  MULTILINESTRING((5 0,8 0),(8 0,10 0))
5715 
5716  * We will re-split in a subsequent step, by splitting
5717  * the final line with pre-existing nodes
5718  */
5719  LWDEBUG(1, "Linemerging intersection");
5720  tmp = lwgeom_linemerge(xset);
5721  LWDEBUGG(1, tmp, "Linemerged");
5722  lwgeom_free(xset);
5723  xset = tmp;
5724 
5725  /*
5726  * Here we union the (linemerged) intersection with
5727  * the difference (new lines)
5728  */
5729  LWDEBUG(1, "Unioning difference and (linemerged) intersection");
5730  noded = lwgeom_union(diff, xset);
5731  LWDEBUGG(1, noded, "Diff-Xset Unioned");
5732  lwgeom_free(xset);
5733  lwgeom_free(diff);
5734 
5735  /* will not release the geoms array */
5736  lwcollection_release(col);
5737  }}
5738 
5739 
5740  /* 2.4. Split by pre-existing nodes
5741  *
5742  * Pre-existing nodes are isolated nodes AND endpoints
5743  * of intersecting edges
5744  */
5745  if ( nearbyedgecount )
5746  {
5747  nearbycount += nearbyedgecount * 2; /* make space for endpoints */
5748  nearby = lwrealloc(nearby, nearbycount * sizeof(LWGEOM *));
5749  for (int i=0; i<nearbyedgecount; i++)
5750  {
5751  LWLINE *edge = lwgeom_as_lwline(nearby[i]);
5752  LWPOINT *startNode = lwline_get_lwpoint(edge, 0);
5753  LWPOINT *endNode = lwline_get_lwpoint(edge, edge->points->npoints-1);
5754  /* TODO: only add if within distance to noded AND if not duplicated */
5755  nearby[nearbyindex++] = lwpoint_as_lwgeom(startNode);
5756  nearbynodecount++;
5757  nearby[nearbyindex++] = lwpoint_as_lwgeom(endNode);
5758  nearbynodecount++;
5759  }
5760  }
5761  if ( nearbynodecount )
5762  {
5764  NULL, nearbynodecount,
5765  nearby + nearbyedgecount);
5766  LWGEOM *inodes = lwcollection_as_lwgeom(col);
5767  /* TODO: use lwgeom_split of lwgeom_union ... */
5768  tmp = _lwt_split_by_nodes(noded, inodes);
5769  lwgeom_free(noded);
5770  noded = tmp;
5771  LWDEBUGG(1, noded, "Node-split");
5772  /* will not release the geoms array */
5773  lwcollection_release(col);
5774  }
5775 
5776 
5777  LWDEBUG(1, "Freeing up nearby elements");
5778 
5779  /* TODO: free up endpoints of nearbyedges */
5780  if ( nearby ) lwfree(nearby);
5781  if ( nodes ) _lwt_release_nodes(nodes, numnodes);
5782  if ( edges ) _lwt_release_edges(edges, numedges);
5783 
5784  LWDEBUGG(1, noded, "Finally-noded");
5785 
5786  /* 3. For each (now-noded) segment, insert an edge */
5787  col = lwgeom_as_lwcollection(noded);
5788  if ( col )
5789  {
5790  LWDEBUG(1, "Noded line was a collection");
5791  geoms = col->geoms;
5792  ngeoms = col->ngeoms;
5793  }
5794  else
5795  {
5796  LWDEBUG(1, "Noded line was a single geom");
5797  geomsbuf[0] = noded;
5798  geoms = geomsbuf;
5799  ngeoms = 1;
5800  }
5801 
5802  LWDEBUGF(1, "Line was split into %d edges", ngeoms);
5803 
5804  /* TODO: refactor to first add all nodes (re-snapping edges if
5805  * needed) and then check all edges for existing already
5806  * ( so to save a DB scan for each edge to be added )
5807  */
5808  ids = lwalloc(sizeof(LWT_ELEMID)*ngeoms);
5809  num = 0;
5810  for ( i=0; i<ngeoms; ++i )
5811  {
5812  LWT_ELEMID id;
5813  LWGEOM *g = geoms[i];
5814  g->srid = noded->srid;
5815 
5816 #if POSTGIS_DEBUG_LEVEL > 0
5817  {
5818  size_t sz;
5819  char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
5820  LWDEBUGF(1, "Component %d of split line is: %s", i, wkt1);
5821  lwfree(wkt1);
5822  }
5823 #endif
5824 
5825  id = _lwt_AddLineEdge( topo, lwgeom_as_lwline(g), tol, handleFaceSplit );
5826  LWDEBUGF(1, "_lwt_AddLineEdge returned %" LWTFMT_ELEMID, id);
5827  if ( id < 0 )
5828  {
5829  lwgeom_free(noded);
5830  lwfree(ids);
5831  return NULL;
5832  }
5833  if ( ! id )
5834  {
5835  LWDEBUGF(1, "Component %d of split line collapsed", i);
5836  continue;
5837  }
5838 
5839  LWDEBUGF(1, "Component %d of split line is edge %" LWTFMT_ELEMID,
5840  i, id);
5841  ids[num++] = id; /* TODO: skip duplicates */
5842  }
5843 
5844  LWDEBUGG(1, noded, "Noded before free");
5845  lwgeom_free(noded);
5846 
5847  /* TODO: XXX remove duplicated ids if not done before */
5848 
5849  *nedges = num;
5850  return ids;
5851 }
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:161
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:321
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:291
#define COLLECTIONTYPE
Definition: liblwgeom.h:122
LWGEOM * lwgeom_node(const LWGEOM *lwgeom_in)
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1138
LWGEOM * lwgeom_intersection(const LWGEOM *geom1, const LWGEOM *geom2)
#define WKT_EXTENDED
Definition: liblwgeom.h:2132
#define MULTIPOINTTYPE
Definition: liblwgeom.h:119
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:326
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:197
char * lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out)
WKT emitter function.
Definition: lwout_wkt.c:676
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
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:215
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
#define LW_ON_INTERRUPT(x)
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:5507
static void _lwt_release_nodes(LWT_ISO_NODE *nodes, int num_nodes)
Definition: lwgeom_topo.c:461
static LWT_ELEMID _lwt_AddLineEdge(LWT_TOPOLOGY *topo, LWLINE *edge, double tol, int handleFaceSplit)
Definition: lwgeom_topo.c:5315
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:4888
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 LWGEOM * _lwt_toposnap(LWGEOM *src, LWGEOM *tgt, double tol)
Definition: lwgeom_topo.c:414
#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
double ymax
Definition: liblwgeom.h:343
double xmax
Definition: liblwgeom.h:341
double ymin
Definition: liblwgeom.h:342
double xmin
Definition: liblwgeom.h:340
uint32_t ngeoms
Definition: liblwgeom.h:566
LWGEOM ** geoms
Definition: liblwgeom.h:561
int32_t srid
Definition: liblwgeom.h:446
POINTARRAY * points
Definition: liblwgeom.h:469
int32_t srid
Definition: liblwgeom.h:470
LWLINE * geom
LWT_ELEMID node_id
LWPOINT * geom
const LWT_BE_IFACE * be_iface
uint32_t npoints
Definition: liblwgeom.h:413

References _lwt_AddLineEdge(), _LWT_MINTOLERANCE, _lwt_release_edges(), _lwt_release_nodes(), _lwt_split_by_nodes(), _lwt_toposnap(), LWT_TOPOLOGY_T::be_iface, COLLECTIONTYPE, gbox_expand(), LWT_ISO_NODE::geom, LWT_ISO_EDGE::geom, LWCOLLECTION::geoms, 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_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, LWGEOM::srid, LWLINE::srid, LWT_TOPOLOGY_T::srid, WKT_EXTENDED, GBOX::xmax, GBOX::xmin, 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: