PostGIS  3.3.9dev-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 2058 of file lwgeodetic.c.

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

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: