feat: add components
This commit is contained in:
243
components/component-geohash.js
Normal file
243
components/component-geohash.js
Normal file
@@ -0,0 +1,243 @@
|
||||
// https://github.com/chrisveness/latlon-geohash/blob/master/latlon-geohash.js
|
||||
(() => {
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
/* Geohash encoding/decoding and associated functions (c) Chris Veness 2014-2016 / MIT Licence */
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* Geohash encode, decode, bounds, neighbours.
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
var Geohash = {};
|
||||
|
||||
/* (Geohash-specific) Base32 map */
|
||||
Geohash.base32 = '0123456789bcdefghjkmnpqrstuvwxyz';
|
||||
|
||||
/**
|
||||
* Encodes latitude/longitude to geohash, either to specified precision or to automatically
|
||||
* evaluated precision.
|
||||
*
|
||||
* @param {number} lat - Latitude in degrees.
|
||||
* @param {number} lon - Longitude in degrees.
|
||||
* @param {number} [precision] - Number of characters in resulting geohash.
|
||||
* @returns {string} Geohash of supplied latitude/longitude.
|
||||
* @throws Invalid geohash.
|
||||
*
|
||||
* @example
|
||||
* var geohash = Geohash.encode(52.205, 0.119, 7); // geohash: 'u120fxw'
|
||||
*/
|
||||
exports.encode = Geohash.encode = function (lat, lon, precision) {
|
||||
// infer precision?
|
||||
if (typeof precision == 'undefined') {
|
||||
// refine geohash until it matches precision of supplied lat/lon
|
||||
for (var p = 1; p <= 12; p++) {
|
||||
var hash = Geohash.encode(lat, lon, p);
|
||||
var posn = Geohash.decode(hash);
|
||||
if (posn.lat == lat && posn.lon == lon) return hash;
|
||||
}
|
||||
precision = 12; // set to maximum
|
||||
}
|
||||
|
||||
lat = Number(lat);
|
||||
lon = Number(lon);
|
||||
precision = Number(precision);
|
||||
|
||||
if (isNaN(lat) || isNaN(lon) || isNaN(precision)) throw new Error('Invalid geohash');
|
||||
|
||||
var idx = 0; // index into base32 map
|
||||
var bit = 0; // each char holds 5 bits
|
||||
var evenBit = true;
|
||||
var geohash = '';
|
||||
|
||||
var latMin = -90, latMax = 90;
|
||||
var lonMin = -180, lonMax = 180;
|
||||
|
||||
while (geohash.length < precision) {
|
||||
if (evenBit) {
|
||||
// bisect E-W longitude
|
||||
var lonMid = (lonMin + lonMax) / 2;
|
||||
if (lon >= lonMid) {
|
||||
idx = idx * 2 + 1;
|
||||
lonMin = lonMid;
|
||||
} else {
|
||||
idx = idx * 2;
|
||||
lonMax = lonMid;
|
||||
}
|
||||
} else {
|
||||
// bisect N-S latitude
|
||||
var latMid = (latMin + latMax) / 2;
|
||||
if (lat >= latMid) {
|
||||
idx = idx * 2 + 1;
|
||||
latMin = latMid;
|
||||
} else {
|
||||
idx = idx * 2;
|
||||
latMax = latMid;
|
||||
}
|
||||
}
|
||||
evenBit = !evenBit;
|
||||
|
||||
if (++bit == 5) {
|
||||
// 5 bits gives us a character: append it and start over
|
||||
geohash += Geohash.base32.charAt(idx);
|
||||
bit = 0;
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return geohash;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Decode geohash to latitude/longitude (location is approximate centre of geohash cell,
|
||||
* to reasonable precision).
|
||||
*
|
||||
* @param {string} geohash - Geohash string to be converted to latitude/longitude.
|
||||
* @returns {{lat:number, lon:number}} (Center of) geohashed location.
|
||||
* @throws Invalid geohash.
|
||||
*
|
||||
* @example
|
||||
* var latlon = Geohash.decode('u120fxw'); // latlon: { lat: 52.205, lon: 0.1188 }
|
||||
*/
|
||||
exports.decode = Geohash.decode = function (geohash) {
|
||||
|
||||
var bounds = Geohash.bounds(geohash); // <-- the hard work
|
||||
// now just determine the centre of the cell...
|
||||
|
||||
var latMin = bounds.sw.lat, lonMin = bounds.sw.lon;
|
||||
var latMax = bounds.ne.lat, lonMax = bounds.ne.lon;
|
||||
|
||||
// cell centre
|
||||
var lat = (latMin + latMax) / 2;
|
||||
var lon = (lonMin + lonMax) / 2;
|
||||
|
||||
// round to close to centre without excessive precision: ⌊2-log10(Δ°)⌋ decimal places
|
||||
lat = lat.toFixed(Math.floor(2 - Math.log(latMax - latMin) / Math.LN10));
|
||||
lon = lon.toFixed(Math.floor(2 - Math.log(lonMax - lonMin) / Math.LN10));
|
||||
|
||||
return { lat: Number(lat), lon: Number(lon) };
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns SW/NE latitude/longitude bounds of specified geohash.
|
||||
*
|
||||
* @param {string} geohash - Cell that bounds are required of.
|
||||
* @returns {{sw: {lat: number, lon: number}, ne: {lat: number, lon: number}}}
|
||||
* @throws Invalid geohash.
|
||||
*/
|
||||
exports.bounds = Geohash.bounds = function (geohash) {
|
||||
if (geohash.length === 0) throw new Error('Invalid geohash');
|
||||
|
||||
geohash = geohash.toLowerCase();
|
||||
|
||||
var evenBit = true;
|
||||
var latMin = -90, latMax = 90;
|
||||
var lonMin = -180, lonMax = 180;
|
||||
|
||||
for (var i = 0; i < geohash.length; i++) {
|
||||
var chr = geohash.charAt(i);
|
||||
var idx = Geohash.base32.indexOf(chr);
|
||||
if (idx == -1) throw new Error('Invalid geohash');
|
||||
|
||||
for (var n = 4; n >= 0; n--) {
|
||||
var bitN = idx >> n & 1;
|
||||
if (evenBit) {
|
||||
// longitude
|
||||
var lonMid = (lonMin + lonMax) / 2;
|
||||
if (bitN == 1) {
|
||||
lonMin = lonMid;
|
||||
} else {
|
||||
lonMax = lonMid;
|
||||
}
|
||||
} else {
|
||||
// latitude
|
||||
var latMid = (latMin + latMax) / 2;
|
||||
if (bitN == 1) {
|
||||
latMin = latMid;
|
||||
} else {
|
||||
latMax = latMid;
|
||||
}
|
||||
}
|
||||
evenBit = !evenBit;
|
||||
}
|
||||
}
|
||||
|
||||
var bounds = {
|
||||
sw: { lat: latMin, lon: lonMin },
|
||||
ne: { lat: latMax, lon: lonMax },
|
||||
};
|
||||
|
||||
return bounds;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Determines adjacent cell in given direction.
|
||||
*
|
||||
* @param geohash - Cell to which adjacent cell is required.
|
||||
* @param direction - Direction from geohash (N/S/E/W).
|
||||
* @returns {string} Geocode of adjacent cell.
|
||||
* @throws Invalid geohash.
|
||||
*/
|
||||
exports.adjacent = Geohash.adjacent = function (geohash, direction) {
|
||||
// based on github.com/davetroy/geohash-js
|
||||
|
||||
geohash = geohash.toLowerCase();
|
||||
direction = direction.toLowerCase();
|
||||
|
||||
if (geohash.length === 0) throw new Error('Invalid geohash');
|
||||
if ('nsew'.indexOf(direction) == -1) throw new Error('Invalid direction');
|
||||
|
||||
var neighbour = {
|
||||
n: ['p0r21436x8zb9dcf5h7kjnmqesgutwvy', 'bc01fg45238967deuvhjyznpkmstqrwx'],
|
||||
s: ['14365h7k9dcfesgujnmqp0r2twvyx8zb', '238967debc01fg45kmstqrwxuvhjyznp'],
|
||||
e: ['bc01fg45238967deuvhjyznpkmstqrwx', 'p0r21436x8zb9dcf5h7kjnmqesgutwvy'],
|
||||
w: ['238967debc01fg45kmstqrwxuvhjyznp', '14365h7k9dcfesgujnmqp0r2twvyx8zb'],
|
||||
};
|
||||
var border = {
|
||||
n: ['prxz', 'bcfguvyz'],
|
||||
s: ['028b', '0145hjnp'],
|
||||
e: ['bcfguvyz', 'prxz'],
|
||||
w: ['0145hjnp', '028b'],
|
||||
};
|
||||
|
||||
var lastCh = geohash.slice(-1); // last character of hash
|
||||
var parent = geohash.slice(0, -1); // hash without last character
|
||||
|
||||
var type = geohash.length % 2;
|
||||
|
||||
// check for edge-cases which don't share common prefix
|
||||
if (border[direction][type].indexOf(lastCh) != -1 && parent !== '') {
|
||||
parent = Geohash.adjacent(parent, direction);
|
||||
}
|
||||
|
||||
// append letter for direction to parent
|
||||
return parent + Geohash.base32.charAt(neighbour[direction][type].indexOf(lastCh));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns all 8 adjacent cells to specified geohash.
|
||||
*
|
||||
* @param {string} geohash - Geohash neighbours are required of.
|
||||
* @returns {{n,ne,e,se,s,sw,w,nw: string}}
|
||||
* @throws Invalid geohash.
|
||||
*/
|
||||
exports.neighbours = Geohash.neighbours = function (geohash) {
|
||||
return {
|
||||
'n': Geohash.adjacent(geohash, 'n'),
|
||||
'ne': Geohash.adjacent(Geohash.adjacent(geohash, 'n'), 'e'),
|
||||
'e': Geohash.adjacent(geohash, 'e'),
|
||||
'se': Geohash.adjacent(Geohash.adjacent(geohash, 's'), 'e'),
|
||||
's': Geohash.adjacent(geohash, 's'),
|
||||
'sw': Geohash.adjacent(Geohash.adjacent(geohash, 's'), 'w'),
|
||||
'w': Geohash.adjacent(geohash, 'w'),
|
||||
'nw': Geohash.adjacent(Geohash.adjacent(geohash, 'n'), 'w'),
|
||||
};
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user