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

◆ lwt_GetFaceContainingPoint()

LWT_ELEMID lwt_GetFaceContainingPoint ( LWT_TOPOLOGY topo,
const LWPOINT pt 
)

Find the face-id of the face properly containing a given point.

Parameters
topothe topology to operate on
pointthe point to use for query
Returns
a face identifier if one is found (0 if universe), -1 on error (point intersects non-dangling edge). The liblwgeom error handler will be invoked in case of error.

Definition at line 7669 of file lwgeom_topo.c.

7670{
7671 LWT_ISO_EDGE* closestEdge;
7672 LWT_ISO_EDGE* edges;
7673 uint64_t numedges, i;
7674 const POINT2D *queryPoint;
7675 const POINT2D *closestPointOnEdge = NULL;
7676 uint32_t closestSegmentIndex;
7677 int closestSegmentSide;
7678 uint32_t closestPointVertex;
7679 const POINT2D *closestSegmentP0, *closestSegmentP1;
7680 LWT_ELEMID closestNode = 0;
7681 double dist;
7682 LWT_ELEMID containingFace = -1;
7683
7684 closestEdge = lwt_be_getClosestEdge( topo, pt, &numedges,
7691 );
7692 if (numedges == UINT64_MAX)
7693 {
7695 /* cberror(topo->be_data, "Error from cb_getClosestEdge"); */
7696 return -1;
7697 }
7698 if (numedges == 0)
7699 {
7700 /* If there are no edges the point is in the universal face */
7701 return 0;
7702 }
7703
7704 if ( closestEdge->face_left < 0 )
7705 {
7706 lwerror("Closest edge %" LWTFMT_ELEMID " has invalid face %" LWTFMT_ELEMID
7707 " on its left side", closestEdge->edge_id, closestEdge->face_left);
7708 _lwt_release_edges(closestEdge, 1);
7709 return -1;
7710 }
7711
7712 if ( closestEdge->face_right < 0 )
7713 {
7714 lwerror("Closest edge %" LWTFMT_ELEMID " has invalid face %" LWTFMT_ELEMID
7715 " on its right side", closestEdge->edge_id, closestEdge->face_right);
7716 _lwt_release_edges(closestEdge, 1);
7717 return -1;
7718 }
7719
7720 if ( closestEdge->geom->points->npoints < 2 )
7721 {
7722 lwerror("Corrupted topology: geometry of edge %" LWTFMT_ELEMID " is EMPTY",
7723 closestEdge->edge_id);
7724 _lwt_release_edges(closestEdge, 1);
7725 return -1;
7726 }
7727
7728 LWDEBUGGF(2, lwline_as_lwgeom(closestEdge->geom), "Closest edge %" LWTFMT_ELEMID, closestEdge->edge_id);
7729
7730 /* Find closest segment of edge to the point */
7731 queryPoint = getPoint2d_cp(pt->point, 0);
7732 closestSegmentIndex = ptarray_closest_segment_2d(closestEdge->geom->points, queryPoint, &dist);
7733 LWDEBUGF(1, "Closest segment on edge %" LWTFMT_ELEMID " is %d (dist %g)", closestEdge->edge_id, closestSegmentIndex, dist);
7734 closestSegmentP0 = getPoint2d_cp(closestEdge->geom->points, closestSegmentIndex);
7735 closestSegmentP1 = getPoint2d_cp(closestEdge->geom->points, closestSegmentIndex + 1);
7736 LWDEBUGF(1, "Closest segment on edge %" LWTFMT_ELEMID " is LINESTRING(%g %g, %g %g)",
7737 closestEdge->edge_id,
7738 closestSegmentP0->x,
7739 closestSegmentP0->y,
7740 closestSegmentP1->x,
7741 closestSegmentP1->y
7742 );
7743
7744 /*
7745 * We use comp.graphics.algorithms Frequently Asked Questions method
7746 *
7747 * (1) AC dot AB
7748 * r = ----------
7749 * ||AB||^2
7750 * r has the following meaning:
7751 * r=0 P = A
7752 * r=1 P = B
7753 * r<0 P is on the backward extension of AB
7754 * r>1 P is on the forward extension of AB
7755 * 0<r<1 P is interior to AB
7756 *
7757 */
7758 const POINT2D *p = queryPoint;
7759 const POINT2D *A = closestSegmentP0;
7760 const POINT2D *B = closestSegmentP1;
7761 double r = ( (p->x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) );
7762 if ( r <= 0 )
7763 {
7764 closestPointOnEdge = A;
7765 closestPointVertex = closestSegmentIndex;
7766 if ( closestSegmentIndex == 0 )
7767 {
7768 closestNode = closestEdge->start_node;
7769 }
7770 }
7771 else if (r >= 1 )
7772 {
7773 closestPointOnEdge = B;
7774 closestPointVertex = closestSegmentIndex + 1;
7775 if ( closestSegmentIndex + 2 == closestEdge->geom->points->npoints )
7776 {
7777 closestNode = closestEdge->end_node;
7778 }
7779 }
7780 else
7781 {
7782 closestPointVertex = closestEdge->geom->points->npoints;
7783 }
7784
7785 if ( closestNode != 0 )
7786 {
7787 LWDEBUGF(1, "Closest point is node %" LWTFMT_ELEMID, closestNode);
7788 if ( dist == 0 )
7789 {
7790 LWDEBUGF(1, "Query point is node %" LWTFMT_ELEMID, closestNode);
7791 /* Query point is the node
7792 *
7793 * If all edges incident to the node are
7794 * dangling, we can return their common
7795 * side face, otherwise the point will be
7796 * on multiple face boundaries
7797 */
7798 if ( closestEdge->face_left != closestEdge->face_right )
7799 {
7800 _lwt_release_edges(closestEdge, 1);
7801 lwerror("Two or more faces found");
7802 return -1;
7803 }
7804 containingFace = closestEdge->face_left;
7805
7806 /* Check other incident edges */
7807 numedges = 1;
7808 edges = lwt_be_getEdgeByNode( topo, &closestNode, &numedges, LWT_COL_EDGE_FACE_LEFT|LWT_COL_EDGE_FACE_RIGHT );
7809 if (numedges == UINT64_MAX)
7810 {
7812 /* cberror(topo->be_data, "Error from cb_getClosestEdge"); */
7813 _lwt_release_edges(closestEdge, 1);
7814 return -1;
7815 }
7816 for (i=0; i<numedges; ++i)
7817 {
7818 if ( edges[i].face_left != containingFace ||
7819 edges[i].face_right != containingFace )
7820 {
7821 _lwt_release_edges(edges, numedges);
7822 _lwt_release_edges(closestEdge, 1);
7823 lwerror("Two or more faces found");
7824 return -1;
7825 }
7826 }
7827 if (numedges < 1 )
7828 {
7829 lwerror("Unexpected backend return: getEdgeByNode(%" LWTFMT_ELEMID
7830 ") returns no edges when we previously found edge %"
7831 LWTFMT_ELEMID " ending on that node",
7832 closestNode, closestEdge->edge_id);
7833 _lwt_release_edges(edges, numedges);
7834 _lwt_release_edges(closestEdge, 1);
7835 return -1;
7836 }
7837 LWDEBUGF(1, "lwt_be_getEdgeByNode returned %" PRIu64 " edges", numedges);
7838 _lwt_release_edges(edges, numedges);
7839 _lwt_release_edges(closestEdge, 1);
7840 return containingFace;
7841 }
7842
7843 /* Closest point is a node, but query point is NOT on the node */
7844
7845 /* let's do azimuth computation */
7846 edgeend ee;
7847 if ( ! azimuth_pt_pt(closestPointOnEdge, queryPoint, &ee.myaz) ) {
7848 lwerror("error computing azimuth of query point [%.15g %.15g,%.15g %.15g]",
7849 closestPointOnEdge->x, closestPointOnEdge->y,
7850 queryPoint->x, queryPoint->y);
7851 _lwt_release_edges(closestEdge, 1);
7852 return -1;
7853 }
7854
7855 LWDEBUGF(1, "Query point azimuth is %g", ee.myaz);
7856
7857 int found = _lwt_FindAdjacentEdges( topo, closestNode, &ee, NULL, -1 );
7858 if ( ! found ) {
7859 lwerror("Unexpected backend return: _lwt_FindAdjacentEdges(%"
7861 ") found no edges when we previously found edge %"
7862 LWTFMT_ELEMID " ending on that node",
7863 closestNode, closestEdge->edge_id);
7864 _lwt_release_edges(closestEdge, 1);
7865 return -1;
7866 }
7867
7868 _lwt_release_edges(closestEdge, 1);
7869 return ee.cwFace;
7870
7871 }
7872
7873 LWDEBUG(1, "Closest point is NOT a node");
7874
7875 /* If this edge has the same face on the left and right sides
7876 * we found the face containing our query point */
7877 if ( closestEdge->face_left == closestEdge->face_right )
7878 {
7879 LWDEBUGF(1, "Closest point is on a edge with face %" LWTFMT_ELEMID
7880 " on both sides", closestEdge->face_left);
7881
7882 containingFace = closestEdge->face_left;
7883 _lwt_release_edges(closestEdge, 1);
7884 return containingFace;
7885 }
7886
7887 if ( dist == 0 )
7888 {
7889 /* We checked the dangling case above */
7890 _lwt_release_edges(closestEdge, 1);
7891 lwerror("Two or more faces found");
7892 return -1;
7893 }
7894
7895 /* Find on which side of the segment the query point lays */
7896 if ( closestPointVertex != closestEdge->geom->points->npoints )
7897 {
7898 /* Closest point is a vertex of the closest segment */
7899 LWDEBUGF(1, "Closest point is vertex %d of closest segment", closestPointVertex);
7900
7901 /*
7902 * We need to check if rotating clockwise the line
7903 * from previous vertex to closest vertex clockwise
7904 * around the closest vertex encounters
7905 * the line to query point first (which means it's on the left
7906 * of the closest edge) or the line to next vertex first (which
7907 * means the query point is on the right)
7908 */
7909
7910 uint32_t prevVertexIndex = closestPointVertex > 0 ?
7911 closestPointVertex - 1u :
7912 closestEdge->geom->points->npoints - 2u; /* last vertex would be == first vertex, this being a closed edge */
7913
7914 const POINT2D *prevVertex = getPoint2d_cp(
7915 closestEdge->geom->points,
7916 prevVertexIndex
7917 );
7918
7919 LWDEBUGF(1, "Previous vertex %u is POINT(%.15g %.15g)",
7920 prevVertexIndex,
7921 prevVertex->x,
7922 prevVertex->y
7923 );
7924
7925 uint32_t nextVertexIndex = closestPointVertex == closestEdge->geom->points->npoints - 1u ?
7926 1u : /* first point would be == last point, this being a closed edge */
7927 closestPointVertex + 1u;
7928
7929 const POINT2D *nextVertex = getPoint2d_cp(
7930 closestEdge->geom->points,
7931 nextVertexIndex
7932 );
7933
7934 LWDEBUGF(1, "Next vertex %u is POINT(%.15g %.15g)",
7935 nextVertexIndex,
7936 nextVertex->x,
7937 nextVertex->y
7938 );
7939
7940
7941 double azS0; /* azimuth from previous vertex to closestPointVertex */
7942 double azS1; /* azimuth from closestPointVertex to next vertex */
7943 double azSL; /* azimuth from closestPointVertex to query point */
7944
7945 if ( ! azimuth_pt_pt(closestPointOnEdge, prevVertex, &azS0)) {
7946 lwerror("error computing azimuth of segment to closest point [%.15g %.15g,%.15g %.15g]",
7947 closestPointOnEdge->x, closestPointOnEdge->y,
7948 prevVertex->x, prevVertex->y);
7949 _lwt_release_edges(closestEdge, 1);
7950 return -1;
7951 }
7952 if ( ! azimuth_pt_pt(closestPointOnEdge, nextVertex, &azS1)) {
7953 lwerror("error computing azimuth of segment from closest point [%.15g %.15g,%.15g %.15g]",
7954 closestPointOnEdge->x, closestPointOnEdge->y,
7955 nextVertex->x, nextVertex->y);
7956 _lwt_release_edges(closestEdge, 1);
7957 return -1;
7958 }
7959 if ( ! azimuth_pt_pt(closestPointOnEdge, queryPoint, &azSL) ) {
7960 lwerror("error computing azimuth of queryPoint [%.15g %.15g,%.15g %.15g]",
7961 closestPointOnEdge->x, closestPointOnEdge->y,
7962 queryPoint->x, queryPoint->y);
7963 _lwt_release_edges(closestEdge, 1);
7964 return -1;
7965 }
7966
7967 double angle_S0_S1 = azS1 - azS0;
7968 if ( angle_S0_S1 < 0 ) angle_S0_S1 += 2 * M_PI;
7969
7970 double angle_S0_SL = azSL - azS0;
7971 if ( angle_S0_SL < 0 ) angle_S0_SL += 2 * M_PI;
7972
7973 LWDEBUGF(1, "Azimuths from closest (vertex) point: P:%g, N:%g (+%g), Q:%g (+%g)",
7974 azS0,
7975 azS1, angle_S0_S1,
7976 azSL, angle_S0_SL
7977 );
7978 if ( angle_S0_SL < angle_S0_S1 )
7979 {
7980 /* line to query point was encountered first, is on the left */
7981 containingFace = closestEdge->face_left;
7982 }
7983 else
7984 {
7985 /* line to query point was NOT encountered first, is on the right */
7986 containingFace = closestEdge->face_right;
7987 }
7988 }
7989 else
7990 {
7991 /* Closest point is internal to closest segment, we can use
7992 * lw_segment_side */
7993
7994 LWDEBUGF(1, "Closest point is internal to closest segment, calling lw_segment_side((%g,%g),(%g,%g),(%g,%g)",
7995 closestSegmentP0->x,
7996 closestSegmentP0->y,
7997 closestSegmentP1->x,
7998 closestSegmentP1->y,
7999 queryPoint->x,
8000 queryPoint->y
8001 );
8002
8003 closestSegmentSide = lw_segment_side(closestSegmentP0, closestSegmentP1, queryPoint);
8004 LWDEBUGF(1, "Side of closest segment query point falls on: %d (%s)",
8005 closestSegmentSide, closestSegmentSide == -1 ? "left" : closestSegmentSide == 1 ? "right" : "collinear" );
8006
8007 if ( closestSegmentSide == -1 ) /* left */
8008 {
8009 containingFace = closestEdge->face_left;
8010 }
8011 else if ( closestSegmentSide == 1 ) /* right */
8012 {
8013 containingFace = closestEdge->face_right;
8014 }
8015 else
8016 {
8017 lwerror("Unexpected collinearity reported from lw_segment_side");
8018 _lwt_release_edges(closestEdge, 1);
8019 return -1;
8020 }
8021
8022 }
8023
8024 _lwt_release_edges(closestEdge, 1);
8025 return containingFace;
8026}
char * r
Definition cu_in_wkt.c:24
int ptarray_closest_segment_2d(const POINTARRAY *pa, const POINT2D *qp, double *dist)
Definition ptarray.c:1447
int azimuth_pt_pt(const POINT2D *p1, const POINT2D *p2, double *ret)
Compute the azimuth of segment AB in radians.
Definition measures.c:2408
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition lwgeom.c:339
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
Definition lwalgorithm.c:70
#define LWT_COL_EDGE_FACE_RIGHT
LWT_INT64 LWT_ELEMID
Identifier of topology element.
#define LWT_COL_EDGE_START_NODE
#define LWT_COL_EDGE_FACE_LEFT
#define LWT_COL_EDGE_EDGE_ID
Edge fields.
#define LWT_COL_EDGE_END_NODE
#define LWT_COL_EDGE_GEOM
#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
#define LWDEBUGGF(level, geom, fmt,...)
Definition lwgeom_log.h:116
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
static LWT_ISO_EDGE * lwt_be_getClosestEdge(const LWT_TOPOLOGY *topo, const LWPOINT *pt, uint64_t *numelems, int fields)
void _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
LWT_ISO_EDGE * lwt_be_getEdgeByNode(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
static int _lwt_FindAdjacentEdges(LWT_TOPOLOGY *topo, LWT_ELEMID node, edgeend *data, edgeend *other, LWT_ELEMID myedge_id)
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:97
POINTARRAY * points
Definition liblwgeom.h:483
POINTARRAY * point
Definition liblwgeom.h:471
LWT_ELEMID face_right
LWT_ELEMID end_node
LWT_ELEMID face_left
LWT_ELEMID edge_id
LWT_ELEMID start_node
double y
Definition liblwgeom.h:390
double x
Definition liblwgeom.h:390
uint32_t npoints
Definition liblwgeom.h:427
double myaz
LWT_ELEMID cwFace

References _lwt_FindAdjacentEdges(), _lwt_release_edges(), azimuth_pt_pt(), 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_EDGE::geom, getPoint2d_cp(), lw_segment_side(), LWDEBUG, LWDEBUGF, LWDEBUGGF, lwerror(), lwline_as_lwgeom(), lwt_be_getClosestEdge(), lwt_be_getEdgeByNode(), LWT_COL_EDGE_EDGE_ID, LWT_COL_EDGE_END_NODE, LWT_COL_EDGE_FACE_LEFT, LWT_COL_EDGE_FACE_RIGHT, LWT_COL_EDGE_GEOM, LWT_COL_EDGE_START_NODE, LWTFMT_ELEMID, edgeend_t::myaz, POINTARRAY::npoints, PGTOPO_BE_ERROR, LWPOINT::point, LWLINE::points, ptarray_closest_segment_2d(), r, LWT_ISO_EDGE::start_node, POINT2D::x, and POINT2D::y.

Referenced by _lwt_AddIsoNode(), lwt_GetFaceByPoint(), and lwt_MoveIsoNode().

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