Reputation: 133
I have the folowing code to define a schema:
var mongoose=require('mongoose');
var Schema=mongoose.Schema;
var PostSchema=new Schema({
location:{type:Array,required:false,select:true}
})
PostSchema.index({location:'2dsphere'});
module.exports=mongoose.model('Post',PostSchema);
I want to make location field to be seen like a circle(ex:location:{loc:[latitude,longitude],radius:radius})..I want to make a query to find circles form my db which are intersecting with a polygon.Thanks for any help :)
Upvotes: 0
Views: 1927
Reputation: 50416
To be valid for a "geospatial query" the "location" must be in longitude, latitude order and cannot contain any other coordinates.
Valid formats are
{
"location": [long,lat]
}
Or
{
"location": { "lng": long, "lat": lat }
}
Or GeoJSON
{
"location": {
"type": "Point",
"coordinates": [long,lat]
}
}
Another field such as "radius" is "another field" and cannot be part of the same array.
Ideally follow GeoJSON:
{
"location": {
"type": "Point",
"coordinates": [long,lat]
},
"radius": radius
}
Which in mongoose schema definition can be as simple as:
var geoSchema = new Schema({
"location": {
"type": String,
"coordinates": []
},
"radius": Number
});
When dealing with geospatial data at real "globe" coordinates your index should be "2dsphere", which you optionally define on the schema as :
geoSchema.index({ "location": "2dsphere" })
Since there is no actual support for a "Circle" object in supported GeoJSON then keeping another field as "radius" and storing the "center point" is recommended.
The "big" advantage with GeoJSON over the other "legacy coordinate pairs" formats is that when returning something like a "distance" from a point via geoNear
or $geoNear
then that "distance" is defined in "meters" consistently. This is also how you should be defining any "radius" value in your storage to remain consistent with that result.
With the other storage formats then the result is returned in "radians", for which you probably want to convert and would prefer not to be storing a "radius" of a circle with that as a measurement.
The way you deal with this is, considering data in this form:
{
"locationtype": "circle",
"location": {
"type": "Point",
"coordinates": [1,1]
},
"radius": 4
}
Then you use .aggregate()
with a $geoNear
stage and a $redact
to filter:
db.collection.aggregate([
// Find points or objects "near" and project the distance
{ "$geoNear": {
"near": {
"type": "Point",
"coordinates": [2,2]
},
"distanceField": "distance",
"query": { "locationType": "circle" }
}},
// Logically filter anything outside of the radius
{ "$redact": {
"$cond": {
"if": { "$gt": [ "$distance", "$radius" ] },
"then": "$$PRUNE",
"else": "$$KEEP"
}
}}
])
Now the values used in the query example are just an example, but as stated with "real" longitude and latitude coordinates the "distance" attributes work out as designed and within the "meters" tolerance as mentioned earlier.
The points here are that $geoNear
will both find "near" to the "circle" center poiny no matter what the object type. Not only that but the command here is producing a "projection" of another field in the document here as named in "distanceField". This represents the distance from the circle "center" in "meters".
The second stage here uses $redact
since it is sort of like a $project
and $match
pipeline stage in one. Unlike $match
this operator can evaluate a "logical" condition by comparing fields present in the document. In this case, operations like $$PRUNE
remove the matched document to the "if" condition where true
and "remove" it from results or otherwise $$KEEP
the document where the condition was false
.
In a "nutshell", if "distance" is "greater than" then "radius" of the "circle" then the object "lies outside" of the circle and does not "intersect". Otherwise "it does".
So that is the basics of "defining a 'circle' for geometry in a collection and "using it" to achieve something like the intersection between a "Point" or other type of Object within the "circle" radius.
Upvotes: 12