Search code examples
javamongodb

Use latitude and logitude to create a grid representing zones?


I'm working on a module for a transport app.

My boss gave me this task: We have a class called CitizenRequest where there's an attribute, called PointPath, which stores the latitude and longitude of a citizen's location.

This data is stored in a JSON file in a MongoDB database.

What I have to do is: Take this JSON, find the minimum PointPath (or else, minimum latitude and longitude), the maximum, and use this to positions to make a square, which will represent our area of transport.

Now, I have to divide this square into other mini squares, and each one of these will become a "zone". This zone will then have an ID, and must be stored into another collection.

The purpose of this collection should be to store how many requests we have for each zone. So, for example, we will see that in the zone with ID=149, we have X requests.

To do so, I should create an algorithm that analyzes latitude and longitude to understand in which zone it falls.

I know this may sound pretty difficult. Still, I have absolutely no idea how to implement this. So I'm asking you if you have solutions or ideas. I don't really think I must use APIs to do so, I think it just requires some logic behind it. Any help?


Solution

  • Assume docs contain points like this:

    {"citizenID:"C2", loc: { type: "Point", coordinates: [ -76.738988, 39.960921 ] }}
    

    then something like this will capture locations in squares inside the bounding rectangle:

    // First, scan the whole collection to get topleft and bottomright max points:
    c=db.foo.aggregate([
        {$group: {_id:null,
                  L: {$min: {$first: '$loc.coordinates'}},
                  R: {$max: {$first: '$loc.coordinates'}},
                  T: {$max: {$last: '$loc.coordinates'}},
                  B: {$min: {$last: '$loc.coordinates'}}
                 }}
    ]);
    d = c.next();
    
    var leftLon = d['L'];
    var topLat = d['T'];
    var rightLon = d['R'];
    var bottomLat = d['B'];
    
    var incr = 0.01;  // this is size of square.  0.01 is 1km.                                       
    var totSqr = 0;
    
    // Left to right, top to bottom:                                                   
    for(var lon = leftLon; lon < rightLon; lon += incr) {
        for(var lat = topLat; lat > bottomLat; lat -= incr) {
    
            // Make a square:                                                          
            var coords = [];
            coords.push( [ lon, lat ] );
            coords.push( [ lon+incr, lat ] );
            coords.push( [ lon+incr, lat-incr ] );
            coords.push( [ lon, lat-incr ] );
            coords.push( [ lon, lat ] ); // must close loop!                                 
    
            c = db.foo.aggregate([
                {$match: { "loc": { $geoWithin: { $geometry:
                                  { type: "Polygon", coordinates: [ coords ] } }}
                         }}
                ,{$group: {_id:null, n: {$sum:1}}}
            ]);
            d = c.next();
            if(d != null) {
                print("square region " + lon + "," + lat + ": found " + d['n']);
                // optionally insert the data somewhere as the OP notes.  You can
                // create the zone ID with an incrementing number e.g.
                // db.results.insertOne({"zone": "Z"+(zid++), n: d['n']});
            }
            totSqr++;
        }
    }