Search all polygons that contains a series of points in mongodb - mongodb

My question is similar to this one Given a set of polygons and a series of points, find the which polygons are the points located
I have a mongodb database with two collections, regions that stores a set of polygons (italy provinces), and points that stores a set of specimens each with a coordinate pair. I am using mongodb 2.4 and GeoJSON format to store data, both collections have a 2dsphere index.
I am able to find if a given point is inside a given polygon.
Now I would like to find all polygons that contains a list of specimens, to draw a map like this one http://www.peerates.org/province.png
Is there a better solution than iterate over all points and check if it is inside each polygon, leveraging mongodb geoindexes?
edit:
i found a partial solution using a function stored in system.js collection
function(){
var found = [];
var notfound = [];
db.regions.find().forEach(
function(region){
var regionId = region._id;
var query = {
'loc':{
$geoWithin: {
$geometry: region.loc
}
}
};
var len = db.points.find(query).size();
if(len>0){
found.push(regionId);
}else{
notfound.push(regionId);
}
}
);
return {
"found":found,
"notfound":notfound
};
}
sadly I cannot use it on mongohq.com it looks like eval() is no more supported.
#RickyA thank you, I will consider moving to a postgis

There is $geoIntersects in the mongodb geospatial library that does that.

Related

Geonear and more than one 2dsphere indexes

I have a question about use of $near vs geonear in returning distance from stored points in database from the user entered point of interest, if more than one 2dsphere index is present in the schema storing the points.
The use case is below.
In my schema I have a source and a destination location as below. The query using Intracity.find works properly and gives me sorted entries from an entered point of interest.
var baseShippingSchema = new mongoose.Schema({
startDate : Date,
endDate : Date,
locSource: {
type: [Number],
index: '2dsphere'
},
locDest: {
type: [Number],
index: '2dsphere'
}
});
var search_begin = moment(request.body.startDate0, "DD-MM-YYYY").toDate();
var search_end = moment(request.body.endDate1, "DD-MM-YYYY").toDate();
var radius = 7000;
Intracity.find({
locSource: {
$near:{$geometry: {type: "Point",
coordinates: [request.body.lng0,request.body.lat0]},
$minDistance: 0,
$maxDistance: radius
}
}).where('startDate').gte(search_begin)
.where('endDate').lte(search_end)
.limit(limit).exec(function(err, results)
{
response.render('test.html', {results : results, error: error});
}
However, I also want to return the "distance" of the stored points from the point of interest, which as per my knowledge and findings, is not possible using $near but is possible using geonear api.
However, the documentation of geonear says the following.
geoNear requires a geospatial index. However, the geoNear command requires that a collection have at most only one 2d index and/or only one 2dsphere.
Since in my schema I have two 2dspehere indexes the following geonear api fails with the error "more than one 2d index, not sure which to run geoNear on"
var point = { name: 'locSource', type : "Point",
coordinates : [request.body.lng0 , request.body.lat0] };
Intracity.geoNear(point, { limit: 10, spherical: true, maxDistance:radius, startDate:{ $gte: search_begin}, endDate:{ $lte:search_end}}, function(err, results, stats) {
if (err){return done(err);}
response.render('test.html', {results : results, error: error});
});
So my question is how can I also get the distance for each of these stored points, from entered point of interest using the schema described above.
Any help would be really great, as my Internet search is not going anywhere.
Thank you
Mrunal
As you noted the mongodb docs state that
The geoNear command and the $geoNear pipeline stage require that a collection have at most only one 2dsphere index and/or only one 2d index
On the other hand calculating distances inside mongo is only possible with the aggregation framework as it is a specialized projection. If you do not want to take option
relational DB approach: maintaining a separate distance table between all items
then your other option is to
document store approach: calculate distances in your server side JS code. You would have to cover memory limits by paginating results.

How do I set up a geospatial 2dsphere index on field that is an array?

I am trying to use geospatial 2dsphere on an field that is an array of coordinates.
I have cities with areas and each city has its own array of areas. When I try to do an intersection, i test myArea only with the areas of a particular city. Thus, I don't want to put ALL AREAS from all cities in the same collection.
Thus my question, how do you set this up?
My server is nodejs and i use native mongodb lib, if it helps.
GeoJSON does define a MultiPoli datatype, which has been implemented in recent releases of MongoDB. If you can't use one of these versions, you can workaround, by creating a document for each city in one collection, and a document for each Polygon associated to a city in another collection while storing the id of the original city.
i.e.
city Collection:
{
_id: ObjectId("51f67cfb20ea59dba539d9f8"),
name: 'New York',
...
}
city_polys Collection
{
city_id: ObjectId("51f67cfb20ea59dba539d9f8"),
loc: {
type: Polygon,
coordinates: ...
},
...
}
You can then create a 2dsphere index on the city_poly's loc field.

how to deal with complicated query in mongodb?

I use mongodb to save the temporal and spatial data, and the document item is structured as follows:
doc = { time:t,
geo:[x,y]
}
If the different of two docs are defined as:
dist(doc1, doc2) = |t1-t2| + |x1-x2| + |y1 - y2|
How can I query the documents by mongodb and sort the results by their distance to a given document doc0 ={ time:t0, geo:[x0,y0] }?
thanks
Instead of calculating the distance manually, you could trust mongodb with that task. Mongodb has built in geospatial query support.
This would look like this:
db.docs.find( {
"time": "t0",
"geo" : { $near : [x0,y0] }
} ).limit(20)
The result would be all documents near the given location [x0,y0], automatically ordered by distance to that point.

Updating MongoDB document for geospatial searching

Currently, I have my lat/long in separate fields in my MongoDB database, but if I want to do geospatial searching I need to have them in this format:
{ location : [ 50 , 30 ] }
By what means can I transpose the values of my lat/long keys into a new key per document as per above?
TIA!
You will have to iterate through all your documents that don't have a location field and add it (presumably deleting the lat/long fields unless this will break your application).
db.mycollection.find( { location : { $exists : false } } ).forEach(
function (doc) {
// Add (lon, lat) pairs .. order is important
doc.location = { lon: doc.lon, lat: doc.lat };
// Remove old properties
delete doc.lon;
delete doc.lat;
// Save the updated document
db.mycollection.save(doc);
}
)
Note that the order for MongoDB geospatial indexing should be consistent in your document as (longitude, latitude).

MongoDB: changing document structure query

We have made a change in our data model and we would like to apply it to all documents in one of our collections:
{
"id":60,
"measurement":{
"steps":1274.0
},
"date":"2012-05-15T00:00:00Z"
}
to:
{
"id":60,
"measurement":{
"distance":{
"steps":1274.0}
},
"date":"2012-05-15T00:00:00Z"
}
Essentially, we want to further nest the field steps, placing it under the distance field.
As for measurement.step, we would like to convert measurement.miles to measurement.distance.miles and measurement.minutes to measurement.time.minutes.
Any thoughts and/or suggestions would be appreciated.
Assuming you're asking how to script the schema change, which wasn't quite clear in the question: I would do something like this, unless you have more cases for the document structure, or mixed cases:
// find all the measurement documents with steps
db.coll.find({"measurement.steps":{$exists:true}}).forEach(function(doc) {
// create a new distance subdoc with the steps
doc.measurement.distance = {steps:doc.measurement.steps};
// delete the old steps subdoc
delete doc.measurement.steps;
// save the document
db.coll.save(doc);
});
// find all the measurement documents with miles
db.coll.find({"measurement.miles":{$exists:true}}).forEach(function(doc) {
// create a new distance subdoc with the miles
doc.measurement.distance = {miles:doc.measurement.miles};
delete doc.measurement.miles;
db.coll.save(doc);
});
// find all the measurement documents with minutes
db.coll.find({"measurement.minutes":{$exists:true}}).forEach(function(doc) {
// create a new time subdoc with the minutes
doc.measurement.time = {minutes:doc.measurement.minutes};
delete doc.measurement.minutes;
db.coll.save(doc);
});
You could pretty easily do the equivalent in the language/driver of your choice to ensure types, but it is probably faster to do in the shell. Hope it helps.