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

◆ _lwt_AddPoint()

static LWT_ELEMID _lwt_AddPoint ( LWT_TOPOLOGY topo,
LWPOINT point,
double  tol,
int  findFace,
int *  moved 
)
static

Definition at line 4919 of file lwgeom_topo.c.

4921{
4922 uint64_t num, i;
4923 double mindist = FLT_MAX;
4924 LWT_ISO_NODE *nodes, *nodes2;
4925 LWT_ISO_EDGE *edges, *edges2;
4926 LWGEOM *pt = lwpoint_as_lwgeom(point);
4927 int flds;
4928 LWT_ELEMID id = 0;
4929 scored_pointer *sorted;
4930
4931 /* Get tolerance, if 0 was given */
4932 if (!tol)
4933 tol = _LWT_MINTOLERANCE(topo, pt);
4934
4935 LWDEBUGG(1, pt, "Adding point");
4936
4937 /*
4938 -- 1. Check if any existing node is closer than the given precision
4939 -- and if so pick the closest
4940 TODO: use WithinBox2D
4941 */
4943 nodes = lwt_be_getNodeWithinDistance2D(topo, point, tol, &num, flds, 0);
4944 if (num == UINT64_MAX)
4945 {
4946 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
4947 return -1;
4948 }
4949 if ( num )
4950 {
4951 LWDEBUGF(1, "New point is within %.15g units of %d nodes", tol, num);
4952 /* Order by distance if there are more than a single return */
4953 if ( num > 1 )
4954 {{
4955 sorted= lwalloc(sizeof(scored_pointer)*num);
4956 for (i=0; i<num; ++i)
4957 {
4958 sorted[i].ptr = nodes+i;
4959 sorted[i].score = lwgeom_mindistance2d(lwpoint_as_lwgeom(nodes[i].geom), pt);
4960 LWDEBUGF(1, "Node %" LWTFMT_ELEMID " distance: %.15g",
4961 ((LWT_ISO_NODE*)(sorted[i].ptr))->node_id, sorted[i].score);
4962 }
4963 qsort(sorted, num, sizeof(scored_pointer), compare_scored_pointer);
4964 nodes2 = lwalloc(sizeof(LWT_ISO_NODE)*num);
4965 for (i=0; i<num; ++i)
4966 {
4967 nodes2[i] = *((LWT_ISO_NODE*)sorted[i].ptr);
4968 }
4969 lwfree(sorted);
4970 lwfree(nodes);
4971 nodes = nodes2;
4972 }}
4973
4974 for ( i=0; i<num; ++i )
4975 {
4976 LWT_ISO_NODE *n = &(nodes[i]);
4977 LWGEOM *g = lwpoint_as_lwgeom(n->geom);
4978 double dist = lwgeom_mindistance2d(g, pt);
4979 /* TODO: move this check in the previous sort scan ... */
4980 /* must be closer than tolerated, unless distance is zero */
4981 if ( dist && dist >= tol ) continue;
4982 if ( ! id || dist < mindist )
4983 {
4984 id = n->node_id;
4985 mindist = dist;
4986 }
4987 }
4988 if ( id )
4989 {
4990 /* found an existing node */
4991 if ( nodes ) _lwt_release_nodes(nodes, num);
4992 if ( moved ) *moved = mindist == 0 ? 0 : 1;
4993 return id;
4994 }
4995 }
4996
4997 initGEOS(lwnotice, lwgeom_geos_error);
4998
4999 /*
5000 -- 2. Check if any existing edge falls within tolerance
5001 -- and if so split it by a point projected on it
5002 TODO: use WithinBox2D
5003 */
5005 edges = lwt_be_getEdgeWithinDistance2D(topo, point, tol, &num, flds, 0);
5006 if (num == UINT64_MAX)
5007 {
5008 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
5009 return -1;
5010 }
5011 if ( num )
5012 {
5013 LWDEBUGF(1, "New point is within %.15g units of %d edges", tol, num);
5014
5015 /* Order by distance if there are more than a single return */
5016 if ( num > 1 )
5017 {{
5018 int j;
5019 sorted = lwalloc(sizeof(scored_pointer)*num);
5020 for (i=0; i<num; ++i)
5021 {
5022 sorted[i].ptr = edges+i;
5023 sorted[i].score = lwgeom_mindistance2d(lwline_as_lwgeom(edges[i].geom), pt);
5024 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID " distance: %.15g",
5025 ((LWT_ISO_EDGE*)(sorted[i].ptr))->edge_id, sorted[i].score);
5026 }
5027 qsort(sorted, num, sizeof(scored_pointer), compare_scored_pointer);
5028 edges2 = lwalloc(sizeof(LWT_ISO_EDGE)*num);
5029 for (j=0, i=0; i<num; ++i)
5030 {
5031 if ( sorted[i].score == sorted[0].score )
5032 {
5033 edges2[j++] = *((LWT_ISO_EDGE*)sorted[i].ptr);
5034 }
5035 else
5036 {
5037 lwline_free(((LWT_ISO_EDGE*)sorted[i].ptr)->geom);
5038 }
5039 }
5040 num = j;
5041 lwfree(sorted);
5042 lwfree(edges);
5043 edges = edges2;
5044 }}
5045
5046 for (i=0; i<num; ++i)
5047 {
5048 /* The point is on or near an edge, split the edge */
5049 LWT_ISO_EDGE *e = &(edges[i]);
5050 LWGEOM *g = lwline_as_lwgeom(e->geom);
5051 LWGEOM *prj;
5052 int contains;
5053 LWT_ELEMID edge_id = e->edge_id;
5054
5055 LWDEBUGF(1, "Splitting edge %" LWTFMT_ELEMID, edge_id);
5056
5057 /* project point to line, split edge by point */
5058 prj = lwgeom_closest_point(g, pt);
5059 if ( moved ) *moved = lwgeom_same(prj,pt) ? 0 : 1;
5060 if ( lwgeom_has_z(pt) )
5061 {{
5062 /*
5063 -- This is a workaround for ClosestPoint lack of Z support:
5064 -- http://trac.osgeo.org/postgis/ticket/2033
5065 */
5066 LWGEOM *tmp;
5067 double z;
5068 POINT4D p4d;
5069 LWPOINT *prjpt;
5070 /* add Z to "prj" */
5071 tmp = lwgeom_force_3dz(prj);
5072 prjpt = lwgeom_as_lwpoint(tmp);
5073 getPoint4d_p(point->point, 0, &p4d);
5074 z = p4d.z;
5075 getPoint4d_p(prjpt->point, 0, &p4d);
5076 p4d.z = z;
5077 ptarray_set_point4d(prjpt->point, 0, &p4d);
5078 lwgeom_free(prj);
5079 prj = tmp;
5080 }}
5081 const POINT2D *pt = getPoint2d_cp(lwgeom_as_lwpoint(prj)->point, 0);
5083 if ( ! contains )
5084 {{
5085 double snaptol;
5086 LWGEOM *snapedge;
5087 LWLINE *snapline;
5088 POINT4D p1, p2;
5089
5090 LWDEBUGF(1, "Edge %" LWTFMT_ELEMID
5091 " does not contain projected point to it",
5092 edge_id);
5093
5094 /* In order to reduce the robustness issues, we'll pick
5095 * an edge that contains the projected point, if possible */
5096 if ( i+1 < num )
5097 {
5098 LWDEBUG(1, "But there's another to check");
5099 lwgeom_free(prj);
5100 continue;
5101 }
5102
5103 /*
5104 -- The tolerance must be big enough for snapping to happen
5105 -- and small enough to snap only to the projected point.
5106 -- Unfortunately ST_Distance returns 0 because it also uses
5107 -- a projected point internally, so we need another way.
5108 */
5109 snaptol = _lwt_minTolerance(prj);
5110 snapedge = _lwt_toposnap(g, prj, snaptol);
5111 snapline = lwgeom_as_lwline(snapedge);
5112
5113 LWDEBUGF(1, "Edge snapped with tolerance %g", snaptol);
5114
5115 /* TODO: check if snapping did anything ? */
5116#if POSTGIS_DEBUG_LEVEL > 0
5117 {
5118 size_t sz;
5119 char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
5120 char *wkt2 = lwgeom_to_wkt(snapedge, WKT_EXTENDED, 15, &sz);
5121 LWDEBUGF(1, "Edge %s snapped became %s", wkt1, wkt2);
5122 lwfree(wkt1);
5123 lwfree(wkt2);
5124 }
5125#endif
5126
5127
5128 /*
5129 -- Snapping currently snaps the first point below tolerance
5130 -- so may possibly move first point. See ticket #1631
5131 */
5132 getPoint4d_p(e->geom->points, 0, &p1);
5133 getPoint4d_p(snapline->points, 0, &p2);
5134 LWDEBUGF(1, "Edge first point is %g %g, "
5135 "snapline first point is %g %g",
5136 p1.x, p1.y, p2.x, p2.y);
5137 if ( p1.x != p2.x || p1.y != p2.y )
5138 {
5139 LWDEBUG(1, "Snapping moved first point, re-adding it");
5140 if ( LW_SUCCESS != ptarray_insert_point(snapline->points, &p1, 0) )
5141 {
5142 lwgeom_free(prj);
5143 lwgeom_free(snapedge);
5144 _lwt_release_edges(edges, num);
5145 lwerror("GEOS exception on Contains: %s", lwgeom_geos_errmsg);
5146 return -1;
5147 }
5148#if POSTGIS_DEBUG_LEVEL > 0
5149 {
5150 size_t sz;
5151 char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
5152 LWDEBUGF(1, "Tweaked snapline became %s", wkt1);
5153 lwfree(wkt1);
5154 }
5155#endif
5156 }
5157#if POSTGIS_DEBUG_LEVEL > 0
5158 else {
5159 LWDEBUG(1, "Snapping did not move first point");
5160 }
5161#endif
5162
5163 if ( -1 == lwt_ChangeEdgeGeom( topo, edge_id, snapline ) )
5164 {
5165 /* TODO: should have invoked lwerror already, leaking memory */
5166 lwgeom_free(prj);
5167 lwgeom_free(snapedge);
5168 _lwt_release_edges(edges, num);
5169 lwerror("lwt_ChangeEdgeGeom failed");
5170 return -1;
5171 }
5172 lwgeom_free(snapedge);
5173 }}
5174#if POSTGIS_DEBUG_LEVEL > 0
5175 else
5176 {{
5177 size_t sz;
5178 char *wkt1 = lwgeom_to_wkt(g, WKT_EXTENDED, 15, &sz);
5179 char *wkt2 = lwgeom_to_wkt(prj, WKT_EXTENDED, 15, &sz);
5180 LWDEBUGF(1, "Edge %s contains projected point %s", wkt1, wkt2);
5181 lwfree(wkt1);
5182 lwfree(wkt2);
5183 }}
5184#endif
5185
5186 /* TODO: pass 1 as last argument (skipChecks) ? */
5187 id = lwt_ModEdgeSplit( topo, edge_id, lwgeom_as_lwpoint(prj), 0 );
5188 if ( -1 == id )
5189 {
5190 /* TODO: should have invoked lwerror already, leaking memory */
5191 lwgeom_free(prj);
5192 _lwt_release_edges(edges, num);
5193 lwerror("lwt_ModEdgeSplit failed");
5194 return -1;
5195 }
5196
5197 lwgeom_free(prj);
5198
5199 /*
5200 * TODO: decimate the two new edges with the given tolerance ?
5201 *
5202 * the edge identifiers to decimate would be: edge_id and "id"
5203 * The problem here is that decimation of existing edges
5204 * may introduce intersections or topological inconsistencies,
5205 * for example:
5206 *
5207 * - A node may end up falling on the other side of the edge
5208 * - The decimated edge might intersect another existing edge
5209 *
5210 */
5211
5212 break; /* we only want to snap a single edge */
5213 }
5214 _lwt_release_edges(edges, num);
5215 }
5216 else
5217 {
5218 /* The point is isolated, add it as such */
5219 /* TODO: pass 1 as last argument (skipChecks) ? */
5220 id = _lwt_AddIsoNode(topo, -1, point, 0, findFace);
5221 if ( moved ) *moved = 0;
5222 if ( -1 == id )
5223 {
5224 /* should have invoked lwerror already, leaking memory */
5225 lwerror("lwt_AddIsoNode failed");
5226 return -1;
5227 }
5228 }
5229
5230 return id;
5231}
char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]
void lwgeom_geos_error(const char *fmt,...)
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition lwgeom.c:326
char lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
geom1 same as geom2 iff
Definition lwgeom.c:573
LWGEOM * lwgeom_closest_point(const LWGEOM *lw1, const LWGEOM *lw2)
Definition measures.c:52
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1138
#define WKT_EXTENDED
Definition liblwgeom.h:2132
#define LW_SUCCESS
Definition liblwgeom.h:111
char * lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out)
WKT emitter function.
Definition lwout_wkt.c:676
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition lwgeom.c:916
void * lwalloc(size_t size)
Definition lwutil.c:227
int ptarray_insert_point(POINTARRAY *pa, const POINT4D *p, uint32_t where)
Insert a point into an existing POINTARRAY.
Definition ptarray.c:85
void lwfree(void *mem)
Definition lwutil.c:242
LWGEOM * lwgeom_force_3dz(const LWGEOM *geom)
Definition lwgeom.c:781
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition lwgeom.c:321
double lwgeom_mindistance2d(const LWGEOM *lw1, const LWGEOM *lw2)
Function initializing min distance calculation.
Definition measures.c:197
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition lwgeom_api.c:125
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition lwgeom.c:161
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition lwgeom_api.c:376
void lwline_free(LWLINE *line)
Definition lwline.c:67
int ptarray_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number)
Definition ptarray.c:746
#define LW_BOUNDARY
LWT_INT64 LWT_ELEMID
Identifier of topology element.
#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
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
Definition lwutil.c:177
#define LWDEBUGG(level, geom, msg)
Definition lwgeom_log.h:93
static LWGEOM * _lwt_toposnap(LWGEOM *src, LWGEOM *tgt, double tol)
LWT_ISO_NODE * lwt_be_getNodeWithinDistance2D(LWT_TOPOLOGY *topo, LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit)
LWT_ELEMID lwt_ModEdgeSplit(LWT_TOPOLOGY *topo, LWT_ELEMID edge, LWPOINT *pt, int skipISOChecks)
Split an edge by a node, modifying the original edge and adding a new one.
static LWT_ELEMID _lwt_AddIsoNode(LWT_TOPOLOGY *topo, LWT_ELEMID face, LWPOINT *pt, int skipISOChecks, int checkFace)
static void _lwt_release_nodes(LWT_ISO_NODE *nodes, int num_nodes)
static double _lwt_minTolerance(LWGEOM *g)
static int compare_scored_pointer(const void *si1, const void *si2)
#define _LWT_MINTOLERANCE(topo, geom)
const char * lwt_be_lastErrorMessage(const LWT_BE_IFACE *be)
LWT_ISO_EDGE * lwt_be_getEdgeWithinDistance2D(LWT_TOPOLOGY *topo, LWPOINT *pt, double dist, uint64_t *numelems, int fields, int64_t limit)
#define LWTFMT_ELEMID
Definition lwgeom_topo.c:43
static void _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
int lwt_ChangeEdgeGeom(LWT_TOPOLOGY *topo, LWT_ELEMID edge_id, LWLINE *geom)
Changes the shape of an edge without affecting the topology structure.
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition lwinline.h:121
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
LWT_ELEMID edge_id
LWT_ELEMID node_id
LWPOINT * geom
const LWT_BE_IFACE * be_iface
double x
Definition liblwgeom.h:400
double z
Definition liblwgeom.h:400
double y
Definition liblwgeom.h:400

References _lwt_AddIsoNode(), _lwt_minTolerance(), _LWT_MINTOLERANCE, _lwt_release_edges(), _lwt_release_nodes(), _lwt_toposnap(), LWT_TOPOLOGY_T::be_iface, compare_scored_pointer(), contains(), LWT_ISO_EDGE::edge_id, LWT_ISO_NODE::geom, LWT_ISO_EDGE::geom, getPoint2d_cp(), getPoint4d_p(), LW_BOUNDARY, LW_SUCCESS, lwalloc(), LWDEBUG, LWDEBUGF, LWDEBUGG, lwerror(), lwfree(), lwgeom_as_lwline(), lwgeom_as_lwpoint(), lwgeom_closest_point(), lwgeom_force_3dz(), lwgeom_free(), lwgeom_geos_errmsg, lwgeom_geos_error(), lwgeom_has_z(), lwgeom_mindistance2d(), lwgeom_same(), lwgeom_to_wkt(), lwline_as_lwgeom(), lwline_free(), lwnotice(), lwpoint_as_lwgeom(), lwt_be_getEdgeWithinDistance2D(), lwt_be_getNodeWithinDistance2D(), lwt_be_lastErrorMessage(), lwt_ChangeEdgeGeom(), LWT_COL_EDGE_EDGE_ID, LWT_COL_EDGE_GEOM, LWT_COL_NODE_GEOM, LWT_COL_NODE_NODE_ID, lwt_ModEdgeSplit(), LWTFMT_ELEMID, LWT_ISO_NODE::node_id, LWPOINT::point, LWLINE::points, ptarray_contains_point_partial(), ptarray_insert_point(), ptarray_set_point4d(), scored_pointer_t::ptr, scored_pointer_t::score, WKT_EXTENDED, POINT4D::x, POINT4D::y, and POINT4D::z.

Referenced by _lwt_AddLineEdge(), and lwt_AddPoint().

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