PostGIS  3.7.0dev-r@@SVN_REVISION@@

◆ 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 7711 of file lwgeom_topo.c.

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

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: