From 6d69952e3304ad09871fe2d9909e2d7e1f9f2ba0 Mon Sep 17 00:00:00 2001 From: AlitaBernachot Date: Tue, 3 Jun 2025 18:10:25 +0200 Subject: [PATCH 1/3] fix(tms): handle error also for TMS/VectorTiles --- .../openlayers/lib/map/create-map.test.ts | 12 ++++++++++- packages/openlayers/lib/map/create-map.ts | 21 ++++++++----------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/packages/openlayers/lib/map/create-map.test.ts b/packages/openlayers/lib/map/create-map.test.ts index 7007928..7ff6115 100644 --- a/packages/openlayers/lib/map/create-map.test.ts +++ b/packages/openlayers/lib/map/create-map.test.ts @@ -25,7 +25,6 @@ import { MapContextLayer, MapContextLayerGeojson, MapContextLayerWms, - SourceLoadErrorEvent, } from "@geospatial-sdk/core"; import Layer from "ol/layer/Layer"; import { @@ -44,6 +43,7 @@ import { import { ImageTile } from "ol"; import TileState from "ol/TileState.js"; import VectorTileLayer from "ol/layer/VectorTile"; +import MVT from "ol/format/MVT"; vi.mock("./handle-errors", async (importOriginal) => { const actual = await importOriginal(); @@ -415,6 +415,16 @@ describe("MapContextService", () => { expect(layer.getOpacity()).toBe(1); expect(layer.get("label")).toBeUndefined(); }); + it("should set tileLoadErrorCatchFunction to handle errors", () => { + const source = layer.getSource() as VectorTile; + const tileLoadFunction = source.getTileLoadFunction(); + expect(tileLoadFunction).toBeInstanceOf(Function); + const tile = new VectorTile({ + format: new MVT(), + }); + tileLoadFunction(tile, "http://example.com/tms/tile"); + expect(tileLoadErrorCatchFunction).toHaveBeenCalled(); + }); }); }); diff --git a/packages/openlayers/lib/map/create-map.ts b/packages/openlayers/lib/map/create-map.ts index 1313024..aaadec8 100644 --- a/packages/openlayers/lib/map/create-map.ts +++ b/packages/openlayers/lib/map/create-map.ts @@ -46,28 +46,25 @@ export async function createLayer(layerModel: MapContextLayer): Promise { switch (type) { case "xyz": { + const tileLoadFunction = (tile: Tile, src: string) => + tileLoadErrorCatchFunction(layer as TileLayer, tile, src); if (layerModel.tileFormat === "application/vnd.mapbox-vector-tile") { layer = new VectorTileLayer({ source: new VectorTile({ format: new MVT(), url: layerModel.url, attributions: layerModel.attributions, + tileLoadFunction, }), }); } else { - layer = new TileLayer({}); - const source = new XYZ({ - url: layerModel.url, - attributions: layerModel.attributions, - }); - source.setTileLoadFunction(function (tile: Tile, src: string) { - return tileLoadErrorCatchFunction( - layer as TileLayer, - tile, - src, - ); + layer = new TileLayer({ + source: new XYZ({ + url: layerModel.url, + attributions: layerModel.attributions, + tileLoadFunction, + }), }); - layer.setSource(source); } } break; From 34160970b339b5ffb12378aabe463beb47cec28b Mon Sep 17 00:00:00 2001 From: AlitaBernachot Date: Wed, 4 Jun 2025 18:44:44 +0200 Subject: [PATCH 2/3] refactor(xyz): make sure layer is set before setting tileLoadError function --- packages/openlayers/lib/map/create-map.test.ts | 6 +++++- packages/openlayers/lib/map/create-map.ts | 11 +++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/openlayers/lib/map/create-map.test.ts b/packages/openlayers/lib/map/create-map.test.ts index 7ff6115..459da83 100644 --- a/packages/openlayers/lib/map/create-map.test.ts +++ b/packages/openlayers/lib/map/create-map.test.ts @@ -97,7 +97,11 @@ describe("MapContextService", () => { () => {}, ); tileLoadFunction(tile, "http://example.com/tile"); - expect(tileLoadErrorCatchFunction).toHaveBeenCalled(); + expect(tileLoadErrorCatchFunction).toHaveBeenCalledWith( + layer, + tile, + "http://example.com/tile", + ); }); }); describe("OGCAPI", () => { diff --git a/packages/openlayers/lib/map/create-map.ts b/packages/openlayers/lib/map/create-map.ts index aaadec8..ab21ef1 100644 --- a/packages/openlayers/lib/map/create-map.ts +++ b/packages/openlayers/lib/map/create-map.ts @@ -40,21 +40,20 @@ import VectorTile from "ol/source/VectorTile"; const GEOJSON = new GeoJSON(); const WFS_MAX_FEATURES = 10000; +type TileSource = VectorTile | XYZ; + export async function createLayer(layerModel: MapContextLayer): Promise { const { type } = layerModel; let layer: Layer | undefined; switch (type) { case "xyz": { - const tileLoadFunction = (tile: Tile, src: string) => - tileLoadErrorCatchFunction(layer as TileLayer, tile, src); if (layerModel.tileFormat === "application/vnd.mapbox-vector-tile") { layer = new VectorTileLayer({ source: new VectorTile({ format: new MVT(), url: layerModel.url, attributions: layerModel.attributions, - tileLoadFunction, }), }); } else { @@ -62,10 +61,14 @@ export async function createLayer(layerModel: MapContextLayer): Promise { source: new XYZ({ url: layerModel.url, attributions: layerModel.attributions, - tileLoadFunction, }), }); } + + const source = layer.getSource(); + (source).setTileLoadFunction((tile: Tile, src: string) => + tileLoadErrorCatchFunction(layer as TileLayer, tile, src), + ); } break; case "wms": From 3c22df585028174ff460693e0565bc7f39e9ce1d Mon Sep 17 00:00:00 2001 From: AlitaBernachot Date: Wed, 4 Jun 2025 21:23:30 +0200 Subject: [PATCH 3/3] fix(tms): specific tileloaf function for vector tile --- packages/openlayers/lib/map/create-map.test.ts | 16 ++++++++-------- packages/openlayers/lib/map/create-map.ts | 18 +++++++++++------- .../openlayers/lib/map/handle-errors.test.ts | 7 +++++-- packages/openlayers/lib/map/handle-errors.ts | 18 ++++++++++++++++-- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/packages/openlayers/lib/map/create-map.test.ts b/packages/openlayers/lib/map/create-map.test.ts index 459da83..80dcf8d 100644 --- a/packages/openlayers/lib/map/create-map.test.ts +++ b/packages/openlayers/lib/map/create-map.test.ts @@ -38,7 +38,7 @@ import { VectorTile } from "ol/source"; import { MapboxVectorLayer } from "ol-mapbox-style"; import { handleEndpointError, - tileLoadErrorCatchFunction, + imageTileLoadErrorCatchFunction, } from "./handle-errors"; import { ImageTile } from "ol"; import TileState from "ol/TileState.js"; @@ -49,7 +49,7 @@ vi.mock("./handle-errors", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, - tileLoadErrorCatchFunction: vi.fn(), + imageTileLoadErrorCatchFunction: vi.fn(), handleEndpointError: vi.fn(), }; }); @@ -85,7 +85,7 @@ describe("MapContextService", () => { "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png", ); }); - it("should set tileLoadErrorCatchFunction to handle errors", () => { + it("should set imageTileLoadErrorCatchFunction to handle errors", () => { const source = layer.getSource() as XYZ; const tileLoadFunction = source.getTileLoadFunction(); expect(tileLoadFunction).toBeInstanceOf(Function); @@ -97,7 +97,7 @@ describe("MapContextService", () => { () => {}, ); tileLoadFunction(tile, "http://example.com/tile"); - expect(tileLoadErrorCatchFunction).toHaveBeenCalledWith( + expect(imageTileLoadErrorCatchFunction).toHaveBeenCalledWith( layer, tile, "http://example.com/tile", @@ -170,7 +170,7 @@ describe("MapContextService", () => { const gutter = source["gutter_"]; expect(gutter).toBe(20); }); - it("should set tileLoadErrorCatchFunction to handle errors", () => { + it("should set imageTileLoadErrorCatchFunction to handle errors", () => { const source = layer.getSource() as TileWMS; const tileLoadFunction = source.getTileLoadFunction(); expect(tileLoadFunction).toBeInstanceOf(Function); @@ -182,7 +182,7 @@ describe("MapContextService", () => { () => {}, ); tileLoadFunction(tile, "http://example.com/tile"); - expect(tileLoadErrorCatchFunction).toHaveBeenCalled(); + expect(imageTileLoadErrorCatchFunction).toHaveBeenCalled(); }); }); @@ -419,7 +419,7 @@ describe("MapContextService", () => { expect(layer.getOpacity()).toBe(1); expect(layer.get("label")).toBeUndefined(); }); - it("should set tileLoadErrorCatchFunction to handle errors", () => { + it("should set imageTileLoadErrorCatchFunction to handle errors", () => { const source = layer.getSource() as VectorTile; const tileLoadFunction = source.getTileLoadFunction(); expect(tileLoadFunction).toBeInstanceOf(Function); @@ -427,7 +427,7 @@ describe("MapContextService", () => { format: new MVT(), }); tileLoadFunction(tile, "http://example.com/tms/tile"); - expect(tileLoadErrorCatchFunction).toHaveBeenCalled(); + expect(imageTileLoadErrorCatchFunction).toHaveBeenCalled(); }); }); }); diff --git a/packages/openlayers/lib/map/create-map.ts b/packages/openlayers/lib/map/create-map.ts index ab21ef1..5da1885 100644 --- a/packages/openlayers/lib/map/create-map.ts +++ b/packages/openlayers/lib/map/create-map.ts @@ -33,7 +33,8 @@ import { MapboxVectorLayer } from "ol-mapbox-style"; import { Tile } from "ol"; import { handleEndpointError, - tileLoadErrorCatchFunction, + imageTileLoadErrorCatchFunction, + vectorTileLoadErrorCatchFunction, } from "./handle-errors"; import VectorTile from "ol/source/VectorTile"; @@ -56,6 +57,10 @@ export async function createLayer(layerModel: MapContextLayer): Promise { attributions: layerModel.attributions, }), }); + const source = layer.getSource(); + source.setTileLoadFunction((tile: Tile, src: string) => + vectorTileLoadErrorCatchFunction(layer, tile, src), + ); } else { layer = new TileLayer({ source: new XYZ({ @@ -63,12 +68,11 @@ export async function createLayer(layerModel: MapContextLayer): Promise { attributions: layerModel.attributions, }), }); + const source = layer.getSource(); + source.setTileLoadFunction((tile: Tile, src: string) => + imageTileLoadErrorCatchFunction(layer, tile, src), + ); } - - const source = layer.getSource(); - (source).setTileLoadFunction((tile: Tile, src: string) => - tileLoadErrorCatchFunction(layer as TileLayer, tile, src), - ); } break; case "wms": @@ -84,7 +88,7 @@ export async function createLayer(layerModel: MapContextLayer): Promise { attributions: layerModel.attributions, }); source.setTileLoadFunction(function (tile: Tile, src: string) { - return tileLoadErrorCatchFunction( + return imageTileLoadErrorCatchFunction( layer as TileLayer, tile, src, diff --git a/packages/openlayers/lib/map/handle-errors.test.ts b/packages/openlayers/lib/map/handle-errors.test.ts index 4f76c09..e5667cf 100644 --- a/packages/openlayers/lib/map/handle-errors.test.ts +++ b/packages/openlayers/lib/map/handle-errors.test.ts @@ -1,7 +1,10 @@ import { ImageTile, Tile } from "ol"; import TileState from "ol/TileState.js"; import { SourceLoadErrorEvent } from "@geospatial-sdk/core"; -import { handleTileError, tileLoadErrorCatchFunction } from "./handle-errors"; +import { + handleTileError, + imageTileLoadErrorCatchFunction, +} from "./handle-errors"; import { Map } from "ol"; import { describe } from "node:test"; import TileLayer from "ol/layer/Tile"; @@ -35,7 +38,7 @@ describe("handle-errors", () => { TileState.IDLE, "", null, - () => tileLoadErrorCatchFunction, + () => imageTileLoadErrorCatchFunction, ); }); diff --git a/packages/openlayers/lib/map/handle-errors.ts b/packages/openlayers/lib/map/handle-errors.ts index 227d50d..3c519b2 100644 --- a/packages/openlayers/lib/map/handle-errors.ts +++ b/packages/openlayers/lib/map/handle-errors.ts @@ -1,11 +1,12 @@ import { EndpointError } from "@camptocamp/ogc-client"; import { SourceLoadErrorEvent } from "@geospatial-sdk/core"; -import { ImageTile, Tile } from "ol"; +import { ImageTile, Tile, VectorTile } from "ol"; import { Layer } from "ol/layer"; import TileLayer from "ol/layer/Tile"; import VectorLayer from "ol/layer/Vector"; import TileSource from "ol/source/Tile"; import VectorSource from "ol/source/Vector"; +import { defaultLoadFunction } from "ol/source/VectorTile"; import TileState from "ol/TileState.js"; export function handleEndpointError( @@ -26,7 +27,20 @@ export function handleTileError( layer.dispatchEvent(new SourceLoadErrorEvent(response)); } -export function tileLoadErrorCatchFunction( +export function vectorTileLoadErrorCatchFunction( + layer: Layer, + tile: Tile, + url: string, +) { + try { + // TODO: WIP override? + defaultLoadFunction(tile, url); + } catch (e) { + handleTileError(e, tile, layer); + } +} + +export function imageTileLoadErrorCatchFunction( layer: Layer, tile: Tile, src: string,