Last active
February 22, 2021 20:19
-
-
Save PascalAnimateur/b73617f0a27475fd4ccb to your computer and use it in GitHub Desktop.
Geospatial example in Sails.js using native MongoDB query
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports.bootstrap = function(cb) { | |
// Ensure we have 2dsphere index on coordinates attribute of Place. | |
sails.models.place.native(function (err, collection) { | |
collection.ensureIndex({ coordinates: '2dsphere' }, function () { | |
// It's very important to trigger this callback method when you are finished | |
// with the bootstrap! (otherwise your server will never lift, since it's waiting on the bootstrap) | |
cb(); | |
}); | |
}); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports.models = { | |
// Geospatial queries require MongoDB connection (sails-mongo) | |
connection: 'someMongodbServer' | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Place model */ | |
module.exports = { | |
attributes: { | |
name: { | |
type: 'string', | |
required: true | |
}, | |
// This is the attribute used for the place's geolocation. | |
// Be careful to store it as [ lng, lat ] or else geoNear queries will give imprecise results. | |
coordinates: { | |
type: 'json', | |
required: true | |
} | |
}, | |
/** | |
* Find places closer than a certain distance (in km) from a specified location [ lng, lat ]. | |
* @param conditions | |
* JSON object should look like this: | |
* { | |
* lng: -72.213, | |
* lat: 45.012, | |
* maxDistance: 100, | |
* limit: 20, | |
* } | |
* | |
* @param callback (err, results) | |
* Returns an array of results (ordered by increasing distance), it looks like this: | |
* [ | |
* { | |
* dis: 10.321, | |
* obj: { JSON object of Place } | |
* }, | |
* { | |
* dis: 20.123, | |
* obj: { JSON object of Place } | |
* } | |
* ] | |
*/ | |
findNear: function (conditions, callback) { | |
Place.native(function (err, collection) { | |
if (err) return callback(err); | |
collection.geoNear({ | |
type: "Point" , | |
coordinates: [ conditions.lng, conditions.lat ] | |
}, { | |
limit: conditions.limit || 30, | |
maxDistance: conditions.maxDistance * 1000.0, | |
distanceMultiplier: 0.001, | |
spherical : true | |
}, function (err, places) { | |
if (err) return callback(err); | |
return callback(null, places.results); | |
}); | |
}); | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Place controller */ | |
module.exports = { | |
/** | |
* Create two dummy places. | |
*/ | |
dummy: function (req, res) { | |
Place.create({ | |
name: 'New Richmond', | |
coordinates: [ -65.8716805, 48.1804069 ] | |
}, function (err, place) { | |
if (err) return res.negotiate(err); | |
console.log('Place created: ', place); | |
res.ok(); | |
}); | |
Place.create({ | |
name: 'Montreal', | |
coordinates: [ -73.5647636, 45.5158157 ] | |
}, function (err, place) { | |
if (err) return res.negotiate(err); | |
console.log('Place created: ', place); | |
res.ok(); | |
}); | |
}, | |
/** | |
* Find places closer than a certain distance (in km) from a specified location [ lng, lat ]. | |
*/ | |
search: function (req, res) { | |
// TODO: Default values and conditions validation should go in Place.findNear | |
var conditions = { | |
lng: parseFloat(req.param('lng')) || 0, | |
lat: parseFloat(req.param('lat')) || 0, | |
maxDistance: parseFloat(req.param('maxDistance')) || 1000, | |
limit: req.param('limit') || 30, | |
}; | |
Place.findNear(conditions, function (err, results) { | |
if (err) return res.negotiate(err); | |
return res.json(results); | |
}); | |
} | |
}; |
@theCrab: For the customer app you need some way of getting a location from IP, you could use something like node-freegeoip.
Seems like an ambitious project for a newcomer to Sails.. good luck!
Thanks @PascalAnimateur. Works perfectly!
module.exports.bootstrap = function(cb) {
// Ensure we have 2dsphere index on coordinates attribute of Place.
sails.models.modelName.native(function (err, collection) {
collection.ensureIndex({ coordinates: '2dsphere' }, function () {
// It's very important to trigger this callback method when you are finished
// with the bootstrap! (otherwise your server will never lift, since it's waiting on the bootstrap)
cb();
});
});
};
its giving me error
error: Bootstrap encountered an error: (see below)
error: TypeError: Cannot read property 'native' of undefined
at Object.module.exports.bootstrap (E:\codes\backend\Masterbackend\config\bootstrap.js:17:32)
at Sails.runBootstrap (C:\Users\hp\AppData\Roaming\npm\node_modules\sails\lib\app\private\bootstrap.js:44:25)
at Sails.wrapper [as runBootstrap] (C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\@sailshq\lodash\lib\index.js:3250:19)
at Sails.initialize (C:\Users\hp\AppData\Roaming\npm\node_modules\sails\lib\app\private\initialize.js:68:9)
at wrapper (C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\@sailshq\lodash\lib\index.js:3250:19)
at C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:713:13
at iterate (C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:262:13)
at C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:274:29
at C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:44:16
at C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:718:17
at C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:167:37
at C:\Users\hp\AppData\Roaming\npm\node_modules\sails\lib\app\load.js:184:13
at C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:52:16
at C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:548:17
at C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:542:17
at _arrayEach (C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:85:13)
at Immediate.taskComplete [as _onImmediate] (C:\Users\hp\AppData\Roaming\npm\node_modules\sails\node_modules\async\lib\async.js:541:13)
at runCallback (timers.js:800:20)
at tryOnImmediate (timers.js:762:5)
at processImmediate [as _immediateCallback] (timers.js:733:5)
any help ????
Found Out Solution
module.exports.bootstrap = function(cb) {
// Ensure we have 2dsphere index on coordinates attribute of Place.
modelName.native(function (err, collection) {
collection.ensureIndex({ coordinates: '2dsphere' }, function () {
// It's very important to trigger this callback method when you are finished
// with the bootstrap! (otherwise your server will never lift, since it's waiting on the bootstrap)
cb();
});
});
};
using native via modelName solve the problem
There are three main things to be considered:
- The 2dsphere index needs to be created properly (As described in the bootstrap.js.
- The name of the coordinates fields must be exactly coordinates (not coord, coordinate, position, ...)
- The positions need to be inserted with [longitude, latitude] not with [latitude, longitude].
Thanks, this gist is really helpful!
Since .native() has been deprecated, the new bootstrap.js like below:
// Ensure we have 2dsphere index on coordinates attribute of Place.
var db = Place.getDatastore().manager;
var collection = db.collection('Place');
collection.ensureIndex({ coordinates: '2dsphere' }, () => {
// It's very important to trigger this callback method when you are finished
// with the bootstrap! (otherwise your server will never lift, since it's waiting on the bootstrap)
cb();
});
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Jamie452 @PascalAnimateur I am trying to do an app that tracks the user position and shows delivery vans that are nearest to them. I don't need to store the van location as its constantly changing. I am using Sails as a backend. The front end is 2 apps. 1 for the van driver which updates Sails every time its location changes significantly (0.5 kilometres). The other is a would be customer app and that just displays map pins of the closest van to them.
I am not very sure what this demands as its my first time doing such an app. After integrating your example above, my app looks like this:
I would love to have some thoughts on what you think the best way to handle this would be? Thanks