Commit 4c1383ef authored by Bruce Momjian's avatar Bruce Momjian

The attached patch defines functions for getting distances between

points on the surface of the earth and locating points within a
specified distance using an index based on the contrib/cube package. The
new functions are all of language type sql. A couple of bugs in the old
earthdistance function based on the point datatype are fixed. A
regression test has been added for both sets of functions. The README
file has been updated to include documentation on the new stuff. There
are comments about how this package is also useful for Astronomers.

Bruno Wolff III
parent 337f73b1
--------------------------------------------------------------------- This contrib package contains two different approaches to calculating
I corrected a bug in the geo_distance code where two double constants great circle distances on the surface of the Earth. The one described
were declared as int. I changed the distance function to use the first depends on the contrib/cube package (which MUST be installed before
haversine formula which is more accurate for small distances. earthdistance is installed). The second one is based on the point
I added a regression test to the package. I added a grant statement datatype using latitude and longitude for the coordinates. The install
to give execute access for geo_distance to public. script makes the defined functions executable by anyone.
Make sure contrib/cube has been installed.
make
make install
make installcheck
To use these functions in a particular database as a postgres superuser do:
psql databasename < earthdistance.sql
-------------------------------------------
contrib/cube based Earth distance functions
Bruno Wolff III Bruno Wolff III
September 2002 September 2002
A spherical model of the Earth is used.
Data is stored in cubes that are points (both corners are the same) using 3
coordinates representing the distance from the center of the Earth.
The radius of the Earth is obtained from the earth() function. It is
given in meters. But by changing this one function you can change it
to use some other units or to use a different value of the radius
that you feel is more appropiate.
This package also has applications to astronomical databases as well.
Astronomers will probably want to change earth() to return a radius of
180/pi() so that distances are in degrees.
Functions are provided to allow for input in latitude and longitude (in
degrees), to allow for output of latitude and longitude, to calculate
the great circle distance between two points and to easily specify a
bounding box usable for index searches.
The functions are all 'sql' functions. If you want to make these functions
executable by other people you will also have to make the referenced
cube functions executable. cube(text), cube_distance(cube,cube),
cube_ll_coord(cube,int) and cube_enlarge(cube,float8,int) are used indirectly
by the earth distance functions. is_point(cube) and cube_dim(cube) are used
in suggested constraints for data in domain earth. cube_ur_coord(cube,int)
is used in the regression tests and might be useful for looking at bounding
box coordinates in user applications.
A domain of type cube named earth is defined. Since check constraints
are not supported for domains yet, this isn't as useful as it might be.
However the checks that should be supplied to all data of type earth are:
constraint not_point check(is_point(earth))
constraint not_3d check(cube_dim(earth) <= 3)
constraint on_surface check(abs(cube_distance(earth, '(0)'::cube) /
earth() - 1) < '10e-12'::float8);
The following functions are provided:
earth() - Returns the radius of the earth in meters.
sec_to_gc(float8) - Converts the normal straight line (secant) distance between
between two points on the surface of the Earth to the great circle distance
between them.
gc_to_sec(float8) - Converts the great circle distance between two points
on the surface of the Earth to the normal straight line (secant) distance
between them.
ll_to_cube(float8, float8) - Returns the location of a point on the surface of
the Earth given its latitude (argument 1) and longitude (argument 2) in degrees.
latitude(earth) - Returns the latitude in degrees of a point on the surface
of the Earth.
longitude(earth) - Returns the longitude in degrees of a point on the surface
of the Earth.
earth_distance(earth, earth) - Returns the great circle distance between
two points on the surface of the Earth.
earth_box(earth, float8) - Returns a box suitable for an indexed search using
the cube @ operator for points within a given great circle distance of a
location. Some points in this box are further than the specified great circle
distance from the location so a second check using earth_distance should be
made at the same time.
One advantage of using cube representation over a point using latitude and
longitude for coordinates, is that you don't have to worry about special
conditions at +/- 180 degrees of longitude or near the poles.
Below is the documentation for the Earth distance operator that works
with the point data type.
---------------------------------------------------------------------
I corrected a bug in the geo_distance code where two double constants
were declared as int. I also changed the distance function to use
the haversine formula which is more accurate for small distances.
Bruno Wolff
September 2002
--------------------------------------------------------------------- ---------------------------------------------------------------------
Date: Wed, 1 Apr 1998 15:19:32 -0600 (CST) Date: Wed, 1 Apr 1998 15:19:32 -0600 (CST)
From: Hal Snyder <hal@vailsys.com> From: Hal Snyder <hal@vailsys.com>
To: vmehr@ctp.com To: vmehr@ctp.com
......
...@@ -3,6 +3,77 @@ SET search_path = public; ...@@ -3,6 +3,77 @@ SET search_path = public;
SET autocommit TO 'on'; SET autocommit TO 'on';
-- The earth functions rely on contrib/cube having been installed and loaded.
-- earth() returns the radius of the earth in meters. This is the only
-- place you need to change things for the cube base distance functions
-- in order to use different units (or a better value for the Earth's radius).
CREATE OR REPLACE FUNCTION earth() RETURNS float8
LANGUAGE 'sql' IMMUTABLE
AS 'SELECT \'6378168\'::float8';
-- Astromers may want to change the earth function so that distances will be
-- returned in degrees. To do this comment out the above definition and
-- uncomment the one below. Note that doing this will break the regression
-- tests.
--
-- CREATE OR REPLACE FUNCTION earth() RETURNS float8
-- LANGUAGE 'sql' IMMUTABLE
-- AS 'SELECT 180/pi()';
-- Define domain for locations on the surface of the earth using a cube
-- datatype with constraints. cube provides 3D indexing.
-- Check constraints aren't currently supported.
CREATE DOMAIN earth AS cube;
-- CONSTRAINT not_point check(is_point(earth))
-- CONSTRAINT not_3d check(cube_dim(earth) <= 3)
-- CONSTRAINT on_surface check(abs(cube_distance(earth, '(0)'::cube) /
-- earth() - 1) < '10e-12'::float8);
CREATE OR REPLACE FUNCTION sec_to_gc(float8)
RETURNS float8
LANGUAGE 'sql'
IMMUTABLE STRICT
AS 'SELECT CASE WHEN $1 < 0 THEN 0::float8 WHEN $1/(2*earth()) > 1 THEN pi()*earth() ELSE 2*earth()*asin($1/(2*earth())) END';
CREATE OR REPLACE FUNCTION gc_to_sec(float8)
RETURNS float8
LANGUAGE 'sql'
IMMUTABLE STRICT
AS 'SELECT CASE WHEN $1 < 0 THEN 0::float8 WHEN $1/earth() > pi() THEN 2*earth() ELSE 2*earth()*sin($1/(2*earth())) END';
CREATE OR REPLACE FUNCTION ll_to_earth(float8, float8)
RETURNS earth
LANGUAGE 'sql'
IMMUTABLE STRICT
AS 'SELECT cube(\'(\'||earth()*cos(radians($1))*cos(radians($2))||\',\'||earth()*cos(radians($1))*sin(radians($2))||\',\'||earth()*sin(radians($1))||\')\')';
CREATE OR REPLACE FUNCTION latitude(earth)
RETURNS float8
LANGUAGE 'sql'
IMMUTABLE STRICT
AS 'SELECT CASE WHEN cube_ll_coord($1, 3)/earth() < -1 THEN -90::float8 WHEN cube_ll_coord($1, 3)/earth() > 1 THEN 90::float8 ELSE degrees(asin(cube_ll_coord($1, 3)/earth())) END';
CREATE OR REPLACE FUNCTION longitude(earth)
RETURNS float8
LANGUAGE 'sql'
IMMUTABLE STRICT
AS 'SELECT degrees(atan2(cube_ll_coord($1, 2), cube_ll_coord($1, 1)))';
CREATE OR REPLACE FUNCTION earth_distance(earth, earth)
RETURNS float8
LANGUAGE 'sql'
IMMUTABLE STRICT
AS 'SELECT sec_to_gc(cube_distance($1, $2))';
CREATE OR REPLACE FUNCTION earth_box(earth, float8)
RETURNS cube
LANGUAGE 'sql'
IMMUTABLE STRICT
AS 'SELECT cube_enlarge($1, gc_to_sec($2), 3)';
--------------- geo_distance --------------- geo_distance
CREATE OR REPLACE FUNCTION geo_distance (point, point) CREATE OR REPLACE FUNCTION geo_distance (point, point)
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment