geographiclib 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +12 -0
- data/LICENSE +24 -0
- data/ext/geographiclib/Accumulator.cpp +23 -0
- data/ext/geographiclib/AlbersEqualArea.cpp +445 -0
- data/ext/geographiclib/AzimuthalEquidistant.cpp +41 -0
- data/ext/geographiclib/CassiniSoldner.cpp +89 -0
- data/ext/geographiclib/CircularEngine.cpp +96 -0
- data/ext/geographiclib/DMS.cpp +381 -0
- data/ext/geographiclib/Ellipsoid.cpp +125 -0
- data/ext/geographiclib/EllipticFunction.cpp +512 -0
- data/ext/geographiclib/GARS.cpp +122 -0
- data/ext/geographiclib/GeoCoords.cpp +175 -0
- data/ext/geographiclib/Geocentric.cpp +172 -0
- data/ext/geographiclib/Geodesic.cpp +1908 -0
- data/ext/geographiclib/GeodesicExact.cpp +927 -0
- data/ext/geographiclib/GeodesicExactC4.cpp +7879 -0
- data/ext/geographiclib/GeodesicLine.cpp +321 -0
- data/ext/geographiclib/GeodesicLineExact.cpp +289 -0
- data/ext/geographiclib/GeographicLib/Accumulator.hpp +184 -0
- data/ext/geographiclib/GeographicLib/AlbersEqualArea.hpp +312 -0
- data/ext/geographiclib/GeographicLib/AzimuthalEquidistant.hpp +139 -0
- data/ext/geographiclib/GeographicLib/CassiniSoldner.hpp +204 -0
- data/ext/geographiclib/GeographicLib/CircularEngine.hpp +195 -0
- data/ext/geographiclib/GeographicLib/Config.h +12 -0
- data/ext/geographiclib/GeographicLib/Constants.hpp +387 -0
- data/ext/geographiclib/GeographicLib/DMS.hpp +370 -0
- data/ext/geographiclib/GeographicLib/Ellipsoid.hpp +534 -0
- data/ext/geographiclib/GeographicLib/EllipticFunction.hpp +692 -0
- data/ext/geographiclib/GeographicLib/GARS.hpp +143 -0
- data/ext/geographiclib/GeographicLib/GeoCoords.hpp +544 -0
- data/ext/geographiclib/GeographicLib/Geocentric.hpp +267 -0
- data/ext/geographiclib/GeographicLib/Geodesic.hpp +970 -0
- data/ext/geographiclib/GeographicLib/GeodesicExact.hpp +862 -0
- data/ext/geographiclib/GeographicLib/GeodesicLine.hpp +701 -0
- data/ext/geographiclib/GeographicLib/GeodesicLineExact.hpp +667 -0
- data/ext/geographiclib/GeographicLib/Geohash.hpp +180 -0
- data/ext/geographiclib/GeographicLib/Geoid.hpp +472 -0
- data/ext/geographiclib/GeographicLib/Georef.hpp +160 -0
- data/ext/geographiclib/GeographicLib/Gnomonic.hpp +206 -0
- data/ext/geographiclib/GeographicLib/GravityCircle.hpp +301 -0
- data/ext/geographiclib/GeographicLib/GravityModel.hpp +520 -0
- data/ext/geographiclib/GeographicLib/LambertConformalConic.hpp +313 -0
- data/ext/geographiclib/GeographicLib/LocalCartesian.hpp +236 -0
- data/ext/geographiclib/GeographicLib/MGRS.hpp +355 -0
- data/ext/geographiclib/GeographicLib/MagneticCircle.hpp +178 -0
- data/ext/geographiclib/GeographicLib/MagneticModel.hpp +347 -0
- data/ext/geographiclib/GeographicLib/Math.hpp +920 -0
- data/ext/geographiclib/GeographicLib/NormalGravity.hpp +350 -0
- data/ext/geographiclib/GeographicLib/OSGB.hpp +249 -0
- data/ext/geographiclib/GeographicLib/PolarStereographic.hpp +150 -0
- data/ext/geographiclib/GeographicLib/PolygonArea.hpp +288 -0
- data/ext/geographiclib/GeographicLib/Rhumb.hpp +589 -0
- data/ext/geographiclib/GeographicLib/SphericalEngine.hpp +376 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic.hpp +354 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic1.hpp +281 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic2.hpp +315 -0
- data/ext/geographiclib/GeographicLib/TransverseMercator.hpp +196 -0
- data/ext/geographiclib/GeographicLib/TransverseMercatorExact.hpp +254 -0
- data/ext/geographiclib/GeographicLib/UTMUPS.hpp +421 -0
- data/ext/geographiclib/GeographicLib/Utility.hpp +612 -0
- data/ext/geographiclib/Geohash.cpp +102 -0
- data/ext/geographiclib/Geoid.cpp +509 -0
- data/ext/geographiclib/Georef.cpp +135 -0
- data/ext/geographiclib/Gnomonic.cpp +85 -0
- data/ext/geographiclib/GravityCircle.cpp +129 -0
- data/ext/geographiclib/GravityModel.cpp +360 -0
- data/ext/geographiclib/LambertConformalConic.cpp +456 -0
- data/ext/geographiclib/LocalCartesian.cpp +62 -0
- data/ext/geographiclib/MGRS.cpp +461 -0
- data/ext/geographiclib/MagneticCircle.cpp +52 -0
- data/ext/geographiclib/MagneticModel.cpp +269 -0
- data/ext/geographiclib/Math.cpp +63 -0
- data/ext/geographiclib/NormalGravity.cpp +262 -0
- data/ext/geographiclib/OSGB.cpp +167 -0
- data/ext/geographiclib/PolarStereographic.cpp +108 -0
- data/ext/geographiclib/PolygonArea.cpp +204 -0
- data/ext/geographiclib/Rhumb.cpp +383 -0
- data/ext/geographiclib/SphericalEngine.cpp +477 -0
- data/ext/geographiclib/TransverseMercator.cpp +603 -0
- data/ext/geographiclib/TransverseMercatorExact.cpp +464 -0
- data/ext/geographiclib/UTMUPS.cpp +296 -0
- data/ext/geographiclib/Utility.cpp +61 -0
- data/ext/geographiclib/extconf.rb +3 -0
- data/ext/geographiclib/geographiclib.cpp +62 -0
- data/lib/geographiclib.rb +20 -0
- metadata +140 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
/**
|
2
|
+
* \file GARS.cpp
|
3
|
+
* \brief Implementation for GeographicLib::GARS class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2015) <charles@karney.com> and licensed under
|
6
|
+
* the MIT/X11 License. For more information, see
|
7
|
+
* http://geographiclib.sourceforge.net/
|
8
|
+
**********************************************************************/
|
9
|
+
|
10
|
+
#include <GeographicLib/GARS.hpp>
|
11
|
+
#include <GeographicLib/Utility.hpp>
|
12
|
+
|
13
|
+
namespace GeographicLib {
|
14
|
+
|
15
|
+
using namespace std;
|
16
|
+
|
17
|
+
const string GARS::digits_ = "0123456789";
|
18
|
+
const string GARS::letters_ = "ABCDEFGHJKLMNPQRSTUVWXYZ";
|
19
|
+
|
20
|
+
void GARS::Forward(real lat, real lon, int prec, std::string& gars) {
|
21
|
+
if (abs(lat) > 90)
|
22
|
+
throw GeographicErr("Latitude " + Utility::str(lat)
|
23
|
+
+ "d not in [-90d, 90d]");
|
24
|
+
if (Math::isnan(lat) || Math::isnan(lon)) {
|
25
|
+
gars = "INVALID";
|
26
|
+
return;
|
27
|
+
}
|
28
|
+
lon = Math::AngNormalize(lon); // lon in [-180,180)
|
29
|
+
if (lat == 90) lat *= (1 - numeric_limits<real>::epsilon() / 2);
|
30
|
+
prec = max(0, min(int(maxprec_), prec));
|
31
|
+
int
|
32
|
+
x = int(floor(lon * m_)) - lonorig_ * m_,
|
33
|
+
y = int(floor(lat * m_)) - latorig_ * m_,
|
34
|
+
ilon = x * mult1_ / m_,
|
35
|
+
ilat = y * mult1_ / m_;
|
36
|
+
x -= ilon * m_ / mult1_; y -= ilat * m_ / mult1_;
|
37
|
+
char gars1[maxlen_];
|
38
|
+
++ilon;
|
39
|
+
for (int c = lonlen_; c--;) {
|
40
|
+
gars1[c] = digits_[ ilon % baselon_]; ilon /= baselon_;
|
41
|
+
}
|
42
|
+
for (int c = latlen_; c--;) {
|
43
|
+
gars1[lonlen_ + c] = letters_[ilat % baselat_]; ilat /= baselat_;
|
44
|
+
}
|
45
|
+
if (prec > 0) {
|
46
|
+
ilon = x / mult3_; ilat = y / mult3_;
|
47
|
+
gars1[baselen_] = digits_[mult2_ * (mult2_ - 1 - ilat) + ilon + 1];
|
48
|
+
if (prec > 1) {
|
49
|
+
ilon = x % mult3_; ilat = y % mult3_;
|
50
|
+
gars1[baselen_ + 1] = digits_[mult3_ * (mult3_ - 1 - ilat) + ilon + 1];
|
51
|
+
}
|
52
|
+
}
|
53
|
+
gars.resize(baselen_ + prec);
|
54
|
+
copy(gars1, gars1 + baselen_ + prec, gars.begin());
|
55
|
+
}
|
56
|
+
|
57
|
+
void GARS::Reverse(const std::string& gars, real& lat, real& lon,
|
58
|
+
int& prec, bool centerp) {
|
59
|
+
int len = int(gars.length());
|
60
|
+
if (len >= 3 &&
|
61
|
+
toupper(gars[0]) == 'I' &&
|
62
|
+
toupper(gars[1]) == 'N' &&
|
63
|
+
toupper(gars[2]) == 'V') {
|
64
|
+
lat = lon = Math::NaN();
|
65
|
+
return;
|
66
|
+
}
|
67
|
+
if (len < baselen_)
|
68
|
+
throw GeographicErr("GARS must have at least 5 characters " + gars);
|
69
|
+
if (len > maxlen_)
|
70
|
+
throw GeographicErr("GARS can have at most 7 characters " + gars);
|
71
|
+
int prec1 = len - baselen_;
|
72
|
+
int ilon = 0;
|
73
|
+
for (int c = 0; c < lonlen_; ++c) {
|
74
|
+
int k = Utility::lookup(digits_, gars[c]);
|
75
|
+
if (k < 0)
|
76
|
+
throw GeographicErr("GARS must start with 3 digits " + gars);
|
77
|
+
ilon = ilon * baselon_ + k;
|
78
|
+
}
|
79
|
+
if (!(ilon >= 1 && ilon <= 720))
|
80
|
+
throw GeographicErr("Initial digits in GARS must lie in [1, 720] " +
|
81
|
+
gars);
|
82
|
+
--ilon;
|
83
|
+
int ilat = 0;
|
84
|
+
for (int c = 0; c < latlen_; ++c) {
|
85
|
+
int k = Utility::lookup(letters_, gars[lonlen_ + c]);
|
86
|
+
if (k < 0)
|
87
|
+
throw GeographicErr("Illegal letters in GARS " + gars.substr(3,2));
|
88
|
+
ilat = ilat * baselat_ + k;
|
89
|
+
}
|
90
|
+
if (!(ilat < 360))
|
91
|
+
throw GeographicErr("GARS letters must lie in [AA, QZ] " + gars);
|
92
|
+
real
|
93
|
+
unit = mult1_,
|
94
|
+
lat1 = ilat + latorig_ * unit,
|
95
|
+
lon1 = ilon + lonorig_ * unit;
|
96
|
+
if (prec1 > 0) {
|
97
|
+
int k = Utility::lookup(digits_, gars[baselen_]);
|
98
|
+
if (!(k >= 1 && k <= mult2_ * mult2_))
|
99
|
+
throw GeographicErr("6th character in GARS must [1, 4] " + gars);
|
100
|
+
--k;
|
101
|
+
unit *= mult2_;
|
102
|
+
lat1 = mult2_ * lat1 + (mult2_ - 1 - k / mult2_);
|
103
|
+
lon1 = mult2_ * lon1 + (k % mult2_);
|
104
|
+
if (prec1 > 1) {
|
105
|
+
k = Utility::lookup(digits_, gars[baselen_ + 1]);
|
106
|
+
if (!(k >= 1 /* && k <= mult3_ * mult3_ */))
|
107
|
+
throw GeographicErr("7th character in GARS must [1, 9] " + gars);
|
108
|
+
--k;
|
109
|
+
unit *= mult3_;
|
110
|
+
lat1 = mult3_ * lat1 + (mult3_ - 1 - k / mult3_);
|
111
|
+
lon1 = mult3_ * lon1 + (k % mult3_);
|
112
|
+
}
|
113
|
+
}
|
114
|
+
if (centerp) {
|
115
|
+
unit *= 2; lat1 = 2 * lat1 + 1; lon1 = 2 * lon1 + 1;
|
116
|
+
}
|
117
|
+
lat = lat1 / unit;
|
118
|
+
lon = lon1 / unit;
|
119
|
+
prec = prec1;
|
120
|
+
}
|
121
|
+
|
122
|
+
} // namespace GeographicLib
|
@@ -0,0 +1,175 @@
|
|
1
|
+
/**
|
2
|
+
* \file GeoCoords.cpp
|
3
|
+
* \brief Implementation for GeographicLib::GeoCoords class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2008-2015) <charles@karney.com> and licensed
|
6
|
+
* under the MIT/X11 License. For more information, see
|
7
|
+
* http://geographiclib.sourceforge.net/
|
8
|
+
**********************************************************************/
|
9
|
+
|
10
|
+
#include <GeographicLib/GeoCoords.hpp>
|
11
|
+
#include <GeographicLib/MGRS.hpp>
|
12
|
+
#include <GeographicLib/DMS.hpp>
|
13
|
+
#include <GeographicLib/Utility.hpp>
|
14
|
+
|
15
|
+
namespace GeographicLib {
|
16
|
+
|
17
|
+
using namespace std;
|
18
|
+
|
19
|
+
void GeoCoords::Reset(const std::string& s, bool centerp, bool longfirst) {
|
20
|
+
vector<string> sa;
|
21
|
+
const char* spaces = " \t\n\v\f\r,"; // Include comma as a space
|
22
|
+
for (string::size_type pos0 = 0, pos1; pos0 != string::npos;) {
|
23
|
+
pos1 = s.find_first_not_of(spaces, pos0);
|
24
|
+
if (pos1 == string::npos)
|
25
|
+
break;
|
26
|
+
pos0 = s.find_first_of(spaces, pos1);
|
27
|
+
sa.push_back(s.substr(pos1, pos0 == string::npos ? pos0 : pos0 - pos1));
|
28
|
+
}
|
29
|
+
if (sa.size() == 1) {
|
30
|
+
int prec;
|
31
|
+
MGRS::Reverse(sa[0], _zone, _northp, _easting, _northing, prec, centerp);
|
32
|
+
UTMUPS::Reverse(_zone, _northp, _easting, _northing,
|
33
|
+
_lat, _long, _gamma, _k);
|
34
|
+
} else if (sa.size() == 2) {
|
35
|
+
DMS::DecodeLatLon(sa[0], sa[1], _lat, _long, longfirst);
|
36
|
+
_long = Math::AngNormalize(_long);
|
37
|
+
UTMUPS::Forward( _lat, _long,
|
38
|
+
_zone, _northp, _easting, _northing, _gamma, _k);
|
39
|
+
} else if (sa.size() == 3) {
|
40
|
+
unsigned zoneind, coordind;
|
41
|
+
if (sa[0].size() > 0 && isalpha(sa[0][sa[0].size() - 1])) {
|
42
|
+
zoneind = 0;
|
43
|
+
coordind = 1;
|
44
|
+
} else if (sa[2].size() > 0 && isalpha(sa[2][sa[2].size() - 1])) {
|
45
|
+
zoneind = 2;
|
46
|
+
coordind = 0;
|
47
|
+
} else
|
48
|
+
throw GeographicErr("Neither " + sa[0] + " nor " + sa[2]
|
49
|
+
+ " of the form UTM/UPS Zone + Hemisphere"
|
50
|
+
+ " (ex: 38n, 09s, n)");
|
51
|
+
UTMUPS::DecodeZone(sa[zoneind], _zone, _northp);
|
52
|
+
for (unsigned i = 0; i < 2; ++i)
|
53
|
+
(i ? _northing : _easting) = Utility::num<real>(sa[coordind + i]);
|
54
|
+
UTMUPS::Reverse(_zone, _northp, _easting, _northing,
|
55
|
+
_lat, _long, _gamma, _k);
|
56
|
+
FixHemisphere();
|
57
|
+
} else
|
58
|
+
throw GeographicErr("Coordinate requires 1, 2, or 3 elements");
|
59
|
+
CopyToAlt();
|
60
|
+
}
|
61
|
+
|
62
|
+
string GeoCoords::GeoRepresentation(int prec, bool longfirst) const {
|
63
|
+
prec = max(0, min(9 + Math::extra_digits(), prec) + 5);
|
64
|
+
ostringstream os;
|
65
|
+
os << fixed << setprecision(prec);
|
66
|
+
real a = longfirst ? _long : _lat;
|
67
|
+
real b = longfirst ? _lat : _long;
|
68
|
+
if (!Math::isnan(a))
|
69
|
+
os << a;
|
70
|
+
else
|
71
|
+
os << "nan";
|
72
|
+
os << " ";
|
73
|
+
if (!Math::isnan(b))
|
74
|
+
os << b;
|
75
|
+
else
|
76
|
+
os << "nan";
|
77
|
+
return os.str();
|
78
|
+
}
|
79
|
+
|
80
|
+
string GeoCoords::DMSRepresentation(int prec, bool longfirst,
|
81
|
+
char dmssep) const {
|
82
|
+
prec = max(0, min(10 + Math::extra_digits(), prec) + 5);
|
83
|
+
return DMS::Encode(longfirst ? _long : _lat, unsigned(prec),
|
84
|
+
longfirst ? DMS::LONGITUDE : DMS::LATITUDE, dmssep) +
|
85
|
+
" " + DMS::Encode(longfirst ? _lat : _long, unsigned(prec),
|
86
|
+
longfirst ? DMS::LATITUDE : DMS::LONGITUDE, dmssep);
|
87
|
+
}
|
88
|
+
|
89
|
+
string GeoCoords::MGRSRepresentation(int prec) const {
|
90
|
+
// Max precision is um
|
91
|
+
prec = max(-1, min(6, prec) + 5);
|
92
|
+
string mgrs;
|
93
|
+
MGRS::Forward(_zone, _northp, _easting, _northing, _lat, prec, mgrs);
|
94
|
+
return mgrs;
|
95
|
+
}
|
96
|
+
|
97
|
+
string GeoCoords::AltMGRSRepresentation(int prec) const {
|
98
|
+
// Max precision is um
|
99
|
+
prec = max(-1, min(6, prec) + 5);
|
100
|
+
string mgrs;
|
101
|
+
MGRS::Forward(_alt_zone, _northp, _alt_easting, _alt_northing, _lat, prec,
|
102
|
+
mgrs);
|
103
|
+
return mgrs;
|
104
|
+
}
|
105
|
+
|
106
|
+
void GeoCoords::UTMUPSString(int zone, bool northp,
|
107
|
+
real easting, real northing, int prec,
|
108
|
+
bool abbrev, std::string& utm) {
|
109
|
+
ostringstream os;
|
110
|
+
prec = max(-5, min(9 + Math::extra_digits(), prec));
|
111
|
+
real scale = prec < 0 ? pow(real(10), -prec) : real(1);
|
112
|
+
os << UTMUPS::EncodeZone(zone, northp, abbrev) << fixed << setfill('0');
|
113
|
+
if (Math::isfinite(easting)) {
|
114
|
+
os << " " << Utility::str(easting / scale, max(0, prec));
|
115
|
+
if (prec < 0 && abs(easting / scale) > real(0.5))
|
116
|
+
os << setw(-prec) << 0;
|
117
|
+
} else
|
118
|
+
os << " nan";
|
119
|
+
if (Math::isfinite(northing)) {
|
120
|
+
os << " " << Utility::str(northing / scale, max(0, prec));
|
121
|
+
if (prec < 0 && abs(northing / scale) > real(0.5))
|
122
|
+
os << setw(-prec) << 0;
|
123
|
+
} else
|
124
|
+
os << " nan";
|
125
|
+
utm = os.str();
|
126
|
+
}
|
127
|
+
|
128
|
+
string GeoCoords::UTMUPSRepresentation(int prec, bool abbrev) const {
|
129
|
+
string utm;
|
130
|
+
UTMUPSString(_zone, _northp, _easting, _northing, prec, abbrev, utm);
|
131
|
+
return utm;
|
132
|
+
}
|
133
|
+
|
134
|
+
string GeoCoords::UTMUPSRepresentation(bool northp, int prec, bool abbrev)
|
135
|
+
const {
|
136
|
+
real e, n;
|
137
|
+
int z;
|
138
|
+
UTMUPS::Transfer(_zone, _northp, _easting, _northing,
|
139
|
+
_zone, northp, e, n, z);
|
140
|
+
string utm;
|
141
|
+
UTMUPSString(_zone, northp, e, n, prec, abbrev, utm);
|
142
|
+
return utm;
|
143
|
+
}
|
144
|
+
|
145
|
+
string GeoCoords::AltUTMUPSRepresentation(int prec, bool abbrev) const {
|
146
|
+
string utm;
|
147
|
+
UTMUPSString(_alt_zone, _northp, _alt_easting, _alt_northing, prec,
|
148
|
+
abbrev, utm);
|
149
|
+
return utm;
|
150
|
+
}
|
151
|
+
|
152
|
+
string GeoCoords::AltUTMUPSRepresentation(bool northp, int prec, bool abbrev)
|
153
|
+
const {
|
154
|
+
real e, n;
|
155
|
+
int z;
|
156
|
+
UTMUPS::Transfer(_alt_zone, _northp, _alt_easting, _alt_northing,
|
157
|
+
_alt_zone, northp, e, n, z);
|
158
|
+
string utm;
|
159
|
+
UTMUPSString(_alt_zone, northp, e, n, prec, abbrev, utm);
|
160
|
+
return utm;
|
161
|
+
}
|
162
|
+
|
163
|
+
void GeoCoords::FixHemisphere() {
|
164
|
+
if (_lat == 0 || (_northp && _lat >= 0) || (!_northp && _lat < 0) ||
|
165
|
+
Math::isnan(_lat))
|
166
|
+
// Allow either hemisphere for equator
|
167
|
+
return;
|
168
|
+
if (_zone != UTMUPS::UPS) {
|
169
|
+
_northing += (_northp ? 1 : -1) * UTMUPS::UTMShift();
|
170
|
+
_northp = !_northp;
|
171
|
+
} else
|
172
|
+
throw GeographicErr("Hemisphere mixup");
|
173
|
+
}
|
174
|
+
|
175
|
+
} // namespace GeographicLib
|
@@ -0,0 +1,172 @@
|
|
1
|
+
/**
|
2
|
+
* \file Geocentric.cpp
|
3
|
+
* \brief Implementation for GeographicLib::Geocentric class
|
4
|
+
*
|
5
|
+
* Copyright (c) Charles Karney (2008-2015) <charles@karney.com> and licensed
|
6
|
+
* under the MIT/X11 License. For more information, see
|
7
|
+
* http://geographiclib.sourceforge.net/
|
8
|
+
**********************************************************************/
|
9
|
+
|
10
|
+
#include <GeographicLib/Geocentric.hpp>
|
11
|
+
|
12
|
+
namespace GeographicLib {
|
13
|
+
|
14
|
+
using namespace std;
|
15
|
+
|
16
|
+
Geocentric::Geocentric(real a, real f)
|
17
|
+
: _a(a)
|
18
|
+
, _f(f)
|
19
|
+
, _e2(_f * (2 - _f))
|
20
|
+
, _e2m(Math::sq(1 - _f)) // 1 - _e2
|
21
|
+
, _e2a(abs(_e2))
|
22
|
+
, _e4a(Math::sq(_e2))
|
23
|
+
, _maxrad(2 * _a / numeric_limits<real>::epsilon())
|
24
|
+
{
|
25
|
+
if (!(Math::isfinite(_a) && _a > 0))
|
26
|
+
throw GeographicErr("Major radius is not positive");
|
27
|
+
if (!(Math::isfinite(_f) && _f < 1))
|
28
|
+
throw GeographicErr("Minor radius is not positive");
|
29
|
+
}
|
30
|
+
|
31
|
+
const Geocentric& Geocentric::WGS84() {
|
32
|
+
static const Geocentric wgs84(Constants::WGS84_a(), Constants::WGS84_f());
|
33
|
+
return wgs84;
|
34
|
+
}
|
35
|
+
|
36
|
+
void Geocentric::IntForward(real lat, real lon, real h,
|
37
|
+
real& X, real& Y, real& Z,
|
38
|
+
real M[dim2_]) const {
|
39
|
+
real sphi, cphi, slam, clam;
|
40
|
+
Math::sincosd(Math::LatFix(lat), sphi, cphi);
|
41
|
+
Math::sincosd(lon, slam, clam);
|
42
|
+
real n = _a/sqrt(1 - _e2 * Math::sq(sphi));
|
43
|
+
Z = (_e2m * n + h) * sphi;
|
44
|
+
X = (n + h) * cphi;
|
45
|
+
Y = X * slam;
|
46
|
+
X *= clam;
|
47
|
+
if (M)
|
48
|
+
Rotation(sphi, cphi, slam, clam, M);
|
49
|
+
}
|
50
|
+
|
51
|
+
void Geocentric::IntReverse(real X, real Y, real Z,
|
52
|
+
real& lat, real& lon, real& h,
|
53
|
+
real M[dim2_]) const {
|
54
|
+
real
|
55
|
+
R = Math::hypot(X, Y),
|
56
|
+
slam = R ? Y / R : 0,
|
57
|
+
clam = R ? X / R : 1;
|
58
|
+
h = Math::hypot(R, Z); // Distance to center of earth
|
59
|
+
real sphi, cphi;
|
60
|
+
if (h > _maxrad) {
|
61
|
+
// We really far away (> 12 million light years); treat the earth as a
|
62
|
+
// point and h, above, is an acceptable approximation to the height.
|
63
|
+
// This avoids overflow, e.g., in the computation of disc below. It's
|
64
|
+
// possible that h has overflowed to inf; but that's OK.
|
65
|
+
//
|
66
|
+
// Treat the case X, Y finite, but R overflows to +inf by scaling by 2.
|
67
|
+
R = Math::hypot(X/2, Y/2);
|
68
|
+
slam = R ? (Y/2) / R : 0;
|
69
|
+
clam = R ? (X/2) / R : 1;
|
70
|
+
real H = Math::hypot(Z/2, R);
|
71
|
+
sphi = (Z/2) / H;
|
72
|
+
cphi = R / H;
|
73
|
+
} else if (_e4a == 0) {
|
74
|
+
// Treat the spherical case. Dealing with underflow in the general case
|
75
|
+
// with _e2 = 0 is difficult. Origin maps to N pole same as with
|
76
|
+
// ellipsoid.
|
77
|
+
real H = Math::hypot(h == 0 ? 1 : Z, R);
|
78
|
+
sphi = (h == 0 ? 1 : Z) / H;
|
79
|
+
cphi = R / H;
|
80
|
+
h -= _a;
|
81
|
+
} else {
|
82
|
+
// Treat prolate spheroids by swapping R and Z here and by switching
|
83
|
+
// the arguments to phi = atan2(...) at the end.
|
84
|
+
real
|
85
|
+
p = Math::sq(R / _a),
|
86
|
+
q = _e2m * Math::sq(Z / _a),
|
87
|
+
r = (p + q - _e4a) / 6;
|
88
|
+
if (_f < 0) swap(p, q);
|
89
|
+
if ( !(_e4a * q == 0 && r <= 0) ) {
|
90
|
+
real
|
91
|
+
// Avoid possible division by zero when r = 0 by multiplying
|
92
|
+
// equations for s and t by r^3 and r, resp.
|
93
|
+
S = _e4a * p * q / 4, // S = r^3 * s
|
94
|
+
r2 = Math::sq(r),
|
95
|
+
r3 = r * r2,
|
96
|
+
disc = S * (2 * r3 + S);
|
97
|
+
real u = r;
|
98
|
+
if (disc >= 0) {
|
99
|
+
real T3 = S + r3;
|
100
|
+
// Pick the sign on the sqrt to maximize abs(T3). This minimizes
|
101
|
+
// loss of precision due to cancellation. The result is unchanged
|
102
|
+
// because of the way the T is used in definition of u.
|
103
|
+
T3 += T3 < 0 ? -sqrt(disc) : sqrt(disc); // T3 = (r * t)^3
|
104
|
+
// N.B. cbrt always returns the real root. cbrt(-8) = -2.
|
105
|
+
real T = Math::cbrt(T3); // T = r * t
|
106
|
+
// T can be zero; but then r2 / T -> 0.
|
107
|
+
u += T + (T ? r2 / T : 0);
|
108
|
+
} else {
|
109
|
+
// T is complex, but the way u is defined the result is real.
|
110
|
+
real ang = atan2(sqrt(-disc), -(S + r3));
|
111
|
+
// There are three possible cube roots. We choose the root which
|
112
|
+
// avoids cancellation. Note that disc < 0 implies that r < 0.
|
113
|
+
u += 2 * r * cos(ang / 3);
|
114
|
+
}
|
115
|
+
real
|
116
|
+
v = sqrt(Math::sq(u) + _e4a * q), // guaranteed positive
|
117
|
+
// Avoid loss of accuracy when u < 0. Underflow doesn't occur in
|
118
|
+
// e4 * q / (v - u) because u ~ e^4 when q is small and u < 0.
|
119
|
+
uv = u < 0 ? _e4a * q / (v - u) : u + v, // u+v, guaranteed positive
|
120
|
+
// Need to guard against w going negative due to roundoff in uv - q.
|
121
|
+
w = max(real(0), _e2a * (uv - q) / (2 * v)),
|
122
|
+
// Rearrange expression for k to avoid loss of accuracy due to
|
123
|
+
// subtraction. Division by 0 not possible because uv > 0, w >= 0.
|
124
|
+
k = uv / (sqrt(uv + Math::sq(w)) + w),
|
125
|
+
k1 = _f >= 0 ? k : k - _e2,
|
126
|
+
k2 = _f >= 0 ? k + _e2 : k,
|
127
|
+
d = k1 * R / k2,
|
128
|
+
H = Math::hypot(Z/k1, R/k2);
|
129
|
+
sphi = (Z/k1) / H;
|
130
|
+
cphi = (R/k2) / H;
|
131
|
+
h = (1 - _e2m/k1) * Math::hypot(d, Z);
|
132
|
+
} else { // e4 * q == 0 && r <= 0
|
133
|
+
// This leads to k = 0 (oblate, equatorial plane) and k + e^2 = 0
|
134
|
+
// (prolate, rotation axis) and the generation of 0/0 in the general
|
135
|
+
// formulas for phi and h. using the general formula and division by 0
|
136
|
+
// in formula for h. So handle this case by taking the limits:
|
137
|
+
// f > 0: z -> 0, k -> e2 * sqrt(q)/sqrt(e4 - p)
|
138
|
+
// f < 0: R -> 0, k + e2 -> - e2 * sqrt(q)/sqrt(e4 - p)
|
139
|
+
real
|
140
|
+
zz = sqrt((_f >= 0 ? _e4a - p : p) / _e2m),
|
141
|
+
xx = sqrt( _f < 0 ? _e4a - p : p ),
|
142
|
+
H = Math::hypot(zz, xx);
|
143
|
+
sphi = zz / H;
|
144
|
+
cphi = xx / H;
|
145
|
+
if (Z < 0) sphi = -sphi; // for tiny negative Z (not for prolate)
|
146
|
+
h = - _a * (_f >= 0 ? _e2m : 1) * H / _e2a;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
lat = Math::atan2d(sphi, cphi);
|
150
|
+
lon = Math::atan2d(slam, clam);
|
151
|
+
if (M)
|
152
|
+
Rotation(sphi, cphi, slam, clam, M);
|
153
|
+
}
|
154
|
+
|
155
|
+
void Geocentric::Rotation(real sphi, real cphi, real slam, real clam,
|
156
|
+
real M[dim2_]) {
|
157
|
+
// This rotation matrix is given by the following quaternion operations
|
158
|
+
// qrot(lam, [0,0,1]) * qrot(phi, [0,-1,0]) * [1,1,1,1]/2
|
159
|
+
// or
|
160
|
+
// qrot(pi/2 + lam, [0,0,1]) * qrot(-pi/2 + phi , [-1,0,0])
|
161
|
+
// where
|
162
|
+
// qrot(t,v) = [cos(t/2), sin(t/2)*v[1], sin(t/2)*v[2], sin(t/2)*v[3]]
|
163
|
+
|
164
|
+
// Local X axis (east) in geocentric coords
|
165
|
+
M[0] = -slam; M[3] = clam; M[6] = 0;
|
166
|
+
// Local Y axis (north) in geocentric coords
|
167
|
+
M[1] = -clam * sphi; M[4] = -slam * sphi; M[7] = cphi;
|
168
|
+
// Local Z axis (up) in geocentric coords
|
169
|
+
M[2] = clam * cphi; M[5] = slam * cphi; M[8] = sphi;
|
170
|
+
}
|
171
|
+
|
172
|
+
} // namespace GeographicLib
|