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

◆ lwt_ChangeEdgeGeom()

int lwt_ChangeEdgeGeom ( LWT_TOPOLOGY topo,
LWT_ELEMID  edge,
LWLINE curve 
)

Changes the shape of an edge without affecting the topology structure.

For ST_ChangeEdgeGeom

Parameters
topothe topology to operate on
curvethe edge geometry
Returns
0 on success, -1 on error (liblwgeom error handler will be invoked with error message)

Definition at line 3189 of file lwgeom_topo.c.

3190{
3191 LWT_ISO_EDGE *oldedge;
3192 LWT_ISO_EDGE newedge;
3193 POINT2D p1, p2, pt;
3194 uint64_t i;
3195 int isclosed = 0;
3196
3197 /* curve must be simple */
3198 if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) )
3199 {
3200 lwerror("SQL/MM Spatial exception - curve not simple");
3201 return -1;
3202 }
3203
3204 i = 1;
3205 oldedge = lwt_be_getEdgeById(topo, &edge_id, &i, LWT_COL_EDGE_ALL);
3206 if ( ! oldedge )
3207 {
3208 LWDEBUGF(1, "lwt_ChangeEdgeGeom: "
3209 "lwt_be_getEdgeById returned NULL and set i=%d", i);
3210 if (i == UINT64_MAX)
3211 {
3212 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3213 return -1;
3214 }
3215 else if ( i == 0 )
3216 {
3217 lwerror("SQL/MM Spatial exception - non-existent edge %"
3218 LWTFMT_ELEMID, edge_id);
3219 return -1;
3220 }
3221 else
3222 {
3223 lwerror("Backend coding error: getEdgeById callback returned NULL "
3224 "but numelements output parameter has value %d "
3225 "(expected 0 or 1)", i);
3226 return -1;
3227 }
3228 }
3229
3230 LWDEBUGF(1, "lwt_ChangeEdgeGeom: "
3231 "old edge has %d points, new edge has %d points",
3232 oldedge->geom->points->npoints, geom->points->npoints);
3233
3234 /*
3235 * e) Check StartPoint consistency
3236 */
3237 getPoint2d_p(oldedge->geom->points, 0, &p1);
3238 getPoint2d_p(geom->points, 0, &pt);
3239 if ( ! p2d_same(&p1, &pt) )
3240 {
3241 _lwt_release_edges(oldedge, 1);
3242 lwerror("SQL/MM Spatial exception - "
3243 "start node not geometry start point.");
3244 return -1;
3245 }
3246
3247 /*
3248 * f) Check EndPoint consistency
3249 */
3250 if ( oldedge->geom->points->npoints < 2 )
3251 {
3252 _lwt_release_edges(oldedge, 1);
3253 lwerror("Corrupted topology: edge %" LWTFMT_ELEMID
3254 " has less than 2 vertices", oldedge->edge_id);
3255 return -1;
3256 }
3257 getPoint2d_p(oldedge->geom->points, oldedge->geom->points->npoints-1, &p2);
3258 if ( geom->points->npoints < 2 )
3259 {
3260 _lwt_release_edges(oldedge, 1);
3261 lwerror("Invalid edge: less than 2 vertices");
3262 return -1;
3263 }
3264 getPoint2d_p(geom->points, geom->points->npoints-1, &pt);
3265 if ( ! p2d_same(&pt, &p2) )
3266 {
3267 _lwt_release_edges(oldedge, 1);
3268 lwerror("SQL/MM Spatial exception - "
3269 "end node not geometry end point.");
3270 return -1;
3271 }
3272
3273 /* Not in the specs:
3274 * if the edge is closed, check we didn't change winding !
3275 * (should be part of isomorphism checking)
3276 */
3277 if ( oldedge->start_node == oldedge->end_node )
3278 {
3279 isclosed = 1;
3280#if 1 /* TODO: this is actually bogus as a test */
3281 /* check for valid edge (distinct vertices must exist) */
3282 if ( ! _lwt_GetInteriorEdgePoint(geom, &pt) )
3283 {
3284 _lwt_release_edges(oldedge, 1);
3285 lwerror("Invalid edge (no two distinct vertices exist)");
3286 return -1;
3287 }
3288#endif
3289
3290 if ( ptarray_isccw(oldedge->geom->points) !=
3291 ptarray_isccw(geom->points) )
3292 {
3293 _lwt_release_edges(oldedge, 1);
3294 lwerror("Edge twist at node POINT(%g %g)", p1.x, p1.y);
3295 return -1;
3296 }
3297 }
3298
3299 if ( _lwt_CheckEdgeCrossing(topo, oldedge->start_node,
3300 oldedge->end_node, geom, edge_id ) )
3301 {
3302 /* would have called lwerror already, leaking :( */
3303 _lwt_release_edges(oldedge, 1);
3304 return -1;
3305 }
3306
3307 LWDEBUG(1, "lwt_ChangeEdgeGeom: "
3308 "edge crossing check passed ");
3309
3310 /*
3311 * Not in the specs:
3312 * Check topological isomorphism
3313 */
3314
3315 /* Check that the "motion range" doesn't include any node */
3316 // 1. compute combined bbox of old and new edge
3317 GBOX mbox; /* motion box */
3318 lwgeom_add_bbox((LWGEOM*)oldedge->geom); /* just in case */
3319 lwgeom_add_bbox((LWGEOM*)geom); /* just in case */
3320 gbox_union(oldedge->geom->bbox, geom->bbox, &mbox);
3321 // 2. fetch all nodes in the combined box
3322 LWT_ISO_NODE *nodes;
3323 uint64_t numnodes;
3324 nodes = lwt_be_getNodeWithinBox2D(topo, &mbox, &numnodes,
3325 LWT_COL_NODE_ALL, 0);
3326 LWDEBUGF(1, "lwt_be_getNodeWithinBox2D returned %d nodes", numnodes);
3327 if (numnodes == UINT64_MAX)
3328 {
3329 _lwt_release_edges(oldedge, 1);
3330 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3331 return -1;
3332 }
3333 // 3. if any node beside endnodes are found:
3334 if ( numnodes > ( 1 + isclosed ? 0 : 1 ) )
3335 {{
3336 // 3.2. bail out if any node is in one and not the other
3337 for (i=0; i<numnodes; ++i)
3338 {
3339 LWT_ISO_NODE *n = &(nodes[i]);
3340 if ( n->node_id == oldedge->start_node ) continue;
3341 if ( n->node_id == oldedge->end_node ) continue;
3342 const POINT2D *pt = getPoint2d_cp(n->geom->point, 0);
3343 int ocont = ptarray_contains_point_partial(oldedge->geom->points, pt, isclosed, NULL) == LW_INSIDE;
3344 int ncont = ptarray_contains_point_partial(geom->points, pt, isclosed, NULL) == LW_INSIDE;
3345 if (ocont != ncont)
3346 {
3347 size_t sz;
3348 char *wkt = lwgeom_to_wkt(lwpoint_as_lwgeom(n->geom), WKT_ISO, 15, &sz);
3349 _lwt_release_nodes(nodes, numnodes);
3350 lwerror("Edge motion collision at %s", wkt);
3351 lwfree(wkt); /* would not necessarely reach this point */
3352 return -1;
3353 }
3354 }
3355 }}
3356 if ( numnodes ) _lwt_release_nodes(nodes, numnodes);
3357
3358 LWDEBUG(1, "nodes containment check passed");
3359
3360 /*
3361 * Check edge adjacency before
3362 * TODO: can be optimized to gather azimuths of all edge ends once
3363 */
3364
3365 edgeend span_pre, epan_pre;
3366 /* initialize span_pre.myaz and epan_pre.myaz with existing edge */
3367 int res = _lwt_InitEdgeEndByLine(&span_pre, &epan_pre, oldedge->geom, &p1, &p2);
3368 if (res)
3369 return -1; /* lwerror should have been raised */
3370 _lwt_FindAdjacentEdges( topo, oldedge->start_node, &span_pre,
3371 isclosed ? &epan_pre : NULL, edge_id );
3372 _lwt_FindAdjacentEdges( topo, oldedge->end_node, &epan_pre,
3373 isclosed ? &span_pre : NULL, edge_id );
3374
3375 LWDEBUGF(1, "edges adjacent to old edge are %" LWTFMT_ELEMID
3376 " and %" LWTFMT_ELEMID " (first point), %" LWTFMT_ELEMID
3377 " and %" LWTFMT_ELEMID " (last point)",
3378 span_pre.nextCW, span_pre.nextCCW,
3379 epan_pre.nextCW, epan_pre.nextCCW);
3380
3381 /* update edge geometry */
3382 newedge.edge_id = edge_id;
3383 newedge.geom = geom;
3384 res = lwt_be_updateEdgesById(topo, &newedge, 1, LWT_COL_EDGE_GEOM);
3385 if (res == -1)
3386 {
3387 _lwt_release_edges(oldedge, 1);
3388 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3389 return -1;
3390 }
3391 if (!res)
3392 {
3393 _lwt_release_edges(oldedge, 1);
3394 lwerror("Unexpected error: %d edges updated when expecting 1", i);
3395 return -1;
3396 }
3397
3398 /*
3399 * Check edge adjacency after
3400 */
3401 edgeend span_post, epan_post;
3402 /* initialize epan_post.myaz and epan_post.myaz */
3403 res = _lwt_InitEdgeEndByLine(&span_post, &epan_post, geom, &p1, &p2);
3404 if (res)
3405 return -1; /* lwerror should have been raised */
3406 _lwt_FindAdjacentEdges( topo, oldedge->start_node, &span_post,
3407 isclosed ? &epan_post : NULL, edge_id );
3408 _lwt_FindAdjacentEdges( topo, oldedge->end_node, &epan_post,
3409 isclosed ? &span_post : NULL, edge_id );
3410
3411 LWDEBUGF(1, "edges adjacent to new edge are %" LWTFMT_ELEMID
3412 " and %" LWTFMT_ELEMID " (first point), %" LWTFMT_ELEMID
3413 " and %" LWTFMT_ELEMID " (last point)",
3414 span_pre.nextCW, span_pre.nextCCW,
3415 epan_pre.nextCW, epan_pre.nextCCW);
3416
3417
3418 /* Bail out if next CW or CCW edge on start node changed */
3419 if ( span_pre.nextCW != span_post.nextCW ||
3420 span_pre.nextCCW != span_post.nextCCW )
3421 {{
3422 LWT_ELEMID nid = oldedge->start_node;
3423 _lwt_release_edges(oldedge, 1);
3424 lwerror("Edge changed disposition around start node %"
3425 LWTFMT_ELEMID, nid);
3426 return -1;
3427 }}
3428
3429 /* Bail out if next CW or CCW edge on end node changed */
3430 if ( epan_pre.nextCW != epan_post.nextCW ||
3431 epan_pre.nextCCW != epan_post.nextCCW )
3432 {{
3433 LWT_ELEMID nid = oldedge->end_node;
3434 _lwt_release_edges(oldedge, 1);
3435 lwerror("Edge changed disposition around end node %"
3436 LWTFMT_ELEMID, nid);
3437 return -1;
3438 }}
3439
3440 /*
3441 -- Update faces MBR of left and right faces
3442 -- TODO: think about ways to optimize this part, like see if
3443 -- the old edge geometry participated in the definition
3444 -- of the current MBR (for shrinking) or the new edge MBR
3445 -- would be larger than the old face MBR...
3446 --
3447 */
3448 const GBOX* oldbox = lwgeom_get_bbox(lwline_as_lwgeom(oldedge->geom));
3449 const GBOX* newbox = lwgeom_get_bbox(lwline_as_lwgeom(geom));
3450 if ( ! gbox_same(oldbox, newbox) )
3451 {
3452 uint64_t facestoupdate = 0;
3453 LWT_ISO_FACE faces[2];
3454 LWGEOM *nface1 = NULL;
3455 LWGEOM *nface2 = NULL;
3456 if ( oldedge->face_left > 0 )
3457 {
3458 nface1 = lwt_GetFaceGeometry(topo, oldedge->face_left);
3459 if ( ! nface1 )
3460 {
3461 lwerror("lwt_ChangeEdgeGeom could not construct face %"
3462 LWTFMT_ELEMID ", on the left of edge %" LWTFMT_ELEMID,
3463 oldedge->face_left, edge_id);
3464 return -1;
3465 }
3466 #if 0
3467 {
3468 size_t sz;
3469 char *wkt = lwgeom_to_wkt(nface1, WKT_EXTENDED, 2, &sz);
3470 LWDEBUGF(1, "new geometry of face left (%d): %s", (int)oldedge->face_left, wkt);
3471 lwfree(wkt);
3472 }
3473 #endif
3474 lwgeom_add_bbox(nface1);
3475 if ( ! nface1->bbox )
3476 {
3477 lwerror("Corrupted topology: face %d, left of edge %d, has no bbox",
3478 oldedge->face_left, edge_id);
3479 return -1;
3480 }
3481 faces[facestoupdate].face_id = oldedge->face_left;
3482 /* ownership left to nface */
3483 faces[facestoupdate++].mbr = nface1->bbox;
3484 }
3485 if ( oldedge->face_right > 0
3486 /* no need to update twice the same face.. */
3487 && oldedge->face_right != oldedge->face_left )
3488 {
3489 nface2 = lwt_GetFaceGeometry(topo, oldedge->face_right);
3490 if ( ! nface2 )
3491 {
3492 lwerror("lwt_ChangeEdgeGeom could not construct face %"
3493 LWTFMT_ELEMID ", on the right of edge %" LWTFMT_ELEMID,
3494 oldedge->face_right, edge_id);
3495 return -1;
3496 }
3497 #if 0
3498 {
3499 size_t sz;
3500 char *wkt = lwgeom_to_wkt(nface2, WKT_EXTENDED, 2, &sz);
3501 LWDEBUGF(1, "new geometry of face right (%d): %s", (int)oldedge->face_right, wkt);
3502 lwfree(wkt);
3503 }
3504 #endif
3505 lwgeom_add_bbox(nface2);
3506 if ( ! nface2->bbox )
3507 {
3508 lwerror("Corrupted topology: face %d, right of edge %d, has no bbox",
3509 oldedge->face_right, edge_id);
3510 return -1;
3511 }
3512 faces[facestoupdate].face_id = oldedge->face_right;
3513 faces[facestoupdate++].mbr = nface2->bbox; /* ownership left to nface */
3514 }
3515 LWDEBUGF(1, "%d faces to update", facestoupdate);
3516 if ( facestoupdate )
3517 {
3518 uint64_t updatedFaces = lwt_be_updateFacesById(topo, &(faces[0]), facestoupdate);
3519 if (updatedFaces != facestoupdate)
3520 {
3521 if (nface1)
3522 lwgeom_free(nface1);
3523 if (nface2)
3524 lwgeom_free(nface2);
3525 _lwt_release_edges(oldedge, 1);
3526 if (updatedFaces == UINT64_MAX)
3527 lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3528 else
3529 lwerror("Unexpected error: %d faces found when expecting 1", i);
3530 return -1;
3531 }
3532 }
3533 if ( nface1 ) lwgeom_free(nface1);
3534 if ( nface2 ) lwgeom_free(nface2);
3535 }
3536 else
3537 {
3538 lwnotice("BBOX of changed edge did not change");
3539 }
3540
3541 LWDEBUG(1, "all done, cleaning up edges");
3542
3543 _lwt_release_edges(oldedge, 1);
3544 return 0; /* success */
3545}
int gbox_same(const GBOX *g1, const GBOX *g2)
Check if 2 given Gbox are the same.
Definition gbox.c:164
int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout)
Update the output GBOX to be large enough to include both inputs.
Definition gbox.c:135
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition lwgeom.c:326
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1138
#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
int lwgeom_is_simple(const LWGEOM *lwgeom)
int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point)
Definition lwgeom_api.c:349
void lwfree(void *mem)
Definition lwutil.c:242
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition lwgeom.c:321
#define WKT_ISO
Definition liblwgeom.h:2130
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
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition lwgeom.c:677
int ptarray_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number)
Definition ptarray.c:746
#define LW_INSIDE
Constants for point-in-polygon return values.
int ptarray_isccw(const POINTARRAY *pa)
Definition ptarray.c:1034
int p2d_same(const POINT2D *p1, const POINT2D *p2)
Definition lwalgorithm.c:50
LWT_INT64 LWT_ELEMID
Identifier of topology element.
#define LWT_COL_EDGE_ALL
#define LWT_COL_EDGE_GEOM
#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
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
Definition lwutil.c:177
static uint64_t lwt_be_updateFacesById(LWT_TOPOLOGY *topo, const LWT_ISO_FACE *faces, uint64_t numfaces)
static int _lwt_InitEdgeEndByLine(edgeend *fee, edgeend *lee, LWLINE *edge, POINT2D *fp, POINT2D *lp)
static int _lwt_GetInteriorEdgePoint(const LWLINE *edge, POINT2D *ip)
LWT_ISO_EDGE * lwt_be_getEdgeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
static int _lwt_CheckEdgeCrossing(LWT_TOPOLOGY *topo, LWT_ELEMID start_node, LWT_ELEMID end_node, const LWLINE *geom, LWT_ELEMID myself)
static void _lwt_release_nodes(LWT_ISO_NODE *nodes, int num_nodes)
static int _lwt_FindAdjacentEdges(LWT_TOPOLOGY *topo, LWT_ELEMID node, edgeend *data, edgeend *other, int myedge_id)
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
LWGEOM * lwt_GetFaceGeometry(LWT_TOPOLOGY *topo, LWT_ELEMID faceid)
Return the geometry of a face.
static void _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
static int lwt_be_updateEdgesById(LWT_TOPOLOGY *topo, const LWT_ISO_EDGE *edges, int numedges, int upd_fields)
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
tuple res
Definition window.py:79
GBOX * bbox
Definition liblwgeom.h:444
GBOX * bbox
Definition liblwgeom.h:468
POINTARRAY * points
Definition liblwgeom.h:469
POINTARRAY * point
Definition liblwgeom.h:457
LWT_ELEMID face_right
LWT_ELEMID end_node
LWT_ELEMID face_left
LWT_ELEMID edge_id
LWT_ELEMID start_node
LWT_ELEMID face_id
LWT_ELEMID node_id
LWPOINT * geom
const LWT_BE_IFACE * be_iface
double y
Definition liblwgeom.h:376
double x
Definition liblwgeom.h:376
uint32_t npoints
Definition liblwgeom.h:413
LWT_ELEMID nextCCW
LWT_ELEMID nextCW

References _lwt_CheckEdgeCrossing(), _lwt_FindAdjacentEdges(), _lwt_GetInteriorEdgePoint(), _lwt_InitEdgeEndByLine(), _lwt_release_edges(), _lwt_release_nodes(), LWGEOM::bbox, LWLINE::bbox, LWT_TOPOLOGY_T::be_iface, LWT_ISO_EDGE::edge_id, LWT_ISO_EDGE::end_node, LWT_ISO_FACE::face_id, LWT_ISO_EDGE::face_left, LWT_ISO_EDGE::face_right, gbox_same(), gbox_union(), LWT_ISO_NODE::geom, LWT_ISO_EDGE::geom, getPoint2d_cp(), getPoint2d_p(), LW_INSIDE, LWDEBUG, LWDEBUGF, lwerror(), lwfree(), lwgeom_add_bbox(), lwgeom_free(), lwgeom_get_bbox(), lwgeom_is_simple(), lwgeom_to_wkt(), lwline_as_lwgeom(), lwnotice(), lwpoint_as_lwgeom(), lwt_be_getEdgeById(), lwt_be_getNodeWithinBox2D(), lwt_be_lastErrorMessage(), lwt_be_updateEdgesById(), lwt_be_updateFacesById(), LWT_COL_EDGE_ALL, LWT_COL_EDGE_GEOM, LWT_COL_NODE_ALL, lwt_GetFaceGeometry(), LWTFMT_ELEMID, LWT_ISO_FACE::mbr, edgeend_t::nextCCW, edgeend_t::nextCW, LWT_ISO_NODE::node_id, POINTARRAY::npoints, p2d_same(), LWPOINT::point, LWLINE::points, ptarray_contains_point_partial(), ptarray_isccw(), LWT_ISO_EDGE::start_node, WKT_EXTENDED, WKT_ISO, POINT2D::x, and POINT2D::y.

Referenced by _lwt_AddPoint().

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