PostGIS  2.4.9dev-r@@SVN_REVISION@@

◆ _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

Definition at line 2339 of file lwgeom_topo.c.

References _lwt_AddFaceSplit(), _lwt_CheckEdgeCrossing(), _lwt_FindAdjacentEdges(), _lwt_FirstDistinctVertex2D(), _lwt_release_nodes(), azimuth_pt_pt(), LWT_TOPOLOGY_T::be_iface, 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_lastErrorMessage(), 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(), LWPOINT::point, LWLINE::points, LWT_ISO_EDGE::start_node, edgeend_t::was_isolated, POINT2D::x, and POINT2D::y.

Referenced by lwt_AddEdgeModFace(), and lwt_AddEdgeNewFaces().

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