PostGIS  3.3.9dev-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 7188 of file lwgeom_topo.c.

7189 {
7190  LWT_ISO_EDGE* closestEdge;
7191  LWT_ISO_EDGE* edges;
7192  uint64_t numedges, i;
7193  const POINT2D *queryPoint;
7194  const POINT2D *closestPointOnEdge = NULL;
7195  uint32_t closestSegmentIndex;
7196  int closestSegmentSide;
7197  uint32_t closestPointVertex;
7198  const POINT2D *closestSegmentP0, *closestSegmentP1;
7199  LWT_ELEMID closestNode = 0;
7200  double dist;
7201  int containingFace = -1;
7202 
7203  closestEdge = lwt_be_getClosestEdge( topo, pt, &numedges,
7210  );
7211  if (numedges == UINT64_MAX)
7212  {
7213  lwerror("Backend error: %s", lwt_be_lastErrorMessage(topo->be_iface));
7214  /* cberror(topo->be_data, "Error from cb_getClosestEdge"); */
7215  return -1;
7216  }
7217  if (numedges == 0)
7218  {
7219  /* If there are no edges the point is in the universal face */
7220  return 0;
7221  }
7222 
7223  if ( closestEdge->face_left < 0 )
7224  {
7225  lwerror("Closest edge %" LWTFMT_ELEMID " has invalid face %" LWTFMT_ELEMID
7226  " on its left side", closestEdge->edge_id, closestEdge->face_left);
7227  _lwt_release_edges(closestEdge, 1);
7228  return -1;
7229  }
7230 
7231  if ( closestEdge->face_right < 0 )
7232  {
7233  lwerror("Closest edge %" LWTFMT_ELEMID " has invalid face %" LWTFMT_ELEMID
7234  " on its right side", closestEdge->edge_id, closestEdge->face_right);
7235  _lwt_release_edges(closestEdge, 1);
7236  return -1;
7237  }
7238 
7239  if ( closestEdge->geom->points->npoints < 2 )
7240  {
7241  lwerror("Corrupted topology: geometry of edge %" LWTFMT_ELEMID " is EMPTY",
7242  closestEdge->edge_id);
7243  _lwt_release_edges(closestEdge, 1);
7244  return -1;
7245  }
7246 
7247  LWDEBUGGF(2, lwline_as_lwgeom(closestEdge->geom), "Closest edge %" LWTFMT_ELEMID, closestEdge->edge_id);
7248 
7249  /* Find closest segment of edge to the point */
7250  queryPoint = getPoint2d_cp(pt->point, 0);
7251  closestSegmentIndex = ptarray_closest_segment_2d(closestEdge->geom->points, queryPoint, &dist);
7252  LWDEBUGF(1, "Closest segment on edge %" LWTFMT_ELEMID " is %d (dist %g)", closestEdge->edge_id, closestSegmentIndex, dist);
7253  closestSegmentP0 = getPoint2d_cp(closestEdge->geom->points, closestSegmentIndex);
7254  closestSegmentP1 = getPoint2d_cp(closestEdge->geom->points, closestSegmentIndex + 1);
7255  LWDEBUGF(1, "Closest segment on edge %" LWTFMT_ELEMID " is LINESTRING(%g %g, %g %g)",
7256  closestEdge->edge_id,
7257  closestSegmentP0->x,
7258  closestSegmentP0->y,
7259  closestSegmentP1->x,
7260  closestSegmentP1->y
7261  );
7262 
7263  /*
7264  * We use comp.graphics.algorithms Frequently Asked Questions method
7265  *
7266  * (1) AC dot AB
7267  * r = ----------
7268  * ||AB||^2
7269  * r has the following meaning:
7270  * r=0 P = A
7271  * r=1 P = B
7272  * r<0 P is on the backward extension of AB
7273  * r>1 P is on the forward extension of AB
7274  * 0<r<1 P is interior to AB
7275  *
7276  */
7277  const POINT2D *p = queryPoint;
7278  const POINT2D *A = closestSegmentP0;
7279  const POINT2D *B = closestSegmentP1;
7280  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) );
7281  if ( r <= 0 )
7282  {
7283  closestPointOnEdge = A;
7284  closestPointVertex = closestSegmentIndex;
7285  if ( closestSegmentIndex == 0 )
7286  {
7287  closestNode = closestEdge->start_node;
7288  }
7289  }
7290  else if (r >= 1 )
7291  {
7292  closestPointOnEdge = B;
7293  closestPointVertex = closestSegmentIndex + 1;
7294  if ( closestSegmentIndex + 2 == closestEdge->geom->points->npoints )
7295  {
7296  closestNode = closestEdge->end_node;
7297  }
7298  }
7299  else
7300  {
7301  closestPointVertex = closestEdge->geom->points->npoints;
7302  }
7303 
7304  if ( closestNode != 0 )
7305  {
7306  LWDEBUGF(1, "Closest point is node %d", closestNode);
7307  if ( dist == 0 )
7308  {
7309  LWDEBUGF(1, "Query point is node %d", closestNode);
7310  /* Query point is the node
7311  *
7312  * If all edges incident to the node are
7313  * dangling, we can return their common
7314  * side face, otherwise the point will be
7315  * on multiple face boundaries
7316  */
7317  if ( closestEdge->face_left != closestEdge->face_right )
7318  {
7319  _lwt_release_edges(closestEdge, 1);
7320  lwerror("Two or more faces found");
7321  return -1;
7322  }
7323  containingFace = closestEdge->face_left;
7324 
7325  /* Check other incident edges */
7326  numedges = 1;
7327  edges = lwt_be_getEdgeByNode( topo, &closestNode, &numedges, LWT_COL_EDGE_FACE_LEFT|LWT_COL_EDGE_FACE_RIGHT );
7328  if (numedges == UINT64_MAX)
7329  {
7330  lwerror("Backend error from getEdgeByNode: %s", lwt_be_lastErrorMessage(topo->be_iface));
7331  /* cberror(topo->be_data, "Error from cb_getClosestEdge"); */
7332  _lwt_release_edges(closestEdge, 1);
7333  return -1;
7334  }
7335  for (i=0; i<numedges; ++i)
7336  {
7337  if ( edges[i].face_left != containingFace ||
7338  edges[i].face_right != containingFace )
7339  {
7340  _lwt_release_edges(edges, numedges);
7341  _lwt_release_edges(closestEdge, 1);
7342  lwerror("Two or more faces found");
7343  return -1;
7344  }
7345  }
7346  if (numedges < 1 )
7347  {
7348  lwerror("Unexpected backend return: getEdgeByNode(%d) returns no edges when we previously found edge %d ending on that node",
7349  closestNode, closestEdge->edge_id);
7350  _lwt_release_edges(edges, numedges);
7351  _lwt_release_edges(closestEdge, 1);
7352  return -1;
7353  }
7354  LWDEBUGF(1, "lwt_be_getEdgeByNode returned %d edges", numedges);
7355  _lwt_release_edges(edges, numedges);
7356  _lwt_release_edges(closestEdge, 1);
7357  return containingFace;
7358  }
7359 
7360  /* Closest point is a node, but query point is NOT on the node */
7361 
7362  /* let's do azimuth computation */
7363  edgeend ee;
7364  if ( ! azimuth_pt_pt(closestPointOnEdge, queryPoint, &ee.myaz) ) {
7365  lwerror("error computing azimuth of query point [%.15g %.15g,%.15g %.15g]",
7366  closestPointOnEdge->x, closestPointOnEdge->y,
7367  queryPoint->x, queryPoint->y);
7368  _lwt_release_edges(closestEdge, 1);
7369  return -1;
7370  }
7371 
7372  LWDEBUGF(1, "Query point azimuth is %g", ee.myaz);
7373 
7374  int found = _lwt_FindAdjacentEdges( topo, closestNode, &ee, NULL, -1 );
7375  if ( ! found ) {
7376  lwerror("Unexpected backend return: _lwt_FindAdjacentEdges(%d) found no edges when we previously found edge %d ending on that node",
7377  closestNode, closestEdge->edge_id);
7378  _lwt_release_edges(closestEdge, 1);
7379  return -1;
7380  }
7381 
7382  _lwt_release_edges(closestEdge, 1);
7383  return ee.cwFace;
7384 
7385  }
7386 
7387  LWDEBUG(1, "Closest point is NOT a node");
7388 
7389  /* If this edge has the same face on the left and right sides
7390  * we found the face containing our query point */
7391  if ( closestEdge->face_left == closestEdge->face_right )
7392  {
7393  containingFace = closestEdge->face_left;
7394  _lwt_release_edges(closestEdge, 1);
7395  return containingFace;
7396  }
7397 
7398  if ( dist == 0 )
7399  {
7400  /* We checked the dangling case above */
7401  _lwt_release_edges(closestEdge, 1);
7402  lwerror("Two or more faces found");
7403  return -1;
7404  }
7405 
7406  /* Find on which side of the segment the query point lays */
7407  if ( closestPointVertex != closestEdge->geom->points->npoints )
7408  {
7409  /* Closest point is a vertex of the closest segment */
7410  LWDEBUGF(1, "Closest point is vertex %d of closest segment", closestPointVertex);
7411 
7412  /*
7413  * We need to check if rotating clockwise the line
7414  * from previous vertex to closest vertex clockwise
7415  * around the closest vertex encounters
7416  * the line to query point first (which means it's on the left
7417  * of the closest edge) or the line to next vertex first (which
7418  * means the query point is on the right)
7419  */
7420 
7421  uint32_t prevVertexIndex = closestPointVertex > 0 ?
7422  closestPointVertex - 1u :
7423  closestEdge->geom->points->npoints - 2u; /* last vertex would be == first vertex, this being a closed edge */
7424 
7425  const POINT2D *prevVertex = getPoint2d_cp(
7426  closestEdge->geom->points,
7427  prevVertexIndex
7428  );
7429 
7430  LWDEBUGF(1, "Previous vertex %u is POINT(%.15g %.15g)",
7431  prevVertexIndex,
7432  prevVertex->x,
7433  prevVertex->y
7434  );
7435 
7436  uint32_t nextVertexIndex = closestPointVertex == closestEdge->geom->points->npoints - 1u ?
7437  1u : /* first point would be == last point, this being a closed edge */
7438  closestPointVertex + 1u;
7439 
7440  const POINT2D *nextVertex = getPoint2d_cp(
7441  closestEdge->geom->points,
7442  nextVertexIndex
7443  );
7444 
7445  LWDEBUGF(1, "Next vertex %u is POINT(%.15g %.15g)",
7446  nextVertexIndex,
7447  nextVertex->x,
7448  nextVertex->y
7449  );
7450 
7451 
7452  double azS0; /* azimuth from previous vertex to closestPointVertex */
7453  double azS1; /* azimuth from closestPointVertex to next vertex */
7454  double azSL; /* azimuth from closestPointVertex to query point */
7455 
7456  if ( ! azimuth_pt_pt(closestPointOnEdge, prevVertex, &azS0)) {
7457  lwerror("error computing azimuth of segment to closest point [%.15g %.15g,%.15g %.15g]",
7458  closestPointOnEdge->x, closestPointOnEdge->y,
7459  prevVertex->x, prevVertex->y);
7460  _lwt_release_edges(closestEdge, 1);
7461  return -1;
7462  }
7463  if ( ! azimuth_pt_pt(closestPointOnEdge, nextVertex, &azS1)) {
7464  lwerror("error computing azimuth of segment from closest point [%.15g %.15g,%.15g %.15g]",
7465  closestPointOnEdge->x, closestPointOnEdge->y,
7466  nextVertex->x, nextVertex->y);
7467  _lwt_release_edges(closestEdge, 1);
7468  return -1;
7469  }
7470  if ( ! azimuth_pt_pt(closestPointOnEdge, queryPoint, &azSL) ) {
7471  lwerror("error computing azimuth of queryPoint [%.15g %.15g,%.15g %.15g]",
7472  closestPointOnEdge->x, closestPointOnEdge->y,
7473  queryPoint->x, queryPoint->y);
7474  _lwt_release_edges(closestEdge, 1);
7475  return -1;
7476  }
7477 
7478  double angle_S0_S1 = azS1 - azS0;
7479  if ( angle_S0_S1 < 0 ) angle_S0_S1 += 2 * M_PI;
7480 
7481  double angle_S0_SL = azSL - azS0;
7482  if ( angle_S0_SL < 0 ) angle_S0_SL += 2 * M_PI;
7483 
7484  LWDEBUGF(1, "Azimuths from closest (vertex) point: P:%g, N:%g (+%g), Q:%g (+%g)",
7485  azS0,
7486  azS1, angle_S0_S1,
7487  azSL, angle_S0_SL
7488  );
7489  if ( angle_S0_SL < angle_S0_S1 )
7490  {
7491  /* line to query point was encountered first, is on the left */
7492  containingFace = closestEdge->face_left;
7493  }
7494  else
7495  {
7496  /* line to query point was NOT encountered first, is on the right */
7497  containingFace = closestEdge->face_right;
7498  }
7499  }
7500  else
7501  {
7502  /* Closest point is internal to closest segment, we can use
7503  * lw_segment_side */
7504 
7505  LWDEBUGF(1, "Closest point is internal to closest segment, calling lw_segment_side((%g,%g),(%g,%g),(%g,%g)",
7506  closestSegmentP0->x,
7507  closestSegmentP0->y,
7508  closestSegmentP1->x,
7509  closestSegmentP1->y,
7510  queryPoint->x,
7511  queryPoint->y
7512  );
7513 
7514  closestSegmentSide = lw_segment_side(closestSegmentP0, closestSegmentP1, queryPoint);
7515  LWDEBUGF(1, "Side of closest segment query point falls on: %d", closestSegmentSide);
7516 
7517  if ( closestSegmentSide == -1 ) /* left */
7518  {
7519  containingFace = closestEdge->face_left;
7520  }
7521  else if ( closestSegmentSide == 1 ) /* right */
7522  {
7523  containingFace = closestEdge->face_right;
7524  }
7525  else
7526  {
7527  lwerror("Unexpected collinearity reported from lw_segment_side");
7528  _lwt_release_edges(closestEdge, 1);
7529  return -1;
7530  }
7531 
7532  }
7533 
7534  _lwt_release_edges(closestEdge, 1);
7535  return containingFace;
7536 }
char * r
Definition: cu_in_wkt.c:24
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:339
int ptarray_closest_segment_2d(const POINTARRAY *pa, const POINT2D *qp, double *dist)
Definition: ptarray.c:1307
int azimuth_pt_pt(const POINT2D *p1, const POINT2D *p2, double *ret)
Compute the azimuth of segment AB in radians.
Definition: measures.c:2509
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
Definition: lwalgorithm.c:65
#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 LWDEBUG(level, msg)
Definition: lwgeom_log.h:83
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
#define LWDEBUGGF(level, geom, fmt,...)
Definition: lwgeom_log.h:98
const char * lwt_be_lastErrorMessage(const LWT_BE_IFACE *be)
Definition: lwgeom_topo.c:119
static LWT_ISO_EDGE * lwt_be_getEdgeByNode(LWT_TOPOLOGY *topo, const LWT_ELEMID *ids, uint64_t *numelems, int fields)
Definition: lwgeom_topo.c:232
static LWT_ISO_EDGE * lwt_be_getClosestEdge(const LWT_TOPOLOGY *topo, const LWPOINT *pt, uint64_t *numelems, int fields)
Definition: lwgeom_topo.c:410
static int _lwt_FindAdjacentEdges(LWT_TOPOLOGY *topo, LWT_ELEMID node, edgeend *data, edgeend *other, int myedge_id)
Definition: lwgeom_topo.c:1536
#define LWTFMT_ELEMID
Definition: lwgeom_topo.c:43
static void _lwt_release_edges(LWT_ISO_EDGE *edges, int num_edges)
Definition: lwgeom_topo.c:465
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:101
POINTARRAY * points
Definition: liblwgeom.h:498
POINTARRAY * point
Definition: liblwgeom.h:486
LWT_ELEMID face_right
LWT_ELEMID end_node
LWT_ELEMID face_left
LWLINE * geom
LWT_ELEMID edge_id
LWT_ELEMID start_node
const LWT_BE_IFACE * be_iface
double y
Definition: liblwgeom.h:405
double x
Definition: liblwgeom.h:405
uint32_t npoints
Definition: liblwgeom.h:442
double myaz
Definition: lwgeom_topo.c:1426
LWT_ELEMID cwFace
Definition: lwgeom_topo.c:1420

References _lwt_FindAdjacentEdges(), _lwt_release_edges(), azimuth_pt_pt(), LWT_TOPOLOGY_T::be_iface, 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_be_lastErrorMessage(), 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, 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: