Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

latlon-geohash

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

latlon-geohash - npm Package Compare versions

Comparing version 1.1.0 to 2.0.0

379

latlon-geohash.js
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Geohash encoding/decoding and associated functions (c) Chris Veness 2014-2016 / MIT Licence */
/* Geohash encoding/decoding and associated functions (c) Chris Veness 2014-2019 / MIT Licence */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
'use strict';
const base32 = '0123456789bcdefghjkmnpqrstuvwxyz'; // (geohash-specific) Base32 map
/**
* Geohash encode, decode, bounds, neighbours.
*
* @namespace
* Geohash: Gustavo Niemeyer’s geocoding system.
*/
var Geohash = {};
class 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'
*/
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;
/**
* 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'
*/
static encode(lat, lon, precision) {
// infer precision?
if (typeof precision == 'undefined') {
// refine geohash until it matches precision of supplied lat/lon
for (let p=1; p<=12; p++) {
const hash = Geohash.encode(lat, lon, p);
const posn = Geohash.decode(hash);
if (posn.lat==lat && posn.lon==lon) return hash;
}
precision = 12; // set to maximum
}
precision = 12; // set to maximum
}
lat = Number(lat);
lon = Number(lon);
precision = Number(precision);
lat = Number(lat);
lon = Number(lon);
precision = Number(precision);
if (isNaN(lat) || isNaN(lon) || isNaN(precision)) throw new Error('Invalid geohash');
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 = '';
let idx = 0; // index into base32 map
let bit = 0; // each char holds 5 bits
let evenBit = true;
let geohash = '';
var latMin = -90, latMax = 90;
var lonMin = -180, lonMax = 180;
let latMin = -90, latMax = 90;
let 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;
while (geohash.length < precision) {
if (evenBit) {
// bisect E-W longitude
const lonMid = (lonMin + lonMax) / 2;
if (lon >= lonMid) {
idx = idx*2 + 1;
lonMin = lonMid;
} else {
idx = idx*2;
lonMax = lonMid;
}
} else {
idx = idx*2;
lonMax = lonMid;
// bisect N-S latitude
const latMid = (latMin + latMax) / 2;
if (lat >= latMid) {
idx = idx*2 + 1;
latMin = latMid;
} else {
idx = idx*2;
latMax = latMid;
}
}
} 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;
}
}
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;
}
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 }
*/
static decode(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 }
*/
Geohash.decode = function(geohash) {
const bounds = Geohash.bounds(geohash); // <-- the hard work
// now just determine the centre of the cell...
var bounds = Geohash.bounds(geohash); // <-- the hard work
// now just determine the centre of the cell...
const latMin = bounds.sw.lat, lonMin = bounds.sw.lon;
const latMax = bounds.ne.lat, lonMax = bounds.ne.lon;
var latMin = bounds.sw.lat, lonMin = bounds.sw.lon;
var latMax = bounds.ne.lat, lonMax = bounds.ne.lon;
// cell centre
let lat = (latMin + latMax)/2;
let lon = (lonMin + lonMax)/2;
// 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));
// 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) };
}
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.
*/
static bounds(geohash) {
if (geohash.length == 0) throw new Error('Invalid geohash');
/**
* 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();
geohash = geohash.toLowerCase();
let evenBit = true;
let latMin = -90, latMax = 90;
let lonMin = -180, lonMax = 180;
var evenBit = true;
var latMin = -90, latMax = 90;
var lonMin = -180, lonMax = 180;
for (let i=0; i<geohash.length; i++) {
const chr = geohash.charAt(i);
const idx = base32.indexOf(chr);
if (idx == -1) throw new Error('Invalid geohash');
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;
for (let n=4; n>=0; n--) {
const bitN = idx >> n & 1;
if (evenBit) {
// longitude
const lonMid = (lonMin+lonMax) / 2;
if (bitN == 1) {
lonMin = lonMid;
} else {
lonMax = lonMid;
}
} else {
lonMax = lonMid;
// latitude
const latMid = (latMin+latMax) / 2;
if (bitN == 1) {
latMin = latMid;
} else {
latMax = latMid;
}
}
} else {
// latitude
var latMid = (latMin+latMax) / 2;
if (bitN == 1) {
latMin = latMid;
} else {
latMax = latMid;
}
evenBit = !evenBit;
}
evenBit = !evenBit;
}
const bounds = {
sw: { lat: latMin, lon: lonMin },
ne: { lat: latMax, lon: lonMax },
};
return bounds;
}
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.
*/
static adjacent(geohash, direction) {
// based on github.com/davetroy/geohash-js
geohash = geohash.toLowerCase();
direction = direction.toLowerCase();
/**
* 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.
*/
Geohash.adjacent = function(geohash, direction) {
// based on github.com/davetroy/geohash-js
if (geohash.length == 0) throw new Error('Invalid geohash');
if ('nsew'.indexOf(direction) == -1) throw new Error('Invalid direction');
geohash = geohash.toLowerCase();
direction = direction.toLowerCase();
const neighbour = {
n: [ 'p0r21436x8zb9dcf5h7kjnmqesgutwvy', 'bc01fg45238967deuvhjyznpkmstqrwx' ],
s: [ '14365h7k9dcfesgujnmqp0r2twvyx8zb', '238967debc01fg45kmstqrwxuvhjyznp' ],
e: [ 'bc01fg45238967deuvhjyznpkmstqrwx', 'p0r21436x8zb9dcf5h7kjnmqesgutwvy' ],
w: [ '238967debc01fg45kmstqrwxuvhjyznp', '14365h7k9dcfesgujnmqp0r2twvyx8zb' ],
};
const border = {
n: [ 'prxz', 'bcfguvyz' ],
s: [ '028b', '0145hjnp' ],
e: [ 'bcfguvyz', 'prxz' ],
w: [ '0145hjnp', '028b' ],
};
if (geohash.length === 0) throw new Error('Invalid geohash');
if ('nsew'.indexOf(direction) == -1) throw new Error('Invalid direction');
const lastCh = geohash.slice(-1); // last character of hash
let parent = geohash.slice(0, -1); // hash without last character
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' ],
};
const type = geohash.length % 2;
var lastCh = geohash.slice(-1); // last character of hash
var parent = geohash.slice(0, -1); // hash without last character
// check for edge-cases which don't share common prefix
if (border[direction][type].indexOf(lastCh) != -1 && parent != '') {
parent = Geohash.adjacent(parent, direction);
}
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));
}
// 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.
*/
static neighbours(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'),
};
}
/**
* 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'),
};
};
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if (typeof module != 'undefined' && module.exports) module.exports = Geohash; // CommonJS, node.js
export default Geohash;
{
"name": "latlon-geohash",
"description": "Gustavo Niemeyer’s geocoding system",
"homepage": "http://www.movable-type.co.uk/scripts/geohash.html",
"author": "Chris Veness",
"version": "1.1.0",
"version": "2.0.0",
"license": "MIT",
"homepage": "http://www.movable-type.co.uk/scripts/geohash.html",
"type": "module",
"main": "latlon-geohash.js",

@@ -15,15 +16,22 @@ "repository": {

"scripts": {
"test": "mocha test.js",
"test": "mocha --exit -r esm test.js",
"lint": "eslint latlon-geohash.js"
},
"devDependencies": {
"chai": "^3.5.0",
"eslint": "^3.5.0",
"mocha": "^3.0.2"
"chai": "^4.0.0",
"eslint": "^6.0.0",
"esm": "^3.0.0",
"mocha": "^6.0.0"
},
"eslintConfig": {
"env": {
"node": true,
"mocha": true
"browser": true,
"es6": true,
"mocha": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"extends": "eslint:recommended",

@@ -33,12 +41,20 @@ "rules": {

"comma-dangle": [ "error", "always-multiline" ],
"comma-spacing": [ "error" ],
"curly": [ "error", "multi-line" ],
"indent": [ "error", 4, { "SwitchCase": 1 } ],
"key-spacing": [ "error", { "align": "value" } ],
"no-console": "warn",
"keyword-spacing": [ "error" ],
"no-case-declarations": "warn",
"no-console": [ "warn", { "allow": [ "error", "info" ] } ],
"no-irregular-whitespace": "warn",
"no-redeclare": "warn",
"no-shadow": "warn",
"no-unused-vars": "warn",
"no-var": "error",
"object-curly-spacing": [ "error", "always" ],
"prefer-const": "error",
"quotes": [ "error", "single", "avoid-escape" ],
"semi": [ "error", "always" ],
"space-before-blocks": [ "error", "always" ],
"space-in-parens": [ "error" ],
"strict": [ "error", "global" ]

@@ -45,0 +61,0 @@ }

@@ -7,27 +7,8 @@ geohash

Methods summary:
Note this version 2 uses ES classes and modules: for older browsers (or Node.js <8.0.0),
[v1.1.0](https://github.com/chrisveness/latlon-geohash/tree/v1.1.0) is ES5-based.
- `encode`: latitude/longitude point to geohash
- `decode`: geohash to latitude/longitude
- `bounds` of a geohash cell
- `adjacent` `neighbours` of a geohash
API
---
Install
-------
### in browser
Download the JavaScript [source](https://raw.githubusercontent.com/chrisveness/geodesy/master/latlon-geohash.js)
and reference in HTML page using:
<script src="js/latlon-geohash.js"></script>
### from npm
npm install --save latlon-geohash
Usage
-----
- `Geohash.encode(lat, lon, [precision])`: encode latitude/longitude point to geohash of given precision

@@ -43,11 +24,48 @@ (number of characters in resulting geohash); if precision is not specified, it is inferred from

var neighboursObj = Geohash.neighbours(geohash);
var neighboursArr = Object.keys(neighboursObj).map(function(n) { return neighboursObj[n]; });
const neighboursObj = Geohash.neighbours(geohash);
const neighboursArr = Object.keys(neighboursObj).map(n => neighboursObj[n]);
Note that the parent of a geocode is simply `geocode.slice(0, -1)`.
The parent of a geocode is simply `geocode.slice(0, -1)`.
### Import within node.js
If you want the geohash converted from Base32 to Base4, you can e.g.:
var Geohash = require('latlon-geohash');
parseInt(Geohash.encode(52.20, 0.12, 6), 32).toString(4);
Usage in browser
----------------
Geohash can be used in the browser by taking a local copy, or loading it from
[jsDelivr](https://www.jsdelivr.com/package/npm/latlon-geohash): for example,
```html
<!doctype html><title>geohash example</title><meta charset="utf-8">
<script type="module">
import Geohash from 'https://cdn.jsdelivr.net/npm/latlon-geohash@2.0.0';
const geohash = Geohash.encode(52.20, 0.12, 6);
console.assert(geohash == 'u120fw');
const latlon = Geohash.decode('u120fw');
console.assert(JSON.stringify(latlon) == '{"lat":52.1988,"lon":0.115}');
</script>
```
Usage in Node.js
----------------
Geohash can be used in a Node.js app from [npm](https://www.npmjs.com/package/latlon-geohash)
(currently the [esm](https://www.npmjs.com/package/esm) package is required to load ES-modules):
```shell
$ npm install latlon-geohash esm
$ node -r esm
> import Geohash from 'latlon-geohash';
> const geohash = Geohash.encode(52.20, 0.12, 6);
> console.assert(geohash == 'u120fw');
> const latlon = Geohash.decode('u120fw');
> console.assert(JSON.stringify(latlon) == '{"lat":52.1988,"lon":0.115}');
```
Further details

@@ -54,0 +72,0 @@ ---------------

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Geohash Test Harness (c) Chris Veness 2014-2016 / MIT Licence */
/* Geohash Test Harness (c) Chris Veness 2014-2019 / MIT Licence */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
'use strict'
const chai = require('chai'); // BDD/TDD assertion library
import Geohash from './latlon-geohash.js';
const Geohash = require('./latlon-geohash.js');
if (typeof window == 'undefined') { // node
import('chai').then(chai => { global.should = chai.should(); });
} else { // browser
window.should = chai.should();
}
const should = chai.should();

@@ -12,0 +14,0 @@ describe('latlon-geohash', function() {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc