Loading liblwgeom/libgeom.h +6 −0 Original line number Diff line number Diff line Loading @@ -376,6 +376,12 @@ extern int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox); */ extern double lwgeom_distance_sphere(LWGEOM *lwgeom1, LWGEOM *lwgeom2, GBOX gbox1, GBOX gbox2, double tolerance); /** * Calculate the geodetic distance from lwgeom1 to lwgeom2 on the spheroid. * Pass in a major axis, minor axis and tolerance using the same units for each (meters, generally). */ extern double lwgeom_distance_spheroid(LWGEOM *lwgeom1, LWGEOM *lwgeom2, GBOX gbox1, GBOX gbox2, double a, double b, double tolerance); /** * Calculate the geodetic area of a lwgeom on the unit sphere. The result * will have to by multiplied by the real radius^2 to get the real area. Loading liblwgeom/lwspheroid.c +349 −1 Original line number Diff line number Diff line Loading @@ -100,3 +100,351 @@ double spheroid_distance(GEOGRAPHIC_POINT r, GEOGRAPHIC_POINT s, double a, doubl return distance; } double ptarray_distance_spheroid(POINTARRAY *pa1, POINTARRAY *pa2, double a, double b, double tolerance, int check_intersection) { GEOGRAPHIC_EDGE e1, e2; GEOGRAPHIC_POINT g1, g2; GEOGRAPHIC_POINT nearest1, nearest2; POINT2D p; double radius = (2.0*a + b)/3.0; double distance; int i, j; /* Make result really big, so that everything will be smaller than it */ distance = MAXFLOAT; /* Empty point arrays? Return negative */ if ( pa1->npoints == 0 || pa1->npoints == 0 ) return -1.0; /* Handle point/point case here */ if ( pa1->npoints == 1 && pa2->npoints == 1 ) { getPoint2d_p(pa1, 0, &p); geographic_point_init(p.x, p.y, &g1); getPoint2d_p(pa2, 0, &p); geographic_point_init(p.x, p.y, &g2); return spheroid_distance(g1, g2, a, b); } /* Handle point/line case here */ if ( pa1->npoints == 1 || pa2->npoints == 1 ) { /* Handle one/many case here */ int i; POINTARRAY *pa_one, *pa_many; if( pa1->npoints == 1 ) { pa_one = pa1; pa_many = pa2; } else { pa_one = pa2; pa_many = pa1; } /* Initialize our point */ getPoint2d_p(pa_one, 0, &p); geographic_point_init(p.x, p.y, &g1); /* Initialize start of line */ getPoint2d_p(pa_many, 0, &p); geographic_point_init(p.x, p.y, &(e1.start)); /* Iterate through the edges in our line */ for( i = 1; i < pa_many->npoints; i++ ) { double d; getPoint2d_p(pa_many, i, &p); geographic_point_init(p.x, p.y, &(e1.end)); d = radius * edge_distance_to_point(e1, g1, &g2); if( d < distance ) { distance = d; nearest2 = g2; } if( d < tolerance ) { double sd = spheroid_distance(g1, nearest2, a, b); if( sd < tolerance ) return sd; } e1.start = e1.end; } return spheroid_distance(g1, nearest2, a, b); } /* Initialize start of line 1 */ getPoint2d_p(pa1, 0, &p); geographic_point_init(p.x, p.y, &(e1.start)); /* Handle line/line case */ for( i = 1; i < pa1->npoints; i++ ) { getPoint2d_p(pa1, i, &p); geographic_point_init(p.x, p.y, &(e1.end)); /* Initialize start of line 2 */ getPoint2d_p(pa2, 0, &p); geographic_point_init(p.x, p.y, &(e2.start)); for( j = 1; j < pa2->npoints; j++ ) { double d; GEOGRAPHIC_POINT g; getPoint2d_p(pa2, j, &p); geographic_point_init(p.x, p.y, &(e2.end)); LWDEBUGF(4, "e1.start == GPOINT(%.6g %.6g) ", e1.start.lat, e1.start.lon); LWDEBUGF(4, "e1.end == GPOINT(%.6g %.6g) ", e1.end.lat, e1.end.lon); LWDEBUGF(4, "e2.start == GPOINT(%.6g %.6g) ", e2.start.lat, e2.start.lon); LWDEBUGF(4, "e2.end == GPOINT(%.6g %.6g) ", e2.end.lat, e2.end.lon); if ( check_intersection && edge_intersection(e1, e2, &g) ) { LWDEBUG(4,"edge intersection! returning 0.0"); return 0.0; } d = radius * edge_distance_to_edge(e1, e2, &g1, &g2); LWDEBUGF(4,"got edge_distance_to_edge %.8g", d); if( d < distance ) { distance = d; nearest1 = g1; nearest2 = g2; } if( d < tolerance ) { double sd = spheroid_distance(nearest1, nearest2, a, b); if( sd < tolerance ) return sd; } /* Copy end to start to allow a new end value in next iteration */ e2.start = e2.end; } /* Copy end to start to allow a new end value in next iteration */ e1.start = e1.end; } return spheroid_distance(nearest1, nearest2, a, b); } double lwgeom_distance_spheroid(LWGEOM *lwgeom1, LWGEOM *lwgeom2, GBOX gbox1, GBOX gbox2, double a, double b, double tolerance) { int type1, type2; int check_intersection = LW_FALSE; assert(lwgeom1); assert(lwgeom2); LWDEBUGF(4, "entered function, tolerance %.8g", tolerance); /* What's the distance to an empty geometry? We don't know. */ if( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) ) { return 0.0; } type1 = TYPE_GETTYPE(lwgeom1->type); type2 = TYPE_GETTYPE(lwgeom2->type); /* If the boxes aren't disjoint, we have to check for edge intersections */ if( gbox_overlaps(gbox1, gbox2) ) check_intersection = LW_TRUE; /* Point/line combinations can all be handled with simple point array iterations */ if( ( type1 == POINTTYPE || type1 == LINETYPE ) && ( type2 == POINTTYPE || type2 == LINETYPE ) ) { POINTARRAY *pa1, *pa2; if( type1 == POINTTYPE ) pa1 = ((LWPOINT*)lwgeom1)->point; else pa1 = ((LWLINE*)lwgeom1)->points; if( type2 == POINTTYPE ) pa2 = ((LWPOINT*)lwgeom2)->point; else pa2 = ((LWLINE*)lwgeom2)->points; return ptarray_distance_spheroid(pa1, pa2, a, b, tolerance, check_intersection); } /* Point/Polygon cases, if point-in-poly, return zero, else return distance. */ if( ( type1 == POLYGONTYPE && type2 == POINTTYPE ) || ( type2 == POLYGONTYPE && type1 == POINTTYPE ) ) { POINT2D p; LWPOLY *lwpoly; LWPOINT *lwpt; GBOX gbox; double distance = MAXFLOAT; int i; if( type1 == POINTTYPE ) { lwpt = (LWPOINT*)lwgeom1; lwpoly = (LWPOLY*)lwgeom2; gbox = gbox2; } else { lwpt = (LWPOINT*)lwgeom2; lwpoly = (LWPOLY*)lwgeom1; gbox = gbox1; } getPoint2d_p(lwpt->point, 0, &p); /* Point in polygon implies zero distance */ if( lwpoly_covers_point2d(lwpoly, gbox, p) ) return 0.0; /* Not inside, so what's the actual distance? */ for( i = 0; i < lwpoly->nrings; i++ ) { double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwpt->point, a, b, tolerance, check_intersection); if( ring_distance < distance ) distance = ring_distance; if( distance < tolerance ) return distance; } return distance; } /* Line/polygon case, if start point-in-poly, return zero, else return distance. */ if( ( type1 == POLYGONTYPE && type2 == LINETYPE ) || ( type2 == POLYGONTYPE && type1 == LINETYPE ) ) { POINT2D p; LWPOLY *lwpoly; LWLINE *lwline; GBOX gbox; double distance = MAXFLOAT; int i; if( type1 == LINETYPE ) { lwline = (LWLINE*)lwgeom1; lwpoly = (LWPOLY*)lwgeom2; gbox = gbox2; } else { lwline = (LWLINE*)lwgeom2; lwpoly = (LWPOLY*)lwgeom1; gbox = gbox1; } getPoint2d_p(lwline->points, 0, &p); LWDEBUG(4, "checking if a point of line is in polygon"); /* Point in polygon implies zero distance */ if( lwpoly_covers_point2d(lwpoly, gbox, p) ) return 0.0; LWDEBUG(4, "checking ring distances"); /* Not contained, so what's the actual distance? */ for( i = 0; i < lwpoly->nrings; i++ ) { double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwline->points, a, b, tolerance, check_intersection); LWDEBUGF(4, "ring[%d] ring_distance = %.8g", i, ring_distance); if( ring_distance < distance ) distance = ring_distance; if( distance < tolerance ) return distance; } LWDEBUGF(4, "all rings checked, returning distance = %.8g", distance); return distance; } /* Polygon/polygon case, if start point-in-poly, return zero, else return distance. */ if( ( type1 == POLYGONTYPE && type2 == POLYGONTYPE ) || ( type2 == POLYGONTYPE && type1 == POLYGONTYPE ) ) { POINT2D p; LWPOLY *lwpoly1 = (LWPOLY*)lwgeom1; LWPOLY *lwpoly2 = (LWPOLY*)lwgeom2; double distance = MAXFLOAT; int i, j; /* Point of 2 in polygon 1 implies zero distance */ getPoint2d_p(lwpoly1->rings[0], 0, &p); if( lwpoly_covers_point2d(lwpoly2, gbox2, p) ) return 0.0; /* Point of 1 in polygon 2 implies zero distance */ getPoint2d_p(lwpoly2->rings[0], 0, &p); if( lwpoly_covers_point2d(lwpoly1, gbox1, p) ) return 0.0; /* Not contained, so what's the actual distance? */ for( i = 0; i < lwpoly1->nrings; i++ ) { for( j = 0; j < lwpoly2->nrings; j++ ) { double ring_distance = ptarray_distance_spheroid(lwpoly1->rings[i], lwpoly2->rings[j], a, b, tolerance, check_intersection); if( ring_distance < distance ) distance = ring_distance; if( distance < tolerance ) return distance; } } return distance; } /* Recurse into collections */ if( lwgeom_is_collection(type1) ) { int i; double distance = MAXFLOAT; LWCOLLECTION *col = (LWCOLLECTION*)lwgeom1; for( i = 0; i < col->ngeoms; i++ ) { double geom_distance = lwgeom_distance_spheroid(col->geoms[i], lwgeom2, gbox1, gbox2, a, b, tolerance); if( geom_distance < distance ) distance = geom_distance; if( distance < tolerance ) return distance; } return distance; } /* Recurse into collections */ if( lwgeom_is_collection(type2) ) { int i; double distance = MAXFLOAT; LWCOLLECTION *col = (LWCOLLECTION*)lwgeom2; for( i = 0; i < col->ngeoms; i++ ) { double geom_distance = lwgeom_distance_spheroid(lwgeom1, col->geoms[i], gbox1, gbox2, a, b, tolerance); if( geom_distance < distance ) distance = geom_distance; if( distance < tolerance ) return distance; } return distance; } lwerror("arguments include unsupported geometry type (%s, %s)", lwgeom_typename(type1), lwgeom_typename(type1)); return -1.0; } liblwgeom/lwspheroid.h +1 −0 Original line number Diff line number Diff line Loading @@ -12,5 +12,6 @@ */ double spheroid_distance(GEOGRAPHIC_POINT r, GEOGRAPHIC_POINT s, double a, double b); double ptarray_distance_spheroid(POINTARRAY *pa1, POINTARRAY *pa2, double a, double b, double tolerance, int check_intersection); postgis/geography.h +1 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #define WGS84_MINOR_AXIS 6356752.314245179498 #define WGS84_RADIUS ((2.0 * WGS84_MAJOR_AXIS + WGS84_MINOR_AXIS ) / 3.0) /********************************************************************** ** Useful functions for all GEOGRAPHY handlers. */ Loading postgis/geography.sql.in.c +11 −3 Original line number Diff line number Diff line Loading @@ -435,15 +435,22 @@ CREATE OR REPLACE FUNCTION ST_AsGeoJson(int4, geography, int4, int4) -- Stop calculation and return immediately once distance is less than tolerance -- Availability: 1.5.0 CREATE OR REPLACE FUNCTION _ST_Distance(geography, geography, float8) CREATE OR REPLACE FUNCTION _ST_Distance(geography, geography, float8, boolean) RETURNS float8 AS 'MODULE_PATHNAME','geography_distance_sphere' LANGUAGE 'C' IMMUTABLE STRICT; -- Availability: 1.5.0 CREATE OR REPLACE FUNCTION ST_Distance(geography, geography, boolean) RETURNS float8 AS 'SELECT _ST_Distance($1, $2, 0.0, $3)' LANGUAGE 'SQL' IMMUTABLE STRICT; -- Currently defaulting to spheroid calculations -- Availability: 1.5.0 CREATE OR REPLACE FUNCTION ST_Distance(geography, geography) RETURNS float8 AS 'SELECT _ST_Distance($1, $2, 0.0)' AS 'SELECT _ST_Distance($1, $2, 0.0, true)' LANGUAGE 'SQL' IMMUTABLE STRICT; -- Only expands the bounding box, the actual geometry will remain unchanged, use with care. Loading @@ -453,10 +460,11 @@ CREATE OR REPLACE FUNCTION _ST_Expand(geography, float8) AS 'MODULE_PATHNAME','geography_expand' LANGUAGE 'C' IMMUTABLE STRICT; -- Currently defaulting to spheroid calculations -- Availability: 1.5.0 CREATE OR REPLACE FUNCTION ST_DWithin(geography, geography, float8) RETURNS boolean AS 'SELECT $1 && _ST_Expand($2,$3) AND $2 && _ST_Expand($1,$3) AND _ST_Distance($1, $2, $3) < $3' AS 'SELECT $1 && _ST_Expand($2,$3) AND $2 && _ST_Expand($1,$3) AND _ST_Distance($1, $2, $3, true) < $3' LANGUAGE 'SQL' IMMUTABLE; -- Availability: 1.5.0 Loading Loading
liblwgeom/libgeom.h +6 −0 Original line number Diff line number Diff line Loading @@ -376,6 +376,12 @@ extern int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox); */ extern double lwgeom_distance_sphere(LWGEOM *lwgeom1, LWGEOM *lwgeom2, GBOX gbox1, GBOX gbox2, double tolerance); /** * Calculate the geodetic distance from lwgeom1 to lwgeom2 on the spheroid. * Pass in a major axis, minor axis and tolerance using the same units for each (meters, generally). */ extern double lwgeom_distance_spheroid(LWGEOM *lwgeom1, LWGEOM *lwgeom2, GBOX gbox1, GBOX gbox2, double a, double b, double tolerance); /** * Calculate the geodetic area of a lwgeom on the unit sphere. The result * will have to by multiplied by the real radius^2 to get the real area. Loading
liblwgeom/lwspheroid.c +349 −1 Original line number Diff line number Diff line Loading @@ -100,3 +100,351 @@ double spheroid_distance(GEOGRAPHIC_POINT r, GEOGRAPHIC_POINT s, double a, doubl return distance; } double ptarray_distance_spheroid(POINTARRAY *pa1, POINTARRAY *pa2, double a, double b, double tolerance, int check_intersection) { GEOGRAPHIC_EDGE e1, e2; GEOGRAPHIC_POINT g1, g2; GEOGRAPHIC_POINT nearest1, nearest2; POINT2D p; double radius = (2.0*a + b)/3.0; double distance; int i, j; /* Make result really big, so that everything will be smaller than it */ distance = MAXFLOAT; /* Empty point arrays? Return negative */ if ( pa1->npoints == 0 || pa1->npoints == 0 ) return -1.0; /* Handle point/point case here */ if ( pa1->npoints == 1 && pa2->npoints == 1 ) { getPoint2d_p(pa1, 0, &p); geographic_point_init(p.x, p.y, &g1); getPoint2d_p(pa2, 0, &p); geographic_point_init(p.x, p.y, &g2); return spheroid_distance(g1, g2, a, b); } /* Handle point/line case here */ if ( pa1->npoints == 1 || pa2->npoints == 1 ) { /* Handle one/many case here */ int i; POINTARRAY *pa_one, *pa_many; if( pa1->npoints == 1 ) { pa_one = pa1; pa_many = pa2; } else { pa_one = pa2; pa_many = pa1; } /* Initialize our point */ getPoint2d_p(pa_one, 0, &p); geographic_point_init(p.x, p.y, &g1); /* Initialize start of line */ getPoint2d_p(pa_many, 0, &p); geographic_point_init(p.x, p.y, &(e1.start)); /* Iterate through the edges in our line */ for( i = 1; i < pa_many->npoints; i++ ) { double d; getPoint2d_p(pa_many, i, &p); geographic_point_init(p.x, p.y, &(e1.end)); d = radius * edge_distance_to_point(e1, g1, &g2); if( d < distance ) { distance = d; nearest2 = g2; } if( d < tolerance ) { double sd = spheroid_distance(g1, nearest2, a, b); if( sd < tolerance ) return sd; } e1.start = e1.end; } return spheroid_distance(g1, nearest2, a, b); } /* Initialize start of line 1 */ getPoint2d_p(pa1, 0, &p); geographic_point_init(p.x, p.y, &(e1.start)); /* Handle line/line case */ for( i = 1; i < pa1->npoints; i++ ) { getPoint2d_p(pa1, i, &p); geographic_point_init(p.x, p.y, &(e1.end)); /* Initialize start of line 2 */ getPoint2d_p(pa2, 0, &p); geographic_point_init(p.x, p.y, &(e2.start)); for( j = 1; j < pa2->npoints; j++ ) { double d; GEOGRAPHIC_POINT g; getPoint2d_p(pa2, j, &p); geographic_point_init(p.x, p.y, &(e2.end)); LWDEBUGF(4, "e1.start == GPOINT(%.6g %.6g) ", e1.start.lat, e1.start.lon); LWDEBUGF(4, "e1.end == GPOINT(%.6g %.6g) ", e1.end.lat, e1.end.lon); LWDEBUGF(4, "e2.start == GPOINT(%.6g %.6g) ", e2.start.lat, e2.start.lon); LWDEBUGF(4, "e2.end == GPOINT(%.6g %.6g) ", e2.end.lat, e2.end.lon); if ( check_intersection && edge_intersection(e1, e2, &g) ) { LWDEBUG(4,"edge intersection! returning 0.0"); return 0.0; } d = radius * edge_distance_to_edge(e1, e2, &g1, &g2); LWDEBUGF(4,"got edge_distance_to_edge %.8g", d); if( d < distance ) { distance = d; nearest1 = g1; nearest2 = g2; } if( d < tolerance ) { double sd = spheroid_distance(nearest1, nearest2, a, b); if( sd < tolerance ) return sd; } /* Copy end to start to allow a new end value in next iteration */ e2.start = e2.end; } /* Copy end to start to allow a new end value in next iteration */ e1.start = e1.end; } return spheroid_distance(nearest1, nearest2, a, b); } double lwgeom_distance_spheroid(LWGEOM *lwgeom1, LWGEOM *lwgeom2, GBOX gbox1, GBOX gbox2, double a, double b, double tolerance) { int type1, type2; int check_intersection = LW_FALSE; assert(lwgeom1); assert(lwgeom2); LWDEBUGF(4, "entered function, tolerance %.8g", tolerance); /* What's the distance to an empty geometry? We don't know. */ if( lwgeom_is_empty(lwgeom1) || lwgeom_is_empty(lwgeom2) ) { return 0.0; } type1 = TYPE_GETTYPE(lwgeom1->type); type2 = TYPE_GETTYPE(lwgeom2->type); /* If the boxes aren't disjoint, we have to check for edge intersections */ if( gbox_overlaps(gbox1, gbox2) ) check_intersection = LW_TRUE; /* Point/line combinations can all be handled with simple point array iterations */ if( ( type1 == POINTTYPE || type1 == LINETYPE ) && ( type2 == POINTTYPE || type2 == LINETYPE ) ) { POINTARRAY *pa1, *pa2; if( type1 == POINTTYPE ) pa1 = ((LWPOINT*)lwgeom1)->point; else pa1 = ((LWLINE*)lwgeom1)->points; if( type2 == POINTTYPE ) pa2 = ((LWPOINT*)lwgeom2)->point; else pa2 = ((LWLINE*)lwgeom2)->points; return ptarray_distance_spheroid(pa1, pa2, a, b, tolerance, check_intersection); } /* Point/Polygon cases, if point-in-poly, return zero, else return distance. */ if( ( type1 == POLYGONTYPE && type2 == POINTTYPE ) || ( type2 == POLYGONTYPE && type1 == POINTTYPE ) ) { POINT2D p; LWPOLY *lwpoly; LWPOINT *lwpt; GBOX gbox; double distance = MAXFLOAT; int i; if( type1 == POINTTYPE ) { lwpt = (LWPOINT*)lwgeom1; lwpoly = (LWPOLY*)lwgeom2; gbox = gbox2; } else { lwpt = (LWPOINT*)lwgeom2; lwpoly = (LWPOLY*)lwgeom1; gbox = gbox1; } getPoint2d_p(lwpt->point, 0, &p); /* Point in polygon implies zero distance */ if( lwpoly_covers_point2d(lwpoly, gbox, p) ) return 0.0; /* Not inside, so what's the actual distance? */ for( i = 0; i < lwpoly->nrings; i++ ) { double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwpt->point, a, b, tolerance, check_intersection); if( ring_distance < distance ) distance = ring_distance; if( distance < tolerance ) return distance; } return distance; } /* Line/polygon case, if start point-in-poly, return zero, else return distance. */ if( ( type1 == POLYGONTYPE && type2 == LINETYPE ) || ( type2 == POLYGONTYPE && type1 == LINETYPE ) ) { POINT2D p; LWPOLY *lwpoly; LWLINE *lwline; GBOX gbox; double distance = MAXFLOAT; int i; if( type1 == LINETYPE ) { lwline = (LWLINE*)lwgeom1; lwpoly = (LWPOLY*)lwgeom2; gbox = gbox2; } else { lwline = (LWLINE*)lwgeom2; lwpoly = (LWPOLY*)lwgeom1; gbox = gbox1; } getPoint2d_p(lwline->points, 0, &p); LWDEBUG(4, "checking if a point of line is in polygon"); /* Point in polygon implies zero distance */ if( lwpoly_covers_point2d(lwpoly, gbox, p) ) return 0.0; LWDEBUG(4, "checking ring distances"); /* Not contained, so what's the actual distance? */ for( i = 0; i < lwpoly->nrings; i++ ) { double ring_distance = ptarray_distance_spheroid(lwpoly->rings[i], lwline->points, a, b, tolerance, check_intersection); LWDEBUGF(4, "ring[%d] ring_distance = %.8g", i, ring_distance); if( ring_distance < distance ) distance = ring_distance; if( distance < tolerance ) return distance; } LWDEBUGF(4, "all rings checked, returning distance = %.8g", distance); return distance; } /* Polygon/polygon case, if start point-in-poly, return zero, else return distance. */ if( ( type1 == POLYGONTYPE && type2 == POLYGONTYPE ) || ( type2 == POLYGONTYPE && type1 == POLYGONTYPE ) ) { POINT2D p; LWPOLY *lwpoly1 = (LWPOLY*)lwgeom1; LWPOLY *lwpoly2 = (LWPOLY*)lwgeom2; double distance = MAXFLOAT; int i, j; /* Point of 2 in polygon 1 implies zero distance */ getPoint2d_p(lwpoly1->rings[0], 0, &p); if( lwpoly_covers_point2d(lwpoly2, gbox2, p) ) return 0.0; /* Point of 1 in polygon 2 implies zero distance */ getPoint2d_p(lwpoly2->rings[0], 0, &p); if( lwpoly_covers_point2d(lwpoly1, gbox1, p) ) return 0.0; /* Not contained, so what's the actual distance? */ for( i = 0; i < lwpoly1->nrings; i++ ) { for( j = 0; j < lwpoly2->nrings; j++ ) { double ring_distance = ptarray_distance_spheroid(lwpoly1->rings[i], lwpoly2->rings[j], a, b, tolerance, check_intersection); if( ring_distance < distance ) distance = ring_distance; if( distance < tolerance ) return distance; } } return distance; } /* Recurse into collections */ if( lwgeom_is_collection(type1) ) { int i; double distance = MAXFLOAT; LWCOLLECTION *col = (LWCOLLECTION*)lwgeom1; for( i = 0; i < col->ngeoms; i++ ) { double geom_distance = lwgeom_distance_spheroid(col->geoms[i], lwgeom2, gbox1, gbox2, a, b, tolerance); if( geom_distance < distance ) distance = geom_distance; if( distance < tolerance ) return distance; } return distance; } /* Recurse into collections */ if( lwgeom_is_collection(type2) ) { int i; double distance = MAXFLOAT; LWCOLLECTION *col = (LWCOLLECTION*)lwgeom2; for( i = 0; i < col->ngeoms; i++ ) { double geom_distance = lwgeom_distance_spheroid(lwgeom1, col->geoms[i], gbox1, gbox2, a, b, tolerance); if( geom_distance < distance ) distance = geom_distance; if( distance < tolerance ) return distance; } return distance; } lwerror("arguments include unsupported geometry type (%s, %s)", lwgeom_typename(type1), lwgeom_typename(type1)); return -1.0; }
liblwgeom/lwspheroid.h +1 −0 Original line number Diff line number Diff line Loading @@ -12,5 +12,6 @@ */ double spheroid_distance(GEOGRAPHIC_POINT r, GEOGRAPHIC_POINT s, double a, double b); double ptarray_distance_spheroid(POINTARRAY *pa1, POINTARRAY *pa2, double a, double b, double tolerance, int check_intersection);
postgis/geography.h +1 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #define WGS84_MINOR_AXIS 6356752.314245179498 #define WGS84_RADIUS ((2.0 * WGS84_MAJOR_AXIS + WGS84_MINOR_AXIS ) / 3.0) /********************************************************************** ** Useful functions for all GEOGRAPHY handlers. */ Loading
postgis/geography.sql.in.c +11 −3 Original line number Diff line number Diff line Loading @@ -435,15 +435,22 @@ CREATE OR REPLACE FUNCTION ST_AsGeoJson(int4, geography, int4, int4) -- Stop calculation and return immediately once distance is less than tolerance -- Availability: 1.5.0 CREATE OR REPLACE FUNCTION _ST_Distance(geography, geography, float8) CREATE OR REPLACE FUNCTION _ST_Distance(geography, geography, float8, boolean) RETURNS float8 AS 'MODULE_PATHNAME','geography_distance_sphere' LANGUAGE 'C' IMMUTABLE STRICT; -- Availability: 1.5.0 CREATE OR REPLACE FUNCTION ST_Distance(geography, geography, boolean) RETURNS float8 AS 'SELECT _ST_Distance($1, $2, 0.0, $3)' LANGUAGE 'SQL' IMMUTABLE STRICT; -- Currently defaulting to spheroid calculations -- Availability: 1.5.0 CREATE OR REPLACE FUNCTION ST_Distance(geography, geography) RETURNS float8 AS 'SELECT _ST_Distance($1, $2, 0.0)' AS 'SELECT _ST_Distance($1, $2, 0.0, true)' LANGUAGE 'SQL' IMMUTABLE STRICT; -- Only expands the bounding box, the actual geometry will remain unchanged, use with care. Loading @@ -453,10 +460,11 @@ CREATE OR REPLACE FUNCTION _ST_Expand(geography, float8) AS 'MODULE_PATHNAME','geography_expand' LANGUAGE 'C' IMMUTABLE STRICT; -- Currently defaulting to spheroid calculations -- Availability: 1.5.0 CREATE OR REPLACE FUNCTION ST_DWithin(geography, geography, float8) RETURNS boolean AS 'SELECT $1 && _ST_Expand($2,$3) AND $2 && _ST_Expand($1,$3) AND _ST_Distance($1, $2, $3) < $3' AS 'SELECT $1 && _ST_Expand($2,$3) AND $2 && _ST_Expand($1,$3) AND _ST_Distance($1, $2, $3, true) < $3' LANGUAGE 'SQL' IMMUTABLE; -- Availability: 1.5.0 Loading