Aziule
Aziule

Reputation: 441

Mongoose $maxDistance not precise

I am storing places in a Mongoose model (name + coords as [lng, lat]) and have a 2d index on it.

I want to get the nearest locations from a point (lng, lat) within a radius in km.

For this, I use mongoose's $near and $maxDistance parameters. The issue I am facing is that the radius does not seem to be precise, thus I get wrong results.

Here is my code for the geo search:

LocationModel.find({
        loc: {
            $near: coords,
            $maxDistance: max_distance
        }
    }).exec(function(err, locations)
    {
        if(err)
            return res.status(400).send({
                message: errorHandler.getErrorMessage(err)
            });

        res.json(locations);
    });

I have two locations for my tests:

Place 1 : 6.614090000000033, 45.4203

Place 2 : 6.905578500000047, 45.4683226

Total distance : 23.36km

The max_distance for $maxDistance is calculated this way:

var max_distance = req.params.max_distance / 111.12;

When I provide the reference point as Place A, I only get results when I set the max_distance to 33km and over, and not 24km as it should be.

Can someone explain me what is wrong here? Maybe the 111.12 (which I found all over the Internet), or is it something else?

Thanks

Upvotes: 3

Views: 659

Answers (2)

david strachan
david strachan

Reputation: 7228

1 degree is aprox 111 kms at equator, where your 111.12 comes from. But as we move away from the equator it becomes less due to the convergence of the meridians(longitude) as we approach the pole.

Degree  Lat         Lng
0°      110.574 km  111.320 km
15°     110.649 km  107.551 km
30°     110.852 km  96.486 km
45°     111.132 km  78.847 km
60°     111.412 km  55.800 km
75°     111.618 km  28.902 km
90°     111.694 km  0.000 km

You could use equirectangular approximation for this.

x = Δlat * cos (Δlng/2)
y = ΔLng
d = R ⋅ √x² + y²
where Δlat & Δlng in radians & R = radius of earth
Δlat is the difference between the 2 lats  & Δlng that of lats 

Upvotes: 1

SarathMS
SarathMS

Reputation: 539

Since you are talking about geolocations, I'd suggest these changes:

  1. Use 2dsphere index instead of 2d. This will ensure the search takes into consideration that the earth is not a 2d plane.
  2. Change your model to represent the coordinates in the GeoJSON format.

So your schema should look something like this:

    var LocationSchema = new mongoose.Schema({
      name: String,
      location: {
        type: {
           type: String,
           default: "Point"
        },
        coordinates: {
            type: [Number]
        }
      }
    });
    LocationSchema.index({ location: '2dsphere' });

Now you can use Model.geoNear to perform your query. To get your distance in kilometers, you need to use the distanceMultiplier option with the radius of earth in kilometers as the value. Don't forget the { spherical: true } flag.

Note: I've used it as part of an aggregation and its pretty accurate. It should work the same in the above case also. Mongoose documentation doesn't talk about it. Use this from MongoDB docs

Upvotes: 4

Related Questions