1
votes

I am currently in the process of upgrading from version 2.1.5 to 3.1.1. I ran into an issue with the maxNativeZoom option on tile layer.

Basically it looks like it is being ignored and the map keeps zooming displaying white tiles.

My expectation would be that the tiles should be autoscaled.

This worked fine in version 2.1.5.

Here are the fiddles:

2.1.5: https://jsfiddle.net/jfwkq0s4/4/

3.1.1: http://jsfiddle.net/dbpfcoqo/6/

Here is the small sample code from the fiddles above:

L.mapbox.accessToken = 'pk.eyJ1IjoidHZibG9tYmVyZyIsImEiOiJjaXZsZmpsMWwwNXBuMnRudmJqNGQyMjJwIn0.842ovgKydac51tg6b9qKbg';
var map = L.mapbox.map('map', null, { maxZoom: 20})
.setView([40, -74.50], 9);

var layer = new L.mapbox.tileLayer('mapbox.streets', { maxNativeZoom: 17, maxZoom: 20 });

map.addLayer(layer);

I am wondering if MapBox/Leaflet changed the way you need to set these values or maybe they are no longer supported in their current version?

The option is still listed in their documentation.

I appreciate any help!

2

2 Answers

2
votes

This seems to be a bug in Mapbox.js starting with version 3.0.0 (which uses Leaflet 1.0.1 or 1.0.2 internally).

It is visible on Mapbox.js example page for over zoom tiles: https://www.mapbox.com/mapbox.js/example/v1.0.0/overzoom/

I see that you have already opened an issue on Mapbox.js repository: https://github.com/mapbox/mapbox.js/issues/1250

What seems to happen is that Mapbox correctly freezes the X and Y coordinates of the tile past maxNativeZoom, but not the Z (zoom) value. Therefore it displays totally incorrect tiles, if available.

This bug is specific to Mapbox.js, Leaflet behaves as expected:

var map = L.map('map', {
  maxZoom: 20
}).setView([48.86, 2.35], 11);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
  maxNativeZoom: 11,
  maxZoom: 20
}).addTo(map);

var $zoomLevel = document.getElementById("zoomLevel");
map.on('zoomend', showZoomLevel);
showZoomLevel();

function showZoomLevel() {
  $zoomLevel.innerHTML = map.getZoom();
}
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css">
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js"></script>

Zoom level: <span id='zoomLevel'></span>
<div id="map" style="height: 170px"></div>
2
votes

Here is my fix. I am also going to send this to MapBox. There are two main issues.

  1. The first issue is the minZoom and maxZoom are always being overwritten by the mapbox tile layer setting.

  2. The second issue is the z point was not being set correctly based on the maxNativeZoom.

This seems to work for me. See code example for details.

	var customTileLayer = function (url, options) {
		var formatPattern = /\.((?:png|jpg)\d*)(?=$|\?)/;
		var tileLayerExtend = L.mapbox.TileLayer.extend({
			_setTileJSON: function (json) {
				//util.strict(json, 'object');
				/*We had to create a function for this since util is private in mapbox */
				strict(json, 'object');

				if (!this.options.format) {
					var match = json.tiles[0].match(formatPattern);
					if (match) {
						this.options.format = match[1];
					}
				}

				var minZoom = this.options.minZoom || json.minzoom || 0;
				var maxZoom = this.options.maxZoom || json.maxzoom || 18;

				L.extend(this.options, {
					tiles: json.tiles,
					attribution: this.options.sanitizer(json.attribution),
					minZoom: minZoom,
					maxZoom: maxZoom,
					tms: json.scheme === 'tms',
					// bounds: json.bounds && util.lbounds(json.bounds)
					/*We had to create a function for this since util is private in mapbox */
					bounds: json.bounds && lBounds(json.bounds)
				});

				this._tilejson = json;
				this.redraw();
				return this;
			},
			getTileUrl: function (tilePoint) {
				var tiles = this.options.tiles,
					index = Math.floor(Math.abs(tilePoint.x + tilePoint.y) % tiles.length),
					url = tiles[index];

				tilePoint.z = this._getZoomForUrl();

				var templated = L.Util.template(url, tilePoint);
				if (!templated || !this.options.format) {
					return templated;
				} else {
					return templated.replace(formatPattern,
						(L.Browser.retina ? this.scalePrefix : '.') + this.options.format);
				}
			}
		});

		return new tileLayerExtend(url, options);
	};

	var lBounds = function(_) {
		return new L.LatLngBounds([[_[1], _[0]], [_[3], _[2]]]);
	};

	var strict = function (_, type) {
		if (typeof _ !== type) {
			throw new Error('Invalid argument: ' + type + ' expected');
		}
	};
  
  L.mapbox.accessToken = 'pk.eyJ1IjoidHZibG9tYmVyZyIsImEiOiJjaXZsZmpsMWwwNXBuMnRudmJqNGQyMjJwIn0.842ovgKydac51tg6b9qKbg';
var map = L.mapbox.map('map', null, { maxZoom: 20})
    .setView([40, -74.50], 9);
    
var layer = new customTileLayer('mapbox.streets', { maxNativeZoom: 17, maxZoom: 20 });
var $zoomLevel = document.getElementById("zoomLevel");

map.addLayer(layer);   
$zoomLevel.innerHTML = map.getZoom();
map.on('zoomend', function(e) {
	var zoom = e.target.getZoom();
  $zoomLevel.innerHTML= zoom;
});
  
  
body { margin:0; padding:0; }
#container { position: absolute; top: 0; bottom: 0; width: 100%; height: 100%; }
#map { height: 100%; width:100%; }
<script src="https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.js"></script>
<link href="https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.css" rel="stylesheet" />

<div id="container">
  <span id='zoomLevel'></span>
  <div id='map'></div>
</div>