Project

General

Profile

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
}
(2-2/5)