UNPKG

@wayz/react-gl

Version:

React Component for DeckGL, Base on AMap, Mapbox GL

217 lines (216 loc) 8.67 kB
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Geohash encoding/decoding and associated functions (c) Chris Veness 2014-2019 / MIT Licence */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ var base32 = '0123456789bcdefghjkmnpqrstuvwxyz'; // (geohash-specific) Base32 map /** * Geohash: Gustavo Niemeyer’s geocoding system. */ var Geohash = /** @class */ (function () { function Geohash() { } /** * 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 * const geohash = Geohash.encode(52.205, 0.119, 7); // => 'u120fxw' */ 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 += 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 * const latlon = Geohash.decode('u120fxw'); // => { lat: 52.205, lon: 0.1188 } */ 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. */ 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 = 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 {string} geohash - Cell to which adjacent cell is required. * @param {string} direction - Direction from geohash (N/S/E/W). * @returns {string} Geocode of adjacent cell. * @throws Invalid geohash. */ 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 + 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. */ 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'), }; }; return Geohash; }()); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ export default Geohash;