PostGIS  2.5.7dev-r@@SVN_REVISION@@

◆ lwgeom_distance_spheroid()

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

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

A spheroid with major axis == minor axis will be treated as a sphere. Pass in a tolerance in spheroid units.

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 2187 of file lwgeodetic.c.

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

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: