Search code examples
mongodbgeolocationmongodb-querygeojson

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.


Solution

  • 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.