PostGIS  3.7.0dev-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 2066 of file lwgeodetic.c.

2067 {
2068  uint8_t type1, type2;
2069  int check_intersection = LW_FALSE;
2070  GBOX gbox1, gbox2;
2071 
2072  gbox_init(&gbox1);
2073  gbox_init(&gbox2);
2074 
2075  assert(lwgeom1);
2076  assert(lwgeom2);
2077 
2078  LWDEBUGF(4, "entered function, tolerance %.8g", tolerance);
2079 
2080  /* What's the distance to an empty geometry? We don't know.
2081  Return a negative number so the caller can catch this case. */
2082  if ( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) )
2083  {
2084  return -1.0;
2085  }
2086 
2087  type1 = lwgeom1->type;
2088  type2 = lwgeom2->type;
2089 
2090  /* Make sure we have boxes */
2091  if ( FLAGS_GET_GEODETIC(lwgeom1->flags) && lwgeom1->bbox )
2092  gbox1 = *(lwgeom1->bbox);
2093  else
2094  lwgeom_calculate_gbox_geodetic(lwgeom1, &gbox1);
2095 
2096  /* Make sure we have boxes */
2097  if ( FLAGS_GET_GEODETIC(lwgeom2->flags) && lwgeom2->bbox )
2098  gbox2 = *(lwgeom2->bbox);
2099  else
2100  lwgeom_calculate_gbox_geodetic(lwgeom2, &gbox2);
2101 
2102  /* If the boxes aren't disjoint, we have to check for edge intersections */
2103  if ( gbox_overlaps(&gbox1, &gbox2) )
2104  check_intersection = LW_TRUE;
2105 
2106  /* Point/line combinations can all be handled with simple point array iterations */
2107  if ( ( type1 == POINTTYPE || type1 == LINETYPE ) &&
2108  ( type2 == POINTTYPE || type2 == LINETYPE ) )
2109  {
2110  POINTARRAY *pa1, *pa2;
2111 
2112  if ( type1 == POINTTYPE )
2113  pa1 = ((LWPOINT*)lwgeom1)->point;
2114  else
2115  pa1 = ((LWLINE*)lwgeom1)->points;
2116 
2117  if ( type2 == POINTTYPE )
2118  pa2 = ((LWPOINT*)lwgeom2)->point;
2119  else
2120  pa2 = ((LWLINE*)lwgeom2)->points;
2121 
2122  return ptarray_distance_spheroid(pa1, pa2, spheroid, tolerance, check_intersection);
2123  }
2124 
2125  /* Point/Polygon cases, if point-in-poly, return zero, else return distance. */
2126  if ( ( type1 == POLYGONTYPE && type2 == POINTTYPE ) ||
2127  ( type2 == POLYGONTYPE && type1 == POINTTYPE ) )
2128  {
2129  const POINT2D *p;
2130  LWPOLY *lwpoly;
2131  LWPOINT *lwpt;
2132  double distance = FLT_MAX;
2133  uint32_t i;
2134 
2135  if ( type1 == POINTTYPE )
2136  {
2137  lwpt = (LWPOINT*)lwgeom1;
2138  lwpoly = (LWPOLY*)lwgeom2;
2139  }
2140  else
2141  {
2142  lwpt = (LWPOINT*)lwgeom2;
2143  lwpoly = (LWPOLY*)lwgeom1;
2144  }
2145  p = getPoint2d_cp(lwpt->point, 0);
2146 
2147  /* Point in polygon implies zero distance */
2148  if ( lwpoly_covers_point2d(lwpoly, p) )
2149  {
2150  return 0.0;
2151  }
2152 
2153  /* Not inside, so what's the actual distance? */
2154  for ( i = 0; i < lwpoly->nrings; i++ )
2155  {
2156  double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwpt->point, spheroid, tolerance, check_intersection);
2157  if ( ring_distance < distance )
2158  distance = ring_distance;
2159  if ( distance <= tolerance )
2160  return distance;
2161  }
2162  return distance;
2163  }
2164 
2165  /* Line/polygon case, if start point-in-poly, return zero, else return distance. */
2166  if ( ( type1 == POLYGONTYPE && type2 == LINETYPE ) ||
2167  ( type2 == POLYGONTYPE && type1 == LINETYPE ) )
2168  {
2169  const POINT2D *p;
2170  LWPOLY *lwpoly;
2171  LWLINE *lwline;
2172  double distance = FLT_MAX;
2173  uint32_t i;
2174 
2175  if ( type1 == LINETYPE )
2176  {
2177  lwline = (LWLINE*)lwgeom1;
2178  lwpoly = (LWPOLY*)lwgeom2;
2179  }
2180  else
2181  {
2182  lwline = (LWLINE*)lwgeom2;
2183  lwpoly = (LWPOLY*)lwgeom1;
2184  }
2185  p = getPoint2d_cp(lwline->points, 0);
2186 
2187  LWDEBUG(4, "checking if a point of line is in polygon");
2188 
2189  /* Point in polygon implies zero distance */
2190  if ( lwpoly_covers_point2d(lwpoly, p) )
2191  return 0.0;
2192 
2193  LWDEBUG(4, "checking ring distances");
2194 
2195  /* Not contained, so what's the actual distance? */
2196  for ( i = 0; i < lwpoly->nrings; i++ )
2197  {
2198  double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwline->points, spheroid, tolerance, check_intersection);
2199  LWDEBUGF(4, "ring[%d] ring_distance = %.8g", i, ring_distance);
2200  if ( ring_distance < distance )
2201  distance = ring_distance;
2202  if ( distance <= tolerance )
2203  return distance;
2204  }
2205  LWDEBUGF(4, "all rings checked, returning distance = %.8g", distance);
2206  return distance;
2207 
2208  }
2209 
2210  /* Polygon/polygon case, if start point-in-poly, return zero, else
2211  * return distance. */
2212  if (type1 == POLYGONTYPE && type2 == POLYGONTYPE)
2213  {
2214  const POINT2D* p;
2215  LWPOLY* lwpoly1 = (LWPOLY*)lwgeom1;
2216  LWPOLY* lwpoly2 = (LWPOLY*)lwgeom2;
2217  double distance = FLT_MAX;
2218  uint32_t i, j;
2219 
2220  /* Point of 2 in polygon 1 implies zero distance */
2221  p = getPoint2d_cp(lwpoly1->rings[0], 0);
2222  if (lwpoly_covers_point2d(lwpoly2, p)) return 0.0;
2223 
2224  /* Point of 1 in polygon 2 implies zero distance */
2225  p = getPoint2d_cp(lwpoly2->rings[0], 0);
2226  if (lwpoly_covers_point2d(lwpoly1, p)) return 0.0;
2227 
2228  /* Not contained, so what's the actual distance? */
2229  for (i = 0; i < lwpoly1->nrings; i++)
2230  {
2231  for (j = 0; j < lwpoly2->nrings; j++)
2232  {
2233  double ring_distance =
2235  lwpoly1->rings[i],
2236  lwpoly2->rings[j],
2237  spheroid,
2238  tolerance,
2239  check_intersection);
2240  if (ring_distance < distance)
2241  distance = ring_distance;
2242  if (distance <= tolerance) return distance;
2243  }
2244  }
2245  return distance;
2246  }
2247 
2248  /* Recurse into collections */
2249  if ( lwtype_is_collection(type1) )
2250  {
2251  uint32_t i;
2252  double distance = FLT_MAX;
2253  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom1;
2254 
2255  for ( i = 0; i < col->ngeoms; i++ )
2256  {
2257  double geom_distance = lwgeom_distance_spheroid(
2258  col->geoms[i], lwgeom2, spheroid, tolerance);
2259  if ( geom_distance < distance )
2260  distance = geom_distance;
2261  if ( distance <= tolerance )
2262  return distance;
2263  }
2264  return distance;
2265  }
2266 
2267  /* Recurse into collections */
2268  if ( lwtype_is_collection(type2) )
2269  {
2270  uint32_t i;
2271  double distance = FLT_MAX;
2272  LWCOLLECTION *col = (LWCOLLECTION*)lwgeom2;
2273 
2274  for ( i = 0; i < col->ngeoms; i++ )
2275  {
2276  double geom_distance = lwgeom_distance_spheroid(lwgeom1, col->geoms[i], spheroid, tolerance);
2277  if ( geom_distance < distance )
2278  distance = geom_distance;
2279  if ( distance <= tolerance )
2280  return distance;
2281  }
2282  return distance;
2283  }
2284 
2285 
2286  lwerror("arguments include unsupported geometry type (%s, %s)", lwtype_name(type1), lwtype_name(type1));
2287  return -1.0;
2288 
2289 }
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:1168
#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
#define FLAGS_GET_GEODETIC(flags)
Definition: liblwgeom.h:168
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:2397
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:2066
int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox)
Calculate the geodetic bounding box for an LWGEOM.
Definition: lwgeodetic.c:2876
static double ptarray_distance_spheroid(const POINTARRAY *pa1, const POINTARRAY *pa2, const SPHEROID *s, double tolerance, int check_intersection)
Definition: lwgeodetic.c:1756
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:101
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:106
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
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:97
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:199
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
lwflags_t flags
Definition: liblwgeom.h:461
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(), LWGEOM::flags, FLAGS_GET_GEODETIC, 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: