Note: Requires a Redis server to be accessible to your Node environment.
Note: Will use native Redis geo commands if they are available, and fallback to an emulation if not available.
This Node module provides comprehensive location management and queries to all your special projects. More specifically:
- Basic management (addition, querying and removal) of sets of named geo locations.
- Fast querying of nearby locations to a point/member within a set. Fast like redis is fast.
- Sort, limit, and get location and distance information of nearby query results.
- Defaults to use native Redis geo commands if available (means extra performance!), but falls back to emulation otherwise.
- A simple, easy to use, scalable interface.
Other bonuses:
- Supports node-redis and ioredis modules. fakeredis is unofficially supported.
- Compatible input/output with the popular GeoLib module for further manipulation.
Read more about how this module works.
npm install --save georedis
Usage of this module should be extremely simple. Just make sure that your Redis server is accessible to your Node environment. Because this module uses Redis as a store, almost all methods have integrated error handling for queries.
Include and initialize this module with a redis client instance.
var redis = require('redis'),
client = redis.createClient()
var geo = require('georedis').initialize(client)
Add locations individually:
geo.addLocation('Toronto', {latitude: 43.6667, longitude: -79.4167}, function(err, reply){
if(err) console.error(err)
else console.log('added location:', reply)
})
If you have a large set you'd like to add in bulk, there's a much quicker way:
var locationSet = {
'Toronto': {latitude: 43.6667, longitude: -79.4167},
'Philadelphia': {latitude: 39.9523, longitude: -75.1638},
'Palo Alto': {latitude: 37.4688, longitude: -122.1411},
'San Francisco': {latitude: 37.7691, longitude: -122.4449},
'St. John\'s': {latitude: 47.5500, longitude: -52.6667},
'New York': {latitude: 40.7143, longitude: -74.0060},
'Twillingate': {latitude: 49.6500, longitude: -54.7500},
'Ottawa': {latitude: 45.4167, longitude: -75.7000},
'Calgary': {latitude: 51.0833, longitude: -114.0833},
'Mumbai': {latitude: 18.9750, longitude: 72.8258}
}
geo.addLocations(locationSet, function(err, reply){
if(err) console.error(err)
else console.log('added locations:', reply)
})
geo.location('Toronto', function(err, location){
if(err) console.error(err)
else console.log('Location for Toronto is: ', location.latitude, location.longitude)
})
Or for multiple locations:
geo.locations(['Toronto', 'Philadelphia', 'Palo Alto', 'San Francisco', 'Ottawa'], function(err, locations){
if(err) console.error(err)
else {
for(var locationName in locations){
console.log(locationName + "'s location is:", locations[locationName].latitude, locations[locationName].longitude)
}
}
})
Now you can look for locations that exist approximately within a certain distance of any particular coordinate in the system. This method will return an Array
of location names [locationNameA, locationNameB, locationNameC]
.
// look for all points within ~5000m of Toronto.
geo.nearby({latitude: 43.646838, longitude: -79.403723}, 5000, function(err, locations){
if(err) console.error(err)
else console.log('nearby locations:', locations)
})
Or, if you're more particular about how you'd like your results returned, pass in some options
. Note that by passing in options, the method will return an Array
of location
objects of the form {key: locationName, latitude: 47.6838, longitude: -79.403, distance: 287.22 ... etc}
etc.
var options = {
withCoordinates: true, // Will provide coordinates with locations, default false
withHashes: true, // Will provide a 52bit Geohash Integer, default false
withDistances: true, // Will provide distance from query, default false
order: 'ASC', // or 'DESC' or true (same as 'ASC'), default false
units: 'm', // or 'km', 'mi', 'ft', default 'm'
count: 100, // Number of results to return, default undefined
accurate: true // Useful if in emulated mode and accuracy is important, default false
}
// look for all points within ~5000m of Toronto with the options.
geo.nearby({latitude: 43.646838, longitude: -79.403723}, 5000, options, function(err, locations){
if(err) console.error(err)
else console.log('nearby locations:', locations)
})
If you know the name of a location that you'd like to do a nearby search within, instead of passing in a point, just pass in a locationName
as the first argument:
geo.nearby('Toronto', 5000, options, function(err, locations){
if(err) console.error(err)
else console.log('nearby locations:', locations)
})
Of course you may need to remove some points from your set as users/temporary events/whatever no longer are part of the set.
geo.removeLocation('New York', function(err, reply){
if(err) console.error(err)
else console.log('removed location:', reply)
})
// OR Quicker for Bulk Removals
geo.removeLocations(['New York', 'St. John\'s', 'San Francisco'], function(err, reply){
if(err) console.error(err)
else console.log('removed locations', reply)
})
You can initialize georedis
with a specific redis client instance, but you can also specify a ZSET name to use when storing/querying locations instead of the default geo:locations
. If you want to force using emulated mode even if your redis server supports native geo commands, just set nativeGeo
to false
.
var redis = require('redis'),
client = redis.createClient()
var geo = require('georedis').initialize(client, {
zset: 'mySpecialLocationsSet',
nativeGeo: false
})
If you have different sets of coordinates, you can store and query them separately by adding a new set.
var people = geo.addSet('people')
var places = geo.addSet('places')
var peopleLocations = {
'John': {latitude: 43.6667, longitude: -79.4167},
'Shankar': {latitude: 39.9523, longitude: -75.1638},
'Cynthia': {latitude: 37.4688, longitude: -122.1411},
'Chen': {latitude: 37.7691, longitude: -122.4449}
}
var placeLocations = {
'Toronto': {latitude: 43.6667, longitude: -79.4167},
'Philadelphia': {latitude: 39.9523, longitude: -75.1638},
'Palo Alto': {latitude: 37.4688, longitude: -122.1411},
'San Francisco': {latitude: 37.7691, longitude: -122.4449},
'St. John\'s': {latitude: 47.5500, longitude: -52.6667}
}
people.addLocations(peopleLocations, function(err, reply){
if(err) console.error(err)
else console.log('added people:', reply)
})
places.addLocations(placeLocations, function(err, reply){
if(err) console.error(err)
else console.log('added places:', reply)
})
// will find all PEOPLE ~5000m from the passed in coordinate
people.nearby({latitude: 43.646838, longitude: -79.403723}, 5000, function(err, people){
if(err) console.error(err)
else console.log('people nearby:', people)
})
// will find all PLACES ~5000m from the passed in coordinate
places.nearby({latitude: 43.646838, longitude: -79.403723}, 5000, function(err, places){
if(err) console.error(err)
else console.log('places nearby:', places)
})
Because javascript doesn't guarantee sorted sets, it makes more sense for the nearby
method to by default be an Array
. But you can convert this array to an object by simply using the locationSet
property on the returned Array
.
geo.nearby('Toronto', 5000, options, function(err, locations){
if(err) console.error(err)
else console.log('nearby locations as a Set:', locations.locationSet)
})
This object will have the form:
{
'locationNameA': {
latitude: xx,
longitude: xx,
distance: xx
},
'locationNameB': {
latitude: xx,
longitude: xx,
distance: xx
},
// etc...
}
If you no longer need one of your newly created sets, you can just delete it. Either of the following methods will remove the set from redis and destroy its contents. If you add locations to that set again it will recreate the set on redis and you can use as usual.
// will delete the people set and its contents
people.delete()
// OR
geo.deleteSet('people')
Initialize the module with a redis client.
zset
String: Defaultgeo:locations
. Set this option to specify a zset name to use to store location values.nativeGeo
Boolean: Defaulttrue
if Redis supports geo commands,false
if not. Force tofalse
if you don't want to make use of native geo commands for some reason. Forcing totrue
on non-supported versions of redis will likely cause errors.
var geo = require('georedis').initialize(client, {
zset: 'locations',
nativeGeo: false
})
This method will return a subset that can be queried and hold a unique set of locations from the main set. It will store these new locations in a new redis zset with a unique name related to the parent set (eg. geo:locations:people
).
This method will delete a subset and its contents. You should use the callBack to check for errors or to wait for confirmation that the set is deleted, but this is probably not necessary.
Add a new coordinate to your set.
Adds a set of new coordinates to your set. Use this method for bulk additions, as it is much faster than individual adds. The locationSet
must be in the form:
var locationSet = {
'locationA': {latitude: locationA_latitude, longitude: locationA_lattude},
'locationB': {latitude: locationB_latitude, longitude: locationB_lattude},
'locationC': {latitude: locationC_latitude, longitude: locationC_lattude}
}
Update a coordinate to your set.
Same syntax as addLocations
. Updates all locations passed.
Retrieve the latitude and longitude of a specific named location. Returns an object with name
, latitude
and longitude
properties. latitude
and longitude
will be null if the location does not exist.
Retrieve the latitude and longitude of a list of specific named locations. Returns a set of objects with location names as property names, each with latitude
and longitude
properties. latitude
and longitude
will be null if the location does not exist.
Remove the specified coordinate by name.
Remove a set of coordinates by name. locationNameArray
must be of the form [nameA,nameB,nameC,...,nameN]
.
Get the distance between two locations. Takes two locationName
s, and returns the distance between.
units
String: Default'm'
. Instead of returning distance in meters, return the distance in a unit of your choice:['m', km', 'mi', 'ft']
.
Removes all locations and deletes the zSet from Redis. You should use the callBack to check for errors or to wait for confirmation that the set is deleted, but this is probably not necessary.
First argument can either be an Object with latitude
and longitude
, or a String of the locationName
. Returns an Array of either locationName
s (if no extra properties are needed) or locations
(if optional properties are requested in options).
units
String: Default'm'
. Will consider these units for alldistance
related data.withCoordinates
Boolean: Defaultfalse
. Will providelatitude
andlongitude
properties to returnedlocations
.withDistances
Boolean: Defaultfalse
. Will providedistance
property with the distance this point is from the queried point.withHashes
Boolean: Defaultfalse
. Will provide ahash
property containing a base32 geohash to the returnedlocations
.order
String|Boolean: Defaultfalse
. Will order the nearby locationsArray
by distance from the queried point.true|'ASC'
or'DESC'
.accurate
Boolean: Defaultfalse
. If your Redis server doesn't have native geo commands, you can enable this option to ensure that results are within the querieddistance
.count
Number: Defaultunlimited
. If you'd like to limit the results to a certain number, you can. Note that this is not guaranteed to necessarily reduce compulational load at all.
The same as geo.nearby except that the accurate
option is always true
.
The MIT License (MIT)
Copyright (c) 2015 Arjun Mehta