Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support setting max extent #57

Closed
willwhite opened this issue Sep 28, 2011 · 8 comments
Closed

Support setting max extent #57

willwhite opened this issue Sep 28, 2011 · 8 comments
Milestone

Comments

@willwhite
Copy link

From http://support.mapbox.com/discussions/tilestream/33-defining-bounds-or-max-extent-with-modestmaps:

How can I use the bounds of a tileset to stop ModestMaps / Wax from requesting tiles that do not exist? At the moment I see from the error console that panning outside the tile area causes hundreds of 404 errors. Ideally Wax would restrict panning outside certain bounds.

@RandomEtc
Copy link
Contributor

This is definitely something that should be available in the library. For now, a couple of pointers for workarounds...

Restricting the requests can be done now by returning null from provider.getTileUrl for tiles that don't exist. You can also return null from a custom version of provider.sourceCoordinate, which also handles the horizontal wrapping around of the tile column for the default providers. By default, these two functions use the topLeftOuterLimit and bottomRightInnerLimit of the provider, so you should be able to do something like:

// override sourceCoordinate so that it doesn't use coord limits to wrap tiles
// but so that it rejects any tile coordinates that lie outside the limits
provider.sourceCoordinate = function(coord) {
    // don't need .container() stuff here but it means *something* will get loaded at low zoom levels
    // e.g. at level 0 the base tile could contain the entire extent
    // skip the .container() stuff if you don't want to load/render tiles outside the extent *at all*
    var TL = this.topLeftOuterLimit.zoomTo(coord.zoom).container();
    var BR = this.bottomRightInnerLimit.zoomTo(coord.zoom).container().right().down();
    if (coord.row < TL.row || coord.row >= BR.row || coord.column < TL.column || coord.column >= BR.column) {
        // it's too high or too low or too lefty or too righty:
        console.log(coord.toString() + " is outside bounds");
        return null;
    }
    // otherwise it's cool, let it through
    return coord;
}

var minZoom = 0;
var maxZoom = 10;
var topLeft = new com.modestmaps.Location(51.4, -131.8);
var bottomRight = new com.modestmaps.Location(21.5, -50.5);

provider.bottomRightInnerLimit = provider.locationCoordinate(bottomRight).zoomTo(maxZoom);
provider.topLeftOuterLimit = provider.locationCoordinate(topLeft).zoomTo(minZoom);

Restricting the visible extents of the map is a little trickier, but you can override enforceLimits on the map to use the same values (or different values if you want), something like:

// Prevent the user from navigating the map outside the `outerLimits`
// of the map's provider.
map.enforceLimits = function(coord) {
    coord = coord.copy();
    var limits = this.provider.outerLimits();
    if (limits) {
        var minZoom = limits[0].zoom;
        var maxZoom = limits[1].zoom;
        if (coord.zoom < minZoom) {
            coord = coord.zoomTo(minZoom);
        }
        else if (coord.zoom > maxZoom) {
            coord = coord.zoomTo(maxZoom);
        }

        // this generally does the *intended* thing,
        // but it's not always desired behavior so it's disabled for now

        var topLeftLimit = limits[0].zoomTo(coord.zoom);
        var bottomRightLimit = limits[1].zoomTo(coord.zoom);
        var currentTopLeft = this.pointCoordinate(new MM.Point(0,0));
        var currentBottomRight = this.pointCoordinate(this.dimensions);

        if (bottomRightLimit.row - topLeftLimit.row < currentBottomRight.row - currentTopLeft.row) {
            // if the limit is smaller than the current view center it
            coord.row = (bottomRightLimit.row + topLeftLimit.row) / 2;
        }
        else {
            if (currentTopLeft.row < topLeftLimit.row) {
                coord.row += topLeftLimit.row - currentTopLeft.row;
            }
            else if (currentBottomRight.row > bottomRightLimit.row) {
                coord.row -= currentBottomRight.row - bottomRightLimit.row;
            }
        }
        if (bottomRightLimit.column - topLeftLimit.column < currentBottomRight.column - currentTopLeft.column) {
            // if the limit is smaller than the current view, center it
            coord.column = (bottomRightLimit.column + topLeftLimit.column) / 2;                    
        }
        else {
            if (currentTopLeft.column < topLeftLimit.column) {
                coord.column += topLeftLimit.column - currentTopLeft.column;
            }
            else if (currentBottomRight.column > bottomRightLimit.column) {
                coord.column -= currentBottomRight.column - bottomRightLimit.column;
            }
        }

    }
    return coord;
}

@RandomEtc
Copy link
Contributor

Update: just testing the above code now and it's not quite right, I'll get it working and let you know!

@RandomEtc
Copy link
Contributor

Update: working now. Here's source code https://gist.github.com/1248897 and a working demo http://bl.ocks.org/1248897

@RandomEtc
Copy link
Contributor

To improve this we need to:

  • make provider.sourceCoordinate configurable so that it knows whether to reject or wrap columns and rows outside the provider limits (I believe @migurski's original as2 implementation used -Infinity and Infinity to denote and apply wrappability, this might be worth re-instating in the default sourceCoordinate implementation)
  • make map.enforceLimits configurable so that it knows whether to limit panning or not
  • separate the panning/zooming limits (these should belong in map) from the provider limits (these should stay in provider) so that the latter is only about describing tiles that exist and preventing the loading of non-existent tiles.

Once we've done this, there could be some refinements to the enforceLimits behavior - for example rather than limiting at the edges sometimes you might want to apply the limit so that it constrains only the center point of the map. In the demo I posted it's a bit awkward when you zoom out and the visible area is bigger than the limits. It would be nice to still be able to move the map inside the view, so that it doesn't feel broken. It might be nice to also maintain the mousewheel center, so that zooming in and out around the same point isn't interrupted. I think we got this right with polymaps, but I need to check.

@arturogf
Copy link

some plan to include this feature as a simple option soon?

@RandomEtc
Copy link
Contributor

I've added support for this in the layers-merge branch which we hope will become version 1.0.0.

There are lots of other changes in that branch, many of which haven't been reviewed yet by my fellow maintainers, but it's there if you need it ASAP.

@tmcw
Copy link
Contributor

tmcw commented Dec 7, 2011

Looks good to me, this can be closed on merge.

@tmcw
Copy link
Contributor

tmcw commented Dec 13, 2011

Closing, as we have on other pre-merge issues.

@tmcw tmcw closed this as completed Dec 13, 2011
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants