PostGIS  2.2.7dev-r@@SVN_REVISION@@
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 3268 of file lwgeom_topo.c.

References _lwt_CheckEdgeCrossing(), _lwt_EdgeMotionArea(), _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_union(), LWT_ISO_NODE::geom, LWT_ISO_EDGE::geom, getPoint2d_p(), LWDEBUG, LWDEBUGF, lwerror(), lwfree(), LWGEOM2GEOS(), lwgeom_add_bbox(), lwgeom_free(), lwgeom_geos_errmsg, lwgeom_geos_error(), 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(), LWLINE::points, ptarray_isccw(), LWT_ISO_EDGE::start_node, WKT_EXTENDED, WKT_ISO, POINT2D::x, and POINT2D::y.

Referenced by lwt_AddPoint().

3269 {
3270  LWT_ISO_EDGE *oldedge;
3271  LWT_ISO_EDGE newedge;
3272  POINT2D p1, p2, pt;
3273  int i;
3274  int isclosed = 0;
3275 
3276  /* curve must be simple */
3277  if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) )
3278  {
3279  lwerror("SQL/MM Spatial exception - curve not simple");
3280  return -1;
3281  }
3282 
3283  i = 1;
3284  oldedge = lwt_be_getEdgeById(topo, &edge_id, &i, LWT_COL_EDGE_ALL);
3285  if ( ! oldedge )
3286  {
3287  LWDEBUGF(1, "lwt_ChangeEdgeGeom: "
3288  "lwt_be_getEdgeById returned NULL and set i=%d", i);
3289  if ( i == -1 )
3290  {
3291  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3292  return -1;
3293  }
3294  else if ( i == 0 )
3295  {
3296  lwerror("SQL/MM Spatial exception - non-existent edge %"
3297  LWTFMT_ELEMID, edge_id);
3298  return -1;
3299  }
3300  else
3301  {
3302  lwerror("Backend coding error: getEdgeById callback returned NULL "
3303  "but numelements output parameter has value %d "
3304  "(expected 0 or 1)", i);
3305  return -1;
3306  }
3307  }
3308 
3309  LWDEBUGF(1, "lwt_ChangeEdgeGeom: "
3310  "old edge has %d points, new edge has %d points",
3311  oldedge->geom->points->npoints, geom->points->npoints);
3312 
3313  /*
3314  * e) Check StartPoint consistency
3315  */
3316  getPoint2d_p(oldedge->geom->points, 0, &p1);
3317  getPoint2d_p(geom->points, 0, &pt);
3318  if ( ! p2d_same(&p1, &pt) )
3319  {
3320  _lwt_release_edges(oldedge, 1);
3321  lwerror("SQL/MM Spatial exception - "
3322  "start node not geometry start point.");
3323  return -1;
3324  }
3325 
3326  /*
3327  * f) Check EndPoint consistency
3328  */
3329  if ( oldedge->geom->points->npoints < 2 )
3330  {
3331  _lwt_release_edges(oldedge, 1);
3332  lwerror("Corrupted topology: edge %" LWTFMT_ELEMID
3333  " has less than 2 vertices", oldedge->edge_id);
3334  return -1;
3335  }
3336  getPoint2d_p(oldedge->geom->points, oldedge->geom->points->npoints-1, &p2);
3337  if ( geom->points->npoints < 2 )
3338  {
3339  _lwt_release_edges(oldedge, 1);
3340  lwerror("Invalid edge: less than 2 vertices");
3341  return -1;
3342  }
3343  getPoint2d_p(geom->points, geom->points->npoints-1, &pt);
3344  if ( ! p2d_same(&pt, &p2) )
3345  {
3346  _lwt_release_edges(oldedge, 1);
3347  lwerror("SQL/MM Spatial exception - "
3348  "end node not geometry end point.");
3349  return -1;
3350  }
3351 
3352  /* Not in the specs:
3353  * if the edge is closed, check we didn't change winding !
3354  * (should be part of isomorphism checking)
3355  */
3356  if ( oldedge->start_node == oldedge->end_node )
3357  {
3358  isclosed = 1;
3359 #if 1 /* TODO: this is actually bogus as a test */
3360  /* check for valid edge (distinct vertices must exist) */
3361  if ( ! _lwt_GetInteriorEdgePoint(geom, &pt) )
3362  {
3363  _lwt_release_edges(oldedge, 1);
3364  lwerror("Invalid edge (no two distinct vertices exist)");
3365  return -1;
3366  }
3367 #endif
3368 
3369  if ( ptarray_isccw(oldedge->geom->points) !=
3370  ptarray_isccw(geom->points) )
3371  {
3372  _lwt_release_edges(oldedge, 1);
3373  lwerror("Edge twist at node POINT(%g %g)", p1.x, p1.y);
3374  return -1;
3375  }
3376  }
3377 
3378  if ( _lwt_CheckEdgeCrossing(topo, oldedge->start_node,
3379  oldedge->end_node, geom, edge_id ) )
3380  {
3381  /* would have called lwerror already, leaking :( */
3382  _lwt_release_edges(oldedge, 1);
3383  return -1;
3384  }
3385 
3386  LWDEBUG(1, "lwt_ChangeEdgeGeom: "
3387  "edge crossing check passed ");
3388 
3389  /*
3390  * Not in the specs:
3391  * Check topological isomorphism
3392  */
3393 
3394  /* Check that the "motion range" doesn't include any node */
3395  // 1. compute combined bbox of old and new edge
3396  GBOX mbox; /* motion box */
3397  lwgeom_add_bbox((LWGEOM*)oldedge->geom); /* just in case */
3398  lwgeom_add_bbox((LWGEOM*)geom); /* just in case */
3399  gbox_union(oldedge->geom->bbox, geom->bbox, &mbox);
3400  // 2. fetch all nodes in the combined box
3401  LWT_ISO_NODE *nodes;
3402  int numnodes;
3403  nodes = lwt_be_getNodeWithinBox2D(topo, &mbox, &numnodes,
3404  LWT_COL_NODE_ALL, 0);
3405  LWDEBUGF(1, "lwt_be_getNodeWithinBox2D returned %d nodes", numnodes);
3406  if ( numnodes == -1 ) {
3407  _lwt_release_edges(oldedge, 1);
3408  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3409  return -1;
3410  }
3411  // 3. if any node beside endnodes are found:
3412  if ( numnodes > ( 1 + isclosed ? 0 : 1 ) )
3413  {{
3414  GEOSGeometry *oarea, *narea;
3415  const GEOSPreparedGeometry *oareap, *nareap;
3416 
3417  initGEOS(lwnotice, lwgeom_geos_error);
3418 
3419  oarea = _lwt_EdgeMotionArea(oldedge->geom, isclosed);
3420  if ( ! oarea )
3421  {
3422  _lwt_release_edges(oldedge, 1);
3423  lwerror("Could not compute edge motion area for old edge");
3424  return -1;
3425  }
3426 
3427  narea = _lwt_EdgeMotionArea(geom, isclosed);
3428  if ( ! narea )
3429  {
3430  GEOSGeom_destroy(oarea);
3431  _lwt_release_edges(oldedge, 1);
3432  lwerror("Could not compute edge motion area for new edge");
3433  return -1;
3434  }
3435 
3436  // 3.2. bail out if any node is in one and not the other
3437  oareap = GEOSPrepare( oarea );
3438  nareap = GEOSPrepare( narea );
3439  for (i=0; i<numnodes; ++i)
3440  {
3441  LWT_ISO_NODE *n = &(nodes[i]);
3442  GEOSGeometry *ngg;
3443  int ocont, ncont;
3444  size_t sz;
3445  char *wkt;
3446  if ( n->node_id == oldedge->start_node ) continue;
3447  if ( n->node_id == oldedge->end_node ) continue;
3448  ngg = LWGEOM2GEOS( lwpoint_as_lwgeom(n->geom) , 0);
3449  ocont = GEOSPreparedContains( oareap, ngg );
3450  ncont = GEOSPreparedContains( nareap, ngg );
3451  GEOSGeom_destroy(ngg);
3452  if (ocont == 2 || ncont == 2)
3453  {
3454  _lwt_release_nodes(nodes, numnodes);
3455  GEOSPreparedGeom_destroy(oareap);
3456  GEOSGeom_destroy(oarea);
3457  GEOSPreparedGeom_destroy(nareap);
3458  GEOSGeom_destroy(narea);
3459  lwerror("GEOS exception on PreparedContains: %s", lwgeom_geos_errmsg);
3460  return -1;
3461  }
3462  if (ocont != ncont)
3463  {
3464  GEOSPreparedGeom_destroy(oareap);
3465  GEOSGeom_destroy(oarea);
3466  GEOSPreparedGeom_destroy(nareap);
3467  GEOSGeom_destroy(narea);
3468  wkt = lwgeom_to_wkt(lwpoint_as_lwgeom(n->geom), WKT_ISO, 15, &sz);
3469  _lwt_release_nodes(nodes, numnodes);
3470  lwerror("Edge motion collision at %s", wkt);
3471  lwfree(wkt); /* would not necessarely reach this point */
3472  return -1;
3473  }
3474  }
3475  GEOSPreparedGeom_destroy(oareap);
3476  GEOSGeom_destroy(oarea);
3477  GEOSPreparedGeom_destroy(nareap);
3478  GEOSGeom_destroy(narea);
3479  }}
3480  if ( numnodes ) _lwt_release_nodes(nodes, numnodes);
3481 
3482  LWDEBUG(1, "nodes containment check passed");
3483 
3484  /*
3485  * Check edge adjacency before
3486  * TODO: can be optimized to gather azimuths of all edge ends once
3487  */
3488 
3489  edgeend span_pre, epan_pre;
3490  /* initialize span_pre.myaz and epan_pre.myaz with existing edge */
3491  i = _lwt_InitEdgeEndByLine(&span_pre, &epan_pre,
3492  oldedge->geom, &p1, &p2);
3493  if ( i ) return -1; /* lwerror should have been raised */
3494  _lwt_FindAdjacentEdges( topo, oldedge->start_node, &span_pre,
3495  isclosed ? &epan_pre : NULL, edge_id );
3496  _lwt_FindAdjacentEdges( topo, oldedge->end_node, &epan_pre,
3497  isclosed ? &span_pre : NULL, edge_id );
3498 
3499  LWDEBUGF(1, "edges adjacent to old edge are %" LWTFMT_ELEMID
3500  " and %" LWTFMT_ELEMID " (first point), %" LWTFMT_ELEMID
3501  " and %" LWTFMT_ELEMID " (last point)",
3502  span_pre.nextCW, span_pre.nextCCW,
3503  epan_pre.nextCW, epan_pre.nextCCW);
3504 
3505  /* update edge geometry */
3506  newedge.edge_id = edge_id;
3507  newedge.geom = geom;
3508  i = lwt_be_updateEdgesById(topo, &newedge, 1, LWT_COL_EDGE_GEOM);
3509  if ( i == -1 )
3510  {
3511  _lwt_release_edges(oldedge, 1);
3512  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3513  return -1;
3514  }
3515  if ( ! i )
3516  {
3517  _lwt_release_edges(oldedge, 1);
3518  lwerror("Unexpected error: %d edges updated when expecting 1", i);
3519  return -1;
3520  }
3521 
3522  /*
3523  * Check edge adjacency after
3524  */
3525  edgeend span_post, epan_post;
3526  i = _lwt_InitEdgeEndByLine(&span_post, &epan_post, geom, &p1, &p2);
3527  if ( i ) return -1; /* lwerror should have been raised */
3528  /* initialize epan_post.myaz and epan_post.myaz */
3529  i = _lwt_InitEdgeEndByLine(&span_post, &epan_post,
3530  geom, &p1, &p2);
3531  if ( i ) return -1; /* lwerror should have been raised */
3532  _lwt_FindAdjacentEdges( topo, oldedge->start_node, &span_post,
3533  isclosed ? &epan_post : NULL, edge_id );
3534  _lwt_FindAdjacentEdges( topo, oldedge->end_node, &epan_post,
3535  isclosed ? &span_post : NULL, edge_id );
3536 
3537  LWDEBUGF(1, "edges adjacent to new edge are %" LWTFMT_ELEMID
3538  " and %" LWTFMT_ELEMID " (first point), %" LWTFMT_ELEMID
3539  " and %" LWTFMT_ELEMID " (last point)",
3540  span_pre.nextCW, span_pre.nextCCW,
3541  epan_pre.nextCW, epan_pre.nextCCW);
3542 
3543 
3544  /* Bail out if next CW or CCW edge on start node changed */
3545  if ( span_pre.nextCW != span_post.nextCW ||
3546  span_pre.nextCCW != span_post.nextCCW )
3547  {{
3548  LWT_ELEMID nid = oldedge->start_node;
3549  _lwt_release_edges(oldedge, 1);
3550  lwerror("Edge changed disposition around start node %"
3551  LWTFMT_ELEMID, nid);
3552  return -1;
3553  }}
3554 
3555  /* Bail out if next CW or CCW edge on end node changed */
3556  if ( epan_pre.nextCW != epan_post.nextCW ||
3557  epan_pre.nextCCW != epan_post.nextCCW )
3558  {{
3559  LWT_ELEMID nid = oldedge->end_node;
3560  _lwt_release_edges(oldedge, 1);
3561  lwerror("Edge changed disposition around end node %"
3562  LWTFMT_ELEMID, nid);
3563  return -1;
3564  }}
3565 
3566  /*
3567  -- Update faces MBR of left and right faces
3568  -- TODO: think about ways to optimize this part, like see if
3569  -- the old edge geometry partecipated in the definition
3570  -- of the current MBR (for shrinking) or the new edge MBR
3571  -- would be larger than the old face MBR...
3572  --
3573  */
3574  int facestoupdate = 0;
3575  LWT_ISO_FACE faces[2];
3576  LWGEOM *nface1 = NULL;
3577  LWGEOM *nface2 = NULL;
3578  if ( oldedge->face_left != 0 )
3579  {
3580  nface1 = lwt_GetFaceGeometry(topo, oldedge->face_left);
3581  if ( ! nface1 )
3582  {
3583  lwerror("lwt_ChangeEdgeGeom could not construct face %"
3584  PRId64 ", on the left of edge %" PRId64,
3585  oldedge->face_left, edge_id);
3586  return -1;
3587  }
3588 #if 0
3589  {
3590  size_t sz;
3591  char *wkt = lwgeom_to_wkt(nface1, WKT_EXTENDED, 2, &sz);
3592  LWDEBUGF(1, "new geometry of face left (%d): %s", (int)oldedge->face_left, wkt);
3593  lwfree(wkt);
3594  }
3595 #endif
3596  lwgeom_add_bbox(nface1);
3597  faces[facestoupdate].face_id = oldedge->face_left;
3598  /* ownership left to nface */
3599  faces[facestoupdate++].mbr = nface1->bbox;
3600  }
3601  if ( oldedge->face_right != 0
3602  /* no need to update twice the same face.. */
3603  && oldedge->face_right != oldedge->face_left )
3604  {
3605  nface2 = lwt_GetFaceGeometry(topo, oldedge->face_right);
3606  if ( ! nface2 )
3607  {
3608  lwerror("lwt_ChangeEdgeGeom could not construct face %"
3609  PRId64 ", on the right of edge %" PRId64,
3610  oldedge->face_right, edge_id);
3611  return -1;
3612  }
3613 #if 0
3614  {
3615  size_t sz;
3616  char *wkt = lwgeom_to_wkt(nface2, WKT_EXTENDED, 2, &sz);
3617  LWDEBUGF(1, "new geometry of face right (%d): %s", (int)oldedge->face_right, wkt);
3618  lwfree(wkt);
3619  }
3620 #endif
3621  lwgeom_add_bbox(nface2);
3622  faces[facestoupdate].face_id = oldedge->face_right;
3623  faces[facestoupdate++].mbr = nface2->bbox; /* ownership left to nface */
3624  }
3625  LWDEBUGF(1, "%d faces to update", facestoupdate);
3626  if ( facestoupdate )
3627  {
3628  i = lwt_be_updateFacesById( topo, &(faces[0]), facestoupdate );
3629  if ( i != facestoupdate )
3630  {
3631  if ( nface1 ) lwgeom_free(nface1);
3632  if ( nface2 ) lwgeom_free(nface2);
3633  _lwt_release_edges(oldedge, 1);
3634  if ( i == -1 )
3635  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
3636  else
3637  lwerror("Unexpected error: %d faces found when expecting 1", i);
3638  return -1;
3639  }
3640  }
3641  if ( nface1 ) lwgeom_free(nface1);
3642  if ( nface2 ) lwgeom_free(nface2);
3643 
3644  LWDEBUG(1, "all done, cleaning up edges");
3645 
3646  _lwt_release_edges(oldedge, 1);
3647  return 0; /* success */
3648 }
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:593
static void _lwt_release_nodes(LWT_ISO_NODE *nodes, int num_nodes)
Definition: lwgeom_topo.c:481
static int _lwt_GetInteriorEdgePoint(const LWLINE *edge, POINT2D *ip)
Definition: lwgeom_topo.c:1719
LWT_ELEMID face_left
GBOX * bbox
Definition: liblwgeom.h:382
GBOX * bbox
Definition: liblwgeom.h:404
static int lwt_be_updateEdgesById(LWT_TOPOLOGY *topo, const LWT_ISO_EDGE *edges, int numedges, int upd_fields)
Definition: lwgeom_topo.c:310
LWPOINT * geom
char * lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out)
WKT emitter function.
Definition: lwout_wkt.c:655
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
Definition: lwutil.c:61
void lwfree(void *mem)
Definition: lwutil.c:214
int npoints
Definition: liblwgeom.h:355
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1050
char lwgeom_geos_errmsg[LWGEOM_GEOS_ERRMSG_MAXSIZE]
LWLINE * geom
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:50
LWT_ISO_EDGE * lwt_be_getEdgeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, int *numelems, int fields)
Definition: lwgeom_topo.c:229
LWT_ELEMID nextCCW
Definition: lwgeom_topo.c:1397
double x
Definition: liblwgeom.h:312
int p2d_same(const POINT2D *p1, const POINT2D *p2)
Definition: lwalgorithm.c:35
int ptarray_isccw(const POINTARRAY *pa)
Definition: ptarray.c:1026
#define WKT_ISO
Definition: liblwgeom.h:1939
static int lwt_be_updateFacesById(LWT_TOPOLOGY *topo, const LWT_ISO_FACE *faces, int numfaces)
Definition: lwgeom_topo.c:302
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:249
void lwgeom_geos_error(const char *fmt,...)
const LWT_BE_IFACE * be_iface
LWT_ELEMID face_id
LWT_ELEMID face_right
static GEOSGeometry * _lwt_EdgeMotionArea(LWLINE *geom, int isclosed)
Definition: lwgeom_topo.c:3212
#define LWT_COL_EDGE_ALL
LWT_ELEMID node_id
double y
Definition: liblwgeom.h:312
LWT_ELEMID edge_id
int getPoint2d_p(const POINTARRAY *pa, int n, POINT2D *point)
Definition: lwgeom_api.c:448
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, int autofix)
int gbox_union(const GBOX *g1, const GBOX *g2, GBOX *gout)
Update the output GBOX to be large enough to include both inputs.
Definition: g_box.c:111
#define WKT_EXTENDED
Definition: liblwgeom.h:1941
static int _lwt_FindAdjacentEdges(LWT_TOPOLOGY *topo, LWT_ELEMID node, edgeend *data, edgeend *other, int myedge_id)
Definition: lwgeom_topo.c:1511
LWGEOM * lwt_GetFaceGeometry(LWT_TOPOLOGY *topo, LWT_ELEMID faceid)
Return the geometry of a face.
Definition: lwgeom_topo.c:2828
#define LWT_COL_NODE_ALL
LWT_ELEMID start_node
int lwgeom_is_simple(const LWGEOM *lwgeom)
static void _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
Definition: lwgeom_topo.c:471
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition: lwgeom.c:599
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:254
LWT_INT64 LWT_ELEMID
Identifier of topology element.
#define LWT_COL_EDGE_GEOM
static int _lwt_InitEdgeEndByLine(edgeend *fee, edgeend *lee, LWLINE *edge, POINT2D *fp, POINT2D *lp)
Definition: lwgeom_topo.c:1453
LWT_ELEMID nextCW
Definition: lwgeom_topo.c:1393
LWT_ELEMID end_node
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:55
static LWT_ISO_NODE * lwt_be_getNodeWithinBox2D(const LWT_TOPOLOGY *topo, const GBOX *box, int *numelems, int fields, int limit)
Definition: lwgeom_topo.c:175
const char * lwt_be_lastErrorMessage(const LWT_BE_IFACE *be)
Definition: lwgeom_topo.c:124
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:74
#define LWTFMT_ELEMID
Definition: lwgeom_topo.c:36
POINTARRAY * points
Definition: liblwgeom.h:406

Here is the call graph for this function:

Here is the caller graph for this function: