From abbc512b2ece9aada687fddcac0bb733e395f0d7 Mon Sep 17 00:00:00 2001 From: Tristen Brown Date: Mon, 13 Nov 2023 03:18:21 -0500 Subject: [PATCH 01/44] Fix small spec inconsistencies with model types (#12968) - Provide a default value of `""` for `model-id` - Change `model-emissive-strength` unit value to `intensity` --- src/style-spec/reference/v8.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index c5cdbab1712..333ec6b7078 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -1269,6 +1269,7 @@ }, "model-id": { "type": "string", + "default": "", "doc": "Model to render.", "property-type": "data-driven", "expression": { @@ -8450,7 +8451,7 @@ "default": 0.0, "minimum": 0, "maximum": 5, - "units": "number", + "units": "intensity", "doc": "Strength of the emission. There is no emission for value 0. For value 1.0, only emissive component (no shading) is displayed and values above 1.0 produce light contribution to surrounding area, for some of the parts (e.g. doors). Expressions that depend on measure-light are not supported when using GeoJSON or vector tile as the model layer source.", "expression": { "interpolated": true, From d699447ebb5c2996b0ab168ff2ba19db86344b24 Mon Sep 17 00:00:00 2001 From: stampyzfanz <34364128+stampyzfanz@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:23:31 +1100 Subject: [PATCH 02/44] Fix a typo in videoSource documentation (#12957) --- src/source/video_source.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source/video_source.js b/src/source/video_source.js index 9c5682a077c..264c86eadc7 100644 --- a/src/source/video_source.js +++ b/src/source/video_source.js @@ -175,7 +175,7 @@ class VideoSource extends ImageSource { * // Add a video source to the map to map * map.addSource('video_source_id', { * type: 'video', - * url: [ + * urls: [ * 'https://www.mapbox.com/blog/assets/baltimore-smoke.mp4', * 'https://www.mapbox.com/blog/assets/baltimore-smoke.webm' * ], From e511f22eee7da3f9800b4ed26ff8ec41c6d0c88a Mon Sep 17 00:00:00 2001 From: Volodymyr Agafonkin Date: Mon, 13 Nov 2023 12:56:01 +0200 Subject: [PATCH 03/44] remove stats collection CI job (#12969) --- .circleci/config.yml | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a2cf39c8953..5609def78ca 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -92,15 +92,6 @@ workflows: filters: tags: only: /.*/ - - collect-stats: - requires: - - build - - install-mbx-ci - filters: - tags: - ignore: /.*/ - branches: - only: main - deploy-release: requires: - install-mbx-ci @@ -336,20 +327,6 @@ jobs: yarn run codegen git add -A && git diff --staged --exit-code | tee check.patch - collect-stats: - <<: *linux-defaults - steps: - - attach_workspace: - at: ~/ - - browser-tools/install-chrome - - run: - name: Collect performance stats - command: node bench/gl-stats.js - - aws-cli/install - - run: - name: Upload performance stats - command: aws s3 cp data.json.gz s3://mapbox-loading-dock/raw/gl_js.perf_metrics_staging/ci/`git show -s --date=short --format=%cd-%h HEAD`.json.gz - test-browser: <<: *linux-defaults steps: From c45f38feb941f61e91292e9475c175e5325fbaaf Mon Sep 17 00:00:00 2001 From: Volodymyr Agafonkin Date: Mon, 13 Nov 2023 13:35:29 +0200 Subject: [PATCH 04/44] Downgrade rollup to fix CodeBuild (#12962) (#12970) Co-authored-by: Stepan Kuzmin --- package.json | 2 +- yarn.lock | 80 +++------------------------------------------------- 2 files changed, 5 insertions(+), 77 deletions(-) diff --git a/package.json b/package.json index 95a5b893d8f..14116a8fbec 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "pretty-bytes": "^6.0.0", "puppeteer-core": "^21.5.0", "qrcode-terminal": "^0.12.0", - "rollup": "^4.3.0", + "rollup": "3.29.4", "rollup-plugin-sourcemaps": "^0.6.3", "rollup-plugin-unassert": "^0.6.0", "selenium-webdriver": "^4.15.0", diff --git a/yarn.lock b/yarn.lock index af2ce206e9e..ac3d13d4c04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -821,66 +821,6 @@ estree-walker "^2.0.2" picomatch "^2.3.1" -"@rollup/rollup-android-arm-eabi@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.3.0.tgz#8ad8a660b18f1a24ad4a272738a65ac4788a8811" - integrity sha512-/4pns6BYi8MXdwnXM44yoGAcFYVHL/BYlB2q1HXZ6AzH++LaiEVWFpBWQ/glXhbMbv3E3o09igrHFbP/snhAvA== - -"@rollup/rollup-android-arm64@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.3.0.tgz#17b0f412034d14668c8acc8b7cbd8b1c76279599" - integrity sha512-nLO/JsL9idr416vzi3lHm3Xm+QZh4qHij8k3Er13kZr5YhL7/+kBAx84kDmPc7HMexLmwisjDCeDIKNFp8mDlQ== - -"@rollup/rollup-darwin-arm64@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.3.0.tgz#80c4a4dd7b120906d4e655808fb9005784a8bf35" - integrity sha512-dGhVBlllt4iHwTGy21IEoMOTN5wZoid19zEIxsdY29xcEiOEHqzDa7Sqrkh5OE7LKCowL61eFJXxYe/+pYa7ZQ== - -"@rollup/rollup-darwin-x64@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.3.0.tgz#52ad0db40d9b5ae047dfc08e54e4b3f42feaef82" - integrity sha512-h8wRfHeLEbU3NzaP1Oku7BYXCJQiTRr+8U0lklyOQXxXiEpHLL8tk1hFl+tezoRKLcPJD7joKaK74ASsqt3Ekg== - -"@rollup/rollup-linux-arm-gnueabihf@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.3.0.tgz#2ad3d190af01d7fc8704e8e782c4a24006a9f21a" - integrity sha512-wP4VgR/gfV18sylTuym3sxRTkAgUR2vh6YLeX/GEznk5jCYcYSlx585XlcUcl0c8UffIZlRJ09raWSX3JDb4GA== - -"@rollup/rollup-linux-arm64-gnu@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.3.0.tgz#4f7ba42f779f06e93876755b7393c61676e2958a" - integrity sha512-v/14JCYVkqRSJeQbxFx4oUkwVQQw6lFMN7bd4vuARBc3X2lmomkxBsc+BFiIDL/BK+CTx5AOh/k9XmqDnKWRVg== - -"@rollup/rollup-linux-arm64-musl@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.3.0.tgz#64795a09dac02b4d779819509a793b93ba7e4c0d" - integrity sha512-tNhfYqFH5OxtRzfkTOKdgFYlPSZnlDLNW4+leNEvQZhwTJxoTwsZAAhR97l3qVry/kkLyJPBK+Q8EAJLPinDIg== - -"@rollup/rollup-linux-x64-gnu@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.3.0.tgz#00c1ff131ba16881eb1a0ad46b0aa10dcacb010e" - integrity sha512-pw77m8QywdsoFdFOgmc8roF1inBI0rciqzO8ffRUgLoq7+ee9o5eFqtEcS6hHOOplgifAUUisP8cAnwl9nUYPw== - -"@rollup/rollup-linux-x64-musl@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.3.0.tgz#89479dce5e5bf6850fbca92fa7f1637ddd70c9ef" - integrity sha512-tJs7v2MnV2F8w6X1UpPHl/43OfxjUy9SuJ2ZPoxn79v9vYteChVYO/ueLHCpRMmyTUIVML3N9z4azl9ENH8Xxg== - -"@rollup/rollup-win32-arm64-msvc@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.3.0.tgz#1a36aba17c7efe6d61e98b8049e70b40e33b1f45" - integrity sha512-OKGxp6kATQdTyI2DF+e9s+hB3/QZB45b6e+dzcfW1SUqiF6CviWyevhmT4USsMEdP3mlpC9zxLz3Oh+WaTMOSw== - -"@rollup/rollup-win32-ia32-msvc@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.3.0.tgz#a0b1f79afde51e390a7725b7c15ab4e0df780aea" - integrity sha512-DDZ5AH68JJ2ClQFEA1aNnfA7Ybqyeh0644rGbrLOdNehTmzfICHiWSn0OprzYi9HAshTPQvlwrM+bi2kuaIOjQ== - -"@rollup/rollup-win32-x64-msvc@4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.3.0.tgz#0b9bcc159b93c911efb5a2c39ec5d70dd0a589dc" - integrity sha512-dMvGV8p92GQ8jhNlGIKpyhVZPzJlT258pPrM5q2F8lKcc9Iv9BbfdnhX1OfinYWnb9ms5zLw6MlaMnqLfUkKnQ== - "@sinonjs/commons@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" @@ -7271,23 +7211,11 @@ rollup-plugin-unassert@^0.6.0: multi-stage-sourcemap "^0.3.1" unassert "^2.0.0" -rollup@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.3.0.tgz#198e6ae4355899db630d75bc0e17b53f5d0fc20e" - integrity sha512-scIi1NrKLDIYSPK66jjECtII7vIgdAMFmFo8h6qm++I6nN9qDSV35Ku6erzGVqYjx+lj+j5wkusRMr++8SyDZg== +rollup@3.29.4: + version "3.29.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.4.tgz#4d70c0f9834146df8705bfb69a9a19c9e1109981" + integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw== optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.3.0" - "@rollup/rollup-android-arm64" "4.3.0" - "@rollup/rollup-darwin-arm64" "4.3.0" - "@rollup/rollup-darwin-x64" "4.3.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.3.0" - "@rollup/rollup-linux-arm64-gnu" "4.3.0" - "@rollup/rollup-linux-arm64-musl" "4.3.0" - "@rollup/rollup-linux-x64-gnu" "4.3.0" - "@rollup/rollup-linux-x64-musl" "4.3.0" - "@rollup/rollup-win32-arm64-msvc" "4.3.0" - "@rollup/rollup-win32-ia32-msvc" "4.3.0" - "@rollup/rollup-win32-x64-msvc" "4.3.0" fsevents "~2.3.2" run-parallel@^1.1.9: From d92ba58ba7f1d46702be912e0a9906e59521b1ab Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Wed, 8 Nov 2023 18:24:15 +0200 Subject: [PATCH 05/44] [GLJS-559][GLJS-560] Group Worker sources by scope (internal-922) --- src/source/worker.js | 58 ++++++++++++++++++++------------- test/unit/source/worker.test.js | 17 +++++++++- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/src/source/worker.js b/src/source/worker.js index e9abdbcc771..84699f86480 100644 --- a/src/source/worker.js +++ b/src/source/worker.js @@ -40,7 +40,7 @@ export default class Worker { layerIndexes: {[mapId: string]: {[scope: string]: StyleLayerIndex }}; availableImages: {[mapId: string]: {[scope: string]: Array}}; workerSourceTypes: {[_: string]: Class }; - workerSources: {[_: string]: {[_: string]: {[_: string]: WorkerSource } } }; + workerSources: {[mapId: string]: {[scope: string]: {[sourceType: string]: {[sourceId: string]: WorkerSource}}}}; demWorkerSources: {[mapId: string]: {[scope: string]: {[sourceId: string]: RasterDEMTileWorkerSource }}}; projections: {[_: string]: Projection }; defaultProjection: Projection; @@ -68,7 +68,7 @@ export default class Worker { 'batched-model': Tiled3dModelWorkerSource }; - // [mapId][sourceType][sourceName] => worker source instance + // [mapId][scope][sourceType][sourceName] => worker source instance this.workerSources = {}; this.demWorkerSources = {}; @@ -107,36 +107,46 @@ export default class Worker { this.referrer = referrer; } - spriteLoaded(mapId: string, params: {scope: string, isLoaded: boolean}) { + spriteLoaded(mapId: string, {scope, isLoaded}: {scope: string, isLoaded: boolean}) { if (!this.isSpriteLoaded[mapId]) this.isSpriteLoaded[mapId] = {}; - this.isSpriteLoaded[mapId][params.scope] = params.isLoaded; - for (const workerSource in this.workerSources[mapId]) { - const ws = this.workerSources[mapId][workerSource]; + this.isSpriteLoaded[mapId][scope] = isLoaded; + + if (!this.workerSources[mapId] || !this.workerSources[mapId][scope]) { + return; + } + + for (const workerSource in this.workerSources[mapId][scope]) { + const ws = this.workerSources[mapId][scope][workerSource]; for (const source in ws) { if (ws[source] instanceof VectorTileWorkerSource) { - ws[source].isSpriteLoaded = params.isLoaded; + ws[source].isSpriteLoaded = isLoaded; ws[source].fire(new Event('isSpriteLoaded')); } } } } - setImages(mapId: string, params: {scope: string, images: Array}, callback: WorkerTileCallback) { - const {scope, images} = params; - + setImages(mapId: string, {scope, images}: {scope: string, images: Array}, callback: WorkerTileCallback) { if (!this.availableImages[mapId]) { this.availableImages[mapId] = {}; } this.availableImages[mapId][scope] = images; - for (const workerSource in this.workerSources[mapId]) { - const ws = this.workerSources[mapId][workerSource]; + + if (!this.workerSources[mapId] || !this.workerSources[mapId][scope]) { + callback(); + return; + } + + for (const workerSource in this.workerSources[mapId][scope]) { + const ws = this.workerSources[mapId][scope][workerSource]; for (const source in ws) { ws[source].availableImages = images; } } + callback(); } @@ -196,18 +206,20 @@ export default class Worker { this.getWorkerSource(mapId, params.type, params.source, params.scope).removeTile(params, callback); } - removeSource(mapId: string, params: {source: string} & {type: string}, callback: WorkerTileCallback) { + removeSource(mapId: string, params: {source: string, scope: string, type: string}, callback: WorkerTileCallback) { assert(params.type); + assert(params.scope); assert(params.source); if (!this.workerSources[mapId] || - !this.workerSources[mapId][params.type] || - !this.workerSources[mapId][params.type][params.source]) { + !this.workerSources[mapId][params.scope] || + !this.workerSources[mapId][params.scope][params.type] || + !this.workerSources[mapId][params.scope][params.type][params.source]) { return; } - const worker = this.workerSources[mapId][params.type][params.source]; - delete this.workerSources[mapId][params.type][params.source]; + const worker = this.workerSources[mapId][params.scope][params.type][params.source]; + delete this.workerSources[mapId][params.scope][params.type][params.source]; if (worker.removeSource !== undefined) { worker.removeSource(params, callback); @@ -286,12 +298,14 @@ export default class Worker { getWorkerSource(mapId: string, type: string, source: string, scope: string): WorkerSource { if (!this.workerSources[mapId]) this.workerSources[mapId] = {}; - if (!this.workerSources[mapId][type]) - this.workerSources[mapId][type] = {}; + if (!this.workerSources[mapId][scope]) + this.workerSources[mapId][scope] = {}; + if (!this.workerSources[mapId][scope][type]) + this.workerSources[mapId][scope][type] = {}; if (!this.isSpriteLoaded[mapId]) this.isSpriteLoaded[mapId] = {}; - if (!this.workerSources[mapId][type][source]) { + if (!this.workerSources[mapId][scope][type][source]) { // use a wrapped actor so that we can attach a target mapId param // to any messages invoked by the WorkerSource const actor = { @@ -300,7 +314,7 @@ export default class Worker { }, scheduler: this.actor.scheduler }; - this.workerSources[mapId][type][source] = new (this.workerSourceTypes[type]: any)( + this.workerSources[mapId][scope][type][source] = new (this.workerSourceTypes[type]: any)( (actor: any), this.getLayerIndex(mapId, scope), this.getAvailableImages(mapId, scope), @@ -309,7 +323,7 @@ export default class Worker { this.brightness); } - return this.workerSources[mapId][type][source]; + return this.workerSources[mapId][scope][type][source]; } getDEMWorkerSource(mapId: string, source: string, scope: string): RasterDEMTileWorkerSource { diff --git a/test/unit/source/worker.test.js b/test/unit/source/worker.test.js index 611e65dd920..e60631e29a3 100644 --- a/test/unit/source/worker.test.js +++ b/test/unit/source/worker.test.js @@ -15,6 +15,7 @@ test('load tile', (t) => { worker.loadTile(0, { type: 'vector', source: 'source', + scope: 'scope', uid: 0, tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}}, request: {url: '/error'}// Sinon fake server gives 404 responses by default @@ -62,5 +63,19 @@ test('worker source messages dispatched to the correct map instance', (t) => { }; }); - worker.loadTile(999, {type: 'test'}); + worker.loadTile(999, {type: 'test', source: 'source', scope: 'scope'}); +}); + +test('worker sources should be scoped', (t) => { + const worker = new Worker(_self); + + // eslint-disable-next-line prefer-arrow-callback + _self.registerWorkerSource('sourceType', function() {}); + + const a = worker.getWorkerSource(999, 'sourceType', 'sourceId', 'scope1'); + const b = worker.getWorkerSource(999, 'sourceType', 'sourceId', 'scope2'); + + t.notEqual(a, b); + + t.end(); }); From 1a5626463111fcaa9c5c8aafd455f98773f026cd Mon Sep 17 00:00:00 2001 From: Fouad Valadbeigi <67509069+akoylasar@users.noreply.github.com> Date: Wed, 8 Nov 2023 18:01:35 +0100 Subject: [PATCH 06/44] [MAPS3D-1124] fix inconsistency in 3d light properties specs and version (internal-923) --- src/style-spec/reference/v8.json | 91 ++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 23 deletions(-) diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index 333ec6b7078..808d518f06a 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -354,7 +354,14 @@ ] }, "doc": "Direction of the light source specified as [a azimuthal angle, p polar angle] where a indicates the azimuthal angle of the light relative to north (in degrees and proceeding clockwise), and p indicates polar angle of the light (from 0°, directly above, to 180°, directly below).", - "example": [90, 40] + "example": [90, 40], + "sdk-support": { + "basic functionality": { + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" + } + } }, "color": { "type": "color", @@ -367,7 +374,14 @@ ] }, "transition": true, - "doc": "Color of the directional light." + "doc": "Color of the directional light.", + "sdk-support": { + "basic functionality": { + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" + } + } }, "intensity": { "type": "number", @@ -382,7 +396,14 @@ ] }, "transition": true, - "doc": "A multiplier for the color of the directional light." + "doc": "A multiplier for the color of the directional light.", + "sdk-support": { + "basic functionality": { + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" + } + } }, "cast-shadows": { "type": "boolean", @@ -392,7 +413,14 @@ "expression": { "interpolated": false }, - "property-type": "data-constant" + "property-type": "data-constant", + "sdk-support": { + "basic functionality": { + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" + } + } }, "shadow-intensity": { "type": "number", @@ -407,7 +435,14 @@ ] }, "doc": "Determines the shadow strength, affecting the shadow receiver surfaces final color. Values near 0.0 reduce the shadow contribution to the final color. Values near to 1.0 make occluded surfaces receive almost no directional light. Designed to be used mostly for transitioning between values 0 and 1.", - "transition": true + "transition": true, + "sdk-support": { + "basic functionality": { + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" + } + } } }, "properties_light_ambient": { @@ -422,7 +457,14 @@ ] }, "transition": true, - "doc": "Color of the ambient light." + "doc": "Color of the ambient light.", + "sdk-support": { + "basic functionality": { + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" + } + } }, "intensity": { "type": "number", @@ -437,7 +479,14 @@ ] }, "transition": true, - "doc": "A multiplier for the color of the ambient light." + "doc": "A multiplier for the color of the ambient light.", + "sdk-support": { + "basic functionality": { + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" + } + } } }, "properties_light_flat": { @@ -464,10 +513,9 @@ "example": "map", "sdk-support": { "basic functionality": { - "js": "0.27.0", - "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" } } }, @@ -496,10 +544,9 @@ ], "sdk-support": { "basic functionality": { - "js": "0.27.0", - "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" } } }, @@ -517,10 +564,9 @@ "doc": "Color tint for lighting extruded geometries.", "sdk-support": { "basic functionality": { - "js": "0.27.0", - "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" } } }, @@ -540,10 +586,9 @@ "doc": "Intensity of lighting (on a scale from 0 to 1). Higher numbers will present as more extreme contrast.", "sdk-support": { "basic functionality": { - "js": "0.27.0", - "android": "5.1.0", - "ios": "3.6.0", - "macos": "0.5.0" + "js": "3.0.0", + "android": "11.0.0", + "ios": "11.0.0" } } } From 9b182139a8f50582a6426e1dac906d0bb7d29faf Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Thu, 9 Nov 2023 16:18:36 +0200 Subject: [PATCH 07/44] Add Standard Style debug page (internal-909) * Add Standard Style debug page * Switch to tweakpane, add config support * Add Style Editor --- debug/standard-style.html | 161 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 debug/standard-style.html diff --git a/debug/standard-style.html b/debug/standard-style.html new file mode 100644 index 00000000000..f7e2bb4d0f4 --- /dev/null +++ b/debug/standard-style.html @@ -0,0 +1,161 @@ + + + + + Mapbox GL JS debug page + + + + + + + + +
+
+
Loading...
+ + + + + + + From d27ecfde5d490eaea6cee9f695b9675106b5c390 Mon Sep 17 00:00:00 2001 From: Fouad Valadbeigi <67509069+akoylasar@users.noreply.github.com> Date: Thu, 9 Nov 2023 16:02:36 +0100 Subject: [PATCH 08/44] [MAPS3D-1159] avoid elevating symbols on fill extrusions they have 0 opacity (internal-924) --- src/source/building_index.js | 14 +- .../model-layer/MAPS3D-1159/expected.png | Bin 0 -> 39335 bytes .../model-layer/MAPS3D-1159/style.json | 737 ++++++++++++++++++ 3 files changed, 747 insertions(+), 4 deletions(-) create mode 100644 test/integration/render-tests/model-layer/MAPS3D-1159/expected.png create mode 100644 test/integration/render-tests/model-layer/MAPS3D-1159/style.json diff --git a/src/source/building_index.js b/src/source/building_index.js index d4b0704cb68..7247a70bdff 100644 --- a/src/source/building_index.js +++ b/src/source/building_index.js @@ -13,7 +13,7 @@ import type Style from '../style/style.js'; class BuildingIndex { style: Style; layers: Array; - currentBuildingBuckets: Array<{bucket: ?Bucket, tileID: OverscaledTileID}>; + currentBuildingBuckets: Array<{bucket: ?Bucket, tileID: OverscaledTileID, verticalScale: number}>; constructor(style: Style) { this.style = style; @@ -41,6 +41,13 @@ class BuildingIndex { const layer = this.layers[i]; const sourceCache = this.style.getLayerSourceCache(layer); + let verticalScale = 1; + if (layer.type === 'fill-extrusion') { + // See https://mapbox.atlassian.net/browse/MAPS3D-1159 for more details on why we should take opacity into account. + const opacity = ((layer: any): FillExtrusionStyleLayer).paint.get('fill-extrusion-opacity'); + verticalScale = opacity > 0.0 ? ((layer: any): FillExtrusionStyleLayer).paint.get('fill-extrusion-vertical-scale') : 0; + } + let tile = sourceCache ? sourceCache.getTile(tileID) : null; if (!tile && sourceCache && tileID.canonical.z > sourceCache.getSource().minzoom) { @@ -51,7 +58,7 @@ class BuildingIndex { id = id.scaledTo(id.overscaledZ - 1); } } - this.currentBuildingBuckets.push({bucket: tile ? tile.getBucket(layer) : null, tileID: tile ? tile.tileID : tileID}); + this.currentBuildingBuckets.push({bucket: tile ? tile.getBucket(layer) : null, tileID: tile ? tile.tileID : tileID, verticalScale}); } symbolBucket.hasAnyZOffset = false; @@ -96,7 +103,7 @@ class BuildingIndex { for (let i = 0; i < this.layers.length; ++i) { const layer = this.layers[i]; if (layer.type !== 'fill-extrusion') continue; - const {bucket, tileID} = this.currentBuildingBuckets[i]; + const {bucket, tileID, verticalScale} = this.currentBuildingBuckets[i]; if (!bucket) continue; const {tileX, tileY} = this._mapCoordToOverlappingTile(tid, x, y, tileID); @@ -108,7 +115,6 @@ class BuildingIndex { availableHeight = heightData.height; continue; } - const verticalScale = ((layer: any): FillExtrusionStyleLayer).paint.get('fill-extrusion-vertical-scale'); return heightData.height * verticalScale; } diff --git a/test/integration/render-tests/model-layer/MAPS3D-1159/expected.png b/test/integration/render-tests/model-layer/MAPS3D-1159/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..7cb69470a9242d321083155d3a915eacac20030b GIT binary patch literal 39335 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 literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/model-layer/MAPS3D-1159/style.json b/test/integration/render-tests/model-layer/MAPS3D-1159/style.json new file mode 100644 index 00000000000..6af0039881c --- /dev/null +++ b/test/integration/render-tests/model-layer/MAPS3D-1159/style.json @@ -0,0 +1,737 @@ +{ + "version": 8, + "metadata": { + "test": { + "allowed": 0.0012, + "width": 512, + "height": 512 + } + }, + "lights": [ + { + "type": "ambient", + "id": "environment" + }, + { + "type": "directional", + "id": "sun_light", + "properties": { + "cast-shadows": true, + "intensity": 0.8599999904632569, + "direction": [ + 311.9219970703125, + 82.37799835205078 + ] + } + } + ], + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "sprite": "local://sprites/sprite", + "sources": { + "composite": { + "type": "vector", + "maxzoom": 15, + "tiles": [ + "local://models/vector/{z}-{x}-{y}.vector.pbf" + ] + }, + "file-system-tiles": { + "type": "batched-model", + "maxzoom": 14, + "tiles": [ + "local://models/landmark/mbx/{x}-{y}-{z}.b3dm" + ] + } + }, + "transition": { + "duration": 0 + }, + "pitch": 55, + "zoom": 16.9, + "bearing": 0, + "center": [ + 11.58132, + 48.131099 + ], + "layers": [ + { + "type": "background", + "paint": { + "background-color": [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 11.0, + [ + "rgba", + 239.00001525878907, + 233.00001525878907, + 225.00001525878907, + 1.0 + ], + 13.0, + [ + "rgba", + 230.00001525878907, + 228.00001525878907, + 224.00001525878907, + 1.0 + ] + ] + }, + "id": "land" + }, + { + "type": "fill", + "id": "landcover", + "source": "composite", + "maxzoom": 7.0, + "paint": { + "fill-antialias": false, + "fill-opacity": [ + "interpolate", + [ + "exponential", + 1.5 + ], + [ + "zoom" + ], + 2.0, + 0.3, + 7.0, + 0.0 + ], + "fill-color": [ + "match", + [ + "get", + "class" + ], + "snow", + [ + "rgba", + 255.0, + 255.0, + 255.0, + 1.0 + ], + [ + "rgba", + 222.00001525878907, + 237.00001525878907, + 177.0, + 1.0 + ] + ] + }, + "source-layer": "landcover" + }, + { + "type": "fill", + "source": "composite", + "paint": { + "fill-color": [ + "rgba", + 117.00000762939453, + 207.00001525878907, + 240.00001525878907, + 1.0 + ] + }, + "source-layer": "water", + "id": "water" + }, + { + "minzoom": 11.0, + "type": "line", + "paint": { + "line-opacity": [ + "step", + [ + "zoom" + ], + 0.0, + 14.0, + 1.0 + ], + "line-color": [ + "match", + [ + "get", + "class" + ], + "street_limited", + [ + "rgba", + 240.00001525878907, + 238.00001525878907, + 235.00001525878907, + 1.0 + ], + [ + "rgba", + 255.0, + 255.0, + 255.0, + 1.0 + ] + ], + "line-width": [ + "interpolate", + [ + "exponential", + 1.5 + ], + [ + "zoom" + ], + 12.0, + 0.5, + 14.0, + 2.0, + 18.0, + 18.0 + ] + }, + "source-layer": "road", + "filter": [ + "all", + [ + "match", + [ + "get", + "class" + ], + [ + "primary_link", + "street", + "street_limited" + ], + true, + false + ], + [ + "match", + [ + "get", + "structure" + ], + [ + "ford", + "none" + ], + true, + false + ], + [ + "==", + [ + "geometry-type" + ], + "LineString" + ] + ], + "layout": { + "line-join": "round", + "line-cap": "round" + }, + "source": "composite", + "id": "road-street" + }, + { + "minzoom": 15.0, + "type": "fill-extrusion", + "paint": { + "fill-extrusion-height": [ + "number", + [ + "get", + "height" + ] + ], + "fill-extrusion-ambient-occlusion-intensity": 0.3499999940395355, + "fill-extrusion-ambient-occlusion-ground-radius": 5, + "fill-extrusion-ambient-occlusion-wall-radius": 5, + "fill-extrusion-opacity": 0.0, + "fill-extrusion-base": [ + "number", + [ + "get", + "min_height" + ] + ], + "fill-extrusion-color": [ + "rgba", + 255.0, + 255.0, + 255.0, + 1.0 + ], + "fill-extrusion-vertical-gradient": false, + "fill-extrusion-vertical-scale": [ + "interpolate", + ["linear"], + ["zoom"], + 16, 0, + 17, 2.0 + ] + }, + "source-layer": "building", + "filter": [ + "==", + [ + "get", + "extrude" + ], + "true" + ], + "source": "composite", + "id": "3d-buildings" + }, + { + "minzoom": 10.0, + "type": "symbol", + "paint": { + "text-halo-width": 1.0, + "text-halo-blur": 1.0, + "text-halo-color": [ + "match", + [ + "get", + "class" + ], + "ferry", + [ + "rgba", + 117.00000762939453, + 207.00001525878907, + 240.00001525878907, + 1.0 + ], + [ + "motorway", + "trunk" + ], + [ + "rgba", + 255.0, + 255.0, + 255.0, + 0.75 + ], + [ + "rgba", + 255.0, + 255.0, + 255.0, + 1.0 + ] + ], + "text-color": [ + "match", + [ + "get", + "class" + ], + "ferry", + [ + "rgba", + 58.000003814697269, + 76.0, + 166.0, + 1.0 + ], + [ + "rgba", + 0.0, + 0.0, + 0.0, + 1.0 + ] + ] + }, + "source-layer": "road", + "filter": [ + "step", + [ + "zoom" + ], + [ + "match", + [ + "get", + "class" + ], + [ + "motorway", + "primary", + "secondary", + "tertiary", + "trunk" + ], + true, + false + ], + 12.0, + [ + "match", + [ + "get", + "class" + ], + [ + "motorway", + "pedestrian", + "primary", + "secondary", + "street", + "street_limited", + "tertiary", + "trunk" + ], + true, + false + ], + 15.0, + [ + "match", + [ + "get", + "class" + ], + "golf", + false, + true + ] + ], + "layout": { + "text-font": [ "NotoCJK" ], + "text-max-angle": 30.0, + "text-size": [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 10.0, + [ + "match", + [ + "get", + "class" + ], + [ + "motorway", + "primary", + "secondary", + "tertiary", + "trunk" + ], + 10.0, + [ + "motorway_link", + "pedestrian", + "primary_link", + "secondary_link", + "street", + "street_limited", + "tertiary_link", + "trunk_link" + ], + 9.0, + 6.5 + ], + 18.0, + [ + "match", + [ + "get", + "class" + ], + [ + "motorway", + "primary", + "secondary", + "tertiary", + "trunk" + ], + 16.0, + [ + "motorway_link", + "pedestrian", + "primary_link", + "secondary_link", + "street", + "street_limited", + "tertiary_link", + "trunk_link" + ], + 14.0, + 13.0 + ] + ], + "text-field": [ + "format", + [ + "coalesce", + [ + "get", + "name_en" + ], + [ + "get", + "name" + ] + ], + {} + ], + "symbol-placement": "line", + "text-rotation-alignment": "map", + "text-pitch-alignment": "viewport" + }, + "source": "composite", + "id": "road-label" + }, + { + "id": "poi-label", + "type": "symbol", + "metadata": { + "mapbox:group": "Point of interest labels, poi-labels" + }, + "source": "composite", + "source-layer": "poi_label", + "minzoom": 6, + "filter": [ + "all", + [ + "<=", + [ "number", [ "get", "filterrank" ] ], + ["+", ["step", ["zoom"], 1, 16, 2, 17, 3], + [ + "match", + ["get", "class"], + "park_like", + 4, + "visitor_amenities", + 4, + "store_like", + 3, + "lodging", + 1, + 2 + ] + ] + ], + [ + "case", + [ + "<=", + [ + "pitch" + ], + 40.0 + ], + true, + [ + "step", + [ "pitch" ], + true, + 40, + [ "<", [ "distance-from-center" ], 1.2 ], + 50, + [ "<", [ "distance-from-center" ], 1 ], + 55, + [ "<", [ "distance-from-center" ], 0.8 ], + 60, + [ "<=", [ "distance-from-center" ], 0.6 ] + ] + ] + ], + "layout": { + "symbol-z-elevate": true, + "text-size": [ + "step", + [ "zoom" ], + [ "step", [ "number", [ "get", "sizerank" ] ], 18, 5, 12 ], + 17, + [ "step", [ "number", [ "get", "sizerank" ] ], 18, 13, 12 ] + ], + "text-field": [ + "format", + [ "coalesce", [ "get", "name_en" ], [ "get", "name" ] ], + { } + ], + "text-font": [ "NotoCJK" ], + "text-padding": 4, + "icon-image": "building-12", + "text-offset": [ + "step", + [ "zoom" ], + [ + "step", + [ "number", [ "get", "sizerank" ] ], + [ "literal", [ 0, 0 ] ], + 5, + [ "literal", [ 0, 1 ] ] + ], + 17, + [ + "step", + [ "number", [ "get", "sizerank" ] ], + [ "literal", [ 0, 0 ] ], + 13, + [ "literal", [ 0, 1 ] ] + ] + ], + "text-anchor": [ + "step", + [ "zoom" ], + [ + "step", + [ "number", [ "get", "sizerank" ] ], + "center", + 5, + "top" + ], + 17, + [ + "step", + [ "number", [ "get", "sizerank" ] ], + "center", + 13, + "top" + ] + ] + }, + "paint": { + "icon-image-cross-fade": [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.25, + 0.0, + 0.3, + 1.0 + ], + "icon-opacity": [ + "step", + [ "zoom" ], + [ "step", [ "number", [ "get", "sizerank" ] ], 0, 5, 1 ], + 17, + [ "step", [ "number", [ "get", "sizerank" ] ], 0, 13, 1 ] + ], + "text-halo-width": 1, + "text-halo-blur": 0, + "text-halo-color": [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.25, + "hsl(0, 0%, 10%)", + 0.3, + "hsl(0, 0%, 100%)" + ], + "text-color": [ + "match", + [ "get", "class" ], + "food_and_drink", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.25, + "hsl(40, 95%, 70%)", + 0.3, + "hsl(30, 100%, 48%)" + ], + "park_like", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.25, + "hsl(110, 55%, 65%)", + 0.3, + "hsl(110, 70%, 28%)" + ], + "education", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.25, + "hsl(30, 50%, 70%)", + 0.3, + "hsl(30, 50%, 38%)" + ], + "medical", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.25, + "hsl(0, 70%, 70%)", + 0.3, + "hsl(0, 90%, 60%)" + ], + "sport_and_leisure", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.25, + "hsl(190, 60%, 70%)", + 0.3, + "hsl(190, 75%, 38%)" + ], + [ "store_like", "food_and_drink_stores" ], + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.25, + "hsl(210, 70%, 75%)", + 0.3, + "hsl(210, 75%, 53%)" + ], + [ "commercial_services", "motorist", "lodging" ], + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.25, + "hsl(260, 70%, 75%)", + 0.3, + "hsl(250, 75%, 60%)" + ], + [ "arts_and_entertainment", "historic", "landmark" ], + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.25, + "hsl(320, 70%, 75%)", + 0.3, + "hsl(320, 85%, 60%)" + ], + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.25, + "hsl(210, 20%, 70%)", + 0.3, + "hsl(210, 20%, 43%)" + ] + ] + } + } + ] +} From 99ad4003bf2a02a0d24af633026d10a1cad6ebd7 Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Thu, 9 Nov 2023 19:27:58 +0200 Subject: [PATCH 09/44] [GLJS-566] Use proper fragment when updating workers (internal-925) * Use proper fragment when updating workers * Add unit test --- src/style/style.js | 5 ++-- test/unit/style/style_imports.test.js | 34 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/style/style.js b/src/style/style.js index cde2a0a7b96..1e6f327c783 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -1183,11 +1183,12 @@ class Style extends Evented { } _updateWorkerLayers(scope: string, updatedIds?: Array, removedIds?: Array) { + const fragmentStyle = this.getFragmentStyle(scope); this.dispatcher.broadcast('updateLayers', { - layers: updatedIds ? this._serializeLayers(updatedIds) : [], + layers: updatedIds ? fragmentStyle._serializeLayers(updatedIds) : [], scope, removedIds: removedIds || [], - options: this.options + options: fragmentStyle.options }); } diff --git a/test/unit/style/style_imports.test.js b/test/unit/style/style_imports.test.js index 02df0313ada..8fa9dfdb85b 100644 --- a/test/unit/style/style_imports.test.js +++ b/test/unit/style/style_imports.test.js @@ -1724,6 +1724,40 @@ test('Style#setGeoJSONSourceData', (t) => { }); test('Style#setConfigProperty', (t) => { + t.test('Updates layers in scope', (t) => { + const style = new Style(new StubMap()); + + const initialStyle = createStyleJSON({ + imports: [{ + id: 'standard', + url: '/standard.json', + config: {showBackground: false}, + data: createStyleJSON({ + layers: [{ + id: 'background', + type: 'background', + layout: {visibility: ['case', ['config', 'showBackground'], 'visible', 'none']}}] + }) + }] + }); + + style.on('style.load', () => { + style.dispatcher.broadcast = function(key, value) { + t.equal(key, 'updateLayers'); + t.equal(value.scope, 'standard'); + t.deepEqual(value.removedIds, []); + t.deepEqual(value.options.get('showBackground').value, true); + t.deepEqual(value.layers.map(layer => layer.id), ['background']); + t.end(); + }; + + style.setConfigProperty('standard', 'showBackground', true); + style.update({}); + }); + + style.loadJSON(initialStyle); + }); + t.test('Reevaluates layer visibility', (t) => { const style = new Style(new StubMap()); From 294682594b161f63c38603da842af7af45574b16 Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Fri, 10 Nov 2023 15:14:29 +0200 Subject: [PATCH 10/44] Validate import id (internal-927) --- src/style-spec/validate/validate_import.js | 7 +++++++ test/unit/style-spec/fixture/bad-import.input.json | 11 +++++++++++ .../fixture/bad-import.output-api-supported.json | 6 ++++++ test/unit/style-spec/fixture/bad-import.output.json | 6 ++++++ 4 files changed, 30 insertions(+) create mode 100644 test/unit/style-spec/fixture/bad-import.input.json create mode 100644 test/unit/style-spec/fixture/bad-import.output-api-supported.json create mode 100644 test/unit/style-spec/fixture/bad-import.output.json diff --git a/src/style-spec/validate/validate_import.js b/src/style-spec/validate/validate_import.js index 4492b68874c..1e43dd204bb 100644 --- a/src/style-spec/validate/validate_import.js +++ b/src/style-spec/validate/validate_import.js @@ -4,6 +4,7 @@ import extend from '../util/extend.js'; import validateStyle from './validate_style.js'; import validateObject from './validate_object.js'; import ValidationError from '../error/validation_error.js'; +import {unbundle} from '../util/unbundle_jsonlint.js'; import type {ValidationOptions} from './validate.js'; @@ -22,6 +23,12 @@ export default function validateImport(options: ValidationOptions): ValidationEr valueSpec: styleSpec.import })); + // Empty string is reserved for the root style id + if (unbundle(importSpec.id) === '') { + const key = `${options.key}.id`; + errors.push(new ValidationError(key, importSpec, `import id can't be an empty string`)); + } + if (data) { const key = `${options.key}.data`; errors = errors.concat(validateStyle(data, styleSpec, {key})); diff --git a/test/unit/style-spec/fixture/bad-import.input.json b/test/unit/style-spec/fixture/bad-import.input.json new file mode 100644 index 00000000000..e0ec5b7a3fd --- /dev/null +++ b/test/unit/style-spec/fixture/bad-import.input.json @@ -0,0 +1,11 @@ +{ + "version": 8, + "imports": [ + { + "id": "", + "url": "" + } + ], + "sources": {}, + "layers": [] +} diff --git a/test/unit/style-spec/fixture/bad-import.output-api-supported.json b/test/unit/style-spec/fixture/bad-import.output-api-supported.json new file mode 100644 index 00000000000..0fcfda49edc --- /dev/null +++ b/test/unit/style-spec/fixture/bad-import.output-api-supported.json @@ -0,0 +1,6 @@ +[ + { + "message": "imports[0].id: import id can't be an empty string", + "line": 4 + } +] diff --git a/test/unit/style-spec/fixture/bad-import.output.json b/test/unit/style-spec/fixture/bad-import.output.json new file mode 100644 index 00000000000..0fcfda49edc --- /dev/null +++ b/test/unit/style-spec/fixture/bad-import.output.json @@ -0,0 +1,6 @@ +[ + { + "message": "imports[0].id: import id can't be an empty string", + "line": 4 + } +] From 32f8c25288a91ab8002453e1c6b9049ba12896dd Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Fri, 10 Nov 2023 16:27:09 +0200 Subject: [PATCH 11/44] Validate unknown 3D Lights properties (internal-928) --- src/style-spec/validate/validate_lights.js | 18 +++++++++++------- .../style-spec/fixture/bad-lights.input.json | 3 ++- .../bad-lights.output-api-supported.json | 14 +++++++++----- .../style-spec/fixture/bad-lights.output.json | 14 +++++++++----- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/style-spec/validate/validate_lights.js b/src/style-spec/validate/validate_lights.js index 89b545644da..b6e515cb978 100644 --- a/src/style-spec/validate/validate_lights.js +++ b/src/style-spec/validate/validate_lights.js @@ -66,13 +66,17 @@ export default function validateLights(options: Options): Array return errors; } for (const propertyKey in properties) { - errors = errors.concat(validate({ - key: propertyKey, - value: properties[propertyKey], - valueSpec: lightPropertySpec[propertyKey], - style, - styleSpec - })); + if (!lightPropertySpec[propertyKey]) { + errors = errors.concat([new ValidationWarning(options.key, properties[propertyKey], `unknown property "${propertyKey}"`)]); + } else { + errors = errors.concat(validate({ + key: propertyKey, + value: properties[propertyKey], + valueSpec: lightPropertySpec[propertyKey], + style, + styleSpec + })); + } } } else { const transitionMatch = key.match(/^(.*)-transition$/); diff --git a/test/unit/style-spec/fixture/bad-lights.input.json b/test/unit/style-spec/fixture/bad-lights.input.json index ffb2167446a..105b535bb08 100644 --- a/test/unit/style-spec/fixture/bad-lights.input.json +++ b/test/unit/style-spec/fixture/bad-lights.input.json @@ -8,7 +8,8 @@ "type": "ambient", "properties": { "color": "rgba(255.0, 0.0, 0.0, 1.0)", - "intensity": 0.4 + "intensity": 0.4, + "star-intensity": 0 } }, { diff --git a/test/unit/style-spec/fixture/bad-lights.output-api-supported.json b/test/unit/style-spec/fixture/bad-lights.output-api-supported.json index afebae8de80..f80f723e4b3 100644 --- a/test/unit/style-spec/fixture/bad-lights.output-api-supported.json +++ b/test/unit/style-spec/fixture/bad-lights.output-api-supported.json @@ -1,18 +1,22 @@ [ { - "message": "lights[2]: duplicate light type \"directional\", previously defined at line 15", - "line": 26 + "message": "lights[0]: unknown property \"star-intensity\"", + "line": 12 + }, + { + "message": "lights[2]: duplicate light type \"directional\", previously defined at line 16", + "line": 27 }, { "message": "properties: object expected, array found", - "line": 28 + "line": 29 }, { "message": "light-3d: missing property type on light", - "line": 30 + "line": 31 }, { "message": "lights[4]: duplicate light type \"ambient\", previously defined at line 7", - "line": 34 + "line": 35 } ] diff --git a/test/unit/style-spec/fixture/bad-lights.output.json b/test/unit/style-spec/fixture/bad-lights.output.json index afebae8de80..f80f723e4b3 100644 --- a/test/unit/style-spec/fixture/bad-lights.output.json +++ b/test/unit/style-spec/fixture/bad-lights.output.json @@ -1,18 +1,22 @@ [ { - "message": "lights[2]: duplicate light type \"directional\", previously defined at line 15", - "line": 26 + "message": "lights[0]: unknown property \"star-intensity\"", + "line": 12 + }, + { + "message": "lights[2]: duplicate light type \"directional\", previously defined at line 16", + "line": 27 }, { "message": "properties: object expected, array found", - "line": 28 + "line": 29 }, { "message": "light-3d: missing property type on light", - "line": 30 + "line": 31 }, { "message": "lights[4]: duplicate light type \"ambient\", previously defined at line 7", - "line": 34 + "line": 35 } ] From 5bc8a2020bfe351fde9d2d1efc4c47385b5dd62a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandar=20Stojiljkovi=C4=87?= Date: Mon, 13 Nov 2023 10:48:28 +0200 Subject: [PATCH 12/44] [MAPS3D-1137] Do not reparse tiles and recreate buckets when terrain gets enabled/disabled (internal-903) There's no need for this anymore after fill extrusion bucket handles both modes the same. Set RC style as default to help Android Shell testing. Co-authored-by: Volodymyr Agafonkin --- .../source/tiled_3d_model_worker_source.js | 3 --- src/data/bucket.js | 1 - src/data/dem_data.js | 6 +---- src/source/raster_dem_tile_worker_source.js | 4 ++-- src/source/vector_tile_worker_source.js | 1 - src/source/worker.js | 23 ++++--------------- src/source/worker_source.js | 4 +--- src/source/worker_tile.js | 3 --- src/style/style.js | 2 -- test/unit/terrain/terrain.test.js | 4 ++-- test/util/dem_mock.js | 2 +- 11 files changed, 12 insertions(+), 41 deletions(-) diff --git a/3d-style/source/tiled_3d_model_worker_source.js b/3d-style/source/tiled_3d_model_worker_source.js index 89bacd742d0..babb3d63a0d 100644 --- a/3d-style/source/tiled_3d_model_worker_source.js +++ b/3d-style/source/tiled_3d_model_worker_source.js @@ -30,7 +30,6 @@ class Tiled3dWorkerTile { tileSize: number; source: string; overscaling: number; - enableTerrain: boolean; projection: Projection; status: 'parsing' | 'done'; reloadCallback: ?WorkerTileCallback; @@ -46,7 +45,6 @@ class Tiled3dWorkerTile { this.tileSize = params.tileSize; this.source = params.source; this.overscaling = this.tileID.overscaleFactor(); - this.enableTerrain = !!params.enableTerrain; this.projection = params.projection; this.brightness = brightness; } @@ -141,7 +139,6 @@ class Tiled3dModelWorkerSource implements WorkerSource { const uid = params.uid; if (loaded && loaded[uid]) { const workerTile = loaded[uid]; - workerTile.enableTerrain = !!params.enableTerrain; workerTile.projection = params.projection; workerTile.brightness = params.brightness; diff --git a/src/data/bucket.js b/src/data/bucket.js index b7fe50cfc6d..1657fecfbba 100644 --- a/src/data/bucket.js +++ b/src/data/bucket.js @@ -24,7 +24,6 @@ export type BucketParameters = { collisionBoxArray: CollisionBoxArray, sourceLayerIndex: number, sourceID: string, - enableTerrain: boolean, projection: ProjectionSpecification } diff --git a/src/data/dem_data.js b/src/data/dem_data.js index a4b5562eacb..34c75292a25 100644 --- a/src/data/dem_data.js +++ b/src/data/dem_data.js @@ -58,7 +58,7 @@ export default class DEMData { // RGBAImage data has uniform 1px padding on all sides: square tile edge size defines stride // and dim is calculated as stride - 2. constructor(uid: number, data: ImageData, sourceEncoding: DEMSourceEncoding, - convertToFloat: boolean, borderReady: boolean = false, buildQuadTree: boolean = false): void { + convertToFloat: boolean, borderReady: boolean = false): void { this.uid = uid; if (data.height !== data.width) throw new RangeError('DEM tiles must be square'); if (sourceEncoding && sourceEncoding !== "mapbox" && sourceEncoding !== "terrarium") return warnOnce( @@ -93,10 +93,6 @@ export default class DEMData { values[this._idx(dim, dim)] = values[this._idx(dim - 1, dim - 1)]; } - if (buildQuadTree) { - this._buildQuadTree(); - } - if (convertToFloat) { const floatView = new Float32Array(data.data.buffer); const unpack = this.encoding === "terrarium" ? unpackTerrarium : unpackMapbox; diff --git a/src/source/raster_dem_tile_worker_source.js b/src/source/raster_dem_tile_worker_source.js index c31799ac0b9..31914d831d0 100644 --- a/src/source/raster_dem_tile_worker_source.js +++ b/src/source/raster_dem_tile_worker_source.js @@ -12,11 +12,11 @@ class RasterDEMTileWorkerSource { offscreenCanvasContext: CanvasRenderingContext2D; loadTile(params: WorkerDEMTileParameters, callback: WorkerDEMTileCallback) { - const {uid, encoding, rawImageData, padding, buildQuadTree} = params; + const {uid, encoding, rawImageData, padding} = params; // Main thread will transfer ImageBitmap if offscreen decode with OffscreenCanvas is supported, else it will transfer an already decoded image. // Flow struggles to refine ImageBitmap type, likely due to the JSDom shim const imagePixels = window.ImageBitmap && rawImageData instanceof window.ImageBitmap ? this.getImageData(rawImageData, padding) : ((rawImageData: any): ImageData); - const dem = new DEMData(uid, imagePixels, encoding, params.convertToFloat, padding < 1, buildQuadTree); + const dem = new DEMData(uid, imagePixels, encoding, params.convertToFloat, padding < 1); callback(null, dem); } diff --git a/src/source/vector_tile_worker_source.js b/src/source/vector_tile_worker_source.js index d3a3c15e354..7eed1d87dcb 100644 --- a/src/source/vector_tile_worker_source.js +++ b/src/source/vector_tile_worker_source.js @@ -254,7 +254,6 @@ class VectorTileWorkerSource extends Evented implements WorkerSource { if (loaded && loaded[uid]) { const workerTile = loaded[uid]; workerTile.showCollisionBoxes = params.showCollisionBoxes; - workerTile.enableTerrain = !!params.enableTerrain; workerTile.projection = params.projection; workerTile.brightness = params.brightness; workerTile.tileTransform = tileTransform(params.tileID.canonical, params.projection); diff --git a/src/source/worker.js b/src/source/worker.js index 84699f86480..d5d88679c21 100644 --- a/src/source/worker.js +++ b/src/source/worker.js @@ -10,7 +10,6 @@ import Tiled3dModelWorkerSource from '../../3d-style/source/tiled_3d_model_worke import assert from 'assert'; import {plugin as globalRTLTextPlugin} from './rtl_text_plugin.js'; import {enforceCacheSizeLimit} from '../util/tile_request_cache.js'; -import {extend} from '../util/util.js'; import {PerformanceUtils} from '../util/performance.js'; import {Event} from '../util/evented.js'; import {getProjection} from '../geo/projection/index.js'; @@ -47,7 +46,6 @@ export default class Worker { isSpriteLoaded: {[mapId: string]: {[scope: string]: boolean}}; referrer: ?string; dracoUrl: ?string - terrain: ?boolean; brightness: ?number; constructor(self: WorkerGlobalScopeInterface) { @@ -150,11 +148,6 @@ export default class Worker { callback(); } - enableTerrain(mapId: string, enable: boolean, callback: WorkerTileCallback) { - this.terrain = enable; - callback(); - } - setProjection(mapId: string, config: ProjectionSpecification) { this.projections[mapId] = getProjection(config); } @@ -176,24 +169,18 @@ export default class Worker { loadTile(mapId: string, params: WorkerTileParameters & {type: string}, callback: WorkerTileCallback) { assert(params.type); - // $FlowFixMe[method-unbinding] - const p = this.enableTerrain ? extend({enableTerrain: this.terrain}, params) : params; - p.projection = this.projections[mapId] || this.defaultProjection; - this.getWorkerSource(mapId, params.type, params.source, params.scope).loadTile(p, callback); + params.projection = this.projections[mapId] || this.defaultProjection; + this.getWorkerSource(mapId, params.type, params.source, params.scope).loadTile(params, callback); } loadDEMTile(mapId: string, params: WorkerDEMTileParameters, callback: WorkerDEMTileCallback) { - // $FlowFixMe[method-unbinding] - const p = this.enableTerrain ? extend({buildQuadTree: this.terrain}, params) : params; - this.getDEMWorkerSource(mapId, params.source, params.scope).loadTile(p, callback); + this.getDEMWorkerSource(mapId, params.source, params.scope).loadTile(params, callback); } reloadTile(mapId: string, params: WorkerTileParameters & {type: string}, callback: WorkerTileCallback) { assert(params.type); - // $FlowFixMe[method-unbinding] - const p = this.enableTerrain ? extend({enableTerrain: this.terrain}, params) : params; - p.projection = this.projections[mapId] || this.defaultProjection; - this.getWorkerSource(mapId, params.type, params.source, params.scope).reloadTile(p, callback); + params.projection = this.projections[mapId] || this.defaultProjection; + this.getWorkerSource(mapId, params.type, params.source, params.scope).reloadTile(params, callback); } abortTile(mapId: string, params: TileParameters & {type: string}, callback: WorkerTileCallback) { diff --git a/src/source/worker_source.js b/src/source/worker_source.js index af91e123c69..efe50a25f1e 100644 --- a/src/source/worker_source.js +++ b/src/source/worker_source.js @@ -37,7 +37,6 @@ export type WorkerTileParameters = RequestedTileParameters & { pixelRatio: number, showCollisionBoxes: boolean, collectResourceTiming?: boolean, - enableTerrain?: boolean, projection: Projection, brightness: number, extraShadowCaster?: boolean @@ -50,8 +49,7 @@ export type WorkerDEMTileParameters = TileParameters & { rawImageData: ImageData | ImageBitmap, encoding: DEMSourceEncoding, padding: number, - convertToFloat: boolean, - buildQuadTree?: boolean + convertToFloat: boolean }; export type WorkerTileResult = { diff --git a/src/source/worker_tile.js b/src/source/worker_tile.js index d2532206ea6..d29b9b61b57 100644 --- a/src/source/worker_tile.js +++ b/src/source/worker_tile.js @@ -48,7 +48,6 @@ class WorkerTile { overscaling: number; showCollisionBoxes: boolean; collectResourceTiming: boolean; - enableTerrain: boolean; isSymbolTile: ?boolean; extraShadowCaster: ?boolean; projection: Projection; @@ -77,7 +76,6 @@ class WorkerTile { this.showCollisionBoxes = params.showCollisionBoxes; this.collectResourceTiming = !!params.collectResourceTiming; this.promoteId = params.promoteId; - this.enableTerrain = !!params.enableTerrain; this.isSymbolTile = params.isSymbolTile; this.tileTransform = tileTransform(params.tileID.canonical, params.projection); this.projection = params.projection; @@ -182,7 +180,6 @@ class WorkerTile { collisionBoxArray: this.collisionBoxArray, sourceLayerIndex, sourceID: this.source, - enableTerrain: this.enableTerrain, projection: this.projection.spec }); diff --git a/src/style/style.js b/src/style/style.js index 1e6f327c783..cb4d7b4982a 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -2272,7 +2272,6 @@ class Style extends Evented { if (!terrainOptions) { delete this.terrain; delete this.stylesheet.terrain; - this.dispatcher.broadcast('enableTerrain', false); this._force3DLayerUpdate(); this._markersNeedUpdate = true; return; @@ -2409,7 +2408,6 @@ class Style extends Evented { terrain.setScope(this.scope); this.stylesheet.terrain = terrainOptions; - this.dispatcher.broadcast('enableTerrain', !this.terrainSetForDrapingOnly()); this.mergeTerrain(); this.updateDrapeFirstLayers(); this._force3DLayerUpdate(); diff --git a/test/unit/terrain/terrain.test.js b/test/unit/terrain/terrain.test.js index 200548d31b4..f766233a81e 100644 --- a/test/unit/terrain/terrain.test.js +++ b/test/unit/terrain/terrain.test.js @@ -56,7 +56,7 @@ const createGradientDEM = () => { } } } - return new DEMData(0, new RGBAImage({height: TILE_SIZE + 2, width: TILE_SIZE + 2}, pixels), "mapbox", false, true); + return new DEMData(0, new RGBAImage({height: TILE_SIZE + 2, width: TILE_SIZE + 2}, pixels), "mapbox", false); }; const createNegativeGradientDEM = () => { @@ -81,7 +81,7 @@ const createNegativeGradientDEM = () => { } } } - return new DEMData(0, new RGBAImage({height: TILE_SIZE + 2, width: TILE_SIZE + 2}, pixels), "mapbox", false, true); + return new DEMData(0, new RGBAImage({height: TILE_SIZE + 2, width: TILE_SIZE + 2}, pixels), "mapbox", false); }; test('Elevation', (t) => { diff --git a/test/util/dem_mock.js b/test/util/dem_mock.js index 5f1f66f063b..566c7ca0f2a 100644 --- a/test/util/dem_mock.js +++ b/test/util/dem_mock.js @@ -12,7 +12,7 @@ export function createConstElevationDEM(elevation, tileSize) { pixelData[i + 2] = encoded[2]; pixelData[i + 3] = encoded[3]; } - return new DEMData(0, new RGBAImage({height: tileSize + 2, width: tileSize + 2}, pixelData, "mapbox", false, true)); + return new DEMData(0, new RGBAImage({height: tileSize + 2, width: tileSize + 2}, pixelData, "mapbox", false)); } export function setMockElevationTerrain(map, demData, tileSize, maxzoom) { From c289fa158eb8da5b0296bc2bf13510efdde781ea Mon Sep 17 00:00:00 2001 From: Volodymyr Agafonkin Date: Mon, 13 Nov 2023 11:43:29 +0200 Subject: [PATCH 13/44] add sdk version to Mapbox style URLs (internal-929) --- src/util/mapbox.js | 1 + test/unit/util/mapbox.test.js | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/util/mapbox.js b/src/util/mapbox.js index 44b2b26c1f7..fc605f7eb8f 100644 --- a/src/util/mapbox.js +++ b/src/util/mapbox.js @@ -76,6 +76,7 @@ export class RequestManager { normalizeStyleURL(url: string, accessToken?: string): string { if (!isMapboxURL(url)) return url; const urlObject = parseUrl(url); + urlObject.params.push(`sdk=js-${sdkVersion}`); urlObject.path = `/styles/v1${urlObject.path}`; return this._makeAPIURL(urlObject, this._customAccessToken || accessToken); } diff --git a/test/unit/util/mapbox.test.js b/test/unit/util/mapbox.test.js index 2c41aa41f56..256f3c94297 100644 --- a/test/unit/util/mapbox.test.js +++ b/test/unit/util/mapbox.test.js @@ -124,7 +124,7 @@ test("mapbox", (t) => { t.test('takes map-specific tokens correctly', (t) => { const m = new mapbox.RequestManager(undefined, 'customAccessToken'); - t.equal(m.normalizeStyleURL('mapbox://styles/user/style'), 'https://api.mapbox.com/styles/v1/user/style?access_token=customAccessToken'); + t.equal(m.normalizeStyleURL('mapbox://styles/user/style'), `https://api.mapbox.com/styles/v1/user/style?sdk=js-${version}&access_token=customAccessToken`); t.end(); }); @@ -132,15 +132,15 @@ test("mapbox", (t) => { t.test('.normalizeStyleURL', (t) => { t.test('returns an API URL with access_token parameter when no query string', (t) => { - t.equal(manager.normalizeStyleURL('mapbox://styles/user/style'), 'https://api.mapbox.com/styles/v1/user/style?access_token=key'); - t.equal(manager.normalizeStyleURL('mapbox://styles/user/style/draft'), 'https://api.mapbox.com/styles/v1/user/style/draft?access_token=key'); + t.equal(manager.normalizeStyleURL('mapbox://styles/user/style'), `https://api.mapbox.com/styles/v1/user/style?sdk=js-${version}&access_token=key`); + t.equal(manager.normalizeStyleURL('mapbox://styles/user/style/draft'), `https://api.mapbox.com/styles/v1/user/style/draft?sdk=js-${version}&access_token=key`); t.end(); }); t.test('returns an API URL with access_token parameter when query string exists', (t) => { - t.equal(manager.normalizeStyleURL('mapbox://styles/user/style?fresh=true'), 'https://api.mapbox.com/styles/v1/user/style?fresh=true&access_token=key'); - t.equal(manager.normalizeStyleURL('mapbox://styles/user/style/draft?fresh=true'), 'https://api.mapbox.com/styles/v1/user/style/draft?fresh=true&access_token=key'); - t.equal(manager.normalizeStyleURL('mapbox://styles/foo/bar'), 'https://api.mapbox.com/styles/v1/foo/bar?access_token=key'); + t.equal(manager.normalizeStyleURL('mapbox://styles/user/style?fresh=true'), `https://api.mapbox.com/styles/v1/user/style?fresh=true&sdk=js-${version}&access_token=key`); + t.equal(manager.normalizeStyleURL('mapbox://styles/user/style/draft?fresh=true'), `https://api.mapbox.com/styles/v1/user/style/draft?fresh=true&sdk=js-${version}&access_token=key`); + t.equal(manager.normalizeStyleURL('mapbox://styles/foo/bar'), `https://api.mapbox.com/styles/v1/foo/bar?sdk=js-${version}&access_token=key`); t.end(); }); @@ -153,7 +153,7 @@ test("mapbox", (t) => { config.API_URL = 'https://test.example.com/api.mapbox.com'; t.equal( manager.normalizeStyleURL('mapbox://styles/foo/bar'), - 'https://test.example.com/api.mapbox.com/styles/v1/foo/bar?access_token=key' + `https://test.example.com/api.mapbox.com/styles/v1/foo/bar?sdk=js-${version}&access_token=key` ); t.end(); }); From d1abb3a54b7d48cddbcbd4054977e346daaf93f4 Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Mon, 13 Nov 2023 14:34:09 +0200 Subject: [PATCH 14/44] Use Mapbox-hosted Draco decoder (internal-934) --- src/util/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/config.js b/src/util/config.js index 8fd5fd777bd..d103616e961 100644 --- a/src/util/config.js +++ b/src/util/config.js @@ -79,7 +79,7 @@ const config: Config = { ACCESS_TOKEN: null, DEFAULT_STYLE: 'mapbox://styles/mapbox/standard', MAX_PARALLEL_IMAGE_REQUESTS: 16, - DRACO_URL: 'https://www.gstatic.com/draco/versioned/decoders/1.5.6/draco_decoder_gltf.wasm', + DRACO_URL: 'https://api.mapbox.com/mapbox-gl-js/draco_decoder_gltf_v1.5.6.wasm', GLYPHS_URL: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf' }; From 37624d45b7185d5a213b8a5cd094ed95a9eb49d4 Mon Sep 17 00:00:00 2001 From: Volodymyr Agafonkin Date: Mon, 13 Nov 2023 15:16:43 +0200 Subject: [PATCH 15/44] v3.0.0-rc.2 --- CHANGELOG.md | 18 ++++++++++++++++-- package.json | 2 +- src/style-spec/package.json | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df36bdab106..f14c74a6c1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ -## 3.0.0-rc.1 +## 3.0.0-rc.2 + +Mapbox GL JS v3 enables the Mapbox Standard Style, a new realistic 3D lighting system, building shadows and many other visual enhancements, and an ergonomic API for using a new kind of rich, evolving, configurable map styles and seamless integration with custom data. You can get more information about the new features in the [Mapbox GL JS v3 migration guide](./MIGRATION_GUIDE_v3.md). Changes since `v3.0.0-rc.1`: + +### Features and improvements ✨ + +- Improve performance for styles that use both hillshade layers and terrain. -Mapbox GL JS v3 enables the Mapbox Standard Style, a new realistic 3D lighting system, building shadows and many other visual enhancements, and an ergonomic API for using a new kind of rich, evolving, configurable map styles and seamless integration with custom data. You can get more information about the new features in the [Mapbox GL JS v3 migration guide](./MIGRATION_GUIDE_v3.md). Changes since `v3.0.0-beta.5`: +### Bug fixes 🐞 + +- Fix an issue with `setConfigProperty` not taking effect for certain properties. +- Fix rendering issues when a style and its imports share the same source IDs. +- Fix `symbol-z-elevate` elevating symbols over invisible fill extrusions (with zero opacity). +- Fix an issue with style validation accepting a style import with an empty ID. +- Fix an issue with validation of unknown properties in `lights` objects. + +## 3.0.0-rc.1 ### Features and improvements ✨ diff --git a/package.json b/package.json index 14116a8fbec..cdb4eed2731 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mapbox-gl", "description": "A WebGL interactive maps library", - "version": "3.0.0-rc.1", + "version": "3.0.0-rc.2", "main": "dist/mapbox-gl.js", "style": "dist/mapbox-gl.css", "license": "SEE LICENSE IN LICENSE.txt", diff --git a/src/style-spec/package.json b/src/style-spec/package.json index 29d67cc62c9..3c1f70e969b 100644 --- a/src/style-spec/package.json +++ b/src/style-spec/package.json @@ -1,7 +1,7 @@ { "name": "@mapbox/mapbox-gl-style-spec", "description": "a specification for mapbox gl styles", - "version": "14.0.0-rc.1", + "version": "14.0.0-rc.2", "author": "Mapbox", "keywords": [ "mapbox", From a921d7354249fd492d11743362dcb7849f30854a Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Mon, 13 Nov 2023 15:40:38 +0200 Subject: [PATCH 16/44] Use dynamic style by default in Standard Style debug page (internal-930) --- debug/standard-style.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/debug/standard-style.html b/debug/standard-style.html index f7e2bb4d0f4..64e5455d4fc 100644 --- a/debug/standard-style.html +++ b/debug/standard-style.html @@ -60,7 +60,7 @@
Loading...
- + - From 456467ed811bf3b2ed17d26f5b2fcba7c27fa6fa Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Wed, 15 Nov 2023 14:39:59 +0200 Subject: [PATCH 19/44] [GLJS-559] Clear source caches in imports on projection change (internal-944) * Reload sources on projection change * Fix source cache tile cleanup on changing map projection * Add allowed value to imports/set-projection --- src/style/style.js | 14 ++- src/ui/map.js | 9 +- test/integration/lib/localize-urls.js | 2 +- .../imports/set-projection/expected.png | Bin 0 -> 27429 bytes .../imports/set-projection/style.json | 98 ++++++++++++++++++ 5 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 test/integration/render-tests/imports/set-projection/expected.png create mode 100644 test/integration/render-tests/imports/set-projection/style.json diff --git a/src/style/style.js b/src/style/style.js index cb4d7b4982a..d10163196bd 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -2493,6 +2493,12 @@ class Style extends Evented { } } + clearSources() { + for (const id in this._mergedSourceCaches) { + this._mergedSourceCaches[id].clearTiles(); + } + } + reloadSource(id: string) { const sourceCaches = this.getSourceCaches(id); for (const sourceCache of sourceCaches) { @@ -2501,14 +2507,6 @@ class Style extends Evented { } } - reloadSources() { - for (const source of this.getSources()) { - if (source.reload) { - source.reload(); - } - } - } - updateSources(transform: Transform) { let lightDirection: ?Vec3; if (this.directionalLight) { diff --git a/src/ui/map.js b/src/ui/map.js index 4a39119b622..14e07d7851a 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -1149,7 +1149,7 @@ class Map extends Camera { if (!this.style || newLanguage === this._language) return this; this._language = newLanguage; - this.style.reloadSources(); + this.style.clearSources(); for (const control of this._controls) { if (control._setLanguage) { @@ -1192,7 +1192,7 @@ class Map extends Camera { if (!this.style || worldview === this._worldview) return this; this._worldview = worldview; - this.style.reloadSources(); + this.style.clearSources(); return this; } @@ -1300,9 +1300,8 @@ class Map extends Camera { if (projectionHasChanged) { this.painter.clearBackgroundTiles(); - for (const id in this.style._sourceCaches) { - this.style._sourceCaches[id].clearTiles(); - } + this.style.clearSources(); + this._update(true); this._forceMarkerAndPopupUpdate(true); } diff --git a/test/integration/lib/localize-urls.js b/test/integration/lib/localize-urls.js index b7abab9384a..0573e849a27 100644 --- a/test/integration/lib/localize-urls.js +++ b/test/integration/lib/localize-urls.js @@ -23,7 +23,7 @@ export default function localizeURLs(style, port) { localizeSourceURLs(op[2], port); } else if (op[0] === 'setStyle') { if (typeof op[1] === 'object') { - localizeStyleURLs(op[1], port); + localizeURLs(op[1], port); return; } if (op[1].startsWith('mapbox://')) return; diff --git a/test/integration/render-tests/imports/set-projection/expected.png b/test/integration/render-tests/imports/set-projection/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..a60da898f53f3d127b4b9a7650ede9870f628715 GIT binary patch literal 27429 zcmXtfcRZEv|NniCgJW+=#v!sPB-@cK*)zM$Y!b4LQC1-{G7c$ZZ!!+DviHnh*(>uH zzq|M6_x;B?9*=YGbKlo^J;&>LMQExkkr2`o0sugwqAafs08sEP6oBJ_FMqv9FTfXw zhqlsVptzr50{{?!ioC3@x7kjkf!AH7>gqa6_?uuWAM^W9!vxc@~-05=t!$IKub!LHX#-7eqxpLn6M^O+l{eDIFiNRRa1BYAhovx__dbfgR`s~k|AuP0SH@(^$-;riVhXtxTnYFYP zp$E`VYbjC?AOY&30%@mUU~pteHdy`@dou;4P7#k$^{4Qoh`Z45TiD8r`qKpO&XN%+_hQLv+wSu)Q zzh{-x@n67MW0=-f5zR~n~x+xzLr2L zK?cvx2#rDntD(9!ispHHZL)00?)j;Xos! z#+7chulA~cTz_YMWfAZ%U`PnD8CDtnIvrw&tNE0A zbu|%O5N1*wc8_2v1ZQ7-Rnre-$ZMyU_I&pCsZkZ)Xotey?JINl_1Mbxy#Z;VR{p56^wA#2`2(?mS_S||5K#{jJ*sSnj z$j25`{7KB)ZqfyTjHdW2DG&EB61f+Rmd@$y0ZsMhrW?bXTc@4Y`BCDXawEzxxWWkq!%B6=D5(Zm$v}gdSOhv>1iZ?zI>-6OvGh($)syuccaE6UOYGj$gQPc>Y!rvhJgR9Nd2Bm^uIJR+74T@Jqg*Rmdty zt!DvnB(TzHpq=y>P+nB!dmwOhYD}ba96{W%%lSI3>0o0uLMkvI7Fqj*#j^k3kM~+;uRR9B<5UG^3q>zjD_+-p1q-f@b)6hf zU;uR;&PYI3z@D|*-oT2%7oW3_rreCywW!J^mS67Cy&oHJW6yt#R{Dic%7Uyzu$R#@Zxa+V7>MoSun6PtDfow(fkppclvqLu zvg6thr5YF+LymIAo77d+P<1A`aEtT?h;bYn;$-V%4lbv$7&wQk$}ea{AD|No^JRUn z9ix8480b_+tM@yVf^fh80=U}`>_8aya;7z zj%zjk9-JK;(g+7qyqj9s*t!>H(w3xKhbFYI;dIY&cv0(b@b^_7wsiKSs^Fwtf~@*d*X0vn=hvVx zj+SSbn=oTP`}594)Z$|jc5Jl~ddnt8SxP|}hXO>!Y!Y$+?-+ch#ZR%7ax&xow++Q%U-Sql*nN+?vRPcD6%V|&_sD>P_C`=MUEHs%> zz+lf*zvFyar0qoi7s;_PoM--5L9s3BXBJs)ED@J;gp*B83M+4nfdb%eI9Tk4mNnlM z!%A_{1-7h{Z(C%bmDJ@7=@7cc!Oold^3g9A98UfX^YVV);jgO{K9^dZ7-@LtV-wVj}2nymE0(GA4@L75*#JpteJ*It~IbSl-*kjmIk6@9-_NvJqF^%!;eK-Fo zMZ$F`2S5=rO@x)BFv!By;?w7f-`(fyLF7I0#MHfTde(5rI%0OOwfJ~d;n`8$;d;PN z#E7Duw$IVglO|itf-%i!(iJ?94hy}5@D26dI@oL$8o+gDq< z3sG;=cq3i8xp|y#=|dv7O+BXZAh90iN86d^$sU=1`hDVAF`fd$&SN+!yGy|ur$;|= zEU#Xic8-ktHZg>2K72%BzLACpMcVE)i)evxnVlp2be;ER&(A9wZ%6D|puoJ=j7{HV zdWyf5K>{y(4ZK0)a{7cJLm}MJIQaHC*Wxjm^V{)ee27?E={!EvB2E=M0aBmZwH`ua zNR}xHfiSp#&=GgDQ?)9uZqQU){c%n6vQk4kP@0fWdkf}h_CrJx_oTwk@{hHj+^FJ= zAk%EHz{#uFr}8TUqC(WO0kyg6bLR8q8V5(V+^hwhue3 zlNx0yn9HD=Yy!9H`N#pW6PL+tnZA*&euvlUmBIl#>T6X$@>l(TwFD;J5#!FdE9kzx zE3dY08=3!{LPo0L)v?dQIg#EH*GEMwLr#5&U}((u7ht&w|8Tz^+6qIaChc5#rzYL( zdw*BG4l3mJvfq)}rPxz24k^bN{iZ4!%G4zlQHeye!hYHN`?s8E?6p(sCH)wkKOplg z80N4)>B-r8X);eQGqYVxQ~;>NE1h$L@Rx`Fq;=bf0El+4`FrQaLXeen_?dGxSrwx$ z2kG^JrAkVe6T^`9cS9=@7>1wFK;XBePbt&(SjUZUU^NZhs8So*?jxe^qpOq>_+tP@ z)y|Dlc9ElJB!FLMZ`g@Dsu~0PK%0^9YmbS+TZTBIvgf z)F0<=>4z!bR!>Eb#cNMxAO94h7F>0k?#NAkTVtra8 zE%Opvx(~_Od;&90FYW)#S1IfmumHMFwrY&ge>W=1AEVEwtm$9#EXQrTVm>I$yWo@r z6b8n|D19nF6W_Z&5(p!(LDJ44(*?oFwz9KN5cv+oPElz*kA1zH!%-K=Bw-#S?xIfUPpUcZL^7v4gFoqtkYt{!p1DNXlO9YU<)_XVG9tLcJG&)GqFeMSGLCGx ztnF$tLJ0wS`=aU@hy(E z{nPpBgC0cRZLVaAgQt7(e+4HUe3{mjsC&{=q8^3fTrHdRGqYILXeWGBba*Gq0ID}E zZ)8saQl6;Fr8i4(U{6+bz0={^FEDsPZ_iZ@_h% z2ujNo;q~Dy(3Rd3zC?T-&_{CTY^isAcHnO$KlW&(>T0~TEw1vv@juf4n`r9dBk4k_0K$(yE% z(ARQZ0^quZtp3{iX8&gWgEHUpR&ctK!|Q9Kkifyi{2FZ;iS15jRoXtM?HPlmhLFkD zcCg4^3XWdOpIpm1q^mDDf9YD3!EoE0_qfD*$;+Vu(QS@nuU&b&k77pCm^5_%^2R9y z0rYO*_&9LEkaLn92kzIW$zd!WOSc6Cmp@co?n!;vn@gTMKQXZwOi3vcC#GX#HA!=q zhrNeKc~@R}R5xLmvOk|>NusSr0|v9&Idb4VA^V6adX|5j5Wdf z`NlJ}T(~$PfSDm?HGQa7Dn_iDxY$P3GUr6c-$_e6V8Ci_NnKWBXCEB;uo@D%w(-0Y zABwOIu!58Z`i^*+&-mK))@SwxH#bEZ+{Wu%ky%+}L|8o#>$zbt57_DZ0{1IlZByBM zM5kz~AM--VaIX7#%(zJbb((Z!`sy@lb8n8c(0QDX*u?^|{`=iBrc`8MPYG5^h?xzzz0mU+Kra)$q+R#2$g;+7q{Y2rWatw-X$LLrT8R&KR*+&yK#>bk1^Y)ofct$B8?~1?sW9r6uAtHSlCKekNHJPM>v>D_=U+JzWF(Rc|ncd4D~0b z(*7kL_-*yhf$I`-P21(U&48`akAaxyQ%8L~sm<6CMXMohcn4Ko90-cB#1rzWdMrT0 zJ0E=I(~Toee8~CVn_-uXk6!vIaB^yHH53^<=~jK^#kAbKWCbal&>%RJ3RX&jBF&Nj zS<9@{O1VZAu)twqa3e@h9?S$;!YD{l8?H2-NWQvrQu2^9eoR24?eTua?9NTzbw5tL zuv41|GPP36;GgrMm1iBRbi5!i5}Jnt&BR}*z?0KTS zdi|Sv+UQVjM<~QrqV-QNArno~t-w|Z8N(4iK~8o?;%CZHQTzd zBMw_9Eqmu(074#P&Rth?vu8^?kazB?kDXWsMMY*_a)~!d{p1Uc_S^LOf;Cf$%}gX$ zOP@w-n>;J&U!TPoLIx)-@lrslg=2ek7`SuB#CNlJ5L!hQbF)9QBNU8TL8sh*fCowO zxc%U}>^L0upNZ3wW4Hbs0cCBLr~L}TUc!>vw^AbwK;q#p$hNvz>9+?K+x!Kc2b)Gq zCx9v!WDp5BUqR`gm%qsZE6qNv(B_%fSv{-u9J)AqeJs!ilD&$E$ z!TT63Q_IPzg`z&R?gYu3e6h}|q5>s2mV>IAr60po+$w@%3P92Pb7O?}NVs80EpyDs zZ_oUc^?(tUDpjqI!VrueT_K?SLLToe_#x_6wQNorC~7D5cv6G%^_SjPaCx7y2aG+M zQOT%zCjbuojpqdyfR&0&jGA}1ef0$){`OLcRiNlSXfG{KFA2~Eg?y=vCu_1bZ|K*ERc5;B;S5gJi z+}E*){E#kSL9c#5gdDyfaq5c?L1)DiKOlf2Unp_#0jZhh*_OHAA3#2dM1f!-jRl=9 z=_`Ht0v15{_~md2X2cOjJY0zr`U%vjnE@{fD5yCxO+2ZqAiLbR8-5NLL&W~T_^BS8 zt@oIlIh?vMehKm262*^oc|_a#298Rr(|SwiisyHV9R3f2s?vt;513$(GHY60fl*~7 zv4PCAS-AhG>T`w%rPu+GkC&-AQ=mYTb%WRtw=1UPk{1O1nw!YyQlJ=+59@fWY;nMDoroA*b<|PY0KQiHoB+_i&r_!td$oi&vg>Ts^LAZ&MWhXH4v_ zKumt)-7n*?nz4bS9M5@*O!eWes}l3+UjsabszYoOd7+^H{48*<`pV@$uNruSn;@>L zX9*#@$>U~PWm7_5Z4qorntaX!DWKRK=Ts&*WDwtXh2)d}^X?VdRB1u`*dw@mZh#{n z3zH3bwaq)Q<9(^2V?7l_aogAvz0O^S^|n8Z*_kPq~V5*UE&t z3gMMaO~{bOjr~DE>y?jMsUkH^b;yv}yB*|!{jicLIq(OES^*}EsZ~z=$^_c-K^7Ts zbnWYCb6V_%%{ti&?FS+@2P4Fn)+KlchCvXa1QC?r)5HR!*)+HB0j}^; zJElxWGMj7n>(xCupC%2E^unz}0rJ*Rc>;29Ap5TPwdcALd^zeR7#+@^f4ZE>6sfIg7M_r1QcTN zhAh@GFcxk%FRGr7@A+#u*2DlxcR6yT1~M&SJ7!#{`^$H5)hnb}`=4GK)PY4jMN_xHo4o|oo zna?ji=rkq+-T(6;w;sN(#N4<3Wj*rY_W4t=8#0-E&47kE(JM- ziR#n|XzaG6lN16uZFk1r|Jx!QoOhd9(FYsWrY>aS{DR9@WvC@ z62ua`{=)MXxIcaM^3Glj$-?O1F?CjyU{xXKP$oQ{mIfVW<*x6uF{Ws`-1fs0y<73=s*cji} zsU4jLc2^>8r_vl5VlPlD9hK_fR( zqVzr-^-8p$5EqJMf11Gl&r=Mg99E`)Q$hMwQMy^mylOxQ=00~3rL%dxs@2ofP$cqM zU7l+P*o^A`I`v3d88De)Eg!r}@Fmi^Nx1v8pt*y?plj2&x0(5`=3a9e^ zAZ(5qQR{m%iQG-eSREAJ?)PA%yFA*K%AVaD&~))7fFkCddlQezfjW1CfcG>M!0%MF zlIG!vwm%5FgPpgucE( z1H#>g#b}(Nf40+?rtlz|l9?|@SU>6k68z1-QH25Rfxj@06eL4W719VHh{q~>!sNgk zY_8q-Vse!o(^#I=oNaIj9_MLH1{Is#;^PodLd+DkjI;);x03*}niuy%fWe7J3YElA zbhz1A&Qn&BV4 zv<6s@ev&d#5qikLkaR4KH=uu86Bud32ty7@emC-!mj_58-#zGIDDfnt1Q;Y1*VAJw z6u3We`lP?L=l`+*`IRQ}z?mZQN&Al_Jt!pBCG#<`G85=i!30IPF!iK>yBu|TX>fC4 zk^Rt&MbmUXH{itsTfY8rlYf+>vxpIj;Fn!V{?AM&vj0i|1HFcGoLcTeRI z&{HHyV%xPAe-i-OP-M=kNxBUyA2b9;!2V{HGaU>yzRQ*7Mh?97$PXYnq=H<^8JO-2 zf~`j!l8!0B$NUE_a(S7D3WOeuykbwgjYQi++;zz?Dr=2X8cL9lc~z#RkU?UF!d8b& z%ptD+J*4|WTllx3$l)3GC=qG;zP}+%NsE{bX}D_j56xdJ0O6|o&K;10Hbxz~Ac%mB zpo4(xX=ATW62fV`1sJv_`U4JRH4`b$w@?tLKDlbN6j5Vnr}^!etc))<+P2Lh2q3q8 zn{WU!-8S2B%E$0hV6y4;80qm=hW}xV>COr%ALioK_fT}079jBnxzonacXJ()jb3B6a+!Y3m*z70k@~z6^Dy>C_4BKH7UR`A z(*Z~0HG#hIq`S^wz1)F^Y$o4S-G`H=et(DZC9-g?c*+y_#_Co>pe^{3_W}D2A)3n< zNsUS*EMQNmLX~R_ukyNhZem$iYr*Jbcx?T9q>dSBHIQ&uAxP+7f%IilZ*GJx%;l~{ z&dPX4!|eioJ(<+Q%GaNA8!vUNRYGlQuvfP?odgt|gH(7RA6eMbD4+<-X3l_M00xN5 zFTs98nQzA zuX%#B3K)u!Vs(Sc+5%~jSH+il|DqJra;*}O=eurXb6PH%FalH~vxd&;x!@9hcpa-g z2uX%#u}1$ouWa|(k~tPgVQoFwetSy@`_W#_V znm>FMan=_)hLKF`qJKN{`$D?NyW5S>nx*>sdPTO6vzm3l5ftfvSnU7t;P~n+PcbO^ z{G9!}^Y+N0OYYv45WVzSr@^)A*rIo-+Y$jk0+B6bpX0#-}T+u5;eq2I}zVR@`v2Ba_) zgFsI7KY73;vqFow??%*I)xexICU#5HXmc2CO7tXSdVVSc>98*o6I`zn6C!^fRSFhImHI)eZxp;6EM!Ci=c!KQA( z3ly6klVEwCqc$PxIsHcV@-|)l=Wox2KpU!Wh^parT8Mv{W~14iwE+?Pjl^F_5njP} zjho)=tj;r{GMOHR4VUiOoHESeu9}uHT-G3wU9y*&CV-&BmnAM^L$%<0KTl zXLh+=`K2$(dMh$+P6Fq|%=W$Zx2|rSPW3;M&+ZnO>zlqc&A|Ev>Bw$v5eZKcO-6DV zyVcPd=%Khp|4T~nDh#7Hm6$Ep2r9$S?I^6o_*+E6I( zB@+eQPeL;Mvs8oq7oz2UpP2D3+b<;e4viAxGsB3_bHs4SsKWb$`tE=BJePo>_%rVR zB!vS)mudDq%W$A((Ng9lMWh^^s6+w0&lY}!K0b{Llt5N$tgM!@!jQ5UTB)p^yiL%raCEyh(T_R- z83D}XiC_EnLVrgd$t0K>owX()P<`SME}Fxx(sZsVhFsY=~FdPO>oGG^hVvl6rY8 z^Z$Jvio{h&px_3q$j`%u7~>vt&T#a^M+R`b_+tLd7BPk0pa#3eZG7sVFXw!(wD63a zdoMS@Aj`?0r=e{0wP1^LI4F*mU)b)TJBqFMBR>@sNq<|g4b(p5I85dJ_wJWL_cyG+ z6U`CDh&NlUl3^tFd>nrAzTTxl)$XXisb*9Fay)fpa{@u)tofK;4;*gYkMd$vu-_qp zTc3|k3LCFahU^a!CT{qj*k+q?6i%mBdglcG?)1+OXkn^ibGSAV{k_K@kHz<@5TU?M z^3EfOmGoRew4je(?pxqrda92VtVpk}G9k{vzFirO z8Otb-BezDuuIC?c*7lFu>*q7S&Q(u);Sa<6cxGZ=Q2;yU8k*~i)4DZNmnBax)z{a0 z5(MOi$_`gcej9GVf%SlDp`#fv&IV=?hjb;Z_S|eC*}BzsS3f^j1FqNgnT+K)nzT-C zn^u_8#<>(RqM(*tKh|&Tjf99E8GBzV#B*HaJKEt65NF7f5r3V{C*QI!&D^HyRU!Mad)>xEf0b_WAik0>JAh=m zshJSPDVr1jUwfH&Lyo{Z`DV7F58dlS4mi&^aDevhSguarP$Q#dh-sWq;G@FYfn&jS zFlqPf&8m-G;48|r^LLpBW>-COi-K`#bd{bU@niF>=mE9Q%uPh)90l+}qxgNvw%!&# z2!J^A7bh-EUqY~35tDINSvq8^CNt#*;;*hkNv>xmYQo=bOkun?*bi8`E2TfgluR0^ zq-$8`D>=BlV^(|GBaQj_1(c}SE;dpd{xf|m4kiUxHaC_}`nh4vmv#3%nYz-iqqi7N zQS_^SeSTlqu4YP9D~%@k1eDeUZ+3ZY&6W?oDTX+a8c_7r($>OIbBRonI2f;6i`C{7|7d&a1J~LA^b{~D~Pk_#&TlqvUuL-!)wwCE+;jlWkAR{C}wSU zh3cc8iO!eevp7E*P{#LtXl>I&ocF`gplh;_IN=@cL+M5}eV4;J2RJ~&sE3FL^ENAL zjH#4t04=}c$Wmf*(7k^8_ueS@%;&WN6P5I5Uhgv=YW`wKTXv_PsM)@Q+t0CU(OX0L zlEGfXLc^p+`7Jr{#cZJl7i5z^OP;wop#aWe7abR)!G(c$y%!(J?p&rdSZ>5Qx*9Ts zDCgdop6*SZoM6BOIrLX*AN@L!TkhG-LQ-uWx6#S5!l|@(T(4F4J2=|SkKoZGIFMo% zT3HV6EUO3>f5gAXKIvT#EbhohT@oCu1MITw*05Yu!sfdjH7vA_%Vy0vTM+8v6kHBMdE!QMxXPujQ~)@M9M=ed+87k?_&m7LavP&mULLY z?&EMlJBee7)A{qkg;JfYi~11_w!QheoRIoEP4|vI5H`KEBZ)j8vz7*JQCZR3^Q%5Q zV0@-yM7bas`5;h1H&*s`E36>|?WPvv(JqX!^i2=7)&Y}M2e4r?5~NI!KavylTCNhd{j_gJ}KHM^MW*iclvVX z!KLEXpTgkD(0)B~C7Ur~5E}0SXS3&7Na8oXziwP*CIz}Q^-B)kwx_MX#(PWt8cb2^ zCnuU8nYd`d0B2yO?$+AR*PuW=Q+dLpM{@mJG&-l#Yf?cU+HikupmJLen8TU}?wzz{ zeo44>`(#nJ4SmV#wysn4$X)KZ|C17A;T8#4_xA$yVOt0wa-seGG+G-FeHu;~GiRh< zletl3Sa}`v1CZbzdN(_>{-gAUIn$d77d8K#28O(BLQ&d)6cI@lA&-b?nSa3cGUvzU zCZwoI`g|dlR%kO$;WFXAxhM7Qw8~O3?UKQ}@$eT?yi(<@0>kVul(QLkhSYyUxx}aH zzHhrMx92-xXK9`P#KC2*LZ_E7?+bmL$JHQXX*l!5?MjEr>ub(u=0_Dzgtrase$qfE z9q{pP&N6;|0h%R%gR5xD6_5ya2?)xi zplePcsQ_)U9B3;`|M*y719o4!P2WDCg`)2ltL3uT6JM9XP+Ofc(c$5bJ$BESiY^z+ z8e?R(b$9GScP*6ruLIJMkF~Z1A85?!L-wglBN~OVB65J$UdV65*zpBsuk*RO%*<~< z9>`mFfF47ZD3Y6xbK5E+rD%c$0v+3({s_~A{gCYPvz;~S3O4{UB!3GiuYPu{!` z*AJgqqA4H;mbPOB#UV~Glu&cRbxciV8JGC@(yE#<5Q^L8hNs;m(^z}linn_ij@Q>w zaTw{3(;V$$emMJC-Qz1O51!HJ*58jE$9(05aaWN`pcJmS&t9nxM`_9{#{v*OJmb9u zC`BwP_XYLFS?86Y@%yFF^23p8{L^uqk;545S%1T96d(2@1D1+1R;dek8)37fgf+d< zHhdU)$bw!z2RlQr#(3dY{J^D65*XZlg?l4jL3hdc#HQ~(Pmk$8vudOU*V_S=NwS+9 zA40dkKwcvGH$z}XgZ8Kp10w#|#Jy$8+7B=N+}VvJ;_t?8#d%Tz;m}W8y9o}Oi){IBSnlzu6M_0o~-&l^i2tL{sIc% z0O9Kj{~rPUH%PQ<@LbSO3Jxti>TPVn-}fd)T9@nwAtoz%fbCxw^PVq7=sQ*yMKv z0?7Hu)q_q4_F;{|facmuYU;-bJqSYLOrly2mNOA^g%@axi!y+v-LKy;Vl&VI!*5>@ zl7!N8L9M4*8RZJXyecj+U^6r5?)W-|< zmkWQ0^1hT>@^=FJh)xCRlV5i$5>ragM`cd8z!ZMhQr8~LTaZaH-wdj9`hsF9;ED#c z9~p-85V3i{f^7SF+(FX@Xf1F%-U|oV>2@9mCtcc(#r`GZaCz9v=m0{v7K%AVBP|)G zu(`P94?6u0E=;%y|KIUbpsoEb9~FM3ETn?iXkzHN14Xu8PVZ8G__t zC|@zLO)mS7_vB#Vnui)AXMq{d`kR^@xwr#`Yf4N>{C*_vbx+JEkWcRCDQlsGZI?V}kGSI^f4E(gY%zj~~Tv)pj({Svq_+y22AMHo%YaQt(} zqxj-&)fgj>{>VV%j#c42yV}B;K9|Vs&&d4{V-sbtKEygC-Csg|$n!2e z%u_Zhme(Gd-K67$e{!QorQlNzOMnBXR{GM7jqk{u+TjCju}F(&~P%a>Z@+6TjggV2YJ`{TDk>Q?f zA$bu0Gjz6>z23Jyv7M!WUN?$DCYe=<3;CTMx=U-xSm)_(RWQ3=o9z6xc)j4kS#C5F z4HIQJI}pr@J-?c?&MJRO3+cuW*faQMbCHTq84OYN^n1X_T@RLB`%drG$&MiI^nw6*z|o^BeD%MB3RAl7V@ok1P?FOav%Y2a>*G^r4c;LQI=8bIOrC=z zKi0Y+zGrIC64hGV$A-RMO;RqI?9ECpPN?D?F^K2U2!e%d+K$UN|5|59`SJb*H9GsSUs z-g&HLdT|fs_#TG?h@BuOrL}UAC4OQ>(yg+floCnMw)3TY?Mw=v1Z18RAoGl+1hPXg znOXrKfh!x0%Vi^@4Y+x3Bq199ym^NV!=s=z6}tLaX0}{oQ6du^G^oKDnz(c7%Lwd8SP)y-T76ldjXnI58xAJ`rdl#dJc@Df zc9OeICRC4l@(U2Ie$5;!_&Xad`UTz9)}`JFbEPkz}wvZjonKxik&-?Q`lS=LSw z*MAf3yQ-CgaRTG9$!7;#co2%r0!J}hdXG{{Qgi226W_}`Mx-ecuM+BQ1a8A zFvWDk?jq8Np9Qa(Lhb?(Qz>dr6K+L!9pW8CD&vyiKxd3vgp~UGO2Te3U&UnV? zei=e{!XD|tD$YZ(Fd?K%h$FBre0pg8M#|-ve8e9P9VN}_^u7K#i3Z_ZqD_b0Blv&k z$HaUv##RfjyVvqUF3p0?O<7m->?FFC!;q6M$*MdJvc;}v8tkSC>~hTpKo{w?AdUEg zaqGj{k#FY;A6|Y@lO75RotZsb=^o8`l=5RKXEh{cjwVBNRpHl`*>vU@nfDV5efa$S zwcSM6+_<5y!=Y?;>7~~6JIN_Z5TPYR6}^u2+g9D81J3;uB72WqzO zba>t&kEtRzZ1gf}y6ORg7iLkKI`lbGr|N$i=R+u3r;xLto*dYi26@-ZVb z-IkNUly8R{K4EV3K?qAR-6bpbz-`OxX#Hm@2tC|OaV9wwmdj1rf1DGP=S+~Ok1wG1 z83Ko9{v8W3(Vy6Z;(YgSiFK_%tOkwgd8!K&{kriQGA;Hit*kkml4vmpwrIF(JIT8B z)m)VI;@VU&>k1(=QADE5(@gPO#Vhx-x`R}4&5f|}sscUjvnQNoxN$G>yY-Y1eLH;H zHw8y`R3kMk9s~OpyB|E6atQ;l4cJ%g>J+U$xA>h$mR6HnH_}7!FIE5F{vW{wInkwy&qlNR@vRN1Hup<^!rWbFQOs( zRRPF?5-H8q47HxcpCKWQsg)%EB@BL79<^I?nLZHAQA8el@prjm90n($%6)x#U17}$ zfa}0}y*^;%a&ZNCAc_9*VhrnJwi3&9bASKye&c#i8(I5)Zzi+PVq?G}8QFet?sfbb z%aXyiXsqVmYFz2B-B6WD>R+*RDNB;ED*+de`T`if)OB&oqD}6!HEFRvW3;)+&WYvc zg9brSBnz@}-3`C}CCa5zuIKcfjh(WQ(q7zE15G8(*mw%>N;V2^)_3&4Y zds!MEC6(jJp!O#&pd|fE5MP+a1?Zv{79L9CjBgm}o+i&lpNyAG;vD@}oRMyq+4}jU zb$r3DbgdjbxTr3rVQjji)`%nELEBv2QuU_U7{3A9(sSrn{P-k{=OsDI)EnKux5up8 zD6;x}$c*LNi60dHFy$1G4id&b0?)(+X2GgpIk*5Bo8OQ5JI^2X@}>t!rq2)rm`-s;(#=eJ5szp)u?t^V?S2cIOw34lmBE{3I*JK!q+o z(=V~av7YX?V;-Zkp0#CpcLLIqlN8%SeiWz{uZzuyFPP`$*|U zTdpB&n7ZcE)vba(>aPz$^;)tR(9_c}(ASg8@MMm(_)0u~$WxgfMwz&sWnm1mh!eZ7cm~k;H48DbE@YVJX?K56Se*dy7e{JsklL7Map;H%UH1U-%9Upx}CO z?yOqOZ0x_ao%z2L{TJA=6o;@wn@O}zimMt00-TzaMvQ>EQ3{yTZGCUuZ{BF3`Y^t=;rm@S%EMucNikVO-&Ww93I$%^D@9W{8!KS*us=fN zPtE};zh4I$>o*l$lzE)RbNw-o*K0ea)<1FHLvO%eb)Ht}@;z$n9V$5lix)Ki0SLn- zQ@pvR^UWRX?K-nzPb6Ei;VT zX6Ysgq4)Y99am^TWp4{lPyjNu~4B(zg9c(c*pRd#%@i+65_P>zwo0xATp~#Y<``m8iNq{{Nq19}m zXVT+TY5}e1xAccOvItKIVuo`sGRW~-@@r-lFBe;=YP`^-WR|P^(M_TA^3AaBaq#H$n<|%0M|2Y;}=u5`TZwBpbe95N7+fTQ1sTai>Xl}etWE$ z^?_P8-{V%Eh)6A)=P+HRsWfX&o12I_3S1sU>>Wt6)9m>!!|~@OgTK`KDpl~k!nUjB zmKoy5WZX2{a{=E;_w$LN2nVvf5}BeYjJX;`A&_^z-OE?#tQxW>Zg(>VsBq zYUkvxa|l+{ffaumbni!s)Ael&Dg4tI8zVoyU$;laGJrsDyfU!Y8;QcYrRGTBcQaEc zdo*`P`V2PHy@o&PPKmFSmo#TzGZywE!+>%C>=dh!l+1Gz>I2$8VdU4iWY6!d(kH85SNpudK7D}>7}@z$q+C^A}p2+MP<&Lf+sr+gevNmeNQZyUTMr< z%Syh7{rT-}SX=rO|Fo6)-jWer=eD{3m2_28lZC*lI5QmbaDI~wpZ6iHq5!+2x!)-C+y?*5 z=YVUI_@v@*R0q7`>3%D}uhuPBn*BcUIDU1Ur{?!oY(;DKx^?$wGX|XOUTuxqDGVI9 z90oEBu)1i&+_``>Yv-oB8e-luh)pt`e@@_v7j*+mxc?iuNk~DS2;11f;j`j!M33A| zE$08Vch-MRwr||OHU~GIQyc5W zUvHX@vN(;$`h-j_+7ch^3iO|eLO&))C;l#J!M}^zPl+F3_&vf<8p-lcl8Bl0HAIR5+vO2;nrzLH^s9MaeUwB^-T|51D}UO!e|f_1!K6&dTcHoPqf(VBlx;TX3vUbH zjLHI*x7$kE`%5gyqPtAuW%!>K^|~K9Hhk*)75Vdumw)#506AMpSl5(Ob-_`-ZkV6= zWBC4QXz&Zy*O^aQX4BNzbPA4I?#_F{E4XVP`1;#&(~sU==bzsBmi}l)Gzf?g1@3F4 z&RXpx37ay1iAC3vLrL+2N4j#Aj!8XcZ=F$+3(ItLW0u=(dKj5WrCOaTc=ynr>};-Y z5IvEU1i0^IN{(Ie!!S=S#8Sf1YWcWNJn%{usOwDLbITWtO?ja!p@(Gg&7DCNmlj*^ z2WX59#%hp=csS+1`u$l|G%n>W)NrM(gNKOE|O4o4(H-(M@w4x z)yH$puJPG@vEF~2lZOuX1;mLf4>(FLY;I7~1etsh+xmRywc%lC86_g-wlXx~Mt;qr zP`K?HRE&B0x`v@OAF3#`F@w#(L_>UaGOR>{6^xlKTP!2CTt$k}#YUB4qoQgiKN&GtpxJNFfm2L`zX0w`xUZe$ESP0YwBi1pc(aq+Wny$ ziRT@(wRi^MxDfs@rH#oB>Fg2mc!L_f_x zPO4y2G*n3;od+)CyOB$@ck&5I-b{zq&8B;R$KTTxrQ#c<@d~QhwTu%HO;}go6N{U_ z^CH%~ZLF(Em7Vp7>6xE5)57<3RMV1@&Wy0hdEW*k_Soe`!Anr@e7achw5a_=i>mc< zVLpS8*SAp1mU6)(#V)J?Fl-s93tf4;>VpY1AjnrahII?T63DUqdsD^MROUNLKva|{ z555laWSNq>j3Vydv9WJ)lfmnnF7_5E$oSSPY})+-Sa;b%Z2D;TD%o1wt5EKx!^~+G zlD;x6dIrgS6B^>0d|ROTzX`b7{&QZ7>6Lbm3*!77h3Jj@`GtsW{p!x znqeDqFZ^eTPlwV@KSNN9LVE$#^%FDIV~Yi|Qy7Qs;PGJ>!Y$UqH$_{h=H)-P z+uW3TcGvnlBInW<`wl{gt2-~d$z7QCjVI@NJ}({+E(cygVCBtgDVi-_-UF<&a&y?7 zddvnPI3f+u7i>#fk8><`xB~<>tU>7&EZo+5N@fhxQPA9eUuB3ls3JDL&Hd+kX&eQz zA&b~A%V0~)O1Z8|#U%V58me>enCNa+3x!OBBd1xSJq`~}_pZ&~-z(S>A)F|#KQMh( zCodMZDZwacCIm%}D#qf~cwfuQqCj_hALn<0l##bwWzmT?*FKyl;+Jqyh1MQ7Mko=5 zM@etFzh+qOB+=RRN6`6RrNWkAo?2CZR$*HI^8zEU@pbOIsd&zp=bteZhMkAR987%? z=AqI7^N9n^ExU`^YmwntF&LyW{i{n>p+hEvcBq>h|@Z^3PAX9PbR zKC>ZAPVfj!MxS?GsCEe~8?++X>)%<54yX`mA`G2Lk2DqZ4nYk7 z#S4e@3miL)UqvHQI2(yhrz@Rm85RP`C@>1O?^dJ+d`>%0iPT<^nraRC*-H!eIc2%r z>FFWepe=gTG^K3X8TY$OSH4O49S9U}f^^R(vV}vQyRY5OSM;GO zwsv@_1N>J;ZL4~LFgl^zx3HA|c`gg5TQ?t9fA zP^wvAA`3WeUZQoV_8v)E?wIL4`8X(ngfzSqF;%mkp&%!1x@0)l77Wg3&kQ4@8)#6i z^su^RRV4Okk3PCfFx(TS?T^Py5NGA~E$G`j9+hNG8gm4#(k_pkmWNSNbWK2hrY!vXBQLJD7~wmXknu!%1be(?h;XFUT>=6R1@n;RLAyi7@b}b&*y}(46t1k(B-|^`4(Ei+XZs5WZvM}g`dd5qnO_< zn6V(R1jUyD2aNW_!>0ZylMcf6_8GBYO;W342uJKLGj5)cKC-}ROb|~D2=PT3(F$RQ z>walUkKlHB!drKC6lDRfn^3_5(D zDJWSSf#nglmFdKTzVn1Dz&Z%UyD+gEF=$={)CDsq=NZqZbr59(F6_;FMDr5?RnCjnLJ#`bJCy>%>(I_z5q0DcB z6V$@`K};470X1PNJm!~SVtDom7|jzOUUKNLUz!0MSNChuTH5Rb4oHM_SHEqjC~_P{ zHou~|BB%`rzoXxbwDf|jTYrYMd_Tqf0kYMJWxh+akKn#4q?sM;sS?TH0W8p`F>3Il zDJTDp{mkk6Q)cc#fn3;d-Sb3W9udS<3s9*cU&JVd|)d;`D(d}&hQE04| z_&^WuMVjw$hp@Y_grN(`v7$AZ#fGK`25r+N9eiH(x4$9w3`l|7ICd$TVxqrqnp++f zvoW;f<(YUOm*%Ia$_Rn`JCGxvmOj@*m<2NTe2u#RkjpAIHxNNki$gVYkb}^Rtiy*k zi@jlbH$=Z6GKnl?s?GsIU=P<&%}V=TK-&#A#R7})x}(*#d+;b_!!0UTdH4&4a64Ew zOwdkZV1O>~H_CjGpd7F-fF&Vxegoebg=>ICjYr%ct(CrdzbcI)6ZW2hd+N~+{(X4c zt^dgP(p#e^&Pz<^tXO#9_SWY^A+E#oU9yyk*~vgx~ssJTPw z^5IYQ1P&v7(R~cadTp7nDB$$+NDe0Se8c{?dCaLnbZV?ikNk5hc@3U?ysH*6L(asx z8oz_T;-oQSHMyWe-$O0z`osS3n#Vk?s=b^wG>T{TE>@)|E{Lp6v+q zP=?#ZuB-IMS+YRF_#~2ZEZtDB=%&iK)R3^p*2DSQGDvGp=oT@q8lte*#35l*dpNuw zjt{qLtZjlhhrg`VBLd|=UE85Jm9P>F*7=H16@m%V)aHe8p&X`1xTrdlYUXAkG)@>c zMbL3FfF1b=|8~*zFq-f@jmuX&EjJsW2zF^q&P}q^$AWnL8Xd}5^rG|#8bh7lhwm~ zC%-t-{NriFTSf?M`!m{{JSQ_(1{gz=;%LBoUmskUY)1sN^ z3tTJ*O6m?&LFu_`p7$HgLy6C-i#)2Y{)$ORooKf?yicaMWN5n72OO@tmt4I3pgEY! zTAu&wK16(!3iXx&1g{!!`FsXLf=-`*@ zKIf&fW-;X&CPDpi0gSz4e~@nfm&=hqCtE_` z_iJkktW-eM%v158Lcsa~$z;KP>R)Gs<4p6063QnTr_7bUo@t-H=3}xAh$6P~$gn7XnV z)#|NgJ4H9|I>-%E%Afz^^c zA>y`M@z-p5Xc?j7y4B4s&x4vM^Pp>`ud=tjfiL4ig+jGb;-@xC`f`ZRll5$|z*M;h zAbf9yC9l)~VH@(pKbwpmZ^K?8PaFt@%*0%?nZI*eCnkM6<_ zXG@=?ng2oBs; zxa1gpj=JXYg0~$XgCC99)0^Qj$MUdJEqAt77L7|W;Qc1A;#b&D*+yv^q|bT~db=Dw zFYht5=)c0*pT#_>8d+B`CNwUUpS9FC*Zu?4w^^G`8`=^S7(ZF)!v|KdvwLOlx3Z!p_L%RZ zCvFC-;69}L*krPZpFuWh)Fix4W-CqWHjH~X2vx#oF?BQ|H3QG01-0+ z<~jkzWEuOx=5Aqn`cTa^wFiDr68*i>I%?yY`w6?CoM#Mbrb2|5ar=Ne4NvWEu>Jn; zCjsa24u#P<^wsgq-ZUViRn<4HGm??yeX)|?a$5A?OI=BhpUSTCTNzIDfg_DfSp7r| z10X|&t$aK`=-d|Wq_xK@JlpMTQ7Su^ddHpBL;w7nPW3rw1a?!=%e_{~Sxsr-AYr5M zh&xFdDB@^z;7EVaAIbl|XL8z9RIdnfK&m> zY!ubKjkvvfu244Ct=Iezz^LQCHeuWbB=!wX=Tj^Z)pi%Ul)CQJIX_LK@WVWfZl!_) zei%{_wuu$A|71$?o>;DaC#LJpdOiY1-sO)c+IMXyhSX1Shw!`jVu6z=bk_K61~`xT zQh1hTjik|Ca^||yhE$fwp3RE{y4LRjc~xg$dt=7s;hwii%r2Sa>f~^`pIl^b4@dF; zqg&6)B=FCXQeNKO+)38Rjz>#B+LF}H)2=ZHVe8G}S?;{V-UP!{M9czTqq{IZ}Xh zvVPF;PgHktR-4NUue&{Rr^bOl&EYdeuRdO^KGwX8WP*ZwcAZ>^@M9tJGdZFLd@Ic& z(_N|?-NPt|kUM(pzmrDkBej<~D5S%L1gPW~r4vz!bM8c|5d2i6-AH`J8$(05;>y(_ z&H>9Mp_=uyBPWbMjf3-Zjc?RAX-|L&Z>#mwz)AE3&EGQ?gH*JQ@9w(tLV~@PYg)NE zDdwnGx4{MfTjga75>*pVvXdM%Cw^Ny;-*OX5IVRHP+_W^i)r-g?B4%Y??5Ns9rHaF z&%J-j4^w*}k$Vw1GGKWL%^gY#YS*B*cdgd6^1D+6QLg}H%0pes@y8ds(ft4g0ZsYe zN1v$>Ck&E=%$0JIb8W`h&_}y#=l87Ex?PC@%eElQg$wEm=aOn&HO@eHceLudQ?8?4 zk@$9zN{*SvB8-RT2f11`QH)!>X#m?#|9Z1)nK%JdtTHgy!;O={c{t}jDE_r;JKj*e zEsI0_l1%UkGDh=AfXS%>aj`ROmN>(yD_43ARLwtlseTwd!FALAX0RSzd~v#i3M65Cx!?{*DCqBL34I=#dz;TAP4*j}biIUC_sQO^ZTHH<)4r+)sq5&@w z#K-3OokO*U>`A7AIfnHeN|j#%Bb0e`;CEp+n4h@)0;R+spmx;Abp1Ar7l6m;vVdEo zC2k8DBUfGTj_hoUr}FL`xz5YR(!fc)5%~%YQ1^!qNS>((Sh%F|t?hQzX(3&9%|oF^ zmW-F^5%6C71QYLgulO1Lqi;0tAhj1Z7F=J~GtLom0hm~#l{qIWYE^&!Vtl0hG1j>F zPxr+K3l1)$(peV;Y*ks0tl%ooQ9UPampV3Ggk@maQk{~0*{c1YW8k^0aQOvWX#M1# z1O$--v{)ORDXRhGm3v*^`jn#a)4P1{A-D%vjeCAbw&gi^yT7A2bZu)F_wdbF4F5S< zr@v)OQPv%|KOI_Ecpsvblmbr|E;l$@0_0fsJzviDTVB+-=**IQV!~@n)-;v>i93ZE z)dTib*vDMz^;}I9;eHQ4K=#1Gk)nE&^=P(=>W&|J1&oQAE*l4$KqP9~eq?I$*lAd2 z3E6Sb8 z?J*i$2DD`#C#HLSn80<$&TnHc4U?QE5hM-HwK4w3uqC2q$Q5)Z^Phw zLzwoK8?oZI;a)x$8xsD1fodPb*sTtCKE0U?%+l^$mQVxuMzE7XSJj*{y9eacQhR4= zZ>9Qz1OW_0WzWU;jIF`Y@pAWd^ycUnU5hgAV_savoHBuf>>e7;51ta+l{Dn1c?oEb zqVa&T{0(-boL25Qqt6FFWd7M`&x-sEt}8k8+|A#pQu)pPHQkeQGn?_JE`g=p9IMw6 zXdD83k)Yd^ZQa;I5g_$bdY~aL$$kzySFt5XpoFVyne;LnAS_j9f-U8i$ zu7Xqvyj+cQxmmRYr5_sY3BG8gTG))jp%gxK)v)OYp@WSKQ(Mbja2oLxtdYr)GSue5 zq3~pTKg?!u@N-sM8Up!VLoyXlI4^15_;n_4@x*`HChtSW^i(%rmi%Pqf!vF9Ba5Gs zAv9;v5CRTDH`!5`zlSs~wZk0eWa*A*Jb?721SG%uItmB>Yj>5@E*P{sTKbo^n#E>T z^&k7=a+_jUg#M^}?6%`yHdYFFr6721j=KvsUckmvTE2CP0)4zG@~D6HAms28`Obk= z0~q5F448F5^Q>)1QvItDf5m1tfQ9)Pw?gii7;)m@08YE(P)5bNSGH~`_= zFx^KM#^q4Vu zs91mqIKhL3`NMyn#(ohxvQf62ia8g!1wIi00slGuft!(f7ooirHZUpPy!n*`Ceh{J zvTS~`7emoW=}Q9|StILi=*uWpUNxx8EmOA%KF)GjN42uCB+L!h$!IfDU`ofyGk}{& zD;UM$(*gZ3L_|D3GS$%sFk>kbJ9ifOQt^MCfnRypcZ;c(K_mi=rUVZ`){VF2(h~k@ zMBw2?P}macdvkIOXUJIs8L(v*f3YvLmPty{f5Xor&^*eZ2UWwx6ni@-jqE&~n4^<~ zwAw18inx$N^|1qcT0v{Ylg-3h8x{5@=MFOi_C{1GqHO_&MtmR504w&fczOc~=LtU#!uBm^`0irlU4ot9rro`gt(#j> z5W^S1^Xme$EMTjkCJA2iYyuctp^Vor&E`OxPJM=5z zJXK@e7j{2WBG{8m1ar&^&r6tq8X!xdXyA%(QaRNnLiaT5-m|}N3n;-WBpr^KB?NXD zwRs_t6V~9uum7gioeuBm#*Yo5PJGN8dGH)V-r-SlV2J&rK#q~-?w>Qwq5j|vT{+%c z3N6^D9wvI~9lA+<0hNWKz7*izJh*KPswTcnCO2Gq>^zrzlctwlkrp{BrIB(aN7;Kn zuTCHQ6jq`jd|pvi1VgG5gsJ@s4`-*VEneQKS__v`_KdJaC$1__KQ$!mna%y~P|uTL z7QEwmu!lh#cH2KR?CU1HNhC*(hE6#K{9O$)Tez0%6C%fcn+%4!L0Yga?St<*&3I!> zb(FjYx;28VwgxqqZ$Bb?g?dUk^=m7Gx{jyf0?Tdt)w#%>oR^QC&np^YZG0H|Z-Cv8 zwiJj6a#wrf&evjaW`%{noO}CX^}u-HpZSLkN5g{RUpK~@{qXn_52{5N4Am0{9%a`= z?^s#FlWtv1`6ZvNX1Sa>rb942WOT2tOv+xT+#L)vU^Lm?_jayKWPGl2N^{e3qR^ZR ziA9Tm7kCFqUw$(V;R!U`W%T^3!$F(kX1{**>z=rNUgfxe%_pu?l3`3Fd6YmG(n`Yo z{UiCqV9!d$5_giBLzVi8%Z(`O)-_+!9E$ypxVcPR{Zv|U?lf(uwYI&dEP`Z6xe`3P zo_jytpmwq40-Io{weT)u4D?*1|tkligytyJwMDum|T* zD5$E{0uDSh2kSljGN3~WLJ)!dsN7#Y6zHN-Y{j^ynDh5ue7CPs$ClMgHPU2hMTMZo zo`2Wqt#3t@@~e$%i2m=IZdEkSQ#NCv(T8;_sv1H55<;@xF#mmK=qo3xDb;uqKrLKs zuc$Rbfz{bIIor|z-x*(JrGI6u5wPRw*H>4H;XL{r-{BZev>ST{0jIhxzrEGW_`D_`F2Jw9;DQm2THiqY&9 zHHCA5%o<{gCp->%B~DFuYO0`cpp>qQ1+kS0@*59^zVuEd-!v${Y0&gz-vkuw6>3Wg z;G`~ksGgh9XGZdsJb#x*K&ZKLo4Hf6BkIfnaJL-K650RiZui1+Rq5yRlHLZ#4J@aHkWd<8?TUhJ_qT~#uyq8{vQt@)C=;VpelkKz^ z_To$AF8CFuz{DLLN{FOf*VM0?zkF7zrzIt*223+N;ZH~c42z+tQ~m|i7x8iw?i!qA z#mAGM6{#4(XR;~AC{+;b#`=KtpAh0Dl2sh)0oF#OAJ90G_roU-u4M{RXrQFP-UZvc zqBafQDfsv{5CZ_usNTn<&mdSDn2A`IeWZ_{+5fu!?=k%^B>p#Y{(qPyj)(=n3yjA4 SBB>zoPhC}0<;zv`m;VQk`LKZi literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/imports/set-projection/style.json b/test/integration/render-tests/imports/set-projection/style.json new file mode 100644 index 00000000000..7f3bae358ac --- /dev/null +++ b/test/integration/render-tests/imports/set-projection/style.json @@ -0,0 +1,98 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 512, + "height": 512, + "allowed": 0.0003, + "operations": [ + ["setStyle", { + "version": 8, + "zoom": 0, + "center": [0, 0], + "sources": {}, + "layers": [], + "imports": [{ + "id": "basemap", + "url": "", + "data": { + "version": 8, + "projection": { + "name": "albers" + }, + "sources": { + "mapbox": { + "type": "vector", + "tiles": ["local://tiles/mapbox.mapbox-streets-v8/{z}-{x}-{y}.mvt"] + } + }, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "pink" + } + }, + { + "id": "country-boundaries", + "type": "fill", + "source": "mapbox", + "source-layer": "country_boundaries", + "layout": {}, + "paint": { + "fill-color": "violet" + } + } + ] + } + }] + }], + ["wait"] + ] + } + }, + "zoom": 0, + "center": [0, 0], + "sources": {}, + "layers": [], + "imports": [ + { + "id": "basemap", + "url": "", + "data": { + "version": 8, + "projection": { + "name": "globe" + }, + "sources": { + "mapbox": { + "type": "vector", + "tiles": [ + "local://tiles/mapbox.mapbox-streets-v8/{z}-{x}-{y}.mvt" + ] + } + }, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "pink" + } + }, + { + "id": "country-boundaries", + "type": "fill", + "source": "mapbox", + "source-layer": "country_boundaries", + "layout": {}, + "paint": { + "fill-color": "violet" + } + } + ] + } + } + ] +} From c0bebe4f4d53e0ee20fba1967dcafe62933b439c Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Wed, 15 Nov 2023 16:29:39 +0200 Subject: [PATCH 20/44] [GLJS-578] Don't reset shared style changes when creating fragments (internal-946) * Don't reset shared style changes when creating fragments * Add unit test --- src/style/style.js | 22 +++++++++++++--------- test/unit/style/style_imports.test.js | 11 ++++++++--- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/style/style.js b/src/style/style.js index d10163196bd..38ce09f5d92 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -326,8 +326,6 @@ class Style extends Evented { this.options = options.config || new Map(); this._configDependentLayers = new Set(); - this._changes.reset(); - this.dispatcher.broadcast('setReferrer', getReferrer()); const self = this; @@ -501,9 +499,10 @@ class Style extends Evented { options: this.options }); - this._shouldPrecompile = this.importDepth === 0; + const isRootStyle = this.isRootStyle(); + this._shouldPrecompile = isRootStyle; this.fire(new Event('data', {dataType: 'style'})); - this.fire(new Event(this.importDepth === 0 ? 'style.load' : 'style.import.load')); + this.fire(new Event(isRootStyle ? 'style.load' : 'style.import.load')); } _load(json: StyleSpecification, validate: boolean) { @@ -511,7 +510,7 @@ class Style extends Evented { // This style was loaded as a root style, but it is marked as a fragment and/or has a schema. We instead load // it as an import with the well-known ID "basemap" to make sure that we don't expose the internals. - if (this.importDepth === 0 && (json.fragment || (schema && json.fragment !== false))) { + if (this.isRootStyle() && (json.fragment || (schema && json.fragment !== false))) { const basemap = {id: 'basemap', data: json, url: ''}; const style = extend({}, empty, {imports: [basemap]}); this._load(style, validate); @@ -626,11 +625,16 @@ class Style extends Evented { options: this.options }); - this._shouldPrecompile = this.importDepth === 0; - this.fire(new Event(this.importDepth === 0 ? 'style.load' : 'style.import.load')); + const isRootStyle = this.isRootStyle(); + this._shouldPrecompile = isRootStyle; + this.fire(new Event(isRootStyle ? 'style.load' : 'style.import.load')); } } + isRootStyle(): boolean { + return this.importDepth === 0; + } + mergeAll() { let light; let ambientLight; @@ -848,7 +852,7 @@ class Style extends Evented { _updateMapProjection() { // Skip projection updates from the children fragments - if (this.importDepth > 0) return; + if (!this.isRootStyle()) return; if (!this.map._useExplicitProjection) { // Update the visible projection if map's is null this.map._prioritizeAndUpdateProjection(null, this.projection); @@ -2479,7 +2483,7 @@ class Style extends Evented { delete this.directionalLight; // Shared managers should be removed only on removing the root style - if (this.importDepth === 0) { + if (this.isRootStyle()) { this.imageManager.setEventedParent(null); this.modelManager.setEventedParent(null); this.dispatcher.remove(); diff --git a/test/unit/style/style_imports.test.js b/test/unit/style/style_imports.test.js index 8fa9dfdb85b..fac7bc85c68 100644 --- a/test/unit/style/style_imports.test.js +++ b/test/unit/style/style_imports.test.js @@ -1891,7 +1891,8 @@ test('Style#setState', (t) => { const style = new Style(new StubMap()); const initialStyle = createStyleJSON({ - imports: [{id: 'a', url: '/style.json'}] + imports: [{id: 'a', url: '/style.json'}], + layers: [{id: 'b', type: 'background', paint: {'background-color': 'red'}}] }); window.server.respondWith('/style.json', JSON.stringify(createStyleJSON())); @@ -1900,17 +1901,21 @@ test('Style#setState', (t) => { await new Promise((resolve) => style.on('style.load', resolve)); const nextStyle = createStyleJSON({ - imports: [{id: 'a', url: '/styles/streets-v12.json'}] + imports: [{id: 'a', url: '/styles/streets-v12.json'}], + layers: [{id: 'b', type: 'background', paint: {'background-color': 'pink'}}] }); const data = createStyleJSON({layers: [{id: 'a', type: 'background'}]}); window.server.respondWith('/styles/streets-v12.json', JSON.stringify(data)); style.setState(nextStyle); + t.equal(style._changes.updatedPaintProps.has('b'), true, 'Keeps previous changes intact'); + await new Promise((resolve) => style.on('style.load', resolve)); t.deepEqual(style.serialize(), createStyleJSON({ - imports: [{id: 'a', url: '/styles/streets-v12.json', data}] + imports: [{id: 'a', url: '/styles/streets-v12.json', data}], + layers: [{id: 'b', type: 'background', paint: {'background-color': 'pink'}}] })); t.end(); From 38432e7b6a80cb3fb198f7f278a8c9500f032a90 Mon Sep 17 00:00:00 2001 From: Fouad Valadbeigi <67509069+akoylasar@users.noreply.github.com> Date: Wed, 15 Nov 2023 16:07:24 +0100 Subject: [PATCH 21/44] [MAPS3D-1044] Fix breakage of ground effect that occurs when edges cross tile boundaries (internal-943) * [MAPS3D-1044] Fix breakage of ground effect that occurs when edges cross tile boundaries * render test --- src/data/bucket/fill_extrusion_bucket.js | 6 +- .../flood-light/MAPS3D-1044/expected.png | Bin 0 -> 6153 bytes .../flood-light/MAPS3D-1044/style.json | 108 ++++++++++++++++++ 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 test/integration/render-tests/lighting-3d-mode/fill-extrusion/flood-light/MAPS3D-1044/expected.png create mode 100644 test/integration/render-tests/lighting-3d-mode/fill-extrusion/flood-light/MAPS3D-1044/style.json diff --git a/src/data/bucket/fill_extrusion_bucket.js b/src/data/bucket/fill_extrusion_bucket.js index addf23ea02a..0200361356a 100644 --- a/src/data/bucket/fill_extrusion_bucket.js +++ b/src/data/bucket/fill_extrusion_bucket.js @@ -372,7 +372,7 @@ export class GroundEffect { hasData(): boolean { return this.vertexArray.length !== 0; } - addData(polyline: Array, bounds: [Point, Point], maxRadius: number) { + addData(polyline: Array, bounds: [Point, Point], maxRadius: number, roundedEdges: boolean = false) { const n = polyline.length; if (n > 2) { let sid = Math.max(0, this._segments.get().length - 1); @@ -410,7 +410,7 @@ export class GroundEffect { const a1 = factor; if (isEdgeOutsideBounds(pa, pb, bounds) || - (pointOutsideBounds(pa, bounds) && pointOutsideBounds(pb, bounds))) { + (roundedEdges && pointOutsideBounds(pa, bounds) && pointOutsideBounds(pb, bounds))) { prevFactor = factor; continue; } @@ -1091,7 +1091,7 @@ class FillExtrusionBucket implements Bucket { if (groundPolyline.length !== 0 && isDuplicate(groundPolyline, groundPolyline[0])) { groundPolyline.pop(); } - this.groundEffect.addData(groundPolyline, bounds, maxRadius); + this.groundEffect.addData(groundPolyline, bounds, maxRadius, edgeRadius > 0); } } this.footprintSegments.push(fpSegment); diff --git a/test/integration/render-tests/lighting-3d-mode/fill-extrusion/flood-light/MAPS3D-1044/expected.png b/test/integration/render-tests/lighting-3d-mode/fill-extrusion/flood-light/MAPS3D-1044/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..cb8da989ca4ea8a9d32b3d9158ac2298e7c05a63 GIT binary patch literal 6153 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 Date: Wed, 15 Nov 2023 17:51:06 +0200 Subject: [PATCH 22/44] [GLJS-579] Fix adding first import (internal-947) * Fix adding first import * simplify import management code in style.js (internal-948) --------- Co-authored-by: Volodymyr Agafonkin --- src/style/style.js | 30 +++++++-------------------- test/unit/style/style_imports.test.js | 20 ++++++++++++++++++ 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/style/style.js b/src/style/style.js index 38ce09f5d92..b056792d2ed 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -2618,21 +2618,14 @@ class Style extends Evented { addImport(importSpec: ImportSpecification): Style { this._checkLoaded(); - const imports = this.stylesheet.imports; - if (!imports) return this; + const imports = this.stylesheet.imports = this.stylesheet.imports || []; const index = imports.findIndex(({id}) => id === importSpec.id); if (index !== -1) { - this.fire(new ErrorEvent(new Error(`Import with id '${importSpec.id}' already exists in the map's style.`))); - return this; - } - - if (this.stylesheet.imports) { - this.stylesheet.imports.push(importSpec); - } else { - this.stylesheet.imports = [importSpec]; + return this.fire(new ErrorEvent(new Error(`Import with id '${importSpec.id}' already exists in the map's style.`))); } + imports.push(importSpec); this._loadImports([importSpec], true); return this; } @@ -2640,9 +2633,7 @@ class Style extends Evented { setImportUrl(importId: string, url: string): Style { this._checkLoaded(); - const imports = this.stylesheet.imports; - if (!imports) return this; - + const imports = this.stylesheet.imports || []; const index = this.getImportIndex(importId); if (index === -1) return this; @@ -2663,10 +2654,8 @@ class Style extends Evented { setImportData(importId: string, stylesheet: ?StyleSpecification): Style { this._checkLoaded(); - const imports = this.stylesheet.imports; - if (!imports) return this; - const index = this.getImportIndex(importId); + const imports = this.stylesheet.imports || []; if (index === -1) return this; // Reload import from the URL if import data is unset @@ -2686,9 +2675,7 @@ class Style extends Evented { removeImport(importId: string): Style { this._checkLoaded(); - const imports = this.stylesheet.imports; - if (!imports) return this; - + const imports = this.stylesheet.imports || []; const index = this.getImportIndex(importId); if (index === -1) return this; @@ -2704,14 +2691,11 @@ class Style extends Evented { } getImportIndex(importId: string): number { - const imports = this.stylesheet.imports; - if (!imports) return -1; - + const imports = this.stylesheet.imports || []; const index = imports.findIndex((importSpec) => importSpec.id === importId); if (index === -1) { this.fire(new ErrorEvent(new Error(`Import '${importId}' does not exist in the map's style and cannot be updated.`))); } - return index; } diff --git a/test/unit/style/style_imports.test.js b/test/unit/style/style_imports.test.js index fac7bc85c68..f2058c470ee 100644 --- a/test/unit/style/style_imports.test.js +++ b/test/unit/style/style_imports.test.js @@ -1804,6 +1804,26 @@ test('Style#setState', (t) => { t.test('Adds fragment', async (t) => { const style = new Style(new StubMap()); + const initialStyle = createStyleJSON(); + style.loadJSON(initialStyle); + + await new Promise((resolve) => style.on('style.load', resolve)); + + const nextStyle = createStyleJSON({ + imports: [{id: 'a', url: '', data: createStyleJSON()}] + }); + + style.setState(nextStyle); + await new Promise((resolve) => style.on('style.load', resolve)); + + t.deepEqual(style.serialize(), nextStyle); + + t.end(); + }); + + t.test('Adds fragment to the existing fragments', async (t) => { + const style = new Style(new StubMap()); + const initialStyle = createStyleJSON({ imports: [{id: 'a', url: '', data: createStyleJSON()}] }); From ed3be18e5ec98eb3b376ab1860fa9b8d89368b63 Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Wed, 15 Nov 2023 18:13:55 +0200 Subject: [PATCH 23/44] [GLJS-580] Ignore layer slot in style diffs (internal-949) * Ignore layer slot in style diffs * Add unit test --- src/style-spec/diff.js | 4 +-- test/unit/style-spec/diff.test.js | 53 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/style-spec/diff.js b/src/style-spec/diff.js index 42bf793dfb5..3adcae5f27f 100644 --- a/src/style-spec/diff.js +++ b/src/style-spec/diff.js @@ -335,7 +335,7 @@ function diffLayers(before: Array, after: Array, after: Array { }] }], 'updates import data'); + t.deepEqual(diffStyles({ + 'layers': [{ + 'id': 'national-park', + 'type': 'fill', + 'slot': 'below-water', + 'paint': {'fill-color': 'green'} + }], + 'imports': [{ + 'id': 'basemap', + 'url': '', + 'data': { + 'version': 8, + 'layers': [ + {'id': 'below-water', 'type': 'slot'}, + {'id': 'water', 'type': 'fill', 'layout': {}, 'paint': {'fill-color': 'blue'}}, + {'id': 'above-water', 'type': 'slot'} + ] + } + }] + }, { + 'layers': [{ + 'id': 'national-park', + 'type': 'fill', + 'slot': 'above-water', + 'paint': {'fill-color': 'violet'} + }], + 'imports': [{ + 'id': 'basemap', + 'url': '', + 'data': { + 'version': 8, + 'layers': [ + {'id': 'below-water', 'type': 'slot'}, + {'id': 'water', 'type': 'background', 'layout': {}, 'paint': {'background-color': 'pink'}}, + {'id': 'above-water', 'type': 'slot'} + ] + }, + }] + }), [{ + command: 'setPaintProperty', + args: ['national-park', 'fill-color', 'violet', null] + }, { + command: 'setImportData', + args: ['basemap', { + 'version': 8, + 'layers': [ + {'id': 'below-water', 'type': 'slot'}, + {'id': 'water', 'type': 'background', 'layout': {}, 'paint': {'background-color': 'pink'}}, + {'id': 'above-water', 'type': 'slot'} + ] + }] + }], 'updates import data and ignores slots'); + t.end(); }); From 1380a736ac0351c82bca012c6459d1b75456a96d Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Wed, 15 Nov 2023 18:15:34 +0200 Subject: [PATCH 24/44] v3.0.0-rc.3 (internal-951) --- package.json | 2 +- src/style-spec/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index cdb4eed2731..c67d3ef45f7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mapbox-gl", "description": "A WebGL interactive maps library", - "version": "3.0.0-rc.2", + "version": "3.0.0-rc.3", "main": "dist/mapbox-gl.js", "style": "dist/mapbox-gl.css", "license": "SEE LICENSE IN LICENSE.txt", diff --git a/src/style-spec/package.json b/src/style-spec/package.json index 3c1f70e969b..530ea2f0693 100644 --- a/src/style-spec/package.json +++ b/src/style-spec/package.json @@ -1,7 +1,7 @@ { "name": "@mapbox/mapbox-gl-style-spec", "description": "a specification for mapbox gl styles", - "version": "14.0.0-rc.2", + "version": "14.0.0-rc.3", "author": "Mapbox", "keywords": [ "mapbox", From 04a8ce3fc64f1607b90a36e610d721a0a33cb3d7 Mon Sep 17 00:00:00 2001 From: Fouad Valadbeigi <67509069+akoylasar@users.noreply.github.com> Date: Thu, 16 Nov 2023 19:57:48 +0100 Subject: [PATCH 25/44] [GLJS-584] Fix missing condition for circle layer depth occlusion (internal-953) * [GLJS-584] fix missing condition for circle layer depth occlusion * add render test --- src/geo/transform.js | 3 + src/render/draw_circle.js | 2 +- src/render/draw_symbol.js | 2 +- .../render-tests/GLJS-584/expected.png | Bin 0 -> 3975 bytes .../render-tests/GLJS-584/style.json | 60 ++++++++++++++++++ 5 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 test/integration/render-tests/GLJS-584/expected.png create mode 100644 test/integration/render-tests/GLJS-584/style.json diff --git a/src/geo/transform.js b/src/geo/transform.js index 6ea9d5739cf..23cb0b707c2 100644 --- a/src/geo/transform.js +++ b/src/geo/transform.js @@ -271,6 +271,9 @@ class Transform { this._updateCameraOnTerrain(); this._calcMatrices(); } + get depthOcclusionForSymbolsAndCircles(): boolean { + return this.projection.name !== 'globe' && !this.isOrthographic; + } updateElevation(constrainCameraOverTerrain: boolean, adaptCameraAltitude: boolean = false) { const centerAltitudeChanged = this._elevation && this._elevation.exaggeration() !== this._centerAltitudeValidForExaggeration; diff --git a/src/render/draw_circle.js b/src/render/draw_circle.js index cc5f1c3079a..3c0d6740113 100644 --- a/src/render/draw_circle.js +++ b/src/render/draw_circle.js @@ -119,7 +119,7 @@ function drawCircles(painter: Painter, sourceCache: SourceCache, layer: CircleSt segmentsRenderStates.sort((a, b) => a.sortKey - b.sortKey); } - const terrainOptions = {useDepthForOcclusion: !isGlobeProjection}; + const terrainOptions = {useDepthForOcclusion: tr.depthOcclusionForSymbolsAndCircles}; for (const segmentsState of segmentsRenderStates) { const {programConfiguration, program, layoutVertexBuffer, globeExtVertexBuffer, indexBuffer, uniformValues, tile} = segmentsState.state; diff --git a/src/render/draw_symbol.js b/src/render/draw_symbol.js index 797316b9a94..95dd82ee081 100644 --- a/src/render/draw_symbol.js +++ b/src/render/draw_symbol.js @@ -457,7 +457,7 @@ function drawLayerSymbols(painter: Painter, sourceCache: SourceCache, layer: Sym const state = segmentState.state; if (painter.terrain) { const options = { - useDepthForOcclusion: !isGlobeProjection && !tr.isOrthographic, + useDepthForOcclusion: tr.depthOcclusionForSymbolsAndCircles, labelPlaneMatrixInv: state.labelPlaneMatrixInv }; painter.terrain.setupElevationDraw(state.tile, state.program, options); diff --git a/test/integration/render-tests/GLJS-584/expected.png b/test/integration/render-tests/GLJS-584/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..415c5a734522cd2107d61b9ca7751291ae0dd7e2 GIT binary patch literal 3975 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 literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/GLJS-584/style.json b/test/integration/render-tests/GLJS-584/style.json new file mode 100644 index 00000000000..c3d71d8cb38 --- /dev/null +++ b/test/integration/render-tests/GLJS-584/style.json @@ -0,0 +1,60 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 256, + "width": 256, + "operations": [ + ["setCenter", [24.9, 60.2]], + ["wait"], + ["setStyle", { + "version": 8, + "center": [24.9, 60.2], + "projection": {"name": "globe"}, + "camera": {"camera-projection": "orthographic"}, + "zoom": 5.99, + "pitch": 0, + "terrain": { + "source": "rgbterrain" + }, + "sources": { + "rgbterrain": { + "type": "raster-dem", + "tiles": [ + "local://tiles/{z}-{x}-{y}.terrain.png" + ], + "maxzoom": 12, + "tileSize": 256 + }, + "point": { + "type": "geojson", + "data": {"type": "Point", "coordinates": [24.9, 60.2]} + } + }, + "layers": [{ + "id": "circles", + "type": "circle", + "source": "point", + "paint": { + "circle-radius": 50, + "circle-color": "green" + } + }] + }], + ["wait"], + ["setZoom", 6.01], + ["wait"] + ] + } + }, + "center": [24.9, 60.2], + "zoom": 5.9, + "projection": { + "name": "globe" + }, + "camera": {"camera-projection": "orthographic"}, + "sources": { + }, + "layers": [ + ] +} \ No newline at end of file From 2a0e16d3ee2b51ce00ec00507ee3e13311a1e342 Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Mon, 20 Nov 2023 12:13:31 +0200 Subject: [PATCH 26/44] [GLJS-493] Diff configs on setStyle (internal-952) * Diff configs on setStyle * Add render test --- src/style-spec/diff.js | 11 +- src/style/style.js | 99 ++++++++---- .../imports/set-style-config/expected.png | Bin 0 -> 1572 bytes .../imports/set-style-config/style.json | 145 ++++++++++++++++++ 4 files changed, 222 insertions(+), 33 deletions(-) create mode 100644 test/integration/render-tests/imports/set-style-config/expected.png create mode 100644 test/integration/render-tests/imports/set-style-config/style.json diff --git a/src/style-spec/diff.js b/src/style-spec/diff.js index 3adcae5f27f..b9335aa7a8a 100644 --- a/src/style-spec/diff.js +++ b/src/style-spec/diff.js @@ -151,7 +151,12 @@ export const operations: {[_: string]: string} = { /* * { command: 'setImportData', args: [importId, stylesheet] } */ - setImportData: 'setImportData' + setImportData: 'setImportData', + + /* + * { command: 'setImportConfig', args: [importId, config] } + */ + setImportConfig: 'setImportConfig' }; function addSource(sourceId: string, after: Sources, commands: Array) { @@ -411,6 +416,10 @@ export function diffImports(before: Array = [], after: Arra const beforeImport = beforeIndex[afterImport.id]; if (!beforeImport || isEqual(beforeImport, afterImport)) continue; + if (!isEqual(beforeImport.config, afterImport.config)) { + commands.push({command: operations.setImportConfig, args: [afterImport.id, afterImport.config]}); + } + if (!isEqual(beforeImport.url, afterImport.url)) { commands.push({command: operations.setImportUrl, args: [afterImport.id, afterImport.url]}); } diff --git a/src/style/style.js b/src/style/style.js index b056792d2ed..c1e9be9a7cb 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -92,6 +92,7 @@ import type { TransitionSpecification, PropertyValueSpecification, ConfigSpecification, + SchemaSpecification, CameraSpecification } from '../style-spec/types.js'; import type {CustomLayerInterface} from './style_layer/custom_style_layer.js'; @@ -129,6 +130,7 @@ const supportedDiffOperations = pick(diffOperations, [ 'removeImport', 'setImportUrl', 'setImportData', + 'setImportConfig', // 'setGlyphs', // 'setSprite', ]); @@ -157,7 +159,6 @@ export type StyleOptions = { importDepth?: number, importsCache?: Map, resolvedImports?: Set, - config?: Map, }; export type StyleSetterOptions = { @@ -323,7 +324,7 @@ class Style extends Evented { this._order = []; this._markersNeedUpdate = false; - this.options = options.config || new Map(); + this.options = new Map(); this._configDependentLayers = new Set(); this.dispatcher.broadcast('setReferrer', getReferrer()); @@ -457,20 +458,8 @@ class Style extends Evented { _createFragmentStyle(importSpec: ImportSpecification): Style { const scope = this.scope ? makeFQID(importSpec.id, this.scope) : importSpec.id; - const config = new Map(); - const importConfig = importSpec.config; - if (importConfig) { - for (const key of Object.keys(importConfig)) { - const expressionParsed = createExpression(importConfig[key]); - if (expressionParsed.result === 'success') { - config.set(key, expressionParsed.value.expression); - } - } - } - const style = new Style(this.map, { scope, - config, styleChanges: this._changes, importDepth: this.importDepth + 1, importsCache: this.importsCache, @@ -486,6 +475,8 @@ class Style extends Evented { // Bubble all events fired by the style to the map. style.setEventedParent(this.map, {style}); + style.setConfig(importSpec.config); + return style; } @@ -517,19 +508,7 @@ class Style extends Evented { return; } - if (schema) { - for (const id of Object.keys(schema)) { - // already set by config - if (this.options.has(id)) continue; - - const expression = schema[id].default; - - const expressionParsed = createExpression(expression); - if (expressionParsed.result === 'success') { - this.options.set(id, expressionParsed.value.expression); - } - } - } + this.updateSchema(schema); if (validate && emitValidationErrors(this, validateStyle(json))) { return; @@ -1559,8 +1538,40 @@ class Style extends Evented { const fragmentStyle = this.getFragmentStyle(fragmentId); fragmentStyle.options.set(key, expression); + fragmentStyle.updateConfigDependencies(); + } + + setConfig(config: ?ConfigSpecification, schema: ?SchemaSpecification) { + this.options.clear(); + if (!config) return; + + for (const key in config) { + const expressionParsed = createExpression(config[key]); + if (expressionParsed.result === 'success') { + this.options.set(key, expressionParsed.value.expression); + } + } - for (const id of fragmentStyle._configDependentLayers) { + this.updateSchema(schema); + } + + updateSchema(schema: ?SchemaSpecification) { + if (!schema) return; + + for (const id in schema) { + // already set by config + if (this.options.has(id)) continue; + + const expression = schema[id].default; + const expressionParsed = createExpression(expression); + if (expressionParsed.result === 'success') { + this.options.set(id, expressionParsed.value.expression); + } + } + } + + updateConfigDependencies() { + for (const id of this._configDependentLayers) { const layer = this.getLayer(id); if (layer) { layer.possiblyEvaluateVisibility(); @@ -1570,12 +1581,12 @@ class Style extends Evented { // If the root style uses the lights from the updated fragment, // update the configs in the corresponding light instances. - if (this.ambientLight && this.ambientLight.scope === fragmentId) { - this.ambientLight.updateConfig(fragmentStyle.options); + if (this.ambientLight && this.ambientLight.scope === this.scope) { + this.ambientLight.updateConfig(this.options); } - if (this.directionalLight && this.directionalLight.scope === fragmentId) { - this.directionalLight.updateConfig(fragmentStyle.options); + if (this.directionalLight && this.directionalLight.scope === this.scope) { + this.directionalLight.updateConfig(this.options); } this._changes.changed = true; @@ -2672,6 +2683,30 @@ class Style extends Evented { return this; } + setImportConfig(importId: string, config: ?ConfigSpecification): Style { + this._checkLoaded(); + + const index = this.getImportIndex(importId); + const imports = this.stylesheet.imports || []; + if (index === -1) return this; + + if (config) { + imports[index].config = config; + } else { + delete imports[index].config; + } + + // Update related fragment + const fragment = this.fragments[index]; + const schema = fragment.style.stylesheet && fragment.style.stylesheet.schema; + + fragment.config = config; + fragment.style.setConfig(config, schema); + fragment.style.updateConfigDependencies(); + + return this; + } + removeImport(importId: string): Style { this._checkLoaded(); diff --git a/test/integration/render-tests/imports/set-style-config/expected.png b/test/integration/render-tests/imports/set-style-config/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..0ca8fc24914b1d7c66a65852b5157c49279d554f GIT binary patch literal 1572 zcmV+<2HW|GP)Px)-bqA3RCr$PTU$(2R}@`5OL>Tul2i~1Q63EiqzMF4G!c9uAjnG-HNpq*10*EG zm0(7 zd@ytFxqGj(_FCthd&gv{v1J2B+)N|@BnC(&AZdURYak>Mlw3e^0m%g<7m!@Qh!K!% zQ13QqZU)$V4p>_O78dWhx4H@}{sSy50spS)m5=&i0441572vQNFg5vjg6kW=c4;iH06+f@%*<dJ2gU3qs#Oh(Ekn~ zc8J`>2Y~2)(0ga9Ynlip-~CqfvJe*lqP(A{rm1PxzxNkWOF}#VXy(8BMzdtJ?cMp4 zXiN|X0IKmi=tsmlry#vgxGxA(UFgae?Dy5BB*5%fSf z072hs@AE?EkJbr4^a&b87yxPt-}?GGy1KexV`BqvZ*M^XD_ty55QPEY?G8A8v139l zEiFh+PDW^GC~9kKckHiGhl#&|x4&t0$>z?&0Qf#YE#-gt@+D54I)#LU1n~hNM`-CX z006YB3-SNxjn~%JU}k29SFc_%0P*qhN&rwFYSd5b_PR&wU8MF5*hc*~Ha771@nbxC z^a!)Fv+(otLqI?P($mus8yl+tpuWBy&CSgi92^8)d7R+jU{2QD+#DSp9X#v){{D*36gMB1 z0V6WQ128Z!fFnnaz{|@Ejg5`4va;f!Uc7jL=;-JzB1qOaJUooi(NShZLqh{@-MWSH z@^W0fcoA`NaUl1gaj1lfii-G;*pZr=x;0sY0HC4-DBy^Q2%J255)U3cfQiZb)1Thn zUWA2(F$lyA`v3a%>*(p};o07`YZn$47SP(-ipt7L)&gPzu_8M=8#y^SIC0_xMn*GvE6oyV0nNBfCSx$#+S(X^s;VkPMn;0zl9iPOUteFmdGm%DLz+NLAO=wD z?(Rl-csMLAE#d0w%6edLZ@)EvgDr$83>8aZ^6$L7JhnXx3kx_oIWZ6qA3lVen;Xw4 z3Es79*Dy0P18Zw*_I{#!Nl6LU{Q2|eIZ0Y1QMo*Q`V?2LT;Xe!nn9Ps+iZqN;mPI} z7Z+Ku_UzdMDi2!iIXF0Q4wI9UENbKwR4Vix^&vpyvrGVxMv#k8xsU}aHG^)3C>kAY zhiM^2Gk@X21!f1W5#*w(0KwqfVTwct2O{M6w0))p6_pWbN=QhEsOm2tO#7AK=5Rzl z61#EZ25SiEM@mWxyM|KxWRAorG94U>l9iJBt8Aa#p(sVE=IM~?j5PdIpzv+Jn>ZyP zA5jN-pahM8Ri7%D#RWjl1fzX4CWqLlAC7M3nbAHT|9`O=?F$mBRL3Y^kVu18}0qOSw;=g?;rb_8}EJ*|<5tKww5<#OD!T$hN Wl0B|;tJP=#0000X3?nt literal 0 HcmV?d00001 diff --git a/test/integration/render-tests/imports/set-style-config/style.json b/test/integration/render-tests/imports/set-style-config/style.json new file mode 100644 index 00000000000..9f26380bc37 --- /dev/null +++ b/test/integration/render-tests/imports/set-style-config/style.json @@ -0,0 +1,145 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 64, + "width": 64, + "operations": [ + ["setStyle", { + "version": 8, + "center": [0, 0], + "zoom": 0, + "transition": {"duration": 0}, + "sources": {}, + "layers": [], + "imports": [ + { + "id": "basemap", + "url": "", + "config": { + "lightPreset": "day" + }, + "data": { + "version": 8, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "lights": [{ + "type": "ambient", + "id": "ambient", + "properties": { + "color": ["match", ["config", "lightPreset"], "day", "hsl(0, 0%, 100%)", "night", "hsl(217, 100%, 11%)", "hsl(0, 0%, 100%)"], + "intensity": 0.5 + } + }, { + "type": "directional", + "id": "directional", + "properties": { + "color": ["match", ["config","lightPreset"], "day", "hsl(0, 0%, 100%)", "night", "hsl(0, 0%, 29%)", "hsl(0, 0%, 100%)"], + "intensity": 0.5 + } + }], + "sources": { + "point": {"type": "geojson", "data": {"type": "Point", "coordinates": [0, 0]}} + }, + "layers": [{ + "id": "background", + "type": "background", + "paint": { + "background-color": "pink" + } + }, { + "id": "circle", + "type": "circle", + "source": "point", + "paint": { + "circle-radius": 18, + "circle-color": ["match", ["config", "lightPreset"], "day", "white", "night", "black", "red"] + } + }, { + "id": "text", + "type": "symbol", + "source": "point", + "layout": { + "text-field": ["config", "lightPreset"], + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ] + }, + "paint": { + "text-color": ["match", ["config", "lightPreset"], "day", "black", "night", "white", "red"] + } + }] + } + } + ] + }], + ["wait"] + ] + } + }, + "center": [0, 0], + "zoom": 0, + "transition": {"duration": 0}, + "sources": {}, + "layers": [], + "imports": [ + { + "id": "basemap", + "url": "", + "config": { + "lightPreset": "night" + }, + "data": { + "version": 8, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "lights": [{ + "type": "ambient", + "id": "ambient", + "properties": { + "color": ["match", ["config", "lightPreset"], "day", "hsl(0, 0%, 100%)", "night", "hsl(217, 100%, 11%)", "hsl(0, 0%, 100%)"], + "intensity": 0.5 + } + }, { + "type": "directional", + "id": "directional", + "properties": { + "color": ["match", ["config","lightPreset"], "day", "hsl(0, 0%, 100%)", "night", "hsl(0, 0%, 29%)", "hsl(0, 0%, 100%)"], + "intensity": 0.5 + } + }], + "sources": { + "point": {"type": "geojson", "data": {"type": "Point", "coordinates": [0, 0]}} + }, + "layers": [{ + "id": "background", + "type": "background", + "paint": { + "background-color": "violet" + } + }, { + "id": "circle", + "type": "circle", + "source": "point", + "paint": { + "circle-radius": 18, + "circle-color": ["match", ["config", "lightPreset"], "day", "white", "night", "black", "red"] + } + }, { + "id": "text", + "type": "symbol", + "source": "point", + "layout": { + "text-field": ["config", "lightPreset"], + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ] + }, + "paint": { + "text-color": ["match", ["config", "lightPreset"], "day", "black", "night", "white", "red"] + } + }] + } + } + ] +} From c51898bb506613176b665de311be043196ccf607 Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Mon, 20 Nov 2023 14:33:07 +0200 Subject: [PATCH 27/44] [GLJS-587] Fix Style.mergeAll when removing all imports (internal-954) * Fix Style.mergeAll when removing all imports * Ignore legacy light since it's always set * Add scope check for terrain in mergeAll * Add layers check to the unit test * Cleanup Style._remove() * Check for terrain or globe on Style.mergeAll() --- src/style/style.js | 42 ++++++++++++++------------- test/unit/style/style_imports.test.js | 38 ++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/style/style.js b/src/style/style.js index c1e9be9a7cb..1712661f2e0 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -625,16 +625,21 @@ class Style extends Evented { let camera; this.forEachFragmentStyle((style: Style) => { + if (!style.stylesheet) return; + if (style.light != null) light = style.light; - if (style.ambientLight != null) - ambientLight = style.ambientLight; + if (style.stylesheet.lights) { + if (style.ambientLight != null) + ambientLight = style.ambientLight; - if (style.directionalLight != null) - directionalLight = style.directionalLight; + if (style.directionalLight != null) + directionalLight = style.directionalLight; + } - if (style.terrain != null) { + const isGlobe = style.stylesheet.projection && style.stylesheet.projection.name === 'globe'; + if ((style.stylesheet.terrain || isGlobe) && style.terrain != null) { const nextIsElevated = style.terrain.drapeRenderMode === DrapeRenderMode.elevated; const prevIsDeffered = terrain && terrain.drapeRenderMode === DrapeRenderMode.deferred; if (!terrain || prevIsDeffered || nextIsElevated) { @@ -642,11 +647,9 @@ class Style extends Evented { } } - if (style.fog != null) + if (style.stylesheet.fog && style.fog != null) fog = style.fog; - if (!style.stylesheet) return; - if (style.stylesheet.camera != null) camera = style.stylesheet.camera; @@ -2464,10 +2467,6 @@ class Style extends Evented { } _remove() { - for (const {style} of this.fragments) { - style._remove(); - } - if (this._request) { this._request.cancel(); this._request = null; @@ -2476,20 +2475,23 @@ class Style extends Evented { this._spriteRequest.cancel(); this._spriteRequest = null; } - rtlTextPluginEvented.off('pluginStateChange', this._rtlTextPluginCallback); - this.mergeLayers(); - this.mergeSources(); + rtlTextPluginEvented.off('pluginStateChange', this._rtlTextPluginCallback); - for (const layerId in this._layers) { - const layer = this._layers[layerId]; + for (const layerId in this._mergedLayers) { + const layer = this._mergedLayers[layerId]; layer.setEventedParent(null); } - for (const id in this._sourceCaches) { - this._sourceCaches[id].clearTiles(); - this._sourceCaches[id].setEventedParent(null); + + for (const id in this._mergedSourceCaches) { + this._mergedSourceCaches[id].clearTiles(); + this._mergedSourceCaches[id].setEventedParent(null); } + this.setEventedParent(null); + + delete this.fog; + delete this.terrain; delete this.ambientLight; delete this.directionalLight; diff --git a/test/unit/style/style_imports.test.js b/test/unit/style/style_imports.test.js index f2058c470ee..767d2844c1d 100644 --- a/test/unit/style/style_imports.test.js +++ b/test/unit/style/style_imports.test.js @@ -1885,6 +1885,44 @@ test('Style#setState', (t) => { t.end(); }); + t.test('Removes all fragments', async (t) => { + const style = new Style(new StubMap()); + + const fragmentStyle = createStyleJSON({ + lights: [ + {id: 'sun', type: 'directional', properties: {intensity: 0.5}}, + {id: 'environment', type: 'ambient', properties: {intensity: 0.5}} + ], + fog: {range: [1, 2], color: 'white'}, + terrain: {source: 'mapbox-dem', exaggeration: 1.5}, + layers: [{id: 'land', type: 'background'}], + sources: {'mapbox-dem': {type: 'raster-dem', tiles: ['http://example.com/{z}/{x}/{y}.png']}}, + }); + + const initialStyle = createStyleJSON({imports: [{id: 'a', url: '', data: fragmentStyle}]}); + + style.loadJSON(initialStyle); + await new Promise((resolve) => style.on('style.load', resolve)); + + const nextStyle = createStyleJSON({ + sources: {mapbox: {type: 'vector', tiles: []}}, + layers: [{id: 'land', type: 'background'}] + }); + + style.setState(nextStyle); + t.deepEqual(style.serialize(), nextStyle); + + t.deepEqual(style.order, ['land']); + t.deepEqual(style.getSources().map((s) => s.id), ['mapbox']); + + t.notOk(style.ambientLight); + t.notOk(style.directionalLight); + t.notOk(style.fog); + t.notOk(style.terrain); + + t.end(); + }); + t.test('Moves fragment', async (t) => { const style = new Style(new StubMap()); From 20182c9e02b3522bf6fbcff1340f2723b81cc0b3 Mon Sep 17 00:00:00 2001 From: Volodymyr Agafonkin Date: Mon, 20 Nov 2023 14:44:50 +0200 Subject: [PATCH 28/44] v3.0.0-rc.4 (internal-955) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c67d3ef45f7..170e8211ba4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mapbox-gl", "description": "A WebGL interactive maps library", - "version": "3.0.0-rc.3", + "version": "3.0.0-rc.4", "main": "dist/mapbox-gl.js", "style": "dist/mapbox-gl.css", "license": "SEE LICENSE IN LICENSE.txt", From 60512ff0c5f514cc48130095fba839738d47424c Mon Sep 17 00:00:00 2001 From: Stepan Kuzmin Date: Tue, 21 Nov 2023 17:26:32 +0200 Subject: [PATCH 29/44] Update terrain tile fixtures (internal-961) --- .../globe/globe-terrain/expected.png | Bin 128596 -> 148317 bytes .../imports/globe-terrain/expected.png | Bin 128596 -> 148317 bytes .../imports/set-style/expected.png | Bin 201 -> 202 bytes .../render-tests/imports/set-style/style.json | 3 ++- test/integration/tiles/3-5-2.terrain.png | Bin 0 -> 102763 bytes test/integration/tiles/3-6-2.terrain.png | Bin 0 -> 158811 bytes test/integration/tiles/4-10-6.terrain.png | Bin 106552 -> 129712 bytes test/integration/tiles/4-11-6.terrain.png | Bin 126327 -> 172907 bytes 8 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 test/integration/tiles/3-5-2.terrain.png create mode 100644 test/integration/tiles/3-6-2.terrain.png diff --git a/test/integration/render-tests/globe/globe-terrain/expected.png b/test/integration/render-tests/globe/globe-terrain/expected.png index af7a246626b55232feeac3634d3849c73689eef1..c6fadbc05e379848f1642a7fc9657db5af934ff2 100644 GIT binary patch literal 148317 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