From 80dbf17b3a1e060c43191788747a1ac536465895 Mon Sep 17 00:00:00 2001 From: Mhayk Whandson Date: Wed, 15 Jul 2026 15:34:46 +0100 Subject: [PATCH] Fix removeSource throwing TypeError in Terrain.update under globe projection Style#_createTerrain left terrain.properties uninitialised until the first Style#update ran from Map#_render. Under globe projection, the draping terrain created on style load could therefore be consumed by Terrain#update (via Map#removeSource -> Map#_updateTerrain) before the properties were evaluated, throwing 'TypeError: Cannot read properties of undefined (reading get)'. Evaluate the terrain properties synchronously in Style#_createTerrain so they are always available to consumers. Fixes #13665 Fixes #13703 --- src/style/style.ts | 4 ++++ test/unit/terrain/terrain.test.ts | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/style/style.ts b/src/style/style.ts index b73c5904e7b..8e607eefbd6 100644 --- a/src/style/style.ts +++ b/src/style/style.ts @@ -4306,6 +4306,10 @@ class Style extends Evented { this._force3DLayerUpdate(); const parameters = this._getTransitionParameters({duration: 0}); terrain.updateTransitions(parameters); + // Evaluate the terrain properties synchronously so that consumers such as + // Terrain#update can access them before the next Style#update runs (#13665). + const evaluationParameters = new EvaluationParameters(this.z || 0, {...parameters, worldview: this.map.getWorldview()}); + terrain.recalculate(evaluationParameters); } _force3DLayerUpdate() { diff --git a/test/unit/terrain/terrain.test.ts b/test/unit/terrain/terrain.test.ts index 390d4a90481..8062451d6c7 100644 --- a/test/unit/terrain/terrain.test.ts +++ b/test/unit/terrain/terrain.test.ts @@ -163,6 +163,13 @@ describe('Elevation', () => { }); }); + test('mapbox-gl-js#13665: removeSource before the first render under globe projection does not throw', async () => { + const map = createMap({projection: 'globe'}); + await waitFor(map, 'style.load'); + map.addSource('repro-src', {type: 'geojson', data: {type: 'FeatureCollection', features: []}}); + expect(() => map.removeSource('repro-src')).not.toThrowError(); + }); + test('style diff=false removes dem source', async () => { const map = createMap(); await waitFor(map, "style.load");