You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从OpenLayers 8.2.0升级到10.2.1致自定义缓存瓦片加载函数失效

OpenLayers 8.2.0 升级到 10.2.1 后矢量瓦片预加载失效导致闪烁问题的解决方案

问题根源分析

从OpenLayers 8.x到10.x的版本迭代中,VectorTile的加载机制发生了核心变更:

  • OL9+开始限制tile.setLoader()的自定义使用场景,VectorTileSource内部会接管瓦片的加载流程,直接在tileLoadFunction中设置loader会被内部逻辑覆盖,导致自定义的缓存判断和预加载逻辑无法正确触发。
  • 异步加载的处理逻辑更严格,原代码中依赖loader的异步回调执行预加载,可能因OL内部的状态管理被忽略。

修复方案

重构自定义tileLoadFunction,跳过tile.setLoader(),直接在tileLoadFunction内完成缓存校验、加载逻辑和预触发:

修改后的代码

import { VectorTileSource, VectorTile } from 'ol/source';
import { MVT } from 'ol/format';
import { Extent, ProjectionLike } from 'ol/proj';
import { Feature, Geometry } from 'ol';
import axios from 'axios';
import { useVectorTileStore } from './your-store-path'; // 替换为实际路径

export class VectorTileSourceWithCache extends VectorTileSource {
    constructor(options: any) { // 可根据OL10的Options类型调整
        super(options);
        this.tileLoadFunction = this.tileLoadFunctionWithCache.bind(this);
    }

    private async preloadAllInstances(baseUrl: string, tile: VectorTile, extent: Extent, projection: ProjectionLike): Promise<void> {
        const vectorTileStore = useVectorTileStore();
        const instants = [/* 你的时刻数组 */];
        const urls: string[] = [];
        
        for (const instant of instants) {
            // 构建对应时刻的瓦片URL
            const url = this.buildInstantUrl(baseUrl, instant); // 替换为实际URL构建逻辑
            urls.push(url);
        }

        vectorTileStore.addUlrsToPreloadQueue(urls, tile, extent, projection);
    }

    // 新增URL构建辅助方法(可选,优化代码结构)
    private buildInstantUrl(baseUrl: string, instant: any): string {
        // 示例:替换URL中的时刻参数
        return baseUrl.replace('{instant}', instant.toString());
    }

    private tileLoadFunctionWithCache(tile: VectorTile, url: string): void {
        const vectorTileStore = useVectorTileStore();
        const format = tile.getFormat() as MVT;
        const tileExtent = tile.getExtent();
        const tileProjection = tile.getProjection();

        // 1. 检查瓦片是否在产品边界内
        if (!vectorTileStore.isTileInProductBounds(url)) {
            tile.setFeatures([] as Array<Feature<Geometry>>);
            this.preloadAllInstances(url, tile, tileExtent, tileProjection).catch(console.error);
            return;
        }

        // 2. 检查缓存是否存在
        if (vectorTileStore.isTileLoaded(url)) {
            const data = vectorTileStore.getDataByUrl(url);
            if (data) {
                const features = format.readFeatures(data, {
                    extent: tileExtent,
                    featureProjection: tileProjection
                });
                tile.setFeatures(features as Array<Feature<Geometry>>);
            } else {
                tile.setFeatures([] as Array<Feature<Geometry>>);
            }
            this.preloadAllInstances(url, tile, tileExtent, tileProjection).catch(console.error);
            return;
        }

        // 3. 缓存不存在时发起请求
        console.log("PBF not found, requesting...");
        axios.get(url, { responseType: "arraybuffer" })
            .then(response => {
                if (response.status !== 200) {
                    tile.setFeatures([] as Array<Feature<Geometry>>);
                    return;
                }
                const data = response.data;
                const features = format.readFeatures(data, {
                    extent: tileExtent,
                    featureProjection: tileProjection
                });
                tile.setFeatures(features as Array<Feature<Geometry>>);
                vectorTileStore.addFeatures(url, data, format, tileExtent, tileProjection);
                // 触发预加载
                this.preloadAllInstances(url, tile, tileExtent, tileProjection).catch(console.error);
            })
            .catch(error => {
                console.error("Error loading tile:", error);
                tile.setFeatures([] as Array<Feature<Geometry>>);
                this.preloadAllInstances(url, tile, tileExtent, tileProjection).catch(console.error);
            });
    }
}

关键修改点

  1. 移除tile.setLoader():直接在tileLoadFunction中处理所有加载逻辑,避免被OL内部逻辑覆盖。
  2. 使用瓦片自身的extent和projection:替换原loader参数中的extent和projection,确保与OL10中瓦片的状态一致。
  3. 预加载逻辑独立触发:在每一个分支(边界判断、缓存命中、加载完成/失败)都触发预加载,确保逻辑不遗漏。
  4. 异步错误处理:预加载的异步调用添加catch,避免未处理的Promise异常阻塞流程。

额外检查项

  • 确认vectorTileStore的缓存键(URL)与OL10中瓦片请求的URL完全一致,避免因URL参数顺序、编码差异导致缓存不命中。
  • 检查预加载队列的处理逻辑,确保预加载的瓦片数据被正确存入缓存,且后续请求能正确读取。

内容的提问来源于stack exchange,提问作者Joan Morilla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 18:35:07