UNPKG

vgridjs

Version:

Vgrid DGGS JS

188 lines (187 loc) 5.59 kB
// dggs/georef.ts var digits = "0123456789"; var lontile = "ABCDEFGHJKLMNPQRSTUVWXYZ"; var lattile = "ABCDEFGHJKLMM"; var degrees = "ABCDEFGHJKLMNPQ"; var tile = 15; var lonorig = -180; var latorig = -90; var base = 10; var baselen = 4; var maxprec = 11; var maxlen = baselen + 2 * maxprec; var GeorefException = class extends Error { constructor(message) { super(message); this.name = "GeorefException"; } }; function findFirstNotOf(s, sSet) { for (let i = 0; i < s.length; i++) { if (!sSet.includes(s[i])) { return i; } } return -1; } function lookup(s, c) { const r = s.indexOf(c); return r < 0 ? -1 : r; } function encode(lat, lon, prec) { if (lat > 90 || lat < -90) { throw new GeorefException("Latitude not in -90 to 90 range"); } if (lon < -180 || lon > 360) { throw new GeorefException("Longitude is out of range"); } if (lon >= 180) { lon = lon - 360; } if (lat === 90) { lat = lat - Number.EPSILON; } prec = Math.max(-1, Math.min(Math.floor(maxprec), prec)); if (prec === 1) { prec = prec + 1; } const m = 6e10; const x = Math.floor(lon * m) - lonorig * m; const y = Math.floor(lat * m) - latorig * m; const ilon = Math.floor(x / m); const ilat = Math.floor(y / m); const georef1 = new Array(maxlen).fill(""); georef1[0] = lontile[Math.floor(ilon / tile)]; georef1[1] = lattile[Math.floor(ilat / tile)]; if (prec >= 0) { georef1[2] = degrees[ilon % tile]; georef1[3] = degrees[ilat % tile]; if (prec > 0) { let x1 = Math.floor(x - m * ilon); let y1 = Math.floor(y - m * ilat); const d = Math.pow(base, maxprec - prec); x1 = Math.floor(x1 / d); y1 = Math.floor(y1 / d); let c = prec; while (c) { georef1[baselen + c] = digits[x1 % base]; x1 = Math.floor(x1 / base); georef1[baselen + c + prec] = digits[y1 % base]; y1 = Math.floor(y1 / base); c = c - 1; } } } return georef1.join(""); } function decode(georef, centerp = false) { if (!georef) { throw new GeorefException("Invalid Georef string: None"); } georef = georef.toUpperCase(); const leng = georef.length; if (leng >= 3 && georef[0] === "I" && georef[1] === "N" && georef[2] === "V") { throw new GeorefException("Invalid Georef string"); } if (leng < baselen - 2) { throw new GeorefException(`Georef must start with at least 2 letters: ${georef}`); } const prec1 = Math.floor((2 + leng - baselen) / 2 - 1); let k = lookup(lontile, georef[0]); if (k < 0) { throw new GeorefException(`Bad longitude tile letter in georef: ${georef}`); } let lon1 = k + lonorig / tile; k = lookup(lattile, georef[1]); if (k < 0) { throw new GeorefException(`Bad latitude tile letter in georef: ${georef}`); } let lat1 = k + latorig / tile; let unit = 1; if (leng > 2) { unit = unit * tile; k = lookup(degrees, georef[2]); if (k < 0) { throw new GeorefException(`Bad longitude degree letter in georef: ${georef}`); } lon1 = lon1 * tile + k; if (leng < 4) { throw new GeorefException(`Missing latitude degree letter in georef: ${georef}`); } k = lookup(degrees, georef[3]); if (k < 0) { throw new GeorefException(`Bad latitude degree letter in georef: ${georef}`); } lat1 = lat1 * tile + k; if (prec1 > 0) { if (findFirstNotOf(georef.slice(baselen), digits) !== -1) { throw new GeorefException(`Non digits in trailing portion of georef: ${georef.slice(baselen)}`); } if (leng % 2) { throw new GeorefException(`Georef must end with an even number of digits: ${georef.slice(baselen)}`); } if (prec1 === 1) { throw new GeorefException(`Georef needs at least 4 digits for minutes: ${georef.slice(baselen)}`); } if (prec1 > maxprec) { throw new GeorefException(`More than ${2 * maxprec} digits in georef: ${georef.slice(baselen)}`); } let i = 0; while (i < prec1) { const m = i ? base : 6; unit = unit * m; const x = lookup(digits, georef[baselen + i]); const y = lookup(digits, georef[baselen + i + prec1]); if (!(i || x < m && y < m)) { throw new GeorefException(`Minutes terms in georef must be less than 60: ${georef.slice(baselen)}`); } lon1 = m * lon1 + x; lat1 = m * lat1 + y; i = i + 1; } } } if (centerp) { unit = unit * 2; lat1 = 2 * lat1 + 1; lon1 = 2 * lon1 + 1; } const lat = tile * lat1 / unit; const lon = tile * lon1 / unit; const prec = prec1; return [lat, lon, prec]; } function georefCell(georefId) { const [centerLat, centerLon, resolution] = decode(georefId, true); let gridSize; switch (resolution) { case 0: gridSize = 15; break; case 1: gridSize = 1; break; case 2: gridSize = 1 / 60; break; case 3: gridSize = 1 / 600; break; case 4: gridSize = 1 / 6e3; break; case 5: gridSize = 1 / 6e4; break; default: throw new GeorefException(`Invalid resolution: ${resolution}`); } const minLon = Math.floor(centerLon / gridSize) * gridSize; const maxLon = minLon + gridSize; const minLat = Math.floor(centerLat / gridSize) * gridSize; const maxLat = minLat + gridSize; return [centerLat, centerLon, minLat, minLon, maxLat, maxLon, resolution]; } export { GeorefException, decode, encode, georefCell }; //# sourceMappingURL=georef.js.map //# sourceMappingURL=georef.js.map