最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Finding Closest Points to a certain Point Given its Coordinates and Maximum Distance - Query Result Undefined using

programmeradmin1浏览0评论

I've an issue that I wasn't able to solve in some days, even looking at related Stack Overflow Q/A.

I'm developing an application reusing Scotch's Create a MEAN Stack Google Map App Tutorial by Ahmed Haque approach.

I'm trying to implement an application that uses Google Maps API to draw Points, LineStrings and Polygons which coordinates are contained in GeoJson files that are stored in a MongoDB instance.

I'm using Mongoose to build the Schema for my data and query my MongoDB database.

I would like to find the closest Points CP to a certain Points P0 given P0's latitude and longitude and given a maximum radius distance used to find the interested points.

Given the image over, I would like that, for example, if I insert 2000 (kilometers), my query will find all the points maximum 2000 kilometers away from P0. In this example, it should probably give me P1 and P2.

I was able to do it when I only had Points in my Mongoose Schema.

I had this Schema with only markers (Points):

// Pulls Mongoose dependency for creating schemas
var mongoose    = require('mongoose');
var Schema      = mongoose.Schema;

// Creates a User Schema. 
var MarkerSchema = new Schema({
    username: {type: String, required: true},
    location: {type: [Number], required: true}, // [Long, Lat]
    created_at: {type: Date, default: Date.now},
    updated_at: {type: Date, default: Date.now}
});

// Indexes this schema in 2dsphere format
MarkerSchema.index({location: '2dsphere'});

module.exports = mongoose.model('mean-markers', MarkerSchema);

And this was my Old Query for only Markers:

var User = require('./model.js');

app.post('/query/', function(req, res) {

        // Grab all of the query parameters from the body.
        var lat = req.body.latitude;
        var long = req.body.longitude;
        var distance = req.body.distance;
        var reqVerified = req.body.reqVerified;

        // Opens a generic Mongoose Query
        var query = User.find({});

        // ...include filter by Max Distance (converting miles to meters)
        if (distance) {

            // Using MongoDB's geospatial querying features
            query = query.where('location').near({
                center: {
                    type: 'Point',
                    coordinates: [long, lat]
                },

                // Converting meters to miles
                maxDistance: distance * 1609.34,
                spherical: true
            });
        }
});

It worked really well, and I was able to get close points.

Then, I changed my Schema to be more dynamic and also support Polylines and Polygons.

I'm able to insert and draw new Points, Polylines and Polygons with the following Schema:

var mongoose = require('mongoose');
var GeoJSON  = require('geojson');
var Schema   = mongoose.Schema;

// Creates a Location Schema.
var LocationSchema = new Schema({
                                    name: {type: String, required: true},
                                    location: {
                                      type: {type : String, required: true},
                                      coordinates : [Schema.Types.Mixed]
                                    },
                                    created_at: {type: Date, default: Date.now},
                                    updated_at: {type: Date, default: Date.now}
});

LocationSchema.index({location: '2dsphere'});
module.exports = mongoose.model('mean-locations', LocationSchema);

And this is my Mongoose Query:

var GeoObjects = require('./model.js');

app.post('/query/', function(req, res) {

    // Grab all of the query parameters from the body.
    var lat = req.body.latitude;
    var long = req.body.longitude;
    var distance = req.body.distance;

    var query;

    if (distance) {
        query = GeoObjects.find({'location.type':'Point'})
                    .where('location.coordinates').near({
                      center: {
                        type: 'Point',
                        coordinates: [lat, long]
                      },
                      // Converting meters to miles
                      maxDistance: distance * 1609.34,
                      spherical: true
        });
    }

    // Execute Query and Return the Query Results
    query.exec(function(err, users) {
        if (err)
            res.send(err);
        console.log(users);
        // If no errors, respond with a JSON of all users that meet the criteria
        res.json(users);
    });
});

console.log(users); gives me undefined.

Logging query results in my queryCtrl.js gives me the following error message:

name: "MongoError", message: "error processing query: ns=MeanMapApp.mean-locatio…ed error: unable to find index for $geoNear query", waitedMS: 0, ok: 0, errmsg: "error processing query: ns=MeanMapApp.mean-locatio…ed error: unable to find index for $geoNear query"

Same with a little variation:

app.post('/query/', function(req, res) {

    // Grab all of the query parameters from the body.
    var lat = req.body.latitude;
    var long = req.body.longitude;
    var distance = req.body.distance;

    console.log(lat,long,distance);

    var points = GeoObjects.find({'location.type':'Point'});

    var loc = parseFloat(points.location.coordinates);
    console.log(JSON.stringify(loc));

    if (distance) {
        var query = points.near(loc, {
                      center: {
                        type: 'Point',
                        coordinates: [parseFloat(lat), parseFloat(long)]
                      },
                      // Converting meters to miles
                      maxDistance: distance * 1609.34,
                      spherical: true
        });
    }
});

This is an example of a marker:

{
  "name": "user01",
  "location": {
                "type":"Point",
                "coordinates": [102.0, 0.0]

  }
}

How $near operator works with distance and maxDistance:

From Scotch's Making MEAN Apps with Google Maps (Part II) by Ahmed Haque

MongoDB search parameter $near and its associated properties maxDistance and spherical to specify the range we’re looking to cover. We’re multiplying the distance of our query body by 1609.34, because we want to take our users’ input (in miles) and convert it into the units MongoDB expects (in meters).

  1. Why Am I getting undefined?
  2. Is it possible that this problem is caused by my Schema?
  3. How can I fix this?

If you would like to receive some clarifications just post a ment below.

Thanks in Advance.

I've an issue that I wasn't able to solve in some days, even looking at related Stack Overflow Q/A.

I'm developing an application reusing Scotch's Create a MEAN Stack Google Map App Tutorial by Ahmed Haque approach.

I'm trying to implement an application that uses Google Maps API to draw Points, LineStrings and Polygons which coordinates are contained in GeoJson files that are stored in a MongoDB instance.

I'm using Mongoose to build the Schema for my data and query my MongoDB database.

I would like to find the closest Points CP to a certain Points P0 given P0's latitude and longitude and given a maximum radius distance used to find the interested points.

Given the image over, I would like that, for example, if I insert 2000 (kilometers), my query will find all the points maximum 2000 kilometers away from P0. In this example, it should probably give me P1 and P2.

I was able to do it when I only had Points in my Mongoose Schema.

I had this Schema with only markers (Points):

// Pulls Mongoose dependency for creating schemas
var mongoose    = require('mongoose');
var Schema      = mongoose.Schema;

// Creates a User Schema. 
var MarkerSchema = new Schema({
    username: {type: String, required: true},
    location: {type: [Number], required: true}, // [Long, Lat]
    created_at: {type: Date, default: Date.now},
    updated_at: {type: Date, default: Date.now}
});

// Indexes this schema in 2dsphere format
MarkerSchema.index({location: '2dsphere'});

module.exports = mongoose.model('mean-markers', MarkerSchema);

And this was my Old Query for only Markers:

var User = require('./model.js');

app.post('/query/', function(req, res) {

        // Grab all of the query parameters from the body.
        var lat = req.body.latitude;
        var long = req.body.longitude;
        var distance = req.body.distance;
        var reqVerified = req.body.reqVerified;

        // Opens a generic Mongoose Query
        var query = User.find({});

        // ...include filter by Max Distance (converting miles to meters)
        if (distance) {

            // Using MongoDB's geospatial querying features
            query = query.where('location').near({
                center: {
                    type: 'Point',
                    coordinates: [long, lat]
                },

                // Converting meters to miles
                maxDistance: distance * 1609.34,
                spherical: true
            });
        }
});

It worked really well, and I was able to get close points.

Then, I changed my Schema to be more dynamic and also support Polylines and Polygons.

I'm able to insert and draw new Points, Polylines and Polygons with the following Schema:

var mongoose = require('mongoose');
var GeoJSON  = require('geojson');
var Schema   = mongoose.Schema;

// Creates a Location Schema.
var LocationSchema = new Schema({
                                    name: {type: String, required: true},
                                    location: {
                                      type: {type : String, required: true},
                                      coordinates : [Schema.Types.Mixed]
                                    },
                                    created_at: {type: Date, default: Date.now},
                                    updated_at: {type: Date, default: Date.now}
});

LocationSchema.index({location: '2dsphere'});
module.exports = mongoose.model('mean-locations', LocationSchema);

And this is my Mongoose Query:

var GeoObjects = require('./model.js');

app.post('/query/', function(req, res) {

    // Grab all of the query parameters from the body.
    var lat = req.body.latitude;
    var long = req.body.longitude;
    var distance = req.body.distance;

    var query;

    if (distance) {
        query = GeoObjects.find({'location.type':'Point'})
                    .where('location.coordinates').near({
                      center: {
                        type: 'Point',
                        coordinates: [lat, long]
                      },
                      // Converting meters to miles
                      maxDistance: distance * 1609.34,
                      spherical: true
        });
    }

    // Execute Query and Return the Query Results
    query.exec(function(err, users) {
        if (err)
            res.send(err);
        console.log(users);
        // If no errors, respond with a JSON of all users that meet the criteria
        res.json(users);
    });
});

console.log(users); gives me undefined.

Logging query results in my queryCtrl.js gives me the following error message:

name: "MongoError", message: "error processing query: ns=MeanMapApp.mean-locatio…ed error: unable to find index for $geoNear query", waitedMS: 0, ok: 0, errmsg: "error processing query: ns=MeanMapApp.mean-locatio…ed error: unable to find index for $geoNear query"

Same with a little variation:

app.post('/query/', function(req, res) {

    // Grab all of the query parameters from the body.
    var lat = req.body.latitude;
    var long = req.body.longitude;
    var distance = req.body.distance;

    console.log(lat,long,distance);

    var points = GeoObjects.find({'location.type':'Point'});

    var loc = parseFloat(points.location.coordinates);
    console.log(JSON.stringify(loc));

    if (distance) {
        var query = points.near(loc, {
                      center: {
                        type: 'Point',
                        coordinates: [parseFloat(lat), parseFloat(long)]
                      },
                      // Converting meters to miles
                      maxDistance: distance * 1609.34,
                      spherical: true
        });
    }
});

This is an example of a marker:

{
  "name": "user01",
  "location": {
                "type":"Point",
                "coordinates": [102.0, 0.0]

  }
}

How $near operator works with distance and maxDistance:

From Scotch's Making MEAN Apps with Google Maps (Part II) by Ahmed Haque

MongoDB search parameter $near and its associated properties maxDistance and spherical to specify the range we’re looking to cover. We’re multiplying the distance of our query body by 1609.34, because we want to take our users’ input (in miles) and convert it into the units MongoDB expects (in meters).

  1. Why Am I getting undefined?
  2. Is it possible that this problem is caused by my Schema?
  3. How can I fix this?

If you would like to receive some clarifications just post a ment below.

Thanks in Advance.

Share Improve this question edited Jun 16, 2016 at 20:40 AndreaM16 asked Jun 9, 2016 at 17:54 AndreaM16AndreaM16 3,9855 gold badges37 silver badges76 bronze badges 2
  • 1 Hey @AndreaM16. Any chance you have this code on GitHub or something so we can pull it down and take a closer look? – Ahmed Haque Commented Jun 13, 2016 at 16:31
  • Hi Ahmed, did you take a look? – AndreaM16 Commented Jun 14, 2016 at 15:22
Add a ment  | 

2 Answers 2

Reset to default 5 +25

I don't understand what's beneath all your code, but I know a thing:
If you are using Google's Radar Search, you must take into consideration that

The maximum allowed radius is 50 000 meters.

Just take a look at their Documentation

Meaning that if you try with higher radius, results could be Zero

I finally managed to solve this issue.

Essentially, the issue was caused by the schema, since 2dIndex was referred to a wrong field (type and coordinates).

I solved using the following Schema:

var mongoose = require('mongoose');
var GeoJSON  = require('geojson');
var Schema   = mongoose.Schema;

var geoObjects = new Schema({
                               name : {type: String},
                               type: {
                                       type: String,
                                       enum: [
                                               "Point",
                                               "LineString",
                                               "Polygon"
                                             ]
                                      },
                                coordinates: [Number],
                                created_at: {type: Date, default: Date.now},
                                updated_at: {type: Date, default: Date.now}
});

// Sets the created_at parameter equal to the current time
geoObjects.pre('save', function(next){
   now = new Date();
   this.updated_at = now;
   if(!this.created_at) {
      this.created_at = now
   }
   next();
});

geoObjects.index({coordinates: '2dsphere'});

module.exports = mongoose.model('geoObjects', geoObjects);

And the following Query:

app.post('/query/', function(req, res) {

        // Grab all of the query parameters from the body.
        var lat = req.body.latitude;
        var long = req.body.longitude;
        var distance = req.body.distance;

        var query = GeoObjects.find({'type':'Point'});

        // ...include filter by Max Distance 
        if (distance) {

            // Using MongoDB's geospatial querying features. 
            query = query.where('coordinates').near({
                center: {
                    type: 'Point',
                    coordinates: [lat, long]
                },

                // Converting meters to miles
                maxDistance: distance * 1609.34,
                spherical: true
            });
        }

        // Execute Query and Return the Query Results
        query.exec(function(err, geoObjects) {
            if (err)
                res.send(err);

            // If no errors, respond with a JSON 
            res.json(geoObjects);
        });
    });

I hope it will help someone!

EDIT

The schema I put over causes a bit of problems with LineStrings and Polygons.

Here are correct schemas which allow using geoQueries

linestring-model.js:

var mongoose = require('mongoose');
var Schema   = mongoose.Schema;

// Creates a LineString Schema.
var linestrings = new Schema({
    name: {type: String, required : true},
    geo : {
        type : {type: String,
            default: "LineString"},
        coordinates : Array
    },
    created_at: {type: Date, default: Date.now},
    updated_at: {type: Date, default: Date.now}
});

// Sets the created_at parameter equal to the current time
linestrings.pre('save', function(next){
    now = new Date();
    this.updated_at = now;
    if(!this.created_at) {
        this.created_at = now
    }
    next();
});

linestrings.index({geo : '2dsphere'});
module.exports = mongoose.model('linestrings', linestrings);

polygon-model.js

var mongoose = require('mongoose');
var Schema   = mongoose.Schema;

// Creates a Polygon Schema.
var polygons = new Schema({
    name: {type: String, required : true},
    geo : {
        type : {type: String,
            default: "Polygon"},
        coordinates : Array
    },
    created_at: {type: Date, default: Date.now},
    updated_at: {type: Date, default: Date.now}
});

// Sets the created_at parameter equal to the current time
polygons.pre('save', function(next){
    now = new Date();
    this.updated_at = now;
    if(!this.created_at) {
        this.created_at = now
    }
    next();
});

polygons.index({geo : '2dsphere'});
module.exports = mongoose.model('polygons', polygons);

LineString Insert:

{  
    "name" : "myLinestring", 
    "geo" : {
        "type" : "LineString", 
        "coordinates" : [
            [
                17.811, 
                12.634
            ], 
            [
                12.039, 
                18.962
            ], 
            [
                15.039, 
                18.962
            ], 
            [
                29.039, 
                18.962
            ]
        ]
    }
}

Polygon Insert:

{  
    "name" : "Poly", 
    "geo" : {
        "type" : "Polygon", 
        "coordinates" :  [
                           [ 
                             [25.774, -80.190], [18.466, -66.118], 
                             [32.321, -64.757], [25.774, -80.190] 
                           ]
                         ]
    }
}

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论