Thread: Earth Distance patch

Earth Distance patch

From
Bruno Wolff III
Date:
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.

Attachment

Re: Earth Distance patch

From
Tom Lane
Date:
Bruno Wolff III <bruno@wolff.to> writes:
> 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.

Is this bug fix, or new features?  We are past the point of accepting
new features for 7.3.

            regards, tom lane

Re: Earth Distance patch

From
Bruno Wolff III
Date:
On Thu, Sep 12, 2002 at 10:35:49 -0400,
  Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Bruno Wolff III <bruno@wolff.to> writes:
> > 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.
>
> Is this bug fix, or new features?  We are past the point of accepting
> new features for 7.3.

Some of both.

I found some bugs in the original earthdistance code while trying to make
up regression tests.

If you would like I can split this. I can submit the bug fixes and a regression
test for the old stuff. And then after 7.3 comes out, resubmit the additional
documentation, functions (all of language type sql) and regression tests.

Re: Earth Distance patch

From
Bruce Momjian
Date:
This was the email that prompted the second, smaller page.  Do we
feature freeze beta?

---------------------------------------------------------------------------

Bruno Wolff III wrote:
> On Thu, Sep 12, 2002 at 10:35:49 -0400,
>   Tom Lane <tgl@sss.pgh.pa.us> wrote:
> > Bruno Wolff III <bruno@wolff.to> writes:
> > > 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.
> >
> > Is this bug fix, or new features?  We are past the point of accepting
> > new features for 7.3.
>
> Some of both.
>
> I found some bugs in the original earthdistance code while trying to make
> up regression tests.
>
> If you would like I can split this. I can submit the bug fixes and a regression
> test for the old stuff. And then after 7.3 comes out, resubmit the additional
> documentation, functions (all of language type sql) and regression tests.
>
> ---------------------------(end of broadcast)---------------------------
> TIP 3: if posting/reading through Usenet, please send an appropriate
> subscribe-nomail command to majordomo@postgresql.org so that your
> message can get through to the mailing list cleanly
>

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: Earth Distance patch

From
Bruce Momjian
Date:
This has been saved for the 7.4 release:

    http://candle.pha.pa.us/cgi-bin/pgpatches2

We will apply only the non-7.3 stuff.  In fact, I will probably just get
back to you on this during 7.4 or you will submit a new one.

---------------------------------------------------------------------------


Bruno Wolff III wrote:
> 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.

[ Attachment, skipping... ]

>
> ---------------------------(end of broadcast)---------------------------
> TIP 5: Have you checked our extensive FAQ?
>
> http://www.postgresql.org/users-lounge/docs/faq.html

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: Earth Distance patch

From
Bruno Wolff III
Date:
On Wed, Sep 18, 2002 at 00:40:27 -0400,
  Bruce Momjian <pgman@candle.pha.pa.us> wrote:
>
> This has been saved for the 7.4 release:
>
>     http://candle.pha.pa.us/cgi-bin/pgpatches2
>
> We will apply only the non-7.3 stuff.  In fact, I will probably just get
> back to you on this during 7.4 or you will submit a new one.

That is fine.

There may be better ways to handle that contrib than putting it with
earthdistance. Most people aren't going to want both at the same time (as one
uses point and the other uses cube).

Re: Earth Distance patch

From
Bruce Momjian
Date:
I have applied your patch by pickout out the pieces that were already
not in CVS. Thanks.

---------------------------------------------------------------------------

Bruno Wolff III wrote:
> 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.

[ Attachment, skipping... ]

>
> ---------------------------(end of broadcast)---------------------------
> TIP 5: Have you checked our extensive FAQ?
>
> http://www.postgresql.org/users-lounge/docs/faq.html

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: contrib/earthdistance/README.earthdistance
===================================================================
RCS file: /cvsroot/contrib/earthdistance/README.earthdistance,v
retrieving revision 1.2
diff -c -c -r1.2 README.earthdistance
*** contrib/earthdistance/README.earthdistance    20 Sep 2002 03:47:22 -0000    1.2
--- contrib/earthdistance/README.earthdistance    8 Nov 2002 20:18:25 -0000
***************
*** 1,13 ****
! ---------------------------------------------------------------------
! I corrected a bug in the geo_distance code where two double constants
! were declared as int. I changed the distance function to use the
! haversine formula which is more accurate for small distances.
! I added a regression test to the package. I added a grant statement
! to give execute access for geo_distance to public.

  Bruno Wolff III
  September 2002
  ---------------------------------------------------------------------
  Date: Wed, 1 Apr 1998 15:19:32 -0600 (CST)
  From: Hal Snyder <hal@vailsys.com>
  To: vmehr@ctp.com
--- 1,107 ----
! This contrib package contains two different approaches to calculating
! great circle distances on the surface of the Earth. The one described
! first depends on the contrib/cube package (which MUST be installed before
! earthdistance is installed). The second one is based on the point
! datatype using latitude and longitude for the coordinates. The install
! 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
  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)
  From: Hal Snyder <hal@vailsys.com>
  To: vmehr@ctp.com
Index: contrib/earthdistance/earthdistance.c
===================================================================
RCS file: /cvsroot/contrib/earthdistance/earthdistance.c,v
retrieving revision 1.6
diff -c -c -r1.6 earthdistance.c
*** contrib/earthdistance/earthdistance.c    20 Sep 2002 03:47:22 -0000    1.6
--- contrib/earthdistance/earthdistance.c    8 Nov 2002 20:18:25 -0000
***************
*** 50,56 ****
                  long2,
                  lat2;
      double        longdiff;
!         double        sino;
      double       *resultp = palloc(sizeof(double));

      /* convert degrees to radians */
--- 50,56 ----
                  long2,
                  lat2;
      double        longdiff;
!     double        sino;
      double       *resultp = palloc(sizeof(double));

      /* convert degrees to radians */
Index: contrib/earthdistance/earthdistance.sql.in
===================================================================
RCS file: /cvsroot/contrib/earthdistance/earthdistance.sql.in,v
retrieving revision 1.4
diff -c -c -r1.4 earthdistance.sql.in
*** contrib/earthdistance/earthdistance.sql.in    18 Oct 2002 18:41:20 -0000    1.4
--- contrib/earthdistance/earthdistance.sql.in    8 Nov 2002 20:18:25 -0000
***************
*** 3,8 ****
--- 3,79 ----

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

  CREATE OR REPLACE FUNCTION geo_distance (point, point)
Index: contrib/earthdistance/expected/earthdistance.out
===================================================================
RCS file: /cvsroot/contrib/earthdistance/expected/earthdistance.out,v
retrieving revision 1.3
diff -c -c -r1.3 earthdistance.out
*** contrib/earthdistance/expected/earthdistance.out    21 Oct 2002 20:33:21 -0000    1.3
--- contrib/earthdistance/expected/earthdistance.out    8 Nov 2002 20:18:26 -0000
***************
*** 3,81 ****
  --
  --
  -- first, define the datatype.  Turn off echoing so that expected file
! -- does not depend on contents of earthdistance.sql.
  --
  \set ECHO none
  --
  -- Test getting the distance between two points using geo_distance.
  --
! SELECT geo_distance('(0,0)'::point,'(0,0)'::point)::numeric(20,5);
   geo_distance
  --------------
        0.00000
  (1 row)

! SELECT geo_distance('(0,0)'::point,'(180,0)'::point)::numeric(20,5);
   geo_distance
  --------------
    12436.77274
  (1 row)

! SELECT geo_distance('(0,0)'::point,'(0,90)'::point)::numeric(20,5);
   geo_distance
  --------------
     6218.38637
  (1 row)

! SELECT geo_distance('(0,0)'::point,'(90,0)'::point)::numeric(20,5);
   geo_distance
  --------------
     6218.38637
  (1 row)

! SELECT geo_distance('(0,0)'::point,'(1,0)'::point)::numeric(20,5);
   geo_distance
  --------------
       69.09318
  (1 row)

! SELECT geo_distance('(0,0)'::point,'(0,1)'::point)::numeric(20,5);
   geo_distance
  --------------
       69.09318
  (1 row)

! SELECT geo_distance('(0,30)'::point,'(1,30)'::point)::numeric(20,5);
   geo_distance
  --------------
       59.83626
  (1 row)

! SELECT geo_distance('(0,30)'::point,'(0,31)'::point)::numeric(20,5);
   geo_distance
  --------------
       69.09318
  (1 row)

! SELECT geo_distance('(0,60)'::point,'(1,60)'::point)::numeric(20,5);
   geo_distance
  --------------
       34.54626
  (1 row)

! SELECT geo_distance('(0,60)'::point,'(0,61)'::point)::numeric(20,5);
   geo_distance
  --------------
       69.09318
  (1 row)

! SELECT geo_distance('(87.6,41.8)'::point,'(106.7,35.1)'::point)::numeric(20,5);
   geo_distance
  --------------
     1129.18983
  (1 row)

! SELECT (geo_distance('(87.6,41.8)'::point,'(106.7,35.1)'::point)*5280.*12.*2.54/100.)::numeric(20,5);
      numeric
  ---------------
   1817254.87730
--- 3,628 ----
  --
  --
  -- first, define the datatype.  Turn off echoing so that expected file
! -- does not depend on contents of earthdistance.sql or cube.sql.
  --
  \set ECHO none
+ psql:../cube/cube.sql:12: NOTICE:  ProcedureCreate: type cube is not yet defined
+ psql:../cube/cube.sql:17: NOTICE:  Argument type "cube" is only a shell
+ --
+ -- The radius of the Earth we are using.
+ --
+ select earth()::numeric(20,5);
+      earth
+ ---------------
+  6378168.00000
+ (1 row)
+
+ --
+ -- Convert straight line distances to great circle distances.
+ --
+ select (pi()*earth())::numeric(20,5);
+     numeric
+ ----------------
+  20037605.73216
+ (1 row)
+
+ select sec_to_gc(0)::numeric(20,5);
+  sec_to_gc
+ -----------
+    0.00000
+ (1 row)
+
+ select sec_to_gc(2*earth())::numeric(20,5);
+    sec_to_gc
+ ----------------
+  20037605.73216
+ (1 row)
+
+ select sec_to_gc(10*earth())::numeric(20,5);
+    sec_to_gc
+ ----------------
+  20037605.73216
+ (1 row)
+
+ select sec_to_gc(-earth())::numeric(20,5);
+  sec_to_gc
+ -----------
+    0.00000
+ (1 row)
+
+ select sec_to_gc(1000)::numeric(20,5);
+  sec_to_gc
+ ------------
+  1000.00000
+ (1 row)
+
+ select sec_to_gc(10000)::numeric(20,5);
+   sec_to_gc
+ -------------
+  10000.00102
+ (1 row)
+
+ select sec_to_gc(100000)::numeric(20,5);
+   sec_to_gc
+ --------------
+  100001.02426
+ (1 row)
+
+ select sec_to_gc(1000000)::numeric(20,5);
+    sec_to_gc
+ ---------------
+  1001027.07131
+ (1 row)
+
+ --
+ -- Convert great circle distances to straight line distances.
+ --
+ select gc_to_sec(0)::numeric(20,5);
+  gc_to_sec
+ -----------
+    0.00000
+ (1 row)
+
+ select gc_to_sec(sec_to_gc(2*earth()))::numeric(20,5);
+    gc_to_sec
+ ----------------
+  12756336.00000
+ (1 row)
+
+ select gc_to_sec(10*earth())::numeric(20,5);
+    gc_to_sec
+ ----------------
+  12756336.00000
+ (1 row)
+
+ select gc_to_sec(pi()*earth())::numeric(20,5);
+    gc_to_sec
+ ----------------
+  12756336.00000
+ (1 row)
+
+ select gc_to_sec(-1000)::numeric(20,5);
+  gc_to_sec
+ -----------
+    0.00000
+ (1 row)
+
+ select gc_to_sec(1000)::numeric(20,5);
+  gc_to_sec
+ ------------
+  1000.00000
+ (1 row)
+
+ select gc_to_sec(10000)::numeric(20,5);
+  gc_to_sec
+ ------------
+  9999.99898
+ (1 row)
+
+ select gc_to_sec(100000)::numeric(20,5);
+   gc_to_sec
+ -------------
+  99998.97577
+ (1 row)
+
+ select gc_to_sec(1000000)::numeric(20,5);
+   gc_to_sec
+ --------------
+  998976.08618
+ (1 row)
+
+ --
+ -- Set coordinates using latitude and longitude.
+ -- Extract each coordinate separately so we can round them.
+ --
+ select cube_ll_coord(ll_to_earth(0,0),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(0,0),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(0,0),3)::numeric(20,5);
+  cube_ll_coord | cube_ll_coord | cube_ll_coord
+ ---------------+---------------+---------------
+  6378168.00000 |       0.00000 |       0.00000
+ (1 row)
+
+ select cube_ll_coord(ll_to_earth(360,360),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(360,360),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(360,360),3)::numeric(20,5);
+  cube_ll_coord | cube_ll_coord | cube_ll_coord
+ ---------------+---------------+---------------
+  6378168.00000 |       0.00000 |       0.00000
+ (1 row)
+
+ select cube_ll_coord(ll_to_earth(180,180),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(180,180),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(180,180),3)::numeric(20,5);
+  cube_ll_coord | cube_ll_coord | cube_ll_coord
+ ---------------+---------------+---------------
+  6378168.00000 |       0.00000 |       0.00000
+ (1 row)
+
+ select cube_ll_coord(ll_to_earth(180,360),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(180,360),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(180,360),3)::numeric(20,5);
+  cube_ll_coord  | cube_ll_coord | cube_ll_coord
+ ----------------+---------------+---------------
+  -6378168.00000 |       0.00000 |       0.00000
+ (1 row)
+
+ select cube_ll_coord(ll_to_earth(-180,-360),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(-180,-360),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(-180,-360),3)::numeric(20,5);
+  cube_ll_coord  | cube_ll_coord | cube_ll_coord
+ ----------------+---------------+---------------
+  -6378168.00000 |       0.00000 |       0.00000
+ (1 row)
+
+ select cube_ll_coord(ll_to_earth(0,180),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(0,180),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(0,180),3)::numeric(20,5);
+  cube_ll_coord  | cube_ll_coord | cube_ll_coord
+ ----------------+---------------+---------------
+  -6378168.00000 |       0.00000 |       0.00000
+ (1 row)
+
+ select cube_ll_coord(ll_to_earth(0,-180),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(0,-180),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(0,-180),3)::numeric(20,5);
+  cube_ll_coord  | cube_ll_coord | cube_ll_coord
+ ----------------+---------------+---------------
+  -6378168.00000 |       0.00000 |       0.00000
+ (1 row)
+
+ select cube_ll_coord(ll_to_earth(90,0),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(90,0),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(90,0),3)::numeric(20,5);
+  cube_ll_coord | cube_ll_coord | cube_ll_coord
+ ---------------+---------------+---------------
+        0.00000 |       0.00000 | 6378168.00000
+ (1 row)
+
+ select cube_ll_coord(ll_to_earth(90,180),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(90,180),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(90,180),3)::numeric(20,5);
+  cube_ll_coord | cube_ll_coord | cube_ll_coord
+ ---------------+---------------+---------------
+        0.00000 |       0.00000 | 6378168.00000
+ (1 row)
+
+ select cube_ll_coord(ll_to_earth(-90,0),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(-90,0),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(-90,0),3)::numeric(20,5);
+  cube_ll_coord | cube_ll_coord | cube_ll_coord
+ ---------------+---------------+----------------
+        0.00000 |       0.00000 | -6378168.00000
+ (1 row)
+
+ select cube_ll_coord(ll_to_earth(-90,180),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(-90,180),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(-90,180),3)::numeric(20,5);
+  cube_ll_coord | cube_ll_coord | cube_ll_coord
+ ---------------+---------------+----------------
+        0.00000 |       0.00000 | -6378168.00000
+ (1 row)
+
+ --
+ -- Test getting the latitude of a location.
+ --
+ select latitude(ll_to_earth(0,0))::numeric(20,10);
+    latitude
+ --------------
+  0.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(45,0))::numeric(20,10);
+    latitude
+ ---------------
+  45.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(90,0))::numeric(20,10);
+    latitude
+ ---------------
+  90.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(-45,0))::numeric(20,10);
+     latitude
+ ----------------
+  -45.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(-90,0))::numeric(20,10);
+     latitude
+ ----------------
+  -90.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(0,90))::numeric(20,10);
+    latitude
+ --------------
+  0.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(45,90))::numeric(20,10);
+    latitude
+ ---------------
+  45.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(90,90))::numeric(20,10);
+    latitude
+ ---------------
+  90.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(-45,90))::numeric(20,10);
+     latitude
+ ----------------
+  -45.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(-90,90))::numeric(20,10);
+     latitude
+ ----------------
+  -90.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(0,180))::numeric(20,10);
+    latitude
+ --------------
+  0.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(45,180))::numeric(20,10);
+    latitude
+ ---------------
+  45.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(90,180))::numeric(20,10);
+    latitude
+ ---------------
+  90.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(-45,180))::numeric(20,10);
+     latitude
+ ----------------
+  -45.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(-90,180))::numeric(20,10);
+     latitude
+ ----------------
+  -90.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(0,-90))::numeric(20,10);
+    latitude
+ --------------
+  0.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(45,-90))::numeric(20,10);
+    latitude
+ ---------------
+  45.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(90,-90))::numeric(20,10);
+    latitude
+ ---------------
+  90.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(-45,-90))::numeric(20,10);
+     latitude
+ ----------------
+  -45.0000000000
+ (1 row)
+
+ select latitude(ll_to_earth(-90,-90))::numeric(20,10);
+     latitude
+ ----------------
+  -90.0000000000
+ (1 row)
+
+ --
+ -- Test getting the longitude of a location.
+ --
+ select longitude(ll_to_earth(0,0))::numeric(20,10);
+   longitude
+ --------------
+  0.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(45,0))::numeric(20,10);
+   longitude
+ --------------
+  0.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(90,0))::numeric(20,10);
+   longitude
+ --------------
+  0.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(-45,0))::numeric(20,10);
+   longitude
+ --------------
+  0.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(-90,0))::numeric(20,10);
+   longitude
+ --------------
+  0.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(0,90))::numeric(20,10);
+    longitude
+ ---------------
+  90.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(45,90))::numeric(20,10);
+    longitude
+ ---------------
+  90.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(90,90))::numeric(20,10);
+    longitude
+ ---------------
+  90.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(-45,90))::numeric(20,10);
+    longitude
+ ---------------
+  90.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(-90,90))::numeric(20,10);
+    longitude
+ ---------------
+  90.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(0,180))::numeric(20,10);
+    longitude
+ ----------------
+  180.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(45,180))::numeric(20,10);
+    longitude
+ ----------------
+  180.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(90,180))::numeric(20,10);
+    longitude
+ ----------------
+  180.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(-45,180))::numeric(20,10);
+    longitude
+ ----------------
+  180.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(-90,180))::numeric(20,10);
+    longitude
+ ----------------
+  180.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(0,-90))::numeric(20,10);
+    longitude
+ ----------------
+  -90.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(45,-90))::numeric(20,10);
+    longitude
+ ----------------
+  -90.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(90,-90))::numeric(20,10);
+    longitude
+ ----------------
+  -90.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(-45,-90))::numeric(20,10);
+    longitude
+ ----------------
+  -90.0000000000
+ (1 row)
+
+ select longitude(ll_to_earth(-90,-90))::numeric(20,10);
+    longitude
+ ----------------
+  -90.0000000000
+ (1 row)
+
+ --
+ -- For the distance tests the following is some real life data.
+ --
+ -- Chicago has a latitude of 41.8 and a longitude of 87.6.
+ -- Albuquerque has a latitude of 35.1 and a longitude of 106.7.
+ -- (Note that latitude and longitude are specified differently
+ -- in the cube based functions than for the point based functions.)
+ --
+ --
+ -- Test getting the distance between two points using earth_distance.
+ --
+ select earth_distance(ll_to_earth(0,0),ll_to_earth(0,0))::numeric(20,5);
+  earth_distance
+ ----------------
+         0.00000
+ (1 row)
+
+ select earth_distance(ll_to_earth(0,0),ll_to_earth(0,180))::numeric(20,5);
+  earth_distance
+ ----------------
+  20037605.73216
+ (1 row)
+
+ select earth_distance(ll_to_earth(0,0),ll_to_earth(90,0))::numeric(20,5);
+  earth_distance
+ ----------------
+  10018802.86608
+ (1 row)
+
+ select earth_distance(ll_to_earth(0,0),ll_to_earth(0,90))::numeric(20,5);
+  earth_distance
+ ----------------
+  10018802.86608
+ (1 row)
+
+ select earth_distance(ll_to_earth(0,0),ll_to_earth(0,1))::numeric(20,5);
+  earth_distance
+ ----------------
+    111320.03185
+ (1 row)
+
+ select earth_distance(ll_to_earth(0,0),ll_to_earth(1,0))::numeric(20,5);
+  earth_distance
+ ----------------
+    111320.03185
+ (1 row)
+
+ select earth_distance(ll_to_earth(30,0),ll_to_earth(30,1))::numeric(20,5);
+  earth_distance
+ ----------------
+     96405.66962
+ (1 row)
+
+ select earth_distance(ll_to_earth(30,0),ll_to_earth(31,0))::numeric(20,5);
+  earth_distance
+ ----------------
+    111320.03185
+ (1 row)
+
+ select earth_distance(ll_to_earth(60,0),ll_to_earth(60,1))::numeric(20,5);
+  earth_distance
+ ----------------
+     55659.48608
+ (1 row)
+
+ select earth_distance(ll_to_earth(60,0),ll_to_earth(61,0))::numeric(20,5);
+  earth_distance
+ ----------------
+    111320.03185
+ (1 row)
+
+ select earth_distance(ll_to_earth(41.8,87.6),ll_to_earth(35.1,106.7))::numeric(20,5);
+  earth_distance
+ ----------------
+   1819303.21265
+ (1 row)
+
+ select (earth_distance(ll_to_earth(41.8,87.6),ll_to_earth(35.1,106.7))*
+       100./2.54/12./5280.)::numeric(20,5);
+   numeric
+ ------------
+  1130.46261
+ (1 row)
+
  --
  -- Test getting the distance between two points using geo_distance.
  --
! select geo_distance('(0,0)'::point,'(0,0)'::point)::numeric(20,5);
   geo_distance
  --------------
        0.00000
  (1 row)

! select geo_distance('(0,0)'::point,'(180,0)'::point)::numeric(20,5);
   geo_distance
  --------------
    12436.77274
  (1 row)

! select geo_distance('(0,0)'::point,'(0,90)'::point)::numeric(20,5);
   geo_distance
  --------------
     6218.38637
  (1 row)

! select geo_distance('(0,0)'::point,'(90,0)'::point)::numeric(20,5);
   geo_distance
  --------------
     6218.38637
  (1 row)

! select geo_distance('(0,0)'::point,'(1,0)'::point)::numeric(20,5);
   geo_distance
  --------------
       69.09318
  (1 row)

! select geo_distance('(0,0)'::point,'(0,1)'::point)::numeric(20,5);
   geo_distance
  --------------
       69.09318
  (1 row)

! select geo_distance('(0,30)'::point,'(1,30)'::point)::numeric(20,5);
   geo_distance
  --------------
       59.83626
  (1 row)

! select geo_distance('(0,30)'::point,'(0,31)'::point)::numeric(20,5);
   geo_distance
  --------------
       69.09318
  (1 row)

! select geo_distance('(0,60)'::point,'(1,60)'::point)::numeric(20,5);
   geo_distance
  --------------
       34.54626
  (1 row)

! select geo_distance('(0,60)'::point,'(0,61)'::point)::numeric(20,5);
   geo_distance
  --------------
       69.09318
  (1 row)

! select geo_distance('(87.6,41.8)'::point,'(106.7,35.1)'::point)::numeric(20,5);
   geo_distance
  --------------
     1129.18983
  (1 row)

! select (geo_distance('(87.6,41.8)'::point,'(106.7,35.1)'::point)*5280.*12.*2.54/100.)::numeric(20,5);
      numeric
  ---------------
   1817254.87730
***************
*** 84,158 ****
  --
  -- Test getting the distance between two points using the <@> operator.
  --
! SELECT ('(0,0)'::point <@> '(0,0)'::point)::numeric(20,5);
   numeric
  ---------
   0.00000
  (1 row)

! SELECT ('(0,0)'::point <@> '(180,0)'::point)::numeric(20,5);
     numeric
  -------------
   12436.77274
  (1 row)

! SELECT ('(0,0)'::point <@> '(0,90)'::point)::numeric(20,5);
    numeric
  ------------
   6218.38637
  (1 row)

! SELECT ('(0,0)'::point <@> '(90,0)'::point)::numeric(20,5);
    numeric
  ------------
   6218.38637
  (1 row)

! SELECT ('(0,0)'::point <@> '(1,0)'::point)::numeric(20,5);
   numeric
  ----------
   69.09318
  (1 row)

! SELECT ('(0,0)'::point <@> '(0,1)'::point)::numeric(20,5);
   numeric
  ----------
   69.09318
  (1 row)

! SELECT ('(0,30)'::point <@> '(1,30)'::point)::numeric(20,5);
   numeric
  ----------
   59.83626
  (1 row)

! SELECT ('(0,30)'::point <@> '(0,31)'::point)::numeric(20,5);
   numeric
  ----------
   69.09318
  (1 row)

! SELECT ('(0,60)'::point <@> '(1,60)'::point)::numeric(20,5);
   numeric
  ----------
   34.54626
  (1 row)

! SELECT ('(0,60)'::point <@> '(0,61)'::point)::numeric(20,5);
   numeric
  ----------
   69.09318
  (1 row)

! SELECT ('(87.6,41.8)'::point <@> '(106.7,35.1)'::point)::numeric(20,5);
    numeric
  ------------
   1129.18983
  (1 row)

! SELECT (('(87.6,41.8)'::point <@> '(106.7,35.1)'::point)*5280.*12.*2.54/100.)::numeric(20,5);
      numeric
  ---------------
   1817254.87730
  (1 row)

--- 631,948 ----
  --
  -- Test getting the distance between two points using the <@> operator.
  --
! select ('(0,0)'::point <@> '(0,0)'::point)::numeric(20,5);
   numeric
  ---------
   0.00000
  (1 row)

! select ('(0,0)'::point <@> '(180,0)'::point)::numeric(20,5);
     numeric
  -------------
   12436.77274
  (1 row)

! select ('(0,0)'::point <@> '(0,90)'::point)::numeric(20,5);
    numeric
  ------------
   6218.38637
  (1 row)

! select ('(0,0)'::point <@> '(90,0)'::point)::numeric(20,5);
    numeric
  ------------
   6218.38637
  (1 row)

! select ('(0,0)'::point <@> '(1,0)'::point)::numeric(20,5);
   numeric
  ----------
   69.09318
  (1 row)

! select ('(0,0)'::point <@> '(0,1)'::point)::numeric(20,5);
   numeric
  ----------
   69.09318
  (1 row)

! select ('(0,30)'::point <@> '(1,30)'::point)::numeric(20,5);
   numeric
  ----------
   59.83626
  (1 row)

! select ('(0,30)'::point <@> '(0,31)'::point)::numeric(20,5);
   numeric
  ----------
   69.09318
  (1 row)

! select ('(0,60)'::point <@> '(1,60)'::point)::numeric(20,5);
   numeric
  ----------
   34.54626
  (1 row)

! select ('(0,60)'::point <@> '(0,61)'::point)::numeric(20,5);
   numeric
  ----------
   69.09318
  (1 row)

! select ('(87.6,41.8)'::point <@> '(106.7,35.1)'::point)::numeric(20,5);
    numeric
  ------------
   1129.18983
  (1 row)

! select (('(87.6,41.8)'::point <@> '(106.7,35.1)'::point)*5280.*12.*2.54/100.)::numeric(20,5);
      numeric
  ---------------
   1817254.87730
+ (1 row)
+
+ --
+ -- Test getting a bounding box around points.
+ --
+ select cube_ll_coord(earth_box(ll_to_earth(0,0),112000),1)::numeric(20,5),
+        cube_ll_coord(earth_box(ll_to_earth(0,0),112000),2)::numeric(20,5),
+        cube_ll_coord(earth_box(ll_to_earth(0,0),112000),3)::numeric(20,5),
+        cube_ur_coord(earth_box(ll_to_earth(0,0),112000),1)::numeric(20,5),
+        cube_ur_coord(earth_box(ll_to_earth(0,0),112000),2)::numeric(20,5),
+        cube_ur_coord(earth_box(ll_to_earth(0,0),112000),3)::numeric(20,5);
+  cube_ll_coord | cube_ll_coord | cube_ll_coord | cube_ur_coord | cube_ur_coord | cube_ur_coord
+ ---------------+---------------+---------------+---------------+---------------+---------------
+  6266169.43896 | -111998.56104 | -111998.56104 | 6490166.56104 |  111998.56104 |  111998.56104
+ (1 row)
+
+ select cube_ll_coord(earth_box(ll_to_earth(0,0),pi()*earth()),1)::numeric(20,5),
+        cube_ll_coord(earth_box(ll_to_earth(0,0),pi()*earth()),2)::numeric(20,5),
+        cube_ll_coord(earth_box(ll_to_earth(0,0),pi()*earth()),3)::numeric(20,5),
+        cube_ur_coord(earth_box(ll_to_earth(0,0),pi()*earth()),1)::numeric(20,5),
+        cube_ur_coord(earth_box(ll_to_earth(0,0),pi()*earth()),2)::numeric(20,5),
+        cube_ur_coord(earth_box(ll_to_earth(0,0),pi()*earth()),3)::numeric(20,5);
+  cube_ll_coord  |  cube_ll_coord  |  cube_ll_coord  | cube_ur_coord  | cube_ur_coord  | cube_ur_coord
+ ----------------+-----------------+-----------------+----------------+----------------+----------------
+  -6378168.00000 | -12756336.00000 | -12756336.00000 | 19134504.00000 | 12756336.00000 | 12756336.00000
+ (1 row)
+
+ select cube_ll_coord(earth_box(ll_to_earth(0,0),10*earth()),1)::numeric(20,5),
+        cube_ll_coord(earth_box(ll_to_earth(0,0),10*earth()),2)::numeric(20,5),
+        cube_ll_coord(earth_box(ll_to_earth(0,0),10*earth()),3)::numeric(20,5),
+        cube_ur_coord(earth_box(ll_to_earth(0,0),10*earth()),1)::numeric(20,5),
+        cube_ur_coord(earth_box(ll_to_earth(0,0),10*earth()),2)::numeric(20,5),
+        cube_ur_coord(earth_box(ll_to_earth(0,0),10*earth()),3)::numeric(20,5);
+  cube_ll_coord  |  cube_ll_coord  |  cube_ll_coord  | cube_ur_coord  | cube_ur_coord  | cube_ur_coord
+ ----------------+-----------------+-----------------+----------------+----------------+----------------
+  -6378168.00000 | -12756336.00000 | -12756336.00000 | 19134504.00000 | 12756336.00000 | 12756336.00000
+ (1 row)
+
+ --
+ -- Test for points that should be in bounding boxes.
+ --
+ select earth_box(ll_to_earth(0,0),
+        earth_distance(ll_to_earth(0,0),ll_to_earth(0,1))*1.00001) @
+        ll_to_earth(0,1);
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select earth_box(ll_to_earth(0,0),
+        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.1))*1.00001) @
+        ll_to_earth(0,0.1);
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select earth_box(ll_to_earth(0,0),
+        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.01))*1.00001) @
+        ll_to_earth(0,0.01);
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select earth_box(ll_to_earth(0,0),
+        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.001))*1.00001) @
+        ll_to_earth(0,0.001);
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select earth_box(ll_to_earth(0,0),
+        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.0001))*1.00001) @
+        ll_to_earth(0,0.0001);
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select earth_box(ll_to_earth(0,0),
+        earth_distance(ll_to_earth(0,0),ll_to_earth(0.0001,0.0001))*1.00001) @
+        ll_to_earth(0.0001,0.0001);
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select earth_box(ll_to_earth(45,45),
+        earth_distance(ll_to_earth(45,45),ll_to_earth(45.0001,45.0001))*1.00001) @
+        ll_to_earth(45.0001,45.0001);
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select earth_box(ll_to_earth(90,180),
+        earth_distance(ll_to_earth(90,180),ll_to_earth(90.0001,180.0001))*1.00001) @
+        ll_to_earth(90.0001,180.0001);
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ --
+ -- Test for points that shouldn't be in bounding boxes. Note that we need
+ -- to make points way outside, since some points close may be in the box
+ -- but further away than the distance we are testing.
+ --
+ select earth_box(ll_to_earth(0,0),
+        earth_distance(ll_to_earth(0,0),ll_to_earth(0,1))*.57735) @
+        ll_to_earth(0,1);
+  ?column?
+ ----------
+  f
+ (1 row)
+
+ select earth_box(ll_to_earth(0,0),
+        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.1))*.57735) @
+        ll_to_earth(0,0.1);
+  ?column?
+ ----------
+  f
+ (1 row)
+
+ select earth_box(ll_to_earth(0,0),
+        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.01))*.57735) @
+        ll_to_earth(0,0.01);
+  ?column?
+ ----------
+  f
+ (1 row)
+
+ select earth_box(ll_to_earth(0,0),
+        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.001))*.57735) @
+        ll_to_earth(0,0.001);
+  ?column?
+ ----------
+  f
+ (1 row)
+
+ select earth_box(ll_to_earth(0,0),
+        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.0001))*.57735) @
+        ll_to_earth(0,0.0001);
+  ?column?
+ ----------
+  f
+ (1 row)
+
+ select earth_box(ll_to_earth(0,0),
+        earth_distance(ll_to_earth(0,0),ll_to_earth(0.0001,0.0001))*.57735) @
+        ll_to_earth(0.0001,0.0001);
+  ?column?
+ ----------
+  f
+ (1 row)
+
+ select earth_box(ll_to_earth(45,45),
+        earth_distance(ll_to_earth(45,45),ll_to_earth(45.0001,45.0001))*.57735) @
+        ll_to_earth(45.0001,45.0001);
+  ?column?
+ ----------
+  f
+ (1 row)
+
+ select earth_box(ll_to_earth(90,180),
+        earth_distance(ll_to_earth(90,180),ll_to_earth(90.0001,180.0001))*.57735) @
+        ll_to_earth(90.0001,180.0001);
+  ?column?
+ ----------
+  f
+ (1 row)
+
+ --
+ -- Test the recommended constraints.
+ --
+ select is_point(ll_to_earth(0,0));
+ ERROR:  Function is_point(earth) does not exist
+     Unable to identify a function that satisfies the given argument types
+     You may need to add explicit typecasts
+ select cube_dim(ll_to_earth(0,0)) <= 3;
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select abs(cube_distance(ll_to_earth(0,0), '(0)'::cube) / earth() - 1) <
+        '10e-12'::float8;
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select is_point(ll_to_earth(30,60));
+ ERROR:  Function is_point(earth) does not exist
+     Unable to identify a function that satisfies the given argument types
+     You may need to add explicit typecasts
+ select cube_dim(ll_to_earth(30,60)) <= 3;
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select abs(cube_distance(ll_to_earth(30,60), '(0)'::cube) / earth() - 1) <
+        '10e-12'::float8;
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select is_point(ll_to_earth(60,90));
+ ERROR:  Function is_point(earth) does not exist
+     Unable to identify a function that satisfies the given argument types
+     You may need to add explicit typecasts
+ select cube_dim(ll_to_earth(60,90)) <= 3;
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select abs(cube_distance(ll_to_earth(60,90), '(0)'::cube) / earth() - 1) <
+        '10e-12'::float8;
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select is_point(ll_to_earth(-30,-90));
+ ERROR:  Function is_point(earth) does not exist
+     Unable to identify a function that satisfies the given argument types
+     You may need to add explicit typecasts
+ select cube_dim(ll_to_earth(-30,-90)) <= 3;
+  ?column?
+ ----------
+  t
+ (1 row)
+
+ select abs(cube_distance(ll_to_earth(-30,-90), '(0)'::cube) / earth() - 1) <
+        '10e-12'::float8;
+  ?column?
+ ----------
+  t
  (1 row)

Index: contrib/earthdistance/sql/earthdistance.sql
===================================================================
RCS file: /cvsroot/contrib/earthdistance/sql/earthdistance.sql,v
retrieving revision 1.5
diff -c -c -r1.5 earthdistance.sql
*** contrib/earthdistance/sql/earthdistance.sql    21 Oct 2002 20:33:21 -0000    1.5
--- contrib/earthdistance/sql/earthdistance.sql    8 Nov 2002 20:18:27 -0000
***************
*** 4,45 ****

  --
  -- first, define the datatype.  Turn off echoing so that expected file
! -- does not depend on contents of earthdistance.sql.
  --
  \set ECHO none
  \i earthdistance.sql
  \set ECHO all

  --
  -- Test getting the distance between two points using geo_distance.
  --

! SELECT geo_distance('(0,0)'::point,'(0,0)'::point)::numeric(20,5);
! SELECT geo_distance('(0,0)'::point,'(180,0)'::point)::numeric(20,5);
! SELECT geo_distance('(0,0)'::point,'(0,90)'::point)::numeric(20,5);
! SELECT geo_distance('(0,0)'::point,'(90,0)'::point)::numeric(20,5);
! SELECT geo_distance('(0,0)'::point,'(1,0)'::point)::numeric(20,5);
! SELECT geo_distance('(0,0)'::point,'(0,1)'::point)::numeric(20,5);
! SELECT geo_distance('(0,30)'::point,'(1,30)'::point)::numeric(20,5);
! SELECT geo_distance('(0,30)'::point,'(0,31)'::point)::numeric(20,5);
! SELECT geo_distance('(0,60)'::point,'(1,60)'::point)::numeric(20,5);
! SELECT geo_distance('(0,60)'::point,'(0,61)'::point)::numeric(20,5);
! SELECT geo_distance('(87.6,41.8)'::point,'(106.7,35.1)'::point)::numeric(20,5);
! SELECT (geo_distance('(87.6,41.8)'::point,'(106.7,35.1)'::point)*5280.*12.*2.54/100.)::numeric(20,5);

  --
  -- Test getting the distance between two points using the <@> operator.
  --

! SELECT ('(0,0)'::point <@> '(0,0)'::point)::numeric(20,5);
! SELECT ('(0,0)'::point <@> '(180,0)'::point)::numeric(20,5);
! SELECT ('(0,0)'::point <@> '(0,90)'::point)::numeric(20,5);
! SELECT ('(0,0)'::point <@> '(90,0)'::point)::numeric(20,5);
! SELECT ('(0,0)'::point <@> '(1,0)'::point)::numeric(20,5);
! SELECT ('(0,0)'::point <@> '(0,1)'::point)::numeric(20,5);
! SELECT ('(0,30)'::point <@> '(1,30)'::point)::numeric(20,5);
! SELECT ('(0,30)'::point <@> '(0,31)'::point)::numeric(20,5);
! SELECT ('(0,60)'::point <@> '(1,60)'::point)::numeric(20,5);
! SELECT ('(0,60)'::point <@> '(0,61)'::point)::numeric(20,5);
! SELECT ('(87.6,41.8)'::point <@> '(106.7,35.1)'::point)::numeric(20,5);
! SELECT (('(87.6,41.8)'::point <@> '(106.7,35.1)'::point)*5280.*12.*2.54/100.)::numeric(20,5);
--- 4,299 ----

  --
  -- first, define the datatype.  Turn off echoing so that expected file
! -- does not depend on contents of earthdistance.sql or cube.sql.
  --
  \set ECHO none
+ \i ../cube/cube.sql
  \i earthdistance.sql
  \set ECHO all

  --
+ -- The radius of the Earth we are using.
+ --
+
+ select earth()::numeric(20,5);
+
+ --
+ -- Convert straight line distances to great circle distances.
+ --
+ select (pi()*earth())::numeric(20,5);
+ select sec_to_gc(0)::numeric(20,5);
+ select sec_to_gc(2*earth())::numeric(20,5);
+ select sec_to_gc(10*earth())::numeric(20,5);
+ select sec_to_gc(-earth())::numeric(20,5);
+ select sec_to_gc(1000)::numeric(20,5);
+ select sec_to_gc(10000)::numeric(20,5);
+ select sec_to_gc(100000)::numeric(20,5);
+ select sec_to_gc(1000000)::numeric(20,5);
+
+ --
+ -- Convert great circle distances to straight line distances.
+ --
+
+ select gc_to_sec(0)::numeric(20,5);
+ select gc_to_sec(sec_to_gc(2*earth()))::numeric(20,5);
+ select gc_to_sec(10*earth())::numeric(20,5);
+ select gc_to_sec(pi()*earth())::numeric(20,5);
+ select gc_to_sec(-1000)::numeric(20,5);
+ select gc_to_sec(1000)::numeric(20,5);
+ select gc_to_sec(10000)::numeric(20,5);
+ select gc_to_sec(100000)::numeric(20,5);
+ select gc_to_sec(1000000)::numeric(20,5);
+
+ --
+ -- Set coordinates using latitude and longitude.
+ -- Extract each coordinate separately so we can round them.
+ --
+
+ select cube_ll_coord(ll_to_earth(0,0),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(0,0),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(0,0),3)::numeric(20,5);
+ select cube_ll_coord(ll_to_earth(360,360),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(360,360),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(360,360),3)::numeric(20,5);
+ select cube_ll_coord(ll_to_earth(180,180),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(180,180),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(180,180),3)::numeric(20,5);
+ select cube_ll_coord(ll_to_earth(180,360),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(180,360),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(180,360),3)::numeric(20,5);
+ select cube_ll_coord(ll_to_earth(-180,-360),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(-180,-360),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(-180,-360),3)::numeric(20,5);
+ select cube_ll_coord(ll_to_earth(0,180),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(0,180),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(0,180),3)::numeric(20,5);
+ select cube_ll_coord(ll_to_earth(0,-180),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(0,-180),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(0,-180),3)::numeric(20,5);
+ select cube_ll_coord(ll_to_earth(90,0),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(90,0),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(90,0),3)::numeric(20,5);
+ select cube_ll_coord(ll_to_earth(90,180),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(90,180),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(90,180),3)::numeric(20,5);
+ select cube_ll_coord(ll_to_earth(-90,0),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(-90,0),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(-90,0),3)::numeric(20,5);
+ select cube_ll_coord(ll_to_earth(-90,180),1)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(-90,180),2)::numeric(20,5),
+  cube_ll_coord(ll_to_earth(-90,180),3)::numeric(20,5);
+
+ --
+ -- Test getting the latitude of a location.
+ --
+
+ select latitude(ll_to_earth(0,0))::numeric(20,10);
+ select latitude(ll_to_earth(45,0))::numeric(20,10);
+ select latitude(ll_to_earth(90,0))::numeric(20,10);
+ select latitude(ll_to_earth(-45,0))::numeric(20,10);
+ select latitude(ll_to_earth(-90,0))::numeric(20,10);
+ select latitude(ll_to_earth(0,90))::numeric(20,10);
+ select latitude(ll_to_earth(45,90))::numeric(20,10);
+ select latitude(ll_to_earth(90,90))::numeric(20,10);
+ select latitude(ll_to_earth(-45,90))::numeric(20,10);
+ select latitude(ll_to_earth(-90,90))::numeric(20,10);
+ select latitude(ll_to_earth(0,180))::numeric(20,10);
+ select latitude(ll_to_earth(45,180))::numeric(20,10);
+ select latitude(ll_to_earth(90,180))::numeric(20,10);
+ select latitude(ll_to_earth(-45,180))::numeric(20,10);
+ select latitude(ll_to_earth(-90,180))::numeric(20,10);
+ select latitude(ll_to_earth(0,-90))::numeric(20,10);
+ select latitude(ll_to_earth(45,-90))::numeric(20,10);
+ select latitude(ll_to_earth(90,-90))::numeric(20,10);
+ select latitude(ll_to_earth(-45,-90))::numeric(20,10);
+ select latitude(ll_to_earth(-90,-90))::numeric(20,10);
+
+ --
+ -- Test getting the longitude of a location.
+ --
+
+ select longitude(ll_to_earth(0,0))::numeric(20,10);
+ select longitude(ll_to_earth(45,0))::numeric(20,10);
+ select longitude(ll_to_earth(90,0))::numeric(20,10);
+ select longitude(ll_to_earth(-45,0))::numeric(20,10);
+ select longitude(ll_to_earth(-90,0))::numeric(20,10);
+ select longitude(ll_to_earth(0,90))::numeric(20,10);
+ select longitude(ll_to_earth(45,90))::numeric(20,10);
+ select longitude(ll_to_earth(90,90))::numeric(20,10);
+ select longitude(ll_to_earth(-45,90))::numeric(20,10);
+ select longitude(ll_to_earth(-90,90))::numeric(20,10);
+ select longitude(ll_to_earth(0,180))::numeric(20,10);
+ select longitude(ll_to_earth(45,180))::numeric(20,10);
+ select longitude(ll_to_earth(90,180))::numeric(20,10);
+ select longitude(ll_to_earth(-45,180))::numeric(20,10);
+ select longitude(ll_to_earth(-90,180))::numeric(20,10);
+ select longitude(ll_to_earth(0,-90))::numeric(20,10);
+ select longitude(ll_to_earth(45,-90))::numeric(20,10);
+ select longitude(ll_to_earth(90,-90))::numeric(20,10);
+ select longitude(ll_to_earth(-45,-90))::numeric(20,10);
+ select longitude(ll_to_earth(-90,-90))::numeric(20,10);
+
+ --
+ -- For the distance tests the following is some real life data.
+ --
+ -- Chicago has a latitude of 41.8 and a longitude of 87.6.
+ -- Albuquerque has a latitude of 35.1 and a longitude of 106.7.
+ -- (Note that latitude and longitude are specified differently
+ -- in the cube based functions than for the point based functions.)
+ --
+
+ --
+ -- Test getting the distance between two points using earth_distance.
+ --
+
+ select earth_distance(ll_to_earth(0,0),ll_to_earth(0,0))::numeric(20,5);
+ select earth_distance(ll_to_earth(0,0),ll_to_earth(0,180))::numeric(20,5);
+ select earth_distance(ll_to_earth(0,0),ll_to_earth(90,0))::numeric(20,5);
+ select earth_distance(ll_to_earth(0,0),ll_to_earth(0,90))::numeric(20,5);
+ select earth_distance(ll_to_earth(0,0),ll_to_earth(0,1))::numeric(20,5);
+ select earth_distance(ll_to_earth(0,0),ll_to_earth(1,0))::numeric(20,5);
+ select earth_distance(ll_to_earth(30,0),ll_to_earth(30,1))::numeric(20,5);
+ select earth_distance(ll_to_earth(30,0),ll_to_earth(31,0))::numeric(20,5);
+ select earth_distance(ll_to_earth(60,0),ll_to_earth(60,1))::numeric(20,5);
+ select earth_distance(ll_to_earth(60,0),ll_to_earth(61,0))::numeric(20,5);
+ select earth_distance(ll_to_earth(41.8,87.6),ll_to_earth(35.1,106.7))::numeric(20,5);
+ select (earth_distance(ll_to_earth(41.8,87.6),ll_to_earth(35.1,106.7))*
+       100./2.54/12./5280.)::numeric(20,5);
+
+ --
  -- Test getting the distance between two points using geo_distance.
  --

! select geo_distance('(0,0)'::point,'(0,0)'::point)::numeric(20,5);
! select geo_distance('(0,0)'::point,'(180,0)'::point)::numeric(20,5);
! select geo_distance('(0,0)'::point,'(0,90)'::point)::numeric(20,5);
! select geo_distance('(0,0)'::point,'(90,0)'::point)::numeric(20,5);
! select geo_distance('(0,0)'::point,'(1,0)'::point)::numeric(20,5);
! select geo_distance('(0,0)'::point,'(0,1)'::point)::numeric(20,5);
! select geo_distance('(0,30)'::point,'(1,30)'::point)::numeric(20,5);
! select geo_distance('(0,30)'::point,'(0,31)'::point)::numeric(20,5);
! select geo_distance('(0,60)'::point,'(1,60)'::point)::numeric(20,5);
! select geo_distance('(0,60)'::point,'(0,61)'::point)::numeric(20,5);
! select geo_distance('(87.6,41.8)'::point,'(106.7,35.1)'::point)::numeric(20,5);
! select (geo_distance('(87.6,41.8)'::point,'(106.7,35.1)'::point)*5280.*12.*2.54/100.)::numeric(20,5);

  --
  -- Test getting the distance between two points using the <@> operator.
  --

! select ('(0,0)'::point <@> '(0,0)'::point)::numeric(20,5);
! select ('(0,0)'::point <@> '(180,0)'::point)::numeric(20,5);
! select ('(0,0)'::point <@> '(0,90)'::point)::numeric(20,5);
! select ('(0,0)'::point <@> '(90,0)'::point)::numeric(20,5);
! select ('(0,0)'::point <@> '(1,0)'::point)::numeric(20,5);
! select ('(0,0)'::point <@> '(0,1)'::point)::numeric(20,5);
! select ('(0,30)'::point <@> '(1,30)'::point)::numeric(20,5);
! select ('(0,30)'::point <@> '(0,31)'::point)::numeric(20,5);
! select ('(0,60)'::point <@> '(1,60)'::point)::numeric(20,5);
! select ('(0,60)'::point <@> '(0,61)'::point)::numeric(20,5);
! select ('(87.6,41.8)'::point <@> '(106.7,35.1)'::point)::numeric(20,5);
! select (('(87.6,41.8)'::point <@> '(106.7,35.1)'::point)*5280.*12.*2.54/100.)::numeric(20,5);
!
! --
! -- Test getting a bounding box around points.
! --
!
! select cube_ll_coord(earth_box(ll_to_earth(0,0),112000),1)::numeric(20,5),
!        cube_ll_coord(earth_box(ll_to_earth(0,0),112000),2)::numeric(20,5),
!        cube_ll_coord(earth_box(ll_to_earth(0,0),112000),3)::numeric(20,5),
!        cube_ur_coord(earth_box(ll_to_earth(0,0),112000),1)::numeric(20,5),
!        cube_ur_coord(earth_box(ll_to_earth(0,0),112000),2)::numeric(20,5),
!        cube_ur_coord(earth_box(ll_to_earth(0,0),112000),3)::numeric(20,5);
! select cube_ll_coord(earth_box(ll_to_earth(0,0),pi()*earth()),1)::numeric(20,5),
!        cube_ll_coord(earth_box(ll_to_earth(0,0),pi()*earth()),2)::numeric(20,5),
!        cube_ll_coord(earth_box(ll_to_earth(0,0),pi()*earth()),3)::numeric(20,5),
!        cube_ur_coord(earth_box(ll_to_earth(0,0),pi()*earth()),1)::numeric(20,5),
!        cube_ur_coord(earth_box(ll_to_earth(0,0),pi()*earth()),2)::numeric(20,5),
!        cube_ur_coord(earth_box(ll_to_earth(0,0),pi()*earth()),3)::numeric(20,5);
! select cube_ll_coord(earth_box(ll_to_earth(0,0),10*earth()),1)::numeric(20,5),
!        cube_ll_coord(earth_box(ll_to_earth(0,0),10*earth()),2)::numeric(20,5),
!        cube_ll_coord(earth_box(ll_to_earth(0,0),10*earth()),3)::numeric(20,5),
!        cube_ur_coord(earth_box(ll_to_earth(0,0),10*earth()),1)::numeric(20,5),
!        cube_ur_coord(earth_box(ll_to_earth(0,0),10*earth()),2)::numeric(20,5),
!        cube_ur_coord(earth_box(ll_to_earth(0,0),10*earth()),3)::numeric(20,5);
!
! --
! -- Test for points that should be in bounding boxes.
! --
!
! select earth_box(ll_to_earth(0,0),
!        earth_distance(ll_to_earth(0,0),ll_to_earth(0,1))*1.00001) @
!        ll_to_earth(0,1);
! select earth_box(ll_to_earth(0,0),
!        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.1))*1.00001) @
!        ll_to_earth(0,0.1);
! select earth_box(ll_to_earth(0,0),
!        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.01))*1.00001) @
!        ll_to_earth(0,0.01);
! select earth_box(ll_to_earth(0,0),
!        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.001))*1.00001) @
!        ll_to_earth(0,0.001);
! select earth_box(ll_to_earth(0,0),
!        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.0001))*1.00001) @
!        ll_to_earth(0,0.0001);
! select earth_box(ll_to_earth(0,0),
!        earth_distance(ll_to_earth(0,0),ll_to_earth(0.0001,0.0001))*1.00001) @
!        ll_to_earth(0.0001,0.0001);
! select earth_box(ll_to_earth(45,45),
!        earth_distance(ll_to_earth(45,45),ll_to_earth(45.0001,45.0001))*1.00001) @
!        ll_to_earth(45.0001,45.0001);
! select earth_box(ll_to_earth(90,180),
!        earth_distance(ll_to_earth(90,180),ll_to_earth(90.0001,180.0001))*1.00001) @
!        ll_to_earth(90.0001,180.0001);
!
! --
! -- Test for points that shouldn't be in bounding boxes. Note that we need
! -- to make points way outside, since some points close may be in the box
! -- but further away than the distance we are testing.
! --
!
! select earth_box(ll_to_earth(0,0),
!        earth_distance(ll_to_earth(0,0),ll_to_earth(0,1))*.57735) @
!        ll_to_earth(0,1);
! select earth_box(ll_to_earth(0,0),
!        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.1))*.57735) @
!        ll_to_earth(0,0.1);
! select earth_box(ll_to_earth(0,0),
!        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.01))*.57735) @
!        ll_to_earth(0,0.01);
! select earth_box(ll_to_earth(0,0),
!        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.001))*.57735) @
!        ll_to_earth(0,0.001);
! select earth_box(ll_to_earth(0,0),
!        earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.0001))*.57735) @
!        ll_to_earth(0,0.0001);
! select earth_box(ll_to_earth(0,0),
!        earth_distance(ll_to_earth(0,0),ll_to_earth(0.0001,0.0001))*.57735) @
!        ll_to_earth(0.0001,0.0001);
! select earth_box(ll_to_earth(45,45),
!        earth_distance(ll_to_earth(45,45),ll_to_earth(45.0001,45.0001))*.57735) @
!        ll_to_earth(45.0001,45.0001);
! select earth_box(ll_to_earth(90,180),
!        earth_distance(ll_to_earth(90,180),ll_to_earth(90.0001,180.0001))*.57735) @
!        ll_to_earth(90.0001,180.0001);
!
! --
! -- Test the recommended constraints.
! --
!
! select is_point(ll_to_earth(0,0));
! select cube_dim(ll_to_earth(0,0)) <= 3;
! select abs(cube_distance(ll_to_earth(0,0), '(0)'::cube) / earth() - 1) <
!        '10e-12'::float8;
! select is_point(ll_to_earth(30,60));
! select cube_dim(ll_to_earth(30,60)) <= 3;
! select abs(cube_distance(ll_to_earth(30,60), '(0)'::cube) / earth() - 1) <
!        '10e-12'::float8;
! select is_point(ll_to_earth(60,90));
! select cube_dim(ll_to_earth(60,90)) <= 3;
! select abs(cube_distance(ll_to_earth(60,90), '(0)'::cube) / earth() - 1) <
!        '10e-12'::float8;
! select is_point(ll_to_earth(-30,-90));
! select cube_dim(ll_to_earth(-30,-90)) <= 3;
! select abs(cube_distance(ll_to_earth(-30,-90), '(0)'::cube) / earth() - 1) <
!        '10e-12'::float8;