PostGIS  3.4.0dev-r@@SVN_REVISION@@

◆ 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 3234 of file lwgeom_topo.c.

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

References _lwt_CheckEdgeCrossing(), _lwt_FindAdjacentEdges(), _lwt_GetInteriorEdgePoint(), _lwt_InitEdgeEndByLine(), _lwt_release_edges(), _lwt_release_nodes(), 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_get_bbox(), lwgeom_is_simple(), lwgeom_to_wkt(), lwline_as_lwgeom(), lwpoint_as_lwgeom(), lwt_be_computeFaceMBR(), 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, LWTFMT_ELEMID, LWT_ISO_FACE::mbr, edgeend_t::nextCCW, edgeend_t::nextCW, LWT_ISO_NODE::node_id, POINTARRAY::npoints, P2D_SAME_STRICT, LWPOINT::point, LWLINE::points, ptarray_contains_point_partial(), ptarray_isccw(), window::res, LWT_ISO_EDGE::start_node, 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: