确保在打开的图层中加载所有图块3 XYZ源

时间:2015-10-11 03:25:33

标签: openlayers-3 tiles

我们有一些使用ol.source.XYZ来源的图层。对于加载策略,我们使用ol.loadingstrategy.tile(new ol.tilegrid.createXYZ({}))。在继续执行其他操作之前,我们需要确保所有切片都已在地图视图中完全加载。

我们遇到过多篇关于此的文章,但尚未找到100%的解决方案,这将为我们提供所需的解决方案。即使情况并非如此,逻辑也会返回true。我们已经尝试使用example页面上显示的tileloadstart,tileloadend,tileloaderror事件,但这似乎并不总是返回预期的结果。

GIS Stack Exchange文章here似乎很有希望,因为我们可以将下面列出的代码与tileloadstart / tileloadend事件结合使用,但是有许多函数调用仅在 ol-debug中可用.js 而不是 ol.js 源代码。因此,下面粘贴的代码不适用于 ol.js 。此代码只是引用的GIS Stack Exchange文章的副本。

function calculateNumberOfTiles(tileSource) {
     var tg = (tileSource.getTileGrid()) ? tileSource.getTileGrid(): ol.tilegrid.getForProjection(map.getView().getProjection()), 
            z = tg.getZForResolution(map.getView().getResolution()),
            tileRange = tg.getTileRangeForExtentAndZ(map.getView().calculateExtent(map.getSize()), z),
            xTiles = tileRange['maxX'] - tileRange['minX'] + 1,
            yTiles = tileRange['maxY'] - tileRange['minY'] + 1;
        return xTiles * yTiles;
}

我有两个问题,任何人都可以提供我们可能缺少的任何其他想法吗?谢谢你的帮助。

  1. 为什么函数调用在ol-debug.js中而不是ol.js中,当它们挂起tilegrid对象的原型时?
  2. 如何告诉所有图块在地图中完全加载的任何其他建议?

3 个答案:

答案 0 :(得分:9)

加载活动

您认为源上的每个tileloadstart事件后面都应跟有相应图块的tileloadendtileloaderror,这是正确的。如在链接的官方示例中那样,可以使用它来跟踪加载瓦片的数量。

当发出的tileloadendtileloaderror事件的总和等于tileloadstart事件的数量时,不会进行任何加载。如果不是这种情况,您应该尝试制作一个可重现的示例,因为它可能是库中的错误。

了解这些事件的意义非常重要。 tileloadend事件并不意味着图块在地图上可见,这意味着图块已完成加载并可用于渲染。在调用事件处理程序之后,将完成tile的实际呈现。因此,任何需要关于何时加载和渲染所有图块(例如拍摄屏幕截图/创建图片时)的信息的图块加载逻辑都必须等到下一个postrender事件。

你提到tileloadend和实际出现在地图上的图块之间的5-10秒,这对于渲染相关太长了(除非你做了一些非常怪异的渲染回调)。

ol-debug.js vs ol.js

与许多JS库一样,OpenLayers代码在构建过程中进行了优化和最小化,以创建更小,更高效的构建。任何不属于API的类型或功能都将被缩小或删除。只应使用ol.js中可用的方法,并记录在openlayers.org上,因为任何缩小的方法都可能会改变每个构建。

ol-debug.js是该库的非优化版本,旨在用于调试或探索。

答案 1 :(得分:6)

这是我的方法。它使用未记录的API,但它适用于非调试openlayers 4.2.0。

//"Dirty" tiles can be in one of two states: Either they are being downloaded,
//or the map is holding off downloading their replacement, and they are "wanted."
//We can tell when the map is ready when there are no tiles in either of these
//states, and rendering is done.

var numInFlightTiles = 0;
map.getLayers().forEach(function (layer) {
    var source = layer.getSource();
    if (source instanceof ol.source.TileImage) {
        source.on('tileloadstart', function () {++numInFlightTiles})
        source.on('tileloadend', function () {--numInFlightTiles})
    }
})

map.on('postrender', function (evt) {
    if (!evt.frameState)
        return;

    var numHeldTiles = 0;
    var wanted = evt.frameState.wantedTiles;
    for (var layer in wanted)
        if (wanted.hasOwnProperty(layer))
            numHeldTiles += Object.keys(wanted[layer]).length;

    var ready = numInFlightTiles === 0 && numHeldTiles === 0;
    if (map.get('ready') !== ready)
        map.set('ready', ready);
});

map.set('ready', false);

function whenMapIsReady(callback) {
    if (map.get('ready'))
        callback();
    else
        map.once('change:ready', whenMapIsReady.bind(null, callback));
}

答案 2 :(得分:0)

此答案已经过时,不适用于openlayers 6或更高版本。

如此处所述:https://github.com/openlayers/openlayers/releases/tag/v6.0.0

一个解决方案是将后期渲染放置在这样的层上

const mapLayer = map.getLayers().getArray()[0];

mapLayer.on('postrender', (evt) => {});

这将在渲染第一层后触发后期渲染