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 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 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: