Changing my email
[viking.git] / src / coords.c
blobdf2391bc31813ccb14d1f13e4d8d19024e23cdfd
1 /*
2 coords.c
3 borrowed from:
4 http://acme.com/software/coords/
5 I (Evan Battaglia <viking@greentorch.org>) have only made some small changes such as
6 renaming functions and defining LatLon and UTM structs.
7 2004-02-10 -- I also added a function of my own -- a_coords_utm_diff() -- that I felt belonged in coords.c
8 2004-02-21 -- I also added a_coords_utm_equal().
9 */
10 /* coords.h - include file for coords routines
12 ** Copyright © 2001 by Jef Poskanzer <jef@acme.com>.
13 ** All rights reserved.
15 ** Redistribution and use in source and binary forms, with or without
16 ** modification, are permitted provided that the following conditions
17 ** are met:
18 ** 1. Redistributions of source code must retain the above copyright
19 ** notice, this list of conditions and the following disclaimer.
20 ** 2. Redistributions in binary form must reproduce the above copyright
21 ** notice, this list of conditions and the following disclaimer in the
22 ** documentation and/or other materials provided with the distribution.
24 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 ** SUCH DAMAGE.
37 #include <stdlib.h>
38 #include <string.h>
39 #include <math.h>
41 #include "viking.h"
43 #ifdef WINDOWS
44 #define M_PI 3.14159265358979
45 #endif
47 #define PIOVER180 0.01745329252
49 #define K0 0.9996
51 /* WGS-84 */
52 #define EquatorialRadius 6378137
53 #define EccentricitySquared 0.00669438
55 static char coords_utm_letter( double latitude );
57 int a_coords_utm_equal( const struct UTM *utm1, const struct UTM *utm2 )
59 return ( utm1->easting == utm2->easting && utm1->northing == utm2->northing && utm1->zone == utm2->zone );
62 double a_coords_utm_diff( const struct UTM *utm1, const struct UTM *utm2 )
64 static struct LatLon tmp1, tmp2;
65 if ( utm1->zone == utm2->zone ) {
66 return sqrt ( pow ( utm1->easting - utm2->easting, 2 ) + pow ( utm1->northing - utm2->northing, 2 ) );
67 } else {
68 a_coords_utm_to_latlon ( utm1, &tmp1 );
69 a_coords_utm_to_latlon ( utm2, &tmp2 );
70 return a_coords_latlon_diff ( &tmp1, &tmp2 );
74 double a_coords_latlon_diff ( const struct LatLon *ll1, const struct LatLon *ll2 )
76 static struct LatLon tmp1, tmp2;
77 tmp1.lat = ll1->lat * PIOVER180;
78 tmp1.lon = ll1->lon * PIOVER180;
79 tmp2.lat = ll2->lat * PIOVER180;
80 tmp2.lon = ll2->lon * PIOVER180;
81 return EquatorialRadius * acos(sin(tmp1.lat)*sin(tmp2.lat)+cos(tmp1.lat)*cos(tmp2.lat)*cos(tmp1.lon-tmp2.lon));
84 void a_coords_latlon_to_utm( const struct LatLon *latlon, struct UTM *utm )
86 double latitude;
87 double longitude;
88 double lat_rad, long_rad;
89 double long_origin, long_origin_rad;
90 double eccPrimeSquared;
91 double N, T, C, A, M;
92 int zone;
93 double northing, easting;
95 longitude = latlon->lon;
96 latitude = latlon->lat;
98 /* We want the longitude within -180..180. */
99 if ( longitude < -180.0 )
100 longitude += 360.0;
101 if ( longitude > 180.0 )
102 longitude -= 360.0;
104 /* Now convert. */
105 lat_rad = latitude * M_PI / 180.0;
106 long_rad = longitude * M_PI / 180.0;
107 zone = (int) ( ( longitude + 180 ) / 6 ) + 1;
108 if ( latitude >= 56.0 && latitude < 64.0 &&
109 longitude >= 3.0 && longitude < 12.0 )
110 zone = 32;
111 /* Special zones for Svalbard. */
112 if ( latitude >= 72.0 && latitude < 84.0 )
114 if ( longitude >= 0.0 && longitude < 9.0 ) zone = 31;
115 else if ( longitude >= 9.0 && longitude < 21.0 ) zone = 33;
116 else if ( longitude >= 21.0 && longitude < 33.0 ) zone = 35;
117 else if ( longitude >= 33.0 && longitude < 42.0 ) zone = 37;
119 long_origin = ( zone - 1 ) * 6 - 180 + 3; /* +3 puts origin in middle of zone */
120 long_origin_rad = long_origin * M_PI / 180.0;
121 eccPrimeSquared = EccentricitySquared / ( 1.0 - EccentricitySquared );
122 N = EquatorialRadius / sqrt( 1.0 - EccentricitySquared * sin( lat_rad ) * sin( lat_rad ) );
123 T = tan( lat_rad ) * tan( lat_rad );
124 C = eccPrimeSquared * cos( lat_rad ) * cos( lat_rad );
125 A = cos( lat_rad ) * ( long_rad - long_origin_rad );
126 M = EquatorialRadius * ( ( 1.0 - EccentricitySquared / 4 - 3 * EccentricitySquared * EccentricitySquared / 64 - 5 * EccentricitySquared * EccentricitySquared * EccentricitySquared / 256 ) * lat_rad - ( 3 * EccentricitySquared / 8 + 3 * EccentricitySquared * EccentricitySquared / 32 + 45 * EccentricitySquared * EccentricitySquared * EccentricitySquared / 1024 ) * sin( 2 * lat_rad ) + ( 15 * EccentricitySquared * EccentricitySquared / 256 + 45 * EccentricitySquared * EccentricitySquared * EccentricitySquared / 1024 ) * sin( 4 * lat_rad ) - ( 35 * EccentricitySquared * EccentricitySquared * EccentricitySquared / 3072 ) * sin( 6 * lat_rad ) );
127 easting =
128 K0 * N * ( A + ( 1 - T + C ) * A * A * A / 6 + ( 5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared ) * A * A * A * A * A / 120 ) + 500000.0;
129 northing =
130 K0 * ( M + N * tan( lat_rad ) * ( A * A / 2 + ( 5 - T + 9 * C + 4 * C * C ) * A * A * A * A / 24 + ( 61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared ) * A * A * A * A * A * A / 720 ) );
131 if ( latitude < 0.0 )
132 northing += 10000000.0; /* 1e7 meter offset for southern hemisphere */
134 utm->northing = northing;
135 utm->easting = easting;
136 utm->zone = zone;
137 utm->letter = coords_utm_letter( latitude );
139 /* All done. */
143 static char coords_utm_letter( double latitude )
145 /* This routine determines the correct UTM letter designator for the
146 ** given latitude. It returns 'Z' if the latitude is outside the UTM
147 ** limits of 84N to 80S.
149 if ( latitude <= 84.0 && latitude >= 72.0 ) return 'X';
150 else if ( latitude < 72.0 && latitude >= 64.0 ) return 'W';
151 else if ( latitude < 64.0 && latitude >= 56.0 ) return 'V';
152 else if ( latitude < 56.0 && latitude >= 48.0 ) return 'U';
153 else if ( latitude < 48.0 && latitude >= 40.0 ) return 'T';
154 else if ( latitude < 40.0 && latitude >= 32.0 ) return 'S';
155 else if ( latitude < 32.0 && latitude >= 24.0 ) return 'R';
156 else if ( latitude < 24.0 && latitude >= 16.0 ) return 'Q';
157 else if ( latitude < 16.0 && latitude >= 8.0 ) return 'P';
158 else if ( latitude < 8.0 && latitude >= 0.0 ) return 'N';
159 else if ( latitude < 0.0 && latitude >= -8.0 ) return 'M';
160 else if ( latitude < -8.0 && latitude >= -16.0 ) return 'L';
161 else if ( latitude < -16.0 && latitude >= -24.0 ) return 'K';
162 else if ( latitude < -24.0 && latitude >= -32.0 ) return 'J';
163 else if ( latitude < -32.0 && latitude >= -40.0 ) return 'H';
164 else if ( latitude < -40.0 && latitude >= -48.0 ) return 'G';
165 else if ( latitude < -48.0 && latitude >= -56.0 ) return 'F';
166 else if ( latitude < -56.0 && latitude >= -64.0 ) return 'E';
167 else if ( latitude < -64.0 && latitude >= -72.0 ) return 'D';
168 else if ( latitude < -72.0 && latitude >= -80.0 ) return 'C';
169 else return 'Z';
174 void a_coords_utm_to_latlon( const struct UTM *utm, struct LatLon *latlon )
176 double northing, easting;
177 int zone;
178 char letter[100];
179 double x, y;
180 double eccPrimeSquared;
181 double e1;
182 double N1, T1, C1, R1, D, M;
183 double long_origin;
184 double mu, phi1_rad;
185 int northernHemisphere; /* 1 for northern hemisphere, 0 for southern */
186 double latitude, longitude;
188 northing = utm->northing;
189 easting = utm->easting;
190 zone = utm->zone;
191 letter[0] = utm->letter;
193 /* Now convert. */
194 x = easting - 500000.0; /* remove 500000 meter offset */
195 y = northing;
196 if ( ( *letter - 'N' ) >= 0 )
197 northernHemisphere = 1; /* northern hemisphere */
198 else
200 northernHemisphere = 0; /* southern hemisphere */
201 y -= 10000000.0; /* remove 1e7 meter offset */
203 long_origin = ( zone - 1 ) * 6 - 180 + 3; /* +3 puts origin in middle of zone */
204 eccPrimeSquared = EccentricitySquared / ( 1.0 - EccentricitySquared );
205 e1 = ( 1.0 - sqrt( 1.0 - EccentricitySquared ) ) / ( 1.0 + sqrt( 1.0 - EccentricitySquared ) );
206 M = y / K0;
207 mu = M / ( EquatorialRadius * ( 1.0 - EccentricitySquared / 4 - 3 * EccentricitySquared * EccentricitySquared / 64 - 5 * EccentricitySquared * EccentricitySquared * EccentricitySquared / 256 ) );
208 phi1_rad = mu + ( 3 * e1 / 2 - 27 * e1 * e1 * e1 / 32 )* sin( 2 * mu ) + ( 21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32 ) * sin( 4 * mu ) + ( 151 * e1 * e1 * e1 / 96 ) * sin( 6 *mu );
209 N1 = EquatorialRadius / sqrt( 1.0 - EccentricitySquared * sin( phi1_rad ) * sin( phi1_rad ) );
210 T1 = tan( phi1_rad ) * tan( phi1_rad );
211 C1 = eccPrimeSquared * cos( phi1_rad ) * cos( phi1_rad );
212 R1 = EquatorialRadius * ( 1.0 - EccentricitySquared ) / pow( 1.0 - EccentricitySquared * sin( phi1_rad ) * sin( phi1_rad ), 1.5 );
213 D = x / ( N1 * K0 );
214 latitude = phi1_rad - ( N1 * tan( phi1_rad ) / R1 ) * ( D * D / 2 -( 5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * eccPrimeSquared ) * D * D * D * D / 24 + ( 61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * eccPrimeSquared - 3 * C1 * C1 ) * D * D * D * D * D * D / 720 );
215 latitude = latitude * 180.0 / M_PI;
216 longitude = ( D - ( 1 + 2 * T1 + C1 ) * D * D * D / 6 + ( 5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * eccPrimeSquared + 24 * T1 * T1 ) * D * D * D * D * D / 120 ) / cos( phi1_rad );
217 longitude = long_origin + longitude * 180.0 / M_PI;
219 /* Show results. */
221 latlon->lat = latitude;
222 latlon->lon = longitude;