PostGIS  2.5.0beta1dev-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 3171 of file lwgeom_topo.c.

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().

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