1
|
/*
|
2
|
Author: Patrice G. Cappelaere (Got library from Geoserver)
|
3
|
License: LGPL as per: http://www.gnu.org/copyleft/lesser.html
|
4
|
|
5
|
$Id: MGRS.js 2546 2007-01-23 12:07:39Z gjvoosten $
|
6
|
*/
|
7
|
|
8
|
|
9
|
function MGRS() {
|
10
|
/**
|
11
|
* UTM zones are grouped, and assigned to one of a group of 6
|
12
|
* sets.
|
13
|
*/
|
14
|
var NUM_100K_SETS = 6;
|
15
|
|
16
|
/**
|
17
|
* The column letters (for easting) of the lower left value, per
|
18
|
* set.
|
19
|
*/
|
20
|
var SET_ORIGIN_COLUMN_LETTERS = new Array('A','J','S','A','J','S');
|
21
|
|
22
|
/**
|
23
|
* The row letters (for northing) of the lower left value, per
|
24
|
* set.
|
25
|
*/
|
26
|
var SET_ORIGIN_ROW_LETTERS = new Array('A','F','A','F','A','F');
|
27
|
|
28
|
/**
|
29
|
* The column letters (for easting) of the lower left value, per
|
30
|
* set,, for Bessel Ellipsoid.
|
31
|
*/
|
32
|
var BESSEL_SET_ORIGIN_COLUMN_LETTERS = new Array( 'A', 'J', 'S', 'A', 'J', 'S' );
|
33
|
|
34
|
/**
|
35
|
* The row letters (for northing) of the lower left value, per
|
36
|
* set, for Bessel Ellipsoid.
|
37
|
*/
|
38
|
var BESSEL_SET_ORIGIN_ROW_LETTERS = new Array( 'L', 'R', 'L', 'R', 'L', 'R' );
|
39
|
var SET_NORTHING_ROLLOVER = 20000000;
|
40
|
|
41
|
/**
|
42
|
* Use 5 digits for northing and easting values, for 1 meter
|
43
|
* accuracy of coordinate.
|
44
|
*/
|
45
|
|
46
|
var ACCURACY_1_METER = 5;
|
47
|
|
48
|
/**
|
49
|
* Use 4 digits for northing and easting values, for 10 meter
|
50
|
* accuracy of coordinate.
|
51
|
*/
|
52
|
var ACCURACY_10_METER = 4;
|
53
|
|
54
|
/**
|
55
|
* Use 3 digits for northing and easting values, for 100 meter
|
56
|
* accuracy of coordinate.
|
57
|
*/
|
58
|
var ACCURACY_100_METER = 3;
|
59
|
|
60
|
/**
|
61
|
* Use 2 digits for northing and easting values, for 1000 meter
|
62
|
* accuracy of coordinate.
|
63
|
*/
|
64
|
|
65
|
var ACCURACY_1000_METER = 2;
|
66
|
/**
|
67
|
* Use 1 digits for northing and easting values, for 10000 meter
|
68
|
* accuracy of coordinate.
|
69
|
*/
|
70
|
var ACCURACY_10000_METER = 1;
|
71
|
|
72
|
/** The set origin column letters to use. */
|
73
|
var originColumnLetters = SET_ORIGIN_COLUMN_LETTERS;
|
74
|
|
75
|
/** The set origin row letters to use. */
|
76
|
var originRowLetters = SET_ORIGIN_ROW_LETTERS;
|
77
|
|
78
|
var A = 65; //AsciiToNum('A');
|
79
|
var I = 73; //AsciiToNum('I');
|
80
|
var O = 79; //AsciiToNum('O');
|
81
|
var V = 86; //AsciiToNum('V');
|
82
|
var Z = 90; //AsciiToNum('Z');
|
83
|
|
84
|
var DEBUG = false;
|
85
|
|
86
|
/** The String holding the MGRS coordinate value. */
|
87
|
var mgrs_;
|
88
|
var lat_;
|
89
|
var lon_;
|
90
|
var radlat_ ;
|
91
|
var radlon_ ;
|
92
|
|
93
|
var northing_;
|
94
|
var easting_;
|
95
|
var zone_number_;
|
96
|
var zone_letter_;
|
97
|
|
98
|
/**
|
99
|
* Conversion of lat/lon to MGRS
|
100
|
*/
|
101
|
this.convert = function(latitude, longitude) {
|
102
|
lat_ = parseFloat(latitude);
|
103
|
lon_ = parseFloat(longitude);
|
104
|
radlat_ = degToRad(lat_);
|
105
|
radlon_ = degToRad(lon_);
|
106
|
|
107
|
LLtoUTM();
|
108
|
return formatMGRS();
|
109
|
}
|
110
|
|
111
|
function degToRad(deg) {
|
112
|
return (deg * (Math.PI / 180.0));
|
113
|
}
|
114
|
|
115
|
/**
|
116
|
* Converts a set of Longitude and Latitude co-ordinates to UTM
|
117
|
* given an ellipsoid
|
118
|
*
|
119
|
* @param ellip an ellipsoid definition.
|
120
|
* @param llpoint the coordinate to be converted
|
121
|
* @param utmpoint A UTMPoint instance to put the results in. If
|
122
|
* null, a new UTMPoint will be allocated.
|
123
|
* @return A UTM class instance containing the value of
|
124
|
* <code>null</code> if conversion failed. If you pass
|
125
|
* in a UTMPoint, it will be returned as well if
|
126
|
* successful.
|
127
|
*/
|
128
|
function LLtoUTM() {
|
129
|
var Lat = lat_;
|
130
|
var Long = lon_;
|
131
|
var a = 6378137.0; //ellip.radius;
|
132
|
var eccSquared = 0.00669438; //ellip.eccsq;
|
133
|
var k0 = 0.9996;
|
134
|
var LongOrigin;
|
135
|
var eccPrimeSquared;
|
136
|
var N, T, C, A, M;
|
137
|
var LatRad = radlat_;
|
138
|
var LongRad = radlon_;
|
139
|
var LongOriginRad;
|
140
|
var ZoneNumber;
|
141
|
// (int)
|
142
|
ZoneNumber = Math.floor((Long + 180) / 6) + 1;
|
143
|
|
144
|
//Make sure the longitude 180.00 is in Zone 60
|
145
|
if (Long == 180) {
|
146
|
ZoneNumber = 60;
|
147
|
}
|
148
|
|
149
|
// Special zone for Norway
|
150
|
if (Lat >= 56.0 && Lat < 64.0 && Long >= 3.0 && Long < 12.0) {
|
151
|
ZoneNumber = 32;
|
152
|
}
|
153
|
|
154
|
// Special zones for Svalbard
|
155
|
if (Lat >= 72.0 && Lat < 84.0) {
|
156
|
if (Long >= 0.0 && Long < 9.0)
|
157
|
ZoneNumber = 31;
|
158
|
else if (Long >= 9.0 && Long < 21.0)
|
159
|
ZoneNumber = 33;
|
160
|
else if (Long >= 21.0 && Long < 33.0)
|
161
|
ZoneNumber = 35;
|
162
|
else if (Long >= 33.0 && Long < 42.0)
|
163
|
ZoneNumber = 37;
|
164
|
}
|
165
|
|
166
|
LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3; //+3 puts origin
|
167
|
// in middle of
|
168
|
// zone
|
169
|
LongOriginRad = degToRad(LongOrigin);
|
170
|
|
171
|
eccPrimeSquared = (eccSquared) / (1 - eccSquared);
|
172
|
|
173
|
N = a / Math.sqrt(1 - eccSquared * Math.sin(LatRad) * Math.sin(LatRad));
|
174
|
T = Math.tan(LatRad) * Math.tan(LatRad);
|
175
|
C = eccPrimeSquared * Math.cos(LatRad) * Math.cos(LatRad);
|
176
|
A = Math.cos(LatRad) * (LongRad - LongOriginRad);
|
177
|
|
178
|
M = a
|
179
|
* ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5
|
180
|
* eccSquared * eccSquared * eccSquared / 256)
|
181
|
* LatRad
|
182
|
- (3 * eccSquared / 8 + 3 * eccSquared * eccSquared
|
183
|
/ 32 + 45 * eccSquared * eccSquared
|
184
|
* eccSquared / 1024)
|
185
|
* Math.sin(2 * LatRad)
|
186
|
+ (15 * eccSquared * eccSquared / 256 + 45 * eccSquared
|
187
|
* eccSquared * eccSquared / 1024)
|
188
|
* Math.sin(4 * LatRad) - (35 * eccSquared * eccSquared
|
189
|
* eccSquared / 3072)
|
190
|
* Math.sin(6 * LatRad));
|
191
|
|
192
|
var UTMEasting = (k0
|
193
|
* N
|
194
|
* (A + (1 - T + C) * A * A * A / 6.0 + (5 - 18 * T + T * T
|
195
|
+ 72 * C - 58 * eccPrimeSquared)
|
196
|
* A * A * A * A * A / 120.0) + 500000.0);
|
197
|
|
198
|
var UTMNorthing = (k0 * (M + N
|
199
|
* Math.tan(LatRad)
|
200
|
* (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A
|
201
|
/ 24.0 + (61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared)
|
202
|
* A * A * A * A * A * A / 720.0)));
|
203
|
if (Lat < 0.0) {
|
204
|
UTMNorthing += 10000000.0; //10000000 meter offset for
|
205
|
// southern hemisphere
|
206
|
}
|
207
|
|
208
|
northing_ = Math.round(UTMNorthing);
|
209
|
easting_ = Math.round(UTMEasting);
|
210
|
zone_number_ = ZoneNumber;
|
211
|
zone_letter_ = getLetterDesignator(Lat);
|
212
|
}
|
213
|
|
214
|
function getLetterDesignator(lat) {
|
215
|
//This is here as an error flag to show that the Latitude is
|
216
|
//outside MGRS limits
|
217
|
var LetterDesignator = 'Z';
|
218
|
|
219
|
if ((84 >= lat) && (lat >= 72))
|
220
|
LetterDesignator = 'X';
|
221
|
else if ((72 > lat) && (lat >= 64))
|
222
|
LetterDesignator = 'W';
|
223
|
else if ((64 > lat) && (lat >= 56))
|
224
|
LetterDesignator = 'V';
|
225
|
else if ((56 > lat) && (lat >= 48))
|
226
|
LetterDesignator = 'U';
|
227
|
else if ((48 > lat) && (lat >= 40))
|
228
|
LetterDesignator = 'T';
|
229
|
else if ((40 > lat) && (lat >= 32))
|
230
|
LetterDesignator = 'S';
|
231
|
else if ((32 > lat) && (lat >= 24))
|
232
|
LetterDesignator = 'R';
|
233
|
else if ((24 > lat) && (lat >= 16))
|
234
|
LetterDesignator = 'Q';
|
235
|
else if ((16 > lat) && (lat >= 8))
|
236
|
LetterDesignator = 'P';
|
237
|
else if ((8 > lat) && (lat >= 0))
|
238
|
LetterDesignator = 'N';
|
239
|
else if ((0 > lat) && (lat >= -8))
|
240
|
LetterDesignator = 'M';
|
241
|
else if ((-8 > lat) && (lat >= -16))
|
242
|
LetterDesignator = 'L';
|
243
|
else if ((-16 > lat) && (lat >= -24))
|
244
|
LetterDesignator = 'K';
|
245
|
else if ((-24 > lat) && (lat >= -32))
|
246
|
LetterDesignator = 'J';
|
247
|
else if ((-32 > lat) && (lat >= -40))
|
248
|
LetterDesignator = 'H';
|
249
|
else if ((-40 > lat) && (lat >= -48))
|
250
|
LetterDesignator = 'G';
|
251
|
else if ((-48 > lat) && (lat >= -56))
|
252
|
LetterDesignator = 'F';
|
253
|
else if ((-56 > lat) && (lat >= -64))
|
254
|
LetterDesignator = 'E';
|
255
|
else if ((-64 > lat) && (lat >= -72))
|
256
|
LetterDesignator = 'D';
|
257
|
else if ((-72 > lat) && (lat >= -80))
|
258
|
LetterDesignator = 'C';
|
259
|
return LetterDesignator;
|
260
|
}
|
261
|
|
262
|
function formatMGRS() {
|
263
|
var seasting = "" + easting_;
|
264
|
var snorthing = ""+ northing_;
|
265
|
while( snorthing.length > 6 )
|
266
|
snorthing = snorthing.substr(1,snorthing.length-1);
|
267
|
|
268
|
var str= zone_number_ + "" + zone_letter_ +
|
269
|
get100kID(easting_, northing_, zone_number_) +
|
270
|
seasting.substr(1,4) + snorthing.substr(1,4);
|
271
|
|
272
|
return str;
|
273
|
}
|
274
|
|
275
|
/**
|
276
|
* Get the two letter 100k designator for a given UTM easting,
|
277
|
* northing and zone number value.
|
278
|
*/
|
279
|
function get100kID( easting, northing, zone_number) {
|
280
|
var setParm = get100kSetForZone(zone_number);
|
281
|
var setColumn = Math.floor( easting / 100000);
|
282
|
var setRow = Math.floor( northing / 100000) % 20;
|
283
|
//trace("get100kID:"+setParm+" "+setColumn+" "+setRow);
|
284
|
return getLetter100kID(setColumn, setRow, setParm);
|
285
|
}
|
286
|
|
287
|
/**
|
288
|
* Given a UTM zone number, figure out the MGRS 100K set it is in.
|
289
|
*/
|
290
|
function get100kSetForZone(i) {
|
291
|
var setParm = i % NUM_100K_SETS;
|
292
|
if (setParm == 0)
|
293
|
setParm = NUM_100K_SETS;
|
294
|
|
295
|
return setParm;
|
296
|
}
|
297
|
|
298
|
/**
|
299
|
* Get the two-letter MGRS 100k designator given information
|
300
|
* translated from the UTM northing, easting and zone number.
|
301
|
*
|
302
|
* @param setColumn the column index as it relates to the MGRS
|
303
|
* 100k set spreadsheet, created from the UTM easting.
|
304
|
* Values are 1-8.
|
305
|
* @param setRow the row index as it relates to the MGRS 100k set
|
306
|
* spreadsheet, created from the UTM northing value. Values
|
307
|
* are from 0-19.
|
308
|
* @param setParm the set block, as it relates to the MGRS 100k set
|
309
|
* spreadsheet, created from the UTM zone. Values are from
|
310
|
* 1-60.
|
311
|
* @return two letter MGRS 100k code.
|
312
|
*/
|
313
|
|
314
|
function getLetter100kID( column, row, parm) {
|
315
|
// colOrigin and rowOrigin are the letters at the origin of the set
|
316
|
var index = parm-1;
|
317
|
var colOrigin = AsciiToNum(SET_ORIGIN_COLUMN_LETTERS[index]);
|
318
|
var rowOrigin = AsciiToNum(SET_ORIGIN_ROW_LETTERS[index]);
|
319
|
|
320
|
// colInt and rowInt are the letters to build to return
|
321
|
var colInt = colOrigin + column - 1;
|
322
|
var rowInt = rowOrigin + row;
|
323
|
var rollover = false;
|
324
|
|
325
|
if ( colInt > Z ) {
|
326
|
colInt = colInt - Z + A - 1;
|
327
|
rollover = true;
|
328
|
}
|
329
|
|
330
|
if (colInt == I || (colOrigin < I && colInt > I)
|
331
|
|| ((colInt > I || colOrigin < I) && rollover)) {
|
332
|
colInt++;
|
333
|
}
|
334
|
|
335
|
if (colInt == O || (colOrigin < O && colInt > O)
|
336
|
|| ((colInt > O || colOrigin < O) && rollover)) {
|
337
|
colInt++;
|
338
|
|
339
|
if (colInt == I) {
|
340
|
colInt++;
|
341
|
}
|
342
|
}
|
343
|
|
344
|
if (colInt > Z) {
|
345
|
colInt = colInt - Z + A - 1;
|
346
|
}
|
347
|
|
348
|
if (rowInt > V) {
|
349
|
rowInt = rowInt - V + A - 1;
|
350
|
rollover = true;
|
351
|
} else {
|
352
|
rollover = false;
|
353
|
}
|
354
|
|
355
|
if( ((rowInt == I) || ((rowOrigin < I) && (rowInt > I)))
|
356
|
|| (((rowInt > I)||(rowOrigin < I)) && rollover)) {
|
357
|
rowInt++;
|
358
|
}
|
359
|
|
360
|
if( ((rowInt == O) || ((rowOrigin < O) && (rowInt > O)))
|
361
|
|| (((rowInt > O)|| (rowOrigin < O)) && rollover)) {
|
362
|
rowInt++;
|
363
|
|
364
|
if (rowInt == I) {
|
365
|
rowInt++;
|
366
|
}
|
367
|
}
|
368
|
|
369
|
if (rowInt > V) {
|
370
|
rowInt = rowInt - V + A - 1;
|
371
|
}
|
372
|
|
373
|
var twoLetter = NumToAscii(colInt) + "" + NumToAscii(rowInt);
|
374
|
return twoLetter;
|
375
|
}
|
376
|
|
377
|
|
378
|
function AsciiToNum( ascii ) {
|
379
|
switch( ascii ) {
|
380
|
case 'A':
|
381
|
return 65;
|
382
|
case 'B':
|
383
|
return 66;
|
384
|
case 'C':
|
385
|
return 67;
|
386
|
case 'D':
|
387
|
return 68;
|
388
|
case 'E':
|
389
|
return 69;
|
390
|
case 'F':
|
391
|
return 70;
|
392
|
case 'G':
|
393
|
return 71;
|
394
|
case 'H':
|
395
|
return 72;
|
396
|
case 'I':
|
397
|
return 73;
|
398
|
case 'J':
|
399
|
return 74;
|
400
|
case 'K':
|
401
|
return 75;
|
402
|
case 'L':
|
403
|
return 76;
|
404
|
case 'M':
|
405
|
return 77;
|
406
|
case 'N':
|
407
|
return 78;
|
408
|
case 'O':
|
409
|
return 79;
|
410
|
case 'P':
|
411
|
return 80;
|
412
|
case 'Q':
|
413
|
return 81;
|
414
|
case 'R':
|
415
|
return 82;
|
416
|
case 'S':
|
417
|
return 83;
|
418
|
case 'T':
|
419
|
return 84;
|
420
|
case 'U':
|
421
|
return 85;
|
422
|
case 'V':
|
423
|
return 86;
|
424
|
case 'W':
|
425
|
return 87;
|
426
|
case 'X':
|
427
|
return 88;
|
428
|
case 'Y':
|
429
|
return 89;
|
430
|
case 'Z':
|
431
|
return 90;
|
432
|
}
|
433
|
}
|
434
|
|
435
|
function NumToAscii( num ) {
|
436
|
switch( num ) {
|
437
|
case 65:
|
438
|
return 'A';
|
439
|
case 66:
|
440
|
return 'B';
|
441
|
case 67:
|
442
|
return 'C';
|
443
|
case 68:
|
444
|
return 'D';
|
445
|
case 69:
|
446
|
return 'E';
|
447
|
case 70:
|
448
|
return 'F';
|
449
|
case 71:
|
450
|
return 'G';
|
451
|
case 72:
|
452
|
return 'H';
|
453
|
case 73:
|
454
|
return 'I';
|
455
|
case 74:
|
456
|
return 'J';
|
457
|
case 75 :
|
458
|
return 'K';
|
459
|
case 76:
|
460
|
return 'L';
|
461
|
case 77:
|
462
|
return 'M';
|
463
|
case 78:
|
464
|
return 'N';
|
465
|
case 79:
|
466
|
return 'O';
|
467
|
case 80:
|
468
|
return 'P';
|
469
|
case 81:
|
470
|
return 'Q';
|
471
|
case 82:
|
472
|
return 'R';
|
473
|
case 83:
|
474
|
return 'S';
|
475
|
case 84:
|
476
|
return 'T';
|
477
|
case 85:
|
478
|
return 'U';
|
479
|
case 86:
|
480
|
return 'V';
|
481
|
case 87:
|
482
|
return 'W';
|
483
|
case 88:
|
484
|
return 'X';
|
485
|
case 89:
|
486
|
return 'Y';
|
487
|
case 90:
|
488
|
return 'Z';
|
489
|
}
|
490
|
}
|
491
|
}
|