]> granicus.if.org Git - postgresql/blob - contrib/earthdistance/earthdistance.c
Separate block sampling functions
[postgresql] / contrib / earthdistance / earthdistance.c
1 /* contrib/earthdistance/earthdistance.c */
2
3 #include "postgres.h"
4
5 #include <math.h>
6
7 #include "utils/geo_decls.h"    /* for Point */
8
9 #ifndef M_PI
10 #define M_PI 3.14159265358979323846
11 #endif
12
13 PG_MODULE_MAGIC;
14
15 /* Earth's radius is in statute miles. */
16 static const double EARTH_RADIUS = 3958.747716;
17 static const double TWO_PI = 2.0 * M_PI;
18
19
20 /******************************************************
21  *
22  * degtorad - convert degrees to radians
23  *
24  * arg: double, angle in degrees
25  *
26  * returns: double, same angle in radians
27  ******************************************************/
28
29 static double
30 degtorad(double degrees)
31 {
32         return (degrees / 360.0) * TWO_PI;
33 }
34
35 /******************************************************
36  *
37  * geo_distance_internal - distance between points
38  *
39  * args:
40  *       a pair of points - for each point,
41  *         x-coordinate is longitude in degrees west of Greenwich
42  *         y-coordinate is latitude in degrees above equator
43  *
44  * returns: double
45  *       distance between the points in miles on earth's surface
46  ******************************************************/
47
48 static double
49 geo_distance_internal(Point *pt1, Point *pt2)
50 {
51         double          long1,
52                                 lat1,
53                                 long2,
54                                 lat2;
55         double          longdiff;
56         double          sino;
57
58         /* convert degrees to radians */
59
60         long1 = degtorad(pt1->x);
61         lat1 = degtorad(pt1->y);
62
63         long2 = degtorad(pt2->x);
64         lat2 = degtorad(pt2->y);
65
66         /* compute difference in longitudes - want < 180 degrees */
67         longdiff = fabs(long1 - long2);
68         if (longdiff > M_PI)
69                 longdiff = TWO_PI - longdiff;
70
71         sino = sqrt(sin(fabs(lat1 - lat2) / 2.) * sin(fabs(lat1 - lat2) / 2.) +
72                         cos(lat1) * cos(lat2) * sin(longdiff / 2.) * sin(longdiff / 2.));
73         if (sino > 1.)
74                 sino = 1.;
75
76         return 2. * EARTH_RADIUS * asin(sino);
77 }
78
79
80 /******************************************************
81  *
82  * geo_distance - distance between points
83  *
84  * args:
85  *       a pair of points - for each point,
86  *         x-coordinate is longitude in degrees west of Greenwich
87  *         y-coordinate is latitude in degrees above equator
88  *
89  * returns: float8
90  *       distance between the points in miles on earth's surface
91  *
92  * If float8 is passed-by-value, the oldstyle version-0 calling convention
93  * is unportable, so we use version-1.  However, if it's passed-by-reference,
94  * continue to use oldstyle.  This is just because we'd like earthdistance
95  * to serve as a canary for any unintentional breakage of version-0 functions
96  * with float8 results.
97  ******************************************************/
98
99 #ifdef USE_FLOAT8_BYVAL
100
101 PG_FUNCTION_INFO_V1(geo_distance);
102
103 Datum
104 geo_distance(PG_FUNCTION_ARGS)
105 {
106         Point      *pt1 = PG_GETARG_POINT_P(0);
107         Point      *pt2 = PG_GETARG_POINT_P(1);
108         float8          result;
109
110         result = geo_distance_internal(pt1, pt2);
111         PG_RETURN_FLOAT8(result);
112 }
113 #else                                                   /* !USE_FLOAT8_BYVAL */
114
115 double     *geo_distance(Point *pt1, Point *pt2);
116
117 double *
118 geo_distance(Point *pt1, Point *pt2)
119 {
120         double     *resultp = palloc(sizeof(double));
121
122         *resultp = geo_distance_internal(pt1, pt2);
123         return resultp;
124 }
125
126 #endif   /* USE_FLOAT8_BYVAL */