首页 文章

如何使用Leaflet Map 'chunky'(粗粒度)缩放级别?

提问于
浏览
2

我使用Leaflet.js作为基于桌面和移动浏览器的 Map ,并且需要支持各种 Map 图块服务 . 其中一些切片服务使用粗略缩放级别(如1,5,10,15)定义,如果我请求不受支持的缩放级别,则服务不会返回切片 . (例如,如果我在不支持缩放级别 6 时请求 service/tiles/6/x/y ) .

Leaflet tile layers支持 minZoommaxZoom ,但我想弄清楚是否有最佳做法来进行粗略缩放级别,以及其他人是否遇到过此问题 .

我在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 ,而是特定于域和移动应用程序,其中空间和带宽非常宝贵 . )

谢谢!

UPDATE :我发现我使用此代码的问题是 map.setZoom(z) 在从 zoomEnd 处理程序中调用时无法正常工作(它确实设置了缩放,但导致显示灰色/不存在的图块问题,可能是因为Leaflet仍在缩放/缩放的过程) . 修复是使用 setTimeout 将调用延迟到 setZoom() . 但是,我'm still really curious if anybody else has dealt with this, and if there is a '更好的方式'...(我更新了以上代码以使用 setZoom 修复)

2 回答

  • 1

    目前在GitHub上的Leaflet存储库中正在审核一个提交 . 它将zoomFactor添加到 Map 的选项中 . 也许这就是你要找的东西 . 至少,我认为只要您的可用tileset具有缩放级别(不知道这是否是正确的技术术语)是最低可用缩放级别的倍数,它就会起作用 .

    见:https://github.com/Leaflet/Leaflet/pull/3285

  • 1

    以下(不保证,基于this)应与Leaflet v1.7.3一起使用,但可能不适用于当前的master .

    它使用 serverZooms 选项将tile服务器上的可用缩放级别指定为有序数组 .

    覆盖 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>
    

相关问题