PostGIS 3.7.0dev-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 7828 of file lwgeom_topo.c.

7829{
7830 LWT_ISO_EDGE* closestEdge;
7831 LWT_ISO_EDGE* edges;
7832 uint64_t numedges, i;
7833 const POINT2D *queryPoint;
7834 const POINT2D *closestPointOnEdge = NULL;
7835 uint32_t closestSegmentIndex;
7836 int closestSegmentSide;
7837 uint32_t closestPointVertex;
7838 const POINT2D *closestSegmentP0, *closestSegmentP1;
7839 LWT_ELEMID closestNode = 0;
7840 double dist;
7841 LWT_ELEMID containingFace = -1;
7842
7843 closestEdge = lwt_be_getClosestEdge( topo, pt, &numedges,
7850 );
7851 if (numedges == UINT64_MAX)
7852 {
7854 /* cberror(topo->be_data, "Error from cb_getClosestEdge"); */
7855 return -1;
7856 }
7857 if (numedges == 0)
7858 {
7859 /* If there are no edges the point is in the universal face */
7860 return 0;
7861 }
7862
7863 if ( closestEdge->face_left < 0 )
7864 {
7865 lwerror("Closest edge %" LWTFMT_ELEMID " has invalid face %" LWTFMT_ELEMID
7866 " on its left side", closestEdge->edge_id, closestEdge->face_left);
7867 _lwt_release_edges(closestEdge, 1);
7868 return -1;
7869 }
7870
7871 if ( closestEdge->face_right < 0 )
7872 {
7873 lwerror("Closest edge %" LWTFMT_ELEMID " has invalid face %" LWTFMT_ELEMID
7874 " on its right side", closestEdge->edge_id, closestEdge->face_right);
7875 _lwt_release_edges(closestEdge, 1);
7876 return -1;
7877 }
7878
7879 if ( closestEdge->geom->points->npoints < 2 )
7880 {
7881 lwerror("Corrupted topology: geometry of edge %" LWTFMT_ELEMID " is EMPTY",
7882 closestEdge->edge_id);
7883 _lwt_release_edges(closestEdge, 1);
7884 return -1;
7885 }
7886
7887 LWDEBUGGF(2, lwline_as_lwgeom(closestEdge->geom), "Closest edge %" LWTFMT_ELEMID, closestEdge->edge_id);
7888
7889 /* Find closest segment of edge to the point */
7890 queryPoint = getPoint2d_cp(pt->point, 0);
7891 closestSegmentIndex = ptarray_closest_segment_2d(closestEdge->geom->points, queryPoint, &dist);
7892 LWDEBUGF(1, "Closest segment on edge %" LWTFMT_ELEMID " is %d (dist %g)", closestEdge->edge_id, closestSegmentIndex, dist);
7893 closestSegmentP0 = getPoint2d_cp(closestEdge->geom->points, closestSegmentIndex);
7894 closestSegmentP1 = getPoint2d_cp(closestEdge->geom->points, closestSegmentIndex + 1);
7895 LWDEBUGF(1, "Closest segment on edge %" LWTFMT_ELEMID " is LINESTRING(%g %g, %g %g)",
7896 closestEdge->edge_id,
7897 closestSegmentP0->x,
7898 closestSegmentP0->y,
7899 closestSegmentP1->x,
7900 closestSegmentP1->y
7901 );
7902
7903 /*
7904 * We use comp.graphics.algorithms Frequently Asked Questions method
7905 *
7906 * (1) AC dot AB
7907 * r = ----------
7908 * ||AB||^2
7909 * r has the following meaning:
7910 * r=0 P = A
7911 * r=1 P = B
7912 * r<0 P is on the backward extension of AB
7913 * r>1 P is on the forward extension of AB
7914 * 0<r<1 P is interior to AB
7915 *
7916 */
7917 const POINT2D *p = queryPoint;
7918 const POINT2D *A = closestSegmentP0;
7919 const POINT2D *B = closestSegmentP1;
7920 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) );
7921 if ( r <= 0 )
7922 {
7923 closestPointOnEdge = A;
7924 closestPointVertex = closestSegmentIndex;
7925 if ( closestSegmentIndex == 0 )
7926 {
7927 closestNode = closestEdge->start_node;
7928 }
7929 }
7930 else if (r >= 1 )
7931 {
7932 closestPointOnEdge = B;
7933 closestPointVertex = closestSegmentIndex + 1;
7934 if ( closestSegmentIndex + 2 == closestEdge->geom->points->npoints )
7935 {
7936 closestNode = closestEdge->end_node;
7937 }
7938 }
7939 else
7940 {
7941 closestPointVertex = closestEdge->geom->points->npoints;
7942 }
7943
7944 if ( closestNode != 0 )
7945 {
7946 LWDEBUGF(1, "Closest point is node %" LWTFMT_ELEMID, closestNode);
7947 if ( dist == 0 )
7948 {
7949 LWDEBUGF(1, "Query point is node %" LWTFMT_ELEMID, closestNode);
7950 /* Query point is the node
7951 *
7952 * If all edges incident to the node are
7953 * dangling, we can return their common
7954 * side face, otherwise the point will be
7955 * on multiple face boundaries
7956 */
7957 if ( closestEdge->face_left != closestEdge->face_right )
7958 {
7959 _lwt_release_edges(closestEdge, 1);
7960 lwerror("Two or more faces found");
7961 return -1;
7962 }
7963 containingFace = closestEdge->face_left;
7964
7965 /* Check other incident edges */
7966 numedges = 1;
7967 edges = lwt_be_getEdgeByNode( topo, &closestNode, &numedges, LWT_COL_EDGE_FACE_LEFT|LWT_COL_EDGE_FACE_RIGHT );
7968 if (numedges == UINT64_MAX)
7969 {
7971 /* cberror(topo->be_data, "Error from cb_getClosestEdge"); */
7972 _lwt_release_edges(closestEdge, 1);
7973 return -1;
7974 }
7975 for (i=0; i<numedges; ++i)
7976 {
7977 if ( edges[i].face_left != containingFace ||
7978 edges[i].face_right != containingFace )
7979 {
7980 _lwt_release_edges(edges, numedges);
7981 _lwt_release_edges(closestEdge, 1);
7982 lwerror("Two or more faces found");
7983 return -1;
7984 }
7985 }
7986 if (numedges < 1 )
7987 {
7988 lwerror("Unexpected backend return: getEdgeByNode(%" LWTFMT_ELEMID
7989 ") returns no edges when we previously found edge %"
7990 LWTFMT_ELEMID " ending on that node",
7991 closestNode, closestEdge->edge_id);
7992 _lwt_release_edges(edges, numedges);
7993 _lwt_release_edges(closestEdge, 1);
7994 return -1;
7995 }
7996 LWDEBUGF(1, "lwt_be_getEdgeByNode returned %" PRIu64 " edges", numedges);
7997 _lwt_release_edges(edges, numedges);
7998 _lwt_release_edges(closestEdge, 1);
7999 return containingFace;
8000 }
8001
8002 /* Closest point is a node, but query point is NOT on the node */
8003
8004 /* let's do azimuth computation */
8005 edgeend ee;
8006 if ( ! azimuth_pt_pt(closestPointOnEdge, queryPoint, &ee.myaz) ) {
8007 lwerror("error computing azimuth of query point [%.15g %.15g,%.15g %.15g]",
8008 closestPointOnEdge->x, closestPointOnEdge->y,
8009 queryPoint->x, queryPoint->y);
8010 _lwt_release_edges(closestEdge, 1);
8011 return -1;
8012 }
8013
8014 LWDEBUGF(1, "Query point azimuth is %g", ee.myaz);
8015
8016 int found = _lwt_FindAdjacentEdges( topo, closestNode, &ee, NULL, -1 );
8017 if ( ! found ) {
8018 lwerror("Unexpected backend return: _lwt_FindAdjacentEdges(%"
8020 ") found no edges when we previously found edge %"
8021 LWTFMT_ELEMID " ending on that node",
8022 closestNode, closestEdge->edge_id);
8023 _lwt_release_edges(closestEdge, 1);
8024 return -1;
8025 }
8026
8027 _lwt_release_edges(closestEdge, 1);
8028 return ee.cwFace;
8029
8030 }
8031
8032 LWDEBUG(1, "Closest point is NOT a node");
8033
8034 /* If this edge has the same face on the left and right sides
8035 * we found the face containing our query point */
8036 if ( closestEdge->face_left == closestEdge->face_right )
8037 {
8038 LWDEBUGF(1, "Closest point is on a edge with face %" LWTFMT_ELEMID
8039 " on both sides", closestEdge->face_left);
8040
8041 containingFace = closestEdge->face_left;
8042 _lwt_release_edges(closestEdge, 1);
8043 return containingFace;
8044 }
8045
8046 if ( dist == 0 )
8047 {
8048 /* We checked the dangling case above */
8049 _lwt_release_edges(closestEdge, 1);
8050 lwerror("Two or more faces found");
8051 return -1;
8052 }
8053
8054 /* Find on which side of the segment the query point lays */
8055 if ( closestPointVertex != closestEdge->geom->points->npoints )
8056 {
8057 /* Closest point is a vertex of the closest segment */
8058 LWDEBUGF(1, "Closest point is vertex %d of closest segment", closestPointVertex);
8059
8060 /*
8061 * We need to check if rotating clockwise the line
8062 * from previous vertex to closest vertex clockwise
8063 * around the closest vertex encounters
8064 * the line to query point first (which means it's on the left
8065 * of the closest edge) or the line to next vertex first (which
8066 * means the query point is on the right)
8067 */
8068
8069 uint32_t prevVertexIndex = closestPointVertex > 0 ?
8070 closestPointVertex - 1u :
8071 closestEdge->geom->points->npoints - 2u; /* last vertex would be == first vertex, this being a closed edge */
8072
8073 const POINT2D *prevVertex = getPoint2d_cp(
8074 closestEdge->geom->points,
8075 prevVertexIndex
8076 );
8077
8078 LWDEBUGF(1, "Previous vertex %u is POINT(%.15g %.15g)",
8079 prevVertexIndex,
8080 prevVertex->x,
8081 prevVertex->y
8082 );
8083
8084 uint32_t nextVertexIndex = closestPointVertex == closestEdge->geom->points->npoints - 1u ?
8085 1u : /* first point would be == last point, this being a closed edge */
8086 closestPointVertex + 1u;
8087
8088 const POINT2D *nextVertex = getPoint2d_cp(
8089 closestEdge->geom->points,
8090 nextVertexIndex
8091 );
8092
8093 LWDEBUGF(1, "Next vertex %u is POINT(%.15g %.15g)",
8094 nextVertexIndex,
8095 nextVertex->x,
8096 nextVertex->y
8097 );
8098
8099
8100 double azS0; /* azimuth from previous vertex to closestPointVertex */
8101 double azS1; /* azimuth from closestPointVertex to next vertex */
8102 double azSL; /* azimuth from closestPointVertex to query point */
8103
8104 if ( ! azimuth_pt_pt(closestPointOnEdge, prevVertex, &azS0)) {
8105 lwerror("error computing azimuth of segment to closest point [%.15g %.15g,%.15g %.15g]",
8106 closestPointOnEdge->x, closestPointOnEdge->y,
8107 prevVertex->x, prevVertex->y);
8108 _lwt_release_edges(closestEdge, 1);
8109 return -1;
8110 }
8111 if ( ! azimuth_pt_pt(closestPointOnEdge, nextVertex, &azS1)) {
8112 lwerror("error computing azimuth of segment from closest point [%.15g %.15g,%.15g %.15g]",
8113 closestPointOnEdge->x, closestPointOnEdge->y,
8114 nextVertex->x, nextVertex->y);
8115 _lwt_release_edges(closestEdge, 1);
8116 return -1;
8117 }
8118 if ( ! azimuth_pt_pt(closestPointOnEdge, queryPoint, &azSL) ) {
8119 lwerror("error computing azimuth of queryPoint [%.15g %.15g,%.15g %.15g]",
8120 closestPointOnEdge->x, closestPointOnEdge->y,
8121 queryPoint->x, queryPoint->y);
8122 _lwt_release_edges(closestEdge, 1);
8123 return -1;
8124 }
8125
8126 double angle_S0_S1 = azS1 - azS0;
8127 if ( angle_S0_S1 < 0 ) angle_S0_S1 += 2 * M_PI;
8128
8129 double angle_S0_SL = azSL - azS0;
8130 if ( angle_S0_SL < 0 ) angle_S0_SL += 2 * M_PI;
8131
8132 LWDEBUGF(1, "Azimuths from closest (vertex) point: P:%g, N:%g (+%g), Q:%g (+%g)",
8133 azS0,
8134 azS1, angle_S0_S1,
8135 azSL, angle_S0_SL
8136 );
8137 if ( angle_S0_SL < angle_S0_S1 )
8138 {
8139 /* line to query point was encountered first, is on the left */
8140 containingFace = closestEdge->face_left;
8141 }
8142 else
8143 {
8144 /* line to query point was NOT encountered first, is on the right */
8145 containingFace = closestEdge->face_right;
8146 }
8147 }
8148 else
8149 {
8150 /* Closest point is internal to closest segment, we can use
8151 * lw_segment_side */
8152
8153 LWDEBUGF(1, "Closest point is internal to closest segment, calling lw_segment_side((%g,%g),(%g,%g),(%g,%g)",
8154 closestSegmentP0->x,
8155 closestSegmentP0->y,
8156 closestSegmentP1->x,
8157 closestSegmentP1->y,
8158 queryPoint->x,
8159 queryPoint->y
8160 );
8161
8162 closestSegmentSide = lw_segment_side(closestSegmentP0, closestSegmentP1, queryPoint);
8163 LWDEBUGF(1, "Side of closest segment query point falls on: %d (%s)",
8164 closestSegmentSide, closestSegmentSide == -1 ? "left" : closestSegmentSide == 1 ? "right" : "collinear" );
8165
8166 if ( closestSegmentSide == -1 ) /* left */
8167 {
8168 containingFace = closestEdge->face_left;
8169 }
8170 else if ( closestSegmentSide == 1 ) /* right */
8171 {
8172 containingFace = closestEdge->face_right;
8173 }
8174 else
8175 {
8176 lwerror("Unexpected collinearity reported from lw_segment_side");
8177 _lwt_release_edges(closestEdge, 1);
8178 return -1;
8179 }
8180
8181 }
8182
8183 _lwt_release_edges(closestEdge, 1);
8184 return containingFace;
8185}
char * r
Definition cu_in_wkt.c:24
int ptarray_closest_segment_2d(const POINTARRAY *pa, const POINT2D *qp, double *dist)
Definition ptarray.c:1452
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:367
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: