# ngeohash: geohash encoding for Node, and the 52-bit wall

> ngeohash is a small geohash library with no runtime dependencies, offering the usual string API of encode, decode, bounding box and neighbour lookups, plus a parallel set of integer methods for storing hashes as numbers in a cache. It also builds a browser bundle with browserify, and the project is maintained by one person in their spare time.

**sunng87/node-geohash** — geohash library for nodejs

- Repository: https://github.com/sunng87/node-geohash
- Stars: 586 · Forks: 61
- Language: JavaScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/sunng87-node-geohash

## A geohash is a short string that means a place

A geohash encodes a latitude and longitude into a short string with a useful property: strings that share a prefix refer to nearby places, and the string is ordered so that a prefix search is a spatial query. That is what makes geohashes good as cache keys and as index keys, because the work of finding everything in a rectangle can be turned into finding all the prefixes that fall inside it.

The ngeohash package is a small library for doing that in JavaScript, and it has no runtime dependencies at all, which is stated plainly in the manifest with an empty dependency object. The author is Ning Sun, the package is published to npm, and the licence is MIT.

The API is the classic one, and the usage example is short enough to take in at a glance:
```javascript
var geohash = require('ngeohash');
console.log(geohash.encode(37.8324, 112.5584));
// prints ww8p1r4t8
var latlon = geohash.decode('ww8p1r4t8');
console.log(latlon.latitude);
console.log(latlon.longitude);
```
The encoding takes latitude first and longitude second, which is the opposite order to the way most mapping APIs are written, and it is worth internalising before you pass coordinates to it by accident.

## Encode, decode, and the box in between

The string API has five methods, and each one has a small argument that changes its behaviour.

The encode method takes a latitude and a longitude, with an optional third argument for the length of the hash string, which also affects the precision of the geohash. The default precision is nine characters.

The decode method takes a hash string and returns a JavaScript object with latitude and longitude keys, which are the centre of the area the hash covers rather than the original point, since the hash is a region.

The decode bbox method takes a hash string and returns the bounding box that matches it, as a four-element array in the order minimum latitude, minimum longitude, maximum latitude, maximum longitude. That is the method you want when you need the extent of a hash rather than its centre, which is the usual case for drawing a map viewport or for testing containment.

The bboxes method goes the other way. It takes a minimum and maximum latitude and longitude and returns all the hash strings between them, at a given precision that defaults to nine. The documentation gives the intended use: those keys can be used to find a point of interest stored in a cache with hash string keys, for example to show all the points in the visible range of a map. The southwest corner corresponds to the starting hash and the northeast corner to the ending hash.

## Neighbours, and why the batch version exists

Two methods handle adjacency, and they come as a pair because the second one exists for a reason.

The neighbor method finds the neighbour of a hash in a given direction, and the direction is a two-element array. The documentation gives the convention: 1,0 means north, and minus 1, minus 1 means southwest.

The neighbors method returns all eight neighbours of a hash at once, in the order north, north east, east, south east, south, south west, west, north west. The documentation notes that this method is more efficient than running the neighbor method multiple times for all the directions, which is the kind of note that only appears when someone measured it.

That efficiency point is the interesting one, because neighbour lookups are what a map client does constantly. Each neighbour computation involves re-deriving the coordinate, stepping to the adjacent cell and re-encoding, and doing eight of them one at a time repeats the same boundary handling eight times. The batch method exists because that cost showed up in practice, which is a good sign for the rest of the library, since the author was clearly using it in a real mapping application rather than writing it in isolation.

## The integer API and the 52-bit wall

The second half of the library mirrors every string method with an integer variant, and the reason is storage. A geohash as a number can be a cache key, a database column value or a compact index entry without string parsing.

There is a catch, and the documentation is straightforward about it. Integer precision is capped at 52 bits in JavaScript, because these are regular floating point numbers. The functionality is meant to mimic the uint64 geohash functions in geohash libraries for other languages, and the results are compatible with the geohash integers those produce, but any geohash integer encoded in anything more than 52 bits will be resolved to 52-bit resolution. In other words, if you take an integer produced by another library at 64 bits and decode it here, the extra bits are gone.

The integer methods take a bit depth argument that defaults to 52, and it must be an even number. The documentation also warns that the bit depth must be used consistently when decoding, since it affects the precision, and that a decode without a bit depth defaults to 52. So there are two numbers to keep straight in a system that mixes libraries: the depth you encoded with, and the depth you decode with.

The rest mirrors the string API: an integer encode, a decode that returns approximations, an integer bounding box decode, an integer bboxes call, and integer versions of the neighbour methods.

## A browser bundle from a browserify script

The library can also be bundled for the browser. The project added a browserify script that you run with the npm run browserify command, which generates an ngeohash.js file, and the documentation says you can use that file in your JavaScript application for the browser:
```html
<script src="./path/to/ngeohash.js"></script>
<script>
alert(ngeohash.encode(114.23, 38.23));
</script>
```
The generated file exposes the library as a global, and the build command in the manifest shows the shape of it, running browserify over the package and writing the output with a standalone name, which is what makes the global appear.

The browserify tool is one of three development dependencies, the others being the assert module and nodeunit, and the test script runs nodeunit over a single test file. So the whole test suite is one file and one runner, which for a library this small is proportionate.

The author also maintains a Rust port of the same library, listed in the see also section, which is the sensible thing to do if you find yourself needing the same operations in a different runtime.

## The package details that catch people out

A few things in the manifest are worth reading carefully.

The npm package name is ngeohash, not node-geohash, which is the repository name. The install command in the documentation uses the npm name, and the badge in the project notes points at the same package, so it is consistent, but a search for the repository name in a package registry will not find it.

The version is 0.6.3 and the project publishes no GitHub releases, so the npm version is the only version marker. The last push was on 2026-07-25, so the code is still being touched.

The engine constraint is the odd one. The manifest declares a node engine requirement of greater than or equal to v0.2.0, which is not a meaningful constraint on any runtime anyone uses today, and the build badge in the project notes is for Travis, which suggests the continuous integration setup dates from the same era as the constraint.

The upside of all that is that a package with no runtime dependencies, one test file and a browser bundle is easy to vendor, easy to audit and easy to replace, and the last piece of the documentation is a note that the project accepts donations through liberapay if it has been useful, which is the usual arrangement for a small library kept alive by one person.

## Conclusion

ngeohash suits a Node service that keys cached points of interest by geohash, because the bounding box and neighbour methods are exactly the primitives a map viewport needs and the package has no runtime dependencies to audit. It is the wrong choice if you need precision beyond 52 bits of integer hash, since that is a hard limit of the JavaScript number type rather than of the algorithm, and it is the wrong choice if you want a maintained, actively versioned dependency, because the last release line is old and the engine constraint in the manifest is effectively meaningless. Before you use the integer API, decide on a bit depth and use it everywhere, because the documentation is explicit that the depth must be even and must match when decoding, and remember that the published package name is ngeohash while the repository is called node-geohash. The licence is MIT, the package version is 0.6.3, and the last push was on 2026-07-25.

## FAQ

### How do I install node-geohash?

From npm, with npm install ngeohash. Note that the published package name is ngeohash while the repository is called node-geohash, and the library has no runtime dependencies at all.

### How do I find every geohash inside a bounding box?

Use the bboxes method with a minimum and maximum latitude and longitude, and an optional precision that defaults to nine. It returns all the hash strings in that range, which the documentation suggests for looking up points of interest in a cache keyed by hash string, for example everything visible in the current map range.

### How do I get the neighbouring geohashes of a hash?

The neighbors method returns all eight at once, ordered north, north east, east, south east, south, south west, west, north west, and the documentation notes it is more efficient than calling the single-direction neighbor method repeatedly. A direction is a two-element array, where 1,0 means north and minus 1, minus 1 means southwest.

### Why is the integer geohash capped at 52 bits?

Because the integer methods use regular JavaScript floating point numbers, whose integer precision is capped at 52 bits. The values are compatible with uint64 geohash integers from other languages, but anything encoded in more than 52 bits will be resolved to 52-bit resolution, and the bit depth must be even and must be used consistently when decoding.

### Can I use ngeohash in a browser?

Yes. The project ships a browserify script, so running npm run browserify generates an ngeohash.js file you can include with a script tag, which exposes the library as a global called ngeohash.

## Sources

- [Issues](https://github.com/sunng87/node-geohash/issues)
- [License: MIT](https://github.com/sunng87/node-geohash/blob/master/LICENSE)
- [README](https://github.com/sunng87/node-geohash/blob/master/README.md)
- [sunng87/node-geohash on GitHub](https://github.com/sunng87/node-geohash)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sunng87-node-geohash
