UNPKG

vgridjs

Version:

Vgrid DGGS JS

204 lines (203 loc) 5.63 kB
// dggs/geohash.ts var _base32 = "0123456789bcdefghjkmnpqrstuvwxyz"; var _base32Map = {}; for (let i = 0; i < _base32.length; i++) { _base32Map[_base32[i]] = i; } function _floatHexToInt(f) { if (f < -1 || f >= 1) { return null; } if (f === 0) { return [1, 1]; } const h = f.toString(16); const x = h.indexOf("0x1."); if (x < 0) return null; const p = h.indexOf("p"); if (p <= 0) return null; const halfLen = h.slice(x + 4, p).length * 4 - parseInt(h.slice(p + 1)); let r; if (x === 0) { r = (1 << halfLen) + ((1 << h.slice(x + 4, p).length * 4) + parseInt(h.slice(x + 4, p), 16)); } else { r = (1 << halfLen) - ((1 << h.slice(x + 4, p).length * 4) + parseInt(h.slice(x + 4, p), 16)); } return [r, halfLen + 1]; } function _intToFloatHex(i, l) { if (l === 0) { return -1; } const half = 1 << l - 1; const s = Math.floor((l + 3) / 4); if (i >= half) { i = i - half; return parseFloat(`0x0.${(i << s * 4 - l).toString(16).padStart(s, "0")}p1`); } else { i = half - i; return parseFloat(`-0x0.${(i << s * 4 - l).toString(16).padStart(s, "0")}p1`); } } function _encodeI2c(lat, lon, latLength, lonLength) { const precision = Math.floor((latLength + lonLength) / 5); let a, b; if (latLength < lonLength) { a = lon; b = lat; } else { a = lat; b = lon; } const boost = [0, 1, 4, 5, 16, 17, 20, 21]; let ret = ""; for (let i = 0; i < precision; i++) { ret += _base32[boost[a & 7] + (boost[b & 3] << 1) & 31]; const t = a >> 3; a = b >> 2; b = t; } return ret.split("").reverse().join(""); } function encode(latitude, longitude, precision = 12) { if (latitude >= 90 || latitude < -90) { throw new Error("Invalid latitude."); } while (longitude < -180) { longitude += 360; } while (longitude >= 180) { longitude -= 360; } const xprecision = precision + 1; let latLength = Math.floor(xprecision * 5 / 2); let lonLength = latLength; if (xprecision % 2 === 1) { lonLength += 1; } const a = _floatHexToInt(latitude / 90); const o = _floatHexToInt(longitude / 180); if (!a || !o) { throw new Error("Invalid coordinates"); } let ai, oi; if (a[1] > latLength) { ai = a[0] >> a[1] - latLength; } else { ai = a[0] << latLength - a[1]; } if (o[1] > lonLength) { oi = o[0] >> o[1] - lonLength; } else { oi = o[0] << lonLength - o[1]; } return _encodeI2c(ai, oi, latLength, lonLength).slice(0, precision); } function _decodeC2i(hashcode) { let lon = 0; let lat = 0; let bitLength = 0; let latLength = 0; let lonLength = 0; for (const i of hashcode) { const t = _base32Map[i]; if (bitLength % 2 === 0) { lon = lon << 3; lat = lat << 2; lon += t >> 2 & 4; lat += t >> 2 & 2; lon += t >> 1 & 2; lat += t >> 1 & 1; lon += t & 1; lonLength += 3; latLength += 2; } else { lon = lon << 2; lat = lat << 3; lat += t >> 2 & 4; lon += t >> 2 & 2; lat += t >> 1 & 2; lon += t >> 1 & 1; lat += t & 1; lonLength += 2; latLength += 3; } bitLength += 5; } return [lat, lon, latLength, lonLength]; } function decode(hashcode, delta = false) { const [lat, lon, latLength, lonLength] = _decodeC2i(hashcode); const latitudeDelta = 90 / (1 << latLength); const longitudeDelta = 180 / (1 << lonLength); const latitude = _intToFloatHex(lat, latLength) * 90 + latitudeDelta; const longitude = _intToFloatHex(lon, lonLength) * 180 + longitudeDelta; if (delta) { return { latitude, longitude, latitudeDelta, longitudeDelta }; } return { latitude, longitude }; } function decodeExactly(hashcode) { return decode(hashcode, true); } function bbox(hashcode) { const [lat, lon, latLength, lonLength] = _decodeC2i(hashcode); const latitudeDelta = 180 / (1 << latLength); const longitudeDelta = 360 / (1 << lonLength); const latitude = _intToFloatHex(lat, latLength) * 90; const longitude = _intToFloatHex(lon, lonLength) * 180; return { s: latitude, w: longitude, n: latitude + latitudeDelta, e: longitude + longitudeDelta }; } function neighbors(hashcode) { const [lat, lon, latLength, lonLength] = _decodeC2i(hashcode); const ret = []; let tlat = lat; for (const tlon of [lon - 1, lon + 1]) { const code = _encodeI2c(tlat, tlon, latLength, lonLength); if (code) { ret.push(code); } } tlat = lat + 1; if (!(tlat >> latLength)) { for (const tlon of [lon - 1, lon, lon + 1]) { ret.push(_encodeI2c(tlat, tlon, latLength, lonLength)); } } tlat = lat - 1; if (tlat >= 0) { for (const tlon of [lon - 1, lon, lon + 1]) { ret.push(_encodeI2c(tlat, tlon, latLength, lonLength)); } } return ret; } function expand(hashcode) { const ret = neighbors(hashcode); ret.push(hashcode); return ret; } function geohashParent(geohashId) { if (!geohashId || geohashId.length <= 1) { throw new Error("Cannot get parent of an empty or single-character geohash."); } return geohashId.slice(0, -1); } function geohashChildren(geohashId, resolution) { if (geohashId.length >= resolution) { return geohashId.length === resolution ? [geohashId] : []; } let children = [geohashId]; while (children[0].length < resolution) { children = children.flatMap((c) => _base32.split("").map((ch) => c + ch)); } return children; } export { bbox, decode, decodeExactly, encode, expand, geohashChildren, geohashParent, neighbors }; //# sourceMappingURL=geohash.js.map //# sourceMappingURL=geohash.js.map