diff --git a/packages/deck.gl-geotiff/src/cog-layer.ts b/packages/deck.gl-geotiff/src/cog-layer.ts index 92c268d1..f58d7c15 100644 --- a/packages/deck.gl-geotiff/src/cog-layer.ts +++ b/packages/deck.gl-geotiff/src/cog-layer.ts @@ -202,7 +202,9 @@ export class COGLayer< // Clear stale state so renderLayers returns null until the new GeoTIFF is // ready this.clearState(); - this._parseGeoTIFF(); + this._parseGeoTIFF().catch((error: Error) => + this.raiseError(error, "loading GeoTIFF"), + ); } } @@ -219,11 +221,17 @@ export class COGLayer< const signal = this.state.abortController?.signal; let geotiff: GeoTIFF; + let sourceProjection: ProjectionDefinition; try { geotiff = await fetchGeoTIFF(this.props.geotiff, { concurrencyLimiter: this.props.concurrencyLimiter, signal, }); + const crs = geotiff.crs; + sourceProjection = + typeof crs === "number" + ? await this.props.epsgResolver!(crs) + : parseWkt(crs); } catch (err) { // Layer removed mid-open (finalizeState aborted the signal); drop it. if (signal?.aborted) { @@ -231,11 +239,6 @@ export class COGLayer< } throw err; } - const crs = geotiff.crs; - const sourceProjection = - typeof crs === "number" - ? await this.props.epsgResolver!(crs) - : parseWkt(crs); if (signal?.aborted) { return; diff --git a/packages/deck.gl-geotiff/src/multi-cog-layer.ts b/packages/deck.gl-geotiff/src/multi-cog-layer.ts index 7b50a574..ef0697df 100644 --- a/packages/deck.gl-geotiff/src/multi-cog-layer.ts +++ b/packages/deck.gl-geotiff/src/multi-cog-layer.ts @@ -384,7 +384,9 @@ export class MultiCOGLayer extends RasterTileLayer< sources: null, multiDescriptor: null, }); - this._parseAllSources(); + this._parseAllSources().catch((error: Error) => + this.raiseError(error, "loading COG sources"), + ); } } diff --git a/packages/deck.gl-geotiff/tests/cog-layer.test.ts b/packages/deck.gl-geotiff/tests/cog-layer.test.ts index 2b951866..8f5db54c 100644 --- a/packages/deck.gl-geotiff/tests/cog-layer.test.ts +++ b/packages/deck.gl-geotiff/tests/cog-layer.test.ts @@ -77,3 +77,25 @@ describe("COGLayer._onTileUnloadCallback", () => { expect(unloadCallback(layer)).toBe(onTileUnload); }); }); + +describe("COGLayer.updateState", () => { + it("raises a GeoTIFF open failure through onError", async () => { + const onError = vi.fn((_error: Error) => true); + const layer = new COGLayer({ + id: "cog", + geotiff: "https://example.com/x.tif", + onError, + } as never); + vi.spyOn(layer, "clearState").mockImplementation(() => {}); + vi.spyOn(layer, "_parseGeoTIFF").mockRejectedValue(new Error("boom")); + + layer.updateState({ + props: layer.props, + oldProps: layer.props, + changeFlags: { dataChanged: true }, + } as never); + + await vi.waitFor(() => expect(onError).toHaveBeenCalledOnce()); + expect(onError.mock.calls[0]?.[0]?.message).toBe("loading GeoTIFF: boom"); + }); +}); diff --git a/packages/deck.gl-geotiff/tests/multi-cog-layer.test.ts b/packages/deck.gl-geotiff/tests/multi-cog-layer.test.ts index bc7317ee..39702b01 100644 --- a/packages/deck.gl-geotiff/tests/multi-cog-layer.test.ts +++ b/packages/deck.gl-geotiff/tests/multi-cog-layer.test.ts @@ -54,3 +54,27 @@ describe("MultiCOGLayer._onTileUnloadCallback", () => { expect(() => cb?.({ content: null })).not.toThrow(); }); }); + +describe("MultiCOGLayer.updateState", () => { + it("raises a COG open failure through onError", async () => { + const onError = vi.fn((_error: Error) => true); + const layer = new MultiCOGLayer({ + id: "multi", + sources: { a: { url: "https://example.com/a.tif" } }, + onError, + } as never); + vi.spyOn(layer, "setState").mockImplementation(() => {}); + vi.spyOn(layer, "_parseAllSources").mockRejectedValue(new Error("boom")); + + layer.updateState({ + props: layer.props, + oldProps: layer.props, + changeFlags: { dataChanged: true }, + } as never); + + await vi.waitFor(() => expect(onError).toHaveBeenCalledOnce()); + const error = onError.mock.calls[0]?.[0]; + expect(error?.message).toBe("loading COG sources: boom"); + expect(error?.cause).toBeInstanceOf(Error); + }); +});