vgridjs
Version:
Vgrid DGGS JS
204 lines (203 loc) • 5.63 kB
JavaScript
// 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