Geospatial: Finding points outside of a polygon at a distance X

Hi,

Is there any operator that can return points at a distance X from a polygon?

The polygon represents islands, and the points are ships.

Unfortunately $near works only with points and not polygons, so I cannot use it with $geoNear (?)

Any help is much appreciated

Thank you!

Hello, welcome to the MongoDB community!

Yes, the ideal is to use $geoNear and create an index in the point field

db.coll.createIndex( { point_field: "2dsphere" } )

I’m available

Hello!

Thank you for your reply!

The thing is that $geoNear works only with points. I want something like:

db.ships.aggregate([
   {
     $geoNear: {
        near: { type: "Polygon", coordinates: [ [ polygon] ,[points], [here] ] }, //where polygon represents an island. mongoDB states that here you enter only a Point
        distanceField: "dist.calculated",
        maxDistance: 200, //200 meters away
        includeLocs: "dist.location",
        spherical: true
     }
   }
])

Wouldn’t this operator help? https://www.mongodb.com/docs/manual/reference/operator/query/polygon/

Isn’t it used with $geoWithin which is for finding points IN a polygon?

I am looking for points outside the polygon

Hi @eliass

Given a polygon is a collection of points, would it be possible to break the problem down to something like the following:

  1. Store Islands (polygons) in a collection with all its points
  2. run the near query to find all points that are the closest per your spec
  3. Retrieve the polygons and do a geoWithin and remove these from the result set (as the ship can’t be in an island)
  4. Closest point left in result should reference the island closest to the ship

Please not I do not have experience using these operators, so I am unsure as to whether this would work, but from a logic perspective it makes sense. Hope this helps.

Craig.

1 Like

Hi @Craig_Crevola,

Thank you very much for your reply!

Bullets 1,2: what you are proposing is to reverse the query and try to find which polygons are close to the islands like in this post here
Based on that, I’ll get “island 1” the blue island below in the simple case:

This becomes more complicated as the collection “ships” is spatio-temporal, where it has the coordinates of the ships in different timestamps. The faded colors are instances of the ship in different time and the bold are the ones closest to the island 1.

Also there are many more ships in the collection. But If I get the ones close to island 1, and group by id it is easy to filter out and get K closest ships to that island.

My question is how do you check multiple points from the ships-collection against the islands-collection ?

This would be for 1 point (as per the example in the link), but how to make it for all the ships-collection?

db.islands.find({ 
    "loc" : {
        "$near" : { 
            "$geometry" : { 
                "type" : "Point", 
                "coordinates" : [lon, lat]  //here is Ship1 instance at time T
            } 
        } 
    } 
}, { "_id" : 0, "loc.type" : 1 })
{ "loc" : { "type" : "Polygon" } }

Bullet 3: I didn’t understand why I need the $geoWithin? As it checks points in a polygon

Thank you very much for any help! :slight_smile:

Hi @eliass

Point 3 was just in case the polygon is coursely defined, it may include rivers or other inlets (an assumption on my behalf).

If I understand your question, you want to find all of the closest islands to all of the ships. If this is correct you can use this aggregation (against the Ships collection):

[
  {
    $lookup: {
      from: "Islands",
      let: {
        point: "$location",
      },
      pipeline: [
        {
          $geoNear: {
            near: "$$point",
            distance: 0,
            maxDistance: 20,
            distanceField: "distance",
          },
        },
      ],
      as: "islands",
    },
  },
]

Using the following test data:

"Ships": [
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec423"
      },
      "name": "Ship1",
      "location": [-80,25]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec424"
      },
      "name": "Ship2",
      "location": [-70,35]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec425"
      },
      "name": "Ship3",
      "location": [-60,45]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec426"
      },
      "name": "Ship4",
      "location": [-50,55]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec427"
      },
      "name": "Ship5",
      "location": [-40,65]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec428"
      },
      "name": "Ship6",
      "location": [-30,75]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec429"
      },
      "name": "Ship7",
      "location": [-20,85]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec42a"
      },
      "name": "Ship8",
      "location": [-10,95]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec42b"
      },
      "name": "Ship9",
      "location": [0,105]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec42c"
      },
      "name": "Ship10",
      "location": [10,115]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec42d"
      },
      "name": "Ship11",
      "location": [20,125]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec42e"
      },
      "name": "Ship12",
      "location": [30,135]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec42f"
      },
      "name": "Ship13",
      "location": [40,145]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec430"
      },
      "name": "Ship14",
      "location": [50,155]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec431"
      },
      "name": "Ship15",
      "location": [60,165]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec432"
      },
      "name": "Ship16",
      "location": [70,175]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec433"
      },
      "name": "Ship17",
      "location": [80,175]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec434"
      },
      "name": "Ship18",
      "location": [90,105]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec435"
      },
      "name": "Ship19",
      "location": [100,180]
    },
    {
      "_id": {
        "$oid": "65a9bf684b51dce91bcec436"
      },
      "name": "Ship20",
      "location": [110,115]
    }
  ]

and

"Islands": [
    {
      "name": "Island1",
      "points": [[-80,25],[-80,26],[-79,25]]
    },
    {
      "name": "Island2",
      "points": [[-70,35],[-70,36],[-69,35]]
    },
    {
      "name": "Island3",
      "points": [[-60,45],[-60,46],[-59,45]]
    },
    {
      "name": "Island4",
      "points": [[-50,55],[-50,56],[-49,55]]
    },
    {
      "name": "Island5",
      "points": [[-40,65],[-40,66],[-39,65]]
    },
    {
      "name": "Island6",
      "points": [[-30,75],[-30,76],[-29,75]]
    },
    {
      "name": "Island7",
      "points": [[-20,85],[-20,86],[-19,85]]
    },
    {
      "name": "Island8",
      "points": [[-10,95],[-10,96],[-9,95]]
    },
    {
      "name": "Island9",
      "points": [[0,105],[0,106],[1,105]]
    },
    {
      "name": "Island10",
      "points": [[10,115],[10,116],[11,115]]
    }
  ]

You must ensure the 2d index: db.Islands.ensureIndex({ points: “2d” })

Will give you this:

[{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec423"
  },
  "name": "Ship1",
  "location": [-80,25],
  "islands": [
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec444"
      },
      "name": "Island1",
      "points": [[-80,25],[-80,26],[-79,25]],
      "distance": 0
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec445"
      },
      "name": "Island2",
      "points": [[-70,35],[-70,36],[-69,35]],
      "distance": 14.142135623730951
    }
  ]
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec424"
  },
  "name": "Ship2",
  "location": [-70,35],
  "islands": [
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec445"
      },
      "name": "Island2",
      "points": [[-70,35],[-70,36],[-69,35]],
      "distance": 0
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec444"
      },
      "name": "Island1",
      "points": [[-80,25],[-80,26],[-79,25]],
      "distance": 13.45362404707371
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec446"
      },
      "name": "Island3",
      "points": [[-60,45],[-60,46],[-59,45]],
      "distance": 14.142135623730951
    }
  ]
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec425"
  },
  "name": "Ship3",
  "location": [-60,45],
  "islands": [
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec446"
      },
      "name": "Island3",
      "points": [[-60,45],[-60,46],[-59,45]],
      "distance": 0
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec445"
      },
      "name": "Island2",
      "points": [[-70,35],[-70,36],[-69,35]],
      "distance": 13.45362404707371
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec447"
      },
      "name": "Island4",
      "points": [[-50,55],[-50,56],[-49,55]],
      "distance": 14.142135623730951
    }
  ]
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec426"
  },
  "name": "Ship4",
  "location": [-50,55],
  "islands": [
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec447"
      },
      "name": "Island4",
      "points": [[-50,55],[-50,56],[-49,55]],
      "distance": 0
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec446"
      },
      "name": "Island3",
      "points": [[-60,45],[-60,46],[-59,45]],
      "distance": 13.45362404707371
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec448"
      },
      "name": "Island5",
      "points": [[-40,65],[-40,66],[-39,65]],
      "distance": 14.142135623730951
    }
  ]
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec427"
  },
  "name": "Ship5",
  "location": [-40,65],
  "islands": [
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec448"
      },
      "name": "Island5",
      "points": [[-40,65],[-40,66],[-39,65]],
      "distance": 0
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec447"
      },
      "name": "Island4",
      "points": [[-50,55],[-50,56],[-49,55]],
      "distance": 13.45362404707371
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec449"
      },
      "name": "Island6",
      "points": [[-30,75],[-30,76],[-29,75]],
      "distance": 14.142135623730951
    }
  ]
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec428"
  },
  "name": "Ship6",
  "location": [-30,75],
  "islands": [
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec449"
      },
      "name": "Island6",
      "points": [[-30,75],[-30,76],[-29,75]],
      "distance": 0
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec448"
      },
      "name": "Island5",
      "points": [[-40,65],[-40,66],[-39,65]],
      "distance": 13.45362404707371
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec44a"
      },
      "name": "Island7",
      "points": [[-20,85],[-20,86],[-19,85]],
      "distance": 14.142135623730951
    }
  ]
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec429"
  },
  "name": "Ship7",
  "location": [-20,85],
  "islands": [
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec44a"
      },
      "name": "Island7",
      "points": [[-20,85],[-20,86],[-19,85]],
      "distance": 0
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec449"
      },
      "name": "Island6",
      "points": [[-30,75],[-30,76],[-29,75]],
      "distance": 13.45362404707371
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec44b"
      },
      "name": "Island8",
      "points": [[-10,95],[-10,96],[-9,95]],
      "distance": 14.142135623730951
    }
  ]
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec42a"
  },
  "name": "Ship8",
  "location": [-10,95],
  "islands": [
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec44b"
      },
      "name": "Island8",
      "points": [[-10,95],[-10,96],[-9,95]],
      "distance": 0
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec44a"
      },
      "name": "Island7",
      "points": [[-20,85],[-20,86],[-19,85]],
      "distance": 13.45362404707371
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec44c"
      },
      "name": "Island9",
      "points": [[0,105],[0,106],[1,105]],
      "distance": 14.142135623730951
    }
  ]
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec42b"
  },
  "name": "Ship9",
  "location": [0,105],
  "islands": [
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec44c"
      },
      "name": "Island9",
      "points": [[0,105],[0,106],[1,105]],
      "distance": 0
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec44b"
      },
      "name": "Island8",
      "points": [[-10,95],[-10,96],[-9,95]],
      "distance": 13.45362404707371
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec44d"
      },
      "name": "Island10",
      "points": [[10,115],[10,116],[11,115]],
      "distance": 14.142135623730951
    }
  ]
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec42c"
  },
  "name": "Ship10",
  "location": [10,115],
  "islands": [
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec44d"
      },
      "name": "Island10",
      "points": [[10,115],[10,116],[11,115]],
      "distance": 0
    },
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec44c"
      },
      "name": "Island9",
      "points": [[0,105],[0,106],[1,105]],
      "distance": 13.45362404707371
    }
  ]
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec42d"
  },
  "name": "Ship11",
  "location": [20,125],
  "islands": [
    {
      "_id": {
        "$oid": "65a9d5274b51dce91bcec44d"
      },
      "name": "Island10",
      "points": [[10,115],[10,116],[11,115]],
      "distance": 13.45362404707371
    }
  ]
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec42e"
  },
  "name": "Ship12",
  "location": [30,135],
  "islands": []
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec42f"
  },
  "name": "Ship13",
  "location": [40,145],
  "islands": []
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec430"
  },
  "name": "Ship14",
  "location": [50,155],
  "islands": []
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec431"
  },
  "name": "Ship15",
  "location": [60,165],
  "islands": []
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec432"
  },
  "name": "Ship16",
  "location": [70,175],
  "islands": []
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec433"
  },
  "name": "Ship17",
  "location":[80,175],
  "islands": []
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec434"
  },
  "name": "Ship18",
  "location": [90,105],
  "islands": []
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec435"
  },
  "name": "Ship19",
  "location":[100,180],
  "islands": []
},{
  "_id": {
    "$oid": "65a9bf684b51dce91bcec436"
  },
  "name": "Ship20",
  "location": [110,115],
  "islands": []
}]

Please note that I have randomly created the coordinates and they are probably erroneous etc, however, I believe this should work given proper co-ordinates. I added the maxDistance just to limit some record fetching otherwise it will show you all the results with their distances.

Hope this helps

Craig