How to define a circle using GeoJson? - mongodb

I want to use geometry in Mongodb.
But circle is not supported in geojson according to the geojson.org

I had exactly the same problem, the solution is to create a polygon that roughly approximates a circle (imagine a polygon with 32+ edges).
I wrote a module that does this. You can use it like this:
const circleToPolygon = require('circle-to-polygon');
const coordinates = [-27.4575887, -58.99029]; //[lon, lat]
const radius = 100; // in meters
const numberOfEdges = 32; //optional that defaults to 32
let polygon = circleToPolygon(coordinates, radius, numberOfEdges);

You will need to model it as a point and then store the radius in another field. If you want to test whether or not something is inside of that circle, you will need to use the proximity spatial index as discussed here

{
<location field>: {
$geoWithin: { $centerSphere: [ [ <x>, <y> ], <radius> ] }
}
}
https://docs.mongodb.com/manual/reference/operator/query/centerSphere/
Since v1.8

Another approach to it. In this case, I have used mongoose one of the most popular distribution of MongoDB to add a circle to a map with a radius and then query using an external parameter and assessing if it's inside a circle or outside the circle.
This example also has commented section for polygon, where if you have saved a polygon and you want to search if the point exists inside a polygon, you can do that too. Also, there is an upcoming section for a full integration of front end and backend for a complete geofence experience.
The code
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var assert = require('assert');
console.log('\n===========');
console.log(' mongoose version: %s', mongoose.version);
console.log('========\n\n');
var dbname = 'testing_geojsonPoint';
mongoose.connect('localhost', dbname);
mongoose.connection.on('error', function() {
console.error('connection error', arguments);
});
// schema
var schema = new Schema({
loc: {
type: {
type: String
},
coordinates: []
},
radius : {
type : 'Number'
}
});
schema.index({
loc: '2dsphere'
});
var A = mongoose.model('A', schema);
// mongoose.connection.on('open', function() {
// A.on('index', function(err) {
// if (err) return done(err);
// A.create({
// loc: {
// type: 'Polygon',
// coordinates: [
// [
// [77.69866, 13.025621],
// [77.69822, 13.024999, ],
// [77.699314, 13.025025, ],
// [77.69866, 13.025621]
// ]
// ]
// }
// }, function(err) {
// if (err) return done(err);
// A.find({
// loc: {
// $geoIntersects: {
// $geometry: {
// type: 'Point',
// coordinates: [77.69979,13.02593]
// }
// }
// }
// }, function(err, docs) {
// if (err) return done(err);
// console.log(docs);
// done();
// });
// });
// });
// });
mongoose.connection.on('open', function() {
A.on('index', function(err) {
if (err) return done(err);
A.create({
loc: {
type: 'Point',
coordinates: [77.698027,13.025292],
},
radius : 115.1735664276843
}, function(err, docs) {
if (err) return done(err);
A.find({
loc: {
$geoNear: {
$geometry: {
type: 'Point',
coordinates: [77.69735,13.02489]
},
$maxDistance :docs.radius
}
}
}, function(err, docs) {
if (err) return done(err);
console.log(docs);
done();
});
});
});
});
function done(err) {
if (err) console.error(err.stack);
mongoose.connection.db.dropDatabase(function() {
mongoose.connection.close();
});
}
See full example in action

Related

findOneAndUpdate for a 2dsphere

const sessionId = uuid.v4();
//Session.
io.on("connection", (socket) => {
socket.on("locationData", async (data) => {
const latitude = data.latitude;
const longitude = data.longitude;
Session.findOneAndUpdate(
{ sessionId: sessionId },
{
$set: {
location: { coordinates: [longitude, latitude] },
speed: data.speed,
},
},
{ upsert: true, new: true },
(err) => {
if (err) {
console.error(err);
return;
}
console.log("Data inserted/updated");
}
);
console.log(data);
});
});
im trying to get realtime location data with socket io and put it in my DB, it was working perfect till i realised i need to add index:"2dsphere" to the coords, apparently the findOneAndUpdate doesnt work with a geospatial point. what should i use in this case please
const sessionId = uuid.v4();
//Session.
io.on("connection", (socket) => {
socket.on("locationData", async (data) => {
const latitude = data.latitude;
const longitude = data.longitude;
Session.findOneAndUpdate(
{ sessionId: sessionId },
{
$set: {
location: { coordinates: [longitude, latitude] },
speed: data.speed,
},
},
{ upsert: true, new: true },
(err) => {
if (err) {
console.error(err);
return;
}
console.log("Data inserted/updated");
}
);
console.log(data);
});
});
It worked by modifying my code:
location: {
type: "Point",
coordinates: [longitude, latitude],
},

Can't canonicalize query :: caused by :: invalid argument in geo near query

Hoping someone can help with this because I've been working on it all morning
I'm trying to use MongoDB Near to find locations within a radius of a given point on a map. I get the following error:
{"name":"MongoError","message":"Can't canonicalize query :: caused by
:: invalid argument in geo near query: spherical","$err":"Can't
canonicalize query :: caused by :: invalid argument in geo near query:
spherical","code":2,"ok":0}
This is the mongoose schema for locations:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var LocationSchema = new Schema({
title: String,
coordinates: {
type: [Number],
index: '2dsphere'
},
created: {
type: Date,
default: Date.now
}
});
mongoose.model('Location', LocationSchema);
and this is the route where I try to use "Near" - basically I enter a set of coordinates in a form where the target is the following
router.post('/nearme', function (req, res, next) {
// Setup limit
var limit = req.body.limit || 10;
// Default max distance to 10 kilometers
var maxDistance = req.body.distance || 10;
// Setup coords Object = [ <longitude> , <latitude> ]
var coords = [];
// Create the array
coords[0] = req.body.longitude;
coords[1] = req.body.latitude;
// find a location
Location.find({
'coordinates': {
$near: {
$geometry: {
type: "Point",
coordinates: coords
},
// distance to radians
$maxDistance: maxDistance * 1609.34, spherical: true
}
}
}).limit(limit).exec(function (err, stores) {
if (err) {
return res.status(500).json(err);
}
//res.status(200).json(stores);
res.render('locations', {
title: 'Locations',
location: stores,
lat: -23.54312,
long: -46.642748
});
});
Also - I indexed the location collection as follows:
db.locations.ensureIndex({ 'coordinates' : '2dsphere'})
and as far as I can tell, it looks right:
Things I've tried:
using "geoNear" instead of geometry.
removing spherical:true
searching stackoverflow for similar errors - none seem to have the same issue
Any help you could provide would be amazing. Thank you!
Thanks to Joe I reviewed the MongoDB docs and updated the /nearme route as follows (changes made in $near):
router.post('/nearme', function (req, res, next) {
// Setup limit
var limit = req.body.limit || 10;
// Default max distance to 10 kilometers
var maxDistance = req.body.distance || 10;
// Setup coords Object = [ <longitude> , <latitude> ]
var coords = [];
// Create the array
coords[0] = req.body.longitude;
coords[1] = req.body.latitude;
// find a location
Location.find({
'coordinates': {
$near: {
$geometry: {
type: "Point" ,
coordinates: coords
},
$maxDistance: maxDistance * 1609.34,
$minDistance: 0
}
}
}).limit(limit).exec(function (err, stores) {
if (err) {
return res.status(500).json(err);
}
//res.status(200).json(stores);
res.render('locations', {
title: 'Locations',
location: stores,
lat: -23.54312,
long: -46.642748
});
});
});

How do I update an array using an object in mongodb?

I try to add an geojson object to an existing array in mongodb, this is my object that I'd like to add:
const location = {
type: "Feature",
properties: {
description: place.address,
name: place.name
},
geometry: {
coordinates: [
place.latLng.latitude,
place.latLng.longitude
],
type: "Point"
},
userIds: [userId],
id: place.id
}
I tried using this mongodb call without any effect:
db.collection.updateOne(
{ _id: "5e6e32051c9d4400128cba9c" },
{ $push: { features: location } },
function(err, result) {
if (err) {
reject(err);
}
console.log(result);
console.log("Added new location successfully");
resolve(true);
});
This does nothing. Features is an array which should contain geojson objects.
What do I do wrong?
Ok, I found the answer on this page: https://www.quora.com/How-do-I-update-a-document-in-mongodb-using-_id-as-query-parameter
In order to query for an _id you apparently have to convert the _id into an ObjectId first.
So I did this here:
const ObjectID = require('mongodb').ObjectID;
const id = ObjectID("5e6e32051c9d4400128cba9c");
And then:
db.collection.updateOne(
{ _id: id },
{ $push: { features: location } },
function(err, result) {
if (err) {
reject(err);
}
console.log(result);
console.log("Added new location successfully");
resolve(true);
});
This did work! :)

How to make querys when tou have many to many relationships between models?

i am trying to make a game. I need tu create a Match. I think the problem on this Way. The User create a Match. In a third table I save playerId and gameId. When another user join the match, I save again, playerId and gameId. Then, I make a query with player with gameId in common, and start the game.
first, One User may have many Games. second, One Match may have many Games. this is the Match model:
module.exports = {
attributes: {
name: {
type: 'string'
},
description: {
type: 'string'
},
game: {
collection: 'game',
via: 'gameId',
}
}
};
This is the User model:
var bcrypt = require('bcrypt');
module.exports = {
attributes: {
name: {
type:'string'
},
email: {
type: 'email',
required: true,
unique: true
},
password: {
type: 'string',
},
passwordConfirmation: {
type: 'string'
},
passwordEncrypted: {
type: 'string'
},
creator: {
collection: 'game',
via: 'playerId'
},
toJSON: function(){
var obj = this.toObject();
delete obj.password;
delete obj.passwordConfirmation;
delete obj._csrf;
return obj;
}
}, beforeCreate: function(values, next){
console.log("Acabo de entrar a eforeCreate");
var password = values.password;
var passwordConfirmation = values.passwordConfirmation;
if(!password || !passwordConfirmation || password != values.passwordConfirmation) {
var passwordDoesNotMatchError = [{
name: 'passwordDoesNotMatchError',
message: 'Las contraseñas deben coincidir'
}]
return next({
err: passwordDoesNotMatchError
});
}
require('bcrypt').hash(values.password, 10, function passwordEncrypted(err, EncryptedPassword){
values.EncryptedPassword = EncryptedPassword;
next();
});
}
};
This is the Game model:
module.exports = {
attributes: {
gameId: {
model: 'match'
},
playerId: {
model: 'user'
}
}
};
finally, this is my controller:
module.exports = {
createMatch: function(req,res){
var matchObj = {
name: req.param('name'),
description: req.param('description'),
}
Match.create(matchObj, function(err, match){
if(err){
console.log("el error fue: " + err);
return res.send(err);
} console.log("Entro en create");
return res.json(match);
})
var gameObj = {
gameId: 'aclaration: I dont know how do I get the match.id',
playerId: req.session.me
}
Game.create(gameObj,function(err,game){
console.log("entro a GameCreate");
if(err){
return res.send(err);
} return res.json(game);
})
}
};
I can create the Match, but Game.create send this error:
_http_outgoing.js:344 throw new Error('Can\'t set headers after they are sent.'); ^
Error: Can't set headers after they are sent.
Somebody can help me? probably, I have many errors. Thanks.
Couple of things here:
Having an explicit Game model is not required in Sails. It can manage it implicitly, unless you want to store more information than just gameId and userId. So, you can just do away with Game model.
Please refer for async programming: How do I return the response from an asynchronous call?
Below code should work for you. Hope it helps.
module.exports = {
createMatch: function(req, res) {
var matchObj = {
name: req.param('name'),
description: req.param('description'),
};
Match.create(matchObj, function(err, match) {
if (err) {
console.log("el error fue: " + err);
return res.send(err);
}
console.log("Entro en create");
var gameObj = {
gameId: match.id,
playerId: req.session.me
};
Game.create(gameObj, function(err, game) {
console.log("entro a GameCreate");
if (err) {
return res.send(err);
}
return res.json(game);
// return res.json(match);
});
});
}
};

How to update embedded document in mongoose?

I've looked through the mongoose API, and many questions on SO and on the google group, and still can't figure out updating embedded documents.
I'm trying to update this particular userListings object with the contents of args.
for (var i = 0; i < req.user.userListings.length; i++) {
if (req.user.userListings[i].listingId == req.params.listingId) {
User.update({
_id: req.user._id,
'userListings._id': req.user.userListings[i]._id
}, {
'userListings.isRead': args.isRead,
'userListings.isFavorite': args.isFavorite,
'userListings.isArchived': args.isArchived
}, function(err, user) {
res.send(user);
});
}
}
Here are the schemas:
var userListingSchema = new mongoose.Schema({
listingId: ObjectId,
isRead: {
type: Boolean,
default: true
},
isFavorite: {
type: Boolean,
default: false
},
isArchived: {
type: Boolean,
default: false
}
});
var userSchema = new mongoose.Schema({
userListings: [userListingSchema]
});
This find also doesn't work, which is probably the first issue:
User.find({
'_id': req.user._id,
'userListings._id': req.user.userListings[i]._id
}, function(err, user) {
console.log(err ? err : user);
});
which returns:
{ stack: [Getter/Setter],
arguments: [ 'path', undefined ],
type: 'non_object_property_call',
message: [Getter/Setter] }
That should be the equivalent of this mongo client call:
db.users.find({'userListings._id': ObjectId("4e44850101fde3a3f3000002"), _id: ObjectId("4e4483912bb87f8ef2000212")})
Running:
mongoose v1.8.1
mongoose-auth v0.0.11
node v0.4.10
when you already have the user, you can just do something like this:
var listing = req.user.userListings.id(req.params.listingId);
listing.isRead = args.isRead;
listing.isFavorite = args.isFavorite;
listing.isArchived = args.isArchived;
req.user.save(function (err) {
// ...
});
as found here: http://mongoosejs.com/docs/subdocs.html
Finding a sub-document
Each document has an _id. DocumentArrays have a special id method for looking up a document by its _id.
var doc = parent.children.id(id);
* * warning * *
as #zach pointed out, you have to declare the sub-document's schema before the actual document 's schema to be able to use the id() method.
Is this just a mismatch on variables names?
You have user.userListings[i].listingId in the for loop but user.userListings[i]._id in the find.
Are you looking for listingId or _id?
You have to save the parent object, and markModified the nested document.
That´s the way we do it
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Profile.findById(req.params.id, function (err, profile) {
if (err) { return handleError(res, err); }
if(!profile) { return res.send(404); }
var updated = _.merge(profile, req.body);
updated.markModified('NestedObj');
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, profile);
});
});
};