Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript: best way to maintain bounds (Google Maps)?

I am using the Google Maps v3 API. I currently am making a request to fetch new data each time a person changes the viewport (by either zooming or shifting the map) and am throwing away the old data I have. This works great, but now I want to cache the data so that I don't need to fetch the data each time the viewport changes. The Google Maps API defines a viewport by its Northeast and Southwest coordinate consisting of a latitude and a longitude. They are stored in objects called LatLngBounds.

I have come up with 2 ways I can do this:

  1. Store the bounds of each new viewport the user visits and check if the new viewport is in an old viewport and fetch new data only of the part of the new viewport that is not within an old viewport. Essentially,
  2. Divide each new viewport up into rectangular sections of data that we have and data that needs to be fetched. Store the bounds of each of the rectangular sections.

If anyone can think of a better way to do this, feel free to suggest new approaches.

My question is which one is going to be better in terms of better performance/memory usage and overall speed? They are both similar algorithms so does it really matter?

Also, right now both algorithms rely on dividing up the new viewport based on old viewports. What would the algorithm to divide new viewports look like? (Assume I implemented my 2nd algorithm)

var prevBounds = [ /* Array of previously seen bounds */ ];
var newViewport = map.getBounds(); // New Viewport to divide up
var sw = newViewport.getSouthWest();
var swlat = sw.lat();
var swlng = sw.lng();
var ne = newViewport.getNorthEast();
var nelat = ne.lat();
var nelng = ne.lng();
// newViewport.intersects(bounds) 
// Returns true if this bounds shares any points with this bounds.
like image 875
tyronegcarter Avatar asked Nov 14 '22 23:11

tyronegcarter


1 Answers

I might consider doing this more or less exactly the way Google serves up map tiles - instead of loading data for the entire viewport at once, carve up the entire map into square areas (though probably bigger areas than Google's 256x256 tiles), determine which areas are in the current viewport, and load data for those areas. As the user pans and zooms the map, check the viewport bounds to see whether new areas have come into the frame, and load them as necessary. Rough pseudocode:

var cache = {}

function onViewportChange() {
    // get new bounds
    var bounds = map.getBounds();
    // identify all the areas in the bounds
    var areas = getAreas(bounds);
    areas.forEach(function(area) {
        if (area.key in cache) {
            // do nothing, or load items from cache
        } else {
            // load new data, storing the key (and maybe the data) 
            // to the cache
        }
    })
}

function getAreas(bounds) {
    /* given a set of bounds, return an array of objects like:
    [
        {
           x: 1,
           y: 2,
           zoom: 4,
           key: "4-1,2",
           bounds: b // lat/lon bounds of this area
        },
        ...
    ]
    */
}

(See the Google explanation of map coordinates and this example for an idea of how to implement getAreas.)

The appeal of this is that the areas you're retrieving are much simpler, and it becomes very easy to check whether you've already loaded data for a given area - each area can have a simple unique key, probably a string made out of the x/y/zoom coordinates, and as each new area is loaded you save the key (or maybe the key and the data - it depends on whether you're removing old data from the map or just leaving it there) to some cache object. Then all you have to do when the viewport moves to a new area is check for the existence of that key in your cache.

The downside is that you might often load data outside the current viewport, and you're probably sending more requests to the server than you would with the implementations you suggest. This method might work best if you're serving up different data at different zoom levels - otherwise you might be stuck trying to choose a single-size cache area that works well at different zooms.

like image 119
nrabinowitz Avatar answered Dec 29 '22 01:12

nrabinowitz