PostGIS  3.2.2dev-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 2192 of file lwgeodetic.c.

2193 {
2194  uint8_t type1, type2;
2195  int check_intersection = LW_FALSE;
2196  GBOX gbox1, gbox2;
2197 
2198  gbox_init(&gbox1);
2199  gbox_init(&gbox2);
2200 
2201  assert(lwgeom1);
2202  assert(lwgeom2);
2203 
2204  LWDEBUGF(4, "entered function, tolerance %.8g", tolerance);
2205 
2206  /* What's the distance to an empty geometry? We don't know.
2207  Return a negative number so the caller can catch this case. */
2208  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
2209  {
2210  return -1.0;
2211  }
2212 
2213  type1 = lwgeom1->type;
2214  type2 = lwgeom2->type;
2215 
2216  /* Make sure we have boxes */
2217  if ( lwgeom1->bbox )
2218  gbox1 = *(lwgeom1->bbox);
2219  else
2220  lwgeom_calculate_gbox_geodetic(lwgeom1, &gbox1);
2221 
2222  /* Make sure we have boxes */
2223  if ( lwgeom2->bbox )
2224  gbox2 = *(lwgeom2->bbox);
2225  else
2226  lwgeom_calculate_gbox_geodetic(lwgeom2, &gbox2);
2227 
2228  /* If the boxes aren't disjoint, we have to check for edge intersections */
2229  if ( gbox_overlaps(&gbox1, &gbox2) )
2230  check_intersection = LW_TRUE;
2231 
2232  /* Point/line combinations can all be handled with simple point array iterations */
2233  if ( ( type1 == POINTTYPE || type1 == LINETYPE ) &&
2234  ( type2 == POINTTYPE || type2 == LINETYPE ) )
2235  {
2236  POINTARRAY *pa1, *pa2;
2237 
2238  if ( type1 == POINTTYPE )
2239  pa1 = ((LWPOINT*)lwgeom1)->point;
2240  else
2241  pa1 = ((LWLINE*)lwgeom1)->points;
2242 
2243  if ( type2 == POINTTYPE )
2244  pa2 = ((LWPOINT*)lwgeom2)->point;
2245  else
2246  pa2 = ((LWLINE*)lwgeom2)->points;
2247 
2248  return ptarray_distance_spheroid(pa1, pa2, spheroid, tolerance, check_intersection);
2249  }
2250 
2251  /* Point/Polygon cases, if point-in-poly, return zero, else return distance. */
2252  if ( ( type1 == POLYGONTYPE && type2 == POINTTYPE ) ||
2253  ( type2 == POLYGONTYPE && type1 == POINTTYPE ) )
2254  {
2255  const POINT2D *p;
2256  LWPOLY *lwpoly;
2257  LWPOINT *lwpt;
2258  double distance = FLT_MAX;
2259  uint32_t i;
2260 
2261  if ( type1 == POINTTYPE )
2262  {
2263  lwpt = (LWPOINT*)lwgeom1;
2264  lwpoly = (LWPOLY*)lwgeom2;
2265  }
2266  else
2267  {
2268  lwpt = (LWPOINT*)lwgeom2;
2269  lwpoly = (LWPOLY*)lwgeom1;
2270  }
2271  p = getPoint2d_cp(lwpt->point, 0);
2272 
2273  /* Point in polygon implies zero distance */
2274  if ( lwpoly_covers_point2d(lwpoly, p) )
2275  {
2276  return 0.0;
2277  }
2278 
2279  /* Not inside, so what's the actual distance? */
2280  for ( i = 0; i < lwpoly->nrings; i++ )
2281  {
2282  double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwpt->point, spheroid, tolerance, check_intersection);
2283  if ( ring_distance < distance )
2284  distance = ring_distance;
2285  if ( distance < tolerance )
2286  return distance;
2287  }
2288  return distance;
2289  }
2290 
2291  /* Line/polygon case, if start point-in-poly, return zero, else return distance. */
2292  if ( ( type1 == POLYGONTYPE && type2 == LINETYPE ) ||
2293  ( type2 == POLYGONTYPE && type1 == LINETYPE ) )
2294  {
2295  const POINT2D *p;
2296  LWPOLY *lwpoly;
2297  LWLINE *lwline;
2298  double distance = FLT_MAX;
2299  uint32_t i;
2300 
2301  if ( type1 == LINETYPE )
2302  {
2303  lwline = (LWLINE*)lwgeom1;
2304  lwpoly = (LWPOLY*)lwgeom2;
2305  }
2306  else
2307  {
2308  lwline = (LWLINE*)lwgeom2;
2309  lwpoly = (LWPOLY*)lwgeom1;
2310  }
2311  p = getPoint2d_cp(lwline->points, 0);
2312 
2313  LWDEBUG(4, "checking if a point of line is in polygon");
2314 
2315  /* Point in polygon implies zero distance */
2316  if ( lwpoly_covers_point2d(lwpoly, p) )
2317  return 0.0;
2318 
2319  LWDEBUG(4, "checking ring distances");
2320 
2321  /* Not contained, so what's the actual distance? */
2322  for ( i = 0; i < lwpoly->nrings; i++ )
2323  {
2324  double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwline->points, spheroid, tolerance, check_intersection);
2325  LWDEBUGF(4, "ring[%d] ring_distance = %.8g", i, ring_distance);
2326  if ( ring_distance < distance )
2327  distance = ring_distance;
2328  if ( distance < tolerance )
2329  return distance;
2330  }
2331  LWDEBUGF(4, "all rings checked, returning distance = %.8g", distance);
2332  return distance;
2333 
2334  }
2335 
2336  /* Polygon/polygon case, if start point-in-poly, return zero, else
2337  * return distance. */
2338  if (type1 == POLYGONTYPE && type2 == POLYGONTYPE)
2339  {
2340  const POINT2D* p;
2341  LWPOLY* lwpoly1 = (LWPOLY*)lwgeom1;
2342  LWPOLY* lwpoly2 = (LWPOLY*)lwgeom2;
2343  double distance = FLT_MAX;
2344  uint32_t i, j;
2345 
2346  /* Point of 2 in polygon 1 implies zero distance */
2347  p = getPoint2d_cp(lwpoly1->rings[0], 0);
2348  if (lwpoly_covers_point2d(lwpoly2, p)) return 0.0;
2349 
2350  /* Point of 1 in polygon 2 implies zero distance */
2351  p = getPoint2d_cp(lwpoly2->rings[0], 0);
2352  if (lwpoly_covers_point2d(lwpoly1, p)) return 0.0;
2353 
2354  /* Not contained, so what's the actual distance? */
2355  for (i = 0; i < lwpoly1->nrings; i++)
2356  {
2357  for (j = 0; j < lwpoly2->nrings; j++)
2358  {
2359  double ring_distance =
2361  lwpoly1->rings[i],
2362  lwpoly2->rings[j],
2363  spheroid,
2364  tolerance,
2365  check_intersection);
2366  if (ring_distance < distance)
2367  distance = ring_distance;
2368  if (distance < tolerance) return distance;
2369  }
2370  }
2371  return distance;
2372  }
2373 
2374  /* Recurse into collections */
2375  if ( lwtype_is_collection(type1) )
2376  {
2377  uint32_t i;
2378  double distance = FLT_MAX;
2379  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom1;
2380 
2381  for ( i = 0; i < col->ngeoms; i++ )
2382  {
2383  double geom_distance = lwgeom_distance_spheroid(
2384  col->geoms[i], lwgeom2, spheroid, tolerance);
2385  if ( geom_distance < distance )
2386  distance = geom_distance;
2387  if ( distance < tolerance )
2388  return distance;
2389  }
2390  return distance;
2391  }
2392 
2393  /* Recurse into collections */
2394  if ( lwtype_is_collection(type2) )
2395  {
2396  uint32_t i;
2397  double distance = FLT_MAX;
2398  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom2;
2399 
2400  for ( i = 0; i < col->ngeoms; i++ )
2401  {
2402  double geom_distance = lwgeom_distance_spheroid(lwgeom1, col->geoms[i], spheroid, tolerance);
2403  if ( geom_distance < distance )
2404  distance = geom_distance;
2405  if ( distance < tolerance )
2406  return distance;
2407  }
2408  return distance;
2409  }
2410 
2411 
2412  lwerror("arguments include unsupported geometry type (%s, %s)", lwtype_name(type1), lwtype_name(type1));
2413  return -1.0;
2414 
2415 }
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:108
#define LINETYPE
Definition: liblwgeom.h:117
int lwtype_is_collection(uint8_t type)
Determine whether a type number is a collection or not.
Definition: lwgeom.c:1088
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:116
#define POLYGONTYPE
Definition: liblwgeom.h:118
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:107
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:2523
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:2192
int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox)
Calculate the geodetic bounding box for an LWGEOM.
Definition: lwgeodetic.c:3002
static double ptarray_distance_spheroid(const POINTARRAY *pa1, const POINTARRAY *pa2, const SPHEROID *s, double tolerance, int check_intersection)
Definition: lwgeodetic.c:1837
#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:594
LWGEOM ** geoms
Definition: liblwgeom.h:589
uint8_t type
Definition: liblwgeom.h:476
GBOX * bbox
Definition: liblwgeom.h:472
POINTARRAY * points
Definition: liblwgeom.h:497
POINTARRAY * point
Definition: liblwgeom.h:485
POINTARRAY ** rings
Definition: liblwgeom.h:533
uint32_t nrings
Definition: liblwgeom.h:538

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: