-
-
Save PascalAnimateur/b73617f0a27475fd4ccb to your computer and use it in GitHub Desktop.
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(); | |
}); | |
}); | |
}; |
module.exports.models = { | |
// Geospatial queries require MongoDB connection (sails-mongo) | |
connection: 'someMongodbServer' | |
}; |
/* 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); | |
}); | |
}); | |
} | |
}; |
/* 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); | |
}); | |
} | |
}; |
I've updated this to fix the order of coordinates in the geoLocation array... these should be [ lng, lat ] in your records as required by the WGS84 standard used by mongodb. If you experience wrong / funky results from geoNear / findNear (i.e. maxDistance behaving like an ellipse instead of a circle), this is your problem right there. It would still work for plotting points on a map, but the geospatial queries wouldn't have any kind of precision.
Implemented your update and everything is working perfectly now, thanks!
P.S GitHub didn't notify me of your comment, so thanks again for reaching out directly.
@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:
// Location.js
module.exports = {
attributes: {
van_id: {
type: 'string',
required: true,
index: true
},
coordinates: {
type: 'json',
required: true
}
}
}
I would love to have some thoughts on what you think the best way to handle this would be? Thanks
@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();
});
+1 thanks for this, been looking for something like this for a while