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