'Find all circles that contain a given point with a mongodb query

I have a set of circles, defined by points (centers) and a varying radius around it (green, black). I want to query if a given point (red) is contained any of these circles.

Basic image of all the dots and circles

The green points circles should be included in the result since their area include the red point. The black dots should be ignored.

The circles-document consists of a GeoJSON point and a radius, something like this:

{
    "cirleName": "test",
    "radius": "100",
    "gpsLocation" :{"type": "Point", "coordinates": [0, 0]}
}

How would I construct a query that gives me the needed information? I have already found this question that states a where-keyword has to be used in that case but since I'm pretty new to mongodb I can't quite figure out how I would construct that.



Solution 1:[1]

Welcome Cowboy_Patrick,

The answer depends if you want the Euclidean version or the Earth's version, and if you can bound the radius of your maximal radius.

Assuming you want the Earth's case:

If you know the limit for your maximal radius, you can use:

db.getCollection('tA').aggregate([
  {
    $geoNear: {
      near: {
        type: "Point",
        coordinates: [40, 30] // red point geoJson coordinates
      },
      distanceField: "calculatedDist",
      maxDistance: 12000000, // radius limit
      key: 'gpsLocation',
      spherical: true
    }
  },
  {$match: {$expr: {$gte:['$radius', '$calculatedDist']}}}
])

This will first calculate the distance to all circles' centers inside the max radius and then will choose only the circles' centers that their distance to our point is smaller than their radius.

For sample data in which the gpsLocation is geoJson and the distance is in meters:

{
    "_id" : ObjectId("626ef666653cf1987b9fefba"),
    "cirleName" : "test",
    "radius" : 5500000, // meters
    "gpsLocation" : {
        "type" : "Point",
        "coordinates" : [0, 0] // circle center point geoJson coordinates
    }
},
{
    "_id" : ObjectId("626ef695653cf1987b9feff3"),
    "cirleName" : "test1",
    "radius" : 100000, // meters
    "gpsLocation" : {
        "type" : "Point",
        "coordinates" : [0, 80] // circle center point geoJson coordinates
    }
},
{
    "_id" : ObjectId("626ef6a9653cf1987b9ff001"),
    "cirleName" : "test2",
    "radius" : 7000000, // meters
    "gpsLocation" : {
        "type" : "Point",
        "coordinates" : [40, 40] // circle center point geoJson coordinates
    }
}

It will return only test and test2.

If you can't limit the radius size, you will have to calculate the distance on the Earth from our red point origin to all circles. This is less pretty:

db.getCollection('tA').aggregate([
{$addFields: {
     "calculatedDist": {
       $multiply: [6371000, // Earth's radius in meters
         {
           $acos: {
             $max: [
               {
                 $min: [
                   {
                     $add: [
                       {
                         $multiply: [
                           {
                             $sin: {
                               $degreesToRadians: {
                                 $arrayElemAt: ["$gpsLocation.coordinates", 1]
                               }
                             }
                           },
                           {
                             $sin: {$degreesToRadians: 30} // red point latitude
                           }
                         ]
                       },
                       {
                         $multiply: [
                           {
                             $cos: {
                               $degreesToRadians: {
                                 $arrayElemAt: [
                                   "$gpsLocation.coordinates", 1]
                               }
                             }
                           },
                           {
                             $cos: {"$degreesToRadians": 30} // red point latitude
                           },
                           {
                             $cos: {
                               $degreesToRadians: {
                                 "$subtract": [
                                     {$arrayElemAt: ["$gpsLocation.coordinates", 0]},
                                     40] // red point longtitude
                                 }
                             }
                           }
                         ]
                       }
                     ]
                   },
                   1
                 ]
               },
               -1
             ]
           }
         }
       ]
     }
   }
 },
  {$match: {$expr: {$gte:['$radius', '$calculatedDist']}}}
])

But will provide the same results.

If you want the Euclidean case, you can replace the distance calculation of case 2 with a simple Euclidean distance calculation.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1