PostGIS  3.4.0dev-r@@SVN_REVISION@@

◆ lwgeom_distance_spheroid()

double lwgeom_distance_spheroid ( const LWGEOM lwgeom1,
const LWGEOM lwgeom2,
const SPHEROID spheroid,
double  tolerance 
)

Calculate the distance between two LWGEOMs, using the coordinates are longitude and latitude.

Calculate the geodetic distance from lwgeom1 to lwgeom2 on the spheroid.

Return immediately when the calculated distance drops below the tolerance (useful for dwithin calculations). Return a negative distance for incalculable cases.

Definition at line 2204 of file lwgeodetic.c.

2205 {
2206  uint8_t type1, type2;
2207  int check_intersection = LW_FALSE;
2208  GBOX gbox1, gbox2;
2209 
2210  gbox_init(&gbox1);
2211  gbox_init(&gbox2);
2212 
2213  assert(lwgeom1);
2214  assert(lwgeom2);
2215 
2216  LWDEBUGF(4, "entered function, tolerance %.8g", tolerance);
2217 
2218  /* What's the distance to an empty geometry? We don't know.
2219  Return a negative number so the caller can catch this case. */
2220  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
2221  {
2222  return -1.0;
2223  }
2224 
2225  type1 = lwgeom1->type;
2226  type2 = lwgeom2->type;
2227 
2228  /* Make sure we have boxes */
2229  if ( lwgeom1->bbox )
2230  gbox1 = *(lwgeom1->bbox);
2231  else
2232  lwgeom_calculate_gbox_geodetic(lwgeom1, &gbox1);
2233 
2234  /* Make sure we have boxes */
2235  if ( lwgeom2->bbox )
2236  gbox2 = *(lwgeom2->bbox);
2237  else
2238  lwgeom_calculate_gbox_geodetic(lwgeom2, &gbox2);
2239 
2240  /* If the boxes aren't disjoint, we have to check for edge intersections */
2241  if ( gbox_overlaps(&gbox1, &gbox2) )
2242  check_intersection = LW_TRUE;
2243 
2244  /* Point/line combinations can all be handled with simple point array iterations */
2245  if ( ( type1 == POINTTYPE || type1 == LINETYPE ) &&
2246  ( type2 == POINTTYPE || type2 == LINETYPE ) )
2247  {
2248  POINTARRAY *pa1, *pa2;
2249 
2250  if ( type1 == POINTTYPE )
2251  pa1 = ((LWPOINT*)lwgeom1)->point;
2252  else
2253  pa1 = ((LWLINE*)lwgeom1)->points;
2254 
2255  if ( type2 == POINTTYPE )
2256  pa2 = ((LWPOINT*)lwgeom2)->point;
2257  else
2258  pa2 = ((LWLINE*)lwgeom2)->points;
2259 
2260  return ptarray_distance_spheroid(pa1, pa2, spheroid, tolerance, check_intersection);
2261  }
2262 
2263  /* Point/Polygon cases, if point-in-poly, return zero, else return distance. */
2264  if ( ( type1 == POLYGONTYPE && type2 == POINTTYPE ) ||
2265  ( type2 == POLYGONTYPE && type1 == POINTTYPE ) )
2266  {
2267  const POINT2D *p;
2268  LWPOLY *lwpoly;
2269  LWPOINT *lwpt;
2270  double distance = FLT_MAX;
2271  uint32_t i;
2272 
2273  if ( type1 == POINTTYPE )
2274  {
2275  lwpt = (LWPOINT*)lwgeom1;
2276  lwpoly = (LWPOLY*)lwgeom2;
2277  }
2278  else
2279  {
2280  lwpt = (LWPOINT*)lwgeom2;
2281  lwpoly = (LWPOLY*)lwgeom1;
2282  }
2283  p = getPoint2d_cp(lwpt->point, 0);
2284 
2285  /* Point in polygon implies zero distance */
2286  if ( lwpoly_covers_point2d(lwpoly, p) )
2287  {
2288  return 0.0;
2289  }
2290 
2291  /* Not inside, so what's the actual distance? */
2292  for ( i = 0; i < lwpoly->nrings; i++ )
2293  {
2294  double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwpt->point, spheroid, tolerance, check_intersection);
2295  if ( ring_distance < distance )
2296  distance = ring_distance;
2297  if ( distance <= tolerance )
2298  return distance;
2299  }
2300  return distance;
2301  }
2302 
2303  /* Line/polygon case, if start point-in-poly, return zero, else return distance. */
2304  if ( ( type1 == POLYGONTYPE && type2 == LINETYPE ) ||
2305  ( type2 == POLYGONTYPE && type1 == LINETYPE ) )
2306  {
2307  const POINT2D *p;
2308  LWPOLY *lwpoly;
2309  LWLINE *lwline;
2310  double distance = FLT_MAX;
2311  uint32_t i;
2312 
2313  if ( type1 == LINETYPE )
2314  {
2315  lwline = (LWLINE*)lwgeom1;
2316  lwpoly = (LWPOLY*)lwgeom2;
2317  }
2318  else
2319  {
2320  lwline = (LWLINE*)lwgeom2;
2321  lwpoly = (LWPOLY*)lwgeom1;
2322  }
2323  p = getPoint2d_cp(lwline->points, 0);
2324 
2325  LWDEBUG(4, "checking if a point of line is in polygon");
2326 
2327  /* Point in polygon implies zero distance */
2328  if ( lwpoly_covers_point2d(lwpoly, p) )
2329  return 0.0;
2330 
2331  LWDEBUG(4, "checking ring distances");
2332 
2333  /* Not contained, so what's the actual distance? */
2334  for ( i = 0; i < lwpoly->nrings; i++ )
2335  {
2336  double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwline->points, spheroid, tolerance, check_intersection);
2337  LWDEBUGF(4, "ring[%d] ring_distance = %.8g", i, ring_distance);
2338  if ( ring_distance < distance )
2339  distance = ring_distance;
2340  if ( distance <= tolerance )
2341  return distance;
2342  }
2343  LWDEBUGF(4, "all rings checked, returning distance = %.8g", distance);
2344  return distance;
2345 
2346  }
2347 
2348  /* Polygon/polygon case, if start point-in-poly, return zero, else
2349  * return distance. */
2350  if (type1 == POLYGONTYPE && type2 == POLYGONTYPE)
2351  {
2352  const POINT2D* p;
2353  LWPOLY* lwpoly1 = (LWPOLY*)lwgeom1;
2354  LWPOLY* lwpoly2 = (LWPOLY*)lwgeom2;
2355  double distance = FLT_MAX;
2356  uint32_t i, j;
2357 
2358  /* Point of 2 in polygon 1 implies zero distance */
2359  p = getPoint2d_cp(lwpoly1->rings[0], 0);
2360  if (lwpoly_covers_point2d(lwpoly2, p)) return 0.0;
2361 
2362  /* Point of 1 in polygon 2 implies zero distance */
2363  p = getPoint2d_cp(lwpoly2->rings[0], 0);
2364  if (lwpoly_covers_point2d(lwpoly1, p)) return 0.0;
2365 
2366  /* Not contained, so what's the actual distance? */
2367  for (i = 0; i < lwpoly1->nrings; i++)
2368  {
2369  for (j = 0; j < lwpoly2->nrings; j++)
2370  {
2371  double ring_distance =
2373  lwpoly1->rings[i],
2374  lwpoly2->rings[j],
2375  spheroid,
2376  tolerance,
2377  check_intersection);
2378  if (ring_distance < distance)
2379  distance = ring_distance;
2380  if (distance <= tolerance) return distance;
2381  }
2382  }
2383  return distance;
2384  }
2385 
2386  /* Recurse into collections */
2387  if ( lwtype_is_collection(type1) )
2388  {
2389  uint32_t i;
2390  double distance = FLT_MAX;
2391  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom1;
2392 
2393  for ( i = 0; i < col->ngeoms; i++ )
2394  {
2395  double geom_distance = lwgeom_distance_spheroid(
2396  col->geoms[i], lwgeom2, spheroid, tolerance);
2397  if ( geom_distance < distance )
2398  distance = geom_distance;
2399  if ( distance <= tolerance )
2400  return distance;
2401  }
2402  return distance;
2403  }
2404 
2405  /* Recurse into collections */
2406  if ( lwtype_is_collection(type2) )
2407  {
2408  uint32_t i;
2409  double distance = FLT_MAX;
2410  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom2;
2411 
2412  for ( i = 0; i < col->ngeoms; i++ )
2413  {
2414  double geom_distance = lwgeom_distance_spheroid(lwgeom1, col->geoms[i], spheroid, tolerance);
2415  if ( geom_distance < distance )
2416  distance = geom_distance;
2417  if ( distance <= tolerance )
2418  return distance;
2419  }
2420  return distance;
2421  }
2422 
2423 
2424  lwerror("arguments include unsupported geometry type (%s, %s)", lwtype_name(type1), lwtype_name(type1));
2425  return -1.0;
2426 
2427 }
int gbox_overlaps(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the GBOX overlaps, LW_FALSE otherwise.
Definition: gbox.c:283
void gbox_init(GBOX *gbox)
Zero out all the entries in the GBOX.
Definition: gbox.c:40
#define LW_FALSE
Definition: liblwgeom.h:94
#define LINETYPE
Definition: liblwgeom.h:103
int lwtype_is_collection(uint8_t type)
Determine whether a type number is a collection or not.
Definition: lwgeom.c:1105
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
#define POLYGONTYPE
Definition: liblwgeom.h:104
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
int lwpoly_covers_point2d(const LWPOLY *poly, const POINT2D *pt_to_test)
Given a polygon (lon/lat decimal degrees) and point (lon/lat decimal degrees) and a guaranteed outsid...
Definition: lwgeodetic.c:2535
double lwgeom_distance_spheroid(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2, const SPHEROID *spheroid, double tolerance)
Calculate the distance between two LWGEOMs, using the coordinates are longitude and latitude.
Definition: lwgeodetic.c:2204
int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox)
Calculate the geodetic bounding box for an LWGEOM.
Definition: lwgeodetic.c:3027
static double ptarray_distance_spheroid(const POINTARRAY *pa1, const POINTARRAY *pa2, const SPHEROID *s, double tolerance, int check_intersection)
Definition: lwgeodetic.c:1841
#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
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
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition: lwinline.h:203
static double distance(double x1, double y1, double x2, double y2)
Definition: lwtree.c:1032
uint32_t ngeoms
Definition: liblwgeom.h:580
LWGEOM ** geoms
Definition: liblwgeom.h:575
uint8_t type
Definition: liblwgeom.h:462
GBOX * bbox
Definition: liblwgeom.h:458
POINTARRAY * points
Definition: liblwgeom.h:483
POINTARRAY * point
Definition: liblwgeom.h:471
POINTARRAY ** rings
Definition: liblwgeom.h:519
uint32_t nrings
Definition: liblwgeom.h:524

References LWGEOM::bbox, distance(), gbox_init(), gbox_overlaps(), LWCOLLECTION::geoms, getPoint2d_cp(), LINETYPE, LW_FALSE, LW_TRUE, LWDEBUG, LWDEBUGF, lwerror(), lwgeom_calculate_gbox_geodetic(), lwgeom_distance_spheroid(), lwgeom_is_empty(), lwpoly_covers_point2d(), lwtype_is_collection(), lwtype_name(), LWCOLLECTION::ngeoms, LWPOLY::nrings, LWPOINT::point, LWLINE::points, POINTTYPE, POLYGONTYPE, ptarray_distance_spheroid(), LWPOLY::rings, and LWGEOM::type.

Referenced by geography_centroid_from_mline(), geography_distance_knn(), geography_distance_uncached(), geography_dwithin(), geography_dwithin_uncached(), geometry_distance_spheroid(), lwgeom_distance_spheroid(), test_lwgeom_distance_sphere(), test_tree_circ_distance(), and test_tree_circ_distance_threshold().

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