我将Leaflet.js用于基于桌面和移动浏览器的地图,并且需要支持各种地图图块服务。其中一些切片服务使用粗略缩放级别(如1,5,10,15)定义,如果我请求不受支持的缩放级别,则服务不会返回切片。 (例如,如果我在不支持缩放级别service/tiles/6/x/y
时请求6
。)
Leaflet tile图层支持minZoom
和maxZoom
,但我想弄清楚是否有最佳做法来进行粗略缩放级别,以及其他人是否遇到过这种情况。
我在GitHub上发现这篇文章解决了不支持的缩放级别的平铺缩放:https://github.com/Leaflet/Leaflet/pull/1802
但我不确定这是否适用。 (我不确定我是想缩放还是“补间”缩放级别......但如果这有意义并且不太困难我愿意尝试。)
我已经开始尝试这种方法变得混乱,因为缩放会导致更多缩放,我必须区分用户驱动的缩放和系统驱动的缩放:
// layer metadata (assumption: Levels is in ascending order of zoom)
var layerDef = { Title: 'Service lines', Levels: [10, 15, 19] };
// create Leaflet Tile Layer and show on map
var layer = L.tileLayer('://host/service/{z}/{x}/{y}');
layer.minZoom = layerDef.Levels[0];
layer.maxZoom = layerDef.Levels[layerDef.Levels-1];
layer.addTo(map);
// initialize lastZoom for coarse zoom management
var lastZoom = map.getZoom();
var userZoom = true;
// handle supported zoom levels when zoom changes
map.on('zoomend', function (e)
{
// get new zoom level
var z = e.target._zoom || map.getZoom();
if (userZoom) // assume user initiated this zoom
{
// is this zoom level supported?
var zIdx = $.inArray(z, layerDef.Levels);
if (zIdx === -1)
{
// zoom level is not supported; zoom out or in to supported level
// delta: < 0 for zoom out, > 0 for zoom in
var zDelta = z - lastZoom;
var zLastIdx = $.inArray(lastZoom, layerDef.Levels);
var newZoom = -1;
if (zDelta > 0)
{
// user zoomed in to unsupported level.
// zoom in to next supported level (rely on layer.maxZoom)
newZoom = layerDef.Levels[zLastIdx + 1];
}
else
{
// user zoomed out to unsupported level.
// zoom out to next supported level (rely on layer.minZoom)
newZoom = layerDef.Levels[zLastIdx - 1];
}
if (newZoom !== -1)
{
userZoom = false; // set flag
setTimeout(function ()
{
map.setZoom(newZoom); // set zoom -- seems to not work if called from within zoomend handler
}, 100); // delay call to setZoom() to fix issue
}
}
}
else
{
userZoom = true; // clear flag
}
lastZoom = z;
});
(旁注:我希望粗略缩放级别的原因很明显:在每个缩放级别创建和存储光栅图块会很昂贵,特别是对于大型地理区域,尤其是与自己的离线移动设备一起使用时[本地]磁贴服务器,有限的无线数据计划,有限的存储容量等。例如,这可能不是玩具应用程序和谷歌地图可能遇到的问题,而是特定于域和移动应用程序的空间和带宽是非常珍贵。)
谢谢!
更新:我发现我对此代码遇到的问题是map.setZoom(z)
在zoomEnd
处理程序中调用时无法正常工作(它确实设置了缩放,但导致显示灰色/不存在的图块问题,可能是因为Leaflet仍处于缩放/缩放过程中。修复是使用setTimeout
将呼叫延迟到setZoom()
。但是,如果有其他人处理过此问题,我仍然很好奇,如果有更好的方式......(我更新了上面的代码以使用setZoom
修复)
答案 0 :(得分:1)
目前在GitHub上的Leaflet存储库中正在审核一个提交。它将zoomFactor添加到地图的选项中。也许这就是你要找的东西。至少,我认为只要您的可用tileset具有缩放级别(不知道这是否是正确的技术术语)是最低可用缩放级别的倍数,它就会起作用。
答案 1 :(得分:1)
以下(不保证,基于this)应与Leaflet v1.7.3一起使用,但可能不适用于当前的主。
它使用serverZooms
选项将磁贴服务器上的可用缩放级别指定为有序数组。
覆盖L.TileLayer._getZoomForUrl
以返回匹配或下一个较低的可用服务器缩放。还会覆盖L.TileLayer._getTileSize
以增加切片大小,以便在服务器缩放之间缩放切片。
L.TileLayer.Overzoom = L.TileLayer.extend({
options: {
// List of available server zoom levels in ascending order. Empty means all
// client zooms are available (default). Allows to only request tiles at certain
// zooms and resizes tiles on the other zooms.
serverZooms: []
},
// add serverZooms (when maxNativeZoom is not defined)
// @override
_getTileSize: function() {
var map = this._map,
options = this.options,
zoom = map.getZoom() + options.zoomOffset,
zoomN = options.maxNativeZoom || this._getServerZoom(zoom);
// increase tile size when overscaling
return zoomN && zoom !== zoomN ?
Math.round(map.getZoomScale(zoom) / map.getZoomScale(zoomN) * options.tileSize) :
options.tileSize;
},
// @override
_getZoomForUrl: function () {
var zoom = L.TileLayer.prototype._getZoomForUrl.call(this);
return this._getServerZoom(zoom);
},
// Returns the appropriate server zoom to request tiles for the current zoom level.
// Next lower or equal server zoom to current zoom, or minimum server zoom if no lower
// (should be restricted by setting minZoom to avoid loading too many tiles).
_getServerZoom: function(zoom) {
var serverZooms = this.options.serverZooms || [],
result = zoom;
// expects serverZooms to be sorted ascending
for (var i = 0, len = serverZooms.length; i < len; i++) {
if (serverZooms[i] <= zoom) {
result = serverZooms[i];
} else {
if (i === 0) {
// zoom < smallest serverZoom
result = serverZooms[0];
}
break;
}
}
return result;
}
});
(function () {
var map = new L.Map('map');
map.setView([50, 10], 5);
new L.TileLayer.Overzoom('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
serverZooms: [0, 1, 2, 3, 6, 9, 12, 15, 17],
attribution : '© <a target="_parent" href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
})();
body {
margin: 0;
}
html, body, #map {
width: 100%;
height: 100%;
}
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<div id="map"></div>