Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Query to Match a Polygon that contains a Point

The documents in my mongodb collection look like this:

{
"_id" : ObjectId("5562d6831683523f449e7d85")
"geometry" : {
"type" : "Polygon",
"coordinates" : 
    [[
    [-122.4,37.81],
    [-132.9, 39.9],
    [-122.28, 37.80],
    [-124.18, 39.81]
    ]]
}}

I have a point (lat long pair), which I'll call x and y. I need to see if the coordinates of the document create a polygon such that these coordinates are inside that polygon.

So, of course I need to compute the line describing each edge of the polygon, etc., and then see if the coordinates of the point are within that. However, for now, let's just assume we want to query documents that have x and y within the maximum and minimum lat long values of the entire array.

Here is how I tried to query this:

db.<collection-name>.find(
    {'geometry.coordinates':
        {$elemMatch:
            {
                [{$gt:-122.1}, {$lt:-122.0 }], [{$gt: 37.89 },{$lt: 37.91}]
            }
        }
    }
)

however, the result is: Unexpected token [. I tried to follow the example here.

How do I query an array like this, where there are conditions on each element of the array in the document? thanks.

like image 940
makansij Avatar asked Jul 23 '15 01:07

makansij


People also ask

How do you check if a polygon contains a point?

One simple way of finding whether the point is inside or outside a simple polygon is to test how many times a ray, starting from the point and going in any fixed direction, intersects the edges of the polygon. If the point is on the outside of the polygon the ray will intersect its edge an even number of times.


1 Answers

What you seem to be trying to do by matching array elements is actually "Finding a Polygon that contains a Point", which is why I changed your question title.

To accomplish this you are better off using the geoSpatail features of MongoDB rather than trying to work out the bounds yourself. With valid GeoJSON data the query is quite simple using the $geoIntersects operator.

To demonstrate, I'll first set up a collection, with some Polygon data:

db.areas.insert([
    {
        "name": "San Jose",
        "geometry": {
            "type": "Polygon",
            "coordinates": [[
                [ -122.20916748046876, 37.13404537126446  ],
                [ -122.20916748046876, 37.496652341233364 ],
                [ -121.65710449218749, 37.496652341233364 ],
                [ -121.65710449218749, 37.13404537126446  ],
                [ -122.20916748046876, 37.13404537126446  ]
             ]]
         }
    },
    {
        "name": "San Franciso",
        "geometry": {
            "type": "Polygon",
            "coordinates": [[
                [ -122.73651123046874, 37.58811876638322 ],
                [ -122.73651123046874, 37.89219554724437 ],
                [ -122.28332519531249, 37.89219554724437 ],
                [ -122.28332519531249, 37.58811876638322 ],
                [ -122.73651123046874, 37.58811876638322 ]
            ]]
        }
    }
])

Then ( though not required for $geoIntersects specifically ) when working with geoSpatial data it is best to have an "index" defined. The one that makes sense for real GeoJSON locations is "2dsphere". The index is created on the field that contains the "root" of the GeoJSON data, which is in this case called "geometry":

db.areas.createIndex({ "geometry": "2dsphere" })

Then all you need to do is supply a .find() query. I'm using the coordinates for "San Francisco city" here:

db.areas.find({
    "geometry": {
        "$geoIntersects": {
             "$geometry": {
                "type": "Point",
                "coordinates": [ 
                    -122.45361328124999, 
                    37.76420119453823
                ]
             }
         }
    }
})

Which of course returns the "Polyon" defining the "San Franciso" area since that "Point" lies within that object.

    {
        "name": "San Franciso",
        "geometry": {
            "type": "Polygon",
            "coordinates": [[
                [ -122.73651123046874, 37.58811876638322 ],
                [ -122.73651123046874, 37.89219554724437 ],
                [ -122.28332519531249, 37.89219554724437 ],
                [ -122.28332519531249, 37.58811876638322 ],
                [ -122.73651123046874, 37.58811876638322 ]
            ]]
        }
    }

That is all there is to finding whether your "Point" lies within a "Polygon" you have stored in your collection.

Also look at tools such as geojsonlint.com and geojson.io (examples, not endorsements) in order to validate and visualise your data, which from your example does not provide a well formed Polygon.

like image 156
Blakes Seven Avatar answered Sep 27 '22 21:09

Blakes Seven