Mongodb $geoIntersects giving wrong query results - mongodb

I'm facing a rather strange issue with mongodb when I try to get which of my stored polygons intersects with the given area and its throwing results when it should't.
This is what I have stored:
It is the only one element stored in mongo and looks like this in a map:
Now this is the query (among others) that is giving me trouble :
db.getCollection('collection').find({"geometry": {"$geoIntersects": {"$geometry":{"coordinates":[[
[
-25.13671875,
16.636191878397664
],
[
-21.796875,
16.636191878397664
],
[
-21.796875,
19.145168196205297
],
[
-25.13671875,
19.145168196205297
],
[
-25.13671875,
16.636191878397664
]]],"type":"Polygon"}}}})
which in a map looks like this (including the previous mentioned area):
As you can see in the map (or doing simple math calc with the coordinates) those areas do not intersect at all, but mongo insists they do as the query gives me results.
I've tried using the following in $geometry operator but the results are the same
crs: {
type: "name",
properties: { name: "urn:x-mongodb:crs:strictwinding:EPSG:4326" }
}
Last but not least, if the area in the query is east/west/south of the stored one the results are the expected ones, the problem is in areas in close vicinity north of the main polygon.
What am I doing wrong?
Please let me know if more data is needed and thank you all for your time.

Related

Filter Documents by Distance Stored in Document with $near

I am using the following example to better explain my need.
I have a set of points(users) on a map and collection schema is as below
{
location:{
latlong:[long,lat]
},
maxDistance:Number
}
i have another collection with events happening in the area. schema is given below
{
eventLocation:{
latlong:[long,lat]
}
}
now users can add their location and the maximum distance they want to travel for to attend an event and save it.
whenever a new event is posted , all the users satisfying their preferences will get a notification. Now how do i query that. i tried following query on user schema
{
$where: {
'location.latlong': {
$near: {
$geometry: {
type: "Point",
coordinates: [long,lat]
},
$maxDistance: this.distance
}
}
}
}
got an error
error: {
"$err" : "Can't canonicalize query: BadValue $where got bad type",
"code" : 17287
}
how do i query the above case as maxDistance is defined by user and is not fixed. i am using 2dsphere index.
Presuming you have already worked out to act on the event data as you recieve it and have it in hand ( if you have not, then that is another question, but look at tailable cursors ), then you should have an object with that data for which to query the users with.
This is therefore not a case for JavaScript evaluation with $where, as it cannot access the query data returned from a $near operation anyway. What you want instead is $geoNear from the aggregation framework. This can project the "distance" found from the query, and allow a later stage to "filter" the results against the user stored value for the maximum distance they want to travel to published events:
// Represent retrieved event data
var eventData = {
eventLocation: {
latlong: [long,lat]
}
};
// Find users near that event within their stored distance
User.aggregate(
[
{ "$geoNear": {
"near": {
"type": "Point",
"coordinates": eventData.eventLocation.latlong
},
"distanceField": "eventDistance",
"limit": 100000,
"spherical": true
}},
{ "$redact": {
"$cond": {
"if": { "$lt": [ "$eventDistance", "$maxDistance" ] },
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
]
function(err,results) {
// Work with results in here
}
)
Now you do need to be careful with the returned number, as since you appear to be storing in "legacy coordinate pairs" instead of GeoJSON, then the distance returned from this operation will be in radians and not a standard distance. So presuming you are storing in "miles" or "kilometers" on the user objects then you need to calculate via the formula mentioned in the manual under "Calculate Distances Using Spherical Geometry" as mentioned in the manual.
The basics are that you need to divide by the equatorial radius of the earth, being either 3,963.2 miles or 6,378.1 kilometers to convert for a comparison to what you have stored.
The alternate is to store in GeoJSON instead, where there is a consistent measurement in meters.
Assuming "kilometers" that "if" line becomes:
"if": { "$lt": [
"$eventDistance",
{ "$divide": [ "$maxDistance", 6,378.1 ] }
]},
To reliably compare your stored kilometer value to the radian result retured.
The other thing to be aware of is that $geoNear has a default "limit" of 100 results, so you need to "pump up" the "limit" argument there to the number for expected users to possibly match. You might even want to do this in "range lists" of user id's for a really large system, but you can go as big as memory allows within a single aggreation operation and possibly add allowDiskUse where needed.
If you don't tune that parameter, then only the nearest 100 results ( default ) will be returned, which may well no even suit your next operation of filtering those "near" the event to start with. Use common sense though, as you surely have a max distance to even filter out potential users, and that can be added to the query as well.
As stated, the point here is returning the distance for comparison, so the next stage is the $redact operation which can fiter the user's own "travel distance" value against the returned distance from the event. The end result gives only those users that fall within their own distance contraint from the event who will qualify for notification.
That's the logic. You project the distance from the user to the event and then compare to the user stored value for what distance they are prepared to travel. No JavaScript, and all native operators that make it quite fast.
Also as noted in the options and the general commentary, I really do suggest you use a "2dsphere" index for accurate spherical distance calculation as well as converting to GeoJSON storage for your coordinate storage in your database Objects, as they are both general standards that produce consistent results.
Try it without embedding your query in $where: {. The $where operator is for passing a javascript function to the database, which you don't seem to want to do here (and is in fact something you should generally avoid for performance and security reasons). It has nothing to do with location.
{
'location.latlong': {
$near: {
$geometry: {
type: "Point",
coordinates: [long,lat]
},
$maxDistance: this.distance
}
}
}

Mongodb Point inside a Polygon not getting results - Mongoid + Rails

I'm trying to check if a point is inside a polygon with mongodb (mongodb 2.6).
I'm inserting the data in de collection like this:
db.areas.insert({"polygons":
{"type":"Polygon",
coordinates:
[[[-23.0651232, -45.6374645],
[-23.0557255, -45.6435585],
[-23.0370072, -45.6383228],
[-23.0299772, -45.6351471],
[-23.0025649, -45.6480217],
[-22.9723022, -45.6554031],
[-22.9340493, -45.6032181],
[-22.9353140, -45.5925751],
[-22.9383177, -45.5855370],
[-22.9601320, -45.5560112],
[-22.9645577, -45.5597878],
[-22.9938740, -45.5675125],
[-22.9939530, -45.5690575],
[-23.0217620, -45.5712891],
[-23.0241319, -45.5719757],
[-23.0258697, -45.5711174],
[-23.0268966, -45.5721474],
[-23.0656365, -45.6372499],
[-23.0651232, -45.6374645]
]]
}
});
And querying like this:
db.areas.find({polygons:
{$geoIntersects:
{$geometry:{ "type" : "Point",
"coordinates" : [ -22.112, -45.56 ] }
}
}
});
But I'm getting no result. Any ideas?
I've looked this links:
mongodb check if point is in polygon
Search all polygons that contains a series of points in mongodb
Any help will be appreciated
I found the answer.
The problemas was that when I tried to save, the coordinates of the Polygon from a Rails form it was saved as String and not as Double.
I chaged that before save an the problem was solved.

mongodb and geospatial schema

im breaking my head with mongo and geospatial,
so maybe someone has some idea or solution how to solve this:
my object schema is like this sample for geoJSON taken from http://geojson.org/geojson-spec.html.
{
"name":"name",
"geoJSON":{
"type":"FeatureCollection",
"features":[
{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[100,0],[101,0],[101,1],[100,1],[100,0]]]},"properties":{}},
{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[100,0],[101,0],[101,1],[100,1],[100,0]]]},"properties":{}},
{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[100,0],[101,0],[101,1],[100,1],[100,0]]]},"properties":{}}
]}}
additional info: I'm using spring data but that shouldn't influence the answer.
main problem is how/where to put indexes in this schema. I need to make a query to find all documents for given Point if some polygon intersects.
thanks in advance.
By creating a 2d or 2dsphere index on geoJSON.features.geometry you should be able to create an index covering all of the geoJSON-objects.
To get all documents where at least one of the sub-object in the features array covers a certain point, you can use the $geoIntersects operator with a geoJSON Point:
db.yourcollection.find(
{ `geoJSON.features.geometry` :
{ $geoIntersects :
{ $geometry :
{ type : "Point" ,
coordinates: [ 100.5 , 0.5 ]
}
}
}
}
)

Use property for calculation in mongodb

specialists,
I have a document with several properties, one bein an lon/lat-array with a 2d index on it.
Another property is a radius property.
What I want:
{
geo:{
$within: { $center: [[ 9.078597000000036,50.580947], 1+radius]}
}
}
Is this available with mongodb? No matter what I am searching in google I am always directed to the mongodb documentation about geospatial indexes but my question is not getting answered.
Not sure if i understand you, but the docs page about geospatial indexes give a an example of a query like yours:
db.places.find( { geo: { $centerSphere: [ [long,lat ] ,
radius ] } } )
searches places collection for everything withing radius distance (in radians) from the point [long, lat].

Error creating Geospatial index in MongoDB

I have a collection ('stops') with 14000 records imported from a CSV. Included in each record is some geospatial data, which I've converted into an array, so each essentially looks like this:
{
_id: ...,
// other fields
"loc": [
153.019073,
-27.467834
]
}
When I run db.stops.ensureIndex({ loc: '2d' }) it gives the error:
location object expected, location array not in correct format
I guess that something's wrong in one of the fields, but I can't figure out which one. Any ideas?
The problem was that there was a single record which wasn't imported correctly (probably an empty line at the end of the file).
To find it (and later remove it), I used the $where operator:
db.stops.find({ $where: 'typeof this.loc[0] != "number"' })