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

◆ _lwt_AddEdge()

static LWT_ELEMID _lwt_AddEdge ( LWT_TOPOLOGY topo,
LWT_ELEMID  start_node,
LWT_ELEMID  end_node,
LWLINE geom,
int  skipChecks,
int  modFace 
)
static
Parameters
modFacecan be 0 - have two new faces replace a split face 1 - modify a split face, adding a new one -1 - do not check at all for face splitting

Definition at line 2303 of file lwgeom_topo.c.

2306{
2307 LWT_ISO_EDGE newedge;
2308 LWGEOM *cleangeom;
2309 edgeend span; /* start point analysis */
2310 edgeend epan; /* end point analysis */
2311 POINT2D p1, pn, p2;
2312 POINTARRAY *pa;
2313 LWT_ELEMID node_ids[2];
2314 const LWPOINT *start_node_geom = NULL;
2315 const LWPOINT *end_node_geom = NULL;
2316 uint64_t num_nodes;
2317 LWT_ISO_NODE *endpoints;
2318 uint64_t i;
2319 LWT_ELEMID prev_left;
2320 LWT_ELEMID prev_right;
2321 LWT_ISO_EDGE seledge;
2322 LWT_ISO_EDGE updedge;
2323
2324 if ( ! skipChecks )
2325 {
2326 /* curve must be simple */
2327 if ( ! lwgeom_is_simple(lwline_as_lwgeom(geom)) )
2328 {
2329 lwerror("SQL/MM Spatial exception - curve not simple");
2330 return -1;
2331 }
2332 }
2333
2334 newedge.start_node = start_node;
2335 newedge.end_node = end_node;
2336 newedge.geom = geom;
2337 newedge.face_left = -1;
2338 newedge.face_right = -1;
2339 /* TODO: should do the repeated points removal in 2D space */
2340 cleangeom = lwgeom_remove_repeated_points( lwline_as_lwgeom(geom), 0 );
2341
2342 pa = lwgeom_as_lwline(cleangeom)->points;
2343 if ( pa->npoints < 2 ) {
2344 lwgeom_free(cleangeom);
2345 lwerror("Invalid edge (no two distinct vertices exist)");
2346 return -1;
2347 }
2348
2349 /* Initialize endpoint info (some of that ) */
2350 span.cwFace = span.ccwFace =
2351 epan.cwFace = epan.ccwFace = -1;
2352
2353 /* Compute azimuth of first edge end on start node */
2354 getPoint2d_p(pa, 0, &p1);
2355 if ( ! _lwt_FirstDistinctVertex2D(pa, &p1, 0, 1, &pn) )
2356 {
2357 lwgeom_free(cleangeom);
2358 lwerror("Invalid edge (no two distinct vertices exist)");
2359 return -1;
2360 }
2361 if ( ! azimuth_pt_pt(&p1, &pn, &span.myaz) ) {
2362 lwgeom_free(cleangeom);
2363 lwerror("error computing azimuth of first edgeend [%.15g %.15g,%.15g %.15g]",
2364 p1.x, p1.y, pn.x, pn.y);
2365 return -1;
2366 }
2367 LWDEBUGF(1, "edge's start node is %g,%g", p1.x, p1.y);
2368
2369 /* Compute azimuth of last edge end on end node */
2370 getPoint2d_p(pa, pa->npoints-1, &p2);
2371 if ( ! _lwt_FirstDistinctVertex2D(pa, &p2, pa->npoints-1, -1, &pn) )
2372 {
2373 lwgeom_free(cleangeom);
2374 /* This should never happen as we checked the edge while computing first edgend */
2375 lwerror("Invalid clean edge (no two distinct vertices exist) - should not happen");
2376 return -1;
2377 }
2378 lwgeom_free(cleangeom);
2379 if ( ! azimuth_pt_pt(&p2, &pn, &epan.myaz) ) {
2380 lwerror("error computing azimuth of last edgeend [%.15g %.15g,%.15g %.15g]",
2381 p2.x, p2.y, pn.x, pn.y);
2382 return -1;
2383 }
2384 LWDEBUGF(1, "edge's end node is %g,%g", p2.x, p2.y);
2385
2386 /*
2387 * Check endpoints existence, match with Curve geometry
2388 * and get face information (if any)
2389 */
2390
2391 if ( start_node != end_node ) {
2392 num_nodes = 2;
2393 node_ids[0] = start_node;
2394 node_ids[1] = end_node;
2395 } else {
2396 num_nodes = 1;
2397 node_ids[0] = start_node;
2398 }
2399
2400 endpoints = lwt_be_getNodeById( topo, node_ids, &num_nodes, LWT_COL_NODE_ALL );
2401 if (num_nodes == UINT64_MAX)
2402 {
2404 return -1;
2405 }
2406 for ( i=0; i<num_nodes; ++i )
2407 {
2408 LWT_ISO_NODE* node = &(endpoints[i]);
2409 if ( modFace != -1 && node->containing_face != -1 )
2410 {
2411 if ( newedge.face_left == -1 )
2412 {
2413 newedge.face_left = newedge.face_right = node->containing_face;
2414 }
2415 else if ( newedge.face_left != node->containing_face )
2416 {
2417 _lwt_release_nodes(endpoints, num_nodes);
2418 lwerror("SQL/MM Spatial exception - geometry crosses an edge"
2419 " (endnodes in faces %" LWTFMT_ELEMID " and %" LWTFMT_ELEMID ")",
2420 newedge.face_left, node->containing_face);
2421 }
2422 }
2423
2424 LWDEBUGF(1, "Node %" LWTFMT_ELEMID ", with geom %p (looking for %"
2425 LWTFMT_ELEMID " and %" LWTFMT_ELEMID ")",
2426 node->node_id, node->geom, start_node, end_node);
2427 if ( node->node_id == start_node ) {
2428 start_node_geom = node->geom;
2429 }
2430 if ( node->node_id == end_node ) {
2431 end_node_geom = node->geom;
2432 }
2433 }
2434
2435 if ( ! skipChecks )
2436 {
2437 if ( ! start_node_geom )
2438 {
2439 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2440 lwerror("SQL/MM Spatial exception - non-existent node");
2441 return -1;
2442 }
2443 else
2444 {
2445 pa = start_node_geom->point;
2446 getPoint2d_p(pa, 0, &pn);
2447 if ( ! P2D_SAME_STRICT(&pn, &p1) )
2448 {
2449 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2450 lwerror("SQL/MM Spatial exception"
2451 " - start node not geometry start point."
2452 //" - start node not geometry start point (%g,%g != %g,%g).", pn.x, pn.y, p1.x, p1.y
2453 );
2454 return -1;
2455 }
2456 }
2457
2458 if ( ! end_node_geom )
2459 {
2460 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2461 lwerror("SQL/MM Spatial exception - non-existent node");
2462 return -1;
2463 }
2464 else
2465 {
2466 pa = end_node_geom->point;
2467 getPoint2d_p(pa, 0, &pn);
2468 if ( ! P2D_SAME_STRICT(&pn, &p2) )
2469 {
2470 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2471 lwerror("SQL/MM Spatial exception"
2472 " - end node not geometry end point."
2473 //" - end node not geometry end point (%g,%g != %g,%g).", pn.x, pn.y, p2.x, p2.y
2474 );
2475 return -1;
2476 }
2477 }
2478
2479 if ( num_nodes ) _lwt_release_nodes(endpoints, num_nodes);
2480
2481 if ( _lwt_CheckEdgeCrossing( topo, start_node, end_node, geom, 0 ) )
2482 return -1;
2483
2484 } /* ! skipChecks */
2485
2486 /*
2487 * All checks passed, time to prepare the new edge
2488 */
2489
2490 newedge.edge_id = lwt_be_getNextEdgeId( topo );
2491 if ( newedge.edge_id == -1 ) {
2493 return -1;
2494 }
2495
2496 /* Find adjacent edges to each endpoint */
2497 int isclosed = start_node == end_node;
2498 int found;
2499 found = _lwt_FindAdjacentEdges( topo, start_node, &span,
2500 isclosed ? &epan : NULL, -1 );
2501 if ( found ) {
2502 span.was_isolated = 0;
2503 newedge.next_right = span.nextCW ? span.nextCW : -newedge.edge_id;
2504 prev_left = span.nextCCW ? -span.nextCCW : newedge.edge_id;
2505 LWDEBUGF(1, "New edge %" LWTFMT_ELEMID " is connected on start node, "
2506 "next_right is %" LWTFMT_ELEMID
2507 ", prev_left is %" LWTFMT_ELEMID,
2508 newedge.edge_id, newedge.next_right, prev_left);
2509 if ( modFace != -1 )
2510 {
2511 if ( newedge.face_right == -1 ) {
2512 newedge.face_right = span.cwFace;
2513 }
2514 if ( newedge.face_left == -1 ) {
2515 newedge.face_left = span.ccwFace;
2516 }
2517 }
2518 } else {
2519 span.was_isolated = 1;
2520 newedge.next_right = isclosed ? -newedge.edge_id : newedge.edge_id;
2521 prev_left = isclosed ? newedge.edge_id : -newedge.edge_id;
2522 LWDEBUGF(1, "New edge %" LWTFMT_ELEMID " is isolated on start node, "
2523 "next_right is %" LWTFMT_ELEMID
2524 ", prev_left is %" LWTFMT_ELEMID,
2525 newedge.edge_id, newedge.next_right, prev_left);
2526 }
2527
2528 found = _lwt_FindAdjacentEdges( topo, end_node, &epan,
2529 isclosed ? &span : NULL, -1 );
2530 if ( found ) {
2531 epan.was_isolated = 0;
2532 newedge.next_left = epan.nextCW ? epan.nextCW : newedge.edge_id;
2533 prev_right = epan.nextCCW ? -epan.nextCCW : -newedge.edge_id;
2534 LWDEBUGF(1, "New edge %" LWTFMT_ELEMID " is connected on end node, "
2535 "next_left is %" LWTFMT_ELEMID
2536 ", prev_right is %" LWTFMT_ELEMID,
2537 newedge.edge_id, newedge.next_left, prev_right);
2538 if ( modFace != -1 )
2539 {
2540 if ( newedge.face_right == -1 ) {
2541 newedge.face_right = span.ccwFace;
2542 } else if ( newedge.face_right != epan.ccwFace ) {
2543 /* side-location conflict */
2544 lwerror("Side-location conflict: "
2545 "new edge starts in face"
2546 " %" LWTFMT_ELEMID " and ends in face"
2547 " %" LWTFMT_ELEMID,
2548 newedge.face_right, epan.ccwFace
2549 );
2550 return -1;
2551 }
2552 if ( newedge.face_left == -1 ) {
2553 newedge.face_left = span.cwFace;
2554 } else if ( newedge.face_left != epan.cwFace ) {
2555 /* side-location conflict */
2556 lwerror("Side-location conflict: "
2557 "new edge starts in face"
2558 " %" LWTFMT_ELEMID " and ends in face"
2559 " %" LWTFMT_ELEMID,
2560 newedge.face_left, epan.cwFace
2561 );
2562 return -1;
2563 }
2564 }
2565 } else {
2566 epan.was_isolated = 1;
2567 newedge.next_left = isclosed ? newedge.edge_id : -newedge.edge_id;
2568 prev_right = isclosed ? -newedge.edge_id : newedge.edge_id;
2569 LWDEBUGF(1, "New edge %" LWTFMT_ELEMID " is isolated on end node, "
2570 "next_left is %" LWTFMT_ELEMID
2571 ", prev_right is %" LWTFMT_ELEMID,
2572 newedge.edge_id, newedge.next_left, prev_right);
2573 }
2574
2575 /*
2576 * If we don't have faces setup by now we must have encountered
2577 * a malformed topology (no containing_face on isolated nodes, no
2578 * left/right faces on adjacent edges or mismatching values)
2579 */
2580 if ( modFace > -1 )
2581 {
2582 if ( newedge.face_left != newedge.face_right )
2583 {
2584 lwerror("Left(%" LWTFMT_ELEMID ")/right(%" LWTFMT_ELEMID ")"
2585 " faces mismatch: invalid topology ?",
2586 newedge.face_left, newedge.face_right);
2587 return -1;
2588 }
2589 else if ( newedge.face_left == -1 )
2590 {
2591 lwerror("Could not derive edge face from linked primitives:"
2592 " invalid topology ?");
2593 return -1;
2594 }
2595 }
2596
2597 /*
2598 * Insert the new edge, and update all linking
2599 */
2600
2601 int ret = lwt_be_insertEdges(topo, &newedge, 1);
2602 if ( ret == -1 ) {
2604 return -1;
2605 } else if ( ret == 0 ) {
2606 lwerror("Insertion of split edge failed (no reason)");
2607 return -1;
2608 }
2609
2610 int updfields;
2611
2612 /* Link prev_left to us
2613 * (if it's not us already) */
2614 if ( llabs(prev_left) != newedge.edge_id )
2615 {
2616 if ( prev_left > 0 )
2617 {
2618 /* its next_left_edge is us */
2619 updfields = LWT_COL_EDGE_NEXT_LEFT;
2620 updedge.next_left = newedge.edge_id;
2621 seledge.edge_id = prev_left;
2622 }
2623 else
2624 {
2625 /* its next_right_edge is us */
2626 updfields = LWT_COL_EDGE_NEXT_RIGHT;
2627 updedge.next_right = newedge.edge_id;
2628 seledge.edge_id = -prev_left;
2629 }
2630
2631 ret = lwt_be_updateEdges(topo,
2632 &seledge, LWT_COL_EDGE_EDGE_ID,
2633 &updedge, updfields,
2634 NULL, 0);
2635 if ( ret == -1 ) {
2637 return -1;
2638 }
2639 }
2640
2641 /* Link prev_right to us
2642 * (if it's not us already) */
2643 if ( llabs(prev_right) != newedge.edge_id )
2644 {
2645 if ( prev_right > 0 )
2646 {
2647 /* its next_left_edge is -us */
2648 updfields = LWT_COL_EDGE_NEXT_LEFT;
2649 updedge.next_left = -newedge.edge_id;
2650 seledge.edge_id = prev_right;
2651 }
2652 else
2653 {
2654 /* its next_right_edge is -us */
2655 updfields = LWT_COL_EDGE_NEXT_RIGHT;
2656 updedge.next_right = -newedge.edge_id;
2657 seledge.edge_id = -prev_right;
2658 }
2659
2660 ret = lwt_be_updateEdges(topo,
2661 &seledge, LWT_COL_EDGE_EDGE_ID,
2662 &updedge, updfields,
2663 NULL, 0);
2664 if ( ret == -1 ) {
2666 return -1;
2667 }
2668 }
2669
2670 /* NOT IN THE SPECS...
2671 * set containing_face = null for start_node and end_node
2672 * if they where isolated
2673 *
2674 */
2675 LWT_ISO_NODE updnode, selnode;
2676 updnode.containing_face = -1;
2677 if ( span.was_isolated )
2678 {
2679 selnode.node_id = start_node;
2680 ret = lwt_be_updateNodes(topo,
2681 &selnode, LWT_COL_NODE_NODE_ID,
2683 NULL, 0);
2684 if ( ret == -1 ) {
2686 return -1;
2687 }
2688 }
2689 if ( epan.was_isolated )
2690 {
2691 selnode.node_id = end_node;
2692 ret = lwt_be_updateNodes(topo,
2693 &selnode, LWT_COL_NODE_NODE_ID,
2695 NULL, 0);
2696 if ( ret == -1 ) {
2698 return -1;
2699 }
2700 }
2701
2702 /* Check face splitting, if required */
2703
2704 if ( modFace > -1 ) {
2705
2706 if ( ! isclosed && ( epan.was_isolated || span.was_isolated ) )
2707 {
2708 LWDEBUG(1, "New edge is dangling, so it cannot split any face");
2709 return newedge.edge_id; /* no split */
2710 }
2711
2712 int newface1 = -1;
2713
2714 /* IDEA: avoid building edge ring if input is closed, which means we
2715 * know in advance it splits a face */
2716
2717 if ( ! modFace )
2718 {
2719 newface1 = _lwt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 0 );
2720 if ( newface1 == 0 ) {
2721 LWDEBUG(1, "New edge does not split any face");
2722 return newedge.edge_id; /* no split */
2723 }
2724 }
2725
2726 int newface = _lwt_AddFaceSplit( topo, newedge.edge_id,
2727 newedge.face_left, 0 );
2728 if ( modFace )
2729 {
2730 if ( newface == 0 ) {
2731 LWDEBUG(1, "New edge does not split any face");
2732 return newedge.edge_id; /* no split */
2733 }
2734
2735 if ( newface < 0 )
2736 {
2737 /* face on the left is the universe face */
2738 /* must be forming a maximal ring in universal face */
2739 newface = _lwt_AddFaceSplit( topo, -newedge.edge_id,
2740 newedge.face_left, 0 );
2741 if ( newface < 0 ) return newedge.edge_id; /* no split */
2742 }
2743 else
2744 {
2745 _lwt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 1 );
2746 }
2747 }
2748
2749 /*
2750 * Update topogeometries, if needed
2751 */
2752 if ( newedge.face_left != 0 )
2753 {
2754 ret = lwt_be_updateTopoGeomFaceSplit(topo, newedge.face_left,
2755 newface, newface1);
2756 if ( ret == 0 ) {
2758 return -1;
2759 }
2760
2761 if ( ! modFace )
2762 {
2763 /* drop old face from the face table */
2764 ret = lwt_be_deleteFacesById(topo, &(newedge.face_left), 1);
2765 if ( ret == -1 ) {
2767 return -1;
2768 }
2769 }
2770 }
2771
2772 } // end of face split checking
2773
2774 return newedge.edge_id;
2775}
int azimuth_pt_pt(const POINT2D *p1, const POINT2D *p2, double *ret)
Compute the azimuth of segment AB in radians.
Definition measures.c:2408
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1218
int lwgeom_is_simple(const LWGEOM *lwgeom)
int getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point)
Definition lwgeom_api.c:342
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition lwgeom.c:339
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition lwgeom.c:179
LWGEOM * lwgeom_remove_repeated_points(const LWGEOM *in, double tolerance)
Definition lwgeom.c:1534
#define P2D_SAME_STRICT(a, b)
LWT_INT64 LWT_ELEMID
Identifier of topology element.
#define LWT_COL_EDGE_NEXT_RIGHT
#define LWT_COL_NODE_CONTAINING_FACE
#define LWT_COL_EDGE_EDGE_ID
Edge fields.
#define LWT_COL_EDGE_NEXT_LEFT
#define LWT_COL_NODE_NODE_ID
Node fields.
#define LWT_COL_NODE_ALL
#define PGTOPO_BE_ERROR()
#define LWTFMT_ELEMID
#define LWDEBUG(level, msg)
Definition lwgeom_log.h:101
#define LWDEBUGF(level, msg,...)
Definition lwgeom_log.h:106
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
static int lwt_be_deleteFacesById(const LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t numelems)
LWT_ELEMID lwt_be_getNextEdgeId(LWT_TOPOLOGY *topo)
static int lwt_be_updateTopoGeomFaceSplit(LWT_TOPOLOGY *topo, LWT_ELEMID split_face, LWT_ELEMID new_face1, LWT_ELEMID new_face2)
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)
int lwt_be_updateEdges(LWT_TOPOLOGY *topo, const LWT_ISO_EDGE *sel_edge, int sel_fields, const LWT_ISO_EDGE *upd_edge, int upd_fields, const LWT_ISO_EDGE *exc_edge, int exc_fields)
static LWT_ELEMID _lwt_AddFaceSplit(LWT_TOPOLOGY *topo, LWT_ELEMID sedge, LWT_ELEMID face, int mbr_only)
int lwt_be_insertEdges(LWT_TOPOLOGY *topo, LWT_ISO_EDGE *edge, uint64_t numelems)
static int _lwt_FirstDistinctVertex2D(const POINTARRAY *pa, const POINT2D *ref, int from, int dir, POINT2D *op)
LWT_ISO_NODE * lwt_be_getNodeById(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
static int lwt_be_updateNodes(LWT_TOPOLOGY *topo, const LWT_ISO_NODE *sel_node, int sel_fields, const LWT_ISO_NODE *upd_node, int upd_fields, const LWT_ISO_NODE *exc_node, int exc_fields)
static int _lwt_FindAdjacentEdges(LWT_TOPOLOGY *topo, LWT_ELEMID node, edgeend *data, edgeend *other, LWT_ELEMID myedge_id)
POINTARRAY * points
Definition liblwgeom.h:483
POINTARRAY * point
Definition liblwgeom.h:471
LWT_ELEMID face_right
LWT_ELEMID next_right
LWT_ELEMID end_node
LWT_ELEMID face_left
LWT_ELEMID next_left
LWT_ELEMID edge_id
LWT_ELEMID start_node
LWT_ELEMID node_id
LWT_ELEMID containing_face
LWPOINT * geom
double y
Definition liblwgeom.h:390
double x
Definition liblwgeom.h:390
uint32_t npoints
Definition liblwgeom.h:427
double myaz
LWT_ELEMID nextCCW
LWT_ELEMID ccwFace
LWT_ELEMID cwFace
LWT_ELEMID nextCW

References _lwt_AddFaceSplit(), _lwt_CheckEdgeCrossing(), _lwt_FindAdjacentEdges(), _lwt_FirstDistinctVertex2D(), _lwt_release_nodes(), azimuth_pt_pt(), edgeend_t::ccwFace, LWT_ISO_NODE::containing_face, edgeend_t::cwFace, LWT_ISO_EDGE::edge_id, LWT_ISO_EDGE::end_node, LWT_ISO_EDGE::face_left, LWT_ISO_EDGE::face_right, LWT_ISO_NODE::geom, LWT_ISO_EDGE::geom, getPoint2d_p(), LWDEBUG, LWDEBUGF, lwerror(), lwgeom_as_lwline(), lwgeom_free(), lwgeom_is_simple(), lwgeom_remove_repeated_points(), lwline_as_lwgeom(), lwt_be_deleteFacesById(), lwt_be_getNextEdgeId(), lwt_be_getNodeById(), lwt_be_insertEdges(), lwt_be_updateEdges(), lwt_be_updateNodes(), lwt_be_updateTopoGeomFaceSplit(), LWT_COL_EDGE_EDGE_ID, LWT_COL_EDGE_NEXT_LEFT, LWT_COL_EDGE_NEXT_RIGHT, LWT_COL_NODE_ALL, LWT_COL_NODE_CONTAINING_FACE, LWT_COL_NODE_NODE_ID, LWTFMT_ELEMID, edgeend_t::myaz, LWT_ISO_EDGE::next_left, LWT_ISO_EDGE::next_right, edgeend_t::nextCCW, edgeend_t::nextCW, LWT_ISO_NODE::node_id, POINTARRAY::npoints, P2D_SAME_STRICT, PGTOPO_BE_ERROR, LWPOINT::point, LWLINE::points, LWT_ISO_EDGE::start_node, edgeend_t::was_isolated, POINT2D::x, and POINT2D::y.

Referenced by _lwt_AddLineEdge(), lwt_AddEdgeModFace(), and lwt_AddEdgeNewFaces().

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