Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | 15x 4102x 4102x 4102x 4102x 4102x 4102x 3355x 3355x 3355x 4102x 4102x 2690x 2690x 15x 10028x 3012x 3012x 3012x 3012x 3012x 3012x 3012x 3012x 3012x 3012x 3012x 2679x 2679x 2679x 2679x 3012x 2349x 2349x 2349x 2349x 2349x 3012x | /**
* Global Area Reference System (GARS), NGA.
*
* 30' cells: 3-digit longitude band (001-720 from 180°W) + 2-letter latitude band (AA-QZ from 90°S)
* 15' quadrant: 1 2 / 3 4 (1 = NW)
* 5' keypad: 1 2 3 / 4 5 6 / 7 8 9 (1 = NW)
*/
import type { LatLon } from '../../geo/ellipsoid'
import type { GarsPrecision } from './precision'
const LETTERS = 'ABCDEFGHJKLMNPQRSTUVWXYZ' // no I, O
export interface GarsCell {
code: string
west: number
south: number
east: number
north: number
}
export function toGars(
{ lat, lon }: LatLon,
precision: GarsPrecision = 5,
): string {
// Work in whole 5-minute steps to avoid floating-point drift at cell edges.
const lon5 = Math.min(
4319,
Math.max(0, Math.floor(((lon + 180) * 60) / 5 + 1e-9)),
)
const lat5 = Math.min(
2159,
Math.max(0, Math.floor(((lat + 90) * 60) / 5 + 1e-9)),
)
const lonBand = Math.floor(lon5 / 6) + 1
const latIdx = Math.floor(lat5 / 6)
let code =
String(lonBand).padStart(3, '0') +
LETTERS[Math.floor(latIdx / 24)] +
LETTERS[latIdx % 24]
if (precision === 30) return code
const cx = lon5 % 6
const cy = lat5 % 6
const quadrant = (cy >= 3 ? 1 : 3) + (cx >= 3 ? 1 : 0)
code += quadrant
if (precision === 15) return code
const keypad = (2 - (cy % 3)) * 3 + (cx % 3) + 1
return code + keypad
}
const GARS_RE = /^(\d{3})([A-HJ-NP-Q][A-HJ-NP-Z])([1-4])?([1-9])?$/
export function isGars(s: string): boolean {
return GARS_RE.test(s.replace(/\s+/g, '').toUpperCase())
}
export function fromGars(input: string): GarsCell {
const code = input.replace(/\s+/g, '').toUpperCase()
const m = GARS_RE.exec(code)
Iif (!m) throw new Error(`Invalid GARS "${input}"`)
const lonBand = Number(m[1])
Iif (lonBand < 1 || lonBand > 720)
throw new Error('GARS longitude band must be 001-720')
const latIdx = LETTERS.indexOf(m[2]![0]!) * 24 + LETTERS.indexOf(m[2]![1]!)
Iif (latIdx > 359) throw new Error('GARS latitude band must be AA-QZ')
let west = -180 + (lonBand - 1) * 0.5
let south = -90 + latIdx * 0.5
let size = 0.5
if (m[3]) {
const q = Number(m[3])
size = 0.25
if (q === 2 || q === 4) west += size
if (q === 1 || q === 2) south += size
}
if (m[4]) {
Iif (!m[3]) throw new Error('GARS keypad needs a quadrant')
const k = Number(m[4]) - 1
size = 5 / 60
west += (k % 3) * size
south += (2 - Math.floor(k / 3)) * size
}
return { code, west, south, east: west + size, north: south + size }
}
|