geographiclib 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/AUTHORS +12 -0
  3. data/LICENSE +24 -0
  4. data/ext/geographiclib/Accumulator.cpp +23 -0
  5. data/ext/geographiclib/AlbersEqualArea.cpp +445 -0
  6. data/ext/geographiclib/AzimuthalEquidistant.cpp +41 -0
  7. data/ext/geographiclib/CassiniSoldner.cpp +89 -0
  8. data/ext/geographiclib/CircularEngine.cpp +96 -0
  9. data/ext/geographiclib/DMS.cpp +381 -0
  10. data/ext/geographiclib/Ellipsoid.cpp +125 -0
  11. data/ext/geographiclib/EllipticFunction.cpp +512 -0
  12. data/ext/geographiclib/GARS.cpp +122 -0
  13. data/ext/geographiclib/GeoCoords.cpp +175 -0
  14. data/ext/geographiclib/Geocentric.cpp +172 -0
  15. data/ext/geographiclib/Geodesic.cpp +1908 -0
  16. data/ext/geographiclib/GeodesicExact.cpp +927 -0
  17. data/ext/geographiclib/GeodesicExactC4.cpp +7879 -0
  18. data/ext/geographiclib/GeodesicLine.cpp +321 -0
  19. data/ext/geographiclib/GeodesicLineExact.cpp +289 -0
  20. data/ext/geographiclib/GeographicLib/Accumulator.hpp +184 -0
  21. data/ext/geographiclib/GeographicLib/AlbersEqualArea.hpp +312 -0
  22. data/ext/geographiclib/GeographicLib/AzimuthalEquidistant.hpp +139 -0
  23. data/ext/geographiclib/GeographicLib/CassiniSoldner.hpp +204 -0
  24. data/ext/geographiclib/GeographicLib/CircularEngine.hpp +195 -0
  25. data/ext/geographiclib/GeographicLib/Config.h +12 -0
  26. data/ext/geographiclib/GeographicLib/Constants.hpp +387 -0
  27. data/ext/geographiclib/GeographicLib/DMS.hpp +370 -0
  28. data/ext/geographiclib/GeographicLib/Ellipsoid.hpp +534 -0
  29. data/ext/geographiclib/GeographicLib/EllipticFunction.hpp +692 -0
  30. data/ext/geographiclib/GeographicLib/GARS.hpp +143 -0
  31. data/ext/geographiclib/GeographicLib/GeoCoords.hpp +544 -0
  32. data/ext/geographiclib/GeographicLib/Geocentric.hpp +267 -0
  33. data/ext/geographiclib/GeographicLib/Geodesic.hpp +970 -0
  34. data/ext/geographiclib/GeographicLib/GeodesicExact.hpp +862 -0
  35. data/ext/geographiclib/GeographicLib/GeodesicLine.hpp +701 -0
  36. data/ext/geographiclib/GeographicLib/GeodesicLineExact.hpp +667 -0
  37. data/ext/geographiclib/GeographicLib/Geohash.hpp +180 -0
  38. data/ext/geographiclib/GeographicLib/Geoid.hpp +472 -0
  39. data/ext/geographiclib/GeographicLib/Georef.hpp +160 -0
  40. data/ext/geographiclib/GeographicLib/Gnomonic.hpp +206 -0
  41. data/ext/geographiclib/GeographicLib/GravityCircle.hpp +301 -0
  42. data/ext/geographiclib/GeographicLib/GravityModel.hpp +520 -0
  43. data/ext/geographiclib/GeographicLib/LambertConformalConic.hpp +313 -0
  44. data/ext/geographiclib/GeographicLib/LocalCartesian.hpp +236 -0
  45. data/ext/geographiclib/GeographicLib/MGRS.hpp +355 -0
  46. data/ext/geographiclib/GeographicLib/MagneticCircle.hpp +178 -0
  47. data/ext/geographiclib/GeographicLib/MagneticModel.hpp +347 -0
  48. data/ext/geographiclib/GeographicLib/Math.hpp +920 -0
  49. data/ext/geographiclib/GeographicLib/NormalGravity.hpp +350 -0
  50. data/ext/geographiclib/GeographicLib/OSGB.hpp +249 -0
  51. data/ext/geographiclib/GeographicLib/PolarStereographic.hpp +150 -0
  52. data/ext/geographiclib/GeographicLib/PolygonArea.hpp +288 -0
  53. data/ext/geographiclib/GeographicLib/Rhumb.hpp +589 -0
  54. data/ext/geographiclib/GeographicLib/SphericalEngine.hpp +376 -0
  55. data/ext/geographiclib/GeographicLib/SphericalHarmonic.hpp +354 -0
  56. data/ext/geographiclib/GeographicLib/SphericalHarmonic1.hpp +281 -0
  57. data/ext/geographiclib/GeographicLib/SphericalHarmonic2.hpp +315 -0
  58. data/ext/geographiclib/GeographicLib/TransverseMercator.hpp +196 -0
  59. data/ext/geographiclib/GeographicLib/TransverseMercatorExact.hpp +254 -0
  60. data/ext/geographiclib/GeographicLib/UTMUPS.hpp +421 -0
  61. data/ext/geographiclib/GeographicLib/Utility.hpp +612 -0
  62. data/ext/geographiclib/Geohash.cpp +102 -0
  63. data/ext/geographiclib/Geoid.cpp +509 -0
  64. data/ext/geographiclib/Georef.cpp +135 -0
  65. data/ext/geographiclib/Gnomonic.cpp +85 -0
  66. data/ext/geographiclib/GravityCircle.cpp +129 -0
  67. data/ext/geographiclib/GravityModel.cpp +360 -0
  68. data/ext/geographiclib/LambertConformalConic.cpp +456 -0
  69. data/ext/geographiclib/LocalCartesian.cpp +62 -0
  70. data/ext/geographiclib/MGRS.cpp +461 -0
  71. data/ext/geographiclib/MagneticCircle.cpp +52 -0
  72. data/ext/geographiclib/MagneticModel.cpp +269 -0
  73. data/ext/geographiclib/Math.cpp +63 -0
  74. data/ext/geographiclib/NormalGravity.cpp +262 -0
  75. data/ext/geographiclib/OSGB.cpp +167 -0
  76. data/ext/geographiclib/PolarStereographic.cpp +108 -0
  77. data/ext/geographiclib/PolygonArea.cpp +204 -0
  78. data/ext/geographiclib/Rhumb.cpp +383 -0
  79. data/ext/geographiclib/SphericalEngine.cpp +477 -0
  80. data/ext/geographiclib/TransverseMercator.cpp +603 -0
  81. data/ext/geographiclib/TransverseMercatorExact.cpp +464 -0
  82. data/ext/geographiclib/UTMUPS.cpp +296 -0
  83. data/ext/geographiclib/Utility.cpp +61 -0
  84. data/ext/geographiclib/extconf.rb +3 -0
  85. data/ext/geographiclib/geographiclib.cpp +62 -0
  86. data/lib/geographiclib.rb +20 -0
  87. metadata +140 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * \file LocalCartesian.cpp
3
+ * \brief Implementation for GeographicLib::LocalCartesian 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/LocalCartesian.hpp>
11
+
12
+ namespace GeographicLib {
13
+
14
+ using namespace std;
15
+
16
+ void LocalCartesian::Reset(real lat0, real lon0, real h0) {
17
+ _lat0 = Math::LatFix(lat0);
18
+ _lon0 = Math::AngNormalize(lon0);
19
+ _h0 = h0;
20
+ _earth.Forward(_lat0, _lon0, _h0, _x0, _y0, _z0);
21
+ real sphi, cphi, slam, clam;
22
+ Math::sincosd(_lat0, sphi, cphi);
23
+ Math::sincosd(_lon0, slam, clam);
24
+ Geocentric::Rotation(sphi, cphi, slam, clam, _r);
25
+ }
26
+
27
+ void LocalCartesian::MatrixMultiply(real M[dim2_]) const {
28
+ // M = r' . M
29
+ real t[dim2_];
30
+ copy(M, M + dim2_, t);
31
+ for (size_t i = 0; i < dim2_; ++i) {
32
+ size_t row = i / dim_, col = i % dim_;
33
+ M[i] = _r[row] * t[col] + _r[row+3] * t[col+3] + _r[row+6] * t[col+6];
34
+ }
35
+ }
36
+
37
+ void LocalCartesian::IntForward(real lat, real lon, real h,
38
+ real& x, real& y, real& z,
39
+ real M[dim2_]) const {
40
+ real xc, yc, zc;
41
+ _earth.IntForward(lat, lon, h, xc, yc, zc, M);
42
+ xc -= _x0; yc -= _y0; zc -= _z0;
43
+ x = _r[0] * xc + _r[3] * yc + _r[6] * zc;
44
+ y = _r[1] * xc + _r[4] * yc + _r[7] * zc;
45
+ z = _r[2] * xc + _r[5] * yc + _r[8] * zc;
46
+ if (M)
47
+ MatrixMultiply(M);
48
+ }
49
+
50
+ void LocalCartesian::IntReverse(real x, real y, real z,
51
+ real& lat, real& lon, real& h,
52
+ real M[dim2_]) const {
53
+ real
54
+ xc = _x0 + _r[0] * x + _r[1] * y + _r[2] * z,
55
+ yc = _y0 + _r[3] * x + _r[4] * y + _r[5] * z,
56
+ zc = _z0 + _r[6] * x + _r[7] * y + _r[8] * z;
57
+ _earth.IntReverse(xc, yc, zc, lat, lon, h, M);
58
+ if (M)
59
+ MatrixMultiply(M);
60
+ }
61
+
62
+ } // namespace GeographicLib
@@ -0,0 +1,461 @@
1
+ /**
2
+ * \file MGRS.cpp
3
+ * \brief Implementation for GeographicLib::MGRS 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/MGRS.hpp>
11
+ #include <GeographicLib/Utility.hpp>
12
+
13
+ namespace GeographicLib {
14
+
15
+ using namespace std;
16
+
17
+ const string MGRS::hemispheres_ = "SN";
18
+ const string MGRS::utmcols_[3] = { "ABCDEFGH", "JKLMNPQR", "STUVWXYZ" };
19
+ const string MGRS::utmrow_ = "ABCDEFGHJKLMNPQRSTUV";
20
+ const string MGRS::upscols_[4] =
21
+ { "JKLPQRSTUXYZ", "ABCFGHJKLPQR", "RSTUXYZ", "ABCFGHJ" };
22
+ const string MGRS::upsrows_[2] =
23
+ { "ABCDEFGHJKLMNPQRSTUVWXYZ", "ABCDEFGHJKLMNP" };
24
+ const string MGRS::latband_ = "CDEFGHJKLMNPQRSTUVWX";
25
+ const string MGRS::upsband_ = "ABYZ";
26
+ const string MGRS::digits_ = "0123456789";
27
+
28
+ const int MGRS::mineasting_[4] =
29
+ { minupsSind_, minupsNind_, minutmcol_, minutmcol_ };
30
+ const int MGRS::maxeasting_[4] =
31
+ { maxupsSind_, maxupsNind_, maxutmcol_, maxutmcol_ };
32
+ const int MGRS::minnorthing_[4] =
33
+ { minupsSind_, minupsNind_,
34
+ minutmSrow_, minutmSrow_ - (maxutmSrow_ - minutmNrow_) };
35
+ const int MGRS::maxnorthing_[4] =
36
+ { maxupsSind_, maxupsNind_,
37
+ maxutmNrow_ + (maxutmSrow_ - minutmNrow_), maxutmNrow_ };
38
+
39
+ void MGRS::Forward(int zone, bool northp, real x, real y, real lat,
40
+ int prec, std::string& mgrs) {
41
+ // The smallest angle s.t., 90 - angeps() < 90 (approx 50e-12 arcsec)
42
+ // 7 = ceil(log_2(90))
43
+ static const real angeps = pow(real(0.5), Math::digits() - 7);
44
+ if (zone == UTMUPS::INVALID ||
45
+ Math::isnan(x) || Math::isnan(y) || Math::isnan(lat)) {
46
+ mgrs = "INVALID";
47
+ return;
48
+ }
49
+ bool utmp = zone != 0;
50
+ CheckCoords(utmp, northp, x, y);
51
+ if (!(zone >= UTMUPS::MINZONE && zone <= UTMUPS::MAXZONE))
52
+ throw GeographicErr("Zone " + Utility::str(zone) + " not in [0,60]");
53
+ if (!(prec >= -1 && prec <= maxprec_))
54
+ throw GeographicErr("MGRS precision " + Utility::str(prec)
55
+ + " not in [-1, "
56
+ + Utility::str(int(maxprec_)) + "]");
57
+ // Fixed char array for accumulating string. Allow space for zone, 3 block
58
+ // letters, easting + northing. Don't need to allow for terminating null.
59
+ char mgrs1[2 + 3 + 2 * maxprec_];
60
+ int
61
+ zone1 = zone - 1,
62
+ z = utmp ? 2 : 0,
63
+ mlen = z + 3 + 2 * prec;
64
+ if (utmp) {
65
+ mgrs1[0] = digits_[ zone / base_ ];
66
+ mgrs1[1] = digits_[ zone % base_ ];
67
+ // This isn't necessary...! Keep y non-neg
68
+ // if (!northp) y -= maxutmSrow_ * tile_;
69
+ }
70
+ // The C++ standard mandates 64 bits for long long. But
71
+ // check, to make sure.
72
+ GEOGRAPHICLIB_STATIC_ASSERT(numeric_limits<long long>::digits >= 44,
73
+ "long long not wide enough to store 10e12");
74
+ long long
75
+ ix = (long long)(floor(x * mult_)),
76
+ iy = (long long)(floor(y * mult_)),
77
+ m = (long long)(mult_) * (long long)(tile_);
78
+ int xh = int(ix / m), yh = int(iy / m);
79
+ if (utmp) {
80
+ int
81
+ // Correct fuzziness in latitude near equator
82
+ iband = abs(lat) > angeps ? LatitudeBand(lat) : (northp ? 0 : -1),
83
+ icol = xh - minutmcol_,
84
+ irow = UTMRow(iband, icol, yh % utmrowperiod_);
85
+ if (irow != yh - (northp ? minutmNrow_ : maxutmSrow_))
86
+ throw GeographicErr("Latitude " + Utility::str(lat)
87
+ + " is inconsistent with UTM coordinates");
88
+ mgrs1[z++] = latband_[10 + iband];
89
+ mgrs1[z++] = utmcols_[zone1 % 3][icol];
90
+ mgrs1[z++] = utmrow_[(yh + (zone1 & 1 ? utmevenrowshift_ : 0))
91
+ % utmrowperiod_];
92
+ } else {
93
+ bool eastp = xh >= upseasting_;
94
+ int iband = (northp ? 2 : 0) + (eastp ? 1 : 0);
95
+ mgrs1[z++] = upsband_[iband];
96
+ mgrs1[z++] = upscols_[iband][xh - (eastp ? upseasting_ :
97
+ (northp ? minupsNind_ : minupsSind_))];
98
+ mgrs1[z++] = upsrows_[northp][yh - (northp ? minupsNind_ : minupsSind_)];
99
+ }
100
+ if (prec > 0) {
101
+ ix -= m * xh; iy -= m * yh;
102
+ long long d = (long long)(pow(real(base_), maxprec_ - prec));
103
+ ix /= d; iy /= d;
104
+ for (int c = prec; c--;) {
105
+ mgrs1[z + c ] = digits_[ix % base_]; ix /= base_;
106
+ mgrs1[z + c + prec] = digits_[iy % base_]; iy /= base_;
107
+ }
108
+ }
109
+ mgrs.resize(mlen);
110
+ copy(mgrs1, mgrs1 + mlen, mgrs.begin());
111
+ }
112
+
113
+ void MGRS::Forward(int zone, bool northp, real x, real y,
114
+ int prec, std::string& mgrs) {
115
+ real lat, lon;
116
+ if (zone > 0) {
117
+ // Does a rough estimate for latitude determine the latitude band?
118
+ real ys = northp ? y : y - utmNshift_;
119
+ // A cheap calculation of the latitude which results in an "allowed"
120
+ // latitude band would be
121
+ // lat = ApproxLatitudeBand(ys) * 8 + 4;
122
+ //
123
+ // Here we do a more careful job using the band letter corresponding to
124
+ // the actual latitude.
125
+ ys /= tile_;
126
+ if (abs(ys) < 1)
127
+ lat = 0.9 * ys; // accurate enough estimate near equator
128
+ else {
129
+ real
130
+ // The poleward bound is a fit from above of lat(x,y)
131
+ // for x = 500km and y = [0km, 950km]
132
+ latp = real(0.901) * ys + (ys > 0 ? 1 : -1) * real(0.135),
133
+ // The equatorward bound is a fit from below of lat(x,y)
134
+ // for x = 900km and y = [0km, 950km]
135
+ late = real(0.902) * ys * (1 - real(1.85e-6) * ys * ys);
136
+ if (LatitudeBand(latp) == LatitudeBand(late))
137
+ lat = latp;
138
+ else
139
+ // bounds straddle a band boundary so need to compute lat accurately
140
+ UTMUPS::Reverse(zone, northp, x, y, lat, lon);
141
+ }
142
+ } else
143
+ // Latitude isn't needed for UPS specs or for INVALID
144
+ lat = 0;
145
+ Forward(zone, northp, x, y, lat, prec, mgrs);
146
+ }
147
+
148
+ void MGRS::Reverse(const std::string& mgrs,
149
+ int& zone, bool& northp, real& x, real& y,
150
+ int& prec, bool centerp) {
151
+ int
152
+ p = 0,
153
+ len = int(mgrs.length());
154
+ if (len >= 3 &&
155
+ toupper(mgrs[0]) == 'I' &&
156
+ toupper(mgrs[1]) == 'N' &&
157
+ toupper(mgrs[2]) == 'V') {
158
+ zone = UTMUPS::INVALID;
159
+ northp = false;
160
+ x = y = Math::NaN();
161
+ prec = -2;
162
+ return;
163
+ }
164
+ int zone1 = 0;
165
+ while (p < len) {
166
+ int i = Utility::lookup(digits_, mgrs[p]);
167
+ if (i < 0)
168
+ break;
169
+ zone1 = 10 * zone1 + i;
170
+ ++p;
171
+ }
172
+ if (p > 0 && !(zone1 >= UTMUPS::MINUTMZONE && zone1 <= UTMUPS::MAXUTMZONE))
173
+ throw GeographicErr("Zone " + Utility::str(zone1) + " not in [1,60]");
174
+ if (p > 2)
175
+ throw GeographicErr("More than 2 digits at start of MGRS "
176
+ + mgrs.substr(0, p));
177
+ if (len - p < 1)
178
+ throw GeographicErr("MGRS string too short " + mgrs);
179
+ bool utmp = zone1 != UTMUPS::UPS;
180
+ int zonem1 = zone1 - 1;
181
+ const string& band = utmp ? latband_ : upsband_;
182
+ int iband = Utility::lookup(band, mgrs[p++]);
183
+ if (iband < 0)
184
+ throw GeographicErr("Band letter " + Utility::str(mgrs[p-1]) + " not in "
185
+ + (utmp ? "UTM" : "UPS") + " set " + band);
186
+ bool northp1 = iband >= (utmp ? 10 : 2);
187
+ if (p == len) { // Grid zone only (ignore centerp)
188
+ // Approx length of a degree of meridian arc in units of tile.
189
+ real deg = real(utmNshift_) / (90 * tile_);
190
+ zone = zone1;
191
+ northp = northp1;
192
+ if (utmp) {
193
+ // Pick central meridian except for 31V
194
+ x = ((zone == 31 && iband == 17) ? 4 : 5) * tile_;
195
+ // Pick center of 8deg latitude bands
196
+ y = floor(8 * (iband - real(9.5)) * deg + real(0.5)) * tile_
197
+ + (northp ? 0 : utmNshift_);
198
+ } else {
199
+ // Pick point at lat 86N or 86S
200
+ x = ((iband & 1 ? 1 : -1) * floor(4 * deg + real(0.5))
201
+ + upseasting_) * tile_;
202
+ // Pick point at lon 90E or 90W.
203
+ y = upseasting_ * tile_;
204
+ }
205
+ prec = -1;
206
+ return;
207
+ } else if (len - p < 2)
208
+ throw GeographicErr("Missing row letter in " + mgrs);
209
+ const string& col = utmp ? utmcols_[zonem1 % 3] : upscols_[iband];
210
+ const string& row = utmp ? utmrow_ : upsrows_[northp1];
211
+ int icol = Utility::lookup(col, mgrs[p++]);
212
+ if (icol < 0)
213
+ throw GeographicErr("Column letter " + Utility::str(mgrs[p-1])
214
+ + " not in "
215
+ + (utmp ? "zone " + mgrs.substr(0, p-2) :
216
+ "UPS band " + Utility::str(mgrs[p-2]))
217
+ + " set " + col );
218
+ int irow = Utility::lookup(row, mgrs[p++]);
219
+ if (irow < 0)
220
+ throw GeographicErr("Row letter " + Utility::str(mgrs[p-1]) + " not in "
221
+ + (utmp ? "UTM" :
222
+ "UPS " + Utility::str(hemispheres_[northp1]))
223
+ + " set " + row);
224
+ if (utmp) {
225
+ if (zonem1 & 1)
226
+ irow = (irow + utmrowperiod_ - utmevenrowshift_) % utmrowperiod_;
227
+ iband -= 10;
228
+ irow = UTMRow(iband, icol, irow);
229
+ if (irow == maxutmSrow_)
230
+ throw GeographicErr("Block " + mgrs.substr(p-2, 2)
231
+ + " not in zone/band " + mgrs.substr(0, p-2));
232
+
233
+ irow = northp1 ? irow : irow + 100;
234
+ icol = icol + minutmcol_;
235
+ } else {
236
+ bool eastp = iband & 1;
237
+ icol += eastp ? upseasting_ : (northp1 ? minupsNind_ : minupsSind_);
238
+ irow += northp1 ? minupsNind_ : minupsSind_;
239
+ }
240
+ int prec1 = (len - p)/2;
241
+ real
242
+ unit = 1,
243
+ x1 = icol,
244
+ y1 = irow;
245
+ for (int i = 0; i < prec1; ++i) {
246
+ unit *= base_;
247
+ int
248
+ ix = Utility::lookup(digits_, mgrs[p + i]),
249
+ iy = Utility::lookup(digits_, mgrs[p + i + prec1]);
250
+ if (ix < 0 || iy < 0)
251
+ throw GeographicErr("Encountered a non-digit in " + mgrs.substr(p));
252
+ x1 = base_ * x1 + ix;
253
+ y1 = base_ * y1 + iy;
254
+ }
255
+ if ((len - p) % 2) {
256
+ if (Utility::lookup(digits_, mgrs[len - 1]) < 0)
257
+ throw GeographicErr("Encountered a non-digit in " + mgrs.substr(p));
258
+ else
259
+ throw GeographicErr("Not an even number of digits in "
260
+ + mgrs.substr(p));
261
+ }
262
+ if (prec1 > maxprec_)
263
+ throw GeographicErr("More than " + Utility::str(2*maxprec_)
264
+ + " digits in " + mgrs.substr(p));
265
+ if (centerp) {
266
+ unit *= 2; x1 = 2 * x1 + 1; y1 = 2 * y1 + 1;
267
+ }
268
+ zone = zone1;
269
+ northp = northp1;
270
+ x = (tile_ * x1) / unit;
271
+ y = (tile_ * y1) / unit;
272
+ prec = prec1;
273
+ }
274
+
275
+ void MGRS::CheckCoords(bool utmp, bool& northp, real& x, real& y) {
276
+ // Limits are all multiples of 100km and are all closed on the lower end
277
+ // and open on the upper end -- and this is reflected in the error
278
+ // messages. However if a coordinate lies on the excluded upper end (e.g.,
279
+ // after rounding), it is shifted down by eps. This also folds UTM
280
+ // northings to the correct N/S hemisphere.
281
+
282
+ // The smallest length s.t., 1.0e7 - eps() < 1.0e7 (approx 1.9 nm)
283
+ // 25 = ceil(log_2(2e7)) -- use half circumference here because
284
+ // northing 195e5 is a legal in the "southern" hemisphere.
285
+ static const real eps = pow(real(0.5), Math::digits() - 25);
286
+ int
287
+ ix = int(floor(x / tile_)),
288
+ iy = int(floor(y / tile_)),
289
+ ind = (utmp ? 2 : 0) + (northp ? 1 : 0);
290
+ if (! (ix >= mineasting_[ind] && ix < maxeasting_[ind]) ) {
291
+ if (ix == maxeasting_[ind] && x == maxeasting_[ind] * tile_)
292
+ x -= eps;
293
+ else
294
+ throw GeographicErr("Easting " + Utility::str(int(floor(x/1000)))
295
+ + "km not in MGRS/"
296
+ + (utmp ? "UTM" : "UPS") + " range for "
297
+ + (northp ? "N" : "S" ) + " hemisphere ["
298
+ + Utility::str(mineasting_[ind]*tile_/1000)
299
+ + "km, "
300
+ + Utility::str(maxeasting_[ind]*tile_/1000)
301
+ + "km)");
302
+ }
303
+ if (! (iy >= minnorthing_[ind] && iy < maxnorthing_[ind]) ) {
304
+ if (iy == maxnorthing_[ind] && y == maxnorthing_[ind] * tile_)
305
+ y -= eps;
306
+ else
307
+ throw GeographicErr("Northing " + Utility::str(int(floor(y/1000)))
308
+ + "km not in MGRS/"
309
+ + (utmp ? "UTM" : "UPS") + " range for "
310
+ + (northp ? "N" : "S" ) + " hemisphere ["
311
+ + Utility::str(minnorthing_[ind]*tile_/1000)
312
+ + "km, "
313
+ + Utility::str(maxnorthing_[ind]*tile_/1000)
314
+ + "km)");
315
+ }
316
+
317
+ // Correct the UTM northing and hemisphere if necessary
318
+ if (utmp) {
319
+ if (northp && iy < minutmNrow_) {
320
+ northp = false;
321
+ y += utmNshift_;
322
+ } else if (!northp && iy >= maxutmSrow_) {
323
+ if (y == maxutmSrow_ * tile_)
324
+ // If on equator retain S hemisphere
325
+ y -= eps;
326
+ else {
327
+ northp = true;
328
+ y -= utmNshift_;
329
+ }
330
+ }
331
+ }
332
+ }
333
+
334
+ int MGRS::UTMRow(int iband, int icol, int irow) {
335
+ // Input is iband = band index in [-10, 10) (as returned by LatitudeBand),
336
+ // icol = column index in [0,8) with origin of easting = 100km, and irow =
337
+ // periodic row index in [0,20) with origin = equator. Output is true row
338
+ // index in [-90, 95). Returns maxutmSrow_ = 100, if irow and iband are
339
+ // incompatible.
340
+
341
+ // Estimate center row number for latitude band
342
+ // 90 deg = 100 tiles; 1 band = 8 deg = 100*8/90 tiles
343
+ real c = 100 * (8 * iband + 4)/real(90);
344
+ bool northp = iband >= 0;
345
+ // These are safe bounds on the rows
346
+ // iband minrow maxrow
347
+ // -10 -90 -81
348
+ // -9 -80 -72
349
+ // -8 -71 -63
350
+ // -7 -63 -54
351
+ // -6 -54 -45
352
+ // -5 -45 -36
353
+ // -4 -36 -27
354
+ // -3 -27 -18
355
+ // -2 -18 -9
356
+ // -1 -9 -1
357
+ // 0 0 8
358
+ // 1 8 17
359
+ // 2 17 26
360
+ // 3 26 35
361
+ // 4 35 44
362
+ // 5 44 53
363
+ // 6 53 62
364
+ // 7 62 70
365
+ // 8 71 79
366
+ // 9 80 94
367
+ int
368
+ minrow = iband > -10 ?
369
+ int(floor(c - real(4.3) - real(0.1) * northp)) : -90,
370
+ maxrow = iband < 9 ?
371
+ int(floor(c + real(4.4) - real(0.1) * northp)) : 94,
372
+ baserow = (minrow + maxrow) / 2 - utmrowperiod_ / 2;
373
+ // Offset irow by the multiple of utmrowperiod_ which brings it as close as
374
+ // possible to the center of the latitude band, (minrow + maxrow) / 2.
375
+ // (Add maxutmSrow_ = 5 * utmrowperiod_ to ensure operand is positive.)
376
+ irow = (irow - baserow + maxutmSrow_) % utmrowperiod_ + baserow;
377
+ if (!( irow >= minrow && irow <= maxrow )) {
378
+ // Outside the safe bounds, so need to check...
379
+ // Northing = 71e5 and 80e5 intersect band boundaries
380
+ // y = 71e5 in scol = 2 (x = [3e5,4e5] and x = [6e5,7e5])
381
+ // y = 80e5 in scol = 1 (x = [2e5,3e5] and x = [7e5,8e5])
382
+ // This holds for all the ellipsoids given in NGA.SIG.0012_2.0.0_UTMUPS.
383
+ // The following deals with these special cases.
384
+ int
385
+ // Fold [-10,-1] -> [9,0]
386
+ sband = iband >= 0 ? iband : -iband - 1,
387
+ // Fold [-90,-1] -> [89,0]
388
+ srow = irow >= 0 ? irow : -irow - 1,
389
+ // Fold [4,7] -> [3,0]
390
+ scol = icol < 4 ? icol : -icol + 7;
391
+ // For example, the safe rows for band 8 are 71 - 79. However row 70 is
392
+ // allowed if scol = [2,3] and row 80 is allowed if scol = [0,1].
393
+ if ( ! ( (srow == 70 && sband == 8 && scol >= 2) ||
394
+ (srow == 71 && sband == 7 && scol <= 2) ||
395
+ (srow == 79 && sband == 9 && scol >= 1) ||
396
+ (srow == 80 && sband == 8 && scol <= 1) ) )
397
+ irow = maxutmSrow_;
398
+ }
399
+ return irow;
400
+ }
401
+
402
+ void MGRS::Check() {
403
+ real lat, lon, x, y, t = tile_; int zone; bool northp;
404
+ UTMUPS::Reverse(31, true , 1*t, 0*t, lat, lon);
405
+ if (!( lon < 0 ))
406
+ throw GeographicErr("MGRS::Check: equator coverage failure");
407
+ UTMUPS::Reverse(31, true , 1*t, 95*t, lat, lon);
408
+ if (!( lat > 84 ))
409
+ throw GeographicErr("MGRS::Check: UTM doesn't reach latitude = 84");
410
+ UTMUPS::Reverse(31, false, 1*t, 10*t, lat, lon);
411
+ if (!( lat < -80 ))
412
+ throw GeographicErr("MGRS::Check: UTM doesn't reach latitude = -80");
413
+ UTMUPS::Forward(56, 3, zone, northp, x, y, 32);
414
+ if (!( x > 1*t ))
415
+ throw GeographicErr("MGRS::Check: Norway exception creates a gap");
416
+ UTMUPS::Forward(72, 21, zone, northp, x, y, 35);
417
+ if (!( x > 1*t ))
418
+ throw GeographicErr("MGRS::Check: Svalbard exception creates a gap");
419
+ UTMUPS::Reverse(0, true , 20*t, 13*t, lat, lon);
420
+ if (!( lat < 84 ))
421
+ throw GeographicErr("MGRS::Check: North UPS doesn't reach latitude = 84");
422
+ UTMUPS::Reverse(0, false, 20*t, 8*t, lat, lon);
423
+ if (!( lat > -80 ))
424
+ throw
425
+ GeographicErr("MGRS::Check: South UPS doesn't reach latitude = -80");
426
+ // Entries are [band, x, y] either side of the band boundaries. Units for
427
+ // x, y are t = 100km.
428
+ const short tab[] = {
429
+ 0, 5, 0, 0, 9, 0, // south edge of band 0
430
+ 0, 5, 8, 0, 9, 8, // north edge of band 0
431
+ 1, 5, 9, 1, 9, 9, // south edge of band 1
432
+ 1, 5, 17, 1, 9, 17, // north edge of band 1
433
+ 2, 5, 18, 2, 9, 18, // etc.
434
+ 2, 5, 26, 2, 9, 26,
435
+ 3, 5, 27, 3, 9, 27,
436
+ 3, 5, 35, 3, 9, 35,
437
+ 4, 5, 36, 4, 9, 36,
438
+ 4, 5, 44, 4, 9, 44,
439
+ 5, 5, 45, 5, 9, 45,
440
+ 5, 5, 53, 5, 9, 53,
441
+ 6, 5, 54, 6, 9, 54,
442
+ 6, 5, 62, 6, 9, 62,
443
+ 7, 5, 63, 7, 9, 63,
444
+ 7, 5, 70, 7, 7, 70, 7, 7, 71, 7, 9, 71, // y = 71t crosses boundary
445
+ 8, 5, 71, 8, 6, 71, 8, 6, 72, 8, 9, 72, // between bands 7 and 8.
446
+ 8, 5, 79, 8, 8, 79, 8, 8, 80, 8, 9, 80, // y = 80t crosses boundary
447
+ 9, 5, 80, 9, 7, 80, 9, 7, 81, 9, 9, 81, // between bands 8 and 9.
448
+ 9, 5, 95, 9, 9, 95, // north edge of band 9
449
+ };
450
+ const int bandchecks = sizeof(tab) / (3 * sizeof(short));
451
+ for (int i = 0; i < bandchecks; ++i) {
452
+ UTMUPS::Reverse(38, true, tab[3*i+1]*t, tab[3*i+2]*t, lat, lon);
453
+ if (!( LatitudeBand(lat) == tab[3*i+0] ))
454
+ throw GeographicErr("MGRS::Check: Band error, b = " +
455
+ Utility::str(tab[3*i+0]) + ", x = " +
456
+ Utility::str(tab[3*i+1]) + "00km, y = " +
457
+ Utility::str(tab[3*i+2]) + "00km");
458
+ }
459
+ }
460
+
461
+ } // namespace GeographicLib