diff --git a/.circleci/config.yml b/.circleci/config.yml index cd70928f15c..b0128f7e806 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -258,7 +258,7 @@ jobs: - run: npm run build-style-spec - run: npm run build-flow-types - run: npm run test-build - - run: while read l; do cp debug/$l test/release/$l; done < test/release/local_release_page_list.txt + - run: npm run prepare-release-pages - run: name: Create mapbox-gl.tar.gz command: | @@ -279,11 +279,21 @@ jobs: steps: - attach_workspace: at: ~/ + - restore_cache: + keys: + - v0-playwright-{{ checksum "package-lock.json" }} + - run: + name: Playwright version + command: npx playwright --version - run: npx playwright install chromium - save_cache: - key: v0-playwright + key: v0-playwright-{{ checksum "package-lock.json" }} paths: - ~/.cache + - persist_to_workspace: + root: ~/ + paths: + - .cache check-bundle-size: <<: *linux-defaults @@ -306,9 +316,6 @@ jobs: steps: - attach_workspace: at: ~/ - - restore_cache: - keys: - - v0-playwright - run: command: npm run test-unit no_output_timeout: 5m @@ -345,9 +352,6 @@ jobs: steps: - attach_workspace: at: ~/ - - restore_cache: - keys: - - v0-playwright - run: command: npm run test-csp no_output_timeout: 5m diff --git a/3d-style/data/bucket/tiled_3d_model_bucket.js b/3d-style/data/bucket/tiled_3d_model_bucket.js index f15dcbd1e17..fb798e1bf7d 100644 --- a/3d-style/data/bucket/tiled_3d_model_bucket.js +++ b/3d-style/data/bucket/tiled_3d_model_bucket.js @@ -12,6 +12,7 @@ import {clamp} from '../../../src/util/util.js'; import {DEMSampler} from '../../../src/terrain/elevation.js'; import {ZoomConstantExpression} from '../../../src/style-spec/expression/index.js'; import {Aabb} from '../../../src/util/primitives.js'; +import {vec3} from 'gl-matrix'; import type ModelStyleLayer from '../../style/style_layer/model_style_layer.js'; import type {ReplacementSource} from '../../source/replacement_source.js'; @@ -506,17 +507,22 @@ class Tiled3dModelBucket implements Bucket { const nodesInfo = this.getNodesInfo(); const candidates = []; + const tmpVertex = [0, 0, 0]; + for (let i = 0; i < this.nodesInfo.length; i++) { const nodeInfo = nodesInfo[i]; assert(nodeInfo.node.meshes.length > 0); const mesh = nodeInfo.node.meshes[0]; - if (x < mesh.aabb.min[0] || y < mesh.aabb.min[1] || x > mesh.aabb.max[0] || y > mesh.aabb.max[1]) continue; + const meshAabb = Aabb.applyTransform(mesh.aabb, nodeInfo.node.matrix); + if (x < meshAabb.min[0] || y < meshAabb.min[1] || x > meshAabb.max[0] || y > meshAabb.max[1]) continue; assert(mesh.heightmap); const xCell = ((x - mesh.aabb.min[0]) / (mesh.aabb.max[0] - mesh.aabb.min[0]) * HEIGHTMAP_DIM) | 0; const yCell = ((y - mesh.aabb.min[1]) / (mesh.aabb.max[1] - mesh.aabb.min[1]) * HEIGHTMAP_DIM) | 0; const heightmapIndex = Math.min(HEIGHTMAP_DIM - 1, yCell) * HEIGHTMAP_DIM + Math.min(HEIGHTMAP_DIM - 1, xCell); + tmpVertex[2] = mesh.heightmap[heightmapIndex]; + vec3.transformMat4(tmpVertex, tmpVertex, nodeInfo.node.matrix); if (mesh.heightmap[heightmapIndex] < 0 && nodeInfo.node.footprint) { // unpopulated cell. If it is in the building footprint, return undefined height nodeInfo.node.footprint.grid.query(new Point(x, y), new Point(x, y), candidates); @@ -526,7 +532,7 @@ class Tiled3dModelBucket implements Bucket { continue; } if (nodeInfo.hiddenByReplacement) return; // better luck with the next source - return {height: mesh.heightmap[heightmapIndex], maxHeight: nodeInfo.feature.properties["height"], hidden: false, verticalScale: nodeInfo.evaluatedScale[2]}; + return {height: tmpVertex[2], maxHeight: nodeInfo.feature.properties["height"], hidden: false, verticalScale: nodeInfo.evaluatedScale[2]}; } } } diff --git a/3d-style/render/draw_model.js b/3d-style/render/draw_model.js index 61e43880cde..975009c2eeb 100644 --- a/3d-style/render/draw_model.js +++ b/3d-style/render/draw_model.js @@ -148,11 +148,12 @@ function drawMesh(sortedMesh: SortedMesh, painter: Painter, layer: ModelStyleLay mat4.transpose(normalMatrix, normalMatrix); const emissiveStrength = layer.paint.get('model-emissive-strength').constantOr(0.0); - const uniformValues = modelUniformValues( new Float32Array(sortedMesh.worldViewProjection), new Float32Array(lightingMatrix), new Float32Array(normalMatrix), + /* $FlowIgnore[incompatible-call] we can safely pass null for node.matrix as it is handled in the modelUniformValues constructor */ + null, painter, opacity, pbr.baseColorFactor, @@ -621,11 +622,12 @@ function drawInstancedNode(painter: Painter, layer: ModelStyleLayer, node: Node, const layerOpacity = layer.paint.get('model-opacity'); const emissiveStrength = layer.paint.get('model-emissive-strength').constantOr(0.0); - uniformValues = modelUniformValues( coord.expandedProjMatrix, Float32Array.from(node.matrix), new Float32Array(16), + /* $FlowIgnore[incompatible-call] we can safely pass null for node.matrix as it is handled in the modelUniformValues constructor */ + null, painter, layerOpacity, pbr.baseColorFactor, @@ -788,12 +790,15 @@ function drawBatchedModels(painter: Painter, source: SourceCache, layer: ModelSt // keep model and nodemodel matrices separate for rendering door lights const nodeModelMatrix = mat4.multiply([], tileModelMatrix, node.matrix); - const lightingMatrix = mat4.multiply([], zScaleMatrix, tileModelMatrix); + let lightingMatrix = mat4.multiply([], zScaleMatrix, tileModelMatrix); mat4.multiply(lightingMatrix, negCameraPosMatrix, lightingMatrix); const normalMatrix = mat4.invert([], lightingMatrix); mat4.transpose(normalMatrix, normalMatrix); mat4.scale(normalMatrix, normalMatrix, normalScale); + // lighting matrix should take node.matrix into account + lightingMatrix = mat4.multiply(lightingMatrix, lightingMatrix, node.matrix); + const isLightBeamPass = painter.renderPass === 'light-beam'; const wpvForNode = mat4.multiply([], tr.expandedFarZProjMatrix, nodeModelMatrix); // Lights come in tilespace so wvp should not include node.matrix when rendering door ligths @@ -873,7 +878,9 @@ function drawBatchedModels(painter: Painter, source: SourceCache, layer: ModelSt const program = painter.getOrCreateProgram('model', programOptions); if (!isShadowPass && shadowRenderer) { - shadowRenderer.setupShadowsFromMatrix(nodeModelMatrix, program, shadowRenderer.useNormalOffset); + // The shadow matrix does not need to include node transforms, + // as shadow_pos will be performing that transform in the shader + shadowRenderer.setupShadowsFromMatrix(tileModelMatrix, program, shadowRenderer.useNormalOffset); } painter.uploadCommonUniforms(context, program, null, fogMatrixArray); @@ -888,6 +895,7 @@ function drawBatchedModels(painter: Painter, source: SourceCache, layer: ModelSt new Float32Array(worldViewProjection), new Float32Array(lightingMatrix), new Float32Array(normalMatrix), + new Float32Array(node.matrix), painter, layerOpacity, pbr.baseColorFactor, diff --git a/3d-style/render/program/model_program.js b/3d-style/render/program/model_program.js index 1aef8d1b29e..b74eb2529b3 100644 --- a/3d-style/render/program/model_program.js +++ b/3d-style/render/program/model_program.js @@ -1,6 +1,6 @@ // @flow -import {mat3, vec3} from 'gl-matrix'; +import {mat3, mat4, vec3} from 'gl-matrix'; import { Uniform1i, Uniform1f, @@ -22,6 +22,7 @@ export type ModelUniformsType = { 'u_matrix': UniformMatrix4f, 'u_lighting_matrix': UniformMatrix4f, 'u_normal_matrix': UniformMatrix4f, + 'u_node_matrix': UniformMatrix4f, 'u_lightpos': Uniform3f, 'u_lightintensity': Uniform1f, 'u_lightcolor': Uniform3f, @@ -49,6 +50,7 @@ const modelUniforms = (context: Context): ModelUniformsType => ({ 'u_matrix': new UniformMatrix4f(context), 'u_lighting_matrix': new UniformMatrix4f(context), 'u_normal_matrix': new UniformMatrix4f(context), + 'u_node_matrix': new UniformMatrix4f(context), 'u_lightpos': new Uniform3f(context), 'u_lightintensity': new Uniform1f(context), 'u_lightcolor': new Uniform3f(context), @@ -73,10 +75,13 @@ const modelUniforms = (context: Context): ModelUniformsType => ({ }); +const emptyMat4 = new Float32Array(mat4.identity([])); + const modelUniformValues = ( matrix: Float32Array, lightingMatrix: Float32Array, normalMatrix: Float32Array, + nodeMatrix: Float32Array, painter: Painter, opacity: number, baseColorFactor: Color, @@ -112,6 +117,7 @@ const modelUniformValues = ( 'u_matrix': matrix, 'u_lighting_matrix': lightingMatrix, 'u_normal_matrix': normalMatrix, + 'u_node_matrix': nodeMatrix ? nodeMatrix : emptyMat4, 'u_lightpos': lightPos, 'u_lightintensity': light.properties.get('intensity'), 'u_lightcolor': [lightColor.r, lightColor.g, lightColor.b], @@ -150,7 +156,6 @@ const modelDepthUniforms = (context: Context): ModelDepthUniformsType => ({ 'u_node_matrix': new UniformMatrix4f(context) }); -const emptyMat4 = new Float32Array(16); const modelDepthUniformValues = ( matrix: Float32Array, instance: Float32Array = emptyMat4, diff --git a/3d-style/render/shadow_renderer.js b/3d-style/render/shadow_renderer.js index 696bd159459..ae08281ba68 100644 --- a/3d-style/render/shadow_renderer.js +++ b/3d-style/render/shadow_renderer.js @@ -149,6 +149,8 @@ export class ShadowRenderer { shadowDirection: Vec3; useNormalOffset: boolean; + _forceDisable: boolean; + constructor(painter: Painter) { this.painter = painter; this._enabled = false; @@ -159,9 +161,11 @@ export class ShadowRenderer { this._receivers = new ShadowReceivers(); this._depthMode = new DepthMode(painter.context.gl.LEQUAL, DepthMode.ReadWrite, [0, 1]); this._uniformValues = defaultShadowUniformValues(); + this._forceDisable = false; this.useNormalOffset = false; + painter.tp.registerParameter(this, ["Shadows"], "_forceDisable", {label: "forceDisable"}, () => { this.painter.style.map.triggerRepaint(); }); painter.tp.registerParameter(shadowParameters, ["Shadows"], "cascadeCount", {min: 1, max: 2, step: 1}); painter.tp.registerParameter(shadowParameters, ["Shadows"], "shadowMapResolution", {min: 32, max: 2048, step: 32}); painter.tp.registerBinding(this, ["Shadows"], "_numCascadesToRender", {readonly: true, label: 'numCascadesToRender'}); @@ -201,7 +205,7 @@ export class ShadowRenderer { this._enabled = this._shadowLayerCount > 0; - if (!this._enabled) { + if (!this.enabled) { return; } @@ -309,7 +313,7 @@ export class ShadowRenderer { } get enabled(): boolean { - return this._enabled; + return this._enabled && !this._forceDisable; } set enabled(enabled: boolean) { @@ -318,7 +322,7 @@ export class ShadowRenderer { } drawShadowPass(style: Style, sourceCoords: {[_: string]: Array}) { - if (!this._enabled) { + if (!this.enabled) { return; } @@ -356,7 +360,7 @@ export class ShadowRenderer { } drawGroundShadows() { - if (!this._enabled) { + if (!this.enabled) { return; } @@ -425,7 +429,7 @@ export class ShadowRenderer { } setupShadows(unwrappedTileID: UnwrappedTileID, program: Program<*>, normalOffsetMode: ?ShadowNormalOffsetMode, tileOverscaledZ: number = 0) { - if (!this._enabled) { + if (!this.enabled) { return; } @@ -467,7 +471,7 @@ export class ShadowRenderer { } setupShadowsFromMatrix(worldMatrix: Mat4, program: Program<*>, normalOffset: boolean = false) { - if (!this._enabled) { + if (!this.enabled) { return; } diff --git a/3d-style/shaders/model.vertex.glsl b/3d-style/shaders/model.vertex.glsl index 4b2b34a1759..a3d3f14eee6 100644 --- a/3d-style/shaders/model.vertex.glsl +++ b/3d-style/shaders/model.vertex.glsl @@ -22,6 +22,7 @@ in vec3 a_pos_3f; // interpolatedHeight = pow(pos_z * .x + .y, .z) uniform mat4 u_matrix; +uniform mat4 u_node_matrix; uniform mat4 u_lighting_matrix; uniform vec3 u_camera_pos; uniform vec4 u_color_mix; @@ -170,22 +171,22 @@ void main() { #endif #ifdef RENDER_SHADOWS - vec3 shadow_pos = local_pos; + vec4 shadow_pos = u_node_matrix * vec4(local_pos, 1.0); #ifdef NORMAL_OFFSET #ifdef HAS_ATTRIBUTE_a_normal_3f #ifdef MODEL_POSITION_ON_GPU - // flip the xy to bring it to the same, wrong, fill extrusion normal orientation toward inside. - // See the explanation in shadow_normal_offset. - vec3 offset = shadow_normal_offset(vec3(-normal_3f.xy, normal_3f.z)); - shadow_pos += offset * shadow_normal_offset_multiplier0(); + // flip the xy to bring it to the same, wrong, fill extrusion normal orientation toward inside. + // See the explanation in shadow_normal_offset. + vec3 offset = shadow_normal_offset(vec3(-normal_3f.xy, normal_3f.z)); + shadow_pos.xyz += offset * shadow_normal_offset_multiplier0(); #else vec3 offset = shadow_normal_offset_model(normalize(normal_3f)); - shadow_pos += offset * shadow_normal_offset_multiplier0(); + shadow_pos.xyz += offset * shadow_normal_offset_multiplier0(); #endif #endif // HAS_ATTRIBUTE_a_normal_3f #endif // NORMAL_OFFSET - v_pos_light_view_0 = u_light_matrix_0 * vec4(shadow_pos, 1); - v_pos_light_view_1 = u_light_matrix_1 * vec4(shadow_pos, 1); + v_pos_light_view_0 = u_light_matrix_0 * shadow_pos; + v_pos_light_view_1 = u_light_matrix_1 * shadow_pos; v_depth_shadows = gl_Position.w; #endif // RENDER_SHADOWS } diff --git a/3d-style/source/replacement_source.js b/3d-style/source/replacement_source.js index da39b4d14de..5ac6208502f 100644 --- a/3d-style/source/replacement_source.js +++ b/3d-style/source/replacement_source.js @@ -207,7 +207,7 @@ class ReplacementSource { const active = this._activeRegions[idx]; // Go through each footprint in the current priority level - // and check whether they're been occluded by any other regions + // and check whether they've been occluded by any other regions // with higher priority active.hiddenByOverlap = false; @@ -359,9 +359,8 @@ function footprintsIntersect(a: Footprint, aTile: UnwrappedTileID, b: Footprint, const zDiff = Math.pow(2.0, dstId.z - srcId.z); queryVertices = a.vertices.map(v => { - const x = (v.x * srcId.x * EXTENT) * zDiff - dstId.x * EXTENT; - const y = (v.y * srcId.y * EXTENT) * zDiff - dstId.y * EXTENT; - + const x = (v.x + srcId.x * EXTENT) * zDiff - dstId.x * EXTENT; + const y = (v.y + srcId.y * EXTENT) * zDiff - dstId.y * EXTENT; return new Point(x, y); }); } diff --git a/3d-style/style/style_layer/model_style_layer_properties.js b/3d-style/style/style_layer/model_style_layer_properties.js index 97f3cf8d216..d150ccabe73 100644 --- a/3d-style/style/style_layer/model_style_layer_properties.js +++ b/3d-style/style/style_layer/model_style_layer_properties.js @@ -42,6 +42,7 @@ export type PaintProps = {| "model-roughness": DataDrivenProperty, "model-height-based-emissive-strength-multiplier": DataDrivenProperty<[number, number, number, number, number]>, "model-cutoff-fade-range": DataConstantProperty, + "model-front-cutoff": DataConstantProperty<[number, number, number]>, |}; const paint: Properties = new Properties({ @@ -59,6 +60,7 @@ const paint: Properties = new Properties({ "model-roughness": new DataDrivenProperty(styleSpec["paint_model"]["model-roughness"]), "model-height-based-emissive-strength-multiplier": new DataDrivenProperty(styleSpec["paint_model"]["model-height-based-emissive-strength-multiplier"]), "model-cutoff-fade-range": new DataConstantProperty(styleSpec["paint_model"]["model-cutoff-fade-range"]), + "model-front-cutoff": new DataConstantProperty(styleSpec["paint_model"]["model-front-cutoff"]), }); // Note: without adding the explicit type annotation, Flow infers weaker types diff --git a/CHANGELOG.md b/CHANGELOG.md index 80afd397fad..23f203ee7b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,26 @@ -## 3.4.0-beta.1 +## 3.4.0 ### Features and improvements ✨ +- Add `dynamic: true` option for GeoJSON sources that enables partial update API with `source.updateData` method. Further optimizations for this mode are expected in future releases. +- Add `Map` `setConfig`, `getConfig`, `setSchema` and `getSchema` methods for batch setting of style configuration options. +- Add `config` option for the `Map` constructor and `setStyle` methods for conveniently setting style configuration options on map initialization. - Add `icon-color-saturation`, `icon-color-contrast`, `icon-color-brightness-min` and `icon-color-brightness-max` to control symbol layer appearance. - Introduce a new `line-join` mode: `none` to improve line pattern distortions around joins. - Extend `model-id` property to support URIs (in addition to style-defined model references). +- Expose more parameters in map `devtools` UI. ### Bug fixes 🐞 +- Fix an issue with `flyTo` ignoring `padding` in its options. - Respect padding in `cameraForBounds` on globe view. (h/t [@jonasnoki](https://github.com/jonasnoki)) [#13126](https://github.com/mapbox/mapbox-gl-js/pull/13126) - Fix `preloadOnly` not preloading tiles from style imports. - Fix `queryRenderedFeatures` for non-integer ID in non-tiled model sources - Fix `model-scale` property for large number of 3D models. - Fix flickering of `raster-particle` layer on globe view. - Improve rendering of low-resolution `raster-array` data. +- Fix an issue with GL JS bundle not building locally on Windows. +- Fix multiple edge cases when using `symbol-z-elevate`. +- Fix rendering issues with `raster-particle` layer on certain Android devices. +- Fix shadow and lighting rendering issues in certain areas when using Mapbox Standard. ## 3.3.0 diff --git a/build/generate-flow-typed-style-spec.js b/build/generate-flow-typed-style-spec.js index e80c153a98f..a3cd5db41bd 100644 --- a/build/generate-flow-typed-style-spec.js +++ b/build/generate-flow-typed-style-spec.js @@ -1,3 +1,5 @@ +/* eslint-disable flowtype/require-valid-file-annotation */ + import fs from 'fs'; import assert from 'assert'; import spec from '../src/style-spec/reference/v8.json'; @@ -71,7 +73,15 @@ function flowObjectDeclaration(key, properties) { function flowObject(properties, indent, sealing = '') { return `{${sealing} ${Object.keys(properties) - .map(k => ` ${indent}${flowProperty(k, properties[k])}`) + .map(k => { + const property = ` ${indent}${flowProperty(k, properties[k])}`; + if (properties[k].transition) { + const propertyTransition = ` ${indent}"${k}-transition"?: TransitionSpecification`; + return [property, propertyTransition].join(',\n'); + } else { + return property; + } + }) .join(',\n')} ${indent}${sealing}}`; } diff --git a/build/print-release-url.js b/build/print-release-url.js index 46afc692d62..c6309e4e2bb 100644 --- a/build/print-release-url.js +++ b/build/print-release-url.js @@ -1,6 +1,12 @@ -import address from 'address'; -import qrcode from 'qrcode-terminal' +/* eslint-disable flowtype/require-valid-file-annotation */ -const url = `http://${address.ip()}:9966/test/release/index.html`; +import {ip} from 'address'; +import qrcode from 'qrcode-terminal'; + +let url = `http://${ip()}:9966/test/release/index.html`; console.warn(`Scan this QR code or enter ${url}`); + +const accessToken = process.env['MAPBOX_ACCESS_TOKEN']; +if (accessToken) url += `#page=&access_token=${accessToken}`; + qrcode.generate(url); diff --git a/build/rollup_plugins.js b/build/rollup_plugins.js index 46bc659d77a..385d13c5a8c 100644 --- a/build/rollup_plugins.js +++ b/build/rollup_plugins.js @@ -35,12 +35,12 @@ export const plugins = ({minified, production, test, bench, keepClassNames}) => functions: ['PerformanceUtils.*', 'WorkerPerformanceUtils.*', 'Debug.*', 'performance.mark'] }) : false, production || bench ? unassert() : false, - replace({ - 'process.env.ROLLUP_WATCH': JSON.stringify(process.env.ROLLUP_WATCH), - }), test ? replace({ - 'process.env.CI': JSON.stringify(process.env.CI), - 'process.env.UPDATE': JSON.stringify(process.env.UPDATE) + preventAssignment: true, + values: { + 'process.env.CI': JSON.stringify(process.env.CI), + 'process.env.UPDATE': JSON.stringify(process.env.UPDATE) + } }) : false, glsl(['./src/shaders/*.glsl', './3d-style/shaders/*.glsl'], production), minified ? terser({ diff --git a/debug/geojson-partial.html b/debug/geojson-partial.html new file mode 100644 index 00000000000..4d5d48a946c --- /dev/null +++ b/debug/geojson-partial.html @@ -0,0 +1,95 @@ + + + + Mapbox GL JS debug page + + + + + + + +
+ + + + + + diff --git a/debug/raster-particle-layer.html b/debug/raster-particle-layer.html index cbe75204821..591ed3e71d0 100644 --- a/debug/raster-particle-layer.html +++ b/debug/raster-particle-layer.html @@ -181,7 +181,13 @@ map.on('load', () => { map.addSource('raster-array-source', { 'type': 'raster-array', - 'url' : 'http://localhost:9967/debug/wind-512-mrt/tile.json' + 'url' : 'http://localhost:9967/debug/wind-512-mrt/tile.json', + 'bounds': [ + 119.96875, + 22.375, + 150.03125, + 47.62499999999999 + ] }); map.addLayer({ diff --git a/package-lock.json b/package-lock.json index d0188cd755c..28abbf7ccfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mapbox-gl", - "version": "3.4.0-beta.1", + "version": "3.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mapbox-gl", - "version": "3.4.0-beta.1", + "version": "3.4.0", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@mapbox/jsonlint-lines-primitives": "^2.0.2", @@ -67,9 +67,9 @@ "eslint-plugin-flowtype": "^7.0.0", "eslint-plugin-html": "^8.1.1", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsdoc": "^48.2.3", + "eslint-plugin-jsdoc": "^48.2.5", "flow-bin": "0.191.0", - "glob": "^10.3.12", + "glob": "^10.3.16", "is-builtin-module": "^4.0.0", "jest-extended": "^4.0.2", "json-stringify-pretty-compact": "^4.0.0", @@ -77,19 +77,19 @@ "mapbox-gl-styles": "^2.0.2", "minimist": "^1.2.6", "mock-geolocation": "^1.0.11", - "msw": "^2.2.11", + "msw": "^2.3.0", "node-notifier": "^10.0.1", "npm-font-open-sans": "^1.1.0", "npm-run-all": "^4.1.5", "pixelmatch": "^5.3.0", - "playwright": "^1.43.1", + "playwright": "^1.44.0", "postcss": "^8.4.38", "postcss-cli": "^11.0.0", "postcss-inline-svg": "^6.0.0", "pretty-bytes": "^6.0.0", - "puppeteer-core": "^22.8.0", + "puppeteer-core": "^22.9.0", "qrcode-terminal": "^0.12.0", - "rollup": "3.29.4", + "rollup": "^4.17.2", "rollup-plugin-unassert": "^0.6.0", "serve-static": "^1.15.0", "shuffle-seed": "^1.1.6", @@ -100,7 +100,6 @@ "tape-filter": "^1.0.4", "testem": "^3.13.0", "vite-plugin-arraybuffer": "^0.0.7", - "vite-plugin-node-polyfills": "^0.21.0", "vitest": "^1.6.0" } }, @@ -592,11 +591,14 @@ } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.42.0.tgz", - "integrity": "sha512-R1w57YlVA6+YE01wch3GPYn6bCsrOV3YW/5oGGE2tmX6JcL9Nr+b5IikrjMPF+v9CV3ay+obImEdsDhovhJrzw==", + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.43.0.tgz", + "integrity": "sha512-Q1CnsQrytI3TlCB1IVWXWeqUIPGVEKGaE7IbVdt13Nq/3i0JESAkQQERrfiQkmlpijl+++qyqPgaS31Bvc1jRQ==", "dev": true, "dependencies": { + "@types/eslint": "^8.56.5", + "@types/estree": "^1.0.5", + "@typescript-eslint/types": "^7.2.0", "comment-parser": "1.4.1", "esquery": "^1.5.0", "jsdoc-type-pratt-parser": "~4.0.0" @@ -1492,9 +1494,9 @@ } }, "node_modules/@mswjs/interceptors": { - "version": "0.25.16", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.25.16.tgz", - "integrity": "sha512-8QC8JyKztvoGAdPgyZy49c9vSHHAZjHagwl4RY9E8carULk8ym3iTaiawrT1YoLF/qb449h48f71XDPgkUSOUg==", + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.29.1.tgz", + "integrity": "sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==", "dev": true, "dependencies": { "@open-draft/deferred-promise": "^2.2.0", @@ -1915,28 +1917,6 @@ "node": ">=10" } }, - "node_modules/@rollup/plugin-inject": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", - "integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, "node_modules/@rollup/plugin-json": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", @@ -2097,9 +2077,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", + "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", "cpu": [ "arm" ], @@ -2110,9 +2090,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", + "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", "cpu": [ "arm64" ], @@ -2123,9 +2103,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", + "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", "cpu": [ "arm64" ], @@ -2136,9 +2116,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", + "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", "cpu": [ "x64" ], @@ -2149,9 +2129,22 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", + "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", + "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", "cpu": [ "arm" ], @@ -2162,9 +2155,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", + "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", "cpu": [ "arm64" ], @@ -2175,9 +2168,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", + "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", "cpu": [ "arm64" ], @@ -2187,10 +2180,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", + "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", + "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", "cpu": [ "riscv64" ], @@ -2200,10 +2206,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", + "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", + "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", "cpu": [ "x64" ], @@ -2214,9 +2233,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", + "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", "cpu": [ "x64" ], @@ -2227,9 +2246,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", + "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", "cpu": [ "arm64" ], @@ -2240,9 +2259,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", + "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", "cpu": [ "ia32" ], @@ -2253,9 +2272,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", + "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", "cpu": [ "x64" ], @@ -2319,12 +2338,28 @@ "@types/node": "*" } }, + "node_modules/@types/eslint": { + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -2377,6 +2412,19 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/types": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", + "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@vitest/browser": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-1.6.0.tgz", @@ -4208,12 +4256,6 @@ "sha.js": "^2.4.8" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -4750,9 +4792,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1273771", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1273771.tgz", - "integrity": "sha512-QDbb27xcTVReQQW/GHJsdQqGKwYBE7re7gxehj467kKP2DKuYBUj6i2k5LRiAC66J1yZG/9gsxooz/s9pcm0Og==", + "version": "0.0.1286932", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1286932.tgz", + "integrity": "sha512-wu58HMQll9voDjR4NlPyoDEw1syfzaBNHymMMZ/QOXiHRNluOnDgu9hp1yHOKYoMlxCh4lSSiugLITe6Fvu1eA==", "dev": true }, "node_modules/diff": { @@ -5568,19 +5610,19 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "48.2.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.3.tgz", - "integrity": "sha512-r9DMAmFs66VNvNqRLLjHejdnJtILrt3xGi+Qx0op0oRfFGVpOR1Hb3BC++MacseHx93d8SKYPhyrC9BS7Os2QA==", + "version": "48.2.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.5.tgz", + "integrity": "sha512-ZeTfKV474W1N9niWfawpwsXGu+ZoMXu4417eBROX31d7ZuOk8zyG66SO77DpJ2+A9Wa2scw/jRqBPnnQo7VbcQ==", "dev": true, "dependencies": { - "@es-joy/jsdoccomment": "~0.42.0", + "@es-joy/jsdoccomment": "~0.43.0", "are-docs-informative": "^0.0.2", "comment-parser": "1.4.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", "is-builtin-module": "^3.2.1", - "semver": "^7.6.0", + "semver": "^7.6.1", "spdx-expression-parse": "^4.0.0" }, "engines": { @@ -5617,26 +5659,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-jsdoc/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-plugin-jsdoc/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -5644,12 +5671,6 @@ "node": ">=10" } }, - "node_modules/eslint-plugin-jsdoc/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -6361,22 +6382,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/fireworm": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/fireworm/-/fireworm-0.7.2.tgz", @@ -6728,22 +6733,22 @@ "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" }, "node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "version": "10.3.16", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz", + "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", + "jackspeak": "^3.1.2", "minimatch": "^9.0.1", "minipass": "^7.0.4", - "path-scurry": "^1.10.2" + "path-scurry": "^1.11.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -7591,22 +7596,6 @@ "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", "dev": true }, - "node_modules/is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", @@ -7837,19 +7826,10 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/isomorphic-timers-promises": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/isomorphic-timers-promises/-/isomorphic-timers-promises-1.0.1.tgz", - "integrity": "sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -8266,21 +8246,6 @@ "url": "https://github.com/sponsors/antfu" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -8672,9 +8637,9 @@ } }, "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -8808,9 +8773,9 @@ "dev": true }, "node_modules/msw": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.2.11.tgz", - "integrity": "sha512-XtIoewF7XWLT0a39Ftkazt9PprBA1bxHZ4CSlomN74sCBJOJU2w5VwLmGlswwsOBhGoF7jovt6bxrSIESxA1KA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.3.0.tgz", + "integrity": "sha512-cDr1q/QTMzaWhY8n9lpGhceY209k29UZtdTgJ3P8Bzne3TSMchX2EM/ldvn4ATLOktpCefCU2gcEgzHc31GTPw==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -8818,7 +8783,7 @@ "@bundled-es-modules/statuses": "^1.0.1", "@inquirer/confirm": "^3.0.0", "@mswjs/cookies": "^1.1.0", - "@mswjs/interceptors": "^0.25.16", + "@mswjs/interceptors": "^0.29.0", "@open-draft/until": "^2.1.0", "@types/cookie": "^0.6.0", "@types/statuses": "^2.0.4", @@ -9059,119 +9024,6 @@ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, - "node_modules/node-stdlib-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/node-stdlib-browser/-/node-stdlib-browser-1.2.0.tgz", - "integrity": "sha512-VSjFxUhRhkyed8AtLwSCkMrJRfQ3e2lGtG3sP6FEgaLKBBbxM/dLfjRe1+iLhjvyLFW3tBQ8+c0pcOtXGbAZJg==", - "dev": true, - "dependencies": { - "assert": "^2.0.0", - "browser-resolve": "^2.0.0", - "browserify-zlib": "^0.2.0", - "buffer": "^5.7.1", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "create-require": "^1.1.1", - "crypto-browserify": "^3.11.0", - "domain-browser": "^4.22.0", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "isomorphic-timers-promises": "^1.0.1", - "os-browserify": "^0.3.0", - "path-browserify": "^1.0.1", - "pkg-dir": "^5.0.0", - "process": "^0.11.10", - "punycode": "^1.4.1", - "querystring-es3": "^0.2.1", - "readable-stream": "^3.6.0", - "stream-browserify": "^3.0.0", - "stream-http": "^3.2.0", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.1", - "url": "^0.11.0", - "util": "^0.12.4", - "vm-browserify": "^1.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-stdlib-browser/node_modules/assert": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", - "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "is-nan": "^1.3.2", - "object-is": "^1.1.5", - "object.assign": "^4.1.4", - "util": "^0.12.5" - } - }, - "node_modules/node-stdlib-browser/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/node-stdlib-browser/node_modules/domain-browser": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.23.0.tgz", - "integrity": "sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://bevry.me/fund" - } - }, - "node_modules/node-stdlib-browser/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/node-stdlib-browser/node_modules/timers-browserify": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", - "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", - "dev": true, - "dependencies": { - "setimmediate": "^1.0.4" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -9594,36 +9446,6 @@ "node": ">=4" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/pac-proxy-agent": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", @@ -9728,15 +9550,6 @@ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", "dev": true }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -9771,25 +9584,25 @@ } }, "node_modules/path-scurry": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", - "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -9916,18 +9729,6 @@ "pixelmatch": "bin/pixelmatch" } }, - "node_modules/pkg-dir": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", - "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", - "dev": true, - "dependencies": { - "find-up": "^5.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/pkg-types": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.0.tgz", @@ -9940,12 +9741,12 @@ } }, "node_modules/playwright": { - "version": "1.43.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz", - "integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==", + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.0.tgz", + "integrity": "sha512-F9b3GUCLQ3Nffrfb6dunPOkE5Mh68tR7zN32L4jCk4FjQamgesGay7/dAAe1WaMEGV04DkdJfcJzjoCKygUaRQ==", "dev": true, "dependencies": { - "playwright-core": "1.43.1" + "playwright-core": "1.44.0" }, "bin": { "playwright": "cli.js" @@ -9958,9 +9759,9 @@ } }, "node_modules/playwright-core": { - "version": "1.43.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz", - "integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==", + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.0.tgz", + "integrity": "sha512-ZTbkNpFfYcGWohvTTl+xewITm7EOuqIqex0c7dNZ+aXsbrLj0qI8XlGKfPpipjm0Wny/4Lt4CJsWJk1stVS5qQ==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -10812,15 +10613,15 @@ "dev": true }, "node_modules/puppeteer-core": { - "version": "22.8.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.8.0.tgz", - "integrity": "sha512-S5bWx3g/fNuyFxjZX9TkZMN07CEH47+9Zm6IiTl1QfqI9pnVaShbwrD9kRe5vmz/XPp/jLGhhxRUj1sY4wObnA==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.9.0.tgz", + "integrity": "sha512-Q2SYVZ1SIE7jCd/Pp+1/mNLFtdJfGvAF+CqOTDG8HcCNCiBvoXfopXfOfMHQ/FueXhGfJW/I6DartWv6QzpNGg==", "dev": true, "dependencies": { "@puppeteer/browsers": "2.2.3", "chromium-bidi": "0.5.19", "debug": "4.3.4", - "devtools-protocol": "0.0.1273771", + "devtools-protocol": "0.0.1286932", "ws": "8.17.0" }, "engines": { @@ -11188,18 +10989,37 @@ } }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", + "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.17.2", + "@rollup/rollup-android-arm64": "4.17.2", + "@rollup/rollup-darwin-arm64": "4.17.2", + "@rollup/rollup-darwin-x64": "4.17.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", + "@rollup/rollup-linux-arm-musleabihf": "4.17.2", + "@rollup/rollup-linux-arm64-gnu": "4.17.2", + "@rollup/rollup-linux-arm64-musl": "4.17.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", + "@rollup/rollup-linux-riscv64-gnu": "4.17.2", + "@rollup/rollup-linux-s390x-gnu": "4.17.2", + "@rollup/rollup-linux-x64-gnu": "4.17.2", + "@rollup/rollup-linux-x64-musl": "4.17.2", + "@rollup/rollup-win32-arm64-msvc": "4.17.2", + "@rollup/rollup-win32-ia32-msvc": "4.17.2", + "@rollup/rollup-win32-x64-msvc": "4.17.2", "fsevents": "~2.3.2" } }, @@ -11482,12 +11302,6 @@ "node": ">= 0.4" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -13559,54 +13373,6 @@ "integrity": "sha512-c4Egxj7NUGco2Ggw9KUBToOxuc7Ws7mWm0hz/QnaT5Ph8ycC7ypMBOD31NuhPSx+wdUvgIbS1XpMvJLSdHakPA==", "dev": true }, - "node_modules/vite-plugin-node-polyfills": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.21.0.tgz", - "integrity": "sha512-Sk4DiKnmxN8E0vhgEhzLudfJQfaT8k4/gJ25xvUPG54KjLJ6HAmDKbr4rzDD/QWEY+Lwg80KE85fGYBQihEPQA==", - "dev": true, - "dependencies": { - "@rollup/plugin-inject": "^5.0.5", - "node-stdlib-browser": "^1.2.0" - }, - "funding": { - "url": "https://github.com/sponsors/davidmyersdev" - }, - "peerDependencies": { - "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" - } - }, - "node_modules/vite/node_modules/rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", - "dev": true, - "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", - "fsevents": "~2.3.2" - } - }, "node_modules/vitest": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", @@ -14159,18 +13925,6 @@ "fd-slicer": "~1.1.0" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/zod": { "version": "3.22.4", "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", diff --git a/package.json b/package.json index 3b35440d555..f9881b0a7e7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mapbox-gl", "description": "A WebGL interactive maps library", - "version": "3.4.0-beta.1", + "version": "3.4.0", "main": "dist/mapbox-gl.js", "style": "dist/mapbox-gl.css", "license": "SEE LICENSE IN LICENSE.txt", @@ -69,9 +69,9 @@ "eslint-plugin-flowtype": "^7.0.0", "eslint-plugin-html": "^8.1.1", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsdoc": "^48.2.3", + "eslint-plugin-jsdoc": "^48.2.5", "flow-bin": "0.191.0", - "glob": "^10.3.12", + "glob": "^10.3.16", "is-builtin-module": "^4.0.0", "jest-extended": "^4.0.2", "json-stringify-pretty-compact": "^4.0.0", @@ -79,19 +79,19 @@ "mapbox-gl-styles": "^2.0.2", "minimist": "^1.2.6", "mock-geolocation": "^1.0.11", - "msw": "^2.2.11", + "msw": "^2.3.0", "node-notifier": "^10.0.1", "npm-font-open-sans": "^1.1.0", "npm-run-all": "^4.1.5", "pixelmatch": "^5.3.0", - "playwright": "^1.43.1", + "playwright": "^1.44.0", "postcss": "^8.4.38", "postcss-cli": "^11.0.0", "postcss-inline-svg": "^6.0.0", "pretty-bytes": "^6.0.0", - "puppeteer-core": "^22.8.0", + "puppeteer-core": "^22.9.0", "qrcode-terminal": "^0.12.0", - "rollup": "3.29.4", + "rollup": "^4.17.2", "rollup-plugin-unassert": "^0.6.0", "serve-static": "^1.15.0", "shuffle-seed": "^1.1.6", @@ -102,7 +102,6 @@ "tape-filter": "^1.0.4", "testem": "^3.13.0", "vite-plugin-arraybuffer": "^0.0.7", - "vite-plugin-node-polyfills": "^0.21.0", "vitest": "^1.6.0" }, "scripts": { @@ -122,7 +121,8 @@ "start-range-server": "node build/range-request-server.js", "start": "run-p build-token watch-css watch-dev start-server", "start-debug": "run-p build-token watch-css watch-dev start-server", - "start-release": "run-s build-token build-prod-min build-css print-release-url start-server", + "prepare-release-pages": "while read l; do cp debug/$l test/release/$l; done < test/release/local_release_page_list.txt", + "start-release": "run-s build-token build-prod-min build-css print-release-url prepare-release-pages start-server", "lint": "eslint --cache --ignore-path .gitignore src test bench 3d-style debug/*.html", "lint-css": "stylelint 'src/css/mapbox-gl.css'", "test": "run-s lint lint-css test-flow test-unit", diff --git a/rollup.config.js b/rollup.config.js index 8b338a43753..ecd033ed547 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,5 +1,6 @@ /* eslint-disable flowtype/require-valid-file-annotation */ import fs from 'fs'; +import path from 'path'; import {fileURLToPath} from 'url'; import {readFile} from 'node:fs/promises'; @@ -26,50 +27,52 @@ function buildType(build, minified) { } const outputFile = buildType(BUILD, MINIFY); -export default [{ - // First, use code splitting to bundle GL JS into three "chunks": - // - rollup/build/index.js: the main module, plus all its dependencies not shared by the worker module - // - rollup/build/worker.js: the worker module, plus all dependencies not shared by the main module - // - rollup/build/shared.js: the set of modules that are dependencies of both the main module and the worker module - // - // This is also where we do all of our source transformations: removing - // flow annotations, transpiling ES6 features using buble, inlining shader - // sources as strings, etc. - input: ['src/index.js', 'src/source/worker.js'], - output: { - dir: 'rollup/build/mapboxgl', - format: 'amd', - sourcemap: 'inline', - indent: false, - chunkFileNames: 'shared.js', - minifyInternalExports: production - }, - onwarn: production ? onwarn : false, - treeshake: production, - plugins: plugins({minified, production, bench}) -}, { - // Next, bundle together the three "chunks" produced in the previous pass - // into a single, final bundle. See rollup/bundle_prelude.js and - // rollup/mapboxgl.js for details. - input: 'rollup/mapboxgl.js', - output: { - name: 'mapboxgl', - file: outputFile, - format: 'umd', - sourcemap: production ? true : 'inline', - indent: false, - intro: fs.readFileSync(fileURLToPath(new URL('./rollup/bundle_prelude.js', import.meta.url)), 'utf8'), - banner - }, - treeshake: false, - plugins: [ - // Ingest the sourcemaps produced in the first step of the build. - // This is the only reason we use Rollup for this second pass - sourcemaps() - ], -}]; +export default ({watch}) => { + return [{ + // First, use code splitting to bundle GL JS into three "chunks": + // - rollup/build/index.js: the main module, plus all its dependencies not shared by the worker module + // - rollup/build/worker.js: the worker module, plus all dependencies not shared by the main module + // - rollup/build/shared.js: the set of modules that are dependencies of both the main module and the worker module + // + // This is also where we do all of our source transformations: removing + // flow annotations, transpiling ES6 features using buble, inlining shader + // sources as strings, etc. + input: ['src/index.js', 'src/source/worker.js'], + output: { + dir: 'rollup/build/mapboxgl', + format: 'amd', + sourcemap: 'inline', + indent: false, + chunkFileNames: 'shared.js', + minifyInternalExports: production + }, + onwarn: production ? onwarn : false, + treeshake: production, + plugins: plugins({minified, production, bench}) + }, { + // Next, bundle together the three "chunks" produced in the previous pass + // into a single, final bundle. See rollup/bundle_prelude.js and + // rollup/mapboxgl.js for details. + input: 'rollup/mapboxgl.js', + output: { + name: 'mapboxgl', + file: outputFile, + format: 'umd', + sourcemap: production ? true : 'inline', + indent: false, + intro: fs.readFileSync(fileURLToPath(new URL('./rollup/bundle_prelude.js', import.meta.url)), 'utf8'), + banner + }, + treeshake: false, + plugins: [ + // Ingest the sourcemaps produced in the first step of the build. + // This is the only reason we use Rollup for this second pass + sourcemaps({watch}), + ] + }]; +}; -function sourcemaps() { +function sourcemaps({watch}) { const base64SourceMapRegExp = /\/\/# sourceMappingURL=data:[^,]+,([^ ]+)/; return { @@ -83,15 +86,21 @@ function sourcemaps() { const decodedSourceMap = Buffer.from(base64EncodedSourceMap, 'base64').toString('utf-8'); const map = JSON.parse(decodedSourceMap); + // Starting with Rollup 4.x, we need to explicitly watch files + // if their content is returned by the load hook. + // https://github.com/rollup/rollup/pull/5150 + if (watch) this.addWatchFile(id); + return {code, map}; } }; } function onwarn(warning) { + const styleSpecPath = path.resolve('src', 'style-spec'); if (warning.code === 'CIRCULAR_DEPENDENCY') { // Ignore circular dependencies in style-spec and throw on all others - if (!warning.ids[0].includes('/src/style-spec')) throw new Error(warning.message); + if (!warning.ids[0].startsWith(styleSpecPath)) throw new Error(warning.message); } else { console.error(`(!) ${warning.message}`); } diff --git a/src/geo/lng_lat.js b/src/geo/lng_lat.js index 999ef03aec3..500c8d8ab2e 100644 --- a/src/geo/lng_lat.js +++ b/src/geo/lng_lat.js @@ -205,7 +205,8 @@ export default LngLat; /** * A `LngLatBounds` object represents a geographical bounding box, - * defined by its southwest and northeast points in longitude and latitude. + * defined by its southwest and northeast points in [`longitude`](https://docs.mapbox.com/help/glossary/lat-lon/) and [`latitude`](https://docs.mapbox.com/help/glossary/lat-lon/). + * `Longitude` values are typically set between `-180` to `180`, but can exceed this range if `renderWorldCopies` is set to `true`. `Latitude` values must be within `-85.051129` to `85.051129`. * * If no arguments are provided to the constructor, a `null` bounding box is created. * @@ -224,16 +225,17 @@ export class LngLatBounds { _ne: LngLat; _sw: LngLat; - // This constructor is too flexible to type. It should not be so flexible. - constructor(sw: any, ne: any) { + constructor(sw: [number, number, number, number] | [LngLatLike, LngLatLike] | LngLatLike | void, ne: LngLatLike | void) { if (!sw) { // noop } else if (ne) { - this.setSouthWest(sw).setNorthEast(ne); + this.setSouthWest(((sw: any): LngLatLike)).setNorthEast(ne); } else if (sw.length === 4) { - this.setSouthWest([sw[0], sw[1]]).setNorthEast([sw[2], sw[3]]); + const bounds = ((sw: any): [number, number, number, number]); + this.setSouthWest([bounds[0], bounds[1]]).setNorthEast([bounds[2], bounds[3]]); } else { - this.setSouthWest(sw[0]).setNorthEast(sw[1]); + const bounds = ((sw: any): [LngLatLike, LngLatLike]); + this.setSouthWest(bounds[0]).setNorthEast(bounds[1]); } } diff --git a/src/geo/lng_lat_bounds.js b/src/geo/lng_lat_bounds.js deleted file mode 100644 index a5cdccbcb68..00000000000 --- a/src/geo/lng_lat_bounds.js +++ /dev/null @@ -1,323 +0,0 @@ -// @flow - -import LngLat from './lng_lat.js'; - -import type {LngLatLike} from './lng_lat.js'; - -/** - * A `LngLatBounds` object represents a geographical bounding box, - * defined by its southwest and northeast points in longitude and latitude. - * - * If no arguments are provided to the constructor, a `null` bounding box is created. - * - * Note that any Mapbox GL method that accepts a `LngLatBounds` object as an argument or option - * can also accept an `Array` of two {@link LngLatLike} constructs and will perform an implicit conversion. - * This flexible type is documented as {@link LngLatBoundsLike}. - * - * @param {LngLatLike} [sw] The southwest corner of the bounding box. - * @param {LngLatLike} [ne] The northeast corner of the bounding box. - * @example - * const sw = new mapboxgl.LngLat(-73.9876, 40.7661); - * const ne = new mapboxgl.LngLat(-73.9397, 40.8002); - * const llb = new mapboxgl.LngLatBounds(sw, ne); - */ -class LngLatBounds { - _ne: LngLat; - _sw: LngLat; - - // This constructor is too flexible to type. It should not be so flexible. - constructor(sw: any, ne: any) { - if (!sw) { - // noop - } else if (ne) { - this.setSouthWest(sw).setNorthEast(ne); - } else if (sw.length === 4) { - this.setSouthWest([sw[0], sw[1]]).setNorthEast([sw[2], sw[3]]); - } else { - this.setSouthWest(sw[0]).setNorthEast(sw[1]); - } - } - - /** - * Set the northeast corner of the bounding box. - * - * @param {LngLatLike} ne A {@link LngLatLike} object describing the northeast corner of the bounding box. - * @returns {LngLatBounds} Returns itself to allow for method chaining. - * @example - * const sw = new mapboxgl.LngLat(-73.9876, 40.7661); - * const ne = new mapboxgl.LngLat(-73.9397, 40.8002); - * const llb = new mapboxgl.LngLatBounds(sw, ne); - * llb.setNorthEast([-73.9397, 42.8002]); - */ - setNorthEast(ne: LngLatLike): this { - this._ne = ne instanceof LngLat ? new LngLat(ne.lng, ne.lat) : LngLat.convert(ne); - return this; - } - - /** - * Set the southwest corner of the bounding box. - * - * @param {LngLatLike} sw A {@link LngLatLike} object describing the southwest corner of the bounding box. - * @returns {LngLatBounds} Returns itself to allow for method chaining. - * @example - * const sw = new mapboxgl.LngLat(-73.9876, 40.7661); - * const ne = new mapboxgl.LngLat(-73.9397, 40.8002); - * const llb = new mapboxgl.LngLatBounds(sw, ne); - * llb.setSouthWest([-73.9876, 40.2661]); - */ - setSouthWest(sw: LngLatLike): this { - this._sw = sw instanceof LngLat ? new LngLat(sw.lng, sw.lat) : LngLat.convert(sw); - return this; - } - - /** - * Extend the bounds to include a given LngLatLike or LngLatBoundsLike. - * - * @param {LngLatLike|LngLatBoundsLike} obj Object to extend to. - * @returns {LngLatBounds} Returns itself to allow for method chaining. - * @example - * const sw = new mapboxgl.LngLat(-73.9876, 40.7661); - * const ne = new mapboxgl.LngLat(-73.9397, 40.8002); - * const llb = new mapboxgl.LngLatBounds(sw, ne); - * llb.extend([-72.9876, 42.2661]); - */ - extend(obj: LngLatLike | LngLatBoundsLike): this { - const sw = this._sw, - ne = this._ne; - let sw2, ne2; - - if (obj instanceof LngLat) { - sw2 = obj; - ne2 = obj; - - } else if (obj instanceof LngLatBounds) { - sw2 = obj._sw; - ne2 = obj._ne; - - if (!sw2 || !ne2) return this; - - } else if (Array.isArray(obj)) { - // $FlowFixMe[method-unbinding] - if (obj.length === 4 || obj.every(Array.isArray)) { - const lngLatBoundsObj = ((obj: any): LngLatBoundsLike); - return this.extend(LngLatBounds.convert(lngLatBoundsObj)); - } else { - const lngLatObj = ((obj: any): LngLatLike); - return this.extend(LngLat.convert(lngLatObj)); - } - } else if (typeof obj === 'object' && obj !== null && obj.hasOwnProperty("lat") && (obj.hasOwnProperty("lon") || obj.hasOwnProperty("lng"))) { - return this.extend(LngLat.convert(obj)); - } else { - return this; - } - - if (!sw && !ne) { - this._sw = new LngLat(sw2.lng, sw2.lat); - this._ne = new LngLat(ne2.lng, ne2.lat); - - } else { - sw.lng = Math.min(sw2.lng, sw.lng); - sw.lat = Math.min(sw2.lat, sw.lat); - ne.lng = Math.max(ne2.lng, ne.lng); - ne.lat = Math.max(ne2.lat, ne.lat); - } - - return this; - } - - /** - * Returns the geographical coordinate equidistant from the bounding box's corners. - * - * @returns {LngLat} The bounding box's center. - * @example - * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); - * llb.getCenter(); // = LngLat {lng: -73.96365, lat: 40.78315} - */ - getCenter(): LngLat { - return new LngLat((this._sw.lng + this._ne.lng) / 2, (this._sw.lat + this._ne.lat) / 2); - } - - /** - * Returns the southwest corner of the bounding box. - * - * @returns {LngLat} The southwest corner of the bounding box. - * @example - * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); - * llb.getSouthWest(); // LngLat {lng: -73.9876, lat: 40.7661} - */ - getSouthWest(): LngLat { return this._sw; } - - /** - * Returns the northeast corner of the bounding box. - * - * @returns {LngLat} The northeast corner of the bounding box. - * @example - * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); - * llb.getNorthEast(); // LngLat {lng: -73.9397, lat: 40.8002} - */ - getNorthEast(): LngLat { return this._ne; } - - /** - * Returns the northwest corner of the bounding box. - * - * @returns {LngLat} The northwest corner of the bounding box. - * @example - * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); - * llb.getNorthWest(); // LngLat {lng: -73.9876, lat: 40.8002} - */ - getNorthWest(): LngLat { return new LngLat(this.getWest(), this.getNorth()); } - - /** - * Returns the southeast corner of the bounding box. - * - * @returns {LngLat} The southeast corner of the bounding box. - * @example - * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); - * llb.getSouthEast(); // LngLat {lng: -73.9397, lat: 40.7661} - */ - getSouthEast(): LngLat { return new LngLat(this.getEast(), this.getSouth()); } - - /** - * Returns the west edge of the bounding box. - * - * @returns {number} The west edge of the bounding box. - * @example - * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); - * llb.getWest(); // -73.9876 - */ - getWest(): number { return this._sw.lng; } - - /** - * Returns the south edge of the bounding box. - * - * @returns {number} The south edge of the bounding box. - * @example - * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); - * llb.getSouth(); // 40.7661 - */ - getSouth(): number { return this._sw.lat; } - - /** - * Returns the east edge of the bounding box. - * - * @returns {number} The east edge of the bounding box. - * @example - * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); - * llb.getEast(); // -73.9397 - */ - getEast(): number { return this._ne.lng; } - - /** - * Returns the north edge of the bounding box. - * - * @returns {number} The north edge of the bounding box. - * @example - * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); - * llb.getNorth(); // 40.8002 - */ - getNorth(): number { return this._ne.lat; } - - /** - * Returns the bounding box represented as an array. - * - * @returns {Array>} The bounding box represented as an array, consisting of the - * southwest and northeast coordinates of the bounding represented as arrays of numbers. - * @example - * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); - * llb.toArray(); // = [[-73.9876, 40.7661], [-73.9397, 40.8002]] - */ - toArray(): [[number, number], [number, number]] { - return [this._sw.toArray(), this._ne.toArray()]; - } - - /** - * Return the bounding box represented as a string. - * - * @returns {string} The bounding box represents as a string of the format - * `'LngLatBounds(LngLat(lng, lat), LngLat(lng, lat))'`. - * @example - * const llb = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); - * llb.toString(); // = "LngLatBounds(LngLat(-73.9876, 40.7661), LngLat(-73.9397, 40.8002))" - */ - toString(): string { - return `LngLatBounds(${this._sw.toString()}, ${this._ne.toString()})`; - } - - /** - * Check if the bounding box is an empty/`null`-type box. - * - * @returns {boolean} True if bounds have been defined, otherwise false. - * @example - * const llb = new mapboxgl.LngLatBounds(); - * llb.isEmpty(); // true - * llb.setNorthEast([-73.9876, 40.7661]); - * llb.setSouthWest([-73.9397, 40.8002]); - * llb.isEmpty(); // false - */ - isEmpty(): boolean { - return !(this._sw && this._ne); - } - - /** - * Check if the point is within the bounding box. - * - * @param {LngLatLike} lnglat Geographic point to check against. - * @returns {boolean} True if the point is within the bounding box. - * @example - * const llb = new mapboxgl.LngLatBounds( - * new mapboxgl.LngLat(-73.9876, 40.7661), - * new mapboxgl.LngLat(-73.9397, 40.8002) - * ); - * - * const ll = new mapboxgl.LngLat(-73.9567, 40.7789); - * - * console.log(llb.contains(ll)); // = true - */ - contains(lnglat: LngLatLike): boolean { - const {lng, lat} = LngLat.convert(lnglat); - - const containsLatitude = this._sw.lat <= lat && lat <= this._ne.lat; - let containsLongitude = this._sw.lng <= lng && lng <= this._ne.lng; - if (this._sw.lng > this._ne.lng) { // wrapped coordinates - containsLongitude = this._sw.lng >= lng && lng >= this._ne.lng; - } - - return containsLatitude && containsLongitude; - } - - /** - * Converts an array to a `LngLatBounds` object. - * - * If a `LngLatBounds` object is passed in, the function returns it unchanged. - * - * Internally, the function calls `LngLat#convert` to convert arrays to `LngLat` values. - * - * @param {LngLatBoundsLike} input An array of two coordinates to convert, or a `LngLatBounds` object to return. - * @returns {LngLatBounds} A new `LngLatBounds` object, if a conversion occurred, or the original `LngLatBounds` object. - * @example - * const arr = [[-73.9876, 40.7661], [-73.9397, 40.8002]]; - * const llb = mapboxgl.LngLatBounds.convert(arr); - * console.log(llb); // = LngLatBounds {_sw: LngLat {lng: -73.9876, lat: 40.7661}, _ne: LngLat {lng: -73.9397, lat: 40.8002}} - */ - static convert(input: LngLatBoundsLike): LngLatBounds { - if (!input || input instanceof LngLatBounds) return input; - return new LngLatBounds(input); - } -} - -/** - * A {@link LngLatBounds} object, an array of {@link LngLatLike} objects in [sw, ne] order, - * or an array of numbers in [west, south, east, north] order. - * - * @typedef {LngLatBounds | [LngLatLike, LngLatLike] | [number, number, number, number]} LngLatBoundsLike - * @example - * const v1 = new mapboxgl.LngLatBounds( - * new mapboxgl.LngLat(-73.9876, 40.7661), - * new mapboxgl.LngLat(-73.9397, 40.8002) - * ); - * const v2 = new mapboxgl.LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]); - * const v3 = [[-73.9876, 40.7661], [-73.9397, 40.8002]]; - */ -export type LngLatBoundsLike = LngLatBounds | [LngLatLike, LngLatLike] | [number, number, number, number]; - -export default LngLatBounds; diff --git a/src/render/painter.js b/src/render/painter.js index 2a4df45b03a..928b2f9cac9 100644 --- a/src/render/painter.js +++ b/src/render/painter.js @@ -217,6 +217,7 @@ class Painter { showTerrainProxyTiles: boolean; fpsWindow: number; continousRedraw: boolean; + enabledLayers: any; } _timeStamp: number; @@ -239,8 +240,16 @@ class Painter { showTerrainProxyTiles: false, fpsWindow: 30, continousRedraw:false, + enabledLayers: { + } }; + const layerTypes = ["fill", "line", "symbol", "circle", "heatmap", "fill-extrusion", "raster", "raster-particle", "hillshade", "model", "background", "sky"]; + + for (const layerType of layerTypes) { + this._debugParams.enabledLayers[layerType] = true; + } + tp.registerParameter(this._debugParams, ["Terrain"], "showTerrainProxyTiles", {}, () => { this.style.map.triggerRepaint(); }); @@ -261,6 +270,10 @@ class Painter { min: 0, max: 200 }); + // Layers + for (const layerType of layerTypes) { + tp.registerParameter(this._debugParams.enabledLayers, ["Debug", "Layers"], layerType); + } this.setup(); @@ -630,7 +643,17 @@ class Painter { this.options = options; const layers = this.style._mergedLayers; - const layerIds = this.style.order; + + const layerIds = this.style.order.filter((id) => { + const layer = layers[id]; + + if (layer.type in this._debugParams.enabledLayers) { + return this._debugParams.enabledLayers[layer.type]; + } + + return true; + }); + const orderedLayers = layerIds.map(id => layers[id]); const sourceCaches = this.style._mergedSourceCaches; diff --git a/src/render/raster_particle_state.js b/src/render/raster_particle_state.js index 3aceb794642..63aa3b4b3ac 100644 --- a/src/render/raster_particle_state.js +++ b/src/render/raster_particle_state.js @@ -63,17 +63,26 @@ class RasterParticleState { const invScale = 1.0 / PARTICLE_POS_SCALE; const srand = mulberry32(id.key); - // Encode random particle positions into RGBA pixels + // Pack random positions in [0, 1] into RGBA pixels. Matches the GLSL + // `pack_pos_to_rgba` behavior. for (let i = 0; i < particlePositions.length; i += 4) { - // Decoded positions in shader: (PARTICLE_POS_SCALE * p - PARTICLE_POS_OFFSET), where p ∈ [0, 1]. - // x, y are the inverse of the decoded position. const x = invScale * (srand() + PARTICLE_POS_OFFSET); const y = invScale * (srand() + PARTICLE_POS_OFFSET); - // Encode fractional part into RG, integral part into BA. - particlePositions[i + 0] = Math.floor(256 * ((255 * x) % 1)); - particlePositions[i + 1] = Math.floor(256 * ((255 * y) % 1)); - particlePositions[i + 2] = Math.floor(256 * x); - particlePositions[i + 3] = Math.floor(256 * y); + + const rx = x; + const ry = (x * 255.0) % 1; + const rz = y; + const rw = (y * 255.0) % 1; + + const px = rx - ry / 255.0; + const py = ry; + const pz = rz - rw / 255.0; + const pw = rw; + + particlePositions[i + 0] = Math.floor(255.0 * px); + particlePositions[i + 1] = Math.floor(255.0 * py); + particlePositions[i + 2] = Math.floor(255.0 * pz); + particlePositions[i + 3] = Math.floor(255.0 * pw); } const particleImage = new RGBAImage({width: particleTextureDimension, height: particleTextureDimension}, particlePositions); this.particleTexture0 = new Texture(this.context, particleImage, gl.RGBA, {premultiply: false, useMipmap: false}); diff --git a/src/shaders/_prelude_raster_particle.glsl b/src/shaders/_prelude_raster_particle.glsl index 05264b930a4..dca4d3db2ad 100644 --- a/src/shaders/_prelude_raster_particle.glsl +++ b/src/shaders/_prelude_raster_particle.glsl @@ -1,14 +1,14 @@ #ifdef RASTER_ARRAY uniform sampler2D u_velocity; -uniform vec2 u_velocity_res; -uniform float u_max_speed; +uniform mediump vec2 u_velocity_res; +uniform mediump float u_max_speed; const vec4 NO_DATA = vec4(1); const vec2 INVALID_VELOCITY = vec2(-1); -uniform vec2 u_uv_offset; -uniform float u_data_offset; -uniform vec4 u_data_scale; +uniform highp vec2 u_uv_offset; +uniform highp float u_data_offset; +uniform highp vec4 u_data_scale; ivec4 rasterArrayLinearCoord(highp vec2 texCoord, highp vec2 texResolution, out highp vec2 fxy) { texCoord = texCoord * texResolution - 0.5; @@ -17,14 +17,14 @@ ivec4 rasterArrayLinearCoord(highp vec2 texCoord, highp vec2 texResolution, out return ivec4(texCoord.xxyy + vec2(1.5, 0.5).xyxy); } -vec2 lookup_velocity(vec2 uv) { +highp vec2 lookup_velocity(highp vec2 uv) { uv = u_uv_offset.x + u_uv_offset.y * uv; - vec2 fxy; + highp vec2 fxy; ivec4 c = rasterArrayLinearCoord(uv, u_velocity_res, fxy); - vec4 tl = texelFetch(u_velocity, c.yz, 0); - vec4 tr = texelFetch(u_velocity, c.xz, 0); - vec4 bl = texelFetch(u_velocity, c.yw, 0); - vec4 br = texelFetch(u_velocity, c.xw, 0); + highp vec4 tl = texelFetch(u_velocity, c.yz, 0); + highp vec4 tr = texelFetch(u_velocity, c.xz, 0); + highp vec4 bl = texelFetch(u_velocity, c.yw, 0); + highp vec4 br = texelFetch(u_velocity, c.xw, 0); if (tl == NO_DATA) { return INVALID_VELOCITY; @@ -39,15 +39,10 @@ vec2 lookup_velocity(vec2 uv) { return INVALID_VELOCITY; } - vec4 t = mix(mix(bl, br, fxy.x), mix(tl, tr, fxy.x), fxy.y); + highp vec4 t = mix(mix(bl, br, fxy.x), mix(tl, tr, fxy.x), fxy.y); - vec2 velocity; -#ifdef DATA_FORMAT_UINT32 - velocity = vec2(u_data_offset + dot(t, u_data_scale), 0); -#else - velocity = vec2(u_data_offset + dot(t.rg, u_data_scale.yx), -(u_data_offset + dot(t.ba, u_data_scale.yx))); + highp vec2 velocity = vec2(u_data_offset + dot(t.rg, u_data_scale.yx), -(u_data_offset + dot(t.ba, u_data_scale.yx))); velocity /= max(u_max_speed, length(velocity)); -#endif return velocity; } #endif @@ -55,17 +50,15 @@ vec2 lookup_velocity(vec2 uv) { uniform highp float u_particle_pos_scale; uniform highp vec2 u_particle_pos_offset; -vec2 decode_pos(vec4 pixel) { - highp vec2 p = vec2( - pixel.r / 255.0 + pixel.b, - pixel.g / 255.0 + pixel.a); - - return u_particle_pos_scale * p - u_particle_pos_offset; +// Fixed packing code from: https://github.com/mrdoob/three.js/pull/17935 +highp vec4 pack_pos_to_rgba(highp vec2 p) { + highp vec2 v = (p + u_particle_pos_offset) / u_particle_pos_scale; + highp vec4 r = vec4(v.x, fract(v.x * 255.0), v.y, fract(v.y * 255.0)); + return vec4(r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w); } -vec4 encode_pos(vec2 pos) { - highp vec2 p = (pos + u_particle_pos_offset) / u_particle_pos_scale; - return vec4( - fract(p * 255.0), - floor(p * 255.0) / 255.0); +highp vec2 unpack_pos_from_rgba(highp vec4 v) { + v = floor(v * 255.0 + 0.5) / 255.0; + highp vec2 p = vec2(v.x + (v.y / 255.0), v.z + (v.w / 255.0)); + return u_particle_pos_scale * p - u_particle_pos_offset; } diff --git a/src/shaders/fill_extrusion.vertex.glsl b/src/shaders/fill_extrusion.vertex.glsl index 6e2a3ee9168..4e9baf9d933 100644 --- a/src/shaders/fill_extrusion.vertex.glsl +++ b/src/shaders/fill_extrusion.vertex.glsl @@ -37,7 +37,7 @@ uniform mat4 u_light_matrix_1; out highp vec4 v_pos_light_view_0; out highp vec4 v_pos_light_view_1; -out float v_depth; + #endif #if defined(ZERO_ROOF_RADIUS) && !defined(LIGHTING_3D_MODE) diff --git a/src/shaders/line_pattern.fragment.glsl b/src/shaders/line_pattern.fragment.glsl index 9b1de6ad2e1..67cd563fa0d 100644 --- a/src/shaders/line_pattern.fragment.glsl +++ b/src/shaders/line_pattern.fragment.glsl @@ -38,30 +38,29 @@ void main() { vec2 display_size = (pattern_br - pattern_tl) / pixel_ratio; + vec2 pattern_size = vec2(display_size.x / u_tile_units_to_pixels, display_size.y); + float aspect = display_size.y / v_width; - float pattern_size_x = display_size.x / (u_tile_units_to_pixels * aspect); // Calculate the distance of the pixel from the line in pixels. - float dist = length(v_normal) * v_width2.x; + float dist = length(v_normal) * v_width2.s; // Calculate the antialiasing fade factor. This is either when fading in - // the line in case of an offset line (v_width2.y) or when fading out - // (v_width2.x) + // the line in case of an offset line (v_width2.t) or when fading out + // (v_width2.s) float blur2 = (blur + 1.0 / u_device_pixel_ratio) * v_gamma_scale; - float alpha = clamp(min(dist - (v_width2.y - blur2), v_width2.x - dist) / blur2, 0.0, 1.0); + float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); - float pattern_x = v_linesofar / pattern_size_x; + float pattern_x = v_linesofar / pattern_size.x * aspect; + float x = mod(pattern_x, 1.0); - float x = fract(pattern_x); float y = 0.5 * v_normal.y + 0.5; vec2 texel_size = 1.0 / u_texsize; - vec2 tl = pattern_tl * texel_size - texel_size; - vec2 br = pattern_br * texel_size + texel_size; - vec2 pos = mix(tl, br, vec2(x, y)); - float lod_pos_x = mix(tl.x, br.x, pattern_x); - vec4 color = textureLodCustom(u_image, pos, vec2(lod_pos_x, pos.y)); + vec2 pos = mix(pattern_tl * texel_size - texel_size, pattern_br * texel_size + texel_size, vec2(x, y)); + vec2 lod_pos = mix(pattern_tl * texel_size - texel_size, pattern_br * texel_size + texel_size, vec2(pattern_x, y)); + vec4 color = textureLodCustom(u_image, pos, lod_pos); #ifdef RENDER_LINE_TRIM_OFFSET // v_uv[2] and v_uv[3] are specifying the original clip range that the vertex is located in. @@ -91,11 +90,12 @@ void main() { // negative). v_pattern_data.y is not modified because we can't access overlap info for other end of the segment. // All units are tile units. // Distance from segment start point to start of first pattern instance - float segment_phase = pattern_size_x - mod((v_linesofar - v_pattern_data.x), pattern_size_x); + float pattern_len = pattern_size.x / aspect; + float segment_phase = pattern_len - mod((v_linesofar - v_pattern_data.x), pattern_len); // Step is used to check if we can fit an extra pattern cycle when considering the segment overlap at the corner - float visible_start = segment_phase - step(pattern_size_x * 0.5, segment_phase) * pattern_size_x; - float visible_end = floor((v_pattern_data.y - segment_phase) / pattern_size_x) * pattern_size_x + segment_phase; - visible_end += step(pattern_size_x * 0.5, v_pattern_data.y - visible_end) * pattern_size_x; + float visible_start = segment_phase - step(pattern_len * 0.5, segment_phase) * pattern_len; + float visible_end = floor((v_pattern_data.y - segment_phase) / pattern_len) * pattern_len + segment_phase; + visible_end += step(pattern_len * 0.5, v_pattern_data.y - visible_end) * pattern_len; if (v_pattern_data.x < visible_start || v_pattern_data.x >= visible_end) { color = vec4(0.0); diff --git a/src/shaders/line_pattern.vertex.glsl b/src/shaders/line_pattern.vertex.glsl index 583fd1c43a1..4f87813601d 100644 --- a/src/shaders/line_pattern.vertex.glsl +++ b/src/shaders/line_pattern.vertex.glsl @@ -59,10 +59,9 @@ void main() { // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. - float ANTIALIASING = 0.5 / u_device_pixel_ratio; + float ANTIALIASING = 1.0 / u_device_pixel_ratio / 2.0; - // Scale the extrusion vector down to 1<=length<=2 scale - vec2 extrude = (a_data.xy - 128.0) * scale; + vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; vec2 pos = floor(a_pos_normal * 0.5); @@ -76,13 +75,16 @@ void main() { // these transformations used to be applied in the JS and native code bases. // moved them into the shader for clarity and simplicity. - gapwidth = gapwidth * 0.5; + gapwidth = gapwidth / 2.0; + float halfwidth = width / 2.0; + offset = -1.0 * offset; float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0); - float outset = gapwidth + width * (gapwidth > 0.0 ? 1.0 : 0.5) + (width == 0.0 ? 0.0 : ANTIALIASING); + float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + (halfwidth == 0.0 ? 0.0 : ANTIALIASING); - // Scale the extrusion vector up by the line width of this vertex - mediump vec2 dist = outset * extrude; + // Scale the extrusion vector down to a normal and then up by the line width + // of this vertex. + mediump vec2 dist = outset * a_extrude * scale; // Calculate the offset when drawing a line that is to the side of the actual line. // We do this by creating a vector that points towards the extrude, but rotate @@ -90,7 +92,7 @@ void main() { // extrude vector points in another direction. mediump float u = 0.5 * a_direction; mediump float t = 1.0 - abs(u); - mediump vec2 offset2 = -offset * extrude * normal.y * mat2(t, -u, u, t); + mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); vec4 projected_extrude = u_matrix * vec4(dist * u_pixels_to_tile_units, 0.0, 0.0); gl_Position = u_matrix * vec4(pos + offset2 * u_pixels_to_tile_units, 0.0, 1.0) + projected_extrude; diff --git a/src/shaders/raster_particle_draw.fragment.glsl b/src/shaders/raster_particle_draw.fragment.glsl index 4a612d5cd9b..2e9a5168360 100644 --- a/src/shaders/raster_particle_draw.fragment.glsl +++ b/src/shaders/raster_particle_draw.fragment.glsl @@ -1,5 +1,3 @@ -precision highp float; - uniform sampler2D u_color_ramp; in float v_particle_speed; diff --git a/src/shaders/raster_particle_draw.vertex.glsl b/src/shaders/raster_particle_draw.vertex.glsl index 96ce322c5d7..b6798c63c48 100644 --- a/src/shaders/raster_particle_draw.vertex.glsl +++ b/src/shaders/raster_particle_draw.vertex.glsl @@ -13,7 +13,7 @@ void main() { mod(a_index, u_particle_texture_side_len), a_index / u_particle_texture_side_len); vec4 pixel = texelFetch(u_particle_texture, pixel_coord, 0); - vec2 pos = decode_pos(pixel) + u_tile_offset; + vec2 pos = unpack_pos_from_rgba(pixel) + u_tile_offset; vec2 tex_coord = fract(pos); vec2 velocity = lookup_velocity(tex_coord); diff --git a/src/shaders/raster_particle_update.fragment.glsl b/src/shaders/raster_particle_update.fragment.glsl index 7dc2a6babfc..65895543fda 100644 --- a/src/shaders/raster_particle_update.fragment.glsl +++ b/src/shaders/raster_particle_update.fragment.glsl @@ -1,44 +1,39 @@ #include "_prelude_raster_particle.glsl" uniform sampler2D u_particle_texture; -uniform float u_particle_texture_side_len; -uniform float u_speed_factor; -uniform float u_reset_rate; -uniform float u_rand_seed; +uniform mediump float u_particle_texture_side_len; +uniform mediump float u_speed_factor; +uniform highp float u_reset_rate; +uniform highp float u_rand_seed; -in vec2 v_tex_coord; +in highp vec2 v_tex_coord; // pseudo-random generator -const vec3 rand_constants = vec3(12.9898, 78.233, 4375.85453); -float rand(const vec2 co) { - float t = dot(rand_constants.xy, co); +const highp vec3 rand_constants = vec3(12.9898, 78.233, 4375.85453); +highp float rand(const highp vec2 co) { + highp float t = dot(rand_constants.xy, co); return fract(sin(t) * (rand_constants.z + t)); } void main() { ivec2 pixel_coord = ivec2(v_tex_coord * u_particle_texture_side_len); - vec4 pixel = texelFetch(u_particle_texture, pixel_coord, 0); - vec2 pos = decode_pos(pixel); - vec2 velocity = lookup_velocity(clamp(pos, 0.0, 1.0)); - vec2 dp; -#ifdef DATA_FORMAT_UINT32 - dp = vec2(0); -#else - dp = velocity == INVALID_VELOCITY ? vec2(0) : velocity * u_speed_factor; -#endif + highp vec4 pixel = texelFetch(u_particle_texture, pixel_coord, 0); + highp vec2 pos = unpack_pos_from_rgba(pixel); + highp vec2 velocity = lookup_velocity(clamp(pos, 0.0, 1.0)); + highp vec2 dp = velocity == INVALID_VELOCITY ? vec2(0) : velocity * u_speed_factor; pos = pos + dp; - vec2 seed = (pos + v_tex_coord) * u_rand_seed; - vec2 random_pos = vec2(rand(seed + 1.3), rand(seed + 2.1)); - float speed = velocity == INVALID_VELOCITY ? 0.0 : length(velocity); - float reset_rate_bump = speed * u_reset_rate; - vec2 particle_pos_min = -u_particle_pos_offset; - vec2 particle_pos_max = vec2(1.0) + u_particle_pos_offset; + highp vec2 seed = (pos + v_tex_coord) * u_rand_seed; + highp vec2 random_pos = vec2(rand(seed + 1.3), rand(seed + 2.1)); + highp float speed = velocity == INVALID_VELOCITY ? 0.0 : length(velocity); + highp float reset_rate_bump = speed * u_reset_rate; + highp vec2 particle_pos_min = -u_particle_pos_offset; + highp vec2 particle_pos_max = vec2(1.0) + u_particle_pos_offset; // drop rate 0: (min pos) < x < (max pos), else drop rate 1 - vec2 pos_drop_rate = vec2(1.0) - step(particle_pos_min, pos) + step(particle_pos_max, pos); - float drop_rate = max(u_reset_rate + reset_rate_bump, length(pos_drop_rate)); - float drop = step(1.0 - drop_rate, rand(seed)); - vec2 next_pos = mix(pos, random_pos, drop); + highp vec2 pos_drop_rate = vec2(1.0) - step(particle_pos_min, pos) + step(particle_pos_max, pos); + highp float drop_rate = max(u_reset_rate + reset_rate_bump, length(pos_drop_rate)); + highp float drop = step(1.0 - drop_rate, rand(seed)); + highp vec2 next_pos = mix(pos, random_pos, drop); - glFragColor = encode_pos(next_pos); + glFragColor = pack_pos_to_rgba(next_pos); } diff --git a/src/source/building_index.js b/src/source/building_index.js index 3ea09db1124..ab55af90cea 100644 --- a/src/source/building_index.js +++ b/src/source/building_index.js @@ -157,7 +157,7 @@ class BuildingIndex { const heightData = b.getHeightAtTileCoord(tileX, tileY); if (!heightData || heightData.hidden) continue; if (heightData.height === undefined && availableHeight !== undefined) return Math.min(heightData.maxHeight, availableHeight) * heightData.verticalScale; - return (heightData.height || 0) * heightData.verticalScale; + return heightData.height ? heightData.height * heightData.verticalScale : Number.NEGATIVE_INFINITY; } // If we couldn't find a bucket, return Number.NEGATIVE_INFINITY. If a layer got hidden since previous frame, place symbols on ground. return this.layersGotHidden ? 0 : Number.NEGATIVE_INFINITY; diff --git a/src/source/geojson_source.js b/src/source/geojson_source.js index 30c95e771bc..db325e3bb5f 100644 --- a/src/source/geojson_source.js +++ b/src/source/geojson_source.js @@ -147,7 +147,8 @@ class GeoJSONSource extends Evented implements Source { generateId: options.generateId || false }, clusterProperties: options.clusterProperties, - filter: options.filter + filter: options.filter, + dynamic: options.dynamic }, options.workerOptions); } @@ -190,6 +191,41 @@ class GeoJSONSource extends Evented implements Source { return this; } + /** + * Updates the existing GeoJSON data with new features and re-renders the map. + * Can only be used on sources with `dynamic: true` in options. + * Updates features by their IDs: + * + * - If there's a feature with the same ID, overwrite it. + * - If there's a feature with the same ID but the new one's geometry is `null`, remove it + * - If there's no such ID in existing data, add it as a new feature. + * + * @param {Object | string} data A GeoJSON data object or a URL to one. + * @returns {GeoJSONSource} Returns itself to allow for method chaining. + * @example + * // Update the feature with ID=123 in the existing GeoJSON source + * map.getSource('source_id').updateData({ + * "type": "FeatureCollection", + * "features": [{ + * "id": 123, + * "type": "Feature", + * "properties": {"name": "Null Island"}, + * "geometry": { + * "type": "Point", + * "coordinates": [ 0, 0 ] + * } + * }] + * }); + */ + updateData(data: GeoJSON | string): this { + if (!this._options.dynamic) { + return this.fire(new ErrorEvent(new Error("Can't call updateData on a GeoJSON source with dynamic set to false."))); + } + this._data = data; + this._updateWorkerData(true); + return this; + } + /** * For clustered sources, fetches the zoom at which the given cluster expands. * @@ -296,7 +332,7 @@ class GeoJSONSource extends Evented implements Source { * handles loading the geojson data and preparing to serve it up as tiles, * using geojson-vt or supercluster as appropriate. */ - _updateWorkerData() { + _updateWorkerData(append: boolean = false) { // if there's an earlier loadData to finish, wait until it finishes and then do another update if (this._pendingLoad) { this._coalesce = true; @@ -306,7 +342,7 @@ class GeoJSONSource extends Evented implements Source { this.fire(new Event('dataloading', {dataType: 'source'})); this._loaded = false; - const options = extend({}, this.workerOptions); + const options = extend({append}, this.workerOptions); options.scope = this.scope; const data = this._data; if (typeof data === 'string') { @@ -338,7 +374,7 @@ class GeoJSONSource extends Evented implements Source { } if (this._coalesce) { - this._updateWorkerData(); + this._updateWorkerData(append); this._coalesce = false; } }); diff --git a/src/source/geojson_worker_source.js b/src/source/geojson_worker_source.js index 8f43aa5cd16..9633e025fae 100644 --- a/src/source/geojson_worker_source.js +++ b/src/source/geojson_worker_source.js @@ -31,12 +31,14 @@ export type GeoJSONWorkerOptions = { superclusterOptions?: Object, geojsonVtOptions?: Object, clusterProperties?: Object, - filter?: Array + filter?: Array, + dynamic?: boolean }; export type LoadGeoJSONParameters = GeoJSONWorkerOptions & { request?: RequestParameters, - data?: string + data?: string, + append?: boolean }; export type LoadGeoJSON = (params: LoadGeoJSONParameters, callback: ResponseCallback) => void; @@ -92,7 +94,8 @@ function loadGeoJSONTile(params: RequestedTileParameters, callback: LoadVectorDa */ class GeoJSONWorkerSource extends VectorTileWorkerSource { loadGeoJSON: LoadGeoJSON; - _geoJSONIndex: GeoJSONIndex + _geoJSONIndex: GeoJSONIndex; + _featureMap: Map; /** * @param [loadGeoJSON] Optional method for custom loading/parsing of @@ -105,6 +108,7 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource { if (loadGeoJSON) { this.loadGeoJSON = loadGeoJSON; } + this._featureMap = new Map(); } /** @@ -131,8 +135,10 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource { this.loadGeoJSON(params, (err: ?Error, data: ?Object) => { if (err || !data) { return callback(err); + } else if (typeof data !== 'object') { return callback(new Error(`Input data given to '${params.source}' is not a valid GeoJSON object.`)); + } else { try { if (params.filter) { @@ -140,13 +146,35 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource { if (compiled.result === 'error') throw new Error(compiled.value.map(err => `${err.key}: ${err.message}`).join(', ')); - const features = data.features.filter(feature => compiled.value.evaluate({zoom: 0}, feature)); - data = {type: 'FeatureCollection', features}; + data.features = data.features.filter(feature => compiled.value.evaluate({zoom: 0}, feature)); + } + + // for GeoJSON sources that are marked as dynamic, we retain the GeoJSON data + // as a id-to-feature map so that we can later update features by id individually + if (params.dynamic) { + if (data.type === 'Feature') data = {type: 'FeatureCollection', features: [data]}; + + if (!params.append) { + this._featureMap.clear(); + } + + for (const feature of (data.features || [])) { + const id = feature.id; + if (id !== undefined) { + if (!feature.geometry) { + this._featureMap.delete(id); + } else { + this._featureMap.set(id, feature); + } + } + } + data.features = [...this._featureMap.values()]; } this._geoJSONIndex = params.cluster ? new Supercluster(getSuperclusterOptions(params)).load(data.features) : geojsonvt(data, params.geojsonVtOptions); + } catch (err) { return callback(err); } diff --git a/src/source/rtl_text_plugin.js b/src/source/rtl_text_plugin.js index ead7f58de70..9d2a201c781 100644 --- a/src/source/rtl_text_plugin.js +++ b/src/source/rtl_text_plugin.js @@ -15,8 +15,10 @@ const status = { error: 'error' }; +export type PluginStatus = $Values + export type PluginState = { - pluginStatus: $Values; + pluginStatus: PluginStatus; pluginURL: ?string }; @@ -24,7 +26,7 @@ type PluginStateSyncCallback = (state: PluginState) => void; let _completionCallback = null; //Variables defining the current state of the plugin -let pluginStatus = status.unavailable; +let pluginStatus: PluginStatus = status.unavailable; let pluginURL: ?string = null; export const triggerPluginCompletionEvent = function(error: ?Error) { @@ -44,7 +46,7 @@ function sendPluginStateToWorker() { export const evented: Evented = new Evented(); -export const getRTLTextPluginStatus = function (): string { +export const getRTLTextPluginStatus = function (): PluginStatus { return pluginStatus; }; diff --git a/src/style-spec/package.json b/src/style-spec/package.json index 43b1247cb47..0aeededf3cb 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.4.0-beta.1", + "version": "14.4.0", "author": "Mapbox", "keywords": [ "mapbox", diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index d6c813be415..b4dbe16c6d8 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -1004,11 +1004,21 @@ "generateId": { "type": "boolean", "default": false, - "doc": "Whether to generate ids for the geojson features. When enabled, the `feature.id` property will be auto assigned based on its index in the `features` array, over-writing any previous values." + "doc": "Whether to generate ids for the GeoJSON features. When enabled, the `feature.id` property will be auto assigned based on its index in the `features` array, over-writing any previous values." }, "promoteId": { "type": "promoteId", "doc": "A property to use as a feature id (for feature state). Either a property name, or an object of the form `{: }`." + }, + "dynamic": { + "type": "boolean", + "default": false, + "doc": "Whether to optimize this source for frequent data updates (e.g. animating features).", + "sdk-support": { + "basic functionality": { + "js": "3.4.0" + } + } } }, "source_video": { @@ -8691,6 +8701,29 @@ } }, "property-type": "data-constant" + }, + "model-front-cutoff": { + "type": "array", + "private": true, + "value": "number", + "property-type": "data-constant", + "transition": false, + "expression": { + "interpolated": true, + "parameters": ["zoom"] + }, + "length": 3, + "default": [0.0, 0.0, 1.0], + "minimum": [0.0, 0.0, 0.0], + "maximum": [1.0, 1.0, 1.0], + "doc": "An array for configuring the fade-out effect for the front cutoff of content on pitched map views. It contains three values: start, range and final opacity. The start parameter defines the point at which the fade-out effect begins, with smaller values causing the effect to start earlier. The range parameter specifies how long the fade-out effect will last. A value of 0.0 for range makes content disappear immediately without a fade-out effect. The final opacity determines content opacity at the end of the fade-out effect. A value of 1.0 for final opacity means that the cutoff is completely disabled.", + "sdk-support": { + "basic functionality": { + "js": "3.5.0", + "android": "11.5.0", + "ios": "11.5.0" + } + } } }, "transition": { diff --git a/src/style-spec/rollup.config.js b/src/style-spec/rollup.config.js index 9c31e0d43ca..30b78cc49a1 100644 --- a/src/style-spec/rollup.config.js +++ b/src/style-spec/rollup.config.js @@ -44,6 +44,7 @@ const config = [{ replace({ include: /\/jsonlint-lines-primitives\/lib\/jsonlint.js/, delimiters: ['', ''], + preventAssignment: true, values: { '_token_stack:': '' } diff --git a/src/style-spec/types.js b/src/style-spec/types.js index 702a57a5de9..0c695062814 100644 --- a/src/style-spec/types.js +++ b/src/style-spec/types.js @@ -93,27 +93,39 @@ export type ModelsSpecification = { export type LightSpecification = {| "anchor"?: PropertyValueSpecification<"map" | "viewport">, "position"?: PropertyValueSpecification<[number, number, number]>, + "position-transition"?: TransitionSpecification, "color"?: PropertyValueSpecification, - "intensity"?: PropertyValueSpecification + "color-transition"?: TransitionSpecification, + "intensity"?: PropertyValueSpecification, + "intensity-transition"?: TransitionSpecification |} export type TerrainSpecification = {| "source": string, - "exaggeration"?: PropertyValueSpecification + "exaggeration"?: PropertyValueSpecification, + "exaggeration-transition"?: TransitionSpecification |} export type FogSpecification = {| "range"?: PropertyValueSpecification<[number, number]>, + "range-transition"?: TransitionSpecification, "color"?: PropertyValueSpecification, + "color-transition"?: TransitionSpecification, "high-color"?: PropertyValueSpecification, + "high-color-transition"?: TransitionSpecification, "space-color"?: PropertyValueSpecification, + "space-color-transition"?: TransitionSpecification, "horizon-blend"?: PropertyValueSpecification, + "horizon-blend-transition"?: TransitionSpecification, "star-intensity"?: PropertyValueSpecification, - "vertical-range"?: PropertyValueSpecification<[number, number]> + "star-intensity-transition"?: TransitionSpecification, + "vertical-range"?: PropertyValueSpecification<[number, number]>, + "vertical-range-transition"?: TransitionSpecification |} export type CameraSpecification = {| - "camera-projection"?: PropertyValueSpecification<"perspective" | "orthographic"> + "camera-projection"?: PropertyValueSpecification<"perspective" | "orthographic">, + "camera-projection-transition"?: TransitionSpecification |} export type ProjectionSpecification = {| @@ -220,7 +232,8 @@ export type GeoJSONSourceSpecification = {| "clusterProperties"?: mixed, "lineMetrics"?: boolean, "generateId"?: boolean, - "promoteId"?: PromoteIdSpecification + "promoteId"?: PromoteIdSpecification, + "dynamic"?: boolean |} export type VideoSourceSpecification = {| @@ -258,7 +271,9 @@ export type AmbientLightSpecification = {| "id": string, "properties"?: {| "color"?: PropertyValueSpecification, - "intensity"?: PropertyValueSpecification + "color-transition"?: TransitionSpecification, + "intensity"?: PropertyValueSpecification, + "intensity-transition"?: TransitionSpecification |}, "type": "ambient" |} @@ -267,10 +282,14 @@ export type DirectionalLightSpecification = {| "id": string, "properties"?: {| "direction"?: PropertyValueSpecification<[number, number]>, + "direction-transition"?: TransitionSpecification, "color"?: PropertyValueSpecification, + "color-transition"?: TransitionSpecification, "intensity"?: PropertyValueSpecification, + "intensity-transition"?: TransitionSpecification, "cast-shadows"?: ExpressionSpecification, - "shadow-intensity"?: PropertyValueSpecification + "shadow-intensity"?: PropertyValueSpecification, + "shadow-intensity-transition"?: TransitionSpecification |}, "type": "directional" |} @@ -280,8 +299,11 @@ export type FlatLightSpecification = {| "properties"?: {| "anchor"?: PropertyValueSpecification<"map" | "viewport">, "position"?: PropertyValueSpecification<[number, number, number]>, + "position-transition"?: TransitionSpecification, "color"?: PropertyValueSpecification, - "intensity"?: PropertyValueSpecification + "color-transition"?: TransitionSpecification, + "intensity"?: PropertyValueSpecification, + "intensity-transition"?: TransitionSpecification |}, "type": "flat" |} @@ -308,12 +330,17 @@ export type FillLayerSpecification = {| "paint"?: {| "fill-antialias"?: PropertyValueSpecification, "fill-opacity"?: DataDrivenPropertyValueSpecification, + "fill-opacity-transition"?: TransitionSpecification, "fill-color"?: DataDrivenPropertyValueSpecification, + "fill-color-transition"?: TransitionSpecification, "fill-outline-color"?: DataDrivenPropertyValueSpecification, + "fill-outline-color-transition"?: TransitionSpecification, "fill-translate"?: PropertyValueSpecification<[number, number]>, + "fill-translate-transition"?: TransitionSpecification, "fill-translate-anchor"?: PropertyValueSpecification<"map" | "viewport">, "fill-pattern"?: DataDrivenPropertyValueSpecification, - "fill-emissive-strength"?: PropertyValueSpecification + "fill-emissive-strength"?: PropertyValueSpecification, + "fill-emissive-strength-transition"?: TransitionSpecification |} |} @@ -329,7 +356,7 @@ export type LineLayerSpecification = {| "filter"?: FilterSpecification, "layout"?: {| "line-cap"?: DataDrivenPropertyValueSpecification<"butt" | "round" | "square">, - "line-join"?: DataDrivenPropertyValueSpecification<"bevel" | "round" | "miter">, + "line-join"?: DataDrivenPropertyValueSpecification<"bevel" | "round" | "miter" | "none">, "line-miter-limit"?: PropertyValueSpecification, "line-round-limit"?: PropertyValueSpecification, "line-sort-key"?: DataDrivenPropertyValueSpecification, @@ -337,20 +364,30 @@ export type LineLayerSpecification = {| |}, "paint"?: {| "line-opacity"?: DataDrivenPropertyValueSpecification, + "line-opacity-transition"?: TransitionSpecification, "line-color"?: DataDrivenPropertyValueSpecification, + "line-color-transition"?: TransitionSpecification, "line-translate"?: PropertyValueSpecification<[number, number]>, + "line-translate-transition"?: TransitionSpecification, "line-translate-anchor"?: PropertyValueSpecification<"map" | "viewport">, "line-width"?: DataDrivenPropertyValueSpecification, + "line-width-transition"?: TransitionSpecification, "line-gap-width"?: DataDrivenPropertyValueSpecification, + "line-gap-width-transition"?: TransitionSpecification, "line-offset"?: DataDrivenPropertyValueSpecification, + "line-offset-transition"?: TransitionSpecification, "line-blur"?: DataDrivenPropertyValueSpecification, + "line-blur-transition"?: TransitionSpecification, "line-dasharray"?: DataDrivenPropertyValueSpecification>, "line-pattern"?: DataDrivenPropertyValueSpecification, "line-gradient"?: ExpressionSpecification, "line-trim-offset"?: [number, number], "line-emissive-strength"?: PropertyValueSpecification, + "line-emissive-strength-transition"?: TransitionSpecification, "line-border-width"?: DataDrivenPropertyValueSpecification, - "line-border-color"?: DataDrivenPropertyValueSpecification + "line-border-width-transition"?: TransitionSpecification, + "line-border-color"?: DataDrivenPropertyValueSpecification, + "line-border-color-transition"?: TransitionSpecification |} |} @@ -411,21 +448,36 @@ export type SymbolLayerSpecification = {| |}, "paint"?: {| "icon-opacity"?: DataDrivenPropertyValueSpecification, + "icon-opacity-transition"?: TransitionSpecification, "icon-emissive-strength"?: DataDrivenPropertyValueSpecification, + "icon-emissive-strength-transition"?: TransitionSpecification, "text-emissive-strength"?: DataDrivenPropertyValueSpecification, + "text-emissive-strength-transition"?: TransitionSpecification, "icon-color"?: DataDrivenPropertyValueSpecification, + "icon-color-transition"?: TransitionSpecification, "icon-halo-color"?: DataDrivenPropertyValueSpecification, + "icon-halo-color-transition"?: TransitionSpecification, "icon-halo-width"?: DataDrivenPropertyValueSpecification, + "icon-halo-width-transition"?: TransitionSpecification, "icon-halo-blur"?: DataDrivenPropertyValueSpecification, + "icon-halo-blur-transition"?: TransitionSpecification, "icon-translate"?: PropertyValueSpecification<[number, number]>, + "icon-translate-transition"?: TransitionSpecification, "icon-translate-anchor"?: PropertyValueSpecification<"map" | "viewport">, "icon-image-cross-fade"?: DataDrivenPropertyValueSpecification, + "icon-image-cross-fade-transition"?: TransitionSpecification, "text-opacity"?: DataDrivenPropertyValueSpecification, + "text-opacity-transition"?: TransitionSpecification, "text-color"?: DataDrivenPropertyValueSpecification, + "text-color-transition"?: TransitionSpecification, "text-halo-color"?: DataDrivenPropertyValueSpecification, + "text-halo-color-transition"?: TransitionSpecification, "text-halo-width"?: DataDrivenPropertyValueSpecification, + "text-halo-width-transition"?: TransitionSpecification, "text-halo-blur"?: DataDrivenPropertyValueSpecification, + "text-halo-blur-transition"?: TransitionSpecification, "text-translate"?: PropertyValueSpecification<[number, number]>, + "text-translate-transition"?: TransitionSpecification, "text-translate-anchor"?: PropertyValueSpecification<"map" | "viewport">, "icon-color-saturation"?: ExpressionSpecification, "icon-color-contrast"?: ExpressionSpecification, @@ -450,17 +502,26 @@ export type CircleLayerSpecification = {| |}, "paint"?: {| "circle-radius"?: DataDrivenPropertyValueSpecification, + "circle-radius-transition"?: TransitionSpecification, "circle-color"?: DataDrivenPropertyValueSpecification, + "circle-color-transition"?: TransitionSpecification, "circle-blur"?: DataDrivenPropertyValueSpecification, + "circle-blur-transition"?: TransitionSpecification, "circle-opacity"?: DataDrivenPropertyValueSpecification, + "circle-opacity-transition"?: TransitionSpecification, "circle-translate"?: PropertyValueSpecification<[number, number]>, + "circle-translate-transition"?: TransitionSpecification, "circle-translate-anchor"?: PropertyValueSpecification<"map" | "viewport">, "circle-pitch-scale"?: PropertyValueSpecification<"map" | "viewport">, "circle-pitch-alignment"?: PropertyValueSpecification<"map" | "viewport">, "circle-stroke-width"?: DataDrivenPropertyValueSpecification, + "circle-stroke-width-transition"?: TransitionSpecification, "circle-stroke-color"?: DataDrivenPropertyValueSpecification, + "circle-stroke-color-transition"?: TransitionSpecification, "circle-stroke-opacity"?: DataDrivenPropertyValueSpecification, - "circle-emissive-strength"?: PropertyValueSpecification + "circle-stroke-opacity-transition"?: TransitionSpecification, + "circle-emissive-strength"?: PropertyValueSpecification, + "circle-emissive-strength-transition"?: TransitionSpecification |} |} @@ -479,10 +540,13 @@ export type HeatmapLayerSpecification = {| |}, "paint"?: {| "heatmap-radius"?: DataDrivenPropertyValueSpecification, + "heatmap-radius-transition"?: TransitionSpecification, "heatmap-weight"?: DataDrivenPropertyValueSpecification, "heatmap-intensity"?: PropertyValueSpecification, + "heatmap-intensity-transition"?: TransitionSpecification, "heatmap-color"?: ExpressionSpecification, - "heatmap-opacity"?: PropertyValueSpecification + "heatmap-opacity"?: PropertyValueSpecification, + "heatmap-opacity-transition"?: TransitionSpecification |} |} @@ -502,27 +566,44 @@ export type FillExtrusionLayerSpecification = {| |}, "paint"?: {| "fill-extrusion-opacity"?: PropertyValueSpecification, + "fill-extrusion-opacity-transition"?: TransitionSpecification, "fill-extrusion-color"?: DataDrivenPropertyValueSpecification, + "fill-extrusion-color-transition"?: TransitionSpecification, "fill-extrusion-translate"?: PropertyValueSpecification<[number, number]>, + "fill-extrusion-translate-transition"?: TransitionSpecification, "fill-extrusion-translate-anchor"?: PropertyValueSpecification<"map" | "viewport">, "fill-extrusion-pattern"?: DataDrivenPropertyValueSpecification, "fill-extrusion-height"?: DataDrivenPropertyValueSpecification, + "fill-extrusion-height-transition"?: TransitionSpecification, "fill-extrusion-base"?: DataDrivenPropertyValueSpecification, + "fill-extrusion-base-transition"?: TransitionSpecification, "fill-extrusion-vertical-gradient"?: PropertyValueSpecification, "fill-extrusion-ambient-occlusion-intensity"?: PropertyValueSpecification, + "fill-extrusion-ambient-occlusion-intensity-transition"?: TransitionSpecification, "fill-extrusion-ambient-occlusion-radius"?: PropertyValueSpecification, + "fill-extrusion-ambient-occlusion-radius-transition"?: TransitionSpecification, "fill-extrusion-ambient-occlusion-wall-radius"?: PropertyValueSpecification, + "fill-extrusion-ambient-occlusion-wall-radius-transition"?: TransitionSpecification, "fill-extrusion-ambient-occlusion-ground-radius"?: PropertyValueSpecification, + "fill-extrusion-ambient-occlusion-ground-radius-transition"?: TransitionSpecification, "fill-extrusion-ambient-occlusion-ground-attenuation"?: PropertyValueSpecification, + "fill-extrusion-ambient-occlusion-ground-attenuation-transition"?: TransitionSpecification, "fill-extrusion-flood-light-color"?: PropertyValueSpecification, + "fill-extrusion-flood-light-color-transition"?: TransitionSpecification, "fill-extrusion-flood-light-intensity"?: PropertyValueSpecification, + "fill-extrusion-flood-light-intensity-transition"?: TransitionSpecification, "fill-extrusion-flood-light-wall-radius"?: DataDrivenPropertyValueSpecification, + "fill-extrusion-flood-light-wall-radius-transition"?: TransitionSpecification, "fill-extrusion-flood-light-ground-radius"?: DataDrivenPropertyValueSpecification, + "fill-extrusion-flood-light-ground-radius-transition"?: TransitionSpecification, "fill-extrusion-flood-light-ground-attenuation"?: PropertyValueSpecification, + "fill-extrusion-flood-light-ground-attenuation-transition"?: TransitionSpecification, "fill-extrusion-vertical-scale"?: PropertyValueSpecification, + "fill-extrusion-vertical-scale-transition"?: TransitionSpecification, "fill-extrusion-rounded-roof"?: PropertyValueSpecification, "fill-extrusion-cutoff-fade-range"?: ExpressionSpecification, - "fill-extrusion-emissive-strength"?: PropertyValueSpecification + "fill-extrusion-emissive-strength"?: PropertyValueSpecification, + "fill-extrusion-emissive-strength-transition"?: TransitionSpecification |} |} @@ -541,19 +622,29 @@ export type RasterLayerSpecification = {| |}, "paint"?: {| "raster-opacity"?: PropertyValueSpecification, + "raster-opacity-transition"?: TransitionSpecification, "raster-color"?: ExpressionSpecification, "raster-color-mix"?: PropertyValueSpecification<[number, number, number, number]>, + "raster-color-mix-transition"?: TransitionSpecification, "raster-color-range"?: PropertyValueSpecification<[number, number]>, + "raster-color-range-transition"?: TransitionSpecification, "raster-hue-rotate"?: PropertyValueSpecification, + "raster-hue-rotate-transition"?: TransitionSpecification, "raster-brightness-min"?: PropertyValueSpecification, + "raster-brightness-min-transition"?: TransitionSpecification, "raster-brightness-max"?: PropertyValueSpecification, + "raster-brightness-max-transition"?: TransitionSpecification, "raster-saturation"?: PropertyValueSpecification, + "raster-saturation-transition"?: TransitionSpecification, "raster-contrast"?: PropertyValueSpecification, + "raster-contrast-transition"?: TransitionSpecification, "raster-resampling"?: PropertyValueSpecification<"linear" | "nearest">, "raster-fade-duration"?: PropertyValueSpecification, "raster-emissive-strength"?: PropertyValueSpecification, + "raster-emissive-strength-transition"?: TransitionSpecification, "raster-array-band"?: string, - "raster-elevation"?: PropertyValueSpecification + "raster-elevation"?: PropertyValueSpecification, + "raster-elevation-transition"?: TransitionSpecification |} |} @@ -576,7 +667,9 @@ export type RasterParticleLayerSpecification = {| "raster-particle-color"?: ExpressionSpecification, "raster-particle-max-speed"?: number, "raster-particle-speed-factor"?: PropertyValueSpecification, + "raster-particle-speed-factor-transition"?: TransitionSpecification, "raster-particle-fade-opacity-factor"?: PropertyValueSpecification, + "raster-particle-fade-opacity-factor-transition"?: TransitionSpecification, "raster-particle-reset-rate-factor"?: number |} |} @@ -598,10 +691,15 @@ export type HillshadeLayerSpecification = {| "hillshade-illumination-direction"?: PropertyValueSpecification, "hillshade-illumination-anchor"?: PropertyValueSpecification<"map" | "viewport">, "hillshade-exaggeration"?: PropertyValueSpecification, + "hillshade-exaggeration-transition"?: TransitionSpecification, "hillshade-shadow-color"?: PropertyValueSpecification, + "hillshade-shadow-color-transition"?: TransitionSpecification, "hillshade-highlight-color"?: PropertyValueSpecification, + "hillshade-highlight-color-transition"?: TransitionSpecification, "hillshade-accent-color"?: PropertyValueSpecification, - "hillshade-emissive-strength"?: PropertyValueSpecification + "hillshade-accent-color-transition"?: TransitionSpecification, + "hillshade-emissive-strength"?: PropertyValueSpecification, + "hillshade-emissive-strength-transition"?: TransitionSpecification |} |} @@ -621,19 +719,30 @@ export type ModelLayerSpecification = {| |}, "paint"?: {| "model-opacity"?: PropertyValueSpecification, + "model-opacity-transition"?: TransitionSpecification, "model-rotation"?: DataDrivenPropertyValueSpecification<[number, number, number]>, + "model-rotation-transition"?: TransitionSpecification, "model-scale"?: DataDrivenPropertyValueSpecification<[number, number, number]>, + "model-scale-transition"?: TransitionSpecification, "model-translation"?: DataDrivenPropertyValueSpecification<[number, number, number]>, + "model-translation-transition"?: TransitionSpecification, "model-color"?: DataDrivenPropertyValueSpecification, + "model-color-transition"?: TransitionSpecification, "model-color-mix-intensity"?: DataDrivenPropertyValueSpecification, + "model-color-mix-intensity-transition"?: TransitionSpecification, "model-type"?: "common-3d" | "location-indicator", "model-cast-shadows"?: ExpressionSpecification, "model-receive-shadows"?: ExpressionSpecification, "model-ambient-occlusion-intensity"?: PropertyValueSpecification, + "model-ambient-occlusion-intensity-transition"?: TransitionSpecification, "model-emissive-strength"?: DataDrivenPropertyValueSpecification, + "model-emissive-strength-transition"?: TransitionSpecification, "model-roughness"?: DataDrivenPropertyValueSpecification, + "model-roughness-transition"?: TransitionSpecification, "model-height-based-emissive-strength-multiplier"?: DataDrivenPropertyValueSpecification<[number, number, number, number, number]>, - "model-cutoff-fade-range"?: ExpressionSpecification + "model-height-based-emissive-strength-multiplier-transition"?: TransitionSpecification, + "model-cutoff-fade-range"?: ExpressionSpecification, + "model-front-cutoff"?: PropertyValueSpecification<[number, number, number]> |} |} @@ -649,9 +758,12 @@ export type BackgroundLayerSpecification = {| |}, "paint"?: {| "background-color"?: PropertyValueSpecification, + "background-color-transition"?: TransitionSpecification, "background-pattern"?: PropertyValueSpecification, "background-opacity"?: PropertyValueSpecification, - "background-emissive-strength"?: PropertyValueSpecification + "background-opacity-transition"?: TransitionSpecification, + "background-emissive-strength"?: PropertyValueSpecification, + "background-emissive-strength-transition"?: TransitionSpecification |} |} @@ -674,7 +786,8 @@ export type SkyLayerSpecification = {| "sky-gradient"?: ExpressionSpecification, "sky-atmosphere-halo-color"?: ColorSpecification, "sky-atmosphere-color"?: ColorSpecification, - "sky-opacity"?: PropertyValueSpecification + "sky-opacity"?: PropertyValueSpecification, + "sky-opacity-transition"?: TransitionSpecification |} |} diff --git a/src/style/style.js b/src/style/style.js index cd79b910804..e641a78acfa 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -163,6 +163,7 @@ export type StyleOptions = { importsCache?: Map, resolvedImports?: Set, config?: ?ConfigSpecification, + initialConfig?: {[string]: ConfigSpecification}, configDependentLayers?: Set; }; @@ -241,6 +242,7 @@ class Style extends Evented { _brightness: ?number; _configDependentLayers: Set; _config: ?ConfigSpecification; + _initialConfig: ?{[string]: ConfigSpecification}; _buildingIndex: BuildingIndex; _transition: TransitionSpecification; @@ -337,6 +339,7 @@ class Style extends Evented { this.options = options.configOptions ? options.configOptions : new Map(); this._configDependentLayers = options.configDependentLayers ? options.configDependentLayers : new Set(); this._config = options.config; + this._initialConfig = options.initialConfig; this.dispatcher.broadcast('setReferrer', getReferrer()); @@ -573,6 +576,13 @@ class Style extends Evented { _createFragmentStyle(importSpec: ImportSpecification): Style { const scope = this.scope ? makeFQID(importSpec.id, this.scope) : importSpec.id; + // Merge import config and initial config from the Map constructor + let config; + const initialConfig = this._initialConfig && this._initialConfig[scope]; + if (importSpec.config || initialConfig) { + config = extend({}, importSpec.config, initialConfig); + } + const style = new Style(this.map, { scope, styleChanges: this._changes, @@ -585,7 +595,7 @@ class Style extends Evented { imageManager: this.imageManager, glyphManager: this.glyphManager, modelManager: this.modelManager, - config: importSpec.config, + config, configOptions: this.options, configDependentLayers: this._configDependentLayers }); @@ -627,7 +637,7 @@ class Style extends Evented { return; } - this.setConfig(this._config, schema); + this.updateConfig(this._config, schema); if (validate && emitValidationErrors(this, validateStyle(json))) { return; @@ -1737,7 +1747,7 @@ class Style extends Evented { } } - getConfigProperty(fragmentId: string, key: string): ?any { + getConfigProperty(fragmentId: string, key: string): mixed { const fragmentStyle = this.getFragmentStyle(fragmentId); if (!fragmentStyle) return null; const fqid = makeFQID(key, fragmentStyle.scope); @@ -1746,7 +1756,13 @@ class Style extends Evented { return expression ? expression.serialize() : null; } - setConfigProperty(fragmentId: string, key: string, value: any) { + setConfigProperty(fragmentId: string, key: string, value: mixed) { + const fragmentStyle = this.getFragmentStyle(fragmentId); + if (!fragmentStyle) return; + + const schema = fragmentStyle.stylesheet.schema; + if (!schema || !schema[key]) return; + const expressionParsed = createExpression(value); if (expressionParsed.result !== 'success') { emitValidationErrors(this, expressionParsed.value); @@ -1755,18 +1771,77 @@ class Style extends Evented { const expression = expressionParsed.value.expression; - const fragmentStyle = this.getFragmentStyle(fragmentId); - if (!fragmentStyle) return; - const fqid = makeFQID(key, fragmentStyle.scope); const expressions = fragmentStyle.options.get(fqid); if (!expressions) return; - this.options.set(fqid, {...expressions, value: expression}); + let defaultExpression; + const {minValue, maxValue, stepValue, type, values} = schema[key]; + const defaultExpressionParsed = createExpression(schema[key].default); + if (defaultExpressionParsed.result === 'success') { + defaultExpression = defaultExpressionParsed.value.expression; + } + + if (!defaultExpression) { + this.fire(new ErrorEvent(new Error(`No schema defined for the config option "${key}" in the "${fragmentId}" fragment.`))); + return; + } + + this.options.set(fqid, { + ...expressions, + value: expression, + default: defaultExpression, + minValue, maxValue, stepValue, type, values + }); + + this.updateConfigDependencies(); + } + + getConfig(fragmentId: string): ?ConfigSpecification { + const fragmentStyle = this.getFragmentStyle(fragmentId); + if (!fragmentStyle) return null; + + const schema = fragmentStyle.stylesheet.schema; + if (!schema) return null; + + const config = {}; + for (const key in schema) { + const fqid = makeFQID(key, fragmentStyle.scope); + const expressions = fragmentStyle.options.get(fqid); + const expression = expressions ? expressions.value || expressions.default : null; + config[key] = expression ? expression.serialize() : null; + } + + return config; + } + + setConfig(fragmentId: string, config: ?ConfigSpecification) { + const fragmentStyle = this.getFragmentStyle(fragmentId); + if (!fragmentStyle) return; + + const schema = fragmentStyle.stylesheet.schema; + fragmentStyle.updateConfig(config, schema); + + this.updateConfigDependencies(); + } + + getSchema(fragmentId: string): ?SchemaSpecification { + const fragmentStyle = this.getFragmentStyle(fragmentId); + if (!fragmentStyle) return null; + return fragmentStyle.stylesheet.schema; + } + + setSchema(fragmentId: string, schema: SchemaSpecification) { + const fragmentStyle = this.getFragmentStyle(fragmentId); + if (!fragmentStyle) return; + + fragmentStyle.stylesheet.schema = schema; + fragmentStyle.updateConfig(fragmentStyle._config, schema); + this.updateConfigDependencies(); } - setConfig(config: ?ConfigSpecification, schema: ?SchemaSpecification) { + updateConfig(config: ?ConfigSpecification, schema: ?SchemaSpecification) { this._config = config; if (!config && !schema) return; @@ -2302,6 +2377,7 @@ class Style extends Evented { version: this.stylesheet.version, name: this.stylesheet.name, metadata: this.stylesheet.metadata, + fragment: this.stylesheet.fragment, imports: this._serializeImports(), schema: this.stylesheet.schema, camera: this.stylesheet.camera, @@ -2484,7 +2560,7 @@ class Style extends Evented { return (this._flattenAndSortRenderedFeatures(sourceResults): any); } - querySourceFeatures(sourceID: string, params: ?{sourceLayer: ?string, filter: ?Array, validate?: boolean}): Array { + querySourceFeatures(sourceID: string, params: ?{sourceLayer: ?string, filter?: ?Array, validate?: boolean}): Array { if (params && params.filter) { this._validate(validateFilter, 'querySourceFeatures.filter', params.filter, null, params); } @@ -2652,7 +2728,7 @@ class Style extends Evented { return this.fog ? this.fog.get() : null; } - setFog(fogOptions: FogSpecification) { + setFog(fogOptions?: FogSpecification) { this._checkLoaded(); if (!fogOptions) { @@ -3077,7 +3153,7 @@ class Style extends Evented { const schema = fragment.style.stylesheet && fragment.style.stylesheet.schema; fragment.config = config; - fragment.style.setConfig(config, schema); + fragment.style.updateConfig(config, schema); this.updateConfigDependencies(); diff --git a/src/style/style_layer/custom_style_layer.js b/src/style/style_layer/custom_style_layer.js index b415b619bc6..a6f88c5c080 100644 --- a/src/style/style_layer/custom_style_layer.js +++ b/src/style/style_layer/custom_style_layer.js @@ -154,15 +154,15 @@ type CustomRenderMethod = (gl: WebGL2RenderingContext, matrix: Array, pr */ export type CustomLayerInterface = { id: string, - type: "custom", - slot: ?string; - renderingMode: "2d" | "3d", + type: 'custom', + slot?: string | void, + renderingMode?: '2d' | '3d', render: CustomRenderMethod, - prerender: ?CustomRenderMethod, - renderToTile: ?(gl: WebGL2RenderingContext, tileId: MercatorCoordinate) => void, - shouldRerenderTiles: ?() => boolean, - onAdd: ?(map: Map, gl: WebGL2RenderingContext) => void, - onRemove: ?(map: Map, gl: WebGL2RenderingContext) => void + prerender?: CustomRenderMethod | void, + renderToTile?: (gl: WebGL2RenderingContext, tileId: MercatorCoordinate) => void, + shouldRerenderTiles?: () => boolean, + onAdd?: (map: Map, gl: WebGL2RenderingContext) => void, + onRemove?: (map: Map, gl: WebGL2RenderingContext) => void } export function validateCustomStyleLayer(layerObject: CustomLayerInterface): ValidationErrors { diff --git a/src/tracked-parameters/internal/tracked_parameters_ui.js b/src/tracked-parameters/internal/tracked_parameters_ui.js index 3b690f19214..ad27594fc58 100644 --- a/src/tracked-parameters/internal/tracked_parameters_ui.js +++ b/src/tracked-parameters/internal/tracked_parameters_ui.js @@ -175,6 +175,7 @@ class ParameterInfo { defaultValue: any; noSave: boolean; tpBinding: any; + label: string; constructor(object: Object, parameterName: string, defaultValue: any, noSave: boolean, tpBinding: any) { this.containerObject = object; @@ -182,6 +183,7 @@ class ParameterInfo { this.defaultValue = defaultValue; this.noSave = noSave; this.tpBinding = tpBinding; + this.label = tpBinding.label ? tpBinding.label : parameterName; } } @@ -284,7 +286,7 @@ export class TrackedParameters implements ITrackedParameters { const isDefault = JSON.stringify(parameterInfo.defaultValue) === JSON.stringify(parameterInfo.containerObject[parameterInfo.parameterName]); const noSaveIndicator = parameterInfo.noSave ? "❗💾 " : ""; - parameterInfo.tpBinding.label = (isDefault ? " " : "* ") + noSaveIndicator + parameterInfo.parameterName; + parameterInfo.tpBinding.label = (isDefault ? " " : "* ") + noSaveIndicator + parameterInfo.label; const folderName = key.slice(0, key.lastIndexOf("|")); diff --git a/src/ui/camera.js b/src/ui/camera.js index 07a24065c98..aed8a7a75ee 100644 --- a/src/ui/camera.js +++ b/src/ui/camera.js @@ -66,6 +66,8 @@ type Required = ObjMap(v: V) => $NonMaybeType>; * This is useful for drawing attention to a location that is not in the screen center. * `center` is ignored if `around` is included. * @property {PaddingOptions} padding Dimensions in pixels applied on each side of the viewport for shifting the vanishing point. + * Note that when `padding` is used with `jumpTo`, `easeTo`, and `flyTo`, it also sets the global map padding as a side effect, + * affecting all subsequent camera movements until the padding is reset. * @example * // set the map's initial perspective with CameraOptions * const map = new mapboxgl.Map({ @@ -87,14 +89,15 @@ export type CameraOptions = { bearing?: number, pitch?: number, around?: LngLatLike, - padding?: PaddingOptions + padding?: PaddingOptions, + maxZoom?: number }; export type FullCameraOptions = { maxZoom: number, offset: PointLike, padding: Required -} & CameraOptions +} & CameraOptions; /** * Options common to map movement methods that involve animation, such as {@link Map#panBy} and @@ -132,12 +135,17 @@ export type FullCameraOptions = { * @see [Example: Navigate the map with game-like controls](https://docs.mapbox.com/mapbox-gl-js/example/game-controls/) */ export type AnimationOptions = { + animate?: boolean, + curve?: number, duration?: number, easing?: (_: number) => number, - offset?: PointLike, - animate?: boolean, essential?: boolean, - preloadOnly?: boolean + linear?: boolean, + maxDuration?: number, + offset?: PointLike, + preloadOnly?: boolean, + screenSpeed?: number, + speed?: number }; export type EasingOptions = CameraOptions & AnimationOptions; @@ -152,8 +160,12 @@ export type ElevationBoxRaycast = { const freeCameraNotSupportedWarning = 'map.setFreeCameraOptions(...) and map.getFreeCameraOptions() are not yet supported for non-mercator projections.'; /** - * Options for setting padding on calls to methods such as {@link Map#fitBounds}, {@link Map#fitScreenCoordinates}, and {@link Map#setPadding}. Adjust these options to set the amount of padding in pixels added to the edges of the canvas. Set a uniform padding on all edges or individual values for each edge. All properties of this object must be - * non-negative integers. + * Options for setting padding on calls to methods such as {@link Map#jumpTo}, {@link Map#easeTo}, {@link Map#flyTo}, + * {@link Map#fitBounds}, {@link Map#fitScreenCoordinates}, and {@link Map#setPadding}. Adjust these options to set + * the amount of padding in pixels added to the edges of the canvas. Set a uniform padding on all edges or individual + * values for each edge. All properties of this object must be non-negative integers. Note that when `padding` is used with + * `fitBounds`, `flyTo`, or similar methods, it also sets the global map padding as a side effect, affecting all + * subsequent camera movements until the padding is reset. * * @typedef {Object} PaddingOptions * @property {number} top Padding in pixels from the top of the map canvas. @@ -182,6 +194,7 @@ class Camera extends Evented { _zooming: boolean; _rotating: boolean; _pitching: boolean; + _padding: boolean; _bearingSnap: number; _easeStart: number; @@ -605,29 +618,25 @@ class Camera extends Evented { return this._cameraForBounds(this.transform, lnglat0, lnglat1, bearing, pitch, options); } + _extendPadding(padding: ?PaddingOptions | ?number): Required { + const defaultPadding = {top: 0, right: 0, bottom: 0, left: 0}; + if (padding == null) return extend({}, defaultPadding, this.transform.padding); + + if (typeof padding === 'number') { + return {top: padding, bottom: padding, right: padding, left: padding}; + } + + return extend({}, defaultPadding, padding); + } + _extendCameraOptions(options?: CameraOptions): FullCameraOptions { - const defaultPadding = { - top: 0, - bottom: 0, - right: 0, - left: 0 - }; options = extend({ - padding: defaultPadding, offset: [0, 0], maxZoom: this.transform.maxZoom }, options); - if (typeof options.padding === 'number') { - const p = options.padding; - options.padding = { - top: p, - bottom: p, - right: p, - left: p - }; - } - options.padding = extend(defaultPadding, options.padding); + options.padding = this._extendPadding(options.padding); + return options; } @@ -700,9 +709,13 @@ class Camera extends Evented { const cameraToWorld = mat4.invert(new Float64Array(16), worldToCamera); aabb = Aabb.applyTransform(aabb, mat4.multiply([], worldToCamera, aabbOrientation)); + const extendedAabb = this._extendAABB(aabb, tr, eOptions, bearing); + if (!extendedAabb) { + warnOnce('Map cannot fit within canvas with the given bounds, padding, and/or offset.'); + return; + } - aabb = this._extendAABBWithPaddings(aabb, eOptions, tr, bearing); - + aabb = extendedAabb; vec3.transformMat4(center, center, worldToCamera); const aabbHalfExtentZ = (aabb.max[2] - aabb.min[2]) * 0.5; @@ -738,40 +751,62 @@ class Camera extends Evented { return {center: tr.center, zoom, bearing, pitch}; } - _extendAABBWithPaddings(aabb: Aabb, eOptions: FullCameraOptions, tr: Transform, bearing: number): Aabb { - const size = vec3.sub([], aabb.max, aabb.min); + /** + * Extends the AABB with padding, offset, and bearing. + * + * @param {Aabb} aabb The AABB. + * @param {Transform} tr The transform. + * @param {FullCameraOptions} options Camera options. + * @param {number} bearing The bearing. + * @returns {Aabb | null} The extended AABB or null if couldn't scale. + * @private + */ + _extendAABB(aabb: Aabb, tr: Transform, options: FullCameraOptions, bearing: number): Aabb | null { + const padL = options.padding.left || 0; + const padR = options.padding.right || 0; + const padB = options.padding.bottom || 0; + const padT = options.padding.top || 0; + + const halfScreenPadX = (padL + padR) * 0.5; + const halfScreenPadY = (padT + padB) * 0.5; - const screenPadL = tr.padding.left || 0; - const screenPadR = tr.padding.right || 0; - const screenPadB = tr.padding.bottom || 0; - const screenPadT = tr.padding.top || 0; + const top = halfScreenPadY; + const left = halfScreenPadX; + const right = halfScreenPadX; + const bottom = halfScreenPadY; - const {left: padL, right: padR, top: padT, bottom: padB} = eOptions.padding; + const width = tr.width - (left + right); + const height = tr.height - (top + bottom); - const halfScreenPadX = (screenPadL + screenPadR) * 0.5; - const halfScreenPadY = (screenPadT + screenPadB) * 0.5; + const aabbSize: [number, number, number] = vec3.sub(([]: any), aabb.max, aabb.min); - const scaleX = (tr.width - (screenPadL + screenPadR + padL + padR)) / size[0]; - const scaleY = (tr.height - (screenPadB + screenPadT + padB + padT)) / size[1]; + const scaleX = width / aabbSize[0]; + const scaleY = height / aabbSize[1]; - const zoomRef = Math.min(tr.scaleZoom(tr.scale * Math.min(scaleX, scaleY)), eOptions.maxZoom); + const scale = Math.min(scaleX, scaleY); + + const zoomRef = Math.min(tr.scaleZoom(tr.scale * scale), options.maxZoom); + if (isNaN(zoomRef)) { + return null; + } const scaleRatio = tr.scale / tr.zoomScale(zoomRef); - aabb = new Aabb( - [aabb.min[0] - (padL + halfScreenPadX) * scaleRatio, aabb.min[1] - (padB + halfScreenPadY) * scaleRatio, aabb.min[2]], - [aabb.max[0] + (padR + halfScreenPadX) * scaleRatio, aabb.max[1] + (padT + halfScreenPadY) * scaleRatio, aabb.max[2]]); + const extendedAABB = new Aabb( + [aabb.min[0] - left * scaleRatio, aabb.min[1] - bottom * scaleRatio, aabb.min[2]], + [aabb.max[0] + right * scaleRatio, aabb.max[1] + top * scaleRatio, aabb.max[2]] + ); - const centerOffset = (typeof eOptions.offset.x === 'number' && typeof eOptions.offset.y === 'number') ? - new Point(eOptions.offset.x, eOptions.offset.y) : - Point.convert(eOptions.offset); + const centerOffset = (typeof options.offset.x === 'number' && typeof options.offset.y === 'number') ? + new Point(options.offset.x, options.offset.y) : + Point.convert(options.offset); const rotatedOffset = centerOffset.rotate(-degToRad(bearing)); - aabb.center[0] -= rotatedOffset.x * scaleRatio; - aabb.center[1] += rotatedOffset.y * scaleRatio; + extendedAABB.center[0] -= rotatedOffset.x * scaleRatio; + extendedAABB.center[1] += rotatedOffset.y * scaleRatio; - return aabb; + return extendedAABB; } /** @section {Querying features} */ @@ -817,7 +852,7 @@ class Camera extends Evented { * @param {PointLike} [options.offset=[0, 0]] The center of the given bounds relative to the map's center, measured in pixels. * @param {number} [options.maxZoom] The maximum zoom level to allow when the camera would transition to the specified bounds. * @returns {CameraOptions | void} If map is able to fit to provided bounds, returns `CameraOptions` with - * `center`, `zoom`, and `bearing`. If map is unable to fit, method will warn and return undefined. + * `center`, `zoom`, and `bearing`. If map is unable to fit, method will warn and return undefined. * @private * @example * var p0 = [-79, 43]; @@ -862,9 +897,13 @@ class Camera extends Evented { const cameraToWorld = mat4.invert(new Float64Array(16), worldToCamera); aabb = Aabb.applyTransform(aabb, worldToCamera); + const extendedAabb = this._extendAABB(aabb, tr, eOptions, bearing); + if (!extendedAabb) { + warnOnce('Map cannot fit within canvas with the given bounds, padding, and/or offset.'); + return; + } - aabb = this._extendAABBWithPaddings(aabb, eOptions, tr, bearing); - + aabb = extendedAabb; const size = vec3.sub([], aabb.max, aabb.min); const aabbHalfExtentZ = size[2] * 0.5; const frustumDistance = this._minimumAABBFrustumDistance(tr, aabb); @@ -1000,8 +1039,6 @@ class Camera extends Evented { if (!calculatedOptions) return this; options = extend(calculatedOptions, options); - // Explicitly remove the padding field because, calculatedOptions already accounts for padding by setting zoom and center accordingly. - delete options.padding; return options.linear ? this.easeTo(options, eventData) : @@ -1267,7 +1304,7 @@ class Camera extends Evented { zoom = 'zoom' in options ? +options.zoom : startZoom, bearing = 'bearing' in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing, pitch = 'pitch' in options ? +options.pitch : startPitch, - padding = 'padding' in options ? options.padding : tr.padding; + padding = this._extendPadding(options.padding); const offsetAsPoint = Point.convert(options.offset); @@ -1365,6 +1402,7 @@ class Camera extends Evented { this._zooming = zoomChanged; this._rotating = bearingChanged; this._pitching = pitchChanged; + this._padding = paddingChanged; this._easeId = options.easeId; this._prepareEase(eventData, options.noMoveStart, currently); @@ -1429,6 +1467,7 @@ class Camera extends Evented { this._zooming = false; this._rotating = false; this._pitching = false; + this._padding = false; if (wasZooming) { this.fire(new Event('zoomend', eventData)); @@ -1528,25 +1567,19 @@ class Camera extends Evented { const tr = this.transform, startZoom = this.getZoom(), startBearing = this.getBearing(), - startPitch = this.getPitch(); + startPitch = this.getPitch(), + startPadding = this.getPadding(); const zoom = 'zoom' in options ? clamp(+options.zoom, tr.minZoom, tr.maxZoom) : startZoom; const bearing = 'bearing' in options ? this._normalizeBearing(options.bearing, startBearing) : startBearing; const pitch = 'pitch' in options ? +options.pitch : startPitch; + const padding = this._extendPadding(options.padding); const scale = tr.zoomScale(zoom - startZoom); const offsetAsPoint = Point.convert(options.offset); - const pointAtOffset = tr.centerPoint.add(offsetAsPoint); + let pointAtOffset = tr.centerPoint.add(offsetAsPoint); const locationAtOffset = tr.pointLocation(pointAtOffset); - - let center = options.center; - // Calculate center with respect to padding - if (center && options.padding) { - const easingOptions = this._cameraForBounds(this.transform, center, center, bearing, pitch, options); - if (easingOptions) center = easingOptions.center; - } - - center = LngLat.convert(center || locationAtOffset); + const center = LngLat.convert(options.center || locationAtOffset); this._normalizeCenter(center); const from = tr.project(locationAtOffset); @@ -1632,6 +1665,7 @@ class Camera extends Evented { const zoomChanged = true; const bearingChanged = (startBearing !== bearing); const pitchChanged = (pitch !== startPitch); + const paddingChanged = !tr.isPaddingEqual(padding); const frame = (tr: Transform) => (k: number) => { // s: The distance traveled along the flight path, measured in ρ-screenfuls. @@ -1645,6 +1679,12 @@ class Camera extends Evented { if (pitchChanged) { tr.pitch = interpolate(startPitch, pitch, k); } + if (paddingChanged) { + tr.interpolatePadding(startPadding, padding, k); + // When padding is being applied, Transform#centerPoint is changing continuously, + // thus we need to recalculate offsetPoint every frame + pointAtOffset = tr.centerPoint.add(offsetAsPoint); + } const newCenter = k === 1 ? center : tr.unproject(from.add(delta.mult(u(s))).mult(scale)); tr.setLocationAtPoint(tr.renderWorldCopies ? newCenter.wrap() : newCenter, pointAtOffset); @@ -1666,6 +1706,7 @@ class Camera extends Evented { this._zooming = zoomChanged; this._rotating = bearingChanged; this._pitching = pitchChanged; + this._padding = paddingChanged; this._prepareEase(eventData, false); this._ease(frame(tr), () => this._afterEase(eventData), options); diff --git a/src/ui/map.js b/src/ui/map.js index f0b3a3e1344..8ede79bd9af 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -82,7 +82,9 @@ import type { PropertyValueSpecification, TransitionSpecification, CameraSpecification, - ImportSpecification + ImportSpecification, + ConfigSpecification, + SchemaSpecification } from '../style-spec/types.js'; import type StyleLayer from '../style/style_layer.js'; import type {Source} from '../source/source.js'; @@ -104,6 +106,15 @@ export interface IControl { } /* eslint-enable no-use-before-define */ +// Public API type for the Map#setStyle options +// as opposite to the internal StyleOptions type +type SetStyleOptions = { + diff?: boolean, + config?: {[string]: ConfigSpecification}, + localFontFamily: StyleOptions['localFontFamily'], + localIdeographFontFamily: StyleOptions['localIdeographFontFamily'], +} + export const AVERAGE_ELEVATION_SAMPLING_INTERVAL = 500; // ms export const AVERAGE_ELEVATION_EASE_TIME = 300; // ms export const AVERAGE_ELEVATION_EASE_THRESHOLD = 1; // meters @@ -111,6 +122,7 @@ export const AVERAGE_ELEVATION_CHANGE_THRESHOLD = 1e-4; // meters export type MapOptions = { style?: StyleSpecification | string | void, + config?: {[string]: ConfigSpecification}, hash?: boolean | string, interactive?: boolean, container: HTMLElement | string, @@ -134,7 +146,7 @@ export type MapOptions = { maxPitch?: ?number, boxZoom?: boolean, dragRotate?: boolean, - dragPan?: DragPanOptions, + dragPan?: boolean | DragPanOptions, keyboard?: boolean, doubleClickZoom?: boolean, touchZoomRotate?: boolean, @@ -160,7 +172,11 @@ export type MapOptions = { respectPrefersReducedMotion?: boolean, contextCreateOptions?: ContextOptions, devtools?: boolean, - repaint?: boolean + repaint?: boolean, + fadeDuration?: number, + localFontFamily?: string, + localIdeographFontFamily?: string, + performanceMetricsCollection?: boolean, }; const defaultMinZoom = -2; @@ -257,6 +273,21 @@ const defaultOptions = { * Tilesets hosted with Mapbox can be style-optimized if you append `?optimize=true` to the end of your style URL, like `mapbox://styles/mapbox/streets-v11?optimize=true`. * Learn more about style-optimized vector tiles in our [API documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-tiles). * + * @param {Object} [options.config=null] The initial configuration options for the style fragments. Each key in the object is a fragment ID (e.g., `basemap`) and each value is a configuration object. + * @example + * const map = new mapboxgl.Map({ + * container: 'map', + * center: [-122.420679, 37.772537], + * zoom: 13, + * style: 'mapbox://styles/mapbox/standard', + * config: { + * // Initial configuration for the Mapbox Standard style set above. By default, its ID is `basemap`. + * basemap: { + * // Here, we're setting the light preset to `night`. + * lightPreset: 'night' + * } + * } + * }); * @param {(boolean|string)} [options.hash=false] If `true`, the map's [position](https://docs.mapbox.com/help/glossary/camera) (zoom, center latitude, center longitude, bearing, and pitch) will be synced with the hash fragment of the page's URL. * For example, `http://path/to/my/page.html#2.59/39.26/53.07/-24.1/60`. * An additional string may optionally be provided to indicate a parameter-styled hash, @@ -653,7 +684,11 @@ export class Map extends Camera { if (options.style || !options.testMode) { const style = options.style || config.DEFAULT_STYLE; - this.setStyle(style, {localFontFamily: this._localFontFamily, localIdeographFontFamily: this._localIdeographFontFamily}); + this.setStyle(style, { + config: options.config, + localFontFamily: this._localFontFamily, + localIdeographFontFamily: this._localIdeographFontFamily + }); } if (options.projection) { @@ -1678,7 +1713,7 @@ export class Map extends Camera { * @see [Example: Animate the camera around a point with 3D terrain](https://docs.mapbox.com/mapbox-gl-js/example/free-camera-point/) * @see [Example: Play map locations as a slideshow](https://docs.mapbox.com/mapbox-gl-js/example/playback-locations/) */ - once(type: MapEvent, layerIds: any, listener?: Listener): this | Promise { + once(type: MapEvent, layerIds?: any, listener?: Listener): this | Promise { if (listener === undefined) { return super.once(type, layerIds); @@ -1926,7 +1961,7 @@ export class Map extends Camera { * * @see [Example: Highlight features containing similar data](https://www.mapbox.com/mapbox-gl-js/example/query-similar-features/) */ - querySourceFeatures(sourceId: string, parameters: ?{sourceLayer: ?string, filter: ?Array, validate?: boolean}): Array { + querySourceFeatures(sourceId: string, parameters: ?{sourceLayer: ?string, filter?: ?Array, validate?: boolean}): Array { if (!this._isValidId(sourceId)) { return []; } @@ -1972,26 +2007,39 @@ export class Map extends Camera { * In these ranges, font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold). * Set to `false`, to enable font settings from the map's style for these glyph ranges. * Forces a full update. + * @param {Object} [options.config=null] The initial configuration options for the style fragments. + * Each key in the object is a fragment ID (e.g., `basemap`) and each value is a configuration object. * @returns {Map} Returns itself to allow for method chaining. * * @example * map.setStyle("mapbox://styles/mapbox/streets-v11"); * * @see [Example: Change a map's style](https://www.mapbox.com/mapbox-gl-js/example/setstyle/) + * + * @example + * map.setStyle("mapbox://styles/mapbox/standard", { + * "config": { + * "basemap": { + * "lightPreset": "night" + * } + * } + * }); */ - setStyle(style: StyleSpecification | string | null, options?: {diff?: boolean} & StyleOptions): this { + setStyle(style: StyleSpecification | string | null, options?: SetStyleOptions): this { options = extend({}, {localIdeographFontFamily: this._localIdeographFontFamily, localFontFamily: this._localFontFamily}, options); - if ((options.diff !== false && + const diffNeeded = + options.diff !== false && + options.localFontFamily === this._localFontFamily && options.localIdeographFontFamily === this._localIdeographFontFamily && - options.localFontFamily === this._localFontFamily) && this.style && style) { + !options.config; // Rebuild the style from scratch if config is set + + if (this.style && style && diffNeeded) { this.style._diffStyle( style, - (e: any, isUpdateNeeded) => { + (e: Error | {error: string} | null, isUpdateNeeded) => { if (e) { - warnOnce( - `Unable to perform style diff: ${e.message || e.error || e}. Rebuilding the style from scratch.` - ); + warnOnce(`Unable to perform style diff: ${String(e.message || e.error || e)}. Rebuilding the style from scratch.`); this._updateStyle(style, options); } else if (isUpdateNeeded) { this._update(true); @@ -2017,7 +2065,7 @@ export class Map extends Camera { return str; } - _updateStyle(style: StyleSpecification | string | null, options?: {diff?: boolean} & StyleOptions): this { + _updateStyle(style: StyleSpecification | string | null, options?: SetStyleOptions): this { if (this.style) { this.style.setEventedParent(null); this.style._remove(); @@ -2025,7 +2073,15 @@ export class Map extends Camera { } if (style) { - this.style = new Style(this, options) + // Move SetStyleOptions's `config` property to + // StyleOptions's `initialConfig` for internal use + const styleOptions: StyleOptions = extend({}, options); + if (options && options.config) { + styleOptions.initialConfig = options.config; + delete styleOptions.config; + } + + this.style = new Style(this, styleOptions) .setEventedParent(this, {style: this.style}) .load(style); } @@ -2045,6 +2101,12 @@ export class Map extends Camera { /** * Returns the map's Mapbox [style](https://docs.mapbox.com/help/glossary/style/) object, a JSON object which can be used to recreate the map's style. * + * For the Mapbox Standard style or any "fragment" style (which is a style with `fragment: true` + * or a `schema` property defined), this method returns an empty style with no layers or sources. + * The original style is wrapped into an import with the ID `basemap` as a fragment style and is not intended + * to be used directly. This design ensures that user logic is not tied to style internals, allowing Mapbox + * to roll out style updates seamlessly and consistently. + * * @returns {Object} The map's style JSON object. * * @example @@ -3084,10 +3146,66 @@ export class Map extends Camera { /** @section {Style properties} */ + /** + * Returns the imported style schema. + * + * @param {string} importId The name of the imported style (e.g. `basemap`). + * @returns {*} Returns the imported style schema. + * @private + * + * @example + * map.getSchema('basemap'); + */ + getSchema(importId: string): ?SchemaSpecification { + return this.style.getSchema(importId); + } + + /** + * Sets the imported style schema value. + * + * @param {string} importId The name of the imported style (e.g. `basemap`). + * @param {SchemaSpecification} schema The imported style schema. + * @returns {Map} Returns itself to allow for method chaining. + * @private + * + * @example + * map.setSchema('basemap', {lightPreset: {type: 'string', default: 'night', values: ['day', 'night']}}); + */ + setSchema(importId: string, schema: SchemaSpecification): this { + this.style.setSchema(importId, schema); + return this._update(true); + } + + /** + * Returns the imported style configuration. + * + * @param {string} importId The name of the imported style (e.g. `basemap`). + * @returns {*} Returns the imported style configuration. + * @example + * map.getConfig('basemap'); + */ + getConfig(importId: string): ?ConfigSpecification { + return this.style.getConfig(importId); + } + + /** + * Sets the imported style configuration value. + * + * @param {string} importId The name of the imported style (e.g. `basemap`). + * @param {ConfigSpecification} config The imported style configuration value. + * @returns {Map} Returns itself to allow for method chaining. + * @example + * map.setConfig('basemap', {lightPreset: 'night', showPointOfInterestLabels: false}); + */ + setConfig(importId: string, config: ConfigSpecification): this { + this.style.setConfig(importId, config); + return this._update(true); + } + /** * Returns the value of a configuration property in the imported style. * - * @param {string} importId The name of the imported style to set the config for (e.g. `basemap`). + * @param {string} importId The name of the imported style (e.g. `basemap`). * @param {string} configName The name of the configuration property from the style. * @returns {*} Returns the value of the configuration property. * @example @@ -3229,7 +3347,7 @@ export class Map extends Camera { * // update the exaggeration for the existing terrain * map.setTerrain({'exaggeration': 2}); */ - setTerrain(terrain: TerrainSpecification): this { + setTerrain(terrain?: TerrainSpecification): this { this._lazyInitEmptyStyle(); if (!terrain && this.transform.projection.requiresDraping) { this.style.setTerrainForDraping(); @@ -3268,7 +3386,7 @@ export class Map extends Camera { * }); * @see [Example: Add fog to a map](https://docs.mapbox.com/mapbox-gl-js/example/add-fog/) */ - setFog(fog: FogSpecification): this { + setFog(fog?: FogSpecification): this { this._lazyInitEmptyStyle(); this.style.setFog(fog); return this._update(true); diff --git a/test/build/typescript/package-lock.json b/test/build/typescript/package-lock.json index 19838a45e72..d3204cc5199 100644 --- a/test/build/typescript/package-lock.json +++ b/test/build/typescript/package-lock.json @@ -22,10 +22,9 @@ }, "../../..": { "name": "@mapbox/mapbox-gl-private", - "version": "3.3.0", + "version": "3.4.0-beta.1", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { - "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", "@mapbox/mapbox-gl-supported": "^3.0.0", "@mapbox/point-geometry": "^0.1.0", @@ -49,15 +48,17 @@ "rw": "^1.3.3", "serialize-to-js": "^3.1.2", "supercluster": "^8.0.0", - "tiny-lru": "^11.2.5", + "tiny-lru": "^11.2.6", "tinyqueue": "^2.0.3", "tweakpane": "^4.0.3", "vt-pbf": "^3.1.3" }, "devDependencies": { - "@babel/core": "^7.24.4", + "@babel/core": "^7.24.5", + "@babel/eslint-parser": "^7.24.5", + "@mapbox/flow-remove-types": "^2.0.0", "@mapbox/mvt-fixtures": "^3.10.0", - "@octokit/rest": "^20.1.0", + "@octokit/rest": "^20.1.1", "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-json": "^6.1.0", @@ -65,74 +66,57 @@ "@rollup/plugin-replace": "^5.0.5", "@rollup/plugin-strip": "^3.0.4", "@rollup/plugin-terser": "^0.4.4", - "@tweakpane/core": "^2.0.3", - "@types/geojson": "^7946.0.14", - "@types/jest": "^29.5.12", - "@types/mapbox__point-geometry": "^0.1.4", - "@types/mapbox__vector-tile": "^1.3.4", - "@types/node": "^20.12.7", - "@types/offscreencanvas": "^2019.7.3", - "@types/pbf": "^3.0.5", - "@typescript-eslint/eslint-plugin": "^7.7.0", - "@typescript-eslint/parser": "^7.7.0", - "@vitest/browser": "1.2.2", + "@vitest/browser": "^1.6.0", + "@vitest/ui": "^1.6.0", "address": "^2.0.2", "browserify": "^17.0.0", "chalk": "^5.0.1", "chokidar": "^3.6.0", "cross-env": "^7.0.3", - "cssnano": "^6.1.2", + "cssnano": "^7.0.1", "d3-queue": "^3.0.7", "diff": "^5.2.0", - "dts-bundle-generator": "^9.5.0", "ejs": "^3.1.10", "envify": "^4.1.0", - "esbuild": "^0.20.0", - "eslint": "^8.57.0", + "eslint": "^7.32.0", "eslint-config-mourner": "^3.0.0", - "eslint-import-resolver-typescript": "^3.6.1", - "eslint-plugin-html": "^8.1.0", + "eslint-plugin-flowtype": "^7.0.0", + "eslint-plugin-html": "^8.1.1", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsdoc": "^48.2.3", - "flow-bin": "^0.233.0", - "glob": "^10.3.12", - "is-builtin-module": "^3.2.1", + "eslint-plugin-jsdoc": "^48.2.4", + "flow-bin": "0.191.0", + "glob": "^10.3.15", + "is-builtin-module": "^4.0.0", "jest-extended": "^4.0.2", "json-stringify-pretty-compact": "^4.0.0", "lodash.template": "^4.5.0", "mapbox-gl-styles": "^2.0.2", "minimist": "^1.2.6", "mock-geolocation": "^1.0.11", - "msw": "^2.2.11", + "msw": "^2.3.0", "node-notifier": "^10.0.1", "npm-font-open-sans": "^1.1.0", "npm-run-all": "^4.1.5", "pixelmatch": "^5.3.0", - "playwright": "^1.43.1", + "playwright": "^1.44.0", "postcss": "^8.4.38", "postcss-cli": "^11.0.0", "postcss-inline-svg": "^6.0.0", "pretty-bytes": "^6.0.0", - "puppeteer-core": "^22.6.5", + "puppeteer-core": "^22.8.1", "qrcode-terminal": "^0.12.0", "rollup": "3.29.4", - "rollup-plugin-esbuild": "^6.1.1", "rollup-plugin-unassert": "^0.6.0", - "rollup-plugin-visualizer": "^5.12.0", "serve-static": "^1.15.0", "shuffle-seed": "^1.1.6", "st": "^3.0.0", - "stylelint": "^16.3.1", + "stylelint": "^16.5.0", "stylelint-config-standard": "^36.0.0", "tape": "^5.7.5", "tape-filter": "^1.0.4", "testem": "^3.13.0", - "tsx": "^4.7.2", - "typescript": "^5.4.5", - "typescript-eslint": "^7.7.0", - "vite-plugin-arraybuffer": "^0.0.6", - "vite-plugin-node-polyfills": "^0.21.0", - "vitest": "1.2.2" + "vite-plugin-arraybuffer": "^0.0.7", + "vitest": "^1.6.0" } }, "node_modules/@discoveryjs/json-ext": { diff --git a/test/ignores/all.js b/test/ignores/all.js index 1ef6eea8e52..3781f4a7a74 100644 --- a/test/ignores/all.js +++ b/test/ignores/all.js @@ -35,7 +35,15 @@ const todo = [ // Needs port from Native // https://mapbox.atlassian.net/browse/MAPS3D-1331 - "render-tests/model-layer/landmark-shadows-cutoff-range" + "render-tests/model-layer/landmark-shadows-cutoff-range", + + // Needs port from Native + // https://mapbox.atlassian.net/browse/MAPS3D-1347 + "render-tests/model-layer/landmark-front-cutoff", + "render-tests/model-layer/landmark-front-cutoff-disabled", + "render-tests/model-layer/landmark-front-cutoff-no-fade", + "render-tests/model-layer/landmark-front-cutoff-opacity", + "render-tests/model-layer/landmark-front-cutoff-terrain" ]; const skip = [ @@ -191,7 +199,9 @@ const skip = [ "render-tests/model-layer/multiple-models-terrain", // The algorithm for raster colour gradient texels stretching needs an adjustment - "render-tests/raster-color/categorical" + "render-tests/raster-color/categorical", + + "render-tests/model-layer/landmark-front-cutoff-high-zoom", ]; export default {todo, skip}; diff --git a/test/integration/lib/operation-handlers.js b/test/integration/lib/operation-handlers.js index 8e00c58007c..6bc197479f4 100644 --- a/test/integration/lib/operation-handlers.js +++ b/test/integration/lib/operation-handlers.js @@ -112,7 +112,7 @@ export const operationHandlers = { setStyle(map, params, doneCb) { // Disable local ideograph generation (enabled by default) for // consistent local ideograph rendering using fixtures in all runs of the test suite. - map.setStyle(params[0], {localIdeographFontFamily: false}); + map.setStyle(params[0], {localIdeographFontFamily: false, ...params[1]}); doneCb(); }, pauseSource(map, params, doneCb) { @@ -192,6 +192,10 @@ export const operationHandlers = { } } doneCb(); + }, + updateGeoJSONData(map, [sourceId, data], doneCb) { + map.getSource(sourceId).updateData(data); + doneCb(); } }; diff --git a/test/integration/models/landmark/mbx-meshopt/2621-6331-14.glb b/test/integration/models/landmark/mbx-meshopt/2621-6331-14.glb new file mode 100644 index 00000000000..759a59c8375 Binary files /dev/null and b/test/integration/models/landmark/mbx-meshopt/2621-6331-14.glb differ diff --git a/test/integration/models/landmark/mbx-meshopt/8719-5686-14.glb b/test/integration/models/landmark/mbx-meshopt/8719-5686-14.glb new file mode 100644 index 00000000000..cf262ae9b36 Binary files /dev/null and b/test/integration/models/landmark/mbx-meshopt/8719-5686-14.glb differ diff --git a/test/integration/models/landmark/mbx-meshopt/8763-5126-14.glb b/test/integration/models/landmark/mbx-meshopt/8763-5126-14.glb new file mode 100644 index 00000000000..0ae8fb16e24 Binary files /dev/null and b/test/integration/models/landmark/mbx-meshopt/8763-5126-14.glb differ diff --git a/test/integration/models/landmark/mbx-meshopt/9147-5394-14.glb b/test/integration/models/landmark/mbx-meshopt/9147-5394-14.glb new file mode 100644 index 00000000000..55c7dc15430 Binary files /dev/null and b/test/integration/models/landmark/mbx-meshopt/9147-5394-14.glb differ diff --git a/test/integration/models/vector/16-10486-25326.vector.pbf b/test/integration/models/vector/16-10486-25326.vector.pbf new file mode 100644 index 00000000000..ef7b3845cd7 Binary files /dev/null and b/test/integration/models/vector/16-10486-25326.vector.pbf differ diff --git a/test/integration/models/vector/16-10487-25326.vector.pbf b/test/integration/models/vector/16-10487-25326.vector.pbf new file mode 100644 index 00000000000..a02d404dc62 Binary files /dev/null and b/test/integration/models/vector/16-10487-25326.vector.pbf differ diff --git a/test/integration/render-tests/circle-sort-key/cross-tile-sort/expected.png b/test/integration/render-tests/circle-sort-key/cross-tile-sort/expected.png new file mode 100644 index 00000000000..8cf36b85d0f Binary files /dev/null and b/test/integration/render-tests/circle-sort-key/cross-tile-sort/expected.png differ diff --git a/test/integration/render-tests/circle-sort-key/cross-tile-sort/style.json b/test/integration/render-tests/circle-sort-key/cross-tile-sort/style.json new file mode 100644 index 00000000000..4bbab6cb6ad --- /dev/null +++ b/test/integration/render-tests/circle-sort-key/cross-tile-sort/style.json @@ -0,0 +1,119 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 128, + "width": 128 + } + }, + "center": [0, 0], + "zoom": 5, + "sources": { + "geojson": { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "sort-key": 1, + "color": "red" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.2, + -0.6 + ] + } + }, + { + "type": "Feature", + "properties": { + "sort-key": 2, + "color": "orange" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 0.2, + -0.4 + ] + } + }, + { + "type": "Feature", + "properties": { + "sort-key": 3, + "color": "yellow" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.2, + -0.2 + ] + } + }, + { + "type": "Feature", + "properties": { + "sort-key": 4, + "color": "green" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 0.2, + 0 + ] + } + }, + { + "type": "Feature", + "properties": { + "sort-key": 5, + "color": "blue" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -0.2, + 0.2 + ] + } + }, + { + "type": "Feature", + "properties": { + "sort-key": 6, + "color": "violet" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 0.2, + 0.4 + ] + } + } + ] + } + } + }, + "layers": [ + { + "id": "circle", + "type": "circle", + "source": "geojson", + "paint": { + "circle-radius": 16, + "circle-color": ["get", "color"] + }, + "layout": { + "circle-sort-key": ["get", "sort-key"] + } + } + ] +} diff --git a/test/integration/render-tests/fill-color/multipolygon/expected.png b/test/integration/render-tests/fill-color/multipolygon/expected.png new file mode 100644 index 00000000000..fa0c6d1ac73 Binary files /dev/null and b/test/integration/render-tests/fill-color/multipolygon/expected.png differ diff --git a/test/integration/render-tests/fill-color/multipolygon/style.json b/test/integration/render-tests/fill-color/multipolygon/style.json new file mode 100644 index 00000000000..0c8c3d86925 --- /dev/null +++ b/test/integration/render-tests/fill-color/multipolygon/style.json @@ -0,0 +1,48 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 64, + "height": 64 + } + }, + "sources": { + "test": { + "type": "geojson", + "data": { + "type": "Feature", + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [-5, -5], + [-5, 5], + [ 5, 5], + [ 5, -5] + ] + ], + [ + [ + [ 0, 0], + [ 0, 10], + [10, 10], + [10, 0] + ] + ] + ] + } + } + } + }, + "layers": [ + { + "id": "test", + "type": "fill", + "source": "test", + "paint": { + "fill-color": "red" + } + } + ] + } diff --git a/test/integration/render-tests/geojson/update/expected.png b/test/integration/render-tests/geojson/update/expected.png new file mode 100644 index 00000000000..579a4c7c610 Binary files /dev/null and b/test/integration/render-tests/geojson/update/expected.png differ diff --git a/test/integration/render-tests/geojson/update/style.json b/test/integration/render-tests/geojson/update/style.json new file mode 100644 index 00000000000..56797071fe0 --- /dev/null +++ b/test/integration/render-tests/geojson/update/style.json @@ -0,0 +1,97 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 64, + "height": 32, + "operations": [ + [ + "updateGeoJSONData", + "geojson", + { + "type": "FeatureCollection", + "features": [{ + "id": 2, + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [[-5, -10], [5, 10]] + }, + "properties": { + "color": "orange" + } + }, { + "id": 3, + "type": "Feature", + "geometry": null + }, { + "id": 4, + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [[15, -10], [15, 10]] + }, + "properties": { + "color": "green" + } + }] + } + ], + [ + "wait" + ] + ] + } + }, + "sources": { + "geojson": { + "type": "geojson", + "dynamic": true, + "data": { + "type": "FeatureCollection", + "features": [{ + "id": 1, + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [[-15, -10], [-15, 10]] + }, + "properties": { + "color": "black" + } + }, { + "id": 2, + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [[-5, -10], [-5, 10]] + }, + "properties": { + "color": "red" + } + }, { + "id": 3, + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [[5, -10], [5, 10]] + }, + "properties": { + "color": "blue" + } + }] + } + } + }, + "layers": [ + { + "id": "line", + "type": "line", + "source": "geojson", + "paint": { + "line-width": 5, + "line-color": ["get", "color"] + } + } + ] +} diff --git a/test/integration/render-tests/imports/set-style-initial-config/expected.png b/test/integration/render-tests/imports/set-style-initial-config/expected.png new file mode 100644 index 00000000000..0ca8fc24914 Binary files /dev/null and b/test/integration/render-tests/imports/set-style-initial-config/expected.png differ diff --git a/test/integration/render-tests/imports/set-style-initial-config/style.json b/test/integration/render-tests/imports/set-style-initial-config/style.json new file mode 100644 index 00000000000..2bf4e5e3635 --- /dev/null +++ b/test/integration/render-tests/imports/set-style-initial-config/style.json @@ -0,0 +1,227 @@ +{ + "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": "night" + }, + "data": { + "version": 8, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "schema": { + "lightPreset": { + "default": "dawn" + } + }, + "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" + ] + } + } + ] + } + } + ] + }, + { + "config": { + "basemap": { + "lightPreset": "day" + } + } + } + ], + ["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", + "schema": {"lightPreset": {"default": "dawn"}}, + "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"] + } + }] + } + } + ] +} diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff-disabled/expected.png b/test/integration/render-tests/model-layer/landmark-front-cutoff-disabled/expected.png new file mode 100644 index 00000000000..6211d289fa4 Binary files /dev/null and b/test/integration/render-tests/model-layer/landmark-front-cutoff-disabled/expected.png differ diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff-disabled/style.json b/test/integration/render-tests/model-layer/landmark-front-cutoff-disabled/style.json new file mode 100644 index 00000000000..c57a4a775b5 --- /dev/null +++ b/test/integration/render-tests/model-layer/landmark-front-cutoff-disabled/style.json @@ -0,0 +1,91 @@ +{ + "version": 8, + "metadata": { + "test": { + "allowed": 0.00025, + "standardDerivatives": true, + "height": 512, + "operations": [ + ["wait"] + ] + } + }, + "sources": { + "mapbox": { + "type": "vector", + "maxzoom": 14, + "tiles": [ + "local://tiles/{z}-{x}-{y}.vector.pbf" + ] + }, + "landmark": { + "type": "batched-model", + "maxzoom": 14, + "tiles": [ + "local://models/landmark/diffuse/{x}-{y}-{z}.b3dm" + ] + } + }, + "lights": [ + { + "type": "ambient", + "id": "environment", + "properties": { + "intensity": 0.2 + } + }, + { + "type": "directional", + "id": "sun_light", + "properties": { + "intensity": 0.8, + "cast-shadows": true, + "shadow-intensity": 1.0 + } + } + ], + "pitch": 45, + "bearing": 50, + "zoom": 16.2, + "center": [ + -122.394, + 37.792 + ], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#aaaaaa" + } + }, + { + "id": "land", + "type": "fill", + "source": "mapbox", + "source-layer": "water", + "paint": { + "fill-color": "lightblue" + } + }, + { + "id": "road", + "type": "line", + "source": "mapbox", + "source-layer": "road", + "paint": { + "line-color": "lightyellow", + "line-width": 10 + } + }, + { + "id": "landmark", + "type": "model", + "source": "landmark", + "minzoom": 14, + "paint": { + "model-front-cutoff": [0.0, 0.5, 1.0] + } + } + ] +} \ No newline at end of file diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff-high-zoom/expected.png b/test/integration/render-tests/model-layer/landmark-front-cutoff-high-zoom/expected.png new file mode 100644 index 00000000000..bd2be3c3896 Binary files /dev/null and b/test/integration/render-tests/model-layer/landmark-front-cutoff-high-zoom/expected.png differ diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff-high-zoom/style.json b/test/integration/render-tests/model-layer/landmark-front-cutoff-high-zoom/style.json new file mode 100644 index 00000000000..c2b782bc629 --- /dev/null +++ b/test/integration/render-tests/model-layer/landmark-front-cutoff-high-zoom/style.json @@ -0,0 +1,498 @@ +{ + "version": 8, + "metadata": { + "test": { + "allowed": 0.0005, + "standardDerivatives": true + } + }, + "lights": [ + { + "type": "ambient", + "id": "environment", + "properties": { + "intensity": 0.2 + } + }, + { + "type": "directional", + "id": "sun_light", + "properties": { + "intensity": 1, + "cast-shadows": true, + "shadow-intensity": 1.0, + "direction": [ + -40, + 30 + ] + } + } + ], + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "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}.glb" + ] + } + }, + "pitch": 60, + "zoom": 20, + "bearing": 182, + "center": [ + 11.5751, 48.1375 + ], + "layers": [ + { + "type": "background", + "paint": { + "background-color": "#555555" + }, + "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": 1.0, + "line-color": "lightyellow", + "line-width": 10 + }, + "source-layer": "road", + "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-opacity": 1.0, + "fill-extrusion-base": [ + "number", + [ + "get", + "min_height" + ] + ], + "fill-extrusion-color": [ + "rgba", + 50.0, + 150.0, + 255.0, + 1.0 + ], + "fill-extrusion-vertical-gradient": false + }, + "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" + }, + { + "minzoom": 14.0, + "type": "model", + "source": "file-system-tiles", + "paint": { + "model-color-mix-intensity": [ + "match", + [ + "get", + "part" + ], + "logo", + 0.0, + "roof", + 0.0, + 0.4589999914169312 + ], + "model-emissive-strength": [ + "match", + [ + "get", + "part" + ], + "door", + 1.0, + "logo", + 0.0, + "window", + 0.456, + 0.0 + ], + "model-scale": [ + 1.0, + 1.0, + 1.0 + ], + "model-color": [ + "match", + [ + "get", + "part" + ], + "door", + [ + "rgba", + 0.0, + 255.0, + 204.0, + 1.0 + ], + "roof", + [ + "rgba", + 178.5, + 178.5, + 178.5, + 1.0 + ], + "window", + [ + "rgba", + 0.0, + 149.0, + 230.0, + 1.0 + ], + [ + "rgba", + 255.0, + 150.0, + 100.0, + 1.0 + ] + ], + "model-opacity": 1.0, + "model-front-cutoff": [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 18.0, + [ + 1.0, + 0.5, + 0.0 + ], + 20.0, + [ + 0.0, + 0.5, + 0.5 + ] + ], + "model-roughness": [ + "match", + [ + "get", + "part" + ], + "window", + 0.14, + "roof", + 0.3, + 1.0 + ] + }, + "id": "landmarks" + } + ] +} diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff-no-fade/expected.png b/test/integration/render-tests/model-layer/landmark-front-cutoff-no-fade/expected.png new file mode 100644 index 00000000000..534d51cc7f3 Binary files /dev/null and b/test/integration/render-tests/model-layer/landmark-front-cutoff-no-fade/expected.png differ diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff-no-fade/style.json b/test/integration/render-tests/model-layer/landmark-front-cutoff-no-fade/style.json new file mode 100644 index 00000000000..ac4a91150f3 --- /dev/null +++ b/test/integration/render-tests/model-layer/landmark-front-cutoff-no-fade/style.json @@ -0,0 +1,90 @@ +{ + "version": 8, + "metadata": { + "test": { + "standardDerivatives": true, + "height": 512, + "operations": [ + ["wait"] + ] + } + }, + "sources": { + "mapbox": { + "type": "vector", + "maxzoom": 14, + "tiles": [ + "local://tiles/{z}-{x}-{y}.vector.pbf" + ] + }, + "landmark": { + "type": "batched-model", + "maxzoom": 14, + "tiles": [ + "local://models/landmark/diffuse/{x}-{y}-{z}.b3dm" + ] + } + }, + "lights": [ + { + "type": "ambient", + "id": "environment", + "properties": { + "intensity": 0.2 + } + }, + { + "type": "directional", + "id": "sun_light", + "properties": { + "intensity": 0.8, + "cast-shadows": true, + "shadow-intensity": 1.0 + } + } + ], + "pitch": 45, + "bearing": 50, + "zoom": 16.2, + "center": [ + -122.394, + 37.792 + ], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#aaaaaa" + } + }, + { + "id": "land", + "type": "fill", + "source": "mapbox", + "source-layer": "water", + "paint": { + "fill-color": "lightblue" + } + }, + { + "id": "road", + "type": "line", + "source": "mapbox", + "source-layer": "road", + "paint": { + "line-color": "lightyellow", + "line-width": 10 + } + }, + { + "id": "landmark", + "type": "model", + "source": "landmark", + "minzoom": 14, + "paint": { + "model-front-cutoff": [0.0, 0.0, 0.0] + } + } + ] +} \ No newline at end of file diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff-opacity/expected.png b/test/integration/render-tests/model-layer/landmark-front-cutoff-opacity/expected.png new file mode 100644 index 00000000000..05af47997f1 Binary files /dev/null and b/test/integration/render-tests/model-layer/landmark-front-cutoff-opacity/expected.png differ diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff-opacity/style.json b/test/integration/render-tests/model-layer/landmark-front-cutoff-opacity/style.json new file mode 100644 index 00000000000..8607752c68b --- /dev/null +++ b/test/integration/render-tests/model-layer/landmark-front-cutoff-opacity/style.json @@ -0,0 +1,91 @@ +{ + "version": 8, + "metadata": { + "test": { + "standardDerivatives": true, + "height": 512, + "operations": [ + ["wait"] + ] + } + }, + "sources": { + "mapbox": { + "type": "vector", + "maxzoom": 14, + "tiles": [ + "local://tiles/{z}-{x}-{y}.vector.pbf" + ] + }, + "landmark": { + "type": "batched-model", + "maxzoom": 14, + "tiles": [ + "local://models/landmark/diffuse/{x}-{y}-{z}.b3dm" + ] + } + }, + "lights": [ + { + "type": "ambient", + "id": "environment", + "properties": { + "intensity": 0.2 + } + }, + { + "type": "directional", + "id": "sun_light", + "properties": { + "intensity": 0.8, + "cast-shadows": true, + "shadow-intensity": 1.0 + } + } + ], + "pitch": 45, + "bearing": 50, + "zoom": 16.2, + "center": [ + -122.394, + 37.792 + ], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#aaaaaa" + } + }, + { + "id": "land", + "type": "fill", + "source": "mapbox", + "source-layer": "water", + "paint": { + "fill-color": "lightblue" + } + }, + { + "id": "road", + "type": "line", + "source": "mapbox", + "source-layer": "road", + "paint": { + "line-color": "lightyellow", + "line-width": 10 + } + }, + { + "id": "landmark", + "type": "model", + "source": "landmark", + "minzoom": 14, + "paint": { + "model-opacity": 0.7, + "model-front-cutoff": [1.0, 1.0, 0.5] + } + } + ] +} \ No newline at end of file diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff-terrain/expected.png b/test/integration/render-tests/model-layer/landmark-front-cutoff-terrain/expected.png new file mode 100644 index 00000000000..d2711112a32 Binary files /dev/null and b/test/integration/render-tests/model-layer/landmark-front-cutoff-terrain/expected.png differ diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff-terrain/style.json b/test/integration/render-tests/model-layer/landmark-front-cutoff-terrain/style.json new file mode 100644 index 00000000000..3ba1380bc96 --- /dev/null +++ b/test/integration/render-tests/model-layer/landmark-front-cutoff-terrain/style.json @@ -0,0 +1,987 @@ +{ + "version": 8, + "metadata": { + "test": { + "allowed": 0.0002, + "width": 512, + "height": 700, + "standardDerivatives": true, + "operations" :[ + ["wait"], + ["wait"] + ] + } + }, + "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}.glb" + ] + }, + "mapbox-dem": { + "type": "raster-dem", + "tiles": [ + "local://tiles/{z}-{x}-{y}-terrain-512.png" + ], + "tileSize": 512, + "maxzoom": 11 + } + }, + "terrain": { + "source": "mapbox-dem" + }, + "transition": { + "duration": 0 + }, + "pitch": 60, + "zoom": 20.0, + "bearing": 45, + "center": [ + -118.300283, + 34.118248 + ], + "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": 1.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 + }, + "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": "building-models", + "minzoom": 14.0, + "paint": { + "model-ambient-occlusion-intensity": 0.75, + "model-color": [ + "match", + [ + "get", + "part" + ], + "roof", + [ + "hsl", + 22, + 82, + 90 + ], + "wall", + [ + "hsl", + 0, + 0, + 100 + ], + "window", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0, + [ + "hsl", + [ + "random", + 0.0, + 90.0, + [ + "id" + ] + ], + [ + "random", + 20.0, + 100.0, + [ + "id" + ] + ], + 87 + ], + 0.15, + [ + "hsl", + [ + "random", + 200.0, + 215.0, + [ + "id" + ] + ], + 100, + [ + "random", + 70.0, + 80.0, + [ + "id" + ] + ] + ] + ], + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.16, + [ + "hsla", + [ + "random", + 10.0, + 70.0, + [ + "id" + ] + ], + 55 + , + [ + "random", + 80.0, + 90.0, + [ + "id" + ] + ], + 1.0 + ], + 0.4, + "hsl(0, 100%, 100%)" + ] + ], + "model-color-mix-intensity": [ + "match", + [ + "get", + "part" + ], + "logo", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.2, + 0, + 0.4, + 0.3 + ], + 1.0 + ], + "model-emissive-strength": [ + "match", + [ + "get", + "part" + ], + "door", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.2, + 1.5, + 0.4, + 2.5 + ], + "logo", + 0.6, + "window", + [ + "random", + 0.5, + 0.8, + [ + "id" + ] + ], + 0.0 + ], + "model-height-based-emissive-strength-multiplier": [ + "match", + [ + "get", + "part" + ], + "window", + [ + "literal", + [ + 0.0, + 0.9, + 0, + 1, + 0.5 + ] + ], + [ + "literal", + [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ] + ] + ], + "model-front-cutoff": [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 19.0, + [ + 1.0, + 0.5, + 0.0 + ], + 20.0, + [ + 0.0, + 0.2, + 0.4 + ] + ], + "model-opacity": [ + "interpolate", + [ "linear" ], + [ "zoom" ], + 14.2, + 0.0, + 14.5, + 1.0 + ], + "model-roughness": [ + "match", + [ + "get", + "part" + ], + "window", + 0.0, + 1.0 + ], + "model-scale": [ + "interpolate", + [ "linear" ], + [ "zoom" ], + 14.2, + [ + 1.0, + 1.0, + 0.0 + ], + 14.5, + [ + 1.0, + 1.0, + 1.0 + ] + ], + "model-type": "common-3d" + }, + "source": "file-system-tiles", + "type": "model" + }, + { + "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%)" + ] + ] + } + } + ] +} diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff/expected.png b/test/integration/render-tests/model-layer/landmark-front-cutoff/expected.png new file mode 100644 index 00000000000..212583ba66f Binary files /dev/null and b/test/integration/render-tests/model-layer/landmark-front-cutoff/expected.png differ diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff/style.json b/test/integration/render-tests/model-layer/landmark-front-cutoff/style.json new file mode 100644 index 00000000000..5c03353452f --- /dev/null +++ b/test/integration/render-tests/model-layer/landmark-front-cutoff/style.json @@ -0,0 +1,91 @@ +{ + "version": 8, + "metadata": { + "test": { + "allowed": 0.0002, + "standardDerivatives": true, + "height": 512, + "operations": [ + ["wait"] + ] + } + }, + "sources": { + "mapbox": { + "type": "vector", + "maxzoom": 14, + "tiles": [ + "local://tiles/{z}-{x}-{y}.vector.pbf" + ] + }, + "landmark": { + "type": "batched-model", + "maxzoom": 14, + "tiles": [ + "local://models/landmark/diffuse/{x}-{y}-{z}.b3dm" + ] + } + }, + "lights": [ + { + "type": "ambient", + "id": "environment", + "properties": { + "intensity": 0.2 + } + }, + { + "type": "directional", + "id": "sun_light", + "properties": { + "intensity": 0.8, + "cast-shadows": true, + "shadow-intensity": 1.0 + } + } + ], + "pitch": 45, + "bearing": 50, + "zoom": 16.2, + "center": [ + -122.394, + 37.792 + ], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#aaaaaa" + } + }, + { + "id": "land", + "type": "fill", + "source": "mapbox", + "source-layer": "water", + "paint": { + "fill-color": "lightblue" + } + }, + { + "id": "road", + "type": "line", + "source": "mapbox", + "source-layer": "road", + "paint": { + "line-color": "lightyellow", + "line-width": 10 + } + }, + { + "id": "landmark", + "type": "model", + "source": "landmark", + "minzoom": 14, + "paint": { + "model-front-cutoff": [1.0, 0.7, 0.0] + } + } + ] +} \ No newline at end of file diff --git a/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/highlights/expected.png b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/highlights/expected.png new file mode 100644 index 00000000000..5ceed730c3c Binary files /dev/null and b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/highlights/expected.png differ diff --git a/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/highlights/style.json b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/highlights/style.json new file mode 100644 index 00000000000..54402d35d10 --- /dev/null +++ b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/highlights/style.json @@ -0,0 +1,274 @@ +{ + "version": 8, + "metadata": { + "test": { + "allowed": 0.00025, + "width": 1024, + "height": 1024 + } + }, + "lights": [ + { + "type": "ambient", + "id": "test_ambient", + "properties": { + "color": "rgba(255, 255, 255, 1)", + "intensity": 0.5 + } + }, + { + "type": "directional", + "id": "test_directional", + "properties": { + "color": "rgba(255, 255, 255, 1)", + "intensity": 0.5 + } + } + ], + "sources": { + "file-system-tiles": { + "type": "batched-model", + "maxzoom": 14, + "tiles": [ + "local://models/landmark/mbx-meshopt/{x}-{y}-{z}.glb" + ] + } + }, + "pitch": 49, + "zoom": 17.89, + "bearing": -14.4, + "center": [ + 21.002212, + 52.230135 + ], + + "layers": [ + { + "type": "background", + "paint": { + "background-color": "lightgray" + }, + "id": "land" + }, + { + "id": "building-models", + "minzoom": 14.0, + "paint": { + "model-ambient-occlusion-intensity": 0.75, + "model-color": [ + "match", + [ + "get", + "part" + ], + "roof", + [ + "hsl", + 22, + 82, + 90 + ], + "wall", + [ + "hsl", + 0, + 0, + 100 + ], + "window", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0, + [ + "hsl", + [ + "random", + 0.0, + 90.0, + [ + "id" + ] + ], + [ + "random", + 20.0, + 100.0, + [ + "id" + ] + ], + 87 + ], + 0.15, + [ + "hsl", + [ + "random", + 200.0, + 215.0, + [ + "id" + ] + ], + 100, + [ + "random", + 70.0, + 80.0, + [ + "id" + ] + ] + ] + ], + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.16, + [ + "hsla", + [ + "random", + 10.0, + 70.0, + [ + "id" + ] + ], + 55 + , + [ + "random", + 80.0, + 90.0, + [ + "id" + ] + ], + 1.0 + ], + 0.4, + "hsl(0, 100%, 100%)" + ] + ], + "model-color-mix-intensity": [ + "match", + [ + "get", + "part" + ], + "logo", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.2, + 0, + 0.4, + 0.3 + ], + 1.0 + ], + "model-emissive-strength": [ + "match", + [ + "get", + "part" + ], + "door", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.2, + 1.5, + 0.4, + 2.5 + ], + "logo", + 0.6, + "window", + [ + "random", + 0.5, + 0.8, + [ + "id" + ] + ], + 0.0 + ], + "model-height-based-emissive-strength-multiplier": [ + "match", + [ + "get", + "part" + ], + "window", + [ + "literal", + [ + 0.0, + 0.9, + 0, + 1, + 0.5 + ] + ], + [ + "literal", + [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ] + ] + ], + "model-opacity": [ + "interpolate", + [ "linear" ], + [ "zoom" ], + 14.2, + 0.0, + 14.5, + 1.0 + ], + "model-roughness": [ + "match", + [ + "get", + "part" + ], + "window", + 0.0, + 1.0 + ], + "model-scale": [ + "interpolate", + [ "linear" ], + [ "zoom" ], + 14.2, + [ + 1.0, + 1.0, + 0.0 + ], + 14.5, + [ + 1.0, + 1.0, + 1.0 + ] + ], + "model-type": "common-3d" + }, + "source": "file-system-tiles", + "type": "model" + } + ] +} \ No newline at end of file diff --git a/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/shadows-normal-offset/expected.png b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/shadows-normal-offset/expected.png new file mode 100644 index 00000000000..4a23e030a27 Binary files /dev/null and b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/shadows-normal-offset/expected.png differ diff --git a/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/shadows-normal-offset/style.json b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/shadows-normal-offset/style.json new file mode 100644 index 00000000000..c84f5d0d51a --- /dev/null +++ b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/shadows-normal-offset/style.json @@ -0,0 +1,279 @@ +{ + "version": 8, + "metadata": { + "test": { + "allowed": 0.00051, + "width": 512, + "height": 512 + } + }, + "lights": [ + { + "type": "ambient", + "id": "environment", + "properties": { + "color": "rgba(255.0, 255.0, 255.0, 1.0)", + "intensity": 0.25 + } + }, + { + "type": "directional", + "id": "sun_light", + "properties": { + "color": "rgba(255.0, 255.0, 255.0, 1.0)", + "intensity": 1.0, + "direction": [ + 190.0, + 50.0 + ], + "cast-shadows": true, + "shadow-intensity": 1.0 + } + } + ], + "sources": { + "file-system-tiles": { + "type": "batched-model", + "maxzoom": 14, + "tiles": [ + "local://models/landmark/mbx-meshopt/{x}-{y}-{z}.glb" + ] + } + }, + "pitch": 69, + "zoom": 18, + "bearing": -91.7, + "center": [ + 12.5688908, + 55.6965021 + ], + "layers": [ + { + "type": "background", + "paint": { + "background-color": "lightgray" + }, + "id": "land" + }, + { + "id": "building-models", + "minzoom": 14.0, + "paint": { + "model-ambient-occlusion-intensity": 0.75, + "model-color": [ + "match", + [ + "get", + "part" + ], + "roof", + [ + "hsl", + 22, + 82, + 90 + ], + "wall", + [ + "hsl", + 0, + 0, + 100 + ], + "window", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0, + [ + "hsl", + [ + "random", + 0.0, + 90.0, + [ + "id" + ] + ], + [ + "random", + 20.0, + 100.0, + [ + "id" + ] + ], + 87 + ], + 0.15, + [ + "hsl", + [ + "random", + 200.0, + 215.0, + [ + "id" + ] + ], + 100, + [ + "random", + 70.0, + 80.0, + [ + "id" + ] + ] + ] + ], + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.16, + [ + "hsla", + [ + "random", + 10.0, + 70.0, + [ + "id" + ] + ], + 55 + , + [ + "random", + 80.0, + 90.0, + [ + "id" + ] + ], + 1.0 + ], + 0.4, + "hsl(0, 100%, 100%)" + ] + ], + "model-color-mix-intensity": [ + "match", + [ + "get", + "part" + ], + "logo", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.2, + 0, + 0.4, + 0.3 + ], + 1.0 + ], + "model-emissive-strength": [ + "match", + [ + "get", + "part" + ], + "door", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.2, + 1.5, + 0.4, + 2.5 + ], + "logo", + 0.6, + "window", + [ + "random", + 0.5, + 0.8, + [ + "id" + ] + ], + 0.0 + ], + "model-height-based-emissive-strength-multiplier": [ + "match", + [ + "get", + "part" + ], + "window", + [ + "literal", + [ + 0.0, + 0.9, + 0, + 1, + 0.5 + ] + ], + [ + "literal", + [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ] + ] + ], + "model-opacity": [ + "interpolate", + [ "linear" ], + [ "zoom" ], + 14.2, + 0.0, + 14.5, + 1.0 + ], + "model-roughness": [ + "match", + [ + "get", + "part" + ], + "window", + 0.0, + 1.0 + ], + "model-scale": [ + "interpolate", + [ "linear" ], + [ "zoom" ], + 14.2, + [ + 1.0, + 1.0, + 0.0 + ], + 14.5, + [ + 1.0, + 1.0, + 1.0 + ] + ], + "model-type": "common-3d" + }, + "source": "file-system-tiles", + "type": "model" + } + ] +} \ No newline at end of file diff --git a/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/z-offset-v2-port/expected.png b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/z-offset-v2-port/expected.png new file mode 100644 index 00000000000..87388df5695 Binary files /dev/null and b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/z-offset-v2-port/expected.png differ diff --git a/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/z-offset-v2-port/style.json b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/z-offset-v2-port/style.json new file mode 100644 index 00000000000..84a034eb595 --- /dev/null +++ b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/z-offset-v2-port/style.json @@ -0,0 +1,730 @@ +{ + "version": 8, + "metadata": { + "test": { + "allowed": 0.0012, + "width": 1024, + "height": 1024 + } + }, + "lights": [ + { + "type": "ambient", + "id": "environment" + }, + { + "type": "directional", + "id": "sun_light", + "properties": { + "cast-shadows": true, + "intensity": 0.8599999904632569 + } + } + ], + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "sprite": "local://sprites/sprite", + "sources": { + "composite": { + "type": "vector", + "maxzoom": 16, + "tiles": [ + "local://models/vector/{z}-{x}-{y}.vector.pbf" + ] + }, + "file-system-tiles": { + "type": "batched-model", + "maxzoom": 14, + "tiles": [ + "local://models/landmark/mbx-meshopt/{x}-{y}-{z}.glb" + ] + } + }, + "transition": { + "duration": 0 + }, + "pitch": 60, + "zoom": 18.5, + "bearing": -124, + "center": [ + -122.39309057, + 37.79550852 + ], + "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": 1.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" + }, + { + "id": "building-models", + "minzoom": 14.0, + "paint": { + "model-ambient-occlusion-intensity": 0.75, + "model-color": [ + "match", + [ + "get", + "part" + ], + "roof", + [ + "hsl", + 22, + 82, + 90 + ], + "wall", + [ + "hsl", + 0, + 0, + 100 + ], + "window", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0, + [ + "hsl", + [ + "random", + 0.0, + 90.0, + [ + "id" + ] + ], + [ + "random", + 20.0, + 100.0, + [ + "id" + ] + ], + 87 + ], + 0.15, + [ + "hsl", + [ + "random", + 200.0, + 215.0, + [ + "id" + ] + ], + 100, + [ + "random", + 70.0, + 80.0, + [ + "id" + ] + ] + ] + ], + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.16, + [ + "hsla", + [ + "random", + 10.0, + 70.0, + [ + "id" + ] + ], + 55 + , + [ + "random", + 80.0, + 90.0, + [ + "id" + ] + ], + 1.0 + ], + 0.4, + "hsl(0, 100%, 100%)" + ] + ], + "model-color-mix-intensity": [ + "match", + [ + "get", + "part" + ], + "logo", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.2, + 0, + 0.4, + 0.3 + ], + 1.0 + ], + "model-emissive-strength": [ + "match", + [ + "get", + "part" + ], + "door", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.2, + 1.5, + 0.4, + 2.5 + ], + "logo", + 0.6, + "window", + [ + "random", + 0.5, + 0.8, + [ + "id" + ] + ], + 0.0 + ], + "model-height-based-emissive-strength-multiplier": [ + "match", + [ + "get", + "part" + ], + "window", + [ + "literal", + [ + 0.0, + 0.9, + 0, + 1, + 0.5 + ] + ], + [ + "literal", + [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ] + ] + ], + "model-opacity": [ + "interpolate", + [ "linear" ], + [ "zoom" ], + 14.2, + 0.0, + 14.5, + 1.0 + ], + "model-roughness": [ + "match", + [ + "get", + "part" + ], + "window", + 0.0, + 1.0 + ], + "model-type": "common-3d" + }, + "source": "file-system-tiles", + "type": "model" + }, + { + "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%)" + ] + ] + } + } + ] +} diff --git a/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/z-offset-v2/expected.png b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/z-offset-v2/expected.png new file mode 100644 index 00000000000..1498d04f924 Binary files /dev/null and b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/z-offset-v2/expected.png differ diff --git a/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/z-offset-v2/style.json b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/z-offset-v2/style.json new file mode 100644 index 00000000000..f915f66e504 --- /dev/null +++ b/test/integration/render-tests/model-layer/landmark-mbx-meshopt-quantization/z-offset-v2/style.json @@ -0,0 +1,957 @@ +{ + "version": 8, + "metadata": { + "test": { + "allowed": 0.0012, + "width": 1024, + "height": 1024 + } + }, + "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-meshopt/{x}-{y}-{z}.glb" + ] + } + }, + "transition": { + "duration": 0 + }, + "pitch": 55, + "zoom": 16.9, + "bearing": 0, + "center": [ + 11.582243, + 48.130199 + ], + "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": 1.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": "building-models", + "minzoom": 14.0, + "paint": { + "model-ambient-occlusion-intensity": 0.75, + "model-color": [ + "match", + [ + "get", + "part" + ], + "roof", + [ + "hsl", + 22, + 82, + 90 + ], + "wall", + [ + "hsl", + 0, + 0, + 100 + ], + "window", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0, + [ + "hsl", + [ + "random", + 0.0, + 90.0, + [ + "id" + ] + ], + [ + "random", + 20.0, + 100.0, + [ + "id" + ] + ], + 87 + ], + 0.15, + [ + "hsl", + [ + "random", + 200.0, + 215.0, + [ + "id" + ] + ], + 100, + [ + "random", + 70.0, + 80.0, + [ + "id" + ] + ] + ] + ], + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.16, + [ + "hsla", + [ + "random", + 10.0, + 70.0, + [ + "id" + ] + ], + 55 + , + [ + "random", + 80.0, + 90.0, + [ + "id" + ] + ], + 1.0 + ], + 0.4, + "hsl(0, 100%, 100%)" + ] + ], + "model-color-mix-intensity": [ + "match", + [ + "get", + "part" + ], + "logo", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.2, + 0, + 0.4, + 0.3 + ], + 1.0 + ], + "model-emissive-strength": [ + "match", + [ + "get", + "part" + ], + "door", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.2, + 1.5, + 0.4, + 2.5 + ], + "logo", + 0.6, + "window", + [ + "random", + 0.5, + 0.8, + [ + "id" + ] + ], + 0.0 + ], + "model-height-based-emissive-strength-multiplier": [ + "match", + [ + "get", + "part" + ], + "window", + [ + "literal", + [ + 0.0, + 0.9, + 0, + 1, + 0.5 + ] + ], + [ + "literal", + [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ] + ] + ], + "model-opacity": [ + "interpolate", + [ "linear" ], + [ "zoom" ], + 14.2, + 0.0, + 14.5, + 1.0 + ], + "model-roughness": [ + "match", + [ + "get", + "part" + ], + "window", + 0.0, + 1.0 + ], + "model-scale": [ + "interpolate", + [ "linear" ], + [ "zoom" ], + 16.0, + [ + 1.0, + 1.0, + 0.0 + ], + 17.0, + [ + 1.0, + 1.0, + 3.5 + ] + ], + "model-type": "common-3d" + }, + "source": "file-system-tiles", + "type": "model" + }, + { + "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%)" + ] + ] + } + } + ] +} diff --git a/test/integration/render-tests/raster-particle/default/expected.png b/test/integration/render-tests/raster-particle/default/expected.png index a6beb8300b6..2707c755c07 100644 Binary files a/test/integration/render-tests/raster-particle/default/expected.png and b/test/integration/render-tests/raster-particle/default/expected.png differ diff --git a/test/integration/render-tests/raster-particle/raster-particle-array-band/expected.png b/test/integration/render-tests/raster-particle/raster-particle-array-band/expected.png index f3ab8a215dc..1d24637c6dc 100644 Binary files a/test/integration/render-tests/raster-particle/raster-particle-array-band/expected.png and b/test/integration/render-tests/raster-particle/raster-particle-array-band/expected.png differ diff --git a/test/integration/tiles/11-350-817-terrain-512.png b/test/integration/tiles/11-350-817-terrain-512.png index 9aff5504624..a0871089f6c 100644 Binary files a/test/integration/tiles/11-350-817-terrain-512.png and b/test/integration/tiles/11-350-817-terrain-512.png differ diff --git a/test/integration/tiles/11-351-817-terrain-512.png b/test/integration/tiles/11-351-817-terrain-512.png index 765e917a52c..7e48a1d5ff0 100644 Binary files a/test/integration/tiles/11-351-817-terrain-512.png and b/test/integration/tiles/11-351-817-terrain-512.png differ diff --git a/test/unit/source/replacement_source.test.js b/test/unit/source/replacement_source.test.js index 1f776ad261a..7b6f41e39ac 100644 --- a/test/unit/source/replacement_source.test.js +++ b/test/unit/source/replacement_source.test.js @@ -70,6 +70,41 @@ describe('ReplacementSource', () => { }; }; + const createMockSourceFromSingleTriangle = (id, tileId, vertices) => { + const indices = [0, 1, 2]; + + const minx = Math.min(vertices[0].x, vertices[1].x, vertices[2].x); + const miny = Math.min(vertices[0].y, vertices[1].y, vertices[2].y); + const min = new Point(minx, miny); + const maxx = Math.max(vertices[0].x, vertices[1].x, vertices[2].x); + const maxy = Math.max(vertices[0].y, vertices[1].y, vertices[2].y); + const max = new Point(maxx, maxy); + + const grid = new TriangleGridIndex(vertices, indices, 6); + const footprint = {footprint: { + vertices, + indices, + grid, + min, + max + }, id: tileId}; + + const footprints = [footprint]; + return { + id, + tileId, + footprints, + + getSourceId: () => { + return id; + }, + + getFootprints: () => { + return footprints; + } + }; + }; + const createId = (z, x, y) => { return new UnwrappedTileID(0, new CanonicalTileID(z, x, y)); }; @@ -232,6 +267,31 @@ describe('ReplacementSource', () => { expect(regions[0].footprintTileId).toStrictEqual(createId(1, 0, 0)); }); + test('remove source coverted region', () => { + const replacementSource = new ReplacementSource(); + const preUpdateTime = replacementSource.updateTime; + + const triangleA = [new Point(0, 8192), new Point(8192, 0), new Point(8192, 8192)]; + const triangleB = [new Point(-100, -100), new Point(8192, -100), new Point(-100, 8192)]; + + replacementSource._setSources([ + createMockSourceFromSingleTriangle("sourceA", createId(2, 2, 2), triangleA), + createMockSourceFromSingleTriangle("sourceB", createId(2, 3, 3), triangleB) + ]); + const postUpdateTime = replacementSource.updateTime; + + expect(postUpdateTime > preUpdateTime).toBeTruthy(); + + // Change sources and expect to find regions from the existing one only + const regions = replacementSource.getReplacementRegionsForTile(createId(2, 3, 3)); + + expect(regions.length).toEqual(1); + expect(regions[0].min).toStrictEqual(new Point(-100.0, -100.0)); + expect(regions[0].max).toStrictEqual(new Point(8192.0, 8192.0)); + expect(regions[0].sourceId).toEqual("sourceB"); + expect(regions[0].footprintTileId).toStrictEqual(createId(2, 3, 3)); + }); + describe('no unnecessary updates', () => { test('Retain a source between frames', () => { // Retaining a source between frames should not trigger an update diff --git a/test/unit/style/style_imports.test.js b/test/unit/style/style_imports.test.js index b59e1d05422..5e8a10974bc 100644 --- a/test/unit/style/style_imports.test.js +++ b/test/unit/style/style_imports.test.js @@ -53,6 +53,50 @@ afterAll(() => { }); describe('Style#loadURL', () => { + test('wraps style with schema into import', async () => { + const style = new Style(new StubMap()); + + const initialStyle = createStyleJSON({ + name: 'Mapbox Standard', + schema: { + lightPreset: {type: 'string', default: 'day'} + } + }); + + networkWorker.use(http.get('/style.json', () => HttpResponse.json(initialStyle))); + + await new Promise(resolve => { + style.once('style.load', () => { + const rootStyle = style.serialize(); + expect(rootStyle.imports).toEqual([{id: 'basemap', url: '', data: initialStyle}]); + resolve(); + }); + + style.loadURL('/style.json'); + }); + }); + + test('wraps fragment into import', async () => { + const style = new Style(new StubMap()); + + const initialStyle = createStyleJSON({ + name: 'Mapbox Standard', + fragment: true, + }); + + networkWorker.use(http.get('/style.json', () => HttpResponse.json(initialStyle))); + + await new Promise(resolve => { + style.once('style.load', () => { + const rootStyle = style.serialize(); + expect(rootStyle.imports).toEqual([{id: 'basemap', url: '', data: initialStyle}]); + resolve(); + }); + + style.loadURL('/style.json'); + }); + }); + test('imports style from URL', async () => { const style = new Style(new StubMap()); diff --git a/test/unit/ui/camera.test.js b/test/unit/ui/camera.test.js index 3b9ac24e89f..96a886bc79a 100644 --- a/test/unit/ui/camera.test.js +++ b/test/unit/ui/camera.test.js @@ -775,7 +775,7 @@ describe('camera', () => { const bb = [[-133, 16], [-68, 50]]; const transform = camera.cameraForBounds(bb, {padding: {top: 10, right: 75, bottom: 50, left: 25}, duration: 0}); - expect(fixedLngLat(transform.center, 4)).toEqual({lng: -96.5558, lat: 32.0833}); + expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 34.7171}); }); test('bearing and asymmetrical padding', () => { @@ -783,7 +783,7 @@ describe('camera', () => { const bb = [[-133, 16], [-68, 50]]; const transform = camera.cameraForBounds(bb, {bearing: 90, padding: {top: 10, right: 75, bottom: 50, left: 25}, duration: 0}); - expect(fixedLngLat(transform.center, 4)).toEqual({lng: -103.3761, lat: 31.7099}); + expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 34.7171}); }); test( @@ -794,7 +794,7 @@ describe('camera', () => { const bb = [[-133, 16], [-68, 50]]; const transform = camera.cameraForBounds(bb, {bearing: 90, padding: {top: 10, right: 75, bottom: 50, left: 25}, duration: 0}); - expect(fixedLngLat(transform.center, 4)).toEqual({lng: -104.1932, lat: 30.837}); + expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 34.7171}); } ); @@ -819,7 +819,7 @@ describe('camera', () => { const bb = [[-133, 16], [-68, 50]]; const transform = camera.cameraForBounds(bb, {padding: {top: 10, right: 75, bottom: 50, left: 25}, offset: [0, 100]}); - expect(fixedLngLat(transform.center, 4)).toEqual({lng: -96.5558, lat: 44.4189}); + expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 46.6292}); }); test('bearing, asymmetrical padding, and offset', () => { @@ -827,7 +827,19 @@ describe('camera', () => { const bb = [[-133, 16], [-68, 50]]; const transform = camera.cameraForBounds(bb, {bearing: 90, padding: {top: 10, right: 75, bottom: 50, left: 25}, offset: [0, 100], duration: 0}); - expect(fixedLngLat(transform.center, 4)).toEqual({lng: -103.3761, lat: 43.0929}); + expect(fixedLngLat(transform.center, 4)).toEqual({lng: -100.5, lat: 45.6619}); + }); + + test('unable to fit', () => { + const camera = createCamera(); + const bb = [[-180, 10], [180, 50]]; + + vi.spyOn(console, 'warn').mockImplementation(() => {}); + const transform = camera.cameraForBounds(bb, {padding: 1000}); + expect(transform).toEqual(undefined); + + expect(console.warn).toHaveBeenCalledTimes(1); + expect(console.warn.mock.calls[0][0]).toMatch(/Map cannot fit/); }); }); @@ -954,7 +966,7 @@ describe('camera', () => { const bb = [[-133, 16], [-68, 50]]; camera.fitBounds(bb, {padding: {top: 10, right: 75, bottom: 50, left: 25}, duration:0}); - expect(fixedLngLat(camera.getCenter(), 4)).toEqual({lng: -96.5558, lat: 32.0833}); + expect(fixedLngLat(camera.getCenter(), 4)).toEqual({lng: -100.5, lat: 34.7171}); }); test('padding object with pitch', () => { @@ -962,28 +974,16 @@ describe('camera', () => { const bb = [[-133, 16], [-68, 50]]; camera.fitBounds(bb, {padding: {top: 10, right: 75, bottom: 50, left: 25}, duration:0, pitch: 30}); - expect(fixedLngLat(camera.getCenter(), 4)).toEqual({lng: -96.5558, lat: 32.4408}); + expect(fixedLngLat(camera.getCenter(), 4)).toEqual({lng: -100.5, lat: 34.7171}); expect(camera.getPitch()).toEqual(30); }); - test('padding does not get propagated to transform.padding', () => { + test('padding is propagated to the transform.padding', () => { const camera = createCamera(); const bb = [[-133, 16], [-68, 50]]; camera.fitBounds(bb, {padding: {top: 10, right: 75, bottom: 50, left: 25}, duration:0}); - expect(camera.transform.padding).toEqual({ - left: 0, - right: 0, - top: 0, - bottom: 0 - }); - camera.flyTo({center: [0, 0], zoom: 10, padding: {top: 10, right: 75, bottom: 50, left: 25}, animate: false}); - expect(camera.transform.padding).toStrictEqual({ - left: 0, - right: 0, - top: 0, - bottom: 0 - }); + expect(camera.transform.padding).toEqual({top: 10, right: 75, bottom: 50, left: 25}); }); test('#12450', () => { diff --git a/test/unit/ui/map/config.test.js b/test/unit/ui/map/config.test.js new file mode 100755 index 00000000000..fb31253f34c --- /dev/null +++ b/test/unit/ui/map/config.test.js @@ -0,0 +1,96 @@ +import {describe, test, expect, waitFor, createMap} from '../../../util/vitest.js'; +import {createStyle} from '../map/util.js'; + +describe('Map#config', () => { + test('config in constructor', async () => { + const map = createMap({ + style: createStyle({ + // Style with `schema` property will be wrapped into a fragment with ID `basemap` + schema: { + lightPreset: {type: 'string', default: 'day'} + } + }), + config: { + basemap: { + lightPreset: 'night' + } + }, + }); + + await waitFor(map, 'style.load'); + + expect(map.getConfigProperty('basemap', 'lightPreset')).toEqual('night'); + }); + + test('#setStyle', async () => { + const map = createMap(); + + const style = createStyle({ + schema: { + lightPreset: {type: 'string', default: 'day'} + } + }); + + map.setStyle(style, { + config: { + basemap: { + lightPreset: 'night' + } + } + }); + + await waitFor(map, 'style.load'); + + expect(map.getConfigProperty('basemap', 'lightPreset')).toEqual('night'); + }); + + test('#setConfig and #getConfig', async () => { + const map = createMap({ + style: createStyle({ + schema: { + lightPreset: {type: 'string', default: 'day'}, + showPointOfInterestLabels: {type: 'boolean', default: true} + } + }) + }); + + await waitFor(map, 'style.load'); + + expect(map.getConfig('basemap')).toEqual({ + lightPreset: 'day', + showPointOfInterestLabels: true + }); + + map.setConfig('basemap', { + lightPreset: 'night', + showPointOfInterestLabels: false + }); + + expect(map.getConfig('basemap')).toEqual({ + lightPreset: 'night', + showPointOfInterestLabels: false + }); + }); + + test('#setConfigProperty and #getConfigProperty', async () => { + const map = createMap({ + style: createStyle({ + schema: { + lightPreset: {type: 'string', default: 'day'}, + showPointOfInterestLabels: {type: 'boolean', default: true} + } + }) + }); + + await waitFor(map, 'style.load'); + + expect(map.getConfigProperty('basemap', 'lightPreset')).toEqual('day'); + expect(map.getConfigProperty('basemap', 'showPointOfInterestLabels')).toEqual(true); + + map.setConfigProperty('basemap', 'lightPreset', 'night'); + map.setConfigProperty('basemap', 'showPointOfInterestLabels', false); + + expect(map.getConfigProperty('basemap', 'lightPreset')).toEqual('night'); + expect(map.getConfigProperty('basemap', 'showPointOfInterestLabels')).toEqual(false); + }); +}); diff --git a/test/unit/ui/map/schema.test.js b/test/unit/ui/map/schema.test.js new file mode 100755 index 00000000000..4a80b6306c5 --- /dev/null +++ b/test/unit/ui/map/schema.test.js @@ -0,0 +1,32 @@ +import {describe, test, expect, waitFor, createMap} from '../../../util/vitest.js'; +import {createStyle} from '../map/util.js'; + +describe('Map#schema', () => { + test('#setSchema and #getSchema', async () => { + const map = createMap({ + style: createStyle({ + schema: { + lightPreset: {type: 'string', default: 'day'}, + showPointOfInterestLabels: {type: 'boolean', default: true} + } + }) + }); + + await waitFor(map, 'style.load'); + + expect(map.getSchema('basemap')).toEqual({ + lightPreset: {type: 'string', default: 'day'}, + showPointOfInterestLabels: {type: 'boolean', default: true} + }); + + map.setSchema('basemap', { + lightPreset: {type: 'string', default: 'night', values: ['day', 'night']}, + showPointOfInterestLabels: {type: 'boolean', default: false} + }); + + expect(map.getSchema('basemap')).toEqual({ + lightPreset: {type: 'string', default: 'night', values: ['day', 'night']}, + showPointOfInterestLabels: {type: 'boolean', default: false} + }); + }); +}); diff --git a/test/unit/ui/map/util.js b/test/unit/ui/map/util.js index 13059a77fe6..2799e107093 100644 --- a/test/unit/ui/map/util.js +++ b/test/unit/ui/map/util.js @@ -1,4 +1,4 @@ -export function createStyle() { +export function createStyle(options = {}) { return { version: 8, center: [-73.9749, 40.7736], @@ -6,7 +6,8 @@ export function createStyle() { bearing: 29, pitch: 50, sources: {}, - layers: [] + layers: [], + ...options }; } diff --git a/test/util/mockServiceWorker.js b/test/util/mockServiceWorker.js index ded1c6011d1..bdfdb11d380 100644 --- a/test/util/mockServiceWorker.js +++ b/test/util/mockServiceWorker.js @@ -8,7 +8,7 @@ * - Please do NOT serve this file on production. */ -const PACKAGE_VERSION = '2.2.11' +const PACKAGE_VERSION = '2.3.0' const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() diff --git a/vitest.config.base.js b/vitest.config.base.js index 4f61562f6f9..ffdc2a5066a 100644 --- a/vitest.config.base.js +++ b/vitest.config.base.js @@ -7,7 +7,6 @@ import {defineConfig} from 'vite'; import {createFilter} from '@rollup/pluginutils'; import alias from '@rollup/plugin-alias'; import flowRemoveTypes from '@mapbox/flow-remove-types'; -import {nodePolyfills} from 'vite-plugin-node-polyfills'; import arraybuffer from 'vite-plugin-arraybuffer'; const TYPING_DIRS = [ @@ -111,7 +110,6 @@ export default defineConfig({ ] }, plugins: [ - nodePolyfills(), flow(), glsl(['./src/shaders/*.glsl', './3d-style/shaders/*.glsl']), arraybuffer(),