Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoid loading of invisible tiles when using multiple tile layers in Leaflet

Tags:

leaflet

I am using Leaflet with two tile layers. The first one, I will call it basement tile layer, provides tiles for whole world. The second one overlays the basement tile layer in a specific region defined by bounce option. I will call this one overlaying tile layer. In code this looks like the following:

var map = L.map('map');

// OpenstreetMap tile layer as basement
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

// overlaying tile layer for specific region defined by bounds
L.tileLayer('http://{s}.examples.com/overlay/{z}/{x}/{y}.png', {
  bounds: L.latLngBounds(L.latLng(59.321966, 18.05943), L.latLng(59.328469, 18.076167));
}).addTo(map);

The overlaying tile layer are not transparent. So for the bounds region only the tiles of overlaying tile layer are visible. Tiles provided by basement tile layer are not needed. But I didn't find a way to prevent Leaflet from loading these unnecessary tiles yet. I would be glad for any hint.

I thought about using tile events to interrupt loading of tiles which aren't needed. But as far as documented tile events can not manipulate tile loading.

Here is a JSFiddle demonstrating the behavior. As you see e.g. tile 14/4825/6155.png is loaded from openstreetmap.org even so it's invisible.

In my use case another think makes it more complicated: Overlaying map has strict borders cause it's generated by historic map sheet. So tiles are transparent at the borders of overlaying map. In these regions tiles of basement map has to be loaded.

like image 864
jelhan Avatar asked Feb 16 '15 22:02

jelhan


People also ask

Which tile layers should I use for my basemaps?

Raster tile layers are most appropriate for basemaps that give your maps geographic context such as imagery (as in the World Imagery basemap) or feature-based maps such as in the Topographic, National Geographic, Oceans, and other basemaps. Raster tile layers can also be composed of static operational layers such as thematic maps of your data.

What are tile layers?

Tile layers are also useful when you need to expose a map or layer on the web for the visualization of relatively static data. Tile layers come in different formats based on the original source data. Tile layers can be stored as prerendered raster tiles or as vector tiles.

How do I share a vector tile package with my organization?

With ArcGIS Pro 1.2 and later, you can share a vector tile package to your organization and publish the uploaded vector tile package as a hosted layer. With ArcGIS Pro 1.4 and later, you can publish a hosted vector tile layer from a map in ArcGIS Pro directly to ArcGIS Online.

What is the best way to display vector tile layers?

Vector tile layers have the best performance on machines with newer hardware, and they can be displayed in most current versions of desktop browsers, including Chrome, Firefox, Safari, Internet Explorer 11, and Edge. You can add vector tile layers as operational layers or basemaps to Map Viewer or Scene Viewer.


1 Answers

Thanks to @FrankPhillips hints in comment I figured out that I could overwrite _isValidTile method from L.GridLayer to achieve the functionality. I could add a hole option as a opposite to bounce option.

L.ExtendedTileLayer = L.TileLayer.extend({    
    _isValidTile: function (coords) {
        var crs = this._map.options.crs;

        if (!crs.infinite) {
            // don't load tile if it's out of bounds and not wrapped

            /*
             * this._globalTileRange is not defined
             * not quite sure why
             */
            var globalTileRange = this._map.getPixelWorldBounds( coords.z );

            var bounds = globalTileRange;
            if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
                (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
        }

        var tileBounds = this._tileCoordsToBounds(coords);

        // don't load tile if it doesn't intersect the bounds in options
        if (this.options.bounds &&
            ! L.latLngBounds(this.options.bounds).intersects(tileBounds)) {
            return false;
        }

        // don't load tile if it does intersect the hole in options
        if (this.options.hole &&
            L.latLngBounds(this.options.hole).intersects(tileBounds)) {
            return false;
        }

        return true;
    },
});

var map = L.map('map', {
    center: [40.777838, -73.968654], 
    zoom: 14
});

new L.ExtendedTileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
    attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
    hole: L.latLngBounds(
        L.latLng(40.791853, -73.967128),
        L.latLng(40.781455, -73.955713)
    )
}).addTo(map);

L.tileLayer('http://tile.stamen.com/toner/{z}/{x}/{y}.png', {
    attribution: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.',
    bounds: L.latLngBounds(
        L.latLng(40.791853, -73.967128),
        L.latLng(40.781455, -73.955713)
    )
}).addTo(map);

I updated JSFiddle to show it working.

_isValidTile is mostly just copyed from Leaflet original. I had to reimplement this._globalTileRange since is was undefined. Code is only working for leaflet-src.js, since _isValidTime is uglifyed in production build.

like image 82
jelhan Avatar answered Oct 16 '22 03:10

jelhan