4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "Geo/UTM.hpp"
25 #include "Geo/GeoPoint.hpp"
26 #include "Util/Macros.hpp"
28 static constexpr double k0
= 0.9996;
30 static constexpr double e
= 0.00669438;
31 static constexpr double e2
= e
* e
;
32 static constexpr double e3
= e
* e
;
33 static constexpr double e_p2
= e
/ (1.0 - e
);
35 static constexpr double r
= 6378137;
38 UTM::CalculateZoneLetter(const Angle latitude
)
40 static constexpr char letters
[] = "CDEFGHJKLMNPQRSTUVWXX";
41 unsigned index
= (unsigned)((latitude
.Degrees() + fixed(80)) / 8);
42 return (index
< ARRAY_SIZE(letters
)) ? letters
[index
] : '\0';
46 UTM::CalculateZoneNumber(const GeoPoint
&p
)
48 if (p
.latitude
.Degrees() <= fixed(64) &&
49 p
.latitude
.Degrees() >= fixed(56) &&
50 p
.longitude
.Degrees() <= fixed(12) &&
51 p
.longitude
.Degrees() >= fixed(3))
54 if (p
.latitude
.Degrees() <= fixed(84) &&
55 p
.latitude
.Degrees() >= fixed(72) &&
56 p
.longitude
.Degrees() >= fixed(0)) {
57 if (p
.longitude
.Degrees() <= fixed(9))
59 if (p
.longitude
.Degrees() <= fixed(21))
61 if (p
.longitude
.Degrees() <= fixed(33))
63 if (p
.longitude
.Degrees() <= fixed(42))
67 return (int)floor((p
.longitude
.Degrees() + fixed(180)) / 6) + 1;
71 UTM::GetCentralMeridian(unsigned char zone_number
)
73 return Angle::Degrees(fixed((zone_number
- 1) * 6 - 180 + 3));
77 UTM::FromGeoPoint(const GeoPoint
&p
) {
78 double lat
= (double)p
.latitude
.Radians();
79 double _sin
= (double)p
.latitude
.sin();
80 double _cos
= (double)p
.latitude
.cos();
81 double _tan
= _sin
/ _cos
;
82 double tan2
= _tan
* _tan
;
83 double tan4
= tan2
* tan2
;
86 utm
.zone_number
= CalculateZoneNumber(p
);
87 utm
.zone_letter
= CalculateZoneLetter(p
.latitude
);
89 double n
= r
/ sqrt(1 - e
* _sin
* _sin
);
90 double c
= e_p2
* _cos
* _cos
;
92 double a
= _cos
* (double)(p
.longitude
.Radians() -
93 GetCentralMeridian(utm
.zone_number
).Radians());
100 double m
= r
* ((1 - e
/ 4 - 3 * e2
/ 64 - 5 * e3
/ 256) * lat
-
101 (3 * e
/ 8 + 3 * e2
/ 32 + 45 * e3
/ 1024) * sin(2 * lat
) +
102 (15 * e2
/ 256 + 45 * e3
/ 1024) * sin(4 * lat
) -
103 (35 * e3
/ 3072) * sin(6 * lat
));
105 utm
.easting
= fixed(k0
* n
* (a
+ (1 - tan2
+ c
) * a3
/ 6 +
106 (5 - 18 * tan2
+ tan4
+ 72 * c
- 58 * e_p2
) * a5
/ 120) + 500000.0);
108 utm
.northing
= fixed(k0
* (m
+ n
* _tan
*
109 (a2
/ 2 + a4
/ 24 * (5 - tan2
+ 9 * c
+ 4 * c
* c
) +
110 a6
/ 720 * (61 - 58 * tan2
+ tan4
+ 600 * c
- 330 * e_p2
))));
111 if (negative(p
.latitude
.Native()))
112 utm
.northing
+= fixed(10000000);
118 UTM::ToGeoPoint() const
120 // remove longitude offset
121 double x
= (double)easting
- 500000;
122 double y
= (double)northing
;
124 // if southern hemisphere
125 if (zone_letter
< 'N')
129 double mu
= m
/ (r
* (1 - e
/ 4 - 3 * e2
/ 64 - 5 * e3
/ 256));
130 double _e_sqrt
= sqrt(1 - e
);
131 double _e
= (1 - _e_sqrt
) / (1 + _e_sqrt
);
132 double _e2
= _e
* _e
;
133 double _e3
= _e
* _e2
;
134 double _e4
= _e
* _e3
;
136 Angle phi1rad
= Angle::Radians(fixed(mu
+
137 (3 * _e
/ 2 - 27 * _e3
/ 32) * sin(2 * mu
) +
138 (21 * _e3
/ 16 - 55 * _e4
/ 32) * sin(4 * mu
) +
139 (151 * _e3
/ 96) * sin(6 * mu
)));
141 double _sin
= (double)phi1rad
.sin();
142 double sin2
= _sin
* _sin
;
143 double _cos
= (double)phi1rad
.cos();
144 double _tan
= _sin
/ _cos
;
145 double tan2
= _tan
* _tan
;
146 double tan4
= tan2
* tan2
;
148 double _e_sin2_sqrt
= sqrt(1 - e
* sin2
);
149 double _e_sin2_sqrt3
= _e_sin2_sqrt
* _e_sin2_sqrt
* _e_sin2_sqrt
;
150 double n
= r
/ _e_sin2_sqrt
;
151 double c
= e
* _cos
* _cos
;
153 double _r
= r
* (1 - e
) / _e_sin2_sqrt3
;
155 double d
= x
/ (n
* k0
);
162 double latitude
= (double)phi1rad
.Radians() -
163 (n
* _tan
/ _r
) * (d2
/ 2 -
164 d4
/ 24 * (5 + 3 * tan2
+ 10 * c
- 4 * c2
- 9 * e_p2
) +
165 d6
/ 720 * (61 + 90 * tan2
+ 298 * c
+ 45 * tan4
- 252 * e_p2
- 3 * c2
));
167 double longitude
= (d
- d3
/ 6 * (1 + 2 * tan2
+ c
) +
168 d5
/ 120 * (5 - 2 * c
+ 28 * tan2
- 3 * c2
+ 8 * e_p2
+ 24 * tan4
)) / _cos
;
170 return GeoPoint(Angle::Radians(fixed(longitude
)) + GetCentralMeridian(zone_number
),
171 Angle::Radians(fixed(latitude
)));