基于 Leaflet 的缩放功能:在最后一层瓦片缺失时进行优化

文章介绍了如何优化Leaflet地图的缩放功能,以应对最后一层瓦片缺失或错误的情况。方法包括使用maxNativeZoom参数限制缩放级别,使用Leaflet.TileLayer.Fallback插件自动降级到较低缩放级别,以及针对错误图片的识别和处理。这些策略旨在提供更好的用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里写自定义目录标题


引言:Leaflet 是一个广泛使用的开源 JavaScript 库,用于创建交互式、可定制的地图应用程序。在 Leaflet 中,默认情况下,瓦片地图是通过切分成多个瓦片来展示的,这些瓦片组合在一起形成完整的地图。然而,当地图的最后一层瓦片存在缺失或不完整时,可能会导致用户在缩放时出现显示问题。本文将介绍如何优化 Leaflet 缩放功能,以便在最后一层瓦片缺失时提供更好的用户体验

第一种方式

您可以使用参数 maxNativeZoom 来定义从哪一层瓦片开始进行缩放。具体来说,maxNativeZoom 参数用于设置地图的最大本机缩放级别,即地图所使用的原始瓦片的最高缩放级别

缺点:必须指定哪一层,但是瓦片的资源,最后一层是不确定的,打个比方,深圳的最后一层是18层,舟山港口的最后一层是17层,那这就不完美了,除非地图是指定区域使用,比如惠州某园区

var tileLayer = L.tileLayer(url, {
    maxZoom: 23,
    maxNativeZoom: 18,
});

第二种方式

基于第一种方式的缺点,做了优化,基于插件
Leaflet.TileLayer.Fallback(添加链接描述
使用此插件,您无需显式指定 maxNativezoom 选项。平铺层首先尝试获取当前缩放的平铺图像,但在服务器没有该缩放级别的图像并返回404错误的区域,平铺层将自动请求较低的缩放。

// 引用以上插件后这样设置就好
var myTileLayer = L.tileLayer.fallback(url, {
	maxZoom: 22,
});
myTileLayer.addTo(map);

第三种方式

第二种方式只能在返回404错误时监听,这就有个问题了,比如他不是返回404错误,而是返回一张错误的图片,比如
在这里插入图片描述

到这里已经技穷,所以基于Leaflet.TileLayer.Fallback 插件源码做了一点小修改,由于这张错误瓦片没有什么唯一标识,比如url和坐标都不能作为唯一标识,所以打算用图片的responseText的长度作为图片的的唯一标识,源码在最后面
在这里插入图片描述

Leaflet.TileLayer.Fallback 插件 fallback.js,做了改动后的代码,缺点是多请求了一遍瓦片

L.TileLayer.Fallback = L.TileLayer.extend({
    options: {
        minNativeZoom: 0,
    },

    initialize: function (urlTemplate, options) {
        L.TileLayer.prototype.initialize.call(this, urlTemplate, options);
    },

    createTile: function (coords, done) {
        var tile = L.TileLayer.prototype.createTile.call(this, coords, done);
        tile._originalCoords = coords;
        tile._originalSrc = tile.src;

        return tile;
    },

    _createCurrentCoords: function (originalCoords) {
        var currentCoords = this._wrapCoords(originalCoords);

        currentCoords.fallback = true;

        return currentCoords;
    },

    _originalTileOnError: L.TileLayer.prototype._tileOnError,

    _tileOnError: function (done, tile, e) {
        // console.log('zoule');
        var layer = this, // `this` is bound to the Tile Layer in L.TileLayer.prototype.createTile.
            originalCoords = tile._originalCoords,
            currentCoords = (tile._currentCoords = tile._currentCoords || layer._createCurrentCoords(originalCoords)),
            fallbackZoom = (tile._fallbackZoom = tile._fallbackZoom === undefined ? originalCoords.z - 1 : tile._fallbackZoom - 1),
            scale = (tile._fallbackScale = (tile._fallbackScale || 1) * 2),
            tileSize = layer.getTileSize(),
            style = tile.style,
            newUrl,
            top,
            left;

        // If no lower zoom tiles are available, fallback to errorTile.
        if (fallbackZoom < layer.options.minNativeZoom) {
            return this._originalTileOnError(done, tile, e);
        }

        // Modify tilePoint for replacement img.
        currentCoords.z = fallbackZoom;
        currentCoords.x = Math.floor(currentCoords.x / 2);
        currentCoords.y = Math.floor(currentCoords.y / 2);

        // Generate new src path.
        newUrl = layer.getTileUrl(currentCoords);

        // Zoom replacement img.
        style.width = tileSize.x * scale + 'px';
        style.height = tileSize.y * scale + 'px';

        // Compute margins to adjust position.
        top = (originalCoords.y - currentCoords.y * scale) * tileSize.y;
        style.marginTop = -top + 'px';
        left = (originalCoords.x - currentCoords.x * scale) * tileSize.x;
        style.marginLeft = -left + 'px';

        // Crop (clip) image.
        // `clip` is deprecated, but browsers support for `clip-path: inset()` is far behind.
        // http://caniuse.com/#feat=css-clip-path
        style.clip = 'rect(' + top + 'px ' + (left + tileSize.x) + 'px ' + (top + tileSize.y) + 'px ' + left + 'px)';

        layer.fire('tilefallback', {
            tile: tile,
            url: tile._originalSrc,
            urlMissing: tile.src,
            urlFallback: newUrl,
        });

        tile.src = newUrl;
    },

    _tileOnLoad: function (done, tile, e) {
        var layer = this, // `this` is bound to the Tile Layer in L.TileLayer.prototype.createTile.
            originalCoords = tile._originalCoords,
            fallbackZoom = (tile._fallbackZoom = tile._fallbackZoom === undefined ? originalCoords.z - 1 : tile._fallbackZoom - 1);
        if (fallbackZoom > 17) {
            var xhr = new XMLHttpRequest();
            xhr.onload = function () {
                if (xhr.status === 200) {
                    if (xhr.responseText.length == 2423) {
                        var currentCoords = (tile._currentCoords = tile._currentCoords || layer._createCurrentCoords(originalCoords)),
                            scale = (tile._fallbackScale = (tile._fallbackScale || 1) * 2),
                            tileSize = layer.getTileSize(),
                            style = tile.style,
                            newUrl,
                            top,
                            left;
                        currentCoords.z = fallbackZoom;
                        currentCoords.x = Math.floor(currentCoords.x / 2);
                        currentCoords.y = Math.floor(currentCoords.y / 2);
                        // Generate new src path.
                        newUrl = layer.getTileUrl(currentCoords);
                        // Zoom replacement img.
                        style.width = tileSize.x * scale + 'px';
                        style.height = tileSize.y * scale + 'px';
                        // Compute margins to adjust position.
                        top = (originalCoords.y - currentCoords.y * scale) * tileSize.y;
                        style.marginTop = -top + 'px';
                        left = (originalCoords.x - currentCoords.x * scale) * tileSize.x;
                        style.marginLeft = -left + 'px';
                        // Crop (clip) image.
                        // `clip` is deprecated, but browsers support for `clip-path: inset()` is far behind.
                        // http://caniuse.com/#feat=css-clip-path
                        style.clip = 'rect(' + top + 'px ' + (left + tileSize.x) + 'px ' + (top + tileSize.y) + 'px ' + left + 'px)';
                        layer.fire('tilefallback', {
                            tile: tile,
                            url: tile._originalSrc,
                            urlMissing: tile.src,
                            urlFallback: newUrl,
                        });
                        tile.src = newUrl;
                    } else {
                        done(null, tile);
                    }
                }
            };
            // 发送请求获取瓦片文件
            xhr.open('GET', tile.src, true);
            xhr.send();
        } else {
            done(null, tile);
        }
    },
    getTileUrl: function (coords) {
        var z = (coords.z = coords.fallback ? coords.z : this._getZoomForUrl());

        var data = {
            r: L.Browser.retina ? '@2x' : '',
            s: this._getSubdomain(coords),
            x: coords.x,
            y: coords.y,
            z: z,
        };
        if (this._map && !this._map.options.crs.infinite) {
            var invertedY = this._globalTileRange.max.y - coords.y;
            if (this.options.tms) {
                data['y'] = invertedY;
            }
            data['-y'] = invertedY;
        }

        return L.Util.template(this._url, L.extend(data, this.options));
    },
});

// Supply with a factory for consistency with Leaflet.
L.tileLayer.fallback = function (urlTemplate, options) {
    return new L.TileLayer.Fallback(urlTemplate, options);
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值