PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches

◆ _lwt_AddLine()

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

Definition at line 5531 of file lwgeom_topo.c.

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