update to 0.2.2
This commit is contained in:
parent
9f573b9371
commit
b285feb132
16
node_modules/.package-lock.json
generated
vendored
16
node_modules/.package-lock.json
generated
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@svrnty/ngx-open-map-wrapper",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
@ -105,9 +105,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@maplibre/maplibre-gl-style-spec": {
|
||||
"version": "24.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.2.0.tgz",
|
||||
"integrity": "sha512-cE80g83fRcBbZbQC70siOUxUK6YJ/5ZkClDZbmm+hzrUbv+J6yntkMmcpdz9DbOrWOM7FHKR5rruc6Q/hWx5cA==",
|
||||
"version": "24.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.3.0.tgz",
|
||||
"integrity": "sha512-CTJc/Nvldv+GNQuis29VnyV0TYsFTgQBY3SNagTzZ28oHDsDYJ7LwEmfick4Z30wPwI/4gXe3se8PH2IIfLx2g==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@ -224,9 +224,9 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/maplibre-gl": {
|
||||
"version": "5.7.3",
|
||||
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.7.3.tgz",
|
||||
"integrity": "sha512-T6XsjwcSfOr0vtAt4GTzx4m/vD6nrbR0+61MgMzZ9REQwILSEnhqwNpFuFbDedX6LC3ZWjZWnxN7fN/I66WoDQ==",
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.10.0.tgz",
|
||||
"integrity": "sha512-eLhlX8Fnpaoo7+uGqggZmXmZld6WrbzOJUPB7G8JB8XpminlTnrQTtXilMHduR8fsNVxrzD8yRRqEoajONc8LQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@ -237,7 +237,7 @@
|
||||
"@mapbox/unitbezier": "^0.0.1",
|
||||
"@mapbox/vector-tile": "^2.0.4",
|
||||
"@mapbox/whoots-js": "^3.1.0",
|
||||
"@maplibre/maplibre-gl-style-spec": "^24.1.1",
|
||||
"@maplibre/maplibre-gl-style-spec": "^24.3.0",
|
||||
"@maplibre/vt-pbf": "^4.0.3",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"@types/geojson-vt": "3.2.5",
|
||||
|
||||
10
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.cjs
generated
vendored
10
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.cjs
generated
vendored
@ -432,6 +432,16 @@
|
||||
type: "boolean",
|
||||
"default": false
|
||||
},
|
||||
encoding: {
|
||||
type: "enum",
|
||||
values: {
|
||||
mvt: {
|
||||
},
|
||||
mlt: {
|
||||
}
|
||||
},
|
||||
"default": "mvt"
|
||||
},
|
||||
"*": {
|
||||
type: "*"
|
||||
}
|
||||
|
||||
2
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.cjs.map
generated
vendored
2
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.cjs.map
generated
vendored
File diff suppressed because one or more lines are too long
10
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.mjs
generated
vendored
10
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.mjs
generated
vendored
@ -428,6 +428,16 @@ var source_vector = {
|
||||
type: "boolean",
|
||||
"default": false
|
||||
},
|
||||
encoding: {
|
||||
type: "enum",
|
||||
values: {
|
||||
mvt: {
|
||||
},
|
||||
mlt: {
|
||||
}
|
||||
},
|
||||
"default": "mvt"
|
||||
},
|
||||
"*": {
|
||||
type: "*"
|
||||
}
|
||||
|
||||
2
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.mjs.map
generated
vendored
2
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-migrate.mjs.map
generated
vendored
File diff suppressed because one or more lines are too long
10
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.cjs
generated
vendored
10
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.cjs
generated
vendored
@ -7014,6 +7014,16 @@
|
||||
type: "boolean",
|
||||
"default": false
|
||||
},
|
||||
encoding: {
|
||||
type: "enum",
|
||||
values: {
|
||||
mvt: {
|
||||
},
|
||||
mlt: {
|
||||
}
|
||||
},
|
||||
"default": "mvt"
|
||||
},
|
||||
"*": {
|
||||
type: "*"
|
||||
}
|
||||
|
||||
2
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.cjs.map
generated
vendored
2
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.cjs.map
generated
vendored
File diff suppressed because one or more lines are too long
10
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.mjs
generated
vendored
10
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.mjs
generated
vendored
@ -7010,6 +7010,16 @@ var source_vector = {
|
||||
type: "boolean",
|
||||
"default": false
|
||||
},
|
||||
encoding: {
|
||||
type: "enum",
|
||||
values: {
|
||||
mvt: {
|
||||
},
|
||||
mlt: {
|
||||
}
|
||||
},
|
||||
"default": "mvt"
|
||||
},
|
||||
"*": {
|
||||
type: "*"
|
||||
}
|
||||
|
||||
2
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.mjs.map
generated
vendored
2
node_modules/@maplibre/maplibre-gl-style-spec/dist/gl-style-validate.mjs.map
generated
vendored
File diff suppressed because one or more lines are too long
10
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.cjs
generated
vendored
10
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.cjs
generated
vendored
@ -153,6 +153,16 @@
|
||||
type: "boolean",
|
||||
"default": false
|
||||
},
|
||||
encoding: {
|
||||
type: "enum",
|
||||
values: {
|
||||
mvt: {
|
||||
},
|
||||
mlt: {
|
||||
}
|
||||
},
|
||||
"default": "mvt"
|
||||
},
|
||||
"*": {
|
||||
type: "*"
|
||||
}
|
||||
|
||||
2
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.cjs.map
generated
vendored
2
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.cjs.map
generated
vendored
File diff suppressed because one or more lines are too long
34
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.d.ts
generated
vendored
34
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.d.ts
generated
vendored
@ -1026,6 +1026,15 @@ export type VectorSourceSpecification = {
|
||||
* A setting to determine whether a source's tiles are cached locally.
|
||||
*/
|
||||
"volatile"?: boolean;
|
||||
/**
|
||||
* The encoding used by this source. Mapbox Vector Tiles encoding is used by default.
|
||||
*
|
||||
* @default
|
||||
* ```json
|
||||
* "mvt"
|
||||
* ```
|
||||
*/
|
||||
"encoding"?: "mvt" | "mlt";
|
||||
};
|
||||
export type RasterSourceSpecification = {
|
||||
/**
|
||||
@ -4181,6 +4190,31 @@ export declare function validateStyleMin(style: StyleSpecification, styleSpec?:
|
||||
};
|
||||
};
|
||||
};
|
||||
encoding: {
|
||||
type: string;
|
||||
values: {
|
||||
mvt: {
|
||||
doc: string;
|
||||
};
|
||||
mlt: {
|
||||
doc: string;
|
||||
};
|
||||
};
|
||||
default: string;
|
||||
doc: string;
|
||||
"sdk-support": {
|
||||
mvt: {
|
||||
android: string;
|
||||
ios: string;
|
||||
js: string;
|
||||
};
|
||||
mlt: {
|
||||
android: string;
|
||||
ios: string;
|
||||
js: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
"*": {
|
||||
type: string;
|
||||
doc: string;
|
||||
|
||||
10
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.mjs
generated
vendored
10
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.mjs
generated
vendored
@ -147,6 +147,16 @@ var source_vector = {
|
||||
type: "boolean",
|
||||
"default": false
|
||||
},
|
||||
encoding: {
|
||||
type: "enum",
|
||||
values: {
|
||||
mvt: {
|
||||
},
|
||||
mlt: {
|
||||
}
|
||||
},
|
||||
"default": "mvt"
|
||||
},
|
||||
"*": {
|
||||
type: "*"
|
||||
}
|
||||
|
||||
2
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.mjs.map
generated
vendored
2
node_modules/@maplibre/maplibre-gl-style-spec/dist/index.mjs.map
generated
vendored
File diff suppressed because one or more lines are too long
27
node_modules/@maplibre/maplibre-gl-style-spec/dist/latest.json
generated
vendored
27
node_modules/@maplibre/maplibre-gl-style-spec/dist/latest.json
generated
vendored
@ -364,6 +364,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"encoding": {
|
||||
"type": "enum",
|
||||
"values": {
|
||||
"mvt": {
|
||||
"doc": "Mapbox Vector Tiles. See http://github.com/mapbox/vector-tile-spec for more info."
|
||||
},
|
||||
"mlt": {
|
||||
"doc": "MapLibre Vector Tiles. See https://github.com/maplibre/maplibre-tile-spec for more info."
|
||||
}
|
||||
},
|
||||
"default": "mvt",
|
||||
"doc": "The encoding used by this source. Mapbox Vector Tiles encoding is used by default.",
|
||||
"sdk-support": {
|
||||
"mvt": {
|
||||
"android": "supported",
|
||||
"ios": "supported",
|
||||
"js": "supported"
|
||||
},
|
||||
"mlt": {
|
||||
"android": "https://github.com/maplibre/maplibre-native/issues/3721",
|
||||
"ios": "https://github.com/maplibre/maplibre-native/issues/3721",
|
||||
"js": "https://github.com/maplibre/maplibre-gl-js/issues/6258"
|
||||
}
|
||||
}
|
||||
},
|
||||
"*": {
|
||||
"type": "*",
|
||||
"doc": "Other keys to configure the data source."
|
||||
@ -6362,7 +6387,7 @@
|
||||
"ios": "2.0.0"
|
||||
},
|
||||
"data-driven styling": {
|
||||
"js": "https://github.com/maplibre/maplibre-gl-js/issues/1235",
|
||||
"js": "5.8.0",
|
||||
"ios": "https://github.com/maplibre/maplibre-native/issues/744",
|
||||
"android": "https://github.com/maplibre/maplibre-native/issues/744"
|
||||
}
|
||||
|
||||
22
node_modules/@maplibre/maplibre-gl-style-spec/package.json
generated
vendored
22
node_modules/@maplibre/maplibre-gl-style-spec/package.json
generated
vendored
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@maplibre/maplibre-gl-style-spec",
|
||||
"description": "a specification for maplibre styles",
|
||||
"version": "24.2.0",
|
||||
"version": "24.3.0",
|
||||
"author": "MapLibre",
|
||||
"keywords": [
|
||||
"mapbox",
|
||||
@ -64,7 +64,7 @@
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^28.0.6",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-node-resolve": "^16.0.3",
|
||||
"@rollup/plugin-replace": "^6.0.2",
|
||||
"@rollup/plugin-strip": "^3.0.4",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
@ -72,23 +72,23 @@
|
||||
"@stylistic/eslint-plugin": "^5.4.0",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"@types/node": "^24.5.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.44.0",
|
||||
"@typescript-eslint/parser": "^8.42.0",
|
||||
"@types/node": "^24.7.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.46.0",
|
||||
"@typescript-eslint/parser": "^8.46.0",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"@vitest/eslint-plugin": "^1.3.12",
|
||||
"@vitest/eslint-plugin": "^1.3.17",
|
||||
"@vitest/ui": "3.2.4",
|
||||
"dts-bundle-generator": "^9.5.1",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-plugin-jsdoc": "^60.1.0",
|
||||
"eslint": "^9.37.0",
|
||||
"eslint-plugin-jsdoc": "^61.1.1",
|
||||
"glob": "^11.0.3",
|
||||
"globals": "^16.4.0",
|
||||
"rollup": "^4.52.0",
|
||||
"rollup": "^4.52.4",
|
||||
"rollup-plugin-preserve-shebang": "^1.0.1",
|
||||
"semver": "^7.7.2",
|
||||
"semver": "^7.7.3",
|
||||
"ts-node": "^10.9.2",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "3.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
27
node_modules/@maplibre/maplibre-gl-style-spec/src/reference/v8.json
generated
vendored
27
node_modules/@maplibre/maplibre-gl-style-spec/src/reference/v8.json
generated
vendored
@ -364,6 +364,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"encoding": {
|
||||
"type": "enum",
|
||||
"values": {
|
||||
"mvt": {
|
||||
"doc": "Mapbox Vector Tiles. See http://github.com/mapbox/vector-tile-spec for more info."
|
||||
},
|
||||
"mlt": {
|
||||
"doc": "MapLibre Vector Tiles. See https://github.com/maplibre/maplibre-tile-spec for more info."
|
||||
}
|
||||
},
|
||||
"default": "mvt",
|
||||
"doc": "The encoding used by this source. Mapbox Vector Tiles encoding is used by default.",
|
||||
"sdk-support": {
|
||||
"mvt": {
|
||||
"android": "supported",
|
||||
"ios": "supported",
|
||||
"js": "supported"
|
||||
},
|
||||
"mlt": {
|
||||
"android": "https://github.com/maplibre/maplibre-native/issues/3721",
|
||||
"ios": "https://github.com/maplibre/maplibre-native/issues/3721",
|
||||
"js": "https://github.com/maplibre/maplibre-gl-js/issues/6258"
|
||||
}
|
||||
}
|
||||
},
|
||||
"*": {
|
||||
"type": "*",
|
||||
"doc": "Other keys to configure the data source."
|
||||
@ -6362,7 +6387,7 @@
|
||||
"ios": "2.0.0"
|
||||
},
|
||||
"data-driven styling": {
|
||||
"js": "https://github.com/maplibre/maplibre-gl-js/issues/1235",
|
||||
"js": "5.8.0",
|
||||
"ios": "https://github.com/maplibre/maplibre-native/issues/744",
|
||||
"android": "https://github.com/maplibre/maplibre-native/issues/744"
|
||||
}
|
||||
|
||||
11
node_modules/@maplibre/maplibre-gl-style-spec/src/types.g.ts
generated
vendored
11
node_modules/@maplibre/maplibre-gl-style-spec/src/types.g.ts
generated
vendored
@ -724,7 +724,16 @@ export type VectorSourceSpecification = {
|
||||
/**
|
||||
* A setting to determine whether a source's tiles are cached locally.
|
||||
*/
|
||||
"volatile"?: boolean
|
||||
"volatile"?: boolean,
|
||||
/**
|
||||
* The encoding used by this source. Mapbox Vector Tiles encoding is used by default.
|
||||
*
|
||||
* @default
|
||||
* ```json
|
||||
* "mvt"
|
||||
* ```
|
||||
*/
|
||||
"encoding"?: "mvt" | "mlt"
|
||||
};
|
||||
|
||||
export type RasterSourceSpecification = {
|
||||
|
||||
10
node_modules/maplibre-gl/README.md
generated
vendored
10
node_modules/maplibre-gl/README.md
generated
vendored
@ -47,11 +47,11 @@ Full documentation for this library [is available here](https://maplibre.org/map
|
||||
|
||||
Check out the features through [examples](https://maplibre.org/maplibre-gl-js/docs/examples/).
|
||||
|
||||
| Showcases | |
|
||||
| ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
| Showcases | |
|
||||
| ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
<br />
|
||||
|
||||
|
||||
4
node_modules/maplibre-gl/build/generate-struct-arrays.ts
generated
vendored
4
node_modules/maplibre-gl/build/generate-struct-arrays.ts
generated
vendored
@ -22,6 +22,7 @@ import fillExtrusionAttributes from '../src/data/bucket/fill_extrusion_attribute
|
||||
import {lineLayoutAttributes} from '../src/data/bucket/line_attributes';
|
||||
import {lineLayoutAttributesExt} from '../src/data/bucket/line_attributes_ext';
|
||||
import {patternAttributes} from '../src/data/bucket/pattern_attributes';
|
||||
import {dashAttributes} from '../src/data/bucket/dash_attributes';
|
||||
// symbol layer specific arrays
|
||||
import {
|
||||
symbolLayoutAttributes,
|
||||
@ -146,7 +147,8 @@ const layoutAttributes = {
|
||||
heatmap: circleAttributes,
|
||||
line: lineLayoutAttributes,
|
||||
lineExt: lineLayoutAttributesExt,
|
||||
pattern: patternAttributes
|
||||
pattern: patternAttributes,
|
||||
dash: dashAttributes
|
||||
};
|
||||
for (const name in layoutAttributes) {
|
||||
createStructArrayType(`${name.replace(/-/g, '_')}_layout`, layoutAttributes[name]);
|
||||
|
||||
2192
node_modules/maplibre-gl/dist/maplibre-gl-csp-dev.js
generated
vendored
2192
node_modules/maplibre-gl/dist/maplibre-gl-csp-dev.js
generated
vendored
File diff suppressed because it is too large
Load Diff
2
node_modules/maplibre-gl/dist/maplibre-gl-csp-dev.js.map
generated
vendored
2
node_modules/maplibre-gl/dist/maplibre-gl-csp-dev.js.map
generated
vendored
File diff suppressed because one or more lines are too long
389
node_modules/maplibre-gl/dist/maplibre-gl-csp-worker-dev.js
generated
vendored
389
node_modules/maplibre-gl/dist/maplibre-gl-csp-worker-dev.js
generated
vendored
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* MapLibre GL JS
|
||||
* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v5.7.3/LICENSE.txt
|
||||
* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v5.10.0/LICENSE.txt
|
||||
*/
|
||||
var maplibregl = (function () {
|
||||
'use strict';
|
||||
@ -8939,6 +8939,43 @@ function getAABB(points) {
|
||||
}
|
||||
return [tlX, tlY, brX, brY];
|
||||
}
|
||||
/**
|
||||
* For a given set of tile ids, returns the edge tile ids for the bounding box.
|
||||
*/
|
||||
function getEdgeTiles(tileIDs) {
|
||||
if (!tileIDs.length)
|
||||
return new Set();
|
||||
// set a common zoom for calculation (highest zoom) to reproject all tiles to this same zoom
|
||||
const targetZ = Math.max(...tileIDs.map(id => id.canonical.z));
|
||||
// vars to store the min and max tile x/y coordinates for edge finding
|
||||
let minX = Infinity, maxX = -Infinity;
|
||||
let minY = Infinity, maxY = -Infinity;
|
||||
// project all tiles to targetZ while maintaining the reference to the original tile
|
||||
const projected = [];
|
||||
for (const id of tileIDs) {
|
||||
const { x, y, z } = id.canonical;
|
||||
const scale = Math.pow(2, targetZ - z);
|
||||
const px = x * scale;
|
||||
const py = y * scale;
|
||||
projected.push({ id, x: px, y: py });
|
||||
if (px < minX)
|
||||
minX = px;
|
||||
if (px > maxX)
|
||||
maxX = px;
|
||||
if (py < minY)
|
||||
minY = py;
|
||||
if (py > maxY)
|
||||
maxY = py;
|
||||
}
|
||||
// find edge tiles using the reprojected tile ids
|
||||
const edgeTiles = new Set();
|
||||
for (const p of projected) {
|
||||
if (p.x === minX || p.x === maxX || p.y === minY || p.y === maxY) {
|
||||
edgeTiles.add(p.id);
|
||||
}
|
||||
}
|
||||
return edgeTiles;
|
||||
}
|
||||
/**
|
||||
* Given a value `t` that varies between 0 and 1, return
|
||||
* an interpolation function that eases between 0 and 1 in a pleasing
|
||||
@ -9929,6 +9966,16 @@ var source_vector = {
|
||||
type: "boolean",
|
||||
"default": false
|
||||
},
|
||||
encoding: {
|
||||
type: "enum",
|
||||
values: {
|
||||
mvt: {
|
||||
},
|
||||
mlt: {
|
||||
}
|
||||
},
|
||||
"default": "mvt"
|
||||
},
|
||||
"*": {
|
||||
type: "*"
|
||||
}
|
||||
@ -11873,10 +11920,11 @@ var paint_line = {
|
||||
expression: {
|
||||
interpolated: false,
|
||||
parameters: [
|
||||
"zoom"
|
||||
"zoom",
|
||||
"feature"
|
||||
]
|
||||
},
|
||||
"property-type": "cross-faded"
|
||||
"property-type": "cross-faded-data-driven"
|
||||
},
|
||||
"line-pattern": {
|
||||
type: "resolvedImage",
|
||||
@ -20987,10 +21035,7 @@ validateStyleMin.paintProperty = wrapCleanErrors(injectValidateSpec(validatePain
|
||||
validateStyleMin.layoutProperty = wrapCleanErrors(injectValidateSpec(validateLayoutProperty$1));
|
||||
function injectValidateSpec(validator) {
|
||||
return function (options) {
|
||||
return validator({
|
||||
...options,
|
||||
validateSpec: validate,
|
||||
});
|
||||
return validator(Object.assign({}, options, { validateSpec: validate }));
|
||||
};
|
||||
}
|
||||
function sortErrors(errors) {
|
||||
@ -23761,8 +23806,8 @@ class StyleLayer extends Evented {
|
||||
// No-op; can be overridden by derived classes.
|
||||
return false;
|
||||
}
|
||||
isHidden(zoom) {
|
||||
if (this.minzoom && zoom < this.minzoom)
|
||||
isHidden(zoom, roundMinZoom = false) {
|
||||
if (this.minzoom && zoom < (roundMinZoom ? Math.floor(this.minzoom) : this.minzoom))
|
||||
return true;
|
||||
if (this.maxzoom && zoom >= this.maxzoom)
|
||||
return true;
|
||||
@ -24209,6 +24254,37 @@ class StructArrayLayout10ui20 extends StructArray {
|
||||
}
|
||||
StructArrayLayout10ui20.prototype.bytesPerElement = 20;
|
||||
register('StructArrayLayout10ui20', StructArrayLayout10ui20);
|
||||
/**
|
||||
* @internal
|
||||
* Implementation of the StructArray layout:
|
||||
* [0] - Uint16[8]
|
||||
*
|
||||
*/
|
||||
class StructArrayLayout8ui16 extends StructArray {
|
||||
_refreshViews() {
|
||||
this.uint8 = new Uint8Array(this.arrayBuffer);
|
||||
this.uint16 = new Uint16Array(this.arrayBuffer);
|
||||
}
|
||||
emplaceBack(v0, v1, v2, v3, v4, v5, v6, v7) {
|
||||
const i = this.length;
|
||||
this.resize(i + 1);
|
||||
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7);
|
||||
}
|
||||
emplace(i, v0, v1, v2, v3, v4, v5, v6, v7) {
|
||||
const o2 = i * 8;
|
||||
this.uint16[o2 + 0] = v0;
|
||||
this.uint16[o2 + 1] = v1;
|
||||
this.uint16[o2 + 2] = v2;
|
||||
this.uint16[o2 + 3] = v3;
|
||||
this.uint16[o2 + 4] = v4;
|
||||
this.uint16[o2 + 5] = v5;
|
||||
this.uint16[o2 + 6] = v6;
|
||||
this.uint16[o2 + 7] = v7;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
StructArrayLayout8ui16.prototype.bytesPerElement = 16;
|
||||
register('StructArrayLayout8ui16', StructArrayLayout8ui16);
|
||||
/**
|
||||
* @internal
|
||||
* Implementation of the StructArray layout:
|
||||
@ -24898,6 +24974,8 @@ class LineExtLayoutArray extends StructArrayLayout2f8 {
|
||||
}
|
||||
class PatternLayoutArray extends StructArrayLayout10ui20 {
|
||||
}
|
||||
class DashLayoutArray extends StructArrayLayout8ui16 {
|
||||
}
|
||||
class SymbolLayoutArray extends StructArrayLayout4i4ui4i24 {
|
||||
}
|
||||
class SymbolDynamicLayoutArray extends StructArrayLayout3f12 {
|
||||
@ -25030,6 +25108,12 @@ const patternAttributes = createLayout([
|
||||
{ name: 'a_pixel_ratio_to', components: 1, type: 'Uint16' },
|
||||
]);
|
||||
|
||||
const dashAttributes = createLayout([
|
||||
// [0, y, height, width]
|
||||
{ name: 'a_dasharray_from', components: 4, type: 'Uint16' },
|
||||
{ name: 'a_dasharray_to', components: 4, type: 'Uint16' },
|
||||
]);
|
||||
|
||||
var murmurhashJs$1 = {exports: {}};
|
||||
|
||||
var murmurhash3_gc$1 = {exports: {}};
|
||||
@ -25477,16 +25561,36 @@ class CrossFadedConstantBinder {
|
||||
this.patternFrom = posFrom.tlbr;
|
||||
this.patternTo = posTo.tlbr;
|
||||
}
|
||||
setConstantDashPositions(dashTo, dashFrom) {
|
||||
this.dashTo = [0, dashTo.y, dashTo.height, dashTo.width];
|
||||
this.dashFrom = [0, dashFrom.y, dashFrom.height, dashFrom.width];
|
||||
}
|
||||
setUniform(uniform, globals, currentValue, uniformName) {
|
||||
const pos = uniformName === 'u_pattern_to' ? this.patternTo :
|
||||
uniformName === 'u_pattern_from' ? this.patternFrom :
|
||||
uniformName === 'u_pixel_ratio_to' ? this.pixelRatioTo :
|
||||
uniformName === 'u_pixel_ratio_from' ? this.pixelRatioFrom : null;
|
||||
if (pos)
|
||||
uniform.set(pos);
|
||||
let value = null;
|
||||
if (uniformName === 'u_pattern_to') {
|
||||
value = this.patternTo;
|
||||
}
|
||||
else if (uniformName === 'u_pattern_from') {
|
||||
value = this.patternFrom;
|
||||
}
|
||||
else if (uniformName === 'u_dasharray_to') {
|
||||
value = this.dashTo;
|
||||
}
|
||||
else if (uniformName === 'u_dasharray_from') {
|
||||
value = this.dashFrom;
|
||||
}
|
||||
else if (uniformName === 'u_pixel_ratio_to') {
|
||||
value = this.pixelRatioTo;
|
||||
}
|
||||
else if (uniformName === 'u_pixel_ratio_from') {
|
||||
value = this.pixelRatioFrom;
|
||||
}
|
||||
if (value !== null) {
|
||||
uniform.set(value);
|
||||
}
|
||||
}
|
||||
getBinding(context, location, name) {
|
||||
return name.substr(0, 9) === 'u_pattern' ?
|
||||
return (name.substr(0, 9) === 'u_pattern' || name.substr(0, 12) === 'u_dasharray_') ?
|
||||
new Uniform4f(context, location) :
|
||||
new Uniform1f(context, location);
|
||||
}
|
||||
@ -25611,7 +25715,7 @@ class CompositeExpressionBinder {
|
||||
return new Uniform1f(context, location);
|
||||
}
|
||||
}
|
||||
class CrossFadedCompositeBinder {
|
||||
class CrossFadedBinder {
|
||||
constructor(expression, type, useIntegerZoom, zoom, PaintVertexArray, layerId) {
|
||||
this.expression = expression;
|
||||
this.type = type;
|
||||
@ -25625,32 +25729,33 @@ class CrossFadedCompositeBinder {
|
||||
const start = this.zoomInPaintVertexArray.length;
|
||||
this.zoomInPaintVertexArray.resize(length);
|
||||
this.zoomOutPaintVertexArray.resize(length);
|
||||
this._setPaintValues(start, length, feature.patterns && feature.patterns[this.layerId], options.imagePositions);
|
||||
this._setPaintValues(start, length, this.getPositionIds(feature), options);
|
||||
}
|
||||
updatePaintArray(start, end, feature, featureState, options) {
|
||||
this._setPaintValues(start, end, feature.patterns && feature.patterns[this.layerId], options.imagePositions);
|
||||
this._setPaintValues(start, end, this.getPositionIds(feature), options);
|
||||
}
|
||||
_setPaintValues(start, end, patterns, positions) {
|
||||
if (!positions || !patterns)
|
||||
_setPaintValues(start, end, positionIds, options) {
|
||||
const positions = this.getPositions(options);
|
||||
if (!positions || !positionIds)
|
||||
return;
|
||||
const { min, mid, max } = patterns;
|
||||
const imageMin = positions[min];
|
||||
const imageMid = positions[mid];
|
||||
const imageMax = positions[max];
|
||||
if (!imageMin || !imageMid || !imageMax)
|
||||
const min = positions[positionIds.min];
|
||||
const mid = positions[positionIds.mid];
|
||||
const max = positions[positionIds.max];
|
||||
if (!min || !mid || !max)
|
||||
return;
|
||||
// We populate two paint arrays because, for cross-faded properties, we don't know which direction
|
||||
// we're cross-fading to at layout time. In order to keep vertex attributes to a minimum and not pass
|
||||
// unnecessary vertex data to the shaders, we determine which to upload at draw time.
|
||||
for (let i = start; i < end; i++) {
|
||||
this.zoomInPaintVertexArray.emplace(i, imageMid.tl[0], imageMid.tl[1], imageMid.br[0], imageMid.br[1], imageMin.tl[0], imageMin.tl[1], imageMin.br[0], imageMin.br[1], imageMid.pixelRatio, imageMin.pixelRatio);
|
||||
this.zoomOutPaintVertexArray.emplace(i, imageMid.tl[0], imageMid.tl[1], imageMid.br[0], imageMid.br[1], imageMax.tl[0], imageMax.tl[1], imageMax.br[0], imageMax.br[1], imageMid.pixelRatio, imageMax.pixelRatio);
|
||||
this.emplace(this.zoomInPaintVertexArray, i, mid, min);
|
||||
this.emplace(this.zoomOutPaintVertexArray, i, mid, max);
|
||||
}
|
||||
}
|
||||
upload(context) {
|
||||
if (this.zoomInPaintVertexArray && this.zoomInPaintVertexArray.arrayBuffer && this.zoomOutPaintVertexArray && this.zoomOutPaintVertexArray.arrayBuffer) {
|
||||
this.zoomInPaintVertexBuffer = context.createVertexBuffer(this.zoomInPaintVertexArray, patternAttributes.members, this.expression.isStateDependent);
|
||||
this.zoomOutPaintVertexBuffer = context.createVertexBuffer(this.zoomOutPaintVertexArray, patternAttributes.members, this.expression.isStateDependent);
|
||||
const attributes = this.getVertexAttributes();
|
||||
this.zoomInPaintVertexBuffer = context.createVertexBuffer(this.zoomInPaintVertexArray, attributes, this.expression.isStateDependent);
|
||||
this.zoomOutPaintVertexBuffer = context.createVertexBuffer(this.zoomOutPaintVertexArray, attributes, this.expression.isStateDependent);
|
||||
}
|
||||
}
|
||||
destroy() {
|
||||
@ -25660,6 +25765,34 @@ class CrossFadedCompositeBinder {
|
||||
this.zoomInPaintVertexBuffer.destroy();
|
||||
}
|
||||
}
|
||||
class CrossFadedPatternBinder extends CrossFadedBinder {
|
||||
getPositions(options) {
|
||||
return options.imagePositions;
|
||||
}
|
||||
getPositionIds(feature) {
|
||||
return feature.patterns && feature.patterns[this.layerId];
|
||||
}
|
||||
getVertexAttributes() {
|
||||
return patternAttributes.members;
|
||||
}
|
||||
emplace(array, index, midPos, minMaxPos) {
|
||||
array.emplace(index, midPos.tlbr[0], midPos.tlbr[1], midPos.tlbr[2], midPos.tlbr[3], minMaxPos.tlbr[0], minMaxPos.tlbr[1], minMaxPos.tlbr[2], minMaxPos.tlbr[3], midPos.pixelRatio, minMaxPos.pixelRatio);
|
||||
}
|
||||
}
|
||||
class CrossFadedDasharrayBinder extends CrossFadedBinder {
|
||||
getPositions(options) {
|
||||
return options.dashPositions;
|
||||
}
|
||||
getPositionIds(feature) {
|
||||
return feature.dashes && feature.dashes[this.layerId];
|
||||
}
|
||||
getVertexAttributes() {
|
||||
return dashAttributes.members;
|
||||
}
|
||||
emplace(array, index, midPos, minMaxPos) {
|
||||
array.emplace(index, 0, midPos.y, midPos.height, midPos.width, 0, minMaxPos.y, minMaxPos.height, minMaxPos.width);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
* ProgramConfiguration contains the logic for binding style layer properties and tile
|
||||
@ -25706,7 +25839,9 @@ class ProgramConfiguration {
|
||||
else if (expression.kind === 'source' || isCrossFaded) {
|
||||
const StructArrayLayout = layoutType(property, type, 'source');
|
||||
this.binders[property] = isCrossFaded ?
|
||||
new CrossFadedCompositeBinder(expression, type, useIntegerZoom, zoom, StructArrayLayout, layer.id) :
|
||||
property === 'line-dasharray' ?
|
||||
new CrossFadedDasharrayBinder(expression, type, useIntegerZoom, zoom, StructArrayLayout, layer.id) :
|
||||
new CrossFadedPatternBinder(expression, type, useIntegerZoom, zoom, StructArrayLayout, layer.id) :
|
||||
new SourceExpressionBinder(expression, names, type, StructArrayLayout);
|
||||
keys.push(`/a_${property}`);
|
||||
}
|
||||
@ -25725,7 +25860,7 @@ class ProgramConfiguration {
|
||||
populatePaintArrays(newLength, feature, options) {
|
||||
for (const property in this.binders) {
|
||||
const binder = this.binders[property];
|
||||
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedCompositeBinder)
|
||||
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedBinder)
|
||||
binder.populatePaintArray(newLength, feature, options);
|
||||
}
|
||||
}
|
||||
@ -25736,6 +25871,13 @@ class ProgramConfiguration {
|
||||
binder.setConstantPatternPositions(posTo, posFrom);
|
||||
}
|
||||
}
|
||||
setConstantDashPositions(dashTo, dashFrom) {
|
||||
for (const property in this.binders) {
|
||||
const binder = this.binders[property];
|
||||
if (binder instanceof CrossFadedConstantBinder)
|
||||
binder.setConstantDashPositions(dashTo, dashFrom);
|
||||
}
|
||||
}
|
||||
updatePaintArrays(featureStates, featureMap, vtLayer, layer, options) {
|
||||
let dirty = false;
|
||||
for (const id in featureStates) {
|
||||
@ -25745,7 +25887,7 @@ class ProgramConfiguration {
|
||||
for (const property in this.binders) {
|
||||
const binder = this.binders[property];
|
||||
if ((binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder ||
|
||||
binder instanceof CrossFadedCompositeBinder) && binder.expression.isStateDependent === true) {
|
||||
binder instanceof CrossFadedBinder) && binder.expression.isStateDependent === true) {
|
||||
//AHM: Remove after https://github.com/mapbox/mapbox-gl-js/issues/6255
|
||||
const value = layer.paint.get(property);
|
||||
binder.expression = value.value;
|
||||
@ -25776,9 +25918,10 @@ class ProgramConfiguration {
|
||||
result.push(binder.paintVertexAttributes[i].name);
|
||||
}
|
||||
}
|
||||
else if (binder instanceof CrossFadedCompositeBinder) {
|
||||
for (let i = 0; i < patternAttributes.members.length; i++) {
|
||||
result.push(patternAttributes.members[i].name);
|
||||
else if (binder instanceof CrossFadedBinder) {
|
||||
const attributes = binder.getVertexAttributes();
|
||||
for (const attribute of attributes) {
|
||||
result.push(attribute.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25825,7 +25968,7 @@ class ProgramConfiguration {
|
||||
this._buffers = [];
|
||||
for (const property in this.binders) {
|
||||
const binder = this.binders[property];
|
||||
if (crossfade && binder instanceof CrossFadedCompositeBinder) {
|
||||
if (crossfade && binder instanceof CrossFadedBinder) {
|
||||
const patternVertexBuffer = crossfade.fromScale === 2 ? binder.zoomInPaintVertexBuffer : binder.zoomOutPaintVertexBuffer;
|
||||
if (patternVertexBuffer)
|
||||
this._buffers.push(patternVertexBuffer);
|
||||
@ -25838,7 +25981,7 @@ class ProgramConfiguration {
|
||||
upload(context) {
|
||||
for (const property in this.binders) {
|
||||
const binder = this.binders[property];
|
||||
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedCompositeBinder)
|
||||
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedBinder)
|
||||
binder.upload(context);
|
||||
}
|
||||
this.updatePaintBuffers();
|
||||
@ -25846,7 +25989,7 @@ class ProgramConfiguration {
|
||||
destroy() {
|
||||
for (const property in this.binders) {
|
||||
const binder = this.binders[property];
|
||||
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedCompositeBinder)
|
||||
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedBinder)
|
||||
binder.destroy();
|
||||
}
|
||||
}
|
||||
@ -25906,6 +26049,7 @@ function paintAttributeNames(property, type) {
|
||||
'text-halo-width': ['halo_width'],
|
||||
'icon-halo-width': ['halo_width'],
|
||||
'line-gap-width': ['gapwidth'],
|
||||
'line-dasharray': ['dasharray_to', 'dasharray_from'],
|
||||
'line-pattern': ['pattern_to', 'pattern_from', 'pixel_ratio_to', 'pixel_ratio_from'],
|
||||
'fill-pattern': ['pattern_to', 'pattern_from', 'pixel_ratio_to', 'pixel_ratio_from'],
|
||||
'fill-extrusion-pattern': ['pattern_to', 'pattern_from', 'pixel_ratio_to', 'pixel_ratio_from'],
|
||||
@ -25925,7 +26069,11 @@ function getLayoutException(property) {
|
||||
'fill-extrusion-pattern': {
|
||||
'source': PatternLayoutArray,
|
||||
'composite': PatternLayoutArray
|
||||
}
|
||||
},
|
||||
'line-dasharray': {
|
||||
'source': DashLayoutArray,
|
||||
'composite': DashLayoutArray
|
||||
},
|
||||
};
|
||||
return propertyExceptions[property];
|
||||
}
|
||||
@ -25946,7 +26094,8 @@ function layoutType(property, type, binderType) {
|
||||
register('ConstantBinder', ConstantBinder);
|
||||
register('CrossFadedConstantBinder', CrossFadedConstantBinder);
|
||||
register('SourceExpressionBinder', SourceExpressionBinder);
|
||||
register('CrossFadedCompositeBinder', CrossFadedCompositeBinder);
|
||||
register('CrossFadedPatternBinder', CrossFadedPatternBinder);
|
||||
register('CrossFadedDasharrayBinder', CrossFadedDasharrayBinder);
|
||||
register('CompositeExpressionBinder', CompositeExpressionBinder);
|
||||
register('ProgramConfiguration', ProgramConfiguration, { omit: ['_buffers'] });
|
||||
register('ProgramConfigurationSet', ProgramConfigurationSet);
|
||||
@ -26019,7 +26168,7 @@ class CircleBucket {
|
||||
this.layers = options.layers;
|
||||
this.layerIds = this.layers.map(layer => layer.id);
|
||||
this.index = options.index;
|
||||
this.hasPattern = false;
|
||||
this.hasDependencies = false;
|
||||
this.layoutVertexArray = new CircleLayoutArray();
|
||||
this.indexArray = new TriangleIndexArray();
|
||||
this.segments = new SegmentVector();
|
||||
@ -28880,7 +29029,7 @@ class FillBucket {
|
||||
this.layers = options.layers;
|
||||
this.layerIds = this.layers.map(layer => layer.id);
|
||||
this.index = options.index;
|
||||
this.hasPattern = false;
|
||||
this.hasDependencies = false;
|
||||
this.patternFeatures = [];
|
||||
this.layoutVertexArray = new FillLayoutArray();
|
||||
this.indexArray = new TriangleIndexArray();
|
||||
@ -28891,7 +29040,7 @@ class FillBucket {
|
||||
this.stateDependentLayerIds = this.layers.filter((l) => l.isStateDependent()).map((l) => l.id);
|
||||
}
|
||||
populate(features, options, canonical) {
|
||||
this.hasPattern = hasPattern('fill', this.layers, options);
|
||||
this.hasDependencies = hasPattern('fill', this.layers, options);
|
||||
const fillSortKey = this.layers[0].layout.get('fill-sort-key');
|
||||
const sortFeaturesByKey = !fillSortKey.isConstant();
|
||||
const bucketFeatures = [];
|
||||
@ -28920,7 +29069,7 @@ class FillBucket {
|
||||
}
|
||||
for (const bucketFeature of bucketFeatures) {
|
||||
const { geometry, index, sourceLayerIndex } = bucketFeature;
|
||||
if (this.hasPattern) {
|
||||
if (this.hasDependencies) {
|
||||
const patternFeature = addPatternDependencies('fill', this.layers, bucketFeature, { zoom: this.zoom }, options);
|
||||
// pattern features are added only once the pattern is loaded into the image atlas
|
||||
// so are stored during populate until later updated with positions by tile worker in addFeatures
|
||||
@ -29441,7 +29590,7 @@ class FillExtrusionBucket {
|
||||
this.layers = options.layers;
|
||||
this.layerIds = this.layers.map(layer => layer.id);
|
||||
this.index = options.index;
|
||||
this.hasPattern = false;
|
||||
this.hasDependencies = false;
|
||||
this.layoutVertexArray = new FillExtrusionLayoutArray();
|
||||
this.centroidVertexArray = new PosArray();
|
||||
this.indexArray = new TriangleIndexArray();
|
||||
@ -29451,7 +29600,7 @@ class FillExtrusionBucket {
|
||||
}
|
||||
populate(features, options, canonical) {
|
||||
this.features = [];
|
||||
this.hasPattern = hasPattern('fill-extrusion', this.layers, options);
|
||||
this.hasDependencies = hasPattern('fill-extrusion', this.layers, options);
|
||||
for (const { feature, id, index, sourceLayerIndex } of features) {
|
||||
const needGeometry = this.layers[0]._featureFilter.needGeometry;
|
||||
const evaluationFeature = toEvaluationFeature(feature, needGeometry);
|
||||
@ -29466,7 +29615,7 @@ class FillExtrusionBucket {
|
||||
type: feature.type,
|
||||
patterns: {}
|
||||
};
|
||||
if (this.hasPattern) {
|
||||
if (this.hasDependencies) {
|
||||
this.features.push(addPatternDependencies('fill-extrusion', this.layers, bucketFeature, { zoom: this.zoom }, options));
|
||||
}
|
||||
else {
|
||||
@ -29863,7 +30012,7 @@ class LineBucket {
|
||||
this.layers = options.layers;
|
||||
this.layerIds = this.layers.map(layer => layer.id);
|
||||
this.index = options.index;
|
||||
this.hasPattern = false;
|
||||
this.hasDependencies = false;
|
||||
this.patternFeatures = [];
|
||||
this.lineClipsArray = [];
|
||||
this.gradients = {};
|
||||
@ -29879,7 +30028,7 @@ class LineBucket {
|
||||
this.stateDependentLayerIds = this.layers.filter((l) => l.isStateDependent()).map((l) => l.id);
|
||||
}
|
||||
populate(features, options, canonical) {
|
||||
this.hasPattern = hasPattern('line', this.layers, options);
|
||||
this.hasDependencies = hasPattern('line', this.layers, options) || this.hasLineDasharray(this.layers);
|
||||
const lineSortKey = this.layers[0].layout.get('line-sort-key');
|
||||
const sortFeaturesByKey = !lineSortKey.isConstant();
|
||||
const bucketFeatures = [];
|
||||
@ -29899,6 +30048,7 @@ class LineBucket {
|
||||
index,
|
||||
geometry: needGeometry ? evaluationFeature.geometry : loadGeometry(feature),
|
||||
patterns: {},
|
||||
dashes: {},
|
||||
sortKey
|
||||
};
|
||||
bucketFeatures.push(bucketFeature);
|
||||
@ -29910,29 +30060,35 @@ class LineBucket {
|
||||
}
|
||||
for (const bucketFeature of bucketFeatures) {
|
||||
const { geometry, index, sourceLayerIndex } = bucketFeature;
|
||||
if (this.hasPattern) {
|
||||
const patternBucketFeature = addPatternDependencies('line', this.layers, bucketFeature, { zoom: this.zoom }, options);
|
||||
if (this.hasDependencies) {
|
||||
if (hasPattern('line', this.layers, options)) {
|
||||
addPatternDependencies('line', this.layers, bucketFeature, { zoom: this.zoom }, options);
|
||||
}
|
||||
else if (this.hasLineDasharray(this.layers)) {
|
||||
this.addLineDashDependencies(this.layers, bucketFeature, this.zoom, options);
|
||||
}
|
||||
// pattern features are added only once the pattern is loaded into the image atlas
|
||||
// so are stored during populate until later updated with positions by tile worker in addFeatures
|
||||
this.patternFeatures.push(patternBucketFeature);
|
||||
this.patternFeatures.push(bucketFeature);
|
||||
}
|
||||
else {
|
||||
this.addFeature(bucketFeature, geometry, index, canonical, {}, options.subdivisionGranularity);
|
||||
this.addFeature(bucketFeature, geometry, index, canonical, {}, {}, options.subdivisionGranularity);
|
||||
}
|
||||
const feature = features[index].feature;
|
||||
options.featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index);
|
||||
}
|
||||
}
|
||||
update(states, vtLayer, imagePositions) {
|
||||
update(states, vtLayer, imagePositions, dashPositions) {
|
||||
if (!this.stateDependentLayers.length)
|
||||
return;
|
||||
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers, {
|
||||
imagePositions
|
||||
imagePositions,
|
||||
dashPositions
|
||||
});
|
||||
}
|
||||
addFeatures(options, canonical, imagePositions) {
|
||||
addFeatures(options, canonical, imagePositions, dashPositions) {
|
||||
for (const feature of this.patternFeatures) {
|
||||
this.addFeature(feature, feature.geometry, feature.index, canonical, imagePositions, options.subdivisionGranularity);
|
||||
this.addFeature(feature, feature.geometry, feature.index, canonical, imagePositions, dashPositions, options.subdivisionGranularity);
|
||||
}
|
||||
}
|
||||
isEmpty() {
|
||||
@ -29967,7 +30123,7 @@ class LineBucket {
|
||||
return { start, end };
|
||||
}
|
||||
}
|
||||
addFeature(feature, geometry, index, canonical, imagePositions, subdivisionGranularity) {
|
||||
addFeature(feature, geometry, index, canonical, imagePositions, dashPositions, subdivisionGranularity) {
|
||||
const layout = this.layers[0].layout;
|
||||
const join = layout.get('line-join').evaluate(feature, {});
|
||||
const cap = layout.get('line-cap');
|
||||
@ -29977,7 +30133,7 @@ class LineBucket {
|
||||
for (const line of geometry) {
|
||||
this.addLine(line, feature, join, cap, miterLimit, roundLimit, canonical, subdivisionGranularity);
|
||||
}
|
||||
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, { imagePositions, canonical });
|
||||
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, { imagePositions, dashPositions, canonical });
|
||||
}
|
||||
addLine(vertices, feature, join, cap, miterLimit, roundLimit, canonical, subdivisionGranularity) {
|
||||
this.distance = 0;
|
||||
@ -30268,6 +30424,43 @@ class LineBucket {
|
||||
this.distance += prev.dist(next);
|
||||
this.updateScaledDistance();
|
||||
}
|
||||
hasLineDasharray(layers) {
|
||||
for (const layer of layers) {
|
||||
const dasharrayProperty = layer.paint.get('line-dasharray');
|
||||
if (dasharrayProperty && !dasharrayProperty.isConstant()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
addLineDashDependencies(layers, bucketFeature, zoom, options) {
|
||||
for (const layer of layers) {
|
||||
const dasharrayProperty = layer.paint.get('line-dasharray');
|
||||
if (!dasharrayProperty || dasharrayProperty.value.kind === 'constant') {
|
||||
continue;
|
||||
}
|
||||
const round = layer.layout.get('line-cap') === 'round';
|
||||
const min = {
|
||||
dasharray: dasharrayProperty.value.evaluate({ zoom: zoom - 1 }, bucketFeature, {}),
|
||||
round
|
||||
};
|
||||
const mid = {
|
||||
dasharray: dasharrayProperty.value.evaluate({ zoom }, bucketFeature, {}),
|
||||
round
|
||||
};
|
||||
const max = {
|
||||
dasharray: dasharrayProperty.value.evaluate({ zoom: zoom + 1 }, bucketFeature, {}),
|
||||
round
|
||||
};
|
||||
const minKey = `${min.dasharray.join(',')},${min.round}`;
|
||||
const midKey = `${mid.dasharray.join(',')},${mid.round}`;
|
||||
const maxKey = `${max.dasharray.join(',')},${max.round}`;
|
||||
options.dashDependencies[minKey] = min;
|
||||
options.dashDependencies[midKey] = mid;
|
||||
options.dashDependencies[maxKey] = max;
|
||||
bucketFeature.dashes[layer.id] = { min: minKey, mid: midKey, max: maxKey };
|
||||
}
|
||||
}
|
||||
}
|
||||
register('LineBucket', LineBucket, { omit: ['layers', 'patternFeatures'] });
|
||||
|
||||
@ -30291,7 +30484,7 @@ const getPaint$3 = () => paint$3 = paint$3 || new Properties({
|
||||
"line-gap-width": new DataDrivenProperty(v8Spec["paint_line"]["line-gap-width"]),
|
||||
"line-offset": new DataDrivenProperty(v8Spec["paint_line"]["line-offset"]),
|
||||
"line-blur": new DataDrivenProperty(v8Spec["paint_line"]["line-blur"]),
|
||||
"line-dasharray": new CrossFadedProperty(v8Spec["paint_line"]["line-dasharray"]),
|
||||
"line-dasharray": new CrossFadedDataDrivenProperty(v8Spec["paint_line"]["line-dasharray"]),
|
||||
"line-pattern": new CrossFadedDataDrivenProperty(v8Spec["paint_line"]["line-pattern"]),
|
||||
"line-gradient": new ColorRampProperty(v8Spec["paint_line"]["line-gradient"]),
|
||||
});
|
||||
@ -32674,13 +32867,13 @@ class SymbolBucket {
|
||||
constructor(options) {
|
||||
this.collisionBoxArray = options.collisionBoxArray;
|
||||
this.zoom = options.zoom;
|
||||
this.overscaling = options.overscaling;
|
||||
this.overscaling = isSafari(globalThis) ? Math.min(options.overscaling, 128) : options.overscaling;
|
||||
this.layers = options.layers;
|
||||
this.layerIds = this.layers.map(layer => layer.id);
|
||||
this.index = options.index;
|
||||
this.pixelRatio = options.pixelRatio;
|
||||
this.sourceLayerIndex = options.sourceLayerIndex;
|
||||
this.hasPattern = false;
|
||||
this.hasDependencies = false;
|
||||
this.hasRTLText = false;
|
||||
this.sortKeyRanges = [];
|
||||
this.collisionCircleArray = [];
|
||||
@ -34055,7 +34248,8 @@ function getAnchors(line, spacing, maxAngle, shapedText, shapedIcon, glyphSize,
|
||||
function resample(line, offset, spacing, angleWindowSize, maxAngle, labelLength, isLineContinued, placeAtMiddle, tileExtent) {
|
||||
const halfLabelLength = labelLength / 2;
|
||||
const lineLength = getLineLength(line);
|
||||
let distance = 0, markedDistance = offset - spacing;
|
||||
let distance = 0;
|
||||
let markedDistance = offset - spacing;
|
||||
let anchors = [];
|
||||
for (let i = 0; i < line.length - 1; i++) {
|
||||
const a = line[i], b = line[i + 1];
|
||||
@ -35663,7 +35857,9 @@ class CanonicalTileID {
|
||||
equals(id) {
|
||||
return this.z === id.z && this.x === id.x && this.y === id.y;
|
||||
}
|
||||
// given a list of urls, choose a url template and return a tile URL
|
||||
/**
|
||||
* given a list of urls, choose a url template and return a tile URL
|
||||
*/
|
||||
url(urls, pixelRatio, scheme) {
|
||||
const bbox = getTileBBox(this.x, this.y, this.z);
|
||||
const quadkey = getQuadkey(this.z, this.x, this.y);
|
||||
@ -35724,6 +35920,14 @@ class OverscaledTileID {
|
||||
equals(id) {
|
||||
return this.overscaledZ === id.overscaledZ && this.wrap === id.wrap && this.canonical.equals(id.canonical);
|
||||
}
|
||||
/**
|
||||
* Returns a new `OverscaledTileID` representing the tile at the target zoom level.
|
||||
* When targetZ is greater than the current canonical z, the canonical coordinates are unchanged.
|
||||
* When targetZ is less than the current canonical z, the canonical coordinates are updated.
|
||||
* @param targetZ - the zoom level to scale to. Must be less than or equal to this.overscaledZ
|
||||
* @returns a new OverscaledTileID representing the tile at the target zoom level
|
||||
* @throws if targetZ is greater than this.overscaledZ
|
||||
*/
|
||||
scaledTo(targetZ) {
|
||||
if (targetZ > this.overscaledZ)
|
||||
throw new Error(`targetZ > this.overscaledZ; targetZ = ${targetZ}; overscaledZ = ${this.overscaledZ}`);
|
||||
@ -35735,6 +35939,9 @@ class OverscaledTileID {
|
||||
return new OverscaledTileID(targetZ, this.wrap, targetZ, this.canonical.x >> zDifference, this.canonical.y >> zDifference);
|
||||
}
|
||||
}
|
||||
isOverscaled() {
|
||||
return (this.overscaledZ > this.canonical.z);
|
||||
}
|
||||
/*
|
||||
* calculateScaledKey is an optimization:
|
||||
* when withWrap == true, implements the same as this.scaledTo(z).key,
|
||||
@ -35864,6 +36071,7 @@ class WorkerTile {
|
||||
iconDependencies: {},
|
||||
patternDependencies: {},
|
||||
glyphDependencies: {},
|
||||
dashDependencies: {},
|
||||
availableImages,
|
||||
subdivisionGranularity
|
||||
};
|
||||
@ -35889,11 +36097,7 @@ class WorkerTile {
|
||||
if (layer.source !== this.source) {
|
||||
warnOnce(`layer.source = ${layer.source} does not equal this.source = ${this.source}`);
|
||||
}
|
||||
if (layer.minzoom && this.zoom < Math.floor(layer.minzoom))
|
||||
continue;
|
||||
if (layer.maxzoom && this.zoom >= layer.maxzoom)
|
||||
continue;
|
||||
if (layer.visibility === 'none')
|
||||
if (layer.isHidden(this.zoom, true))
|
||||
continue;
|
||||
recalculateLayers(family, this.zoom, availableImages);
|
||||
const bucket = buckets[layer.id] = layer.createBucket({
|
||||
@ -35935,7 +36139,14 @@ class WorkerTile {
|
||||
this.inFlightDependencies.push(abortController);
|
||||
getPatternsPromise = actor.sendAsync({ type: "GI" /* MessageType.getImages */, data: { icons: patterns, source: this.source, tileID: this.tileID, type: 'patterns' } }, abortController);
|
||||
}
|
||||
const [glyphMap, iconMap, patternMap] = yield Promise.all([getGlyphsPromise, getIconsPromise, getPatternsPromise]);
|
||||
const dashes = options.dashDependencies;
|
||||
let getDashesPromise = Promise.resolve({});
|
||||
if (Object.keys(dashes).length) {
|
||||
const abortController = new AbortController();
|
||||
this.inFlightDependencies.push(abortController);
|
||||
getDashesPromise = actor.sendAsync({ type: "GDA" /* MessageType.getDashes */, data: { dashes } }, abortController);
|
||||
}
|
||||
const [glyphMap, iconMap, patternMap, dashPositions] = yield Promise.all([getGlyphsPromise, getIconsPromise, getPatternsPromise, getDashesPromise]);
|
||||
const glyphAtlas = new GlyphAtlas(glyphMap);
|
||||
const imageAtlas = new ImageAtlas(iconMap, patternMap);
|
||||
for (const key in buckets) {
|
||||
@ -35953,12 +36164,9 @@ class WorkerTile {
|
||||
subdivisionGranularity: options.subdivisionGranularity
|
||||
});
|
||||
}
|
||||
else if (bucket.hasPattern &&
|
||||
(bucket instanceof LineBucket ||
|
||||
bucket instanceof FillBucket ||
|
||||
bucket instanceof FillExtrusionBucket)) {
|
||||
else if (bucket.hasDependencies && (bucket instanceof FillBucket || bucket instanceof FillExtrusionBucket || bucket instanceof LineBucket)) {
|
||||
recalculateLayers(bucket.layers, this.zoom, availableImages);
|
||||
bucket.addFeatures(options, this.tileID.canonical, imageAtlas.patternPositions);
|
||||
bucket.addFeatures(options, this.tileID.canonical, imageAtlas.patternPositions, dashPositions);
|
||||
}
|
||||
}
|
||||
this.status = 'done';
|
||||
@ -35968,6 +36176,7 @@ class WorkerTile {
|
||||
collisionBoxArray: this.collisionBoxArray,
|
||||
glyphAtlasImage: glyphAtlas.image,
|
||||
imageAtlas,
|
||||
dashPositions,
|
||||
// Only used for benchmarking:
|
||||
glyphMap: this.returnDependencies ? glyphMap : null,
|
||||
iconMap: this.returnDependencies ? iconMap : null,
|
||||
@ -38115,9 +38324,10 @@ function mergeSourceDiffs(existingDiff, newDiff) {
|
||||
* For a full example, see [mapbox-gl-topojson](https://github.com/developmentseed/mapbox-gl-topojson).
|
||||
*/
|
||||
class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
constructor(actor, layerIndex, availableImages, createGeoJSONIndexFunc = createGeoJSONIndex) {
|
||||
super(actor, layerIndex, availableImages);
|
||||
this._dataUpdateable = new Map();
|
||||
this._createGeoJSONIndex = createGeoJSONIndexFunc;
|
||||
}
|
||||
loadVectorTile(params, _abortController) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
@ -38130,8 +38340,8 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
||||
return null;
|
||||
}
|
||||
const geojsonWrapper = new o(geoJSONTile.features, { version: 2, extent: EXTENT$1 });
|
||||
// Encode the geojson-vt tile into binary vector tile form. This
|
||||
// is a convenience that allows `FeatureIndex` to operate the same way
|
||||
// Encode the geojson-vt tile into binary vector tile form.
|
||||
// This is a convenience that allows `FeatureIndex` to operate the same way
|
||||
// across `VectorTileSource` and `GeoJSONSource` data.
|
||||
let pbf = s(geojsonWrapper);
|
||||
if (pbf.byteOffset !== 0 || pbf.byteLength !== pbf.buffer.byteLength) {
|
||||
@ -38147,7 +38357,10 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
||||
/**
|
||||
* Fetches (if appropriate), parses, and index geojson data into tiles. This
|
||||
* preparatory method must be called before {@link GeoJSONWorkerSource.loadTile}
|
||||
* can correctly serve up tiles.
|
||||
* can correctly serve up tiles. The first call to this method must contain a valid
|
||||
* {@link params.data}, {@link params.request}, or {@link params.dataDiff}. Subsequent
|
||||
* calls may omit these parameters to reprocess the existing data (such as to update
|
||||
* clustering options).
|
||||
*
|
||||
* Defers to {@link GeoJSONWorkerSource.loadAndProcessGeoJSON} for the pre-processing.
|
||||
*
|
||||
@ -38165,11 +38378,13 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
||||
new RequestPerformance(params.request) : false;
|
||||
this._pendingRequest = new AbortController();
|
||||
try {
|
||||
this._pendingData = this.loadAndProcessGeoJSON(params, this._pendingRequest);
|
||||
// Load and process data if no data has been loaded previously, or if there is
|
||||
// a new request, data, or dataDiff to process.
|
||||
if (!this._pendingData || params.request || params.data || params.dataDiff) {
|
||||
this._pendingData = this.loadAndProcessGeoJSON(params, this._pendingRequest);
|
||||
}
|
||||
const data = yield this._pendingData;
|
||||
this._geoJSONIndex = params.cluster ?
|
||||
new Supercluster(getSuperclusterOptions(params)).load(data.features) :
|
||||
geojsonvt(data, params.geojsonVtOptions);
|
||||
this._geoJSONIndex = this._createGeoJSONIndex(data, params);
|
||||
this.loaded = {};
|
||||
const result = { data };
|
||||
if (perf) {
|
||||
@ -38302,6 +38517,10 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
||||
return this._geoJSONIndex.getLeaves(params.clusterId, params.limit, params.offset);
|
||||
}
|
||||
}
|
||||
function createGeoJSONIndex(data, params) {
|
||||
return params.cluster ? new Supercluster(getSuperclusterOptions(params)).load(data.features) :
|
||||
geojsonvt(data, params.geojsonVtOptions);
|
||||
}
|
||||
function getSuperclusterOptions({ superclusterOptions, clusterProperties }) {
|
||||
if (!clusterProperties || !superclusterOptions)
|
||||
return superclusterOptions;
|
||||
|
||||
2
node_modules/maplibre-gl/dist/maplibre-gl-csp-worker-dev.js.map
generated
vendored
2
node_modules/maplibre-gl/dist/maplibre-gl-csp-worker-dev.js.map
generated
vendored
File diff suppressed because one or more lines are too long
4
node_modules/maplibre-gl/dist/maplibre-gl-csp-worker.js
generated
vendored
4
node_modules/maplibre-gl/dist/maplibre-gl-csp-worker.js
generated
vendored
File diff suppressed because one or more lines are too long
2
node_modules/maplibre-gl/dist/maplibre-gl-csp-worker.js.map
generated
vendored
2
node_modules/maplibre-gl/dist/maplibre-gl-csp-worker.js.map
generated
vendored
File diff suppressed because one or more lines are too long
4
node_modules/maplibre-gl/dist/maplibre-gl-csp.js
generated
vendored
4
node_modules/maplibre-gl/dist/maplibre-gl-csp.js
generated
vendored
File diff suppressed because one or more lines are too long
2
node_modules/maplibre-gl/dist/maplibre-gl-csp.js.map
generated
vendored
2
node_modules/maplibre-gl/dist/maplibre-gl-csp.js.map
generated
vendored
File diff suppressed because one or more lines are too long
2246
node_modules/maplibre-gl/dist/maplibre-gl-dev.js
generated
vendored
2246
node_modules/maplibre-gl/dist/maplibre-gl-dev.js
generated
vendored
File diff suppressed because it is too large
Load Diff
2
node_modules/maplibre-gl/dist/maplibre-gl-dev.js.map
generated
vendored
2
node_modules/maplibre-gl/dist/maplibre-gl-dev.js.map
generated
vendored
File diff suppressed because one or more lines are too long
420
node_modules/maplibre-gl/dist/maplibre-gl.d.ts
generated
vendored
420
node_modules/maplibre-gl/dist/maplibre-gl.d.ts
generated
vendored
@ -3,7 +3,7 @@
|
||||
import Point from '@mapbox/point-geometry';
|
||||
import TinySDF from '@mapbox/tiny-sdf';
|
||||
import { VectorTileFeature, VectorTileLayer } from '@mapbox/vector-tile';
|
||||
import { Color, ColorArray, CompositeExpression, DiffCommand, DiffOperations, Feature, FeatureFilter, FeatureState, FilterSpecification, Formatted, FormattedSection, GeoJSONSourceSpecification, GlobalProperties, ICanonicalTileID, IMercatorCoordinate, ImageSourceSpecification, InterpolationType, LayerSpecification, LightSpecification, NumberArray, Padding, ProjectionSpecification, PromoteIdSpecification, PropertyValueSpecification, RasterDEMSourceSpecification, RasterSourceSpecification, ResolvedImage, SkySpecification, SourceExpression, SourceSpecification, SpriteSpecification, StateSpecification, StylePropertyExpression, StylePropertySpecification, StyleSpecification, TerrainSpecification, TransitionSpecification, VariableAnchorOffsetCollection, VectorSourceSpecification, VideoSourceSpecification } from '@maplibre/maplibre-gl-style-spec';
|
||||
import { Color, ColorArray, CompositeExpression, DiffCommand, DiffOperations, Feature, Feature as StyleFeature, FeatureFilter, FeatureState, FilterSpecification, Formatted, FormattedSection, GeoJSONSourceSpecification, GlobalProperties, ICanonicalTileID, IMercatorCoordinate, ImageSourceSpecification, InterpolationType, LayerSpecification, LightSpecification, NumberArray, Padding, ProjectionSpecification, PromoteIdSpecification, PropertyValueSpecification, RasterDEMSourceSpecification, RasterSourceSpecification, ResolvedImage, SkySpecification, SourceExpression, SourceSpecification, SpriteSpecification, StateSpecification, StylePropertyExpression, StylePropertySpecification, StyleSpecification, TerrainSpecification, TransitionSpecification, VariableAnchorOffsetCollection, VectorSourceSpecification, VideoSourceSpecification } from '@maplibre/maplibre-gl-style-spec';
|
||||
import { Options as GeoJSONVTOptions } from 'geojson-vt';
|
||||
import { mat2, mat4, vec3, vec4 } from 'gl-matrix';
|
||||
import KDBush from 'kdbush';
|
||||
@ -388,6 +388,7 @@ declare class PlacedSymbolStruct extends Struct {
|
||||
set crossTileID(x: number);
|
||||
get associatedIconIndex(): number;
|
||||
}
|
||||
type PlacedSymbol = PlacedSymbolStruct;
|
||||
declare class PlacedSymbolArray extends StructArrayLayout2i2ui3ul3ui2f3ub1ul1i48 {
|
||||
/**
|
||||
* Return the PlacedSymbolStruct at the given location in the array.
|
||||
@ -708,6 +709,9 @@ declare class CanonicalTileID implements ICanonicalTileID {
|
||||
key: string;
|
||||
constructor(z: number, x: number, y: number);
|
||||
equals(id: ICanonicalTileID): boolean;
|
||||
/**
|
||||
* given a list of urls, choose a url template and return a tile URL
|
||||
*/
|
||||
url(urls: Array<string>, pixelRatio: number, scheme?: string | null): string;
|
||||
isChildOf(parent: ICanonicalTileID): boolean;
|
||||
getTilePoint(coord: IMercatorCoordinate): Point;
|
||||
@ -737,7 +741,16 @@ export declare class OverscaledTileID {
|
||||
constructor(overscaledZ: number, wrap: number, z: number, x: number, y: number);
|
||||
clone(): OverscaledTileID;
|
||||
equals(id: OverscaledTileID): boolean;
|
||||
/**
|
||||
* Returns a new `OverscaledTileID` representing the tile at the target zoom level.
|
||||
* When targetZ is greater than the current canonical z, the canonical coordinates are unchanged.
|
||||
* When targetZ is less than the current canonical z, the canonical coordinates are updated.
|
||||
* @param targetZ - the zoom level to scale to. Must be less than or equal to this.overscaledZ
|
||||
* @returns a new OverscaledTileID representing the tile at the target zoom level
|
||||
* @throws if targetZ is greater than this.overscaledZ
|
||||
*/
|
||||
scaledTo(targetZ: number): OverscaledTileID;
|
||||
isOverscaled(): boolean;
|
||||
calculateScaledKey(targetZ: number, withWrap: boolean): string;
|
||||
isChildOf(parent: OverscaledTileID): boolean;
|
||||
children(sourceMaxZoom: number): OverscaledTileID[];
|
||||
@ -963,13 +976,6 @@ declare class CrossFadedDataDrivenProperty<T> extends DataDrivenProperty<CrossFa
|
||||
_calculate(min: T, mid: T, max: T, parameters: EvaluationParameters): CrossFaded<T>;
|
||||
interpolate(a: PossiblyEvaluatedPropertyValue<CrossFaded<T>>): PossiblyEvaluatedPropertyValue<CrossFaded<T>>;
|
||||
}
|
||||
declare class CrossFadedProperty<T> implements Property<T, CrossFaded<T>> {
|
||||
specification: StylePropertySpecification;
|
||||
constructor(specification: StylePropertySpecification);
|
||||
possiblyEvaluate(value: PropertyValue<T, CrossFaded<T>>, parameters: EvaluationParameters, canonical?: CanonicalTileID, availableImages?: Array<string>): CrossFaded<T>;
|
||||
_calculate(min: T, mid: T, max: T, parameters: EvaluationParameters): CrossFaded<T>;
|
||||
interpolate(a?: CrossFaded<T> | null): CrossFaded<T>;
|
||||
}
|
||||
declare class ColorRampProperty implements Property<Color, boolean> {
|
||||
specification: StylePropertySpecification;
|
||||
constructor(specification: StylePropertySpecification);
|
||||
@ -2583,38 +2589,24 @@ declare class SourceCache extends Evented {
|
||||
*/
|
||||
_sourceLoaded: boolean;
|
||||
_sourceErrored: boolean;
|
||||
_tiles: {
|
||||
[_: string]: Tile;
|
||||
};
|
||||
_tiles: Record<string, Tile>;
|
||||
_prevLng: number;
|
||||
_cache: TileCache;
|
||||
_timers: {
|
||||
[_ in any]: ReturnType<typeof setTimeout>;
|
||||
};
|
||||
_cacheTimers: {
|
||||
[_ in any]: ReturnType<typeof setTimeout>;
|
||||
};
|
||||
_timers: Record<string, ReturnType<typeof setTimeout>>;
|
||||
_maxTileCacheSize: number;
|
||||
_maxTileCacheZoomLevels: number;
|
||||
_paused: boolean;
|
||||
_shouldReloadOnResume: boolean;
|
||||
_coveredTiles: {
|
||||
[_: string]: boolean;
|
||||
};
|
||||
transform: ITransform;
|
||||
terrain: Terrain;
|
||||
used: boolean;
|
||||
usedForTerrain: boolean;
|
||||
tileSize: number;
|
||||
_state: SourceFeatureState;
|
||||
_loadedParentTiles: {
|
||||
[_: string]: Tile;
|
||||
};
|
||||
_loadedSiblingTiles: {
|
||||
[_: string]: Tile;
|
||||
};
|
||||
_didEmitContent: boolean;
|
||||
_updated: boolean;
|
||||
_rasterFadeDuration: number;
|
||||
_maxFadingAncestorLevels: number;
|
||||
static maxUnderzooming: number;
|
||||
static maxOverzooming: number;
|
||||
constructor(id: string, options: SourceSpecification | CanvasSourceSpecification, dispatcher: Dispatcher);
|
||||
@ -2640,6 +2632,10 @@ declare class SourceCache extends Evented {
|
||||
getRenderableIds(symbolLayer?: boolean): Array<string>;
|
||||
hasRenderableParent(tileID: OverscaledTileID): boolean;
|
||||
_isIdRenderable(id: string, symbolLayer?: boolean): boolean;
|
||||
/**
|
||||
* Reload tiles in this source. If source data has changed, reload all tiles using a state of 'expired',
|
||||
* otherwise reload only non-errored tiles using state of 'reloading'.
|
||||
*/
|
||||
reload(sourceDataChanged?: boolean): void;
|
||||
_reloadTile(id: string, state: TileState): Promise<void>;
|
||||
_tileLoaded(tile: Tile, id: string, previousState: TileState): void;
|
||||
@ -2675,26 +2671,22 @@ declare class SourceCache extends Evented {
|
||||
* - one sheet = ideal tiles at varying overscaledZ
|
||||
* - the second sheet = maxCoveringZoom
|
||||
*/
|
||||
_retainLoadedChildren(targetTiles: {
|
||||
[_: string]: OverscaledTileID;
|
||||
}, retain: {
|
||||
[_: string]: OverscaledTileID;
|
||||
}): void;
|
||||
_retainLoadedChildren(targetTiles: Record<string, OverscaledTileID>, retain: Record<string, OverscaledTileID>): Record<string, OverscaledTileID>;
|
||||
/**
|
||||
* Return dictionary of qualified loaded descendents for each provided target tile id
|
||||
*/
|
||||
_getLoadedDescendents(targetTileIDs: OverscaledTileID[]): {
|
||||
[_: string]: Tile[];
|
||||
};
|
||||
_getLoadedDescendents(targetTileIDs: OverscaledTileID[]): Record<string, Tile[]>;
|
||||
/**
|
||||
* Find a loaded parent of the given tile (up to minCoveringZoom)
|
||||
* Determine if tile ids fully cover the current generation.
|
||||
* - 1st generation: need 4 children or 1 overscaled child
|
||||
* - 2nd generation: need 16 children or 1 overscaled child
|
||||
*/
|
||||
findLoadedParent(tileID: OverscaledTileID, minCoveringZoom: number): Tile;
|
||||
_areDescendentsComplete(generationIDs: OverscaledTileID[], generationZ: number, ancestorZ: number): boolean;
|
||||
/**
|
||||
* Find a loaded sibling of the given tile
|
||||
* Get a loaded tile currently in this source.
|
||||
* - loaded tiles exist in this._tiles - a cached tile is not a loaded tile
|
||||
*/
|
||||
findLoadedSibling(tileID: OverscaledTileID): Tile;
|
||||
_getLoadedTile(tileID: OverscaledTileID): Tile;
|
||||
_getLoadedTile(tileID: OverscaledTileID): Tile | null;
|
||||
/**
|
||||
* Resizes the tile cache based on the current viewport's size
|
||||
* or the maxTileCacheSize option passed during map creation
|
||||
@ -2705,38 +2697,84 @@ declare class SourceCache extends Evented {
|
||||
*/
|
||||
updateCacheSize(transform: IReadonlyTransform): void;
|
||||
handleWrapJump(lng: number): void;
|
||||
_updateCoveredAndRetainedTiles(retain: {
|
||||
[_: string]: OverscaledTileID;
|
||||
}, minCoveringZoom: number, idealTileIDs: OverscaledTileID[], terrain?: Terrain): void;
|
||||
/**
|
||||
* Removes tiles that are outside the viewport and adds new tiles that
|
||||
* are inside the viewport.
|
||||
*/
|
||||
update(transform: ITransform, terrain?: Terrain): void;
|
||||
/**
|
||||
* Remove raster tiles that are no longer retained
|
||||
*/
|
||||
_cleanUpRasterTiles(retain: Record<string, OverscaledTileID>): void;
|
||||
/**
|
||||
* Remove vector tiles that are no longer retained and also not needed for symbol fading
|
||||
*/
|
||||
_cleanUpVectorTiles(retain: Record<string, OverscaledTileID>): void;
|
||||
/**
|
||||
* Add ideal tiles needed for 3D terrain rendering
|
||||
*/
|
||||
_addTerrainIdealTiles(idealTileIDs: OverscaledTileID[]): OverscaledTileID[];
|
||||
releaseSymbolFadeTiles(): void;
|
||||
/**
|
||||
* Set tiles to be retained on update of this source. For ideal tiles that do not have data, retain their loaded
|
||||
* children so they can be displayed as substitutes pending load of each ideal tile (to reduce flickering).
|
||||
* If no loaded children are available, fallback to seeking loaded parents as an alternative substitute.
|
||||
*/
|
||||
_updateRetainedTiles(idealTileIDs: Array<OverscaledTileID>, zoom: number): {
|
||||
[_: string]: OverscaledTileID;
|
||||
};
|
||||
_updateLoadedParentTileCache(): void;
|
||||
_updateRetainedTiles(idealTileIDs: Array<OverscaledTileID>, zoom: number): Record<string, OverscaledTileID>;
|
||||
/**
|
||||
* Update the cache of loaded sibling tiles
|
||||
* Designate fading bases and parents using a many-to-one relationship where the lower children fade in/out
|
||||
* with their parents. Raster shaders are not currently designed for a one-to-many fade relationship.
|
||||
*
|
||||
* Sibling tiles are tiles that share the same zoom level and
|
||||
* x/y position but have different wrap values
|
||||
* Maintaining sibling tile cache allows fading from old to new tiles
|
||||
* of the same position and zoom level
|
||||
* Tiles that are candidates for fading out must be loaded and rendered tiles, as loading a tile to then
|
||||
* fade it out would not appear smoothly. The first source of truth for tile fading always starts at the
|
||||
* ideal tile, which continually changes on map adjustment. The state of the previously rendered ideal
|
||||
* tile plane indicates which direction to fade each part of the newer ideal plane (with varying z).
|
||||
*
|
||||
* For a pitched map, the back of the map can have decreasing zooms while the front can have increasing zooms.
|
||||
* Fade logic must therefore adapt dynamically based on the previously rendered ideal tile set.
|
||||
*/
|
||||
_updateLoadedSiblingTileCache(): void;
|
||||
_updateFadingTiles(idealTileIDs: OverscaledTileID[], retain: Record<string, OverscaledTileID>): void;
|
||||
/**
|
||||
* Many-to-one cross-fade. Set 4 ideal tiles as the fading base for a rendered parent tile
|
||||
* as the fading parent. Here the parent is fading out and the ideal tile is fading in.
|
||||
*
|
||||
* Parent tile - fading out ■ -- Fading Parent
|
||||
* ┌──────────────┬──────┴───────┬──────────────┐
|
||||
* Ideal tiles - fading in ■ ■ ■ ■ -- Base Role = Incoming
|
||||
* ┌───┬─┴─┬───┐ ┌───┬─┴─┬───┐ ┌───┬─┴─┬───┐ ┌───┬─┴─┬───┐
|
||||
* ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
|
||||
*/
|
||||
_updateFadingAncestor(idealTile: Tile, retain: Record<string, OverscaledTileID>, now: number): boolean;
|
||||
/**
|
||||
* Many-to-one cross-fade. Search descendents of ideal tiles as the fading base with the ideal tile
|
||||
* as the fading parent. Here the children are fading out and the ideal tile is fading in.
|
||||
*
|
||||
* ■
|
||||
* ┌──────────────┬──────┴───────┬──────────────┐
|
||||
* Ideal tiles - fading in ■ ■ ■ ■ -- Fading Parent
|
||||
* ┌───┬─┴─┬───┐ ┌───┬─┴─┬───┐ ┌───┬─┴─┬───┐ ┌───┬─┴─┬───┐
|
||||
* Child tiles - fading out ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ -- Base Role = Departing
|
||||
*
|
||||
* Try direct children first. If none found, try grandchildren. Stops at the first generation that provides a fader.
|
||||
*/
|
||||
_updateFadingDescendents(idealTile: Tile, retain: Record<string, OverscaledTileID>, now: number): boolean;
|
||||
_updateFadingChildren(idealTile: Tile, childIDs: OverscaledTileID[], retain: Record<string, OverscaledTileID>, now: number): boolean;
|
||||
/**
|
||||
* One-to-one self fading for unloaded edge tiles (for panning sideways on map). for loading tiles over gaps it feels
|
||||
* more natural for them to fade in, however if they are already loaded/cached then there is no need to fade as map will
|
||||
* look cohesive with no gaps. Note that draw_raster determines fade priority, as many-to-one fade supersedes edge fading.
|
||||
*/
|
||||
_updateFadingEdge(idealTile: Tile, edgeTileIDs: Set<OverscaledTileID>, now: number): boolean;
|
||||
/**
|
||||
* Add a tile, given its coordinate, to the pyramid.
|
||||
*/
|
||||
_addTile(tileID: OverscaledTileID): Tile;
|
||||
/**
|
||||
* Set a timeout to reload the tile after it expires
|
||||
*/
|
||||
_setTileReloadTimer(id: string, tile: Tile): void;
|
||||
_clearTileReloadTimer(id: string): void;
|
||||
_resetTileReloadTimers(): void;
|
||||
/**
|
||||
* Reload any currently renderable tiles that are match one of the incoming `tileId` x/y/z
|
||||
*/
|
||||
@ -2745,7 +2783,11 @@ declare class SourceCache extends Evented {
|
||||
* Remove a tile, given its id, from the pyramid
|
||||
*/
|
||||
_removeTile(id: string): void;
|
||||
/** @internal */
|
||||
/** @internal
|
||||
* Handles incoming source data messages (i.e. after the source has been updated via a worker that has fired
|
||||
* to map.ts data event). For sources with mutable data, the 'content' event fires when the underlying data
|
||||
* to a source has changed. (i.e. GeoJSONSource.setData and ImageSource.setCoordinates)
|
||||
*/
|
||||
private _dataHandler;
|
||||
/**
|
||||
* Remove all tiles from this pyramid
|
||||
@ -2761,6 +2803,7 @@ declare class SourceCache extends Evented {
|
||||
private transformBbox;
|
||||
getVisibleCoordinates(symbolLayer?: boolean): Array<OverscaledTileID>;
|
||||
hasTransition(): boolean;
|
||||
setRasterFadeDuration(fadeDuration: number): void;
|
||||
/**
|
||||
* Set the value of a particular state for a feature
|
||||
*/
|
||||
@ -3138,7 +3181,7 @@ declare class SymbolBucket implements Bucket {
|
||||
iconsNeedLinear: boolean;
|
||||
bucketInstanceId: number;
|
||||
justReloaded: boolean;
|
||||
hasPattern: boolean;
|
||||
hasDependencies: boolean;
|
||||
textSizeData: SizeData;
|
||||
iconSizeData: SizeData;
|
||||
glyphOffsetArray: GlyphOffsetArray;
|
||||
@ -3303,7 +3346,10 @@ declare class Mesh {
|
||||
constructor(vertexBuffer: VertexBuffer, indexBuffer: IndexBuffer, segments: SegmentVector);
|
||||
destroy(): void;
|
||||
}
|
||||
type DashEntry = {
|
||||
/**
|
||||
* A dash entry
|
||||
*/
|
||||
export type DashEntry = {
|
||||
y: number;
|
||||
height: number;
|
||||
width: number;
|
||||
@ -4114,6 +4160,14 @@ interface CoveringTilesDetailsProvider {
|
||||
*/
|
||||
prepareNextFrame(): void;
|
||||
}
|
||||
/**
|
||||
* The callback defining how the transform constrains the viewport's lnglat and zoom to respect the longitude and latitude bounds.
|
||||
* @see [Customize the map transform constrain](https://maplibre.org/maplibre-gl-js/docs/examples/customize-the-map-transform-constrain/)
|
||||
*/
|
||||
export type TransformConstrainFunction = (lngLat: LngLat, zoom: number) => {
|
||||
center: LngLat;
|
||||
zoom: number;
|
||||
};
|
||||
interface ITransformGetters {
|
||||
get tileSize(): number;
|
||||
get tileZoom(): number;
|
||||
@ -4181,6 +4235,10 @@ interface ITransformGetters {
|
||||
get nearZ(): number;
|
||||
get farZ(): number;
|
||||
get autoCalculateNearFarZ(): boolean;
|
||||
/**
|
||||
* Get center lngLat and zoom to ensure that longitude and latitude bounds are respected and regions beyond the map bounds are not displayed.
|
||||
*/
|
||||
get constrain(): TransformConstrainFunction;
|
||||
}
|
||||
interface ITransformMutators {
|
||||
clone(): ITransform;
|
||||
@ -4279,6 +4337,11 @@ interface ITransformMutators {
|
||||
* @param bounds - A {@link LngLatBounds} object describing the new geographic boundaries of the map.
|
||||
*/
|
||||
setMaxBounds(bounds?: LngLatBounds | null): void;
|
||||
/** Sets or clears the callback overriding the transform's default constrain,
|
||||
* whose responsibility is to respect the longitude and latitude bounds by constraining the viewport's lnglat and zoom.
|
||||
* @param constrain - A {@link TransformConstrainFunction} callback defining how the viewport should respect the bounds.
|
||||
*/
|
||||
setConstrain(constrain?: TransformConstrainFunction | null): void;
|
||||
/**
|
||||
* @internal
|
||||
* Called before rendering to allow the transform implementation
|
||||
@ -4408,12 +4471,10 @@ interface IReadonlyTransform extends ITransformGetters {
|
||||
*/
|
||||
isPointOnMapSurface(p: Point, terrain?: Terrain): boolean;
|
||||
/**
|
||||
* Get center lngLat and zoom to ensure that longitude and latitude bounds are respected and regions beyond the map bounds are not displayed.
|
||||
* @internal
|
||||
* The tranform's default callback that ensures that longitude and latitude bounds are respected by the viewport.
|
||||
*/
|
||||
getConstrained(lngLat: LngLat, zoom: number): {
|
||||
center: LngLat;
|
||||
zoom: number;
|
||||
};
|
||||
defaultConstrain: TransformConstrainFunction;
|
||||
maxPitchScaleFactor(): number;
|
||||
/**
|
||||
* The camera looks at the map from a 3D (lng, lat, altitude) location. Let's use `cameraLocation`
|
||||
@ -4739,6 +4800,7 @@ type WorkerDEMTileParameters = TileParameters & {
|
||||
export type WorkerTileResult = ExpiryData & {
|
||||
buckets: Array<Bucket>;
|
||||
imageAtlas: ImageAtlas;
|
||||
dashPositions: Record<string, DashEntry>;
|
||||
glyphAtlasImage: AlphaImage;
|
||||
featureIndex: FeatureIndex;
|
||||
collisionBoxArray: CollisionBoxArray;
|
||||
@ -4830,7 +4892,7 @@ declare class CollisionIndex {
|
||||
number,
|
||||
number
|
||||
], collisionGroupPredicate?: (key: FeatureKey) => boolean, getElevation?: (x: number, y: number) => number, shift?: Point, simpleProjectionMatrix?: mat4): PlacedBox;
|
||||
placeCollisionCircles(overlapMode: OverlapMode, symbol: any, lineVertexArray: SymbolLineVertexArray, glyphOffsetArray: GlyphOffsetArray, fontSize: number, unwrappedTileID: UnwrappedTileID, pitchedLabelPlaneMatrix: mat4, showCollisionCircles: boolean, pitchWithMap: boolean, collisionGroupPredicate: (key: FeatureKey) => boolean, circlePixelDiameter: number, textPixelPadding: number, translation: [
|
||||
placeCollisionCircles(overlapMode: OverlapMode, symbol: PlacedSymbol, lineVertexArray: SymbolLineVertexArray, glyphOffsetArray: GlyphOffsetArray, fontSize: number, unwrappedTileID: UnwrappedTileID, pitchedLabelPlaneMatrix: mat4, showCollisionCircles: boolean, pitchWithMap: boolean, collisionGroupPredicate: (key: FeatureKey) => boolean, circlePixelDiameter: number, textPixelPadding: number, translation: [
|
||||
number,
|
||||
number
|
||||
], getElevation: (x: number, y: number) => number): PlacedCircles;
|
||||
@ -5071,6 +5133,20 @@ type QueryRenderedFeaturesResultsItem = QueryResultsItem & {
|
||||
feature: MapGeoJSONFeature;
|
||||
};
|
||||
type TileState = "loading" | "loaded" | "reloading" | "unloaded" | "errored" | "expired";
|
||||
type CrossFadeArgs = {
|
||||
fadingRole: FadingRoles;
|
||||
fadingDirection: FadingDirections;
|
||||
fadingParentID?: OverscaledTileID;
|
||||
fadeEndTime: number;
|
||||
};
|
||||
declare enum FadingRoles {
|
||||
Base = 0,
|
||||
Parent = 1
|
||||
}
|
||||
declare enum FadingDirections {
|
||||
Departing = 0,
|
||||
Incoming = 1
|
||||
}
|
||||
/**
|
||||
* A tile object is the combination of a Coordinate, which defines
|
||||
* its place, as well as a unique ID and data tracking for its content
|
||||
@ -5087,13 +5163,21 @@ export declare class Tile {
|
||||
latestRawTileData: ArrayBuffer;
|
||||
imageAtlas: ImageAtlas;
|
||||
imageAtlasTexture: Texture;
|
||||
dashPositions: {
|
||||
[_: string]: DashEntry;
|
||||
};
|
||||
glyphAtlasImage: AlphaImage;
|
||||
glyphAtlasTexture: Texture;
|
||||
expirationTime: any;
|
||||
expiredRequestCount: number;
|
||||
state: TileState;
|
||||
fadingRole: FadingRoles;
|
||||
fadingDirection: FadingDirections;
|
||||
fadingParentID: OverscaledTileID;
|
||||
selfFading: boolean;
|
||||
timeAdded: number;
|
||||
fadeEndTime: number;
|
||||
fadeOpacity: number;
|
||||
collisionBoxArray: CollisionBoxArray;
|
||||
redoWhenDone: boolean;
|
||||
showCollisionBoxes: boolean;
|
||||
@ -5135,7 +5219,17 @@ export declare class Tile {
|
||||
* @param size - The tile size
|
||||
*/
|
||||
constructor(tileID: OverscaledTileID, size: number);
|
||||
registerFadeDuration(duration: number): void;
|
||||
isRenderable(symbolLayer: boolean): boolean;
|
||||
/**
|
||||
* @internal
|
||||
* Many-to-one crossfade between a base tile and parent/ancestor tile (when zooming)
|
||||
*/
|
||||
setCrossFadeLogic({ fadingRole, fadingDirection, fadingParentID, fadeEndTime }: CrossFadeArgs): void;
|
||||
/**
|
||||
* Self fading for edge tiles (when panning map)
|
||||
*/
|
||||
setSelfFadeLogic(fadeEndTime: number): void;
|
||||
resetFadeLogic(): void;
|
||||
wasRequested(): boolean;
|
||||
clearTextures(painter: any): void;
|
||||
/**
|
||||
@ -5166,10 +5260,10 @@ export declare class Tile {
|
||||
setExpiryData(data: ExpiryData): void;
|
||||
getExpiryTimeout(): number;
|
||||
setFeatureState(states: LayerFeatureStates, painter: any): void;
|
||||
holdingForFade(): boolean;
|
||||
holdingForSymbolFade(): boolean;
|
||||
symbolFadeFinished(): boolean;
|
||||
clearFadeHold(): void;
|
||||
setHoldDuration(duration: number): void;
|
||||
clearSymbolFadeHold(): void;
|
||||
setSymbolHoldDuration(duration: number): void;
|
||||
setDependencies(namespace: string, dependencies: Array<string>): void;
|
||||
hasDependency(namespaces: Array<string>, keys: Array<string>): boolean;
|
||||
}
|
||||
@ -5204,7 +5298,7 @@ declare class CircleBucket<Layer extends CircleStyleLayer | HeatmapStyleLayer> i
|
||||
layoutVertexBuffer: VertexBuffer;
|
||||
indexArray: TriangleIndexArray;
|
||||
indexBuffer: IndexBuffer;
|
||||
hasPattern: boolean;
|
||||
hasDependencies: boolean;
|
||||
programConfigurations: ProgramConfigurationSet<Layer>;
|
||||
segments: SegmentVector;
|
||||
uploaded: boolean;
|
||||
@ -5283,7 +5377,7 @@ declare class FillBucket implements Bucket {
|
||||
indexBuffer: IndexBuffer;
|
||||
indexArray2: LineIndexArray;
|
||||
indexBuffer2: IndexBuffer;
|
||||
hasPattern: boolean;
|
||||
hasDependencies: boolean;
|
||||
programConfigurations: ProgramConfigurationSet<FillStyleLayer>;
|
||||
segments: SegmentVector;
|
||||
segments2: SegmentVector;
|
||||
@ -5361,7 +5455,7 @@ declare class FillExtrusionBucket implements Bucket {
|
||||
centroidVertexBuffer: VertexBuffer;
|
||||
indexArray: TriangleIndexArray;
|
||||
indexBuffer: IndexBuffer;
|
||||
hasPattern: boolean;
|
||||
hasDependencies: boolean;
|
||||
programConfigurations: ProgramConfigurationSet<FillExtrusionStyleLayer>;
|
||||
segments: SegmentVector;
|
||||
uploaded: boolean;
|
||||
@ -5529,7 +5623,7 @@ declare class LineBucket implements Bucket {
|
||||
layoutVertexBuffer2: VertexBuffer;
|
||||
indexArray: TriangleIndexArray;
|
||||
indexBuffer: IndexBuffer;
|
||||
hasPattern: boolean;
|
||||
hasDependencies: boolean;
|
||||
programConfigurations: ProgramConfigurationSet<LineStyleLayer>;
|
||||
segments: SegmentVector;
|
||||
uploaded: boolean;
|
||||
@ -5537,9 +5631,13 @@ declare class LineBucket implements Bucket {
|
||||
populate(features: Array<IndexedFeature>, options: PopulateParameters, canonical: CanonicalTileID): void;
|
||||
update(states: FeatureStates, vtLayer: VectorTileLayer, imagePositions: {
|
||||
[_: string]: ImagePosition;
|
||||
}, dashPositions: {
|
||||
[_: string]: DashEntry;
|
||||
}): void;
|
||||
addFeatures(options: PopulateParameters, canonical: CanonicalTileID, imagePositions: {
|
||||
[_: string]: ImagePosition;
|
||||
}, dashPositions?: {
|
||||
[_: string]: DashEntry;
|
||||
}): void;
|
||||
isEmpty(): boolean;
|
||||
uploadPending(): boolean;
|
||||
@ -5548,7 +5646,7 @@ declare class LineBucket implements Bucket {
|
||||
lineFeatureClips(feature: BucketFeature): LineClips | undefined;
|
||||
addFeature(feature: BucketFeature, geometry: Array<Array<Point>>, index: number, canonical: CanonicalTileID, imagePositions: {
|
||||
[_: string]: ImagePosition;
|
||||
}, subdivisionGranularity: SubdivisionGranularitySetting): void;
|
||||
}, dashPositions: Record<string, DashEntry>, subdivisionGranularity: SubdivisionGranularitySetting): void;
|
||||
addLine(vertices: Array<Point>, feature: BucketFeature, join: string, cap: string, miterLimit: number, roundLimit: number, canonical: CanonicalTileID | undefined, subdivisionGranularity: SubdivisionGranularitySetting): void;
|
||||
/**
|
||||
* Add two vertices to the buffers.
|
||||
@ -5564,6 +5662,8 @@ declare class LineBucket implements Bucket {
|
||||
addHalfVertex({ x, y }: Point, extrudeX: number, extrudeY: number, round: boolean, up: boolean, dir: number, segment: Segment): void;
|
||||
updateScaledDistance(): void;
|
||||
updateDistance(prev: Point, next: Point): void;
|
||||
private hasLineDasharray;
|
||||
private addLineDashDependencies;
|
||||
}
|
||||
type LineLayoutProps = {
|
||||
"line-cap": DataConstantProperty<"butt" | "round" | "square">;
|
||||
@ -5591,7 +5691,7 @@ type LinePaintProps = {
|
||||
"line-gap-width": DataDrivenProperty<number>;
|
||||
"line-offset": DataDrivenProperty<number>;
|
||||
"line-blur": DataDrivenProperty<number>;
|
||||
"line-dasharray": CrossFadedProperty<Array<number>>;
|
||||
"line-dasharray": CrossFadedDataDrivenProperty<Array<number>>;
|
||||
"line-pattern": CrossFadedDataDrivenProperty<ResolvedImage>;
|
||||
"line-gradient": ColorRampProperty;
|
||||
};
|
||||
@ -5607,7 +5707,7 @@ type LinePaintPropsPossiblyEvaluated = {
|
||||
"line-gap-width": PossiblyEvaluatedPropertyValue<number>;
|
||||
"line-offset": PossiblyEvaluatedPropertyValue<number>;
|
||||
"line-blur": PossiblyEvaluatedPropertyValue<number>;
|
||||
"line-dasharray": CrossFaded<Array<number>>;
|
||||
"line-dasharray": PossiblyEvaluatedPropertyValue<CrossFaded<Array<number>>>;
|
||||
"line-pattern": PossiblyEvaluatedPropertyValue<CrossFaded<ResolvedImage>>;
|
||||
"line-gradient": ColorRampProperty;
|
||||
};
|
||||
@ -5638,6 +5738,9 @@ type PaintOptions = {
|
||||
imagePositions: {
|
||||
[_: string]: ImagePosition;
|
||||
};
|
||||
dashPositions?: {
|
||||
[_: string]: DashEntry;
|
||||
};
|
||||
canonical?: CanonicalTileID;
|
||||
formattedSection?: FormattedSection;
|
||||
globalState?: Record<string, any>;
|
||||
@ -5663,6 +5766,7 @@ declare class ProgramConfiguration {
|
||||
getMaxValue(property: string): number;
|
||||
populatePaintArrays(newLength: number, feature: Feature, options: PaintOptions): void;
|
||||
setConstantPatternPositions(posTo: ImagePosition, posFrom: ImagePosition): void;
|
||||
setConstantDashPositions(dashTo: DashEntry, dashFrom: DashEntry): void;
|
||||
updatePaintArrays(featureStates: FeatureStates, featureMap: FeaturePositionMap, vtLayer: VectorTileLayer, layer: TypedStyleLayer, options: PaintOptions): boolean;
|
||||
defines(): Array<string>;
|
||||
getBinderAttributes(): Array<string>;
|
||||
@ -6781,6 +6885,7 @@ export declare class Style extends Evented {
|
||||
getGlyphs(mapId: string | number, params: GetGlyphsParameters): Promise<GetGlyphsResponse>;
|
||||
getGlyphsUrl(): string;
|
||||
setGlyphs(glyphsUrl: string | null, options?: StyleSetterOptions): void;
|
||||
getDashes(mapId: string | number, params: GetDashesParameters): Promise<GetDashesResponse>;
|
||||
/**
|
||||
* Add a sprite.
|
||||
*
|
||||
@ -6830,6 +6935,10 @@ type PopulateParameters = {
|
||||
iconDependencies: {};
|
||||
patternDependencies: {};
|
||||
glyphDependencies: {};
|
||||
dashDependencies: Record<string, {
|
||||
round: boolean;
|
||||
dasharray: Array<number>;
|
||||
}>;
|
||||
availableImages: Array<string>;
|
||||
subdivisionGranularity: SubdivisionGranularitySetting;
|
||||
};
|
||||
@ -6853,6 +6962,7 @@ type BucketFeature = {
|
||||
"max": string;
|
||||
};
|
||||
};
|
||||
readonly dashes?: NonNullable<StyleFeature["dashes"]>;
|
||||
sortKey?: number;
|
||||
};
|
||||
/**
|
||||
@ -6879,14 +6989,14 @@ type BucketFeature = {
|
||||
*/
|
||||
export interface Bucket {
|
||||
layerIds: Array<string>;
|
||||
hasPattern: boolean;
|
||||
hasDependencies: boolean;
|
||||
readonly layers: Array<any>;
|
||||
readonly stateDependentLayers: Array<any>;
|
||||
readonly stateDependentLayerIds: Array<string>;
|
||||
populate(features: Array<IndexedFeature>, options: PopulateParameters, canonical: CanonicalTileID): void;
|
||||
update(states: FeatureStates, vtLayer: VectorTileLayer, imagePositions: {
|
||||
[_: string]: ImagePosition;
|
||||
}): void;
|
||||
}, dashPositions: Record<string, DashEntry>): void;
|
||||
isEmpty(): boolean;
|
||||
upload(context: Context): void;
|
||||
uploadPending(): boolean;
|
||||
@ -6995,7 +7105,7 @@ export declare abstract class StyleLayer extends Evented {
|
||||
setPaintProperty(name: string, value: unknown, options?: StyleSetterOptions): boolean;
|
||||
_handleSpecialPaintPropertyUpdate(_: string): void;
|
||||
_handleOverridablePaintPropertyUpdate<T, R>(name: string, oldValue: PropertyValue<T, R>, newValue: PropertyValue<T, R>): boolean;
|
||||
isHidden(zoom: number): boolean;
|
||||
isHidden(zoom: number, roundMinZoom?: boolean): boolean;
|
||||
updateTransitions(parameters: TransitionParameters): void;
|
||||
hasTransition(): boolean;
|
||||
recalculate(parameters: EvaluationParameters, availableImages: Array<string>): void;
|
||||
@ -7130,6 +7240,17 @@ type GetGlyphsResponse = {
|
||||
type GetImagesResponse = {
|
||||
[_: string]: StyleImage;
|
||||
};
|
||||
type GetDashesParameters = {
|
||||
dashes: {
|
||||
[key: string]: {
|
||||
dasharray: Array<number>;
|
||||
round: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
type GetDashesResponse = {
|
||||
[dashId: string]: DashEntry;
|
||||
};
|
||||
/**
|
||||
* All the possible message types that can be sent to and from the worker
|
||||
*/
|
||||
@ -7143,6 +7264,7 @@ export declare const enum MessageType {
|
||||
loadTile = "LT",
|
||||
reloadTile = "RT",
|
||||
getGlyphs = "GG",
|
||||
getDashes = "GDA",
|
||||
getImages = "GI",
|
||||
setImages = "SI",
|
||||
updateGlobalState = "UGS",
|
||||
@ -7255,6 +7377,10 @@ export type RequestResponseMessageMap = {
|
||||
RequestParameters,
|
||||
GetResourceResponse<any>
|
||||
];
|
||||
[MessageType.getDashes]: [
|
||||
GetDashesParameters,
|
||||
GetDashesResponse
|
||||
];
|
||||
};
|
||||
/**
|
||||
* The message to be sent by the actor
|
||||
@ -8395,9 +8521,10 @@ declare abstract class Camera extends Evented {
|
||||
* between old and new values. The map will retain its current values for any
|
||||
* details not specified in `options`.
|
||||
*
|
||||
* Note: The transition will happen instantly if the user has enabled
|
||||
* the `reduced motion` accessibility feature enabled in their operating system,
|
||||
* unless `options` includes `essential: true`.
|
||||
* !!! note "Reduced Motion"
|
||||
* The transition will happen instantly if the user has enabled
|
||||
* the `reduced motion` accessibility feature enabled in their operating system,
|
||||
* unless `options` includes `essential: true`.
|
||||
*
|
||||
* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`,
|
||||
* `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`.
|
||||
@ -8458,9 +8585,10 @@ declare abstract class Camera extends Evented {
|
||||
* evokes flight. The animation seamlessly incorporates zooming and panning to help
|
||||
* the user maintain her bearings even after traversing a great distance.
|
||||
*
|
||||
* Note: The animation will be skipped, and this will behave equivalently to `jumpTo`
|
||||
* if the user has the `reduced motion` accessibility feature enabled in their operating system,
|
||||
* unless 'options' includes `essential: true`.
|
||||
* !!! note "Reduced Motion"
|
||||
* The animation will be skipped, and this will behave equivalently to `jumpTo`
|
||||
* if the user has the `reduced motion` accessibility feature enabled in their operating system,
|
||||
* unless 'options' includes `essential: true`.
|
||||
*
|
||||
* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`,
|
||||
* `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`.
|
||||
@ -8613,6 +8741,14 @@ type EventsInProgress = {
|
||||
rotate?: EventInProgress;
|
||||
drag?: EventInProgress;
|
||||
};
|
||||
type MapControlsScenarioOptions = {
|
||||
terrain?: Terrain | null;
|
||||
tr: ITransform;
|
||||
deltasForHelper: MapControlsDeltas;
|
||||
preZoomAroundLoc: LngLat;
|
||||
combinedEventsInProgress: EventsInProgress;
|
||||
panDelta?: Point;
|
||||
};
|
||||
declare class HandlerManager {
|
||||
_map: Map$1;
|
||||
_el: HTMLElement;
|
||||
@ -8673,6 +8809,7 @@ declare class HandlerManager {
|
||||
_updateMapTransform(combinedResult: HandlerResult, combinedEventsInProgress: EventsInProgress, deactivatedHandlers: {
|
||||
[handlerName: string]: Event$1;
|
||||
}): void;
|
||||
_handleMapControls({ terrain, tr, deltasForHelper, preZoomAroundLoc, combinedEventsInProgress, panDelta }: MapControlsScenarioOptions): void;
|
||||
_fireEvents(newEventsInProgress: EventsInProgress, deactivatedHandlers: {
|
||||
[handlerName: string]: Event$1;
|
||||
}, allowEndAnimation: boolean): void;
|
||||
@ -8766,7 +8903,8 @@ export type MapLayerTouchEvent = MapTouchEvent & {
|
||||
export type MapSourceDataType = "content" | "metadata" | "visibility" | "idle";
|
||||
/**
|
||||
* `MapLayerEventType` - a mapping between the event name and the event.
|
||||
* **Note:** These events are compatible with the optional `layerId` parameter.
|
||||
* !!! note
|
||||
* These events are compatible with the optional `layerId` parameter.
|
||||
* If `layerId` is included as the second argument in {@link Map.on}, the event listener will fire only when the
|
||||
* event action contains a visible portion of the specified layer.
|
||||
* The following example can be used for all the events.
|
||||
@ -8793,7 +8931,8 @@ export type MapLayerEventType = {
|
||||
/**
|
||||
* Fired when a pointing device (usually a mouse) is pressed and released twice contains a visible portion of the specified layer.
|
||||
*
|
||||
* **Note:** Under normal conditions, this event will be preceded by two `click` events.
|
||||
* !!! note
|
||||
* Under normal conditions, this event will be preceded by two `click` events.
|
||||
*/
|
||||
dblclick: MapLayerMouseEvent;
|
||||
/**
|
||||
@ -8881,7 +9020,7 @@ export type MapLayerEventType = {
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export type MapEventType = {
|
||||
export interface MapEventType {
|
||||
/**
|
||||
* Fired when an error occurs. This is GL JS's primary error reporting
|
||||
* mechanism. We use an event instead of `throw` to better accommodate
|
||||
@ -9025,7 +9164,8 @@ export type MapEventType = {
|
||||
/**
|
||||
* Fired when a pointing device (usually a mouse) is pressed and released twice at the same point on the map in rapid succession.
|
||||
*
|
||||
* **Note:** Under normal conditions, this event will be preceded by two `click` events.
|
||||
* !!! note
|
||||
* Under normal conditions, this event will be preceded by two `click` events.
|
||||
*/
|
||||
dblclick: MapMouseEvent;
|
||||
/**
|
||||
@ -9158,7 +9298,7 @@ export type MapEventType = {
|
||||
* Fired when map's projection is modified in other ways than by map being moved.
|
||||
*/
|
||||
projectiontransition: MapProjectionEvent;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* The base event for MapLibre
|
||||
*
|
||||
@ -10246,7 +10386,8 @@ export type MapOptions = {
|
||||
/**
|
||||
* If set, an {@link AttributionControl} will be added to the map with the provided options.
|
||||
* To disable the attribution control, pass `false`.
|
||||
* Note: showing the logo of MapLibre is not required for using MapLibre.
|
||||
* !!! note
|
||||
* Showing the logo of MapLibre is not required for using MapLibre.
|
||||
* @defaultValue compact: true, customAttribution: "MapLibre ...".
|
||||
*/
|
||||
attributionControl?: false | AttributionControlOptions;
|
||||
@ -10346,7 +10487,9 @@ export type MapOptions = {
|
||||
*/
|
||||
trackResize?: boolean;
|
||||
/**
|
||||
* The initial geographical centerpoint of the map. If `center` is not specified in the constructor options, MapLibre GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `[0, 0]` Note: MapLibre GL JS uses longitude, latitude coordinate order (as opposed to latitude, longitude) to match GeoJSON.
|
||||
* The initial geographical centerpoint of the map. If `center` is not specified in the constructor options, MapLibre GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `[0, 0]`
|
||||
* !!! note
|
||||
* MapLibre GL JS uses longitude, latitude coordinate order (as opposed to latitude, longitude) to match GeoJSON.
|
||||
* @defaultValue [0, 0]
|
||||
*/
|
||||
center?: LngLatLike;
|
||||
@ -10408,10 +10551,19 @@ export type MapOptions = {
|
||||
*/
|
||||
transformCameraUpdate?: CameraUpdateTransformFunction | null;
|
||||
/**
|
||||
* A patch to apply to the default localization table for UI strings, e.g. control tooltips. The `locale` object maps namespaced UI string IDs to translated strings in the target language; see `src/ui/default_locale.js` for an example with all supported string IDs. The object may specify all UI strings (thereby adding support for a new translation) or only a subset of strings (thereby patching the default translation table).
|
||||
* A callback that overrides how the map constrains the viewport's lnglat and zoom to respect the longitude and latitude bounds.
|
||||
* @see [Customize the map transform constrain](https://maplibre.org/maplibre-gl-js/docs/examples/customize-the-map-transform-constrain/)
|
||||
* Expected to return an object containing center and zoom.
|
||||
* @defaultValue null
|
||||
*/
|
||||
locale?: any;
|
||||
transformConstrain?: TransformConstrainFunction | null;
|
||||
/**
|
||||
* A patch to apply to the default localization table for UI strings, e.g. control tooltips. The `locale` object maps namespaced UI string IDs to translated strings in the target language; see `src/ui/default_locale.js` for an example with all supported string IDs. The object may specify all UI strings (thereby adding support for a new translation) or only a subset of strings (thereby patching the default translation table).
|
||||
* For an example, see https://maplibre.org/maplibre-gl-js/docs/examples/locale-switching/
|
||||
* Alternatively, search the official plugins page for plugins related to localization.
|
||||
* @defaultValue null
|
||||
*/
|
||||
locale?: Record<string, string>;
|
||||
/**
|
||||
* Controls the duration of the fade-in/fade-out animation for label collisions after initial map load, in milliseconds. This setting affects all symbol layers. This setting does not affect the duration of runtime styling transitions or raster tile cross-fading.
|
||||
* @defaultValue 300
|
||||
@ -10445,7 +10597,8 @@ export type MapOptions = {
|
||||
* font-family for locally overriding generation of Chinese, Japanese, and Korean characters.
|
||||
* For these characters, 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.
|
||||
* The purpose of this option is to avoid bandwidth-intensive glyph server requests. (See [Use locally generated ideographs](https://maplibre.org/maplibre-gl-js/docs/examples/use-locally-generated-ideographs).)
|
||||
* The purpose of this option is to avoid bandwidth-intensive glyph server requests.
|
||||
* @see [Use locally generated ideographs](https://maplibre.org/maplibre-gl-js/docs/examples/use-locally-generated-ideographs/)
|
||||
* @defaultValue 'sans-serif'
|
||||
*/
|
||||
localIdeographFontFamily?: string | false;
|
||||
@ -10581,7 +10734,7 @@ declare class Map$1 extends Camera {
|
||||
_localIdeographFontFamily: string | false;
|
||||
_validateStyle: boolean;
|
||||
_requestManager: RequestManager;
|
||||
_locale: typeof defaultLocale;
|
||||
_locale: Record<string, string>;
|
||||
_removed: boolean;
|
||||
_clickTolerance: number;
|
||||
_overridePixelRatio: number | null | undefined;
|
||||
@ -10648,6 +10801,11 @@ declare class Map$1 extends Camera {
|
||||
* @defaultValue true
|
||||
*/
|
||||
cancelPendingTileRequestsWhileZooming: boolean;
|
||||
/**
|
||||
* The map transform's callback that overrides the default constrain function.
|
||||
* @defaultValue null
|
||||
*/
|
||||
transformConstrain: TransformConstrainFunction | null;
|
||||
constructor(options: MapOptions);
|
||||
/**
|
||||
* @internal
|
||||
@ -10939,6 +11097,21 @@ declare class Map$1 extends Camera {
|
||||
* @see [Render world copies](https://maplibre.org/maplibre-gl-js/docs/examples/render-world-copies/)
|
||||
*/
|
||||
setRenderWorldCopies(renderWorldCopies?: boolean | null): Map$1;
|
||||
/** Sets or clears the callback overriding how the map constrains the viewport's lnglat and zoom to respect the longitude and latitude bounds.
|
||||
*
|
||||
* @param constrain - A {@link TransformConstrainFunction} callback defining how the viewport should respect the bounds.
|
||||
*
|
||||
* `null` clears the callback and reverses the override of the map transform's default constrain function.
|
||||
* @example
|
||||
* ```ts
|
||||
* function customTransformConstrain(lngLat, zoom) {
|
||||
* return {center: lngLat, zoom: zoom ?? 0};
|
||||
* };
|
||||
* map.setTransformConstrain(customTransformConstrain);
|
||||
* ```
|
||||
* @see [Customize the map transform constrain](https://maplibre.org/maplibre-gl-js/docs/examples/customize-the-map-transform-constrain/)
|
||||
*/
|
||||
setTransformConstrain(constrain?: TransformConstrainFunction | null): Map$1;
|
||||
/**
|
||||
* Returns a [Point](https://github.com/mapbox/point-geometry) representing pixel coordinates, relative to the map's `container`,
|
||||
* that correspond to the specified geographical location.
|
||||
@ -11830,10 +12003,11 @@ declare class Map$1 extends Camera {
|
||||
* and [maximum zoom level](https://maplibre.org/maplibre-style-spec/layers/#maxzoom))
|
||||
* at which the layer will be rendered.
|
||||
*
|
||||
* Note: For style layers using vector sources, style layers cannot be rendered at zoom levels lower than the
|
||||
* minimum zoom level of the _source layer_ because the data does not exist at those zoom levels. If the minimum
|
||||
* zoom level of the source layer is higher than the minimum zoom level defined in the style layer, the style
|
||||
* layer will not be rendered at all zoom levels in the zoom range.
|
||||
* !!! note
|
||||
* For style layers using vector sources, style layers cannot be rendered at zoom levels lower than the
|
||||
* minimum zoom level of the _source layer_ because the data does not exist at those zoom levels. If the minimum
|
||||
* zoom level of the source layer is higher than the minimum zoom level defined in the style layer, the style
|
||||
* layer will not be rendered at all zoom levels in the zoom range.
|
||||
*
|
||||
* @param layerId - The ID of the layer to which the zoom extent will be applied.
|
||||
* @param minzoom - The minimum zoom to set (0-24).
|
||||
@ -12044,7 +12218,8 @@ declare class Map$1 extends Camera {
|
||||
* - For vector or GeoJSON sources, using the [`promoteId`](https://maplibre.org/maplibre-style-spec/sources/#promoteid) option at the time the source is defined.
|
||||
* - For GeoJSON sources, using the [`generateId`](https://maplibre.org/maplibre-style-spec/sources/#generateid) option to auto-assign an `id` based on the feature's index in the source data. If you change feature data using `map.getSource('some id').setData(..)`, you may need to re-apply state taking into account updated `id` values.
|
||||
*
|
||||
* _Note: You can use the [`feature-state` expression](https://maplibre.org/maplibre-style-spec/expressions/#feature-state) to access the values in a feature's state object for the purposes of styling._
|
||||
* !!! note
|
||||
* You can use the [`feature-state` expression](https://maplibre.org/maplibre-style-spec/expressions/#feature-state) to access the values in a feature's state object for the purposes of styling.
|
||||
*
|
||||
* @param feature - Feature identifier. Feature objects returned from
|
||||
* {@link Map.queryRenderedFeatures} or event handlers can be used as feature identifiers.
|
||||
@ -12121,7 +12296,8 @@ declare class Map$1 extends Camera {
|
||||
* A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime.
|
||||
* Features are identified by their `feature.id` attribute, which can be any number or string.
|
||||
*
|
||||
* _Note: To access the values in a feature's state object for the purposes of styling the feature, use the [`feature-state` expression](https://maplibre.org/maplibre-style-spec/expressions/#feature-state)._
|
||||
* !!! note
|
||||
* To access the values in a feature's state object for the purposes of styling the feature, use the [`feature-state` expression](https://maplibre.org/maplibre-style-spec/expressions/#feature-state).
|
||||
*
|
||||
* @param feature - Feature identifier. Feature objects returned from
|
||||
* {@link Map.queryRenderedFeatures} or event handlers can be used as feature identifiers.
|
||||
@ -13589,7 +13765,7 @@ export declare class TerrainControl implements IControl {
|
||||
* .addControl(new GlobeControl());
|
||||
* ```
|
||||
*
|
||||
* @see [Display a globe with a fill extrusion layer](https://maplibre.org/maplibre-gl-js/docs/examples/globe-fill-extrusion/)
|
||||
* @see [Display a globe with a fill extrusion layer](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-globe-with-a-fill-extrusion-layer/)
|
||||
*/
|
||||
export declare class GlobeControl implements IControl {
|
||||
_map: Map$1;
|
||||
@ -13602,6 +13778,50 @@ export declare class GlobeControl implements IControl {
|
||||
_toggleProjection: () => void;
|
||||
_updateGlobeIcon: () => void;
|
||||
}
|
||||
/**
|
||||
* Freezes time at a specific timestamp for deterministic rendering.
|
||||
* Useful for frame-by-frame video capture where each frame needs
|
||||
* a consistent time value.
|
||||
*
|
||||
* @param timestamp - Time in milliseconds to freeze at
|
||||
* @example
|
||||
* ```ts
|
||||
* // Freeze time for video export at 60fps
|
||||
* setNow(0); // First frame
|
||||
* // ... render frame ...
|
||||
* setNow(16.67); // Second frame
|
||||
* // ... render frame ...
|
||||
* setNow(33.34); // Third frame
|
||||
* // ... done ...
|
||||
* restoreNow(); // Resume normal time
|
||||
* ```
|
||||
*/
|
||||
export declare function setNow(timestamp: number): void;
|
||||
/**
|
||||
* Restores normal time flow after freezing with setNow().
|
||||
* Call this after finishing deterministic rendering operations.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // After video export, resume normal time
|
||||
* setNow(0);
|
||||
* // ... export frames ...
|
||||
* restoreNow(); // Map animations resume normally
|
||||
* ```
|
||||
*/
|
||||
export declare function restoreNow(): void;
|
||||
/**
|
||||
* Returns whether time is currently frozen.
|
||||
* @returns True if time is frozen via setNow(), false otherwise
|
||||
* @example
|
||||
* ```ts
|
||||
* setNow(1000);
|
||||
* console.log(isTimeFrozen()); // true
|
||||
* restoreNow();
|
||||
* console.log(isTimeFrozen()); // false
|
||||
* ```
|
||||
*/
|
||||
export declare function isTimeFrozen(): boolean;
|
||||
/**
|
||||
* Initializes resources like WebWorkers that can be shared across maps to lower load
|
||||
* times in some situations. `setWorkerUrl()` and `setWorkerCount()`, if being
|
||||
@ -13741,11 +13961,13 @@ export declare class GeoJSONSource extends Evented implements Source {
|
||||
_pendingWorkerUpdate: {
|
||||
data?: GeoJSON.GeoJSON | string;
|
||||
diff?: GeoJSONSourceDiff;
|
||||
optionsChanged?: boolean;
|
||||
};
|
||||
_collectResourceTiming: boolean;
|
||||
_removed: boolean;
|
||||
/** @internal */
|
||||
constructor(id: string, options: GeoJSONSourceOptions, dispatcher: Dispatcher, eventedParent: Evented);
|
||||
private _hasPendingWorkerUpdate;
|
||||
private _pixelsToTileUnits;
|
||||
private _getClusterMaxZoom;
|
||||
load(): Promise<void>;
|
||||
|
||||
8
node_modules/maplibre-gl/dist/maplibre-gl.js
generated
vendored
8
node_modules/maplibre-gl/dist/maplibre-gl.js
generated
vendored
File diff suppressed because one or more lines are too long
2
node_modules/maplibre-gl/dist/maplibre-gl.js.map
generated
vendored
2
node_modules/maplibre-gl/dist/maplibre-gl.js.map
generated
vendored
File diff suppressed because one or more lines are too long
52
node_modules/maplibre-gl/package.json
generated
vendored
52
node_modules/maplibre-gl/package.json
generated
vendored
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "maplibre-gl",
|
||||
"description": "BSD licensed community fork of mapbox-gl, a WebGL interactive maps library",
|
||||
"version": "5.7.3",
|
||||
"version": "5.10.0",
|
||||
"main": "dist/maplibre-gl.js",
|
||||
"style": "dist/maplibre-gl.css",
|
||||
"license": "BSD-3-Clause",
|
||||
@ -24,7 +24,7 @@
|
||||
"@mapbox/unitbezier": "^0.0.1",
|
||||
"@mapbox/vector-tile": "^2.0.4",
|
||||
"@mapbox/whoots-js": "^3.1.0",
|
||||
"@maplibre/maplibre-gl-style-spec": "^24.1.1",
|
||||
"@maplibre/maplibre-gl-style-spec": "^24.3.0",
|
||||
"@maplibre/vt-pbf": "^4.0.3",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"@types/geojson-vt": "3.2.5",
|
||||
@ -43,36 +43,36 @@
|
||||
"devDependencies": {
|
||||
"@mapbox/mapbox-gl-rtl-text": "^0.3.0",
|
||||
"@mapbox/mvt-fixtures": "^3.10.0",
|
||||
"@rollup/plugin-commonjs": "^28.0.6",
|
||||
"@rollup/plugin-commonjs": "^28.0.8",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-node-resolve": "^16.0.3",
|
||||
"@rollup/plugin-replace": "^6.0.2",
|
||||
"@rollup/plugin-strip": "^3.0.4",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@rollup/plugin-typescript": "^12.1.4",
|
||||
"@stylistic/eslint-plugin": "^5.3.1",
|
||||
"@stylistic/eslint-plugin": "^5.5.0",
|
||||
"@types/benchmark": "^2.1.5",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/earcut": "^3.0.0",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/gl": "^6.0.5",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
"@types/jsdom": "^27.0.0",
|
||||
"@types/minimist": "^1.2.5",
|
||||
"@types/murmurhash-js": "^1.0.6",
|
||||
"@types/nise": "^1.4.5",
|
||||
"@types/node": "^24.5.2",
|
||||
"@types/node": "^24.9.1",
|
||||
"@types/offscreencanvas": "^2019.7.3",
|
||||
"@types/pixelmatch": "^5.2.6",
|
||||
"@types/pngjs": "^6.0.5",
|
||||
"@types/react": "^19.1.13",
|
||||
"@types/react-dom": "^19.1.9",
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"@types/request": "^2.48.13",
|
||||
"@types/shuffle-seed": "^1.1.3",
|
||||
"@types/window-or-global": "^1.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^8.44.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
||||
"@typescript-eslint/parser": "^8.43.0",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"@vitest/eslint-plugin": "^1.3.12",
|
||||
"@vitest/eslint-plugin": "^1.3.23",
|
||||
"@vitest/ui": "3.2.4",
|
||||
"address": "^2.0.3",
|
||||
"autoprefixer": "^10.4.21",
|
||||
@ -82,19 +82,19 @@
|
||||
"cssnano": "^7.1.1",
|
||||
"d3": "^7.9.0",
|
||||
"d3-queue": "^3.0.7",
|
||||
"devtools-protocol": "^0.0.1517051",
|
||||
"devtools-protocol": "^0.0.1532728",
|
||||
"diff": "^8.0.2",
|
||||
"dts-bundle-generator": "^9.5.1",
|
||||
"eslint": "^9.35.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint-plugin-html": "^8.1.3",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-tsdoc": "0.4.0",
|
||||
"expect": "^30.1.2",
|
||||
"expect": "^30.2.0",
|
||||
"glob": "^11.0.3",
|
||||
"globals": "^16.4.0",
|
||||
"is-builtin-module": "^5.0.0",
|
||||
"jsdom": "^26.1.0",
|
||||
"jsdom": "^27.0.1",
|
||||
"junit-report-builder": "^5.1.1",
|
||||
"minimist": "^1.2.8",
|
||||
"mock-geolocation": "^1.0.11",
|
||||
@ -108,25 +108,25 @@
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"postcss-inline-svg": "^6.0.0",
|
||||
"pretty-bytes": "^7.0.1",
|
||||
"puppeteer": "^24.22.0",
|
||||
"pretty-bytes": "^7.1.0",
|
||||
"puppeteer": "^24.25.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"rollup": "^4.50.2",
|
||||
"react-dom": "^19.2.0",
|
||||
"rollup": "^4.52.5",
|
||||
"rollup-plugin-sourcemaps2": "^0.5.4",
|
||||
"rollup-plugin-visualizer": "^6.0.3",
|
||||
"rollup-plugin-visualizer": "^6.0.5",
|
||||
"rw": "^1.3.3",
|
||||
"semver": "^7.7.2",
|
||||
"semver": "^7.7.3",
|
||||
"sharp": "^0.34.4",
|
||||
"shuffle-seed": "^1.1.6",
|
||||
"st": "^3.0.3",
|
||||
"stylelint": "^16.24.0",
|
||||
"stylelint-config-standard": "^39.0.0",
|
||||
"stylelint": "^16.25.0",
|
||||
"stylelint-config-standard": "^39.0.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"tslib": "^2.8.1",
|
||||
"typedoc": "^0.28.13",
|
||||
"typedoc-plugin-markdown": "^4.8.1",
|
||||
"typescript": "^5.9.2",
|
||||
"typedoc": "^0.28.14",
|
||||
"typedoc-plugin-markdown": "^4.9.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "3.2.4",
|
||||
"vitest-webgl-canvas-mock": "^1.1.0"
|
||||
},
|
||||
|
||||
40
node_modules/maplibre-gl/src/data/array_types.g.ts
generated
vendored
40
node_modules/maplibre-gl/src/data/array_types.g.ts
generated
vendored
@ -250,6 +250,44 @@ class StructArrayLayout10ui20 extends StructArray {
|
||||
StructArrayLayout10ui20.prototype.bytesPerElement = 20;
|
||||
register('StructArrayLayout10ui20', StructArrayLayout10ui20);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Implementation of the StructArray layout:
|
||||
* [0] - Uint16[8]
|
||||
*
|
||||
*/
|
||||
class StructArrayLayout8ui16 extends StructArray {
|
||||
uint8: Uint8Array;
|
||||
uint16: Uint16Array;
|
||||
|
||||
_refreshViews() {
|
||||
this.uint8 = new Uint8Array(this.arrayBuffer);
|
||||
this.uint16 = new Uint16Array(this.arrayBuffer);
|
||||
}
|
||||
|
||||
public emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number) {
|
||||
const i = this.length;
|
||||
this.resize(i + 1);
|
||||
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7);
|
||||
}
|
||||
|
||||
public emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number) {
|
||||
const o2 = i * 8;
|
||||
this.uint16[o2 + 0] = v0;
|
||||
this.uint16[o2 + 1] = v1;
|
||||
this.uint16[o2 + 2] = v2;
|
||||
this.uint16[o2 + 3] = v3;
|
||||
this.uint16[o2 + 4] = v4;
|
||||
this.uint16[o2 + 5] = v5;
|
||||
this.uint16[o2 + 6] = v6;
|
||||
this.uint16[o2 + 7] = v7;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
StructArrayLayout8ui16.prototype.bytesPerElement = 16;
|
||||
register('StructArrayLayout8ui16', StructArrayLayout8ui16);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Implementation of the StructArray layout:
|
||||
@ -1093,6 +1131,7 @@ export class HeatmapLayoutArray extends StructArrayLayout2i4 {}
|
||||
export class LineLayoutArray extends StructArrayLayout2i4ub8 {}
|
||||
export class LineExtLayoutArray extends StructArrayLayout2f8 {}
|
||||
export class PatternLayoutArray extends StructArrayLayout10ui20 {}
|
||||
export class DashLayoutArray extends StructArrayLayout8ui16 {}
|
||||
export class SymbolLayoutArray extends StructArrayLayout4i4ui4i24 {}
|
||||
export class SymbolDynamicLayoutArray extends StructArrayLayout3f12 {}
|
||||
export class SymbolOpacityArray extends StructArrayLayout1ul4 {}
|
||||
@ -1111,6 +1150,7 @@ export {
|
||||
StructArrayLayout2i4ub8,
|
||||
StructArrayLayout2f8,
|
||||
StructArrayLayout10ui20,
|
||||
StructArrayLayout8ui16,
|
||||
StructArrayLayout4i4ui4i24,
|
||||
StructArrayLayout3f12,
|
||||
StructArrayLayout1ul4,
|
||||
|
||||
8
node_modules/maplibre-gl/src/data/bucket.ts
generated
vendored
8
node_modules/maplibre-gl/src/data/bucket.ts
generated
vendored
@ -9,6 +9,8 @@ import type {CanonicalTileID} from '../source/tile_id';
|
||||
import type {VectorTileFeature, VectorTileLayer} from '@mapbox/vector-tile';
|
||||
import type Point from '@mapbox/point-geometry';
|
||||
import type {SubdivisionGranularitySetting} from '../render/subdivision_granularity_settings';
|
||||
import type {DashEntry} from '../render/line_atlas';
|
||||
import type {Feature as StyleFeature} from '@maplibre/maplibre-gl-style-spec';
|
||||
|
||||
export type BucketParameters<Layer extends TypedStyleLayer> = {
|
||||
index: number;
|
||||
@ -26,6 +28,7 @@ export type PopulateParameters = {
|
||||
iconDependencies: {};
|
||||
patternDependencies: {};
|
||||
glyphDependencies: {};
|
||||
dashDependencies: Record<string, {round: boolean; dasharray: Array<number>}>;
|
||||
availableImages: Array<string>;
|
||||
subdivisionGranularity: SubdivisionGranularitySetting;
|
||||
};
|
||||
@ -51,6 +54,7 @@ export type BucketFeature = {
|
||||
'max': string;
|
||||
};
|
||||
};
|
||||
readonly dashes?: NonNullable<StyleFeature['dashes']>;
|
||||
sortKey?: number;
|
||||
};
|
||||
|
||||
@ -78,12 +82,12 @@ export type BucketFeature = {
|
||||
*/
|
||||
export interface Bucket {
|
||||
layerIds: Array<string>;
|
||||
hasPattern: boolean;
|
||||
hasDependencies: boolean;
|
||||
readonly layers: Array<any>;
|
||||
readonly stateDependentLayers: Array<any>;
|
||||
readonly stateDependentLayerIds: Array<string>;
|
||||
populate(features: Array<IndexedFeature>, options: PopulateParameters, canonical: CanonicalTileID): void;
|
||||
update(states: FeatureStates, vtLayer: VectorTileLayer, imagePositions: {[_: string]: ImagePosition}): void;
|
||||
update(states: FeatureStates, vtLayer: VectorTileLayer, imagePositions: {[_: string]: ImagePosition}, dashPositions: Record<string, DashEntry>): void;
|
||||
isEmpty(): boolean;
|
||||
upload(context: Context): void;
|
||||
uploadPending(): boolean;
|
||||
|
||||
4
node_modules/maplibre-gl/src/data/bucket/circle_bucket.ts
generated
vendored
4
node_modules/maplibre-gl/src/data/bucket/circle_bucket.ts
generated
vendored
@ -61,7 +61,7 @@ export class CircleBucket<Layer extends CircleStyleLayer | HeatmapStyleLayer> im
|
||||
indexArray: TriangleIndexArray;
|
||||
indexBuffer: IndexBuffer;
|
||||
|
||||
hasPattern: boolean;
|
||||
hasDependencies: boolean;
|
||||
programConfigurations: ProgramConfigurationSet<Layer>;
|
||||
segments: SegmentVector;
|
||||
uploaded: boolean;
|
||||
@ -72,7 +72,7 @@ export class CircleBucket<Layer extends CircleStyleLayer | HeatmapStyleLayer> im
|
||||
this.layers = options.layers;
|
||||
this.layerIds = this.layers.map(layer => layer.id);
|
||||
this.index = options.index;
|
||||
this.hasPattern = false;
|
||||
this.hasDependencies = false;
|
||||
|
||||
this.layoutVertexArray = new CircleLayoutArray();
|
||||
this.indexArray = new TriangleIndexArray();
|
||||
|
||||
8
node_modules/maplibre-gl/src/data/bucket/fill_bucket.ts
generated
vendored
8
node_modules/maplibre-gl/src/data/bucket/fill_bucket.ts
generated
vendored
@ -51,7 +51,7 @@ export class FillBucket implements Bucket {
|
||||
indexArray2: LineIndexArray;
|
||||
indexBuffer2: IndexBuffer;
|
||||
|
||||
hasPattern: boolean;
|
||||
hasDependencies: boolean;
|
||||
programConfigurations: ProgramConfigurationSet<FillStyleLayer>;
|
||||
segments: SegmentVector;
|
||||
segments2: SegmentVector;
|
||||
@ -63,7 +63,7 @@ export class FillBucket implements Bucket {
|
||||
this.layers = options.layers;
|
||||
this.layerIds = this.layers.map(layer => layer.id);
|
||||
this.index = options.index;
|
||||
this.hasPattern = false;
|
||||
this.hasDependencies = false;
|
||||
this.patternFeatures = [];
|
||||
|
||||
this.layoutVertexArray = new FillLayoutArray();
|
||||
@ -76,7 +76,7 @@ export class FillBucket implements Bucket {
|
||||
}
|
||||
|
||||
populate(features: Array<IndexedFeature>, options: PopulateParameters, canonical: CanonicalTileID) {
|
||||
this.hasPattern = hasPattern('fill', this.layers, options);
|
||||
this.hasDependencies = hasPattern('fill', this.layers, options);
|
||||
const fillSortKey = this.layers[0].layout.get('fill-sort-key');
|
||||
const sortFeaturesByKey = !fillSortKey.isConstant();
|
||||
const bucketFeatures: BucketFeature[] = [];
|
||||
@ -112,7 +112,7 @@ export class FillBucket implements Bucket {
|
||||
for (const bucketFeature of bucketFeatures) {
|
||||
const {geometry, index, sourceLayerIndex} = bucketFeature;
|
||||
|
||||
if (this.hasPattern) {
|
||||
if (this.hasDependencies) {
|
||||
const patternFeature = addPatternDependencies('fill', this.layers, bucketFeature, {zoom: this.zoom}, options);
|
||||
// pattern features are added only once the pattern is loaded into the image atlas
|
||||
// so are stored during populate until later updated with positions by tile worker in addFeatures
|
||||
|
||||
8
node_modules/maplibre-gl/src/data/bucket/fill_extrusion_bucket.ts
generated
vendored
8
node_modules/maplibre-gl/src/data/bucket/fill_extrusion_bucket.ts
generated
vendored
@ -74,7 +74,7 @@ export class FillExtrusionBucket implements Bucket {
|
||||
indexArray: TriangleIndexArray;
|
||||
indexBuffer: IndexBuffer;
|
||||
|
||||
hasPattern: boolean;
|
||||
hasDependencies: boolean;
|
||||
programConfigurations: ProgramConfigurationSet<FillExtrusionStyleLayer>;
|
||||
segments: SegmentVector;
|
||||
uploaded: boolean;
|
||||
@ -86,7 +86,7 @@ export class FillExtrusionBucket implements Bucket {
|
||||
this.layers = options.layers;
|
||||
this.layerIds = this.layers.map(layer => layer.id);
|
||||
this.index = options.index;
|
||||
this.hasPattern = false;
|
||||
this.hasDependencies = false;
|
||||
|
||||
this.layoutVertexArray = new FillExtrusionLayoutArray();
|
||||
this.centroidVertexArray = new PosArray();
|
||||
@ -98,7 +98,7 @@ export class FillExtrusionBucket implements Bucket {
|
||||
|
||||
populate(features: Array<IndexedFeature>, options: PopulateParameters, canonical: CanonicalTileID) {
|
||||
this.features = [];
|
||||
this.hasPattern = hasPattern('fill-extrusion', this.layers, options);
|
||||
this.hasDependencies = hasPattern('fill-extrusion', this.layers, options);
|
||||
|
||||
for (const {feature, id, index, sourceLayerIndex} of features) {
|
||||
const needGeometry = this.layers[0]._featureFilter.needGeometry;
|
||||
@ -116,7 +116,7 @@ export class FillExtrusionBucket implements Bucket {
|
||||
patterns: {}
|
||||
};
|
||||
|
||||
if (this.hasPattern) {
|
||||
if (this.hasDependencies) {
|
||||
this.features.push(addPatternDependencies('fill-extrusion', this.layers, bucketFeature, {zoom: this.zoom}, options));
|
||||
} else {
|
||||
this.addFeature(bucketFeature, bucketFeature.geometry, index, canonical, {}, options.subdivisionGranularity);
|
||||
|
||||
21
node_modules/maplibre-gl/src/data/bucket/line_bucket.test.ts
generated
vendored
21
node_modules/maplibre-gl/src/data/bucket/line_bucket.test.ts
generated
vendored
@ -113,7 +113,7 @@ describe('LineBucket', () => {
|
||||
], polygon, undefined, undefined, undefined, undefined, undefined, noSubdivision);
|
||||
|
||||
const feature = sourceLayer.feature(0);
|
||||
bucket.addFeature(feature as any, feature.loadGeometry(), undefined, undefined, undefined, noSubdivision);
|
||||
bucket.addFeature(feature as any, feature.loadGeometry(), undefined, undefined, undefined, undefined, noSubdivision);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
@ -130,10 +130,10 @@ describe('LineBucket', () => {
|
||||
|
||||
// first add an initial, small feature to make sure the next one starts at
|
||||
// a non-zero offset
|
||||
bucket.addFeature({} as BucketFeature, [createLine(10)], undefined, undefined, undefined, noSubdivision);
|
||||
bucket.addFeature({} as BucketFeature, [createLine(10)], undefined, undefined, undefined, undefined, noSubdivision);
|
||||
|
||||
// add a feature that will break across the group boundary
|
||||
bucket.addFeature({} as BucketFeature, [createLine(128)], undefined, undefined, undefined, noSubdivision);
|
||||
bucket.addFeature({} as BucketFeature, [createLine(128)], undefined, undefined, undefined, undefined, noSubdivision);
|
||||
|
||||
// Each polygon must fit entirely within a segment, so we expect the
|
||||
// first segment to include the first feature and the first polygon
|
||||
@ -173,4 +173,19 @@ describe('LineBucket', () => {
|
||||
test: {min: 'test-pattern', mid: 'test-pattern', max: 'test-pattern'}
|
||||
});
|
||||
});
|
||||
|
||||
test('LineBucket line-dasharray with global-state', () => {
|
||||
const bucket = createLineBucket({id: 'test',
|
||||
paint: {'line-dasharray': ['coalesce', ['get', 'dasharray'], ['global-state', 'dasharray']]},
|
||||
globalState: {'dasharray': [3, 3]},
|
||||
availableImages: []
|
||||
});
|
||||
|
||||
bucket.populate(getFeaturesFromLayer(sourceLayer), createPopulateOptions([]), undefined);
|
||||
|
||||
expect(bucket.patternFeatures.length).toBeGreaterThan(0);
|
||||
expect(bucket.patternFeatures[0].dashes).toEqual({
|
||||
test: {min: '3,3,false', mid: '3,3,false', max: '3,3,false'}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
79
node_modules/maplibre-gl/src/data/bucket/line_bucket.ts
generated
vendored
79
node_modules/maplibre-gl/src/data/bucket/line_bucket.ts
generated
vendored
@ -34,6 +34,7 @@ import type {ImagePosition} from '../../render/image_atlas';
|
||||
import type {VectorTileLayer} from '@mapbox/vector-tile';
|
||||
import {subdivideVertexLine} from '../../render/subdivision';
|
||||
import type {SubdivisionGranularitySetting} from '../../render/subdivision_granularity_settings';
|
||||
import {type DashEntry} from '../../render/line_atlas';
|
||||
|
||||
// NOTE ON EXTRUDE SCALE:
|
||||
// scale the extrusion vector so that the normal length is this value.
|
||||
@ -115,7 +116,7 @@ export class LineBucket implements Bucket {
|
||||
indexArray: TriangleIndexArray;
|
||||
indexBuffer: IndexBuffer;
|
||||
|
||||
hasPattern: boolean;
|
||||
hasDependencies: boolean;
|
||||
programConfigurations: ProgramConfigurationSet<LineStyleLayer>;
|
||||
segments: SegmentVector;
|
||||
uploaded: boolean;
|
||||
@ -126,7 +127,7 @@ export class LineBucket implements Bucket {
|
||||
this.layers = options.layers;
|
||||
this.layerIds = this.layers.map(layer => layer.id);
|
||||
this.index = options.index;
|
||||
this.hasPattern = false;
|
||||
this.hasDependencies = false;
|
||||
this.patternFeatures = [];
|
||||
this.lineClipsArray = [];
|
||||
this.gradients = {};
|
||||
@ -145,7 +146,7 @@ export class LineBucket implements Bucket {
|
||||
}
|
||||
|
||||
populate(features: Array<IndexedFeature>, options: PopulateParameters, canonical: CanonicalTileID) {
|
||||
this.hasPattern = hasPattern('line', this.layers, options);
|
||||
this.hasDependencies = hasPattern('line', this.layers, options) || this.hasLineDasharray(this.layers);
|
||||
const lineSortKey = this.layers[0].layout.get('line-sort-key');
|
||||
const sortFeaturesByKey = !lineSortKey.isConstant();
|
||||
const bucketFeatures: BucketFeature[] = [];
|
||||
@ -168,6 +169,7 @@ export class LineBucket implements Bucket {
|
||||
index,
|
||||
geometry: needGeometry ? evaluationFeature.geometry : loadGeometry(feature),
|
||||
patterns: {},
|
||||
dashes: {},
|
||||
sortKey
|
||||
};
|
||||
|
||||
@ -183,13 +185,18 @@ export class LineBucket implements Bucket {
|
||||
for (const bucketFeature of bucketFeatures) {
|
||||
const {geometry, index, sourceLayerIndex} = bucketFeature;
|
||||
|
||||
if (this.hasPattern) {
|
||||
const patternBucketFeature = addPatternDependencies('line', this.layers, bucketFeature, {zoom: this.zoom}, options);
|
||||
if (this.hasDependencies) {
|
||||
if (hasPattern('line', this.layers, options)) {
|
||||
addPatternDependencies('line', this.layers, bucketFeature, {zoom: this.zoom}, options);
|
||||
} else if (this.hasLineDasharray(this.layers)) {
|
||||
this.addLineDashDependencies(this.layers, bucketFeature, this.zoom, options);
|
||||
}
|
||||
|
||||
// pattern features are added only once the pattern is loaded into the image atlas
|
||||
// so are stored during populate until later updated with positions by tile worker in addFeatures
|
||||
this.patternFeatures.push(patternBucketFeature);
|
||||
this.patternFeatures.push(bucketFeature);
|
||||
} else {
|
||||
this.addFeature(bucketFeature, geometry, index, canonical, {}, options.subdivisionGranularity);
|
||||
this.addFeature(bucketFeature, geometry, index, canonical, {}, {}, options.subdivisionGranularity);
|
||||
}
|
||||
|
||||
const feature = features[index].feature;
|
||||
@ -197,16 +204,17 @@ export class LineBucket implements Bucket {
|
||||
}
|
||||
}
|
||||
|
||||
update(states: FeatureStates, vtLayer: VectorTileLayer, imagePositions: {[_: string]: ImagePosition}) {
|
||||
update(states: FeatureStates, vtLayer: VectorTileLayer, imagePositions: {[_: string]: ImagePosition}, dashPositions: {[_: string]: DashEntry}) {
|
||||
if (!this.stateDependentLayers.length) return;
|
||||
this.programConfigurations.updatePaintArrays(states, vtLayer, this.stateDependentLayers, {
|
||||
imagePositions
|
||||
imagePositions,
|
||||
dashPositions
|
||||
});
|
||||
}
|
||||
|
||||
addFeatures(options: PopulateParameters, canonical: CanonicalTileID, imagePositions: {[_: string]: ImagePosition}) {
|
||||
addFeatures(options: PopulateParameters, canonical: CanonicalTileID, imagePositions: {[_: string]: ImagePosition}, dashPositions?: {[_: string]: DashEntry}) {
|
||||
for (const feature of this.patternFeatures) {
|
||||
this.addFeature(feature, feature.geometry, feature.index, canonical, imagePositions, options.subdivisionGranularity);
|
||||
this.addFeature(feature, feature.geometry, feature.index, canonical, imagePositions, dashPositions, options.subdivisionGranularity);
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,7 +254,7 @@ export class LineBucket implements Bucket {
|
||||
}
|
||||
}
|
||||
|
||||
addFeature(feature: BucketFeature, geometry: Array<Array<Point>>, index: number, canonical: CanonicalTileID, imagePositions: {[_: string]: ImagePosition}, subdivisionGranularity: SubdivisionGranularitySetting) {
|
||||
addFeature(feature: BucketFeature, geometry: Array<Array<Point>>, index: number, canonical: CanonicalTileID, imagePositions: {[_: string]: ImagePosition}, dashPositions: Record<string, DashEntry>, subdivisionGranularity: SubdivisionGranularitySetting) {
|
||||
const layout = this.layers[0].layout;
|
||||
const join = layout.get('line-join').evaluate(feature, {});
|
||||
const cap = layout.get('line-cap');
|
||||
@ -258,7 +266,7 @@ export class LineBucket implements Bucket {
|
||||
this.addLine(line, feature, join, cap, miterLimit, roundLimit, canonical, subdivisionGranularity);
|
||||
}
|
||||
|
||||
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, {imagePositions, canonical});
|
||||
this.programConfigurations.populatePaintArrays(this.layoutVertexArray.length, feature, index, {imagePositions, dashPositions, canonical});
|
||||
}
|
||||
|
||||
addLine(vertices: Array<Point>, feature: BucketFeature, join: string, cap: string, miterLimit: number, roundLimit: number, canonical: CanonicalTileID | undefined, subdivisionGranularity: SubdivisionGranularitySetting) {
|
||||
@ -594,6 +602,51 @@ export class LineBucket implements Bucket {
|
||||
this.distance += prev.dist(next);
|
||||
this.updateScaledDistance();
|
||||
}
|
||||
|
||||
private hasLineDasharray(layers: Array<LineStyleLayer>): boolean {
|
||||
for (const layer of layers) {
|
||||
const dasharrayProperty = layer.paint.get('line-dasharray');
|
||||
if (dasharrayProperty && !dasharrayProperty.isConstant()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private addLineDashDependencies(layers: Array<LineStyleLayer>, bucketFeature: BucketFeature, zoom: number, options: PopulateParameters) {
|
||||
for (const layer of layers) {
|
||||
const dasharrayProperty = layer.paint.get('line-dasharray');
|
||||
|
||||
if (!dasharrayProperty || dasharrayProperty.value.kind === 'constant') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const round = layer.layout.get('line-cap') === 'round';
|
||||
|
||||
const min = {
|
||||
dasharray: dasharrayProperty.value.evaluate({zoom: zoom - 1}, bucketFeature, {}),
|
||||
round
|
||||
};
|
||||
const mid = {
|
||||
dasharray: dasharrayProperty.value.evaluate({zoom}, bucketFeature, {}),
|
||||
round
|
||||
};
|
||||
const max = {
|
||||
dasharray: dasharrayProperty.value.evaluate({zoom: zoom + 1}, bucketFeature, {}),
|
||||
round
|
||||
};
|
||||
|
||||
const minKey = `${min.dasharray.join(',')},${min.round}`;
|
||||
const midKey = `${mid.dasharray.join(',')},${mid.round}`;
|
||||
const maxKey = `${max.dasharray.join(',')},${max.round}`;
|
||||
|
||||
options.dashDependencies[minKey] = min;
|
||||
options.dashDependencies[midKey] = mid;
|
||||
options.dashDependencies[maxKey] = max;
|
||||
|
||||
bucketFeature.dashes[layer.id] = {min: minKey, mid: midKey, max: maxKey};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
register('LineBucket', LineBucket, {omit: ['layers', 'patternFeatures']});
|
||||
|
||||
19
node_modules/maplibre-gl/src/data/bucket/symbol_bucket.test.ts
generated
vendored
19
node_modules/maplibre-gl/src/data/bucket/symbol_bucket.test.ts
generated
vendored
@ -18,7 +18,6 @@ import {SubdivisionGranularitySetting} from '../../render/subdivision_granularit
|
||||
import {MercatorTransform} from '../../geo/projection/mercator_transform';
|
||||
import {createPopulateOptions, loadVectorTile} from '../../../test/unit/lib/tile';
|
||||
|
||||
/*eslint new-cap: 0*/
|
||||
const collisionBoxArray = new CollisionBoxArray();
|
||||
const transform = new MercatorTransform();
|
||||
transform.resize(100, 100);
|
||||
@ -33,7 +32,7 @@ function bucketSetup(text = 'abcde') {
|
||||
return createSymbolBucket('test', 'Test', text, collisionBoxArray);
|
||||
}
|
||||
|
||||
function createIndexedFeature(id, index, iconId) {
|
||||
function createIndexedFeature(id: number, index: number, iconId: string): IndexedFeature {
|
||||
return {
|
||||
feature: {
|
||||
extent: 8192,
|
||||
@ -42,26 +41,26 @@ function createIndexedFeature(id, index, iconId) {
|
||||
properties: {
|
||||
icon: iconId
|
||||
},
|
||||
loadGeometry () {
|
||||
loadGeometry() {
|
||||
return [[{x: 0, y: 0}]];
|
||||
}
|
||||
},
|
||||
id,
|
||||
index,
|
||||
sourceLayerIndex: 0
|
||||
};
|
||||
} as any as IndexedFeature;
|
||||
}
|
||||
|
||||
describe('SymbolBucket', () => {
|
||||
let features;
|
||||
let features: IndexedFeature[];
|
||||
beforeAll(() => {
|
||||
// Load point features from fixture tile.
|
||||
const sourceLayer = loadVectorTile().layers.place_label;
|
||||
features = [{feature: sourceLayer.feature(10)} as IndexedFeature];
|
||||
});
|
||||
test('SymbolBucket', () => {
|
||||
const bucketA = bucketSetup() as any as SymbolBucket;
|
||||
const bucketB = bucketSetup() as any as SymbolBucket;
|
||||
const bucketA = bucketSetup();
|
||||
const bucketB = bucketSetup();
|
||||
const options = createPopulateOptions([]);
|
||||
const placement = new Placement(transform, undefined as any, 0, true);
|
||||
const tileID = new OverscaledTileID(0, 0, 0, 0, 0);
|
||||
@ -114,7 +113,7 @@ describe('SymbolBucket', () => {
|
||||
const spy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
||||
SymbolBucket.MAX_GLYPHS = 5;
|
||||
|
||||
const bucket = bucketSetup() as any as SymbolBucket;
|
||||
const bucket = bucketSetup();
|
||||
const options = {iconDependencies: {}, glyphDependencies: {}} as PopulateParameters;
|
||||
|
||||
bucket.populate(features, options, undefined as any);
|
||||
@ -147,7 +146,7 @@ describe('SymbolBucket', () => {
|
||||
a: new ImagePosition({x: 0, y: 0, w: 10, h: 10}, 1 as any as StyleImage),
|
||||
b: new ImagePosition({x: 10, y: 0, w: 10, h: 10}, 1 as any as StyleImage)
|
||||
};
|
||||
const bucket = createSymbolIconBucket('test', 'icon', collisionBoxArray) as any as SymbolBucket;
|
||||
const bucket = createSymbolIconBucket('test', 'icon', collisionBoxArray);
|
||||
const options = createPopulateOptions([]);
|
||||
|
||||
bucket.populate(
|
||||
@ -190,7 +189,7 @@ describe('SymbolBucket', () => {
|
||||
a: new ImagePosition({x: 0, y: 0, w: 10, h: 10}, 1 as any as StyleImage),
|
||||
b: new ImagePosition({x: 10, y: 0, w: 10, h: 10}, 1 as any as StyleImage)
|
||||
};
|
||||
const bucket = createSymbolIconBucket('test', 'icon', collisionBoxArray) as any as SymbolBucket;
|
||||
const bucket = createSymbolIconBucket('test', 'icon', collisionBoxArray);
|
||||
const options = createPopulateOptions([]);
|
||||
|
||||
bucket.populate(
|
||||
|
||||
7
node_modules/maplibre-gl/src/data/bucket/symbol_bucket.ts
generated
vendored
7
node_modules/maplibre-gl/src/data/bucket/symbol_bucket.ts
generated
vendored
@ -37,6 +37,7 @@ import {EvaluationParameters} from '../../style/evaluation_parameters';
|
||||
import {Formatted, ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
||||
import {rtlWorkerPlugin} from '../../source/rtl_text_plugin_worker';
|
||||
import {getOverlapMode} from '../../style/style_layer/overlap_mode';
|
||||
import {isSafari} from '../../util/util';
|
||||
import type {CanonicalTileID} from '../../source/tile_id';
|
||||
import type {
|
||||
Bucket,
|
||||
@ -323,7 +324,7 @@ export class SymbolBucket implements Bucket {
|
||||
iconsNeedLinear: boolean;
|
||||
bucketInstanceId: number;
|
||||
justReloaded: boolean;
|
||||
hasPattern: boolean;
|
||||
hasDependencies: boolean;
|
||||
|
||||
textSizeData: SizeData;
|
||||
iconSizeData: SizeData;
|
||||
@ -362,13 +363,13 @@ export class SymbolBucket implements Bucket {
|
||||
constructor(options: BucketParameters<SymbolStyleLayer>) {
|
||||
this.collisionBoxArray = options.collisionBoxArray;
|
||||
this.zoom = options.zoom;
|
||||
this.overscaling = options.overscaling;
|
||||
this.overscaling = isSafari(globalThis) ? Math.min(options.overscaling, 128) : options.overscaling;
|
||||
this.layers = options.layers;
|
||||
this.layerIds = this.layers.map(layer => layer.id);
|
||||
this.index = options.index;
|
||||
this.pixelRatio = options.pixelRatio;
|
||||
this.sourceLayerIndex = options.sourceLayerIndex;
|
||||
this.hasPattern = false;
|
||||
this.hasDependencies = false;
|
||||
this.hasRTLText = false;
|
||||
this.sortKeyRanges = [];
|
||||
|
||||
|
||||
180
node_modules/maplibre-gl/src/data/program_configuration.ts
generated
vendored
180
node_modules/maplibre-gl/src/data/program_configuration.ts
generated
vendored
@ -2,9 +2,10 @@ import {packUint8ToFloat} from '../shaders/encode_attribute';
|
||||
import {type Color, supportsPropertyExpression} from '@maplibre/maplibre-gl-style-spec';
|
||||
import {register} from '../util/web_worker_transfer';
|
||||
import {PossiblyEvaluatedPropertyValue} from '../style/properties';
|
||||
import {StructArrayLayout1f4, StructArrayLayout2f8, StructArrayLayout4f16, PatternLayoutArray} from './array_types.g';
|
||||
import {StructArrayLayout1f4, StructArrayLayout2f8, StructArrayLayout4f16, PatternLayoutArray, DashLayoutArray} from './array_types.g';
|
||||
import {clamp} from '../util/util';
|
||||
import {patternAttributes} from './bucket/pattern_attributes';
|
||||
import {dashAttributes} from './bucket/dash_attributes';
|
||||
import {EvaluationParameters} from '../style/evaluation_parameters';
|
||||
import {FeaturePositionMap} from './feature_position_map';
|
||||
import {type Uniform, Uniform1f, UniformColor, Uniform4f} from '../render/uniform_binding';
|
||||
@ -28,6 +29,7 @@ import type {
|
||||
} from '@maplibre/maplibre-gl-style-spec';
|
||||
import type {FeatureStates} from '../source/source_state';
|
||||
import type {VectorTileLayer} from '@mapbox/vector-tile';
|
||||
import type {DashEntry} from '../render/line_atlas';
|
||||
|
||||
export type BinderUniform = {
|
||||
name: string;
|
||||
@ -46,6 +48,9 @@ type PaintOptions = {
|
||||
imagePositions: {
|
||||
[_: string]: ImagePosition;
|
||||
};
|
||||
dashPositions?: {
|
||||
[_: string]: DashEntry;
|
||||
};
|
||||
canonical?: CanonicalTileID;
|
||||
formattedSection?: FormattedSection;
|
||||
globalState?: Record<string, any>;
|
||||
@ -134,6 +139,8 @@ class CrossFadedConstantBinder implements UniformBinder {
|
||||
uniformNames: Array<string>;
|
||||
patternFrom: Array<number>;
|
||||
patternTo: Array<number>;
|
||||
dashFrom: Array<number>;
|
||||
dashTo: Array<number>;
|
||||
pixelRatioFrom: number;
|
||||
pixelRatioTo: number;
|
||||
|
||||
@ -152,17 +159,35 @@ class CrossFadedConstantBinder implements UniformBinder {
|
||||
this.patternTo = posTo.tlbr;
|
||||
}
|
||||
|
||||
setConstantDashPositions(dashTo: DashEntry, dashFrom: DashEntry) {
|
||||
this.dashTo = [0, dashTo.y, dashTo.height, dashTo.width];
|
||||
this.dashFrom = [0, dashFrom.y, dashFrom.height, dashFrom.width];
|
||||
}
|
||||
|
||||
setUniform(uniform: Uniform<any>, globals: GlobalProperties, currentValue: PossiblyEvaluatedPropertyValue<unknown>, uniformName: string) {
|
||||
const pos =
|
||||
uniformName === 'u_pattern_to' ? this.patternTo :
|
||||
uniformName === 'u_pattern_from' ? this.patternFrom :
|
||||
uniformName === 'u_pixel_ratio_to' ? this.pixelRatioTo :
|
||||
uniformName === 'u_pixel_ratio_from' ? this.pixelRatioFrom : null;
|
||||
if (pos) uniform.set(pos);
|
||||
let value = null;
|
||||
|
||||
if (uniformName === 'u_pattern_to') {
|
||||
value = this.patternTo;
|
||||
} else if (uniformName === 'u_pattern_from') {
|
||||
value = this.patternFrom;
|
||||
} else if (uniformName === 'u_dasharray_to') {
|
||||
value = this.dashTo;
|
||||
} else if (uniformName === 'u_dasharray_from') {
|
||||
value = this.dashFrom;
|
||||
} else if (uniformName === 'u_pixel_ratio_to') {
|
||||
value = this.pixelRatioTo;
|
||||
} else if (uniformName === 'u_pixel_ratio_from') {
|
||||
value = this.pixelRatioFrom;
|
||||
}
|
||||
|
||||
if (value !== null) {
|
||||
uniform.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
getBinding(context: Context, location: WebGLUniformLocation, name: string): Partial<Uniform<any>> {
|
||||
return name.substr(0, 9) === 'u_pattern' ?
|
||||
return (name.substr(0, 9) === 'u_pattern' || name.substr(0, 12) === 'u_dasharray_') ?
|
||||
new Uniform4f(context, location) :
|
||||
new Uniform1f(context, location);
|
||||
}
|
||||
@ -321,7 +346,7 @@ class CompositeExpressionBinder implements AttributeBinder, UniformBinder {
|
||||
}
|
||||
}
|
||||
|
||||
class CrossFadedCompositeBinder implements AttributeBinder {
|
||||
abstract class CrossFadedBinder<T> implements AttributeBinder {
|
||||
expression: CompositeExpression;
|
||||
type: string;
|
||||
useIntegerZoom: boolean;
|
||||
@ -351,45 +376,41 @@ class CrossFadedCompositeBinder implements AttributeBinder {
|
||||
const start = this.zoomInPaintVertexArray.length;
|
||||
this.zoomInPaintVertexArray.resize(length);
|
||||
this.zoomOutPaintVertexArray.resize(length);
|
||||
this._setPaintValues(start, length, feature.patterns && feature.patterns[this.layerId], options.imagePositions);
|
||||
this._setPaintValues(start, length, this.getPositionIds(feature), options);
|
||||
}
|
||||
|
||||
updatePaintArray(start: number, end: number, feature: Feature, featureState: FeatureState, options: PaintOptions) {
|
||||
this._setPaintValues(start, end, feature.patterns && feature.patterns[this.layerId], options.imagePositions);
|
||||
this._setPaintValues(start, end, this.getPositionIds(feature), options);
|
||||
}
|
||||
|
||||
_setPaintValues(start, end, patterns, positions) {
|
||||
if (!positions || !patterns) return;
|
||||
abstract getVertexAttributes(): Array<StructArrayMember>;
|
||||
|
||||
const {min, mid, max} = patterns;
|
||||
const imageMin = positions[min];
|
||||
const imageMid = positions[mid];
|
||||
const imageMax = positions[max];
|
||||
if (!imageMin || !imageMid || !imageMax) return;
|
||||
protected abstract getPositionIds(feature: Feature): {min: string; mid: string; max: string};
|
||||
protected abstract getPositions(options: PaintOptions): {[_: string]: T};
|
||||
protected abstract emplace(array: StructArray, index: number, midPos: T, minMaxPos: T): void;
|
||||
|
||||
protected _setPaintValues(start: number, end: number, positionIds: {min: string; mid: string; max: string}, options: PaintOptions) {
|
||||
const positions = this.getPositions(options);
|
||||
if (!positions || !positionIds) return;
|
||||
const min = positions[positionIds.min];
|
||||
const mid = positions[positionIds.mid];
|
||||
const max = positions[positionIds.max];
|
||||
if (!min || !mid || !max) return;
|
||||
|
||||
// We populate two paint arrays because, for cross-faded properties, we don't know which direction
|
||||
// we're cross-fading to at layout time. In order to keep vertex attributes to a minimum and not pass
|
||||
// unnecessary vertex data to the shaders, we determine which to upload at draw time.
|
||||
for (let i = start; i < end; i++) {
|
||||
this.zoomInPaintVertexArray.emplace(i,
|
||||
imageMid.tl[0], imageMid.tl[1], imageMid.br[0], imageMid.br[1],
|
||||
imageMin.tl[0], imageMin.tl[1], imageMin.br[0], imageMin.br[1],
|
||||
imageMid.pixelRatio,
|
||||
imageMin.pixelRatio,
|
||||
);
|
||||
this.zoomOutPaintVertexArray.emplace(i,
|
||||
imageMid.tl[0], imageMid.tl[1], imageMid.br[0], imageMid.br[1],
|
||||
imageMax.tl[0], imageMax.tl[1], imageMax.br[0], imageMax.br[1],
|
||||
imageMid.pixelRatio,
|
||||
imageMax.pixelRatio,
|
||||
);
|
||||
this.emplace(this.zoomInPaintVertexArray, i, mid, min);
|
||||
this.emplace(this.zoomOutPaintVertexArray, i, mid, max);
|
||||
}
|
||||
}
|
||||
|
||||
upload(context: Context) {
|
||||
if (this.zoomInPaintVertexArray && this.zoomInPaintVertexArray.arrayBuffer && this.zoomOutPaintVertexArray && this.zoomOutPaintVertexArray.arrayBuffer) {
|
||||
this.zoomInPaintVertexBuffer = context.createVertexBuffer(this.zoomInPaintVertexArray, patternAttributes.members, this.expression.isStateDependent);
|
||||
this.zoomOutPaintVertexBuffer = context.createVertexBuffer(this.zoomOutPaintVertexArray, patternAttributes.members, this.expression.isStateDependent);
|
||||
const attributes = this.getVertexAttributes();
|
||||
this.zoomInPaintVertexBuffer = context.createVertexBuffer(this.zoomInPaintVertexArray, attributes, this.expression.isStateDependent);
|
||||
this.zoomOutPaintVertexBuffer = context.createVertexBuffer(this.zoomOutPaintVertexArray, attributes, this.expression.isStateDependent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -399,6 +420,50 @@ class CrossFadedCompositeBinder implements AttributeBinder {
|
||||
}
|
||||
}
|
||||
|
||||
class CrossFadedPatternBinder extends CrossFadedBinder<ImagePosition> {
|
||||
protected getPositions(options: PaintOptions): {[_: string]: ImagePosition} {
|
||||
return options.imagePositions;
|
||||
}
|
||||
|
||||
protected getPositionIds(feature: Feature) {
|
||||
return feature.patterns && feature.patterns[this.layerId];
|
||||
}
|
||||
|
||||
getVertexAttributes(): Array<StructArrayMember> {
|
||||
return patternAttributes.members;
|
||||
}
|
||||
|
||||
protected emplace(array: StructArray, index: number, midPos: ImagePosition, minMaxPos: ImagePosition): void {
|
||||
array.emplace(index,
|
||||
midPos.tlbr[0], midPos.tlbr[1], midPos.tlbr[2], midPos.tlbr[3],
|
||||
minMaxPos.tlbr[0], minMaxPos.tlbr[1], minMaxPos.tlbr[2], minMaxPos.tlbr[3],
|
||||
midPos.pixelRatio,
|
||||
minMaxPos.pixelRatio,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CrossFadedDasharrayBinder extends CrossFadedBinder<DashEntry> {
|
||||
protected getPositions(options: PaintOptions): {[_: string]: DashEntry} {
|
||||
return options.dashPositions;
|
||||
}
|
||||
|
||||
protected getPositionIds(feature: Feature) {
|
||||
return feature.dashes && feature.dashes[this.layerId];
|
||||
}
|
||||
|
||||
getVertexAttributes(): Array<StructArrayMember> {
|
||||
return dashAttributes.members;
|
||||
}
|
||||
|
||||
protected emplace(array: StructArray, index: number, midPos: DashEntry, minMaxPos: DashEntry): void {
|
||||
array.emplace(index,
|
||||
0, midPos.y, midPos.height, midPos.width,
|
||||
0, minMaxPos.y, minMaxPos.height, minMaxPos.width,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* ProgramConfiguration contains the logic for binding style layer properties and tile
|
||||
@ -452,7 +517,9 @@ export class ProgramConfiguration {
|
||||
} else if (expression.kind === 'source' || isCrossFaded) {
|
||||
const StructArrayLayout = layoutType(property, type, 'source');
|
||||
this.binders[property] = isCrossFaded ?
|
||||
new CrossFadedCompositeBinder(expression as CompositeExpression, type, useIntegerZoom, zoom, StructArrayLayout, layer.id) :
|
||||
property === 'line-dasharray' ?
|
||||
new CrossFadedDasharrayBinder(expression as CompositeExpression, type, useIntegerZoom, zoom, StructArrayLayout, layer.id) :
|
||||
new CrossFadedPatternBinder(expression as CompositeExpression, type, useIntegerZoom, zoom, StructArrayLayout, layer.id) :
|
||||
new SourceExpressionBinder(expression as SourceExpression, names, type, StructArrayLayout);
|
||||
keys.push(`/a_${property}`);
|
||||
|
||||
@ -474,8 +541,8 @@ export class ProgramConfiguration {
|
||||
populatePaintArrays(newLength: number, feature: Feature, options: PaintOptions) {
|
||||
for (const property in this.binders) {
|
||||
const binder = this.binders[property];
|
||||
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedCompositeBinder)
|
||||
(binder as AttributeBinder).populatePaintArray(newLength, feature, options);
|
||||
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedBinder)
|
||||
binder.populatePaintArray(newLength, feature, options);
|
||||
}
|
||||
}
|
||||
setConstantPatternPositions(posTo: ImagePosition, posFrom: ImagePosition) {
|
||||
@ -486,6 +553,14 @@ export class ProgramConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
setConstantDashPositions(dashTo: DashEntry, dashFrom: DashEntry) {
|
||||
for (const property in this.binders) {
|
||||
const binder = this.binders[property];
|
||||
if (binder instanceof CrossFadedConstantBinder)
|
||||
binder.setConstantDashPositions(dashTo, dashFrom);
|
||||
}
|
||||
}
|
||||
|
||||
updatePaintArrays(
|
||||
featureStates: FeatureStates,
|
||||
featureMap: FeaturePositionMap,
|
||||
@ -503,11 +578,11 @@ export class ProgramConfiguration {
|
||||
for (const property in this.binders) {
|
||||
const binder = this.binders[property];
|
||||
if ((binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder ||
|
||||
binder instanceof CrossFadedCompositeBinder) && (binder as any).expression.isStateDependent === true) {
|
||||
binder instanceof CrossFadedBinder) && binder.expression.isStateDependent === true) {
|
||||
//AHM: Remove after https://github.com/mapbox/mapbox-gl-js/issues/6255
|
||||
const value = (layer.paint as any).get(property);
|
||||
(binder as any).expression = value.value;
|
||||
(binder as AttributeBinder).updatePaintArray(pos.start, pos.end, feature, featureStates[id], options);
|
||||
binder.expression = value.value;
|
||||
binder.updatePaintArray(pos.start, pos.end, feature, featureStates[id], options);
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
@ -535,9 +610,10 @@ export class ProgramConfiguration {
|
||||
for (let i = 0; i < binder.paintVertexAttributes.length; i++) {
|
||||
result.push(binder.paintVertexAttributes[i].name);
|
||||
}
|
||||
} else if (binder instanceof CrossFadedCompositeBinder) {
|
||||
for (let i = 0; i < patternAttributes.members.length; i++) {
|
||||
result.push(patternAttributes.members[i].name);
|
||||
} else if (binder instanceof CrossFadedBinder) {
|
||||
const attributes = binder.getVertexAttributes();
|
||||
for (const attribute of attributes) {
|
||||
result.push(attribute.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -595,7 +671,7 @@ export class ProgramConfiguration {
|
||||
|
||||
for (const property in this.binders) {
|
||||
const binder = this.binders[property];
|
||||
if (crossfade && binder instanceof CrossFadedCompositeBinder) {
|
||||
if (crossfade && binder instanceof CrossFadedBinder) {
|
||||
const patternVertexBuffer = crossfade.fromScale === 2 ? binder.zoomInPaintVertexBuffer : binder.zoomOutPaintVertexBuffer;
|
||||
if (patternVertexBuffer) this._buffers.push(patternVertexBuffer);
|
||||
|
||||
@ -608,7 +684,7 @@ export class ProgramConfiguration {
|
||||
upload(context: Context) {
|
||||
for (const property in this.binders) {
|
||||
const binder = this.binders[property];
|
||||
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedCompositeBinder)
|
||||
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedBinder)
|
||||
binder.upload(context);
|
||||
}
|
||||
this.updatePaintBuffers();
|
||||
@ -617,7 +693,7 @@ export class ProgramConfiguration {
|
||||
destroy() {
|
||||
for (const property in this.binders) {
|
||||
const binder = this.binders[property];
|
||||
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedCompositeBinder)
|
||||
if (binder instanceof SourceExpressionBinder || binder instanceof CompositeExpressionBinder || binder instanceof CrossFadedBinder)
|
||||
binder.destroy();
|
||||
}
|
||||
}
|
||||
@ -677,7 +753,7 @@ export class ProgramConfigurationSet<Layer extends TypedStyleLayer> {
|
||||
}
|
||||
}
|
||||
|
||||
function paintAttributeNames(property, type) {
|
||||
function paintAttributeNames(property: string, type: string) {
|
||||
const attributeNameExceptions = {
|
||||
'text-opacity': ['opacity'],
|
||||
'icon-opacity': ['opacity'],
|
||||
@ -690,6 +766,7 @@ function paintAttributeNames(property, type) {
|
||||
'text-halo-width': ['halo_width'],
|
||||
'icon-halo-width': ['halo_width'],
|
||||
'line-gap-width': ['gapwidth'],
|
||||
'line-dasharray': ['dasharray_to', 'dasharray_from'],
|
||||
'line-pattern': ['pattern_to', 'pattern_from', 'pixel_ratio_to', 'pixel_ratio_from'],
|
||||
'fill-pattern': ['pattern_to', 'pattern_from', 'pixel_ratio_to', 'pixel_ratio_from'],
|
||||
'fill-extrusion-pattern': ['pattern_to', 'pattern_from', 'pixel_ratio_to', 'pixel_ratio_from'],
|
||||
@ -698,7 +775,7 @@ function paintAttributeNames(property, type) {
|
||||
return attributeNameExceptions[property] || [property.replace(`${type}-`, '').replace(/-/g, '_')];
|
||||
}
|
||||
|
||||
function getLayoutException(property) {
|
||||
function getLayoutException(property: string) {
|
||||
const propertyExceptions = {
|
||||
'line-pattern': {
|
||||
'source': PatternLayoutArray,
|
||||
@ -711,13 +788,17 @@ function getLayoutException(property) {
|
||||
'fill-extrusion-pattern': {
|
||||
'source': PatternLayoutArray,
|
||||
'composite': PatternLayoutArray
|
||||
}
|
||||
},
|
||||
'line-dasharray': {
|
||||
'source': DashLayoutArray,
|
||||
'composite': DashLayoutArray
|
||||
},
|
||||
};
|
||||
|
||||
return propertyExceptions[property];
|
||||
}
|
||||
|
||||
function layoutType(property, type, binderType) {
|
||||
function layoutType(property: string, type: string, binderType: string) {
|
||||
const defaultLayouts = {
|
||||
'color': {
|
||||
'source': StructArrayLayout2f8,
|
||||
@ -736,7 +817,8 @@ function layoutType(property, type, binderType) {
|
||||
register('ConstantBinder', ConstantBinder);
|
||||
register('CrossFadedConstantBinder', CrossFadedConstantBinder);
|
||||
register('SourceExpressionBinder', SourceExpressionBinder);
|
||||
register('CrossFadedCompositeBinder', CrossFadedCompositeBinder);
|
||||
register('CrossFadedPatternBinder', CrossFadedPatternBinder);
|
||||
register('CrossFadedDasharrayBinder', CrossFadedDasharrayBinder);
|
||||
register('CompositeExpressionBinder', CompositeExpressionBinder);
|
||||
register('ProgramConfiguration', ProgramConfiguration, {omit: ['_buffers']});
|
||||
register('ProgramConfigurationSet', ProgramConfigurationSet);
|
||||
|
||||
20
node_modules/maplibre-gl/src/geo/projection/covering_tiles.test.ts
generated
vendored
20
node_modules/maplibre-gl/src/geo/projection/covering_tiles.test.ts
generated
vendored
@ -289,7 +289,7 @@ describe('coveringTiles', () => {
|
||||
tileSize: 512
|
||||
};
|
||||
|
||||
const transform = new MercatorTransform(0, 22, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(200, 200);
|
||||
|
||||
test('general', () => {
|
||||
@ -531,7 +531,7 @@ describe('coveringTiles', () => {
|
||||
reparseOverscaled: true
|
||||
};
|
||||
|
||||
const transform = new MercatorTransform(0, 10, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 10, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(10, 400);
|
||||
// make slightly off center so that sort order is not subject to precision issues
|
||||
transform.setCenter(new LngLat(-0.01, 0.01));
|
||||
@ -552,7 +552,7 @@ describe('coveringTiles', () => {
|
||||
tileSize: 512
|
||||
};
|
||||
|
||||
const transform = new MercatorTransform(0, 0, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 0, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(200, 200);
|
||||
transform.setCenter(new LngLat(0.01, 0.01));
|
||||
transform.setZoom(8);
|
||||
@ -569,7 +569,7 @@ describe('coveringTiles', () => {
|
||||
reparseOverscaled: true
|
||||
};
|
||||
|
||||
const transform = new MercatorTransform(0, 15, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 15, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(128, 128);
|
||||
transform.setZoom(11);
|
||||
transform.setCenter(new LngLat(-179.73, -0.087));
|
||||
@ -587,7 +587,7 @@ describe('coveringTiles', () => {
|
||||
reparseOverscaled: true
|
||||
};
|
||||
|
||||
const transform = new MercatorTransform(0, 15, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 15, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(128, 128);
|
||||
transform.setZoom(11);
|
||||
transform.setCenter(new LngLat(-179.73, 60.02));
|
||||
@ -605,7 +605,7 @@ describe('coveringTiles', () => {
|
||||
reparseOverscaled: true
|
||||
};
|
||||
|
||||
const transform = new MercatorTransform(0, 15, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 15, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(128, 128);
|
||||
transform.setZoom(11);
|
||||
transform.setCenter(new LngLat(-179.73, 85.028));
|
||||
@ -623,7 +623,7 @@ describe('coveringTiles', () => {
|
||||
reparseOverscaled: true
|
||||
};
|
||||
|
||||
const transform = new MercatorTransform(0, 15, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 15, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(128, 128);
|
||||
transform.setZoom(11);
|
||||
transform.setCenter(new LngLat(-58.97, 60.02));
|
||||
@ -641,7 +641,7 @@ describe('coveringTiles', () => {
|
||||
reparseOverscaled: true
|
||||
};
|
||||
|
||||
const transform = new MercatorTransform(0, 15, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 15, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(128, 128);
|
||||
transform.setZoom(11);
|
||||
transform.setCenter(new LngLat(-58.97, -0.087));
|
||||
@ -659,7 +659,7 @@ describe('coveringTiles', () => {
|
||||
reparseOverscaled: true
|
||||
};
|
||||
|
||||
const transform = new MercatorTransform(0, 15, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 15, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(128, 128);
|
||||
transform.setZoom(11);
|
||||
transform.setCenter(new LngLat(0.03, 0.0915));
|
||||
@ -679,7 +679,7 @@ describe('coveringZoomLevel', () => {
|
||||
let options: CoveringTilesOptions;
|
||||
|
||||
beforeEach(() => {
|
||||
transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
options = {
|
||||
tileSize: 512,
|
||||
roundZoom: false,
|
||||
|
||||
2
node_modules/maplibre-gl/src/geo/projection/globe_transform.test.ts
generated
vendored
2
node_modules/maplibre-gl/src/geo/projection/globe_transform.test.ts
generated
vendored
@ -597,7 +597,7 @@ describe('GlobeTransform', () => {
|
||||
test('change transform and make sure render world copies is kept', () => {
|
||||
const globeTransform = createGlobeTransform();
|
||||
globeTransform.setRenderWorldCopies(true);
|
||||
const mercator = new MercatorTransform(0, 1, 2, 3, false);
|
||||
const mercator = new MercatorTransform({minZoom: 0, maxZoom: 1, minPitch: 2, maxPitch: 3, renderWorldCopies: false});
|
||||
mercator.apply(globeTransform);
|
||||
|
||||
expect(mercator.renderWorldCopies).toBeTruthy();
|
||||
|
||||
21
node_modules/maplibre-gl/src/geo/projection/globe_transform.ts
generated
vendored
21
node_modules/maplibre-gl/src/geo/projection/globe_transform.ts
generated
vendored
@ -12,7 +12,8 @@ import type {LngLatBounds} from '../lng_lat_bounds';
|
||||
import type {Frustum} from '../../util/primitives/frustum';
|
||||
import type {Terrain} from '../../render/terrain';
|
||||
import type {PointProjection} from '../../symbol/projection';
|
||||
import type {IReadonlyTransform, ITransform} from '../transform_interface';
|
||||
import type {IReadonlyTransform, ITransform, TransformConstrainFunction} from '../transform_interface';
|
||||
import type {TransformOptions} from '../transform_helper';
|
||||
import type {PaddingOptions} from '../edge_insets';
|
||||
import type {ProjectionData, ProjectionDataParams} from './projection_data';
|
||||
import type {CoveringTilesDetailsProvider} from './covering_tiles_details_provider';
|
||||
@ -108,6 +109,9 @@ export class GlobeTransform implements ITransform {
|
||||
setMaxBounds(bounds?: LngLatBounds): void {
|
||||
this._helper.setMaxBounds(bounds);
|
||||
}
|
||||
setConstrain(constrain?: TransformConstrainFunction | null): void {
|
||||
this._helper.setConstrain(constrain);
|
||||
}
|
||||
overrideNearFarZ(nearZ: number, farZ: number): void {
|
||||
this._helper.overrideNearFarZ(nearZ, farZ);
|
||||
}
|
||||
@ -202,6 +206,9 @@ export class GlobeTransform implements ITransform {
|
||||
get cameraToCenterDistance(): number {
|
||||
return this._helper.cameraToCenterDistance;
|
||||
}
|
||||
get constrain(): TransformConstrainFunction {
|
||||
return this._helper.constrain;
|
||||
}
|
||||
public get nearZ(): number {
|
||||
return this._helper.nearZ;
|
||||
}
|
||||
@ -246,11 +253,11 @@ export class GlobeTransform implements ITransform {
|
||||
private _mercatorTransform: MercatorTransform;
|
||||
private _verticalPerspectiveTransform: VerticalPerspectiveTransform;
|
||||
|
||||
public constructor() {
|
||||
public constructor(options?: TransformOptions) {
|
||||
this._helper = new TransformHelper({
|
||||
calcMatrices: () => { this._calcMatrices(); },
|
||||
getConstrained: (center, zoom) => { return this.getConstrained(center, zoom); }
|
||||
});
|
||||
constrain: (center, zoom) => { return this.defaultConstrain(center, zoom); }
|
||||
}, options);
|
||||
this._globeness = 1; // When transform is cloned for use in symbols, `_updateAnimation` function which usually sets this value never gets called.
|
||||
this._mercatorTransform = new MercatorTransform();
|
||||
this._verticalPerspectiveTransform = new VerticalPerspectiveTransform();
|
||||
@ -392,9 +399,9 @@ export class GlobeTransform implements ITransform {
|
||||
return this.currentTransform.getBounds();
|
||||
}
|
||||
|
||||
getConstrained(lngLat: LngLat, zoom: number): { center: LngLat; zoom: number } {
|
||||
return this.currentTransform.getConstrained(lngLat, zoom);
|
||||
}
|
||||
defaultConstrain: TransformConstrainFunction = (lngLat, zoom) => {
|
||||
return this.currentTransform.defaultConstrain(lngLat, zoom);
|
||||
};
|
||||
|
||||
calculateCenterFromCameraLngLatAlt(lngLat: LngLatLike, alt: number, bearing?: number, pitch?: number): {center: LngLat; elevation: number; zoom: number} {
|
||||
return this._helper.calculateCenterFromCameraLngLatAlt(lngLat, alt, bearing, pitch);
|
||||
|
||||
6
node_modules/maplibre-gl/src/geo/projection/mercator_camera_helper.ts
generated
vendored
6
node_modules/maplibre-gl/src/geo/projection/mercator_camera_helper.ts
generated
vendored
@ -87,7 +87,7 @@ export class MercatorCameraHelper implements ICameraHelper {
|
||||
|
||||
let pointAtOffset = tr.centerPoint.add(options.offsetAsPoint);
|
||||
const locationAtOffset = tr.screenPointToLocation(pointAtOffset);
|
||||
const {center, zoom: endZoom} = tr.getConstrained(
|
||||
const {center, zoom: endZoom} = tr.constrain(
|
||||
LngLat.convert(options.center || locationAtOffset),
|
||||
zoom ?? startZoom
|
||||
);
|
||||
@ -144,7 +144,7 @@ export class MercatorCameraHelper implements ICameraHelper {
|
||||
const startZoom = tr.zoom;
|
||||
|
||||
// Obtain target center and zoom
|
||||
const constrained = tr.getConstrained(
|
||||
const constrained = tr.constrain(
|
||||
LngLat.convert(options.center || options.locationAtOffset),
|
||||
optionsZoom ? +options.zoom : startZoom
|
||||
);
|
||||
@ -166,7 +166,7 @@ export class MercatorCameraHelper implements ICameraHelper {
|
||||
|
||||
if (optionsMinZoom) {
|
||||
const minZoomPreConstrain = Math.min(+options.minZoom, startZoom, targetZoom);
|
||||
const minZoom = tr.getConstrained(targetCenter, minZoomPreConstrain).zoom;
|
||||
const minZoom = tr.constrain(targetCenter, minZoomPreConstrain).zoom;
|
||||
scaleOfMinZoom = zoomScale(minZoom - startZoom);
|
||||
}
|
||||
|
||||
|
||||
64
node_modules/maplibre-gl/src/geo/projection/mercator_transform.test.ts
generated
vendored
64
node_modules/maplibre-gl/src/geo/projection/mercator_transform.test.ts
generated
vendored
@ -12,7 +12,7 @@ import {expectToBeCloseToArray} from '../../util/test/util';
|
||||
|
||||
describe('transform', () => {
|
||||
test('creates a transform', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
expect(transform.unmodified).toBe(true);
|
||||
expect(transform.tileSize).toBe(512);
|
||||
@ -60,14 +60,14 @@ describe('transform', () => {
|
||||
|
||||
test('does not throw on bad center', () => {
|
||||
expect(() => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
transform.setCenter(new LngLat(50, -90));
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test('setLocationAt', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
transform.setZoom(4);
|
||||
expect(transform.center).toEqual({lng: 0, lat: 0});
|
||||
@ -76,7 +76,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('setLocationAt tilted', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
transform.setZoom(4);
|
||||
transform.setPitch(50);
|
||||
@ -86,7 +86,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('setLocationAt tilted rolled', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
transform.setZoom(4);
|
||||
transform.setPitch(50);
|
||||
@ -97,26 +97,26 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('has a default zoom', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
expect(transform.tileZoom).toBe(0);
|
||||
expect(transform.tileZoom).toBe(transform.zoom);
|
||||
});
|
||||
|
||||
test('set zoom inits tileZoom with zoom value', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60});
|
||||
transform.setZoom(5);
|
||||
expect(transform.tileZoom).toBe(5);
|
||||
});
|
||||
|
||||
test('set zoom clamps tileZoom to non negative value ', () => {
|
||||
const transform = new MercatorTransform(-2, 22, 0, 60);
|
||||
const transform = new MercatorTransform({minZoom: -2, maxZoom: 22, minPitch: 0, maxPitch: 60});
|
||||
transform.setZoom(-2);
|
||||
expect(transform.tileZoom).toBe(0);
|
||||
});
|
||||
|
||||
test('set fov', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.setFov(10);
|
||||
expect(transform.fov).toBe(10);
|
||||
transform.setFov(10);
|
||||
@ -124,7 +124,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('lngRange & latRange constrain zoom and center', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.setCenter(new LngLat(0, 0));
|
||||
transform.setZoom(10);
|
||||
transform.resize(500, 500);
|
||||
@ -143,7 +143,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('lngRange & latRange constrain zoom and center after cloning', () => {
|
||||
const old = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const old = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
old.setCenter(new LngLat(0, 0));
|
||||
old.setZoom(10);
|
||||
old.resize(500, 500);
|
||||
@ -164,7 +164,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('lngRange can constrain zoom and center across meridian', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.setCenter(new LngLat(180, 0));
|
||||
transform.setZoom(10);
|
||||
transform.resize(500, 500);
|
||||
@ -196,7 +196,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('clamps pitch', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
|
||||
transform.setPitch(45);
|
||||
expect(transform.pitch).toBe(45);
|
||||
@ -209,7 +209,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('visibleUnwrappedCoordinates', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(200, 200);
|
||||
transform.setZoom(0);
|
||||
transform.setCenter(new LngLat(-170.01, 0.01));
|
||||
@ -224,7 +224,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('maintains high float precision when calculating matrices', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(200.25, 200.25);
|
||||
transform.setZoom(20.25);
|
||||
transform.setPitch(67.25);
|
||||
@ -237,7 +237,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('recalculateZoomAndCenter: no change', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.setElevation(200);
|
||||
transform.setCenter(new LngLat(10.0, 50.0));
|
||||
transform.setZoom(14);
|
||||
@ -264,7 +264,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('recalculateZoomAndCenter: elevation increase', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.setElevation(200);
|
||||
transform.setCenter(new LngLat(10.0, 50.0));
|
||||
transform.setZoom(14);
|
||||
@ -296,7 +296,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('recalculateZoomAndCenter: elevation decrease', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.setElevation(200);
|
||||
transform.setCenter(new LngLat(10.0, 50.0));
|
||||
transform.setZoom(14);
|
||||
@ -326,7 +326,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('recalculateZoomAndCenterNoTerrain', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.setElevation(200);
|
||||
transform.setCenter(new LngLat(10.0, 50.0));
|
||||
transform.setZoom(14);
|
||||
@ -354,7 +354,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('pointCoordinate with terrain when returning null should fall back to 2D', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
const terrain = {
|
||||
pointCoordinate: () => null
|
||||
@ -365,7 +365,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('getBounds with horizon', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
|
||||
transform.setPitch(60);
|
||||
@ -378,7 +378,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('lngLatToCameraDepth', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
transform.setCenter(new LngLat(10.0, 50.0));
|
||||
|
||||
@ -389,7 +389,7 @@ describe('transform', () => {
|
||||
|
||||
test('projectTileCoordinates', () => {
|
||||
const precisionDigits = 10;
|
||||
const transform = new MercatorTransform(0, 22, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
transform.setCenter(new LngLat(10.0, 50.0));
|
||||
let projection = transform.projectTileCoordinates(1024, 1024, new UnwrappedTileID(0, new CanonicalTileID(1, 1, 0)), (_x, _y) => 0);
|
||||
@ -407,7 +407,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('getCameraLngLat', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 180, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 180, renderWorldCopies: true});
|
||||
transform.setElevation(200);
|
||||
transform.setCenter(new LngLat(15.0, 55.0));
|
||||
transform.setZoom(14);
|
||||
@ -427,7 +427,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('calculateCenterFromCameraLngLatAlt no pitch no bearing', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 180, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 180, renderWorldCopies: true});
|
||||
transform.setPitch(55);
|
||||
transform.setBearing(75);
|
||||
transform.resize(512, 512);
|
||||
@ -445,7 +445,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('calculateCenterFromCameraLngLatAlt no pitch', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 180, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 180, renderWorldCopies: true});
|
||||
transform.setPitch(55);
|
||||
transform.setBearing(75);
|
||||
transform.resize(512, 512);
|
||||
@ -465,7 +465,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('calculateCenterFromCameraLngLatAlt', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 180, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 180, renderWorldCopies: true});
|
||||
transform.setPitch(55);
|
||||
transform.setBearing(75);
|
||||
transform.resize(512, 512);
|
||||
@ -487,7 +487,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('calculateCenterFromCameraLngLatAlt 89 degrees pitch', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 180, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 180, renderWorldCopies: true});
|
||||
transform.setPitch(55);
|
||||
transform.setBearing(75);
|
||||
transform.resize(512, 512);
|
||||
@ -509,7 +509,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('calculateCenterFromCameraLngLatAlt 89.99 degrees pitch', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 180, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 180, renderWorldCopies: true});
|
||||
transform.setPitch(55);
|
||||
transform.setBearing(75);
|
||||
transform.resize(512, 512);
|
||||
@ -531,7 +531,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('calculateCenterFromCameraLngLatAlt 90 degrees pitch', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 180, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 180, renderWorldCopies: true});
|
||||
transform.setPitch(55);
|
||||
transform.setBearing(75);
|
||||
transform.resize(512, 512);
|
||||
@ -553,7 +553,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('calculateCenterFromCameraLngLatAlt 95 degrees pitch', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 180, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 180, renderWorldCopies: true});
|
||||
transform.setPitch(55);
|
||||
transform.setBearing(75);
|
||||
transform.resize(512, 512);
|
||||
@ -575,7 +575,7 @@ describe('transform', () => {
|
||||
});
|
||||
|
||||
test('calculateCenterFromCameraLngLatAlt 180 degrees pitch', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 180, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 180, renderWorldCopies: true});
|
||||
transform.setPitch(55);
|
||||
transform.setBearing(75);
|
||||
transform.resize(512, 512);
|
||||
|
||||
19
node_modules/maplibre-gl/src/geo/projection/mercator_transform.ts
generated
vendored
19
node_modules/maplibre-gl/src/geo/projection/mercator_transform.ts
generated
vendored
@ -14,7 +14,8 @@ import {MercatorCoveringTilesDetailsProvider} from './mercator_covering_tiles_de
|
||||
import {Frustum} from '../../util/primitives/frustum';
|
||||
|
||||
import type {Terrain} from '../../render/terrain';
|
||||
import type {IReadonlyTransform, ITransform} from '../transform_interface';
|
||||
import type {IReadonlyTransform, ITransform, TransformConstrainFunction} from '../transform_interface';
|
||||
import type {TransformOptions} from '../transform_helper';
|
||||
import type {PaddingOptions} from '../edge_insets';
|
||||
import type {ProjectionData, ProjectionDataParams} from './projection_data';
|
||||
import type {CoveringTilesDetailsProvider} from './covering_tiles_details_provider';
|
||||
@ -107,6 +108,9 @@ export class MercatorTransform implements ITransform {
|
||||
setMaxBounds(bounds?: LngLatBounds): void {
|
||||
this._helper.setMaxBounds(bounds);
|
||||
}
|
||||
setConstrain(constrain?: TransformConstrainFunction | null): void {
|
||||
this._helper.setConstrain(constrain);
|
||||
}
|
||||
overrideNearFarZ(nearZ: number, farZ: number): void {
|
||||
this._helper.overrideNearFarZ(nearZ, farZ);
|
||||
}
|
||||
@ -201,6 +205,9 @@ export class MercatorTransform implements ITransform {
|
||||
get cameraToCenterDistance(): number {
|
||||
return this._helper.cameraToCenterDistance;
|
||||
}
|
||||
get constrain(): TransformConstrainFunction {
|
||||
return this._helper.constrain;
|
||||
}
|
||||
public get nearZ(): number {
|
||||
return this._helper.nearZ;
|
||||
}
|
||||
@ -236,11 +243,11 @@ export class MercatorTransform implements ITransform {
|
||||
|
||||
private _coveringTilesDetailsProvider;
|
||||
|
||||
constructor(minZoom?: number, maxZoom?: number, minPitch?: number, maxPitch?: number, renderWorldCopies?: boolean) {
|
||||
constructor(options?: TransformOptions) {
|
||||
this._helper = new TransformHelper({
|
||||
calcMatrices: () => { this._calcMatrices(); },
|
||||
getConstrained: (center, zoom) => { return this.getConstrained(center, zoom); }
|
||||
}, minZoom, maxZoom, minPitch, maxPitch, renderWorldCopies);
|
||||
constrain: (center, zoom) => { return this.defaultConstrain(center, zoom); }
|
||||
}, options);
|
||||
this._coveringTilesDetailsProvider = new MercatorCoveringTilesDetailsProvider();
|
||||
}
|
||||
|
||||
@ -444,7 +451,7 @@ export class MercatorTransform implements ITransform {
|
||||
*
|
||||
* Bounds are those set by maxBounds or North & South "Poles" and, if only 1 globe is displayed, antimeridian.
|
||||
*/
|
||||
getConstrained(lngLat: LngLat, zoom: number): {center: LngLat; zoom: number} {
|
||||
defaultConstrain: TransformConstrainFunction = (lngLat, zoom) => {
|
||||
zoom = clamp(+zoom, this.minZoom, this.maxZoom);
|
||||
const result = {
|
||||
center: new LngLat(lngLat.lng, lngLat.lat),
|
||||
@ -533,7 +540,7 @@ export class MercatorTransform implements ITransform {
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
calculateCenterFromCameraLngLatAlt(lnglat: LngLatLike, alt: number, bearing?: number, pitch?: number): {center: LngLat; elevation: number; zoom: number} {
|
||||
return this._helper.calculateCenterFromCameraLngLatAlt(lnglat, alt, bearing, pitch);
|
||||
|
||||
12
node_modules/maplibre-gl/src/geo/projection/mercator_utils.test.ts
generated
vendored
12
node_modules/maplibre-gl/src/geo/projection/mercator_utils.test.ts
generated
vendored
@ -9,20 +9,20 @@ import {createIdentityMat4f32, MAX_VALID_LATITUDE} from '../../util/util';
|
||||
|
||||
describe('mercator utils', () => {
|
||||
test('projectToWorldCoordinates basic', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.setZoom(10);
|
||||
expect(projectToWorldCoordinates(transform.worldSize, transform.center)).toEqual(new Point(262144, 262144));
|
||||
});
|
||||
|
||||
test('projectToWorldCoordinates clamps latitude', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
|
||||
expect(projectToWorldCoordinates(transform.worldSize, new LngLat(0, -90))).toEqual(projectToWorldCoordinates(transform.worldSize, new LngLat(0, -MAX_VALID_LATITUDE)));
|
||||
expect(projectToWorldCoordinates(transform.worldSize, new LngLat(0, 90))).toEqual(projectToWorldCoordinates(transform.worldSize, new LngLat(0, MAX_VALID_LATITUDE)));
|
||||
});
|
||||
|
||||
test('getMercatorHorizon', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
transform.setPitch(75);
|
||||
const horizon = getMercatorHorizon(transform);
|
||||
@ -31,7 +31,7 @@ describe('mercator utils', () => {
|
||||
});
|
||||
|
||||
test('getMercatorHorizon90', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 180, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 180, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
transform.setPitch(90);
|
||||
const horizon = getMercatorHorizon(transform);
|
||||
@ -40,7 +40,7 @@ describe('mercator utils', () => {
|
||||
});
|
||||
|
||||
test('getMercatorHorizon95', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 180, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 180, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
transform.setPitch(95);
|
||||
const horizon = getMercatorHorizon(transform);
|
||||
@ -49,7 +49,7 @@ describe('mercator utils', () => {
|
||||
});
|
||||
describe('getProjectionData', () => {
|
||||
test('return identity matrix when not passing overscaledTileID', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 180, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 180, renderWorldCopies: true});
|
||||
const projectionData = transform.getProjectionData({overscaledTileID: null});
|
||||
expect(projectionData.fallbackMatrix).toEqual(createIdentityMat4f32());
|
||||
});
|
||||
|
||||
15
node_modules/maplibre-gl/src/geo/projection/projection_factory.ts
generated
vendored
15
node_modules/maplibre-gl/src/geo/projection/projection_factory.ts
generated
vendored
@ -11,19 +11,20 @@ import {VerticalPerspectiveProjection} from './vertical_perspective_projection';
|
||||
|
||||
import type {ProjectionSpecification} from '@maplibre/maplibre-gl-style-spec';
|
||||
import type {Projection} from './projection';
|
||||
import type {ITransform} from '../transform_interface';
|
||||
import type {ITransform, TransformConstrainFunction} from '../transform_interface';
|
||||
import type {ICameraHelper} from './camera_helper';
|
||||
|
||||
export function createProjectionFromName(name: ProjectionSpecification['type']): {
|
||||
export function createProjectionFromName(name: ProjectionSpecification['type'], transformConstrain?: TransformConstrainFunction): {
|
||||
projection: Projection;
|
||||
transform: ITransform;
|
||||
cameraHelper: ICameraHelper;
|
||||
} {
|
||||
const transformOptions = {constrain: transformConstrain};
|
||||
if (Array.isArray(name)) {
|
||||
const globeProjection = new GlobeProjection({type: name});
|
||||
return {
|
||||
projection: globeProjection,
|
||||
transform: new GlobeTransform(),
|
||||
transform: new GlobeTransform(transformOptions),
|
||||
cameraHelper: new GlobeCameraHelper(globeProjection),
|
||||
};
|
||||
}
|
||||
@ -32,7 +33,7 @@ export function createProjectionFromName(name: ProjectionSpecification['type']):
|
||||
{
|
||||
return {
|
||||
projection: new MercatorProjection(),
|
||||
transform: new MercatorTransform(),
|
||||
transform: new MercatorTransform(transformOptions),
|
||||
cameraHelper: new MercatorCameraHelper(),
|
||||
};
|
||||
}
|
||||
@ -49,7 +50,7 @@ export function createProjectionFromName(name: ProjectionSpecification['type']):
|
||||
]});
|
||||
return {
|
||||
projection: globeProjection,
|
||||
transform: new GlobeTransform(),
|
||||
transform: new GlobeTransform(transformOptions),
|
||||
cameraHelper: new GlobeCameraHelper(globeProjection),
|
||||
};
|
||||
}
|
||||
@ -57,7 +58,7 @@ export function createProjectionFromName(name: ProjectionSpecification['type']):
|
||||
{
|
||||
return {
|
||||
projection: new VerticalPerspectiveProjection(),
|
||||
transform: new VerticalPerspectiveTransform(),
|
||||
transform: new VerticalPerspectiveTransform(transformOptions),
|
||||
cameraHelper: new VerticalPerspectiveCameraHelper(),
|
||||
};
|
||||
}
|
||||
@ -66,7 +67,7 @@ export function createProjectionFromName(name: ProjectionSpecification['type']):
|
||||
warnOnce(`Unknown projection name: ${name}. Falling back to mercator projection.`);
|
||||
return {
|
||||
projection: new MercatorProjection(),
|
||||
transform: new MercatorTransform(),
|
||||
transform: new MercatorTransform(transformOptions),
|
||||
cameraHelper: new MercatorCameraHelper(),
|
||||
};
|
||||
}
|
||||
|
||||
8
node_modules/maplibre-gl/src/geo/projection/vertical_perspective_camera_helper.ts
generated
vendored
8
node_modules/maplibre-gl/src/geo/projection/vertical_perspective_camera_helper.ts
generated
vendored
@ -213,7 +213,7 @@ export class VerticalPerspectiveCameraHelper implements ICameraHelper {
|
||||
// Special zoom & center handling for globe:
|
||||
// Globe constrained center isn't dependent on zoom level
|
||||
const startingLat = tr.center.lat;
|
||||
const constrainedCenter = tr.getConstrained(options.center ? LngLat.convert(options.center) : tr.center, tr.zoom).center;
|
||||
const constrainedCenter = tr.constrain(options.center ? LngLat.convert(options.center) : tr.center, tr.zoom).center;
|
||||
tr.setCenter(constrainedCenter.wrap());
|
||||
|
||||
// Make sure to compute correct target zoom level if no zoom is specified
|
||||
@ -245,7 +245,7 @@ export class VerticalPerspectiveCameraHelper implements ICameraHelper {
|
||||
const preConstrainCenter = options.center ?
|
||||
LngLat.convert(options.center) :
|
||||
startCenter;
|
||||
const constrainedCenter = tr.getConstrained(
|
||||
const constrainedCenter = tr.constrain(
|
||||
preConstrainCenter,
|
||||
startZoom // zoom can be whatever at this stage, it should not affect anything if globe is enabled
|
||||
).center;
|
||||
@ -334,7 +334,7 @@ export class VerticalPerspectiveCameraHelper implements ICameraHelper {
|
||||
const doPadding = !tr.isPaddingEqual(options.padding);
|
||||
|
||||
// Obtain target center and zoom
|
||||
const constrainedCenter = tr.getConstrained(
|
||||
const constrainedCenter = tr.constrain(
|
||||
LngLat.convert(options.center || options.locationAtOffset),
|
||||
startZoom
|
||||
).center;
|
||||
@ -369,7 +369,7 @@ export class VerticalPerspectiveCameraHelper implements ICameraHelper {
|
||||
const normalizedOptionsMinZoom = +options.minZoom + getZoomAdjustment(targetCenter.lat, 0);
|
||||
const normalizedMinZoomPreConstrain = Math.min(normalizedOptionsMinZoom, normalizedStartZoom, normalizedTargetZoom);
|
||||
const minZoomPreConstrain = normalizedMinZoomPreConstrain + getZoomAdjustment(0, targetCenter.lat);
|
||||
const minZoom = tr.getConstrained(targetCenter, minZoomPreConstrain).zoom;
|
||||
const minZoom = tr.constrain(targetCenter, minZoomPreConstrain).zoom;
|
||||
const normalizedMinZoom = minZoom + getZoomAdjustment(targetCenter.lat, 0);
|
||||
scaleOfMinZoom = zoomScale(normalizedMinZoom - normalizedStartZoom);
|
||||
}
|
||||
|
||||
12
node_modules/maplibre-gl/src/geo/projection/vertical_perspective_projection.ts
generated
vendored
12
node_modules/maplibre-gl/src/geo/projection/vertical_perspective_projection.ts
generated
vendored
@ -1,7 +1,7 @@
|
||||
import type {Context} from '../../gl/context';
|
||||
import type {CanonicalTileID} from '../../source/tile_id';
|
||||
import {type Mesh} from '../../render/mesh';
|
||||
import {browser} from '../../util/browser';
|
||||
import {now} from '../../util/time_control';
|
||||
import {easeCubicInOut, lerp} from '../../util/util';
|
||||
import {mercatorYfromLat} from '../mercator_coordinate';
|
||||
import {SubdivisionGranularityExpression, SubdivisionGranularitySetting} from '../../render/subdivision_granularity_settings';
|
||||
@ -103,15 +103,15 @@ export class VerticalPerspectiveProjection implements Projection {
|
||||
const expectedResult = 2.0 * Math.atan(Math.exp(Math.PI - (mercatorY * Math.PI * 2.0))) - Math.PI * 0.5;
|
||||
const newValue = this._errorMeasurement.updateErrorLoop(mercatorY, expectedResult);
|
||||
|
||||
const now = browser.now();
|
||||
const currentTime = now();
|
||||
|
||||
if (newValue !== this._errorMeasurementLastValue) {
|
||||
this._errorCorrectionPreviousValue = this._errorCorrectionUsable; // store the interpolated value
|
||||
this._errorMeasurementLastValue = newValue;
|
||||
this._errorMeasurementLastChangeTime = now;
|
||||
this._errorMeasurementLastChangeTime = currentTime;
|
||||
}
|
||||
|
||||
const sinceUpdateSeconds = (now - this._errorMeasurementLastChangeTime) / 1000.0;
|
||||
const sinceUpdateSeconds = (currentTime - this._errorMeasurementLastChangeTime) / 1000.0;
|
||||
const mix = Math.min(Math.max(sinceUpdateSeconds / globeConstants.errorTransitionTimeSeconds, 0.0), 1.0);
|
||||
const newCorrection = -this._errorMeasurementLastValue; // Note the negation
|
||||
this._errorCorrectionUsable = lerp(this._errorCorrectionPreviousValue, newCorrection, easeCubicInOut(mix));
|
||||
@ -152,10 +152,10 @@ export class VerticalPerspectiveProjection implements Projection {
|
||||
}
|
||||
|
||||
hasTransition(): boolean {
|
||||
const now = browser.now();
|
||||
const currentTime = now();
|
||||
let dirty = false;
|
||||
// Error correction transition
|
||||
dirty = dirty || (now - this._errorMeasurementLastChangeTime) / 1000.0 < (globeConstants.errorTransitionTimeSeconds + 0.2);
|
||||
dirty = dirty || (currentTime - this._errorMeasurementLastChangeTime) / 1000.0 < (globeConstants.errorTransitionTimeSeconds + 0.2);
|
||||
// Error correction query in flight
|
||||
dirty = dirty || (this._errorMeasurement && this._errorMeasurement.awaitingQuery);
|
||||
return dirty;
|
||||
|
||||
20
node_modules/maplibre-gl/src/geo/projection/vertical_perspective_transform.ts
generated
vendored
20
node_modules/maplibre-gl/src/geo/projection/vertical_perspective_transform.ts
generated
vendored
@ -13,7 +13,8 @@ import {Frustum} from '../../util/primitives/frustum';
|
||||
|
||||
import type {Terrain} from '../../render/terrain';
|
||||
import type {PointProjection} from '../../symbol/projection';
|
||||
import type {IReadonlyTransform, ITransform} from '../transform_interface';
|
||||
import type {IReadonlyTransform, ITransform, TransformConstrainFunction} from '../transform_interface';
|
||||
import type {TransformOptions} from '../transform_helper';
|
||||
import type {PaddingOptions} from '../edge_insets';
|
||||
import type {ProjectionData, ProjectionDataParams} from './projection_data';
|
||||
import type {CoveringTilesDetailsProvider} from './covering_tiles_details_provider';
|
||||
@ -127,6 +128,9 @@ export class VerticalPerspectiveTransform implements ITransform {
|
||||
setMaxBounds(bounds?: LngLatBounds): void {
|
||||
this._helper.setMaxBounds(bounds);
|
||||
}
|
||||
setConstrain(constrain?: TransformConstrainFunction | null): void {
|
||||
this._helper.setConstrain(constrain);
|
||||
}
|
||||
overrideNearFarZ(nearZ: number, farZ: number): void {
|
||||
this._helper.overrideNearFarZ(nearZ, farZ);
|
||||
}
|
||||
@ -218,6 +222,9 @@ export class VerticalPerspectiveTransform implements ITransform {
|
||||
get renderWorldCopies(): boolean {
|
||||
return this._helper.renderWorldCopies;
|
||||
}
|
||||
get constrain(): TransformConstrainFunction {
|
||||
return this._helper.constrain;
|
||||
}
|
||||
public get nearZ(): number {
|
||||
return this._helper.nearZ;
|
||||
}
|
||||
@ -251,12 +258,11 @@ export class VerticalPerspectiveTransform implements ITransform {
|
||||
|
||||
private _coveringTilesDetailsProvider: GlobeCoveringTilesDetailsProvider;
|
||||
|
||||
public constructor() {
|
||||
|
||||
public constructor(options?: TransformOptions) {
|
||||
this._helper = new TransformHelper({
|
||||
calcMatrices: () => { this._calcMatrices(); },
|
||||
getConstrained: (center, zoom) => { return this.getConstrained(center, zoom); }
|
||||
});
|
||||
constrain: (center, zoom) => { return this.defaultConstrain(center, zoom); }
|
||||
}, options);
|
||||
this._coveringTilesDetailsProvider = new GlobeCoveringTilesDetailsProvider();
|
||||
}
|
||||
|
||||
@ -636,7 +642,7 @@ export class VerticalPerspectiveTransform implements ITransform {
|
||||
return new LngLatBounds(boundsArray);
|
||||
}
|
||||
|
||||
getConstrained(lngLat: LngLat, zoom: number): { center: LngLat; zoom: number } {
|
||||
defaultConstrain: TransformConstrainFunction = (lngLat, zoom) => {
|
||||
// Globe: TODO: respect _lngRange, _latRange
|
||||
// It is possible to implement exact constrain for globe, but I don't think it is worth the effort.
|
||||
const constrainedLat = clamp(lngLat.lat, -MAX_VALID_LATITUDE, MAX_VALID_LATITUDE);
|
||||
@ -648,7 +654,7 @@ export class VerticalPerspectiveTransform implements ITransform {
|
||||
),
|
||||
zoom: constrainedZoom
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
calculateCenterFromCameraLngLatAlt(lngLat: LngLatLike, alt: number, bearing?: number, pitch?: number): {center: LngLat; elevation: number; zoom: number} {
|
||||
return this._helper.calculateCenterFromCameraLngLatAlt(lngLat, alt, bearing, pitch);
|
||||
|
||||
2
node_modules/maplibre-gl/src/geo/transform_helper.test.ts
generated
vendored
2
node_modules/maplibre-gl/src/geo/transform_helper.test.ts
generated
vendored
@ -8,7 +8,7 @@ import {EXTENT} from '../data/extent';
|
||||
|
||||
const emptyCallbacks = {
|
||||
calcMatrices: () => {},
|
||||
getConstrained: (center, zoom) => { return {center, zoom}; },
|
||||
constrain: (center, zoom) => { return {center, zoom}; },
|
||||
};
|
||||
|
||||
describe('TransformHelper', () => {
|
||||
|
||||
87
node_modules/maplibre-gl/src/geo/transform_helper.ts
generated
vendored
87
node_modules/maplibre-gl/src/geo/transform_helper.ts
generated
vendored
@ -9,7 +9,7 @@ import {cameraMercatorCoordinateFromCenterAndRotation} from './projection/mercat
|
||||
import {EXTENT} from '../data/extent';
|
||||
|
||||
import type {PaddingOptions} from './edge_insets';
|
||||
import type {IReadonlyTransform, ITransformGetters} from './transform_interface';
|
||||
import type {IReadonlyTransform, ITransformGetters, TransformConstrainFunction} from './transform_interface';
|
||||
import type {OverscaledTileID} from '../source/tile_id';
|
||||
import {Bounds} from './bounds';
|
||||
/**
|
||||
@ -50,12 +50,12 @@ export type UnwrappedTileIDType = {
|
||||
|
||||
export type TransformHelperCallbacks = {
|
||||
/**
|
||||
* Get center lngLat and zoom to ensure that
|
||||
* The transform's default getter of center lngLat and zoom to ensure that
|
||||
* 1) everything beyond the bounds is excluded
|
||||
* 2) a given lngLat is as near the center as possible
|
||||
* Bounds are those set by maxBounds or North & South "Poles" and, if only 1 globe is displayed, antimeridian.
|
||||
*/
|
||||
getConstrained: (center: LngLat, zoom: number) => { center: LngLat; zoom: number };
|
||||
constrain: TransformConstrainFunction;
|
||||
|
||||
/**
|
||||
* Updates the underlying transform's internal matrices.
|
||||
@ -63,6 +63,33 @@ export type TransformHelperCallbacks = {
|
||||
calcMatrices: () => void;
|
||||
};
|
||||
|
||||
export type TransformOptions = {
|
||||
/**
|
||||
* The minimum zoom level of the map.
|
||||
*/
|
||||
minZoom?: number;
|
||||
/**
|
||||
* The maximum zoom level of the map.
|
||||
*/
|
||||
maxZoom?: number;
|
||||
/**
|
||||
* The minimum pitch of the map.
|
||||
*/
|
||||
minPitch?: number;
|
||||
/**
|
||||
* The maximum pitch of the map.
|
||||
*/
|
||||
maxPitch?: number;
|
||||
/**
|
||||
* Whether to render multiple copies of the world side by side in the map.
|
||||
*/
|
||||
renderWorldCopies?: boolean;
|
||||
/**
|
||||
* An override of the transform's constraining function for respecting its longitude and latitude bounds.
|
||||
*/
|
||||
constrain?: TransformConstrainFunction | null;
|
||||
};
|
||||
|
||||
function getTileZoom(zoom: number): number {
|
||||
return Math.max(0, Math.floor(zoom));
|
||||
}
|
||||
@ -123,16 +150,20 @@ export class TransformHelper implements ITransformGetters {
|
||||
_farZ: number;
|
||||
_autoCalculateNearFarZ: boolean;
|
||||
|
||||
constructor(callbacks: TransformHelperCallbacks, minZoom?: number, maxZoom?: number, minPitch?: number, maxPitch?: number, renderWorldCopies?: boolean) {
|
||||
_constrain: TransformConstrainFunction;
|
||||
|
||||
constructor(callbacks: TransformHelperCallbacks, options?: TransformOptions) {
|
||||
this._callbacks = callbacks;
|
||||
this._tileSize = 512; // constant
|
||||
|
||||
this._renderWorldCopies = renderWorldCopies === undefined ? true : !!renderWorldCopies;
|
||||
this._minZoom = minZoom || 0;
|
||||
this._maxZoom = maxZoom || 22;
|
||||
this._renderWorldCopies = options?.renderWorldCopies === undefined ? true : !!options?.renderWorldCopies;
|
||||
this._minZoom = options?.minZoom || 0;
|
||||
this._maxZoom = options?.maxZoom || 22;
|
||||
|
||||
this._minPitch = (minPitch === undefined || minPitch === null) ? 0 : minPitch;
|
||||
this._maxPitch = (maxPitch === undefined || maxPitch === null) ? 60 : maxPitch;
|
||||
this._minPitch = (options?.minPitch === undefined || options?.minPitch === null) ? 0 : options?.minPitch;
|
||||
this._maxPitch = (options?.maxPitch === undefined || options?.maxPitch === null) ? 60 : options?.maxPitch;
|
||||
|
||||
this._constrain = options?.constrain ?? this._callbacks.constrain;
|
||||
|
||||
this.setMaxBounds();
|
||||
|
||||
@ -180,7 +211,7 @@ export class TransformHelper implements ITransformGetters {
|
||||
this._farZ = thatI.farZ;
|
||||
this._autoCalculateNearFarZ = !forceOverrideZ && thatI.autoCalculateNearFarZ;
|
||||
if (constrain) {
|
||||
this._constrain();
|
||||
this.constrainInternal();
|
||||
}
|
||||
this._calcMatrices();
|
||||
}
|
||||
@ -221,14 +252,14 @@ export class TransformHelper implements ITransformGetters {
|
||||
setMinZoom(zoom: number) {
|
||||
if (this._minZoom === zoom) return;
|
||||
this._minZoom = zoom;
|
||||
this.setZoom(this.getConstrained(this._center, this.zoom).zoom);
|
||||
this.setZoom(this.constrain(this._center, this.zoom).zoom);
|
||||
}
|
||||
|
||||
get maxZoom(): number { return this._maxZoom; }
|
||||
setMaxZoom(zoom: number) {
|
||||
if (this._maxZoom === zoom) return;
|
||||
this._maxZoom = zoom;
|
||||
this.setZoom(this.getConstrained(this._center, this.zoom).zoom);
|
||||
this.setZoom(this.constrain(this._center, this.zoom).zoom);
|
||||
}
|
||||
|
||||
get minPitch(): number { return this._minPitch; }
|
||||
@ -255,6 +286,16 @@ export class TransformHelper implements ITransformGetters {
|
||||
|
||||
this._renderWorldCopies = renderWorldCopies;
|
||||
}
|
||||
|
||||
get constrain(): TransformConstrainFunction { return this._constrain; }
|
||||
setConstrain(constrain?: TransformConstrainFunction | null) {
|
||||
if (!constrain) {
|
||||
constrain = this._callbacks.constrain;
|
||||
}
|
||||
this._constrain = constrain;
|
||||
this.constrainInternal();
|
||||
this._calcMatrices();
|
||||
}
|
||||
|
||||
get worldSize(): number {
|
||||
return this._tileSize * this._scale;
|
||||
@ -332,13 +373,13 @@ export class TransformHelper implements ITransformGetters {
|
||||
|
||||
get zoom(): number { return this._zoom; }
|
||||
setZoom(zoom: number) {
|
||||
const constrainedZoom = this.getConstrained(this._center, zoom).zoom;
|
||||
const constrainedZoom = this.constrain(this._center, zoom).zoom;
|
||||
if (this._zoom === constrainedZoom) return;
|
||||
this._unmodified = false;
|
||||
this._zoom = constrainedZoom;
|
||||
this._tileZoom = Math.max(0, Math.floor(constrainedZoom));
|
||||
this._scale = zoomScale(constrainedZoom);
|
||||
this._constrain();
|
||||
this.constrainInternal();
|
||||
this._calcMatrices();
|
||||
}
|
||||
|
||||
@ -347,7 +388,7 @@ export class TransformHelper implements ITransformGetters {
|
||||
if (center.lat === this._center.lat && center.lng === this._center.lng) return;
|
||||
this._unmodified = false;
|
||||
this._center = center;
|
||||
this._constrain();
|
||||
this.constrainInternal();
|
||||
this._calcMatrices();
|
||||
}
|
||||
|
||||
@ -358,7 +399,7 @@ export class TransformHelper implements ITransformGetters {
|
||||
setElevation(elevation: number) {
|
||||
if (elevation === this._elevation) return;
|
||||
this._elevation = elevation;
|
||||
this._constrain();
|
||||
this.constrainInternal();
|
||||
this._calcMatrices();
|
||||
}
|
||||
|
||||
@ -422,14 +463,14 @@ export class TransformHelper implements ITransformGetters {
|
||||
interpolatePadding(start: PaddingOptions, target: PaddingOptions, t: number): void {
|
||||
this._unmodified = false;
|
||||
this._edgeInsets.interpolate(start, target, t);
|
||||
this._constrain();
|
||||
this.constrainInternal();
|
||||
this._calcMatrices();
|
||||
}
|
||||
|
||||
resize(width: number, height: number, constrain: boolean = true): void {
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
if (constrain) this._constrain();
|
||||
if (constrain) this.constrainInternal();
|
||||
this._calcMatrices();
|
||||
}
|
||||
|
||||
@ -452,17 +493,13 @@ export class TransformHelper implements ITransformGetters {
|
||||
if (bounds) {
|
||||
this._lngRange = [bounds.getWest(), bounds.getEast()];
|
||||
this._latRange = [bounds.getSouth(), bounds.getNorth()];
|
||||
this._constrain();
|
||||
this.constrainInternal();
|
||||
} else {
|
||||
this._lngRange = null;
|
||||
this._latRange = [-MAX_VALID_LATITUDE, MAX_VALID_LATITUDE];
|
||||
}
|
||||
}
|
||||
|
||||
private getConstrained(lngLat: LngLat, zoom: number): {center: LngLat; zoom: number} {
|
||||
return this._callbacks.getConstrained(lngLat, zoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* When the map is pitched, some of the 3D features that intersect a query will not intersect
|
||||
* the query at the surface of the earth. Instead the feature may be closer and only intersect
|
||||
@ -492,11 +529,11 @@ export class TransformHelper implements ITransformGetters {
|
||||
* @internal
|
||||
* Snaps the transform's center, zoom, etc. into the valid range.
|
||||
*/
|
||||
private _constrain(): void {
|
||||
private constrainInternal(): void {
|
||||
if (!this.center || !this._width || !this._height || this._constraining) return;
|
||||
this._constraining = true;
|
||||
const unmodified = this._unmodified;
|
||||
const {center, zoom} = this.getConstrained(this.center, this.zoom);
|
||||
const {center, zoom} = this.constrain(this.center, this.zoom);
|
||||
this.setCenter(center);
|
||||
this.setZoom(zoom);
|
||||
this._unmodified = unmodified;
|
||||
|
||||
27
node_modules/maplibre-gl/src/geo/transform_interface.ts
generated
vendored
27
node_modules/maplibre-gl/src/geo/transform_interface.ts
generated
vendored
@ -11,6 +11,18 @@ import type {ProjectionData, ProjectionDataParams} from './projection/projection
|
||||
import type {CoveringTilesDetailsProvider} from './projection/covering_tiles_details_provider';
|
||||
import type {Frustum} from '../util/primitives/frustum';
|
||||
|
||||
/**
|
||||
* The callback defining how the transform constrains the viewport's lnglat and zoom to respect the longitude and latitude bounds.
|
||||
* @see [Customize the map transform constrain](https://maplibre.org/maplibre-gl-js/docs/examples/customize-the-map-transform-constrain/)
|
||||
*/
|
||||
export type TransformConstrainFunction = (
|
||||
lngLat: LngLat,
|
||||
zoom: number
|
||||
) => {
|
||||
center: LngLat;
|
||||
zoom: number;
|
||||
};
|
||||
|
||||
export interface ITransformGetters {
|
||||
get tileSize(): number;
|
||||
|
||||
@ -83,6 +95,10 @@ export interface ITransformGetters {
|
||||
get nearZ(): number;
|
||||
get farZ(): number;
|
||||
get autoCalculateNearFarZ(): boolean;
|
||||
/**
|
||||
* Get center lngLat and zoom to ensure that longitude and latitude bounds are respected and regions beyond the map bounds are not displayed.
|
||||
*/
|
||||
get constrain(): TransformConstrainFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,6 +210,12 @@ interface ITransformMutators {
|
||||
*/
|
||||
setMaxBounds(bounds?: LngLatBounds | null): void;
|
||||
|
||||
/** Sets or clears the callback overriding the transform's default constrain,
|
||||
* whose responsibility is to respect the longitude and latitude bounds by constraining the viewport's lnglat and zoom.
|
||||
* @param constrain - A {@link TransformConstrainFunction} callback defining how the viewport should respect the bounds.
|
||||
*/
|
||||
setConstrain(constrain?: TransformConstrainFunction | null): void;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Called before rendering to allow the transform implementation
|
||||
@ -340,9 +362,10 @@ export interface IReadonlyTransform extends ITransformGetters {
|
||||
isPointOnMapSurface(p: Point, terrain?: Terrain): boolean;
|
||||
|
||||
/**
|
||||
* Get center lngLat and zoom to ensure that longitude and latitude bounds are respected and regions beyond the map bounds are not displayed.
|
||||
* @internal
|
||||
* The tranform's default callback that ensures that longitude and latitude bounds are respected by the viewport.
|
||||
*/
|
||||
getConstrained(lngLat: LngLat, zoom: number): {center: LngLat; zoom: number};
|
||||
defaultConstrain: TransformConstrainFunction;
|
||||
|
||||
maxPitchScaleFactor(): number;
|
||||
|
||||
|
||||
10
node_modules/maplibre-gl/src/index.ts
generated
vendored
10
node_modules/maplibre-gl/src/index.ts
generated
vendored
@ -18,6 +18,7 @@ import {MercatorCoordinate} from './geo/mercator_coordinate';
|
||||
import {Evented, type ErrorEvent, Event, type Listener} from './util/evented';
|
||||
import {type AddProtocolAction, config} from './util/config';
|
||||
import {rtlMainThreadPluginFactory} from './source/rtl_text_plugin_main_thread';
|
||||
import {setNow, restoreNow, isTimeFrozen} from './util/time_control';
|
||||
import {WorkerPool} from './util/worker_pool';
|
||||
import {prewarm, clearPrewarmedResources} from './util/global_worker_pool';
|
||||
import {AJAXError, type ExpiryData, type GetResourceResponse, type RequestParameters} from './util/ajax';
|
||||
@ -52,6 +53,7 @@ import type {DistributiveKeys, DistributiveOmit, GeoJSONFeature, MapGeoJSONFeatu
|
||||
import type {Handler, HandlerResult} from './ui/handler_manager';
|
||||
import type {Complete, RequireAtLeastOne, Subscription} from './util/util';
|
||||
import type {CalculateTileZoomFunction, CoveringTilesOptions} from './geo/projection/covering_tiles';
|
||||
import type {TransformConstrainFunction} from './geo/transform_interface';
|
||||
import type {StyleImage, StyleImageData, StyleImageInterface, StyleImageMetadata, TextFit} from './style/style_image';
|
||||
import type {StyleLayer} from './style/style_layer';
|
||||
import type {Tile} from './source/tile';
|
||||
@ -70,6 +72,7 @@ import type {GlyphPosition, GlyphPositions} from './render/glyph_atlas';
|
||||
import type {ImageAtlas} from './render/image_atlas';
|
||||
import type {StyleGlyph} from './style/style_glyph';
|
||||
import type {FeatureIndex} from './data/feature_index';
|
||||
import type {DashEntry} from './render/line_atlas';
|
||||
const version = packageJSON.version;
|
||||
|
||||
export type * from '@maplibre/maplibre-gl-style-spec';
|
||||
@ -241,6 +244,7 @@ export {
|
||||
type Handler,
|
||||
type RequireAtLeastOne,
|
||||
type CameraUpdateTransformFunction,
|
||||
type TransformConstrainFunction,
|
||||
type CustomRenderMethod,
|
||||
type CalculateTileZoomFunction,
|
||||
type MapSourceDataType,
|
||||
@ -351,6 +355,7 @@ export {
|
||||
type ErrorEvent,
|
||||
type GeoJSONFeature,
|
||||
type CoveringTilesOptions,
|
||||
type DashEntry,
|
||||
setRTLTextPlugin,
|
||||
getRTLTextPluginStatus,
|
||||
prewarm,
|
||||
@ -366,5 +371,8 @@ export {
|
||||
removeProtocol,
|
||||
addSourceType,
|
||||
importScriptInWorkers,
|
||||
createTileMesh
|
||||
createTileMesh,
|
||||
setNow,
|
||||
restoreNow,
|
||||
isTimeFrozen
|
||||
};
|
||||
|
||||
2
node_modules/maplibre-gl/src/render/draw_custom.test.ts
generated
vendored
2
node_modules/maplibre-gl/src/render/draw_custom.test.ts
generated
vendored
@ -23,7 +23,7 @@ vi.mock('../symbol/projection');
|
||||
describe('drawCustom', () => {
|
||||
test('should return custom render method inputs', () => {
|
||||
// same transform setup as in transform.test.ts 'creates a transform', so matrices of transform should be the same
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(500, 500);
|
||||
transform.setMinPitch(10);
|
||||
transform.setMaxPitch(10);
|
||||
|
||||
202
node_modules/maplibre-gl/src/render/draw_line.ts
generated
vendored
202
node_modules/maplibre-gl/src/render/draw_line.ts
generated
vendored
@ -5,7 +5,8 @@ import {
|
||||
lineUniformValues,
|
||||
linePatternUniformValues,
|
||||
lineSDFUniformValues,
|
||||
lineGradientUniformValues
|
||||
lineGradientUniformValues,
|
||||
lineGradientSDFUniformValues
|
||||
} from './program/line_program';
|
||||
|
||||
import type {Painter, RenderOptions} from './painter';
|
||||
@ -13,9 +14,129 @@ import type {SourceCache} from '../source/source_cache';
|
||||
import type {LineStyleLayer} from '../style/style_layer/line_style_layer';
|
||||
import type {LineBucket} from '../data/bucket/line_bucket';
|
||||
import type {OverscaledTileID} from '../source/tile_id';
|
||||
import type {Tile} from '../source/tile';
|
||||
import type {Context} from '../gl/context';
|
||||
import type {ProgramConfiguration} from '../data/program_configuration';
|
||||
import {clamp, nextPowerOfTwo} from '../util/util';
|
||||
import {renderColorRamp} from '../util/color_ramp';
|
||||
import {EXTENT} from '../data/extent';
|
||||
import type {RGBAImage} from '../util/image';
|
||||
|
||||
type GradientTexture = {
|
||||
texture?: Texture;
|
||||
gradient?: RGBAImage;
|
||||
version?: number;
|
||||
};
|
||||
|
||||
function updateGradientTexture(
|
||||
painter: Painter,
|
||||
sourceCache: SourceCache,
|
||||
context: Context,
|
||||
gl: WebGLRenderingContext,
|
||||
layer: LineStyleLayer,
|
||||
bucket: LineBucket,
|
||||
coord: OverscaledTileID,
|
||||
layerGradient: GradientTexture
|
||||
): Texture {
|
||||
let textureResolution = 256;
|
||||
if (layer.stepInterpolant) {
|
||||
const sourceMaxZoom = sourceCache.getSource().maxzoom;
|
||||
const potentialOverzoom = coord.canonical.z === sourceMaxZoom ?
|
||||
Math.ceil(1 << (painter.transform.maxZoom - coord.canonical.z)) : 1;
|
||||
const lineLength = bucket.maxLineLength / EXTENT;
|
||||
// Logical pixel tile size is 512px, and 1024px right before current zoom + 1
|
||||
const maxTilePixelSize = 1024;
|
||||
// Maximum possible texture coverage heuristic, bound by hardware max texture size
|
||||
const maxTextureCoverage = lineLength * maxTilePixelSize * potentialOverzoom;
|
||||
textureResolution = clamp(nextPowerOfTwo(maxTextureCoverage), 256, context.maxTextureSize);
|
||||
}
|
||||
layerGradient.gradient = renderColorRamp({
|
||||
expression: layer.gradientExpression(),
|
||||
evaluationKey: 'lineProgress',
|
||||
resolution: textureResolution,
|
||||
image: layerGradient.gradient || undefined,
|
||||
clips: bucket.lineClipsArray
|
||||
});
|
||||
if (layerGradient.texture) {
|
||||
layerGradient.texture.update(layerGradient.gradient);
|
||||
} else {
|
||||
layerGradient.texture = new Texture(context, layerGradient.gradient, gl.RGBA);
|
||||
}
|
||||
layerGradient.version = layer.gradientVersion;
|
||||
return layerGradient.texture;
|
||||
}
|
||||
|
||||
function bindImagePatternTextures(
|
||||
context: Context,
|
||||
gl: WebGLRenderingContext,
|
||||
tile: Tile,
|
||||
programConfiguration: ProgramConfiguration,
|
||||
crossfade: ReturnType<LineStyleLayer['getCrossfadeParameters']>
|
||||
) {
|
||||
context.activeTexture.set(gl.TEXTURE0);
|
||||
tile.imageAtlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
|
||||
programConfiguration.updatePaintBuffers(crossfade);
|
||||
}
|
||||
|
||||
function bindDasharrayTextures(
|
||||
painter: Painter,
|
||||
context: Context,
|
||||
gl: WebGLRenderingContext,
|
||||
programConfiguration: ProgramConfiguration,
|
||||
programChanged: boolean,
|
||||
crossfade: ReturnType<LineStyleLayer['getCrossfadeParameters']>
|
||||
) {
|
||||
if (programChanged || painter.lineAtlas.dirty) {
|
||||
context.activeTexture.set(gl.TEXTURE0);
|
||||
painter.lineAtlas.bind(context);
|
||||
}
|
||||
programConfiguration.updatePaintBuffers(crossfade);
|
||||
}
|
||||
|
||||
function bindGradientTextures(
|
||||
painter: Painter,
|
||||
sourceCache: SourceCache,
|
||||
context: Context,
|
||||
gl: WebGLRenderingContext,
|
||||
layer: LineStyleLayer,
|
||||
bucket: LineBucket,
|
||||
coord: OverscaledTileID
|
||||
) {
|
||||
const layerGradient = bucket.gradients[layer.id];
|
||||
let gradientTexture = layerGradient.texture;
|
||||
if (layer.gradientVersion !== layerGradient.version) {
|
||||
gradientTexture = updateGradientTexture(painter, sourceCache, context, gl, layer, bucket, coord, layerGradient);
|
||||
}
|
||||
context.activeTexture.set(gl.TEXTURE0);
|
||||
gradientTexture.bind(layer.stepInterpolant ? gl.NEAREST : gl.LINEAR, gl.CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
function bindGradientAndDashTextures(
|
||||
painter: Painter,
|
||||
sourceCache: SourceCache,
|
||||
context: Context,
|
||||
gl: WebGLRenderingContext,
|
||||
layer: LineStyleLayer,
|
||||
bucket: LineBucket,
|
||||
coord: OverscaledTileID,
|
||||
programConfiguration: ProgramConfiguration,
|
||||
crossfade: ReturnType<LineStyleLayer['getCrossfadeParameters']>
|
||||
) {
|
||||
// Bind gradient texture to TEXTURE0
|
||||
const layerGradient = bucket.gradients[layer.id];
|
||||
let gradientTexture = layerGradient.texture;
|
||||
if (layer.gradientVersion !== layerGradient.version) {
|
||||
gradientTexture = updateGradientTexture(painter, sourceCache, context, gl, layer, bucket, coord, layerGradient);
|
||||
}
|
||||
context.activeTexture.set(gl.TEXTURE0);
|
||||
gradientTexture.bind(layer.stepInterpolant ? gl.NEAREST : gl.LINEAR, gl.CLAMP_TO_EDGE);
|
||||
|
||||
// Bind dash atlas to TEXTURE1
|
||||
context.activeTexture.set(gl.TEXTURE1);
|
||||
painter.lineAtlas.bind(context);
|
||||
|
||||
programConfiguration.updatePaintBuffers(crossfade);
|
||||
}
|
||||
|
||||
export function drawLine(painter: Painter, sourceCache: SourceCache, layer: LineStyleLayer, coords: Array<OverscaledTileID>, renderOptions: RenderOptions) {
|
||||
if (painter.renderPass !== 'translucent') return;
|
||||
@ -28,18 +149,21 @@ export function drawLine(painter: Painter, sourceCache: SourceCache, layer: Line
|
||||
|
||||
const depthMode = painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
|
||||
const colorMode = painter.colorModeForRenderPass();
|
||||
|
||||
const dasharray = layer.paint.get('line-dasharray');
|
||||
|
||||
const dasharrayProperty = layer.paint.get('line-dasharray');
|
||||
const dasharray = dasharrayProperty.constantOr(1 as any);
|
||||
const patternProperty = layer.paint.get('line-pattern');
|
||||
const image = patternProperty.constantOr(1 as any);
|
||||
|
||||
const gradient = layer.paint.get('line-gradient');
|
||||
const crossfade = layer.getCrossfadeParameters();
|
||||
|
||||
const programId =
|
||||
image ? 'linePattern' :
|
||||
dasharray ? 'lineSDF' :
|
||||
gradient ? 'lineGradient' : 'line';
|
||||
let programId: string;
|
||||
if (image) programId = 'linePattern';
|
||||
else if (dasharray && gradient) programId = 'lineGradientSDF';
|
||||
else if (dasharray) programId = 'lineSDF';
|
||||
else if (gradient) programId = 'lineGradient';
|
||||
else programId = 'line';
|
||||
|
||||
const context = painter.context;
|
||||
const gl = context.gl;
|
||||
@ -62,11 +186,19 @@ export function drawLine(painter: Painter, sourceCache: SourceCache, layer: Line
|
||||
const terrainData = painter.style.map.terrain && painter.style.map.terrain.getTerrainData(coord);
|
||||
|
||||
const constantPattern = patternProperty.constantOr(null);
|
||||
const constantDasharray = dasharrayProperty && dasharrayProperty.constantOr(null);
|
||||
|
||||
if (constantPattern && tile.imageAtlas) {
|
||||
const atlas = tile.imageAtlas;
|
||||
const posTo = atlas.patternPositions[constantPattern.to.toString()];
|
||||
const posFrom = atlas.patternPositions[constantPattern.from.toString()];
|
||||
if (posTo && posFrom) programConfiguration.setConstantPatternPositions(posTo, posFrom);
|
||||
|
||||
} else if (constantDasharray) {
|
||||
const round = layer.layout.get('line-cap') === 'round';
|
||||
const dashTo = painter.lineAtlas.getDash(constantDasharray.to, round);
|
||||
const dashFrom = painter.lineAtlas.getDash(constantDasharray.from, round);
|
||||
programConfiguration.setConstantDashPositions(dashTo, dashFrom);
|
||||
}
|
||||
|
||||
const projectionData = transform.getProjectionData({
|
||||
@ -77,51 +209,21 @@ export function drawLine(painter: Painter, sourceCache: SourceCache, layer: Line
|
||||
|
||||
const pixelRatio = transform.getPixelScale();
|
||||
|
||||
const uniformValues = image ? linePatternUniformValues(painter, tile, layer, pixelRatio, crossfade) :
|
||||
dasharray ? lineSDFUniformValues(painter, tile, layer, pixelRatio, dasharray, crossfade) :
|
||||
gradient ? lineGradientUniformValues(painter, tile, layer, pixelRatio, bucket.lineClipsArray.length) :
|
||||
lineUniformValues(painter, tile, layer, pixelRatio);
|
||||
|
||||
let uniformValues;
|
||||
if (image) {
|
||||
context.activeTexture.set(gl.TEXTURE0);
|
||||
tile.imageAtlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
|
||||
programConfiguration.updatePaintBuffers(crossfade);
|
||||
} else if (dasharray && (programChanged || painter.lineAtlas.dirty)) {
|
||||
context.activeTexture.set(gl.TEXTURE0);
|
||||
painter.lineAtlas.bind(context);
|
||||
uniformValues = linePatternUniformValues(painter, tile, layer, pixelRatio, crossfade);
|
||||
bindImagePatternTextures(context, gl, tile, programConfiguration, crossfade);
|
||||
} else if (dasharray && gradient) {
|
||||
uniformValues = lineGradientSDFUniformValues(painter, tile, layer, pixelRatio, crossfade, bucket.lineClipsArray.length);
|
||||
bindGradientAndDashTextures(painter, sourceCache, context, gl, layer, bucket, coord, programConfiguration, crossfade);
|
||||
} else if (dasharray) {
|
||||
uniformValues = lineSDFUniformValues(painter, tile, layer, pixelRatio, crossfade);
|
||||
bindDasharrayTextures(painter, context, gl, programConfiguration, programChanged, crossfade);
|
||||
} else if (gradient) {
|
||||
const layerGradient = bucket.gradients[layer.id];
|
||||
let gradientTexture = layerGradient.texture;
|
||||
if (layer.gradientVersion !== layerGradient.version) {
|
||||
let textureResolution = 256;
|
||||
if (layer.stepInterpolant) {
|
||||
const sourceMaxZoom = sourceCache.getSource().maxzoom;
|
||||
const potentialOverzoom = coord.canonical.z === sourceMaxZoom ?
|
||||
Math.ceil(1 << (painter.transform.maxZoom - coord.canonical.z)) : 1;
|
||||
const lineLength = bucket.maxLineLength / EXTENT;
|
||||
// Logical pixel tile size is 512px, and 1024px right before current zoom + 1
|
||||
const maxTilePixelSize = 1024;
|
||||
// Maximum possible texture coverage heuristic, bound by hardware max texture size
|
||||
const maxTextureCoverage = lineLength * maxTilePixelSize * potentialOverzoom;
|
||||
textureResolution = clamp(nextPowerOfTwo(maxTextureCoverage), 256, context.maxTextureSize);
|
||||
}
|
||||
layerGradient.gradient = renderColorRamp({
|
||||
expression: layer.gradientExpression(),
|
||||
evaluationKey: 'lineProgress',
|
||||
resolution: textureResolution,
|
||||
image: layerGradient.gradient || undefined,
|
||||
clips: bucket.lineClipsArray
|
||||
});
|
||||
if (layerGradient.texture) {
|
||||
layerGradient.texture.update(layerGradient.gradient);
|
||||
} else {
|
||||
layerGradient.texture = new Texture(context, layerGradient.gradient, gl.RGBA);
|
||||
}
|
||||
layerGradient.version = layer.gradientVersion;
|
||||
gradientTexture = layerGradient.texture;
|
||||
}
|
||||
context.activeTexture.set(gl.TEXTURE0);
|
||||
gradientTexture.bind(layer.stepInterpolant ? gl.NEAREST : gl.LINEAR, gl.CLAMP_TO_EDGE);
|
||||
uniformValues = lineGradientUniformValues(painter, tile, layer, pixelRatio, bucket.lineClipsArray.length);
|
||||
bindGradientTextures(painter, sourceCache, context, gl, layer, bucket, coord);
|
||||
} else {
|
||||
uniformValues = lineUniformValues(painter, tile, layer, pixelRatio);
|
||||
}
|
||||
|
||||
const stencil = painter.stencilModeForClipping(coord);
|
||||
|
||||
156
node_modules/maplibre-gl/src/render/draw_raster.ts
generated
vendored
156
node_modules/maplibre-gl/src/render/draw_raster.ts
generated
vendored
@ -1,22 +1,33 @@
|
||||
import {clamp} from '../util/util';
|
||||
|
||||
import {ImageSource} from '../source/image_source';
|
||||
import {browser} from '../util/browser';
|
||||
import {now} from '../util/time_control';
|
||||
import {StencilMode} from '../gl/stencil_mode';
|
||||
import {DepthMode} from '../gl/depth_mode';
|
||||
import {CullFaceMode} from '../gl/cull_face_mode';
|
||||
import {rasterUniformValues} from './program/raster_program';
|
||||
import {EXTENT} from '../data/extent';
|
||||
import {coveringZoomLevel} from '../geo/projection/covering_tiles';
|
||||
import {FadingDirections} from '../source/tile';
|
||||
import Point from '@mapbox/point-geometry';
|
||||
|
||||
import type {Painter, RenderOptions} from './painter';
|
||||
import type {SourceCache} from '../source/source_cache';
|
||||
import type {RasterStyleLayer} from '../style/style_layer/raster_style_layer';
|
||||
import type {OverscaledTileID} from '../source/tile_id';
|
||||
import type {IReadonlyTransform} from '../geo/transform_interface';
|
||||
import type {Tile} from '../source/tile';
|
||||
import type {Terrain} from './terrain';
|
||||
|
||||
type FadeProperties = {
|
||||
parentTile: Tile;
|
||||
parentScaleBy: number;
|
||||
parentTopLeft: [number, number];
|
||||
fadeValues: FadeValues;
|
||||
};
|
||||
|
||||
type FadeValues = {
|
||||
tileOpacity: number;
|
||||
parentTileOpacity?: number;
|
||||
fadeMix: {opacity: number; mix: number};
|
||||
};
|
||||
|
||||
const cornerCoords = [
|
||||
new Point(0, 0),
|
||||
@ -83,37 +94,32 @@ function drawTiles(
|
||||
|
||||
const colorMode = painter.colorModeForRenderPass();
|
||||
const align = !painter.options.moving;
|
||||
const rasterOpacity = layer.paint.get('raster-opacity');
|
||||
const rasterResampling = layer.paint.get('raster-resampling');
|
||||
const fadeDuration = layer.paint.get('raster-fade-duration');
|
||||
const isTerrain = !!painter.style.map.terrain;
|
||||
|
||||
// Draw all tiles
|
||||
for (const coord of coords) {
|
||||
// Set the lower zoom level to sublayer 0, and higher zoom levels to higher sublayers
|
||||
// Use gl.LESS to prevent double drawing in areas where tiles overlap.
|
||||
const depthMode = painter.getDepthModeForSublayer(coord.overscaledZ - minTileZ,
|
||||
layer.paint.get('raster-opacity') === 1 ? DepthMode.ReadWrite : DepthMode.ReadOnly, gl.LESS);
|
||||
rasterOpacity === 1 ? DepthMode.ReadWrite : DepthMode.ReadOnly, gl.LESS);
|
||||
|
||||
const tile = sourceCache.getTile(coord);
|
||||
const textureFilter = rasterResampling === 'nearest' ? gl.NEAREST : gl.LINEAR;
|
||||
|
||||
tile.registerFadeDuration(layer.paint.get('raster-fade-duration'));
|
||||
|
||||
const parentTile = sourceCache.findLoadedParent(coord, 0);
|
||||
const siblingTile = sourceCache.findLoadedSibling(coord);
|
||||
// Prefer parent tile if present
|
||||
const fadeTileReference = parentTile || siblingTile || null;
|
||||
const fade = getFadeValues(tile, fadeTileReference, sourceCache, layer, painter.transform, painter.style.map.terrain);
|
||||
|
||||
let parentScaleBy, parentTL;
|
||||
|
||||
const textureFilter = layer.paint.get('raster-resampling') === 'nearest' ? gl.NEAREST : gl.LINEAR;
|
||||
|
||||
// create and bind first texture
|
||||
context.activeTexture.set(gl.TEXTURE0);
|
||||
tile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
|
||||
|
||||
// create second texture - use either the current tile or fade tile to bind second texture below
|
||||
context.activeTexture.set(gl.TEXTURE1);
|
||||
|
||||
const {parentTile, parentScaleBy, parentTopLeft, fadeValues} = getFadeProperties(tile, sourceCache, fadeDuration, isTerrain);
|
||||
tile.fadeOpacity = fadeValues.tileOpacity;
|
||||
if (parentTile) {
|
||||
parentTile.fadeOpacity = fadeValues.parentTileOpacity;
|
||||
parentTile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
|
||||
parentScaleBy = Math.pow(2, parentTile.tileID.overscaledZ - tile.tileID.overscaledZ);
|
||||
parentTL = [tile.tileID.canonical.x * parentScaleBy % 1, tile.tileID.canonical.y * parentScaleBy % 1];
|
||||
} else {
|
||||
tile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
|
||||
}
|
||||
@ -127,10 +133,9 @@ function drawTiles(
|
||||
|
||||
const terrainData = painter.style.map.terrain && painter.style.map.terrain.getTerrainData(coord);
|
||||
const projectionData = transform.getProjectionData({overscaledTileID: coord, aligned: align, applyGlobeMatrix: !isRenderingToTexture, applyTerrainMatrix: true});
|
||||
const uniformValues = rasterUniformValues(parentTL || [0, 0], parentScaleBy || 1, fade, layer, corners);
|
||||
const uniformValues = rasterUniformValues(parentTopLeft, parentScaleBy, fadeValues.fadeMix, layer, corners);
|
||||
|
||||
const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder, allowPoles, 'raster');
|
||||
|
||||
const stencilMode = stencilModes ? stencilModes[coord.overscaledZ] : StencilMode.disabled;
|
||||
|
||||
program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, flipCullfaceMode ? CullFaceMode.frontCCW : CullFaceMode.backCCW,
|
||||
@ -139,46 +144,79 @@ function drawTiles(
|
||||
}
|
||||
}
|
||||
|
||||
function getFadeValues(tile: Tile, parentTile: Tile, sourceCache: SourceCache, layer: RasterStyleLayer, transform: IReadonlyTransform, terrain: Terrain) {
|
||||
const fadeDuration = layer.paint.get('raster-fade-duration');
|
||||
/**
|
||||
* Get fade properties for current tile - either cross-fading or self-fading properties.
|
||||
*/
|
||||
function getFadeProperties(tile: Tile, sourceCache: SourceCache, fadeDuration: number, isTerrain: boolean): FadeProperties {
|
||||
const defaults: FadeProperties = {
|
||||
parentTile: null,
|
||||
parentScaleBy: 1,
|
||||
parentTopLeft: [0, 0],
|
||||
fadeValues: {tileOpacity: 1, parentTileOpacity: 1, fadeMix: {opacity: 1, mix: 0}}
|
||||
};
|
||||
|
||||
if (!terrain && fadeDuration > 0) {
|
||||
const now = browser.now();
|
||||
const sinceTile = (now - tile.timeAdded) / fadeDuration;
|
||||
const sinceParent = parentTile ? (now - parentTile.timeAdded) / fadeDuration : -1;
|
||||
if (fadeDuration === 0 || isTerrain) return defaults;
|
||||
|
||||
const source = sourceCache.getSource();
|
||||
const idealZ = coveringZoomLevel(transform, {
|
||||
tileSize: source.tileSize,
|
||||
roundZoom: source.roundZoom
|
||||
});
|
||||
// cross-fade with parent first if available
|
||||
if (tile.fadingParentID) {
|
||||
const parentTile = sourceCache._getLoadedTile(tile.fadingParentID);
|
||||
if (!parentTile) return defaults;
|
||||
|
||||
// if no parent or parent is older, fade in; if parent is younger, fade out
|
||||
const fadeIn = !parentTile || Math.abs(parentTile.tileID.overscaledZ - idealZ) > Math.abs(tile.tileID.overscaledZ - idealZ);
|
||||
const parentScaleBy = Math.pow(2, parentTile.tileID.overscaledZ - tile.tileID.overscaledZ);
|
||||
const parentTopLeft: [number, number] = [
|
||||
(tile.tileID.canonical.x * parentScaleBy) % 1,
|
||||
(tile.tileID.canonical.y * parentScaleBy) % 1
|
||||
];
|
||||
|
||||
const childOpacity = (fadeIn && tile.refreshedUponExpiration) ? 1 : clamp(fadeIn ? sinceTile : 1 - sinceParent, 0, 1);
|
||||
|
||||
// we don't crossfade tiles that were just refreshed upon expiring:
|
||||
// once they're old enough to pass the crossfading threshold
|
||||
// (fadeDuration), unset the `refreshedUponExpiration` flag so we don't
|
||||
// incorrectly fail to crossfade them when zooming
|
||||
if (tile.refreshedUponExpiration && sinceTile >= 1) tile.refreshedUponExpiration = false;
|
||||
|
||||
if (parentTile) {
|
||||
return {
|
||||
opacity: 1,
|
||||
mix: 1 - childOpacity
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
opacity: childOpacity,
|
||||
mix: 0
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
opacity: 1,
|
||||
mix: 0
|
||||
};
|
||||
const fadeValues = getCrossFadeValues(tile, parentTile, fadeDuration);
|
||||
return {parentTile, parentScaleBy, parentTopLeft, fadeValues};
|
||||
}
|
||||
|
||||
// self-fade for edge tiles
|
||||
if (tile.selfFading) {
|
||||
const fadeValues = getSelfFadeValues(tile, fadeDuration);
|
||||
return {parentTile: null, parentScaleBy: 1, parentTopLeft: [0, 0], fadeValues};
|
||||
}
|
||||
|
||||
return defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cross-fade values for a base tile with a parent tile (for zooming in/out)
|
||||
*/
|
||||
function getCrossFadeValues(tile: Tile, parentTile: Tile, fadeDuration: number): FadeValues {
|
||||
const currentTime = now();
|
||||
|
||||
const timeSinceTile = (currentTime - tile.timeAdded) / fadeDuration;
|
||||
const timeSinceParent = (currentTime - parentTile.timeAdded) / fadeDuration;
|
||||
|
||||
// get fading opacity based on current fade direction
|
||||
const doFadeIn = (tile.fadingDirection === FadingDirections.Incoming);
|
||||
const opacity1 = clamp(timeSinceTile, 0, 1);
|
||||
const opacity2 = clamp(1 - timeSinceParent, 0, 1);
|
||||
|
||||
const tileOpacity = doFadeIn ? opacity1 : opacity2;
|
||||
const parentTileOpacity = doFadeIn ? opacity2 : opacity1;
|
||||
const fadeMix = {
|
||||
opacity: 1,
|
||||
mix: 1 - tileOpacity
|
||||
};
|
||||
|
||||
return {tileOpacity, parentTileOpacity, fadeMix};
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple fade-in values for tile without a parent (i.e. edge tiles)
|
||||
*/
|
||||
function getSelfFadeValues(tile: Tile, fadeDuration: number): FadeValues {
|
||||
const currentTime = now();
|
||||
|
||||
const timeSinceTile = (currentTime - tile.timeAdded) / fadeDuration;
|
||||
const tileOpacity = clamp(timeSinceTile, 0, 1);
|
||||
const fadeMix = {
|
||||
opacity: tileOpacity,
|
||||
mix: 0
|
||||
};
|
||||
|
||||
return {tileOpacity, fadeMix};
|
||||
}
|
||||
|
||||
6
node_modules/maplibre-gl/src/render/line_atlas.ts
generated
vendored
6
node_modules/maplibre-gl/src/render/line_atlas.ts
generated
vendored
@ -5,7 +5,7 @@ import type {Context} from '../gl/context';
|
||||
/**
|
||||
* A dash entry
|
||||
*/
|
||||
type DashEntry = {
|
||||
export type DashEntry = {
|
||||
y: number;
|
||||
height: number;
|
||||
width: number;
|
||||
@ -180,8 +180,8 @@ export class LineAtlas {
|
||||
}
|
||||
|
||||
const dashEntry = {
|
||||
y: (this.nextRow + n + 0.5) / this.height,
|
||||
height: 2 * n / this.height,
|
||||
y: this.nextRow + n,
|
||||
height: 2 * n,
|
||||
width: length
|
||||
};
|
||||
|
||||
|
||||
2
node_modules/maplibre-gl/src/render/painter.test.ts
generated
vendored
2
node_modules/maplibre-gl/src/render/painter.test.ts
generated
vendored
@ -8,7 +8,7 @@ const getStubMap = () => new StubMap() as any;
|
||||
|
||||
test('Render must not fail with incompletely loaded style', () => {
|
||||
const gl = document.createElement('canvas').getContext('webgl');
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
const painter = new Painter(gl, transform);
|
||||
const map = getStubMap();
|
||||
const style = new Style(map);
|
||||
|
||||
4
node_modules/maplibre-gl/src/render/painter.ts
generated
vendored
4
node_modules/maplibre-gl/src/render/painter.ts
generated
vendored
@ -1,4 +1,4 @@
|
||||
import {browser} from '../util/browser';
|
||||
import {now} from '../util/time_control';
|
||||
import {mat4} from 'gl-matrix';
|
||||
import {SourceCache} from '../source/source_cache';
|
||||
import {EXTENT} from '../data/extent';
|
||||
@ -484,7 +484,7 @@ export class Painter {
|
||||
this.imageManager = style.imageManager;
|
||||
this.glyphManager = style.glyphManager;
|
||||
|
||||
this.symbolFadeChange = style.placement.symbolFadeChange(browser.now());
|
||||
this.symbolFadeChange = style.placement.symbolFadeChange(now());
|
||||
|
||||
this.imageManager.beginFrame();
|
||||
|
||||
|
||||
101
node_modules/maplibre-gl/src/render/program/line_program.ts
generated
vendored
101
node_modules/maplibre-gl/src/render/program/line_program.ts
generated
vendored
@ -6,7 +6,6 @@ import type {Context} from '../../gl/context';
|
||||
import type {UniformValues, UniformLocations} from '../uniform_binding';
|
||||
import type {IReadonlyTransform} from '../../geo/transform_interface';
|
||||
import type {Tile} from '../../source/tile';
|
||||
import type {CrossFaded} from '../../style/properties';
|
||||
import type {LineStyleLayer} from '../../style/style_layer/line_style_layer';
|
||||
import type {Painter} from '../painter';
|
||||
import type {CrossfadeParameters} from '../../style/evaluation_parameters';
|
||||
@ -43,13 +42,29 @@ export type LineSDFUniformsType = {
|
||||
'u_ratio': Uniform1f;
|
||||
'u_device_pixel_ratio': Uniform1f;
|
||||
'u_units_to_pixels': Uniform2f;
|
||||
'u_patternscale_a': Uniform2f;
|
||||
'u_patternscale_b': Uniform2f;
|
||||
'u_sdfgamma': Uniform1f;
|
||||
'u_tileratio': Uniform1f;
|
||||
'u_crossfade_from': Uniform1f;
|
||||
'u_crossfade_to': Uniform1f;
|
||||
'u_image': Uniform1i;
|
||||
'u_tex_y_a': Uniform1f;
|
||||
'u_tex_y_b': Uniform1f;
|
||||
'u_mix': Uniform1f;
|
||||
'u_lineatlas_width': Uniform1f;
|
||||
'u_lineatlas_height': Uniform1f;
|
||||
};
|
||||
|
||||
export type LineGradientSDFUniformsType = {
|
||||
'u_translation': Uniform2f;
|
||||
'u_ratio': Uniform1f;
|
||||
'u_device_pixel_ratio': Uniform1f;
|
||||
'u_units_to_pixels': Uniform2f;
|
||||
'u_image': Uniform1i;
|
||||
'u_image_height': Uniform1f;
|
||||
'u_tileratio': Uniform1f;
|
||||
'u_crossfade_from': Uniform1f;
|
||||
'u_crossfade_to': Uniform1f;
|
||||
'u_image_dash': Uniform1i;
|
||||
'u_mix': Uniform1f;
|
||||
'u_lineatlas_width': Uniform1f;
|
||||
'u_lineatlas_height': Uniform1f;
|
||||
};
|
||||
|
||||
const lineUniforms = (context: Context, locations: UniformLocations): LineUniformsType => ({
|
||||
@ -84,13 +99,29 @@ const lineSDFUniforms = (context: Context, locations: UniformLocations): LineSDF
|
||||
'u_ratio': new Uniform1f(context, locations.u_ratio),
|
||||
'u_device_pixel_ratio': new Uniform1f(context, locations.u_device_pixel_ratio),
|
||||
'u_units_to_pixels': new Uniform2f(context, locations.u_units_to_pixels),
|
||||
'u_patternscale_a': new Uniform2f(context, locations.u_patternscale_a),
|
||||
'u_patternscale_b': new Uniform2f(context, locations.u_patternscale_b),
|
||||
'u_sdfgamma': new Uniform1f(context, locations.u_sdfgamma),
|
||||
'u_image': new Uniform1i(context, locations.u_image),
|
||||
'u_tex_y_a': new Uniform1f(context, locations.u_tex_y_a),
|
||||
'u_tex_y_b': new Uniform1f(context, locations.u_tex_y_b),
|
||||
'u_mix': new Uniform1f(context, locations.u_mix)
|
||||
'u_mix': new Uniform1f(context, locations.u_mix),
|
||||
'u_tileratio': new Uniform1f(context, locations.u_tileratio),
|
||||
'u_crossfade_from': new Uniform1f(context, locations.u_crossfade_from),
|
||||
'u_crossfade_to': new Uniform1f(context, locations.u_crossfade_to),
|
||||
'u_lineatlas_width': new Uniform1f(context, locations.u_lineatlas_width),
|
||||
'u_lineatlas_height': new Uniform1f(context, locations.u_lineatlas_height)
|
||||
});
|
||||
|
||||
const lineGradientSDFUniforms = (context: Context, locations: UniformLocations): LineGradientSDFUniformsType => ({
|
||||
'u_translation': new Uniform2f(context, locations.u_translation),
|
||||
'u_ratio': new Uniform1f(context, locations.u_ratio),
|
||||
'u_device_pixel_ratio': new Uniform1f(context, locations.u_device_pixel_ratio),
|
||||
'u_units_to_pixels': new Uniform2f(context, locations.u_units_to_pixels),
|
||||
'u_image': new Uniform1i(context, locations.u_image),
|
||||
'u_image_height': new Uniform1f(context, locations.u_image_height),
|
||||
'u_tileratio': new Uniform1f(context, locations.u_tileratio),
|
||||
'u_crossfade_from': new Uniform1f(context, locations.u_crossfade_from),
|
||||
'u_crossfade_to': new Uniform1f(context, locations.u_crossfade_to),
|
||||
'u_image_dash': new Uniform1i(context, locations.u_image_dash),
|
||||
'u_mix': new Uniform1f(context, locations.u_mix),
|
||||
'u_lineatlas_width': new Uniform1f(context, locations.u_lineatlas_width),
|
||||
'u_lineatlas_height': new Uniform1f(context, locations.u_lineatlas_height)
|
||||
});
|
||||
|
||||
const lineUniformValues = (
|
||||
@ -155,29 +186,43 @@ const lineSDFUniformValues = (
|
||||
tile: Tile,
|
||||
layer: LineStyleLayer,
|
||||
ratioScale: number,
|
||||
dasharray: CrossFaded<Array<number>>,
|
||||
crossfade: CrossfadeParameters,
|
||||
): UniformValues<LineSDFUniformsType> => {
|
||||
const transform = painter.transform;
|
||||
const lineAtlas = painter.lineAtlas;
|
||||
const tileRatio = calculateTileRatio(tile, transform);
|
||||
|
||||
const round = layer.layout.get('line-cap') === 'round';
|
||||
return extend(lineUniformValues(painter, tile, layer, ratioScale), {
|
||||
'u_tileratio': tileRatio,
|
||||
'u_crossfade_from': crossfade.fromScale,
|
||||
'u_crossfade_to': crossfade.toScale,
|
||||
'u_image': 0,
|
||||
'u_mix': crossfade.t,
|
||||
'u_lineatlas_width': painter.lineAtlas.width,
|
||||
'u_lineatlas_height': painter.lineAtlas.height,
|
||||
});
|
||||
};
|
||||
|
||||
const posA = lineAtlas.getDash(dasharray.from, round);
|
||||
const posB = lineAtlas.getDash(dasharray.to, round);
|
||||
|
||||
const widthA = posA.width * crossfade.fromScale;
|
||||
const widthB = posB.width * crossfade.toScale;
|
||||
const lineGradientSDFUniformValues = (
|
||||
painter: Painter,
|
||||
tile: Tile,
|
||||
layer: LineStyleLayer,
|
||||
ratioScale: number,
|
||||
crossfade: CrossfadeParameters,
|
||||
imageHeight: number,
|
||||
): UniformValues<LineGradientSDFUniformsType> => {
|
||||
const transform = painter.transform;
|
||||
const tileRatio = calculateTileRatio(tile, transform);
|
||||
|
||||
return extend(lineUniformValues(painter, tile, layer, ratioScale), {
|
||||
'u_patternscale_a': [tileRatio / widthA, -posA.height / 2],
|
||||
'u_patternscale_b': [tileRatio / widthB, -posB.height / 2],
|
||||
'u_sdfgamma': lineAtlas.width / (Math.min(widthA, widthB) * 256 * painter.pixelRatio) / 2,
|
||||
'u_image': 0,
|
||||
'u_tex_y_a': posA.y,
|
||||
'u_tex_y_b': posB.y,
|
||||
'u_mix': crossfade.t
|
||||
'u_image_height': imageHeight,
|
||||
'u_tileratio': tileRatio,
|
||||
'u_crossfade_from': crossfade.fromScale,
|
||||
'u_crossfade_to': crossfade.toScale,
|
||||
'u_image_dash': 1,
|
||||
'u_mix': crossfade.t,
|
||||
'u_lineatlas_width': painter.lineAtlas.width,
|
||||
'u_lineatlas_height': painter.lineAtlas.height,
|
||||
});
|
||||
};
|
||||
|
||||
@ -200,8 +245,10 @@ export {
|
||||
lineGradientUniforms,
|
||||
linePatternUniforms,
|
||||
lineSDFUniforms,
|
||||
lineGradientSDFUniforms,
|
||||
lineUniformValues,
|
||||
lineGradientUniformValues,
|
||||
linePatternUniformValues,
|
||||
lineSDFUniformValues
|
||||
lineSDFUniformValues,
|
||||
lineGradientSDFUniformValues
|
||||
};
|
||||
|
||||
3
node_modules/maplibre-gl/src/render/program/program_uniforms.ts
generated
vendored
3
node_modules/maplibre-gl/src/render/program/program_uniforms.ts
generated
vendored
@ -6,7 +6,7 @@ import {debugUniforms} from './debug_program';
|
||||
import {heatmapUniforms, heatmapTextureUniforms} from './heatmap_program';
|
||||
import {hillshadeUniforms, hillshadePrepareUniforms} from './hillshade_program';
|
||||
import {colorReliefUniforms} from './color_relief_program';
|
||||
import {lineUniforms, lineGradientUniforms, linePatternUniforms, lineSDFUniforms} from './line_program';
|
||||
import {lineUniforms, lineGradientUniforms, linePatternUniforms, lineSDFUniforms, lineGradientSDFUniforms} from './line_program';
|
||||
import {rasterUniforms} from './raster_program';
|
||||
import {symbolIconUniforms, symbolSDFUniforms, symbolTextAndIconUniforms} from './symbol_program';
|
||||
import {backgroundUniforms, backgroundPatternUniforms} from './background_program';
|
||||
@ -39,6 +39,7 @@ export const programUniforms = {
|
||||
lineGradient: lineGradientUniforms,
|
||||
linePattern: linePatternUniforms,
|
||||
lineSDF: lineSDFUniforms,
|
||||
lineGradientSDF: lineGradientSDFUniforms,
|
||||
raster: rasterUniforms,
|
||||
symbolIcon: symbolIconUniforms,
|
||||
symbolSDF: symbolSDFUniforms,
|
||||
|
||||
9
node_modules/maplibre-gl/src/shaders/line_sdf.fragment.glsl
generated
vendored
9
node_modules/maplibre-gl/src/shaders/line_sdf.fragment.glsl
generated
vendored
@ -1,7 +1,7 @@
|
||||
|
||||
uniform lowp float u_device_pixel_ratio;
|
||||
uniform lowp float u_lineatlas_width;
|
||||
uniform sampler2D u_image;
|
||||
uniform float u_sdfgamma;
|
||||
uniform float u_mix;
|
||||
|
||||
in vec2 v_normal;
|
||||
@ -18,6 +18,8 @@ in float v_depth;
|
||||
#pragma mapbox: define lowp float opacity
|
||||
#pragma mapbox: define mediump float width
|
||||
#pragma mapbox: define lowp float floorwidth
|
||||
#pragma mapbox: define mediump vec4 dasharray_from
|
||||
#pragma mapbox: define mediump vec4 dasharray_to
|
||||
|
||||
void main() {
|
||||
#pragma mapbox: initialize highp vec4 color
|
||||
@ -25,6 +27,8 @@ void main() {
|
||||
#pragma mapbox: initialize lowp float opacity
|
||||
#pragma mapbox: initialize mediump float width
|
||||
#pragma mapbox: initialize lowp float floorwidth
|
||||
#pragma mapbox: initialize mediump vec4 dasharray_from
|
||||
#pragma mapbox: initialize mediump vec4 dasharray_to
|
||||
|
||||
// Calculate the distance of the pixel from the line in pixels.
|
||||
float dist = length(v_normal) * v_width2.s;
|
||||
@ -38,7 +42,8 @@ void main() {
|
||||
float sdfdist_a = texture(u_image, v_tex_a).a;
|
||||
float sdfdist_b = texture(u_image, v_tex_b).a;
|
||||
float sdfdist = mix(sdfdist_a, sdfdist_b, u_mix);
|
||||
alpha *= smoothstep(0.5 - u_sdfgamma / floorwidth, 0.5 + u_sdfgamma / floorwidth, sdfdist);
|
||||
float sdfgamma = (u_lineatlas_width / 256.0 / u_device_pixel_ratio) / min(dasharray_from.w, dasharray_to.w);
|
||||
alpha *= smoothstep(0.5 - sdfgamma / floorwidth, 0.5 + sdfgamma / floorwidth, sdfdist);
|
||||
|
||||
fragColor = color * (alpha * opacity);
|
||||
|
||||
|
||||
2
node_modules/maplibre-gl/src/shaders/line_sdf.fragment.glsl.g.ts
generated
vendored
2
node_modules/maplibre-gl/src/shaders/line_sdf.fragment.glsl.g.ts
generated
vendored
@ -1,2 +1,2 @@
|
||||
// This file is generated. Edit build/generate-shaders.ts, then run `npm run codegen`.
|
||||
export default 'uniform lowp float u_device_pixel_ratio;uniform sampler2D u_image;uniform float u_sdfgamma;uniform float u_mix;in vec2 v_normal;in vec2 v_width2;in vec2 v_tex_a;in vec2 v_tex_b;in float v_gamma_scale;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);float sdfdist_a=texture(u_image,v_tex_a).a;float sdfdist_b=texture(u_image,v_tex_b).a;float sdfdist=mix(sdfdist_a,sdfdist_b,u_mix);alpha*=smoothstep(0.5-u_sdfgamma/floorwidth,0.5+u_sdfgamma/floorwidth,sdfdist);fragColor=color*(alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}';
|
||||
export default 'uniform lowp float u_device_pixel_ratio;uniform lowp float u_lineatlas_width;uniform sampler2D u_image;uniform float u_mix;in vec2 v_normal;in vec2 v_width2;in vec2 v_tex_a;in vec2 v_tex_b;in float v_gamma_scale;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n#pragma mapbox: define mediump vec4 dasharray_from\n#pragma mapbox: define mediump vec4 dasharray_to\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\n#pragma mapbox: initialize mediump vec4 dasharray_from\n#pragma mapbox: initialize mediump vec4 dasharray_to\nfloat dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);float sdfdist_a=texture(u_image,v_tex_a).a;float sdfdist_b=texture(u_image,v_tex_b).a;float sdfdist=mix(sdfdist_a,sdfdist_b,u_mix);float sdfgamma=(u_lineatlas_width/256.0/u_device_pixel_ratio)/min(dasharray_from.w,dasharray_to.w);alpha*=smoothstep(0.5-sdfgamma/floorwidth,0.5+sdfgamma/floorwidth,sdfdist);fragColor=color*(alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}';
|
||||
|
||||
21
node_modules/maplibre-gl/src/shaders/line_sdf.vertex.glsl
generated
vendored
21
node_modules/maplibre-gl/src/shaders/line_sdf.vertex.glsl
generated
vendored
@ -16,11 +16,11 @@ in vec4 a_data;
|
||||
uniform vec2 u_translation;
|
||||
uniform mediump float u_ratio;
|
||||
uniform lowp float u_device_pixel_ratio;
|
||||
uniform vec2 u_patternscale_a;
|
||||
uniform float u_tex_y_a;
|
||||
uniform vec2 u_patternscale_b;
|
||||
uniform float u_tex_y_b;
|
||||
uniform vec2 u_units_to_pixels;
|
||||
uniform float u_tileratio;
|
||||
uniform float u_crossfade_from;
|
||||
uniform float u_crossfade_to;
|
||||
uniform float u_lineatlas_height;
|
||||
|
||||
out vec2 v_normal;
|
||||
out vec2 v_width2;
|
||||
@ -38,6 +38,8 @@ out float v_depth;
|
||||
#pragma mapbox: define lowp float offset
|
||||
#pragma mapbox: define mediump float width
|
||||
#pragma mapbox: define lowp float floorwidth
|
||||
#pragma mapbox: define mediump vec4 dasharray_from
|
||||
#pragma mapbox: define mediump vec4 dasharray_to
|
||||
|
||||
void main() {
|
||||
#pragma mapbox: initialize highp vec4 color
|
||||
@ -47,6 +49,8 @@ void main() {
|
||||
#pragma mapbox: initialize lowp float offset
|
||||
#pragma mapbox: initialize mediump float width
|
||||
#pragma mapbox: initialize lowp float floorwidth
|
||||
#pragma mapbox: initialize mediump vec4 dasharray_from
|
||||
#pragma mapbox: initialize mediump vec4 dasharray_to
|
||||
|
||||
// the distance over which the line edge fades out.
|
||||
// Retina devices need a smaller distance to avoid aliasing.
|
||||
@ -103,7 +107,12 @@ void main() {
|
||||
v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective;
|
||||
#endif
|
||||
|
||||
v_tex_a = vec2(a_linesofar * u_patternscale_a.x / floorwidth, normal.y * u_patternscale_a.y + u_tex_y_a);
|
||||
v_tex_b = vec2(a_linesofar * u_patternscale_b.x / floorwidth, normal.y * u_patternscale_b.y + u_tex_y_b);
|
||||
float u_patternscale_a_x = u_tileratio / dasharray_from.w / u_crossfade_from;
|
||||
float u_patternscale_a_y = -dasharray_from.z / 2.0 / u_lineatlas_height;
|
||||
float u_patternscale_b_x = u_tileratio / dasharray_to.w / u_crossfade_to;
|
||||
float u_patternscale_b_y = -dasharray_to.z / 2.0 / u_lineatlas_height;
|
||||
|
||||
v_tex_a = vec2(a_linesofar * u_patternscale_a_x / floorwidth, normal.y * u_patternscale_a_y + (float(dasharray_from.y) + 0.5) / u_lineatlas_height);
|
||||
v_tex_b = vec2(a_linesofar * u_patternscale_b_x / floorwidth, normal.y * u_patternscale_b_y + (float(dasharray_to.y) + 0.5) / u_lineatlas_height);
|
||||
v_width2 = vec2(outset, inset);
|
||||
}
|
||||
|
||||
2
node_modules/maplibre-gl/src/shaders/line_sdf.vertex.glsl.g.ts
generated
vendored
2
node_modules/maplibre-gl/src/shaders/line_sdf.vertex.glsl.g.ts
generated
vendored
@ -1,2 +1,2 @@
|
||||
// This file is generated. Edit build/generate-shaders.ts, then run `npm run codegen`.
|
||||
export default '\n#define scale 0.015873016\n#define LINE_DISTANCE_SCALE 2.0\nin vec2 a_pos_normal;in vec4 a_data;uniform vec2 u_translation;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_patternscale_a;uniform float u_tex_y_a;uniform vec2 u_patternscale_b;uniform float u_tex_y_b;uniform vec2 u_units_to_pixels;out vec2 v_normal;out vec2 v_width2;out vec2 v_tex_a;out vec2 v_tex_b;out float v_gamma_scale;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;float a_linesofar=(floor(a_data.z/4.0)+a_data.w*64.0)*LINE_DISTANCE_SCALE;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;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+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nv_tex_a=vec2(a_linesofar*u_patternscale_a.x/floorwidth,normal.y*u_patternscale_a.y+u_tex_y_a);v_tex_b=vec2(a_linesofar*u_patternscale_b.x/floorwidth,normal.y*u_patternscale_b.y+u_tex_y_b);v_width2=vec2(outset,inset);}';
|
||||
export default '\n#define scale 0.015873016\n#define LINE_DISTANCE_SCALE 2.0\nin vec2 a_pos_normal;in vec4 a_data;uniform vec2 u_translation;uniform mediump float u_ratio;uniform lowp float u_device_pixel_ratio;uniform vec2 u_units_to_pixels;uniform float u_tileratio;uniform float u_crossfade_from;uniform float u_crossfade_to;uniform float u_lineatlas_height;out vec2 v_normal;out vec2 v_width2;out vec2 v_tex_a;out vec2 v_tex_b;out float v_gamma_scale;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma mapbox: define highp vec4 color\n#pragma mapbox: define lowp float blur\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define mediump float gapwidth\n#pragma mapbox: define lowp float offset\n#pragma mapbox: define mediump float width\n#pragma mapbox: define lowp float floorwidth\n#pragma mapbox: define mediump vec4 dasharray_from\n#pragma mapbox: define mediump vec4 dasharray_to\nvoid main() {\n#pragma mapbox: initialize highp vec4 color\n#pragma mapbox: initialize lowp float blur\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize mediump float gapwidth\n#pragma mapbox: initialize lowp float offset\n#pragma mapbox: initialize mediump float width\n#pragma mapbox: initialize lowp float floorwidth\n#pragma mapbox: initialize mediump vec4 dasharray_from\n#pragma mapbox: initialize mediump vec4 dasharray_to\nfloat ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=a_data.xy-128.0;float a_direction=mod(a_data.z,4.0)-1.0;float a_linesofar=(floor(a_data.z/4.0)+a_data.w*64.0)*LINE_DISTANCE_SCALE;vec2 pos=floor(a_pos_normal*0.5);mediump vec2 normal=a_pos_normal-2.0*pos;normal.y=normal.y*2.0-1.0;v_normal=normal;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+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nfloat u_patternscale_a_x=u_tileratio/dasharray_from.w/u_crossfade_from;float u_patternscale_a_y=-dasharray_from.z/2.0/u_lineatlas_height;float u_patternscale_b_x=u_tileratio/dasharray_to.w/u_crossfade_to;float u_patternscale_b_y=-dasharray_to.z/2.0/u_lineatlas_height;v_tex_a=vec2(a_linesofar*u_patternscale_a_x/floorwidth,normal.y*u_patternscale_a_y+(float(dasharray_from.y)+0.5)/u_lineatlas_height);v_tex_b=vec2(a_linesofar*u_patternscale_b_x/floorwidth,normal.y*u_patternscale_b_y+(float(dasharray_to.y)+0.5)/u_lineatlas_height);v_width2=vec2(outset,inset);}';
|
||||
|
||||
3
node_modules/maplibre-gl/src/shaders/shaders.ts
generated
vendored
3
node_modules/maplibre-gl/src/shaders/shaders.ts
generated
vendored
@ -47,6 +47,8 @@ import linePatternFrag from './line_pattern.fragment.glsl.g';
|
||||
import linePatternVert from './line_pattern.vertex.glsl.g';
|
||||
import lineSDFFrag from './line_sdf.fragment.glsl.g';
|
||||
import lineSDFVert from './line_sdf.vertex.glsl.g';
|
||||
import lineGradientSDFFrag from './line_gradient_sdf.fragment.glsl.g';
|
||||
import lineGradientSDFVert from './line_gradient_sdf.vertex.glsl.g';
|
||||
import rasterFrag from './raster.fragment.glsl.g';
|
||||
import rasterVert from './raster.vertex.glsl.g';
|
||||
import symbolIconFrag from './symbol_icon.fragment.glsl.g';
|
||||
@ -104,6 +106,7 @@ export const shaders = {
|
||||
lineGradient: prepare(lineGradientFrag, lineGradientVert),
|
||||
linePattern: prepare(linePatternFrag, linePatternVert),
|
||||
lineSDF: prepare(lineSDFFrag, lineSDFVert),
|
||||
lineGradientSDF: prepare(lineGradientSDFFrag, lineGradientSDFVert),
|
||||
raster: prepare(rasterFrag, rasterVert),
|
||||
symbolIcon: prepare(symbolIconFrag, symbolIconVert),
|
||||
symbolSDF: prepare(symbolSDFFrag, symbolSDFVert),
|
||||
|
||||
124
node_modules/maplibre-gl/src/source/geojson_source.test.ts
generated
vendored
124
node_modules/maplibre-gl/src/source/geojson_source.test.ts
generated
vendored
@ -303,16 +303,12 @@ describe('GeoJSONSource.update', () => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('modifying cluster properties after adding a source', () => {
|
||||
test('modifying cluster properties after adding a source', async () => {
|
||||
// test setCluster function on GeoJSONSource
|
||||
const spy = vi.fn();
|
||||
const mockDispatcher = wrapDispatcher({
|
||||
sendAsync(message) {
|
||||
expect(message.type).toBe(MessageType.loadData);
|
||||
expect(message.data.cluster).toBe(true);
|
||||
expect(message.data.superclusterOptions.radius).toBe(80 * EXTENT / source.tileSize);
|
||||
expect(message.data.superclusterOptions.maxZoom).toBe(16);
|
||||
spy();
|
||||
spy(message);
|
||||
return Promise.resolve({});
|
||||
}
|
||||
});
|
||||
@ -325,8 +321,120 @@ describe('GeoJSONSource.update', () => {
|
||||
clusterMinPoints: 3,
|
||||
generateId: true
|
||||
}, mockDispatcher, undefined);
|
||||
|
||||
// Wait for initial data to be loaded
|
||||
source.load();
|
||||
await waitForEvent(source, 'data', (e: MapSourceDataEvent) => e.sourceDataType === 'metadata');
|
||||
|
||||
spy.mockClear();
|
||||
|
||||
source.setClusterOptions({cluster: true, clusterRadius: 80, clusterMaxZoom: 16});
|
||||
expect(spy).toHaveBeenCalled();
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
expect(spy.mock.calls[0][0].type).toBe(MessageType.loadData);
|
||||
expect(spy.mock.calls[0][0].data.cluster).toBe(true);
|
||||
expect(spy.mock.calls[0][0].data.superclusterOptions.radius).toBe(80 * EXTENT / source.tileSize);
|
||||
expect(spy.mock.calls[0][0].data.superclusterOptions.maxZoom).toBe(16);
|
||||
});
|
||||
|
||||
test('modifying cluster properties with pending data', async () => {
|
||||
const spy = vi.fn();
|
||||
const mockDispatcher = wrapDispatcher({
|
||||
sendAsync(message) {
|
||||
spy(message);
|
||||
return Promise.resolve({});
|
||||
}
|
||||
});
|
||||
const source = new GeoJSONSource('id', {
|
||||
type: 'geojson',
|
||||
data: {} as GeoJSON.GeoJSON,
|
||||
cluster: false,
|
||||
clusterMaxZoom: 8,
|
||||
clusterRadius: 100,
|
||||
clusterMinPoints: 3,
|
||||
generateId: true
|
||||
}, mockDispatcher, undefined);
|
||||
|
||||
// Wait for initial data to be loaded
|
||||
source.load();
|
||||
await waitForEvent(source, 'data', (e: MapSourceDataEvent) => e.sourceDataType === 'metadata');
|
||||
|
||||
spy.mockClear();
|
||||
|
||||
// Initiate first data update
|
||||
const sourceData1 = {id: 'test-1', type: 'FeatureCollection', features: []} as GeoJSON.GeoJSON;
|
||||
source.setData(sourceData1);
|
||||
|
||||
// Immediately modify data again, and update cluster options
|
||||
const sourceData2 = {id: 'test-2', type: 'FeatureCollection', features: []} as GeoJSON.GeoJSON;
|
||||
source.setData(sourceData2);
|
||||
source.setClusterOptions({cluster: true, clusterRadius: 80, clusterMaxZoom: 16});
|
||||
|
||||
await waitForEvent(source, 'data', (e: MapSourceDataEvent) => e.sourceDataType === 'metadata');
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(2);
|
||||
expect(spy.mock.calls[0][0].type).toBe(MessageType.loadData);
|
||||
expect(spy.mock.calls[0][0].data.cluster).toBe(false);
|
||||
expect(spy.mock.calls[0][0].data.data).toBe(JSON.stringify(sourceData1));
|
||||
expect(spy.mock.calls[0][0].data.dataDiff).toBeUndefined();
|
||||
expect(spy.mock.calls[1][0].data.cluster).toBe(true);
|
||||
expect(spy.mock.calls[1][0].data.superclusterOptions.radius).toBe(80 * EXTENT / source.tileSize);
|
||||
expect(spy.mock.calls[1][0].data.superclusterOptions.maxZoom).toBe(16);
|
||||
expect(spy.mock.calls[1][0].data.data).toBe(JSON.stringify(sourceData2));
|
||||
expect(spy.mock.calls[1][0].data.dataDiff).toBeUndefined();
|
||||
});
|
||||
|
||||
test('modifying cluster properties after sending a diff', async () => {
|
||||
const spy = vi.fn();
|
||||
const mockDispatcher = wrapDispatcher({
|
||||
sendAsync(message) {
|
||||
spy(message);
|
||||
return Promise.resolve({});
|
||||
}
|
||||
});
|
||||
const geoJsonData = {
|
||||
'type': 'FeatureCollection',
|
||||
'features': [
|
||||
{
|
||||
'type': 'Feature',
|
||||
'id': 1,
|
||||
'properties': {},
|
||||
'geometry': {
|
||||
'type': 'Point',
|
||||
'coordinates': [-122.48369693756104, 37.83381888486939]
|
||||
}
|
||||
}
|
||||
]
|
||||
} as GeoJSON.GeoJSON;
|
||||
|
||||
const source = new GeoJSONSource('id', {
|
||||
type: 'geojson',
|
||||
data: geoJsonData,
|
||||
cluster: false,
|
||||
clusterMaxZoom: 8,
|
||||
clusterRadius: 100,
|
||||
clusterMinPoints: 3,
|
||||
generateId: true
|
||||
}, mockDispatcher, undefined);
|
||||
|
||||
// Wait for initial data to be loaded
|
||||
source.load();
|
||||
await waitForEvent(source, 'data', (e: MapSourceDataEvent) => e.sourceDataType === 'metadata');
|
||||
|
||||
spy.mockReset();
|
||||
|
||||
const diff = {remove: [1]};
|
||||
source.updateData(diff);
|
||||
source.setClusterOptions({cluster: true, clusterRadius: 80, clusterMaxZoom: 16});
|
||||
|
||||
await waitForEvent(source, 'data', (e: MapSourceDataEvent) => e.sourceDataType === 'metadata');
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(2);
|
||||
expect(spy.mock.calls[0][0].data.cluster).toBe(false);
|
||||
expect(spy.mock.calls[0][0].data.dataDiff).toEqual(diff);
|
||||
expect(spy.mock.calls[1][0].data.cluster).toEqual(true);
|
||||
expect(spy.mock.calls[1][0].data.data).not.toBeDefined();
|
||||
expect(spy.mock.calls[1][0].data.dataDiff).not.toBeDefined();
|
||||
});
|
||||
|
||||
test('forwards Supercluster options with worker request, ignore max zoom of source', () => {
|
||||
@ -831,6 +939,6 @@ describe('GeoJSONSource.load', () => {
|
||||
source.load();
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
expect(warnSpy).toHaveBeenCalledWith('No data or diff provided to GeoJSONSource id.');
|
||||
expect(warnSpy).toHaveBeenCalledWith('No pending worker updates for GeoJSONSource id.');
|
||||
});
|
||||
});
|
||||
|
||||
32
node_modules/maplibre-gl/src/source/geojson_source.ts
generated
vendored
32
node_modules/maplibre-gl/src/source/geojson_source.ts
generated
vendored
@ -126,7 +126,7 @@ export class GeoJSONSource extends Evented implements Source {
|
||||
map: Map;
|
||||
actor: Actor;
|
||||
_isUpdatingWorker: boolean;
|
||||
_pendingWorkerUpdate: { data?: GeoJSON.GeoJSON | string; diff?: GeoJSONSourceDiff };
|
||||
_pendingWorkerUpdate: { data?: GeoJSON.GeoJSON | string; diff?: GeoJSONSourceDiff; optionsChanged?: boolean };
|
||||
_collectResourceTiming: boolean;
|
||||
_removed: boolean;
|
||||
|
||||
@ -199,6 +199,10 @@ export class GeoJSONSource extends Evented implements Source {
|
||||
}
|
||||
}
|
||||
|
||||
private _hasPendingWorkerUpdate(): boolean {
|
||||
return this._pendingWorkerUpdate.data !== undefined || this._pendingWorkerUpdate.diff !== undefined || this._pendingWorkerUpdate.optionsChanged;
|
||||
}
|
||||
|
||||
private _pixelsToTileUnits(pixelValue: number): number {
|
||||
return pixelValue * (EXTENT / this.tileSize);
|
||||
}
|
||||
@ -309,12 +313,13 @@ export class GeoJSONSource extends Evented implements Source {
|
||||
*/
|
||||
setClusterOptions(options: SetClusterOptions): this {
|
||||
this.workerOptions.cluster = options.cluster;
|
||||
if (options) {
|
||||
if (options.clusterRadius !== undefined) this.workerOptions.superclusterOptions.radius = this._pixelsToTileUnits(options.clusterRadius);
|
||||
if (options.clusterMaxZoom !== undefined) {
|
||||
this.workerOptions.superclusterOptions.maxZoom = this._getClusterMaxZoom(options.clusterMaxZoom);
|
||||
}
|
||||
if (options.clusterRadius !== undefined) {
|
||||
this.workerOptions.superclusterOptions.radius = this._pixelsToTileUnits(options.clusterRadius);
|
||||
}
|
||||
if (options.clusterMaxZoom !== undefined) {
|
||||
this.workerOptions.superclusterOptions.maxZoom = this._getClusterMaxZoom(options.clusterMaxZoom);
|
||||
}
|
||||
this._pendingWorkerUpdate.optionsChanged = true;
|
||||
this._updateWorkerData();
|
||||
return this;
|
||||
}
|
||||
@ -382,13 +387,13 @@ export class GeoJSONSource extends Evented implements Source {
|
||||
async _updateWorkerData(): Promise<void> {
|
||||
if (this._isUpdatingWorker) return;
|
||||
|
||||
const {data, diff} = this._pendingWorkerUpdate;
|
||||
|
||||
if (!data && !diff) {
|
||||
warnOnce(`No data or diff provided to GeoJSONSource ${this.id}.`);
|
||||
if (!this._hasPendingWorkerUpdate()) {
|
||||
warnOnce(`No pending worker updates for GeoJSONSource ${this.id}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const {data, diff} = this._pendingWorkerUpdate;
|
||||
|
||||
const options: LoadGeoJSONParameters = extend({type: this.type}, this.workerOptions);
|
||||
if (data) {
|
||||
if (typeof data === 'string') {
|
||||
@ -404,6 +409,9 @@ export class GeoJSONSource extends Evented implements Source {
|
||||
this._pendingWorkerUpdate.diff = undefined;
|
||||
}
|
||||
|
||||
// Reset the flag since this update is using the latest options
|
||||
this._pendingWorkerUpdate.optionsChanged = undefined;
|
||||
|
||||
this._isUpdatingWorker = true;
|
||||
this.fire(new Event('dataloading', {dataType: 'source'}));
|
||||
try {
|
||||
@ -439,14 +447,14 @@ export class GeoJSONSource extends Evented implements Source {
|
||||
this.fire(new ErrorEvent(err));
|
||||
} finally {
|
||||
// If there is more pending data, update worker again.
|
||||
if (this._pendingWorkerUpdate.data || this._pendingWorkerUpdate.diff) {
|
||||
if (this._hasPendingWorkerUpdate()) {
|
||||
this._updateWorkerData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loaded(): boolean {
|
||||
return !this._isUpdatingWorker && this._pendingWorkerUpdate.data === undefined && this._pendingWorkerUpdate.diff === undefined;
|
||||
return !this._isUpdatingWorker && !this._hasPendingWorkerUpdate();
|
||||
}
|
||||
|
||||
async loadTile(tile: Tile): Promise<void> {
|
||||
|
||||
49
node_modules/maplibre-gl/src/source/geojson_worker_source.test.ts
generated
vendored
49
node_modules/maplibre-gl/src/source/geojson_worker_source.test.ts
generated
vendored
@ -1,5 +1,5 @@
|
||||
import {describe, beforeEach, afterEach, test, expect, vi} from 'vitest';
|
||||
import {GeoJSONWorkerSource, type LoadGeoJSONParameters} from './geojson_worker_source';
|
||||
import {createGeoJSONIndex, GeoJSONWorkerSource, type LoadGeoJSONParameters} from './geojson_worker_source';
|
||||
import {StyleLayerIndex} from '../style/style_layer_index';
|
||||
import {OverscaledTileID} from './tile_id';
|
||||
import perf from '../util/performance';
|
||||
@ -202,6 +202,30 @@ describe('loadData', () => {
|
||||
properties: {},
|
||||
} as GeoJSON.GeoJSON;
|
||||
|
||||
const updateableFeatureCollection = {
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
type: 'Feature',
|
||||
id: 'point1',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [0, 0],
|
||||
},
|
||||
properties: {},
|
||||
},
|
||||
{
|
||||
type: 'Feature',
|
||||
id: 'point2',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [1, 1],
|
||||
},
|
||||
properties: {},
|
||||
}
|
||||
]
|
||||
} as GeoJSON.GeoJSON;
|
||||
|
||||
const layerIndex = new StyleLayerIndex(layers);
|
||||
function createWorker() {
|
||||
return new GeoJSONWorkerSource(actor, layerIndex, []);
|
||||
@ -305,6 +329,29 @@ describe('loadData', () => {
|
||||
}]
|
||||
}} as LoadGeoJSONParameters)).resolves.toBeDefined();
|
||||
});
|
||||
|
||||
test('loadData should reject as first call with no data', async () => {
|
||||
const worker = new GeoJSONWorkerSource(actor, layerIndex, []);
|
||||
|
||||
await expect(worker.loadData({} as LoadGeoJSONParameters)).rejects.toBeDefined();
|
||||
});
|
||||
|
||||
test('loadData should resolve as subsequent call with no data', async () => {
|
||||
const worker = new GeoJSONWorkerSource(actor, layerIndex, []);
|
||||
|
||||
await worker.loadData({source: 'source1', data: JSON.stringify(updateableGeoJson)} as LoadGeoJSONParameters);
|
||||
await expect(worker.loadData({} as LoadGeoJSONParameters)).resolves.toBeDefined();
|
||||
});
|
||||
|
||||
test('loadData should process cluster change with no data', async () => {
|
||||
const mockCreateGeoJSONIndex = vi.fn(createGeoJSONIndex);
|
||||
const worker = new GeoJSONWorkerSource(actor, layerIndex, [], mockCreateGeoJSONIndex);
|
||||
|
||||
await worker.loadData({source: 'source1', data: JSON.stringify(updateableFeatureCollection), cluster: false} as LoadGeoJSONParameters);
|
||||
expect(mockCreateGeoJSONIndex.mock.calls[0][1].cluster).toBe(false);
|
||||
await expect(worker.loadData({cluster: true} as LoadGeoJSONParameters)).resolves.toBeDefined();
|
||||
expect(mockCreateGeoJSONIndex.mock.calls[1][1].cluster).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getData', () => {
|
||||
|
||||
32
node_modules/maplibre-gl/src/source/geojson_worker_source.ts
generated
vendored
32
node_modules/maplibre-gl/src/source/geojson_worker_source.ts
generated
vendored
@ -18,6 +18,8 @@ import type {LoadVectorTileResult} from './vector_tile_worker_source';
|
||||
import type {RequestParameters} from '../util/ajax';
|
||||
import {isUpdateableGeoJSON, type GeoJSONSourceDiff, applySourceDiff, toUpdateable, type GeoJSONFeatureId} from './geojson_source_diff';
|
||||
import type {ClusterIDAndSource, GeoJSONWorkerSourceLoadDataResult, RemoveSourceParams} from '../util/actor_messages';
|
||||
import type {IActor} from '../util/actor';
|
||||
import type {StyleLayerIndex} from '../style/style_layer_index';
|
||||
|
||||
/**
|
||||
* The geojson worker options that can be passed to the worker
|
||||
@ -68,6 +70,12 @@ export class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
||||
_pendingRequest: AbortController;
|
||||
_geoJSONIndex: GeoJSONIndex;
|
||||
_dataUpdateable = new Map<GeoJSONFeatureId, GeoJSON.Feature>();
|
||||
_createGeoJSONIndex: typeof createGeoJSONIndex;
|
||||
|
||||
constructor(actor: IActor, layerIndex: StyleLayerIndex, availableImages: Array<string>, createGeoJSONIndexFunc: typeof createGeoJSONIndex = createGeoJSONIndex) {
|
||||
super(actor, layerIndex, availableImages);
|
||||
this._createGeoJSONIndex = createGeoJSONIndexFunc;
|
||||
}
|
||||
|
||||
override async loadVectorTile(params: WorkerTileParameters, _abortController: AbortController): Promise<LoadVectorTileResult | null> {
|
||||
const canonical = params.tileID.canonical;
|
||||
@ -82,8 +90,8 @@ export class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
||||
}
|
||||
|
||||
const geojsonWrapper = new GeoJSONWrapper(geoJSONTile.features, {version: 2, extent: EXTENT});
|
||||
// Encode the geojson-vt tile into binary vector tile form. This
|
||||
// is a convenience that allows `FeatureIndex` to operate the same way
|
||||
// Encode the geojson-vt tile into binary vector tile form.
|
||||
// This is a convenience that allows `FeatureIndex` to operate the same way
|
||||
// across `VectorTileSource` and `GeoJSONSource` data.
|
||||
let pbf = fromVectorTileJs(geojsonWrapper);
|
||||
if (pbf.byteOffset !== 0 || pbf.byteLength !== pbf.buffer.byteLength) {
|
||||
@ -100,7 +108,10 @@ export class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
||||
/**
|
||||
* Fetches (if appropriate), parses, and index geojson data into tiles. This
|
||||
* preparatory method must be called before {@link GeoJSONWorkerSource.loadTile}
|
||||
* can correctly serve up tiles.
|
||||
* can correctly serve up tiles. The first call to this method must contain a valid
|
||||
* {@link params.data}, {@link params.request}, or {@link params.dataDiff}. Subsequent
|
||||
* calls may omit these parameters to reprocess the existing data (such as to update
|
||||
* clustering options).
|
||||
*
|
||||
* Defers to {@link GeoJSONWorkerSource.loadAndProcessGeoJSON} for the pre-processing.
|
||||
*
|
||||
@ -117,13 +128,15 @@ export class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
||||
|
||||
this._pendingRequest = new AbortController();
|
||||
try {
|
||||
this._pendingData = this.loadAndProcessGeoJSON(params, this._pendingRequest);
|
||||
// Load and process data if no data has been loaded previously, or if there is
|
||||
// a new request, data, or dataDiff to process.
|
||||
if (!this._pendingData || params.request || params.data || params.dataDiff) {
|
||||
this._pendingData = this.loadAndProcessGeoJSON(params, this._pendingRequest);
|
||||
}
|
||||
|
||||
const data = await this._pendingData;
|
||||
|
||||
this._geoJSONIndex = params.cluster ?
|
||||
new Supercluster(getSuperclusterOptions(params)).load((data as any).features) :
|
||||
geojsonvt(data, params.geojsonVtOptions);
|
||||
this._geoJSONIndex = this._createGeoJSONIndex(data, params);
|
||||
|
||||
this.loaded = {};
|
||||
|
||||
@ -265,6 +278,11 @@ export class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
||||
}
|
||||
}
|
||||
|
||||
export function createGeoJSONIndex(data: GeoJSON.GeoJSON, params: LoadGeoJSONParameters): GeoJSONIndex {
|
||||
return params.cluster ? new Supercluster(getSuperclusterOptions(params)).load((data as any).features) :
|
||||
geojsonvt(data, params.geojsonVtOptions);
|
||||
}
|
||||
|
||||
function getSuperclusterOptions({superclusterOptions, clusterProperties}: LoadGeoJSONParameters) {
|
||||
if (!clusterProperties || !superclusterOptions) return superclusterOptions;
|
||||
|
||||
|
||||
645
node_modules/maplibre-gl/src/source/source_cache.test.ts
generated
vendored
645
node_modules/maplibre-gl/src/source/source_cache.test.ts
generated
vendored
@ -1,17 +1,18 @@
|
||||
import {describe, afterEach, test, expect, vi} from 'vitest';
|
||||
import type {StyleSpecification} from '@maplibre/maplibre-gl-style-spec';
|
||||
import {describe, beforeEach, afterEach, test, expect, vi} from 'vitest';
|
||||
import {SourceCache} from './source_cache';
|
||||
import {type Map} from '../ui/map';
|
||||
import {type Source, addSourceType} from './source';
|
||||
import {Tile} from './tile';
|
||||
import {Tile, FadingRoles, FadingDirections} from './tile';
|
||||
import {CanonicalTileID, OverscaledTileID} from './tile_id';
|
||||
import {LngLat} from '../geo/lng_lat';
|
||||
import Point from '@mapbox/point-geometry';
|
||||
import {Event, ErrorEvent, Evented} from '../util/evented';
|
||||
import {extend} from '../util/util';
|
||||
import {browser} from '../util/browser';
|
||||
import {type Dispatcher} from '../util/dispatcher';
|
||||
import {TileBounds} from './tile_bounds';
|
||||
import {sleep, waitForEvent} from '../util/test/util';
|
||||
import {sleep, waitForEvent, beforeMapTest, createMap as globalCreateMap} from '../util/test/util';
|
||||
|
||||
import {type Map} from '../ui/map';
|
||||
import {type TileCache} from './tile_cache';
|
||||
import {MercatorTransform} from '../geo/projection/mercator_transform';
|
||||
import {GlobeTransform} from '../geo/projection/globe_transform';
|
||||
@ -37,6 +38,9 @@ class SourceMock extends Evented implements Source {
|
||||
if (sourceOptions.hasTile) {
|
||||
this.hasTile = sourceOptions.hasTile;
|
||||
}
|
||||
if (sourceOptions.raster) {
|
||||
this.type = 'raster';
|
||||
}
|
||||
}
|
||||
loadTile(tile: Tile): Promise<void> {
|
||||
if (this.sourceOptions.expires) {
|
||||
@ -93,14 +97,27 @@ function createSourceCache(options?, used?) {
|
||||
},
|
||||
getTiles(): { [_: string]: Tile } {
|
||||
return this._tiles;
|
||||
},
|
||||
updateLoadedSiblingTileCache(): void {
|
||||
this._updateLoadedSiblingTileCache();
|
||||
}
|
||||
});
|
||||
return scWithTestLogic;
|
||||
}
|
||||
|
||||
type MapOptions = {
|
||||
style: StyleSpecification;
|
||||
};
|
||||
|
||||
function createMap(options: MapOptions) {
|
||||
const container = window.document.createElement('div');
|
||||
window.document.body.appendChild(container);
|
||||
Object.defineProperty(container, 'clientWidth', {value: 512});
|
||||
Object.defineProperty(container, 'clientHeight', {value: 512});
|
||||
return globalCreateMap({container, ...options});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
beforeMapTest();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
@ -586,6 +603,55 @@ describe('SourceCache.update', () => {
|
||||
expect(sourceCache.getIds()).toEqual([new OverscaledTileID(0, 0, 0, 0, 0).key]);
|
||||
});
|
||||
|
||||
test('adds ideal (covering) tiles only once for zoom level on raster maps', async () => {
|
||||
const transform = new MercatorTransform();
|
||||
transform.resize(512, 512);
|
||||
transform.setZoom(1);
|
||||
|
||||
const sourceCache = createSourceCache({raster: true});
|
||||
sourceCache._source.loadTile = async (tile) => {
|
||||
tile.state = 'loaded';
|
||||
};
|
||||
|
||||
const addSpy = vi.spyOn(sourceCache, '_addTile');
|
||||
const dataPromise = waitForEvent(sourceCache, 'data', e => e.sourceDataType === 'metadata');
|
||||
sourceCache.onAdd(undefined);
|
||||
await dataPromise;
|
||||
|
||||
// on update at zoom 1 there should be 4 ideal tiles added through _addTiles
|
||||
sourceCache.update(transform);
|
||||
expect(addSpy).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
test('bypasses fading logic when raster fading is disabled', async () => {
|
||||
const map = createMap({
|
||||
style: {
|
||||
version: 8,
|
||||
sources: {rasterSource: {type: 'raster', tiles: [], tileSize: 256}},
|
||||
layers: [{id: 'rasterLayer', type: 'raster', source: 'rasterSource',
|
||||
paint: {'raster-fade-duration': 0}
|
||||
}]
|
||||
}
|
||||
});
|
||||
await map.once('styledata');
|
||||
|
||||
const style = map.style;
|
||||
const sourceCache = style.sourceCaches['rasterSource'];
|
||||
const spy = vi.spyOn(sourceCache, '_updateFadingTiles');
|
||||
sourceCache._loadTile = async () => {};
|
||||
|
||||
const fakeTile = new Tile(new OverscaledTileID(3, 0, 3, 1, 2), undefined);
|
||||
(fakeTile as any).texture = {bind: () => {}, size: [256, 256]};
|
||||
fakeTile.state = 'loaded';
|
||||
sourceCache._tiles[fakeTile.tileID.key] = fakeTile;
|
||||
|
||||
await map.once('render');
|
||||
map.setZoom(3);
|
||||
await map.once('render');
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('respects Source.hasTile method if it is present', async () => {
|
||||
const transform = new MercatorTransform();
|
||||
transform.resize(511, 511);
|
||||
@ -691,131 +757,6 @@ describe('SourceCache.update', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test('retains covered child tiles while parent tile is fading in', async () => {
|
||||
const transform = new MercatorTransform();
|
||||
transform.resize(511, 511);
|
||||
transform.setZoom(2);
|
||||
|
||||
const sourceCache = createSourceCache();
|
||||
sourceCache._source.loadTile = async (tile) => {
|
||||
tile.timeAdded = Infinity;
|
||||
tile.state = 'loaded';
|
||||
tile.registerFadeDuration(100);
|
||||
};
|
||||
|
||||
(sourceCache._source as any).type = 'raster';
|
||||
|
||||
const dataPromise = waitForEvent(sourceCache, 'data', e => e.sourceDataType === 'metadata');
|
||||
sourceCache.onAdd(undefined);
|
||||
await dataPromise;
|
||||
sourceCache.update(transform);
|
||||
expect(sourceCache.getIds()).toEqual([
|
||||
new OverscaledTileID(2, 0, 2, 2, 2).key,
|
||||
new OverscaledTileID(2, 0, 2, 1, 2).key,
|
||||
new OverscaledTileID(2, 0, 2, 2, 1).key,
|
||||
new OverscaledTileID(2, 0, 2, 1, 1).key
|
||||
]);
|
||||
|
||||
transform.setZoom(0);
|
||||
sourceCache.update(transform);
|
||||
|
||||
expect(sourceCache.getRenderableIds()).toHaveLength(5);
|
||||
});
|
||||
|
||||
test('retains a parent tile for fading even if a tile is partially covered by children', async () => {
|
||||
const transform = new MercatorTransform();
|
||||
transform.resize(511, 511);
|
||||
transform.setZoom(0);
|
||||
|
||||
const sourceCache = createSourceCache();
|
||||
sourceCache._source.loadTile = async (tile) => {
|
||||
tile.timeAdded = Infinity;
|
||||
tile.state = 'loaded';
|
||||
tile.registerFadeDuration(100);
|
||||
};
|
||||
|
||||
(sourceCache._source as any).type = 'raster';
|
||||
|
||||
const dataPromise = waitForEvent(sourceCache, 'data', e => e.sourceDataType === 'metadata');
|
||||
sourceCache.onAdd(undefined);
|
||||
await dataPromise;
|
||||
sourceCache.update(transform);
|
||||
|
||||
transform.setZoom(2);
|
||||
sourceCache.update(transform);
|
||||
|
||||
transform.setZoom(1);
|
||||
sourceCache.update(transform);
|
||||
|
||||
expect(sourceCache._coveredTiles[(new OverscaledTileID(0, 0, 0, 0, 0).key)]).toBe(true);
|
||||
});
|
||||
|
||||
test('retain children for fading fadeEndTime is 0 (added but registerFadeDuration() is not called yet)', async () => {
|
||||
const transform = new MercatorTransform();
|
||||
transform.resize(511, 511);
|
||||
transform.setZoom(1);
|
||||
|
||||
const sourceCache = createSourceCache();
|
||||
sourceCache._source.loadTile = async (tile) => {
|
||||
// not setting fadeEndTime because class Tile default is 0, and need to be tested
|
||||
tile.timeAdded = Date.now();
|
||||
tile.state = 'loaded';
|
||||
};
|
||||
|
||||
(sourceCache._source as any).type = 'raster';
|
||||
|
||||
const dataPromise = waitForEvent(sourceCache, 'data', e => e.sourceDataType === 'metadata');
|
||||
sourceCache.onAdd(undefined);
|
||||
await dataPromise;
|
||||
sourceCache.update(transform);
|
||||
|
||||
transform.setZoom(0);
|
||||
sourceCache.update(transform);
|
||||
|
||||
expect(sourceCache.getRenderableIds()).toHaveLength(5);
|
||||
});
|
||||
|
||||
test('retains children when tile.fadeEndTime is in the future', async () => {
|
||||
const transform = new MercatorTransform();
|
||||
transform.resize(511, 511);
|
||||
transform.setZoom(1);
|
||||
|
||||
const fadeTime = 100;
|
||||
|
||||
const start = Date.now();
|
||||
let time = start;
|
||||
vi.spyOn(browser, 'now').mockImplementation(() => time);
|
||||
|
||||
const sourceCache = createSourceCache();
|
||||
sourceCache._source.loadTile = async (tile) => {
|
||||
tile.timeAdded = browser.now();
|
||||
tile.state = 'loaded';
|
||||
tile.fadeEndTime = browser.now() + fadeTime;
|
||||
};
|
||||
|
||||
(sourceCache._source as any).type = 'raster';
|
||||
|
||||
const dataPromise = waitForEvent(sourceCache, 'data', e => e.sourceDataType === 'metadata');
|
||||
|
||||
sourceCache.onAdd(undefined);
|
||||
await dataPromise;
|
||||
// load children
|
||||
sourceCache.update(transform);
|
||||
|
||||
transform.setZoom(0);
|
||||
sourceCache.update(transform);
|
||||
|
||||
expect(sourceCache.getRenderableIds()).toHaveLength(5);
|
||||
|
||||
time = start + 98;
|
||||
sourceCache.update(transform);
|
||||
expect(sourceCache.getRenderableIds()).toHaveLength(5);
|
||||
|
||||
time = start + fadeTime + 1;
|
||||
sourceCache.update(transform);
|
||||
expect(sourceCache.getRenderableIds()).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('retains children tiles for pending parents', () => {
|
||||
const transform = new GlobeTransform();
|
||||
transform.resize(511, 511);
|
||||
@ -910,6 +851,173 @@ describe('SourceCache.update', () => {
|
||||
expect(sourceCache.getTile(wrappedTileID)).toBe(tile);
|
||||
});
|
||||
|
||||
test('retains fading children and applies fading logic when zooming out', async () => {
|
||||
const transform = new MercatorTransform();
|
||||
transform.resize(1024, 1024);
|
||||
transform.setZoom(10);
|
||||
|
||||
const sourceCache = createSourceCache({raster: true});
|
||||
const loadedTiles: Record<string, Tile> = {};
|
||||
sourceCache._source.loadTile = async (tile) => {
|
||||
loadedTiles[tile.tileID.key] = tile;
|
||||
tile.state = 'loaded';
|
||||
};
|
||||
sourceCache.on('data', (e) => {
|
||||
if (e.dataType === 'source' && e.sourceDataType === 'metadata') {
|
||||
sourceCache.update(transform);
|
||||
}
|
||||
});
|
||||
sourceCache.setRasterFadeDuration(300);
|
||||
sourceCache.onAdd(undefined);
|
||||
|
||||
// get default zoom ideal tiles at zoom specified above
|
||||
await sleep(0);
|
||||
// ideal tiles will become fading children when zooming out
|
||||
const children: Tile[] = Object.values(loadedTiles);
|
||||
|
||||
// zoom out 1 level - ideal tiles (new children) should fade out
|
||||
transform.setZoom(9);
|
||||
sourceCache.update(transform);
|
||||
await sleep(0);
|
||||
|
||||
// ensure that the loaded child was retained and fading logic was applied
|
||||
for (const child of children) {
|
||||
expect(loadedTiles).toHaveProperty(child.tileID.key);
|
||||
expect(child.fadingRole).toEqual(FadingRoles.Base);
|
||||
expect(child.fadingDirection).toEqual(FadingDirections.Departing);
|
||||
expect(child.fadingParentID).toBeInstanceOf(OverscaledTileID);
|
||||
}
|
||||
});
|
||||
|
||||
test('retains fading grandchildren and applies fading logic when zooming out', async () => {
|
||||
const transform = new MercatorTransform();
|
||||
transform.resize(512, 512);
|
||||
transform.setZoom(10);
|
||||
|
||||
const sourceCache = createSourceCache({raster: true});
|
||||
const loadedTiles: Record<string, Tile> = {};
|
||||
sourceCache._source.loadTile = async (tile) => {
|
||||
loadedTiles[tile.tileID.key] = tile;
|
||||
tile.state = 'loaded';
|
||||
};
|
||||
sourceCache.on('data', (e) => {
|
||||
if (e.dataType === 'source' && e.sourceDataType === 'metadata') {
|
||||
sourceCache.update(transform);
|
||||
}
|
||||
});
|
||||
sourceCache.setRasterFadeDuration(300);
|
||||
sourceCache.onAdd(undefined);
|
||||
|
||||
// get default zoom ideal tiles at zoom specified above
|
||||
await sleep(0);
|
||||
// ideal tiles will become fading grandchildren when zooming out
|
||||
const grandChildren: Tile[] = Object.values(loadedTiles);
|
||||
|
||||
// zoom out 2 levels - ideal tiles (new grandchildren) should fade out
|
||||
transform.setZoom(8);
|
||||
sourceCache.update(transform);
|
||||
await sleep(0);
|
||||
|
||||
// ensure that the loaded grandchild was retained and fading logic was applied
|
||||
for (const grandChild of grandChildren) {
|
||||
expect(loadedTiles).toHaveProperty(grandChild.tileID.key);
|
||||
expect(grandChild.fadingRole).toEqual(FadingRoles.Base);
|
||||
expect(grandChild.fadingDirection).toEqual(FadingDirections.Departing);
|
||||
expect(grandChild.fadingParentID).toBeInstanceOf(OverscaledTileID);
|
||||
}
|
||||
});
|
||||
|
||||
test('retains fading parent and applies fading logic when zooming in', async () => {
|
||||
const transform = new MercatorTransform();
|
||||
transform.resize(512, 512);
|
||||
transform.setZoom(10);
|
||||
|
||||
const sourceCache = createSourceCache({raster: true});
|
||||
const loadedTiles: Record<string, Tile> = {};
|
||||
sourceCache._source.loadTile = async (tile) => {
|
||||
loadedTiles[tile.tileID.key] = tile;
|
||||
tile.state = 'loaded';
|
||||
};
|
||||
sourceCache.on('data', (e) => {
|
||||
if (e.dataType === 'source' && e.sourceDataType === 'metadata') {
|
||||
sourceCache.update(transform);
|
||||
}
|
||||
});
|
||||
sourceCache.setRasterFadeDuration(300);
|
||||
sourceCache.onAdd(undefined);
|
||||
|
||||
// get default zoom ideal tiles at zoom specified above
|
||||
await sleep(0);
|
||||
// ideal tiles will become fading parent when zooming in
|
||||
const parents: Tile[] = Object.values(loadedTiles);
|
||||
const parentKeys = new Set(parents.map(p => p.tileID.key));
|
||||
|
||||
// zoom in 1 level - ideal tiles (new parent) should fade out
|
||||
transform.setZoom(11);
|
||||
sourceCache.update(transform);
|
||||
await sleep(0);
|
||||
|
||||
// ensure that the loaded parents were retained and fading logic was applied
|
||||
for (const parent of parents) {
|
||||
expect(loadedTiles).toHaveProperty(parent.tileID.key);
|
||||
expect(parent.fadingRole).toEqual(FadingRoles.Parent);
|
||||
expect(parent.fadingDirection).toEqual(FadingDirections.Departing);
|
||||
}
|
||||
|
||||
// check incoming tiles
|
||||
const incoming = Object.values(loadedTiles).filter(tile => !parentKeys.has(tile.tileID.key));
|
||||
for (const tile of incoming) {
|
||||
expect(tile.fadingRole).toEqual(FadingRoles.Base);
|
||||
expect(tile.fadingDirection).toEqual(FadingDirections.Incoming);
|
||||
expect(tile.fadingParentID).toBeInstanceOf(OverscaledTileID);
|
||||
}
|
||||
});
|
||||
|
||||
test('retains fading grandparent and applies fading logic when zooming in', async () => {
|
||||
const transform = new MercatorTransform();
|
||||
transform.resize(512, 512);
|
||||
transform.setZoom(10);
|
||||
|
||||
const sourceCache = createSourceCache({raster: true});
|
||||
const loadedTiles: Record<string, Tile> = {};
|
||||
sourceCache._source.loadTile = async (tile) => {
|
||||
loadedTiles[tile.tileID.key] = tile;
|
||||
tile.state = 'loaded';
|
||||
};
|
||||
sourceCache.on('data', (e) => {
|
||||
if (e.dataType === 'source' && e.sourceDataType === 'metadata') {
|
||||
sourceCache.update(transform);
|
||||
}
|
||||
});
|
||||
sourceCache.setRasterFadeDuration(300);
|
||||
sourceCache.onAdd(undefined);
|
||||
|
||||
// get default zoom ideal tiles at zoom specified above
|
||||
await sleep(0);
|
||||
// ideal tiles will become fading grandparent when zooming in
|
||||
const grandParents: Tile[] = Object.values(loadedTiles);
|
||||
const grandParentKeys = new Set(grandParents.map(p => p.tileID.key));
|
||||
|
||||
// zoom in 2 levels - ideal tiles (new grandparent) should fade out
|
||||
transform.setZoom(12);
|
||||
sourceCache.update(transform);
|
||||
await sleep(0);
|
||||
|
||||
// ensure that the loaded grandparents were retained and fading logic was applied
|
||||
for (const grandParent of grandParents) {
|
||||
expect(loadedTiles).toHaveProperty(grandParent.tileID.key);
|
||||
expect(grandParent.fadingRole).toEqual(FadingRoles.Parent);
|
||||
expect(grandParent.fadingDirection).toEqual(FadingDirections.Departing);
|
||||
}
|
||||
|
||||
// check incoming tiles
|
||||
const incoming = Object.values(loadedTiles).filter(tile => !grandParentKeys.has(tile.tileID.key));
|
||||
for (const tile of incoming) {
|
||||
expect(tile.fadingRole).toEqual(FadingRoles.Base);
|
||||
expect(tile.fadingDirection).toEqual(FadingDirections.Incoming);
|
||||
expect(tile.fadingParentID).toBeInstanceOf(OverscaledTileID);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('SourceCache._updateRetainedTiles', () => {
|
||||
@ -971,6 +1079,31 @@ describe('SourceCache._updateRetainedTiles', () => {
|
||||
expect(Object.keys(retained).sort()).toEqual(expectedTiles.map(t => t.key).sort());
|
||||
});
|
||||
|
||||
test('_updateRetainedTiles does not retain parents when 2nd generation children are loaded', () => {
|
||||
const sourceCache = createSourceCache();
|
||||
sourceCache._source.loadTile = async (tile) => {
|
||||
tile.state = 'errored';
|
||||
};
|
||||
|
||||
const idealTile = new OverscaledTileID(3, 0, 3, 1, 2);
|
||||
sourceCache._tiles[idealTile.key] = new Tile(idealTile, undefined);
|
||||
sourceCache._tiles[idealTile.key].state = 'errored';
|
||||
|
||||
const secondGeneration = idealTile
|
||||
.children(10)
|
||||
.flatMap(child => child.children(10));
|
||||
expect(secondGeneration.length).toEqual(16);
|
||||
|
||||
for (const id of secondGeneration) {
|
||||
sourceCache._tiles[id.key] = new Tile(id, undefined);
|
||||
sourceCache._tiles[id.key].state = 'loaded';
|
||||
}
|
||||
const expectedTiles = [...secondGeneration, idealTile];
|
||||
|
||||
const retained = sourceCache._updateRetainedTiles([idealTile], 3);
|
||||
expect(Object.keys(retained).sort()).toEqual(expectedTiles.map(t => t.key).sort());
|
||||
});
|
||||
|
||||
for (const pitch of [0, 20, 40, 65, 75, 85]) {
|
||||
test(`retains loaded children for pitch: ${pitch}`, () => {
|
||||
const transform = new MercatorTransform();
|
||||
@ -1070,6 +1203,38 @@ describe('SourceCache._updateRetainedTiles', () => {
|
||||
expect(Object.keys(retained).sort()).toEqual([idealTile].concat(loadedChildren).map(t => t.key).sort());
|
||||
});
|
||||
|
||||
test('_areDescendentsComplete returns true when descendents fully cover a generation', () => {
|
||||
const sourceCache = createSourceCache();
|
||||
const idealTile = new OverscaledTileID(3, 0, 3, 1, 2);
|
||||
|
||||
const firstGen = idealTile.children(10);
|
||||
expect(sourceCache._areDescendentsComplete(firstGen, 4, 3)).toBe(true);
|
||||
|
||||
const secondGen = idealTile.children(10).flatMap(c => c.children(10));
|
||||
expect(sourceCache._areDescendentsComplete(secondGen, 5, 3)).toBe(true);
|
||||
});
|
||||
|
||||
test('_areDescendentsComplete returns false when descendents are incomplete', () => {
|
||||
const sourceCache = createSourceCache();
|
||||
const idealTile = new OverscaledTileID(3, 0, 3, 1, 2);
|
||||
|
||||
const firstGenPartial = idealTile.children(10).slice(0, 3);
|
||||
expect(sourceCache._areDescendentsComplete(firstGenPartial, 4, 3)).toBe(false);
|
||||
|
||||
const secondGenPartial = idealTile.children(10).flatMap(c => c.children(10)).slice(0, 15);
|
||||
expect(sourceCache._areDescendentsComplete(secondGenPartial, 5, 3)).toBe(false);
|
||||
});
|
||||
|
||||
test('_areDescendentsComplete properly handles overscaled tiles', () => {
|
||||
const sourceCache = createSourceCache();
|
||||
|
||||
const correct = new OverscaledTileID(4, 0, 3, 1, 2);
|
||||
expect(sourceCache._areDescendentsComplete([correct], 4, 3)).toBe(true);
|
||||
|
||||
const wrong = new OverscaledTileID(5, 0, 3, 1, 2);
|
||||
expect(sourceCache._areDescendentsComplete([wrong], 4, 3)).toBe(false);
|
||||
});
|
||||
|
||||
test('adds parent tile if ideal tile errors and no child tiles are loaded', () => {
|
||||
const stateCache = {};
|
||||
const sourceCache = createSourceCache();
|
||||
@ -1272,22 +1437,22 @@ describe('SourceCache._updateRetainedTiles', () => {
|
||||
};
|
||||
const idealTile = new OverscaledTileID(2, 0, 2, 0, 0);
|
||||
|
||||
const getTileSpy = vi.spyOn(sourceCache, 'getTile');
|
||||
const retained = sourceCache._updateRetainedTiles([idealTile], 2);
|
||||
|
||||
expect(getTileSpy.mock.calls.map((c) => { return c[0]; })).toEqual([
|
||||
// overzoomed child
|
||||
new OverscaledTileID(3, 0, 2, 0, 0),
|
||||
// parents
|
||||
new OverscaledTileID(1, 0, 1, 0, 0),
|
||||
new OverscaledTileID(0, 0, 0, 0, 0)
|
||||
]);
|
||||
|
||||
expect(retained).toEqual({
|
||||
// ideal tile id (2, 0, 0)
|
||||
'022': new OverscaledTileID(2, 0, 2, 0, 0)
|
||||
const loadedTiles = [
|
||||
new OverscaledTileID(3, 0, 2, 0, 0), // overzoomed child
|
||||
new OverscaledTileID(1, 0, 1, 0, 0), // parent
|
||||
new OverscaledTileID(0, 0, 0, 0, 0) // parent
|
||||
];
|
||||
loadedTiles.forEach(t => {
|
||||
sourceCache._tiles[t.key] = new Tile(t, undefined);
|
||||
sourceCache._tiles[t.key].state = 'loaded';
|
||||
});
|
||||
|
||||
const retained = sourceCache._updateRetainedTiles([idealTile], 2);
|
||||
|
||||
expect(retained).toEqual({
|
||||
'022': new OverscaledTileID(2, 0, 2, 0, 0), // ideal
|
||||
'023': new OverscaledTileID(3, 0, 2, 0, 0) // overzoomed
|
||||
});
|
||||
});
|
||||
|
||||
test('don\'t ascend multiple times if a tile is not found', () => {
|
||||
@ -2138,191 +2303,6 @@ describe('source cache get ids', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('SourceCache.findLoadedParent', () => {
|
||||
|
||||
test('adds from previously used tiles (sourceCache._tiles)', () => {
|
||||
const sourceCache = createSourceCache({});
|
||||
sourceCache.onAdd(undefined);
|
||||
const tr = new MercatorTransform();
|
||||
tr.resize(512, 512);
|
||||
sourceCache.updateCacheSize(tr);
|
||||
|
||||
const tile = {
|
||||
tileID: new OverscaledTileID(1, 0, 1, 0, 0),
|
||||
hasData() { return true; }
|
||||
} as any as Tile;
|
||||
|
||||
sourceCache._tiles[tile.tileID.key] = tile;
|
||||
|
||||
expect(sourceCache.findLoadedParent(new OverscaledTileID(2, 0, 2, 3, 3), 0)).toBeUndefined();
|
||||
expect(sourceCache.findLoadedParent(new OverscaledTileID(2, 0, 2, 0, 0), 0)).toEqual(tile);
|
||||
});
|
||||
|
||||
test('retains parents', () => {
|
||||
const sourceCache = createSourceCache({});
|
||||
sourceCache.onAdd(undefined);
|
||||
const tr = new MercatorTransform();
|
||||
tr.resize(512, 512);
|
||||
sourceCache.updateCacheSize(tr);
|
||||
|
||||
const tile = new Tile(new OverscaledTileID(1, 0, 1, 0, 0), 512);
|
||||
sourceCache._cache.add(tile.tileID, tile);
|
||||
|
||||
expect(sourceCache.findLoadedParent(new OverscaledTileID(2, 0, 2, 3, 3), 0)).toBeUndefined();
|
||||
expect(sourceCache.findLoadedParent(new OverscaledTileID(2, 0, 2, 0, 0), 0)).toBe(tile);
|
||||
expect(sourceCache._cache.order).toHaveLength(1);
|
||||
|
||||
});
|
||||
|
||||
test('Search cache for loaded parent tiles', () => {
|
||||
const sourceCache = createSourceCache({});
|
||||
sourceCache.onAdd(undefined);
|
||||
const tr = new MercatorTransform();
|
||||
tr.resize(512, 512);
|
||||
sourceCache.updateCacheSize(tr);
|
||||
|
||||
const mockTile = id => {
|
||||
const tile = {
|
||||
tileID: id,
|
||||
hasData() { return true; }
|
||||
} as any as Tile;
|
||||
sourceCache._tiles[id.key] = tile;
|
||||
};
|
||||
|
||||
const tiles = [
|
||||
new OverscaledTileID(0, 0, 0, 0, 0),
|
||||
new OverscaledTileID(1, 0, 1, 1, 0),
|
||||
new OverscaledTileID(2, 0, 2, 0, 0),
|
||||
new OverscaledTileID(2, 0, 2, 1, 0),
|
||||
new OverscaledTileID(2, 0, 2, 2, 0),
|
||||
new OverscaledTileID(2, 0, 2, 1, 2)
|
||||
];
|
||||
|
||||
tiles.forEach(t => mockTile(t));
|
||||
sourceCache._updateLoadedParentTileCache();
|
||||
|
||||
// Loaded tiles excluding the root should be in the cache
|
||||
expect(sourceCache.findLoadedParent(tiles[0], 0)).toBeUndefined();
|
||||
expect(sourceCache.findLoadedParent(tiles[1], 0).tileID).toBe(tiles[0]);
|
||||
expect(sourceCache.findLoadedParent(tiles[2], 0).tileID).toBe(tiles[0]);
|
||||
expect(sourceCache.findLoadedParent(tiles[3], 0).tileID).toBe(tiles[0]);
|
||||
expect(sourceCache.findLoadedParent(tiles[4], 0).tileID).toBe(tiles[1]);
|
||||
expect(sourceCache.findLoadedParent(tiles[5], 0).tileID).toBe(tiles[0]);
|
||||
|
||||
expect(tiles[0].key in sourceCache._loadedParentTiles).toBe(false);
|
||||
expect(tiles[1].key in sourceCache._loadedParentTiles).toBe(true);
|
||||
expect(tiles[2].key in sourceCache._loadedParentTiles).toBe(true);
|
||||
expect(tiles[3].key in sourceCache._loadedParentTiles).toBe(true);
|
||||
expect(tiles[4].key in sourceCache._loadedParentTiles).toBe(true);
|
||||
expect(tiles[5].key in sourceCache._loadedParentTiles).toBe(true);
|
||||
|
||||
// Arbitrary tiles should not in the cache
|
||||
const notLoadedTiles = [
|
||||
new OverscaledTileID(2, 1, 2, 0, 0),
|
||||
new OverscaledTileID(2, 0, 2, 3, 0),
|
||||
new OverscaledTileID(2, 0, 2, 3, 3),
|
||||
new OverscaledTileID(3, 0, 3, 2, 1)
|
||||
];
|
||||
|
||||
expect(sourceCache.findLoadedParent(notLoadedTiles[0], 0)).toBeUndefined();
|
||||
expect(sourceCache.findLoadedParent(notLoadedTiles[1], 0).tileID).toBe(tiles[1]);
|
||||
expect(sourceCache.findLoadedParent(notLoadedTiles[2], 0).tileID).toBe(tiles[0]);
|
||||
expect(sourceCache.findLoadedParent(notLoadedTiles[3], 0).tileID).toBe(tiles[3]);
|
||||
|
||||
expect(notLoadedTiles[0].key in sourceCache._loadedParentTiles).toBe(false);
|
||||
expect(notLoadedTiles[1].key in sourceCache._loadedParentTiles).toBe(false);
|
||||
expect(notLoadedTiles[2].key in sourceCache._loadedParentTiles).toBe(false);
|
||||
expect(notLoadedTiles[3].key in sourceCache._loadedParentTiles).toBe(false);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('SourceCache.findLoadedSibling', () => {
|
||||
|
||||
test('adds from previously used tiles (sourceCache._tiles)', () => {
|
||||
const sourceCache = createSourceCache({});
|
||||
sourceCache.onAdd(undefined);
|
||||
const tr = new MercatorTransform();
|
||||
tr.resize(512, 512);
|
||||
sourceCache.updateCacheSize(tr);
|
||||
|
||||
const tile = {
|
||||
tileID: new OverscaledTileID(1, 0, 1, 0, 0),
|
||||
hasData() { return true; }
|
||||
} as any as Tile;
|
||||
|
||||
sourceCache.getTiles()[tile.tileID.key] = tile;
|
||||
|
||||
expect(sourceCache.findLoadedSibling(new OverscaledTileID(1, 0, 1, 1, 0))).toBeNull();
|
||||
expect(sourceCache.findLoadedSibling(new OverscaledTileID(1, 0, 1, 0, 0))).toEqual(tile);
|
||||
});
|
||||
|
||||
test('retains siblings', () => {
|
||||
const sourceCache = createSourceCache({});
|
||||
sourceCache.onAdd(undefined);
|
||||
const tr = new MercatorTransform();
|
||||
tr.resize(512, 512);
|
||||
sourceCache.updateCacheSize(tr);
|
||||
|
||||
const tile = new Tile(new OverscaledTileID(1, 0, 1, 0, 0), 512);
|
||||
sourceCache.getCache().add(tile.tileID, tile);
|
||||
|
||||
expect(sourceCache.findLoadedSibling(new OverscaledTileID(1, 0, 1, 1, 0))).toBeNull();
|
||||
expect(sourceCache.findLoadedSibling(new OverscaledTileID(1, 0, 1, 0, 0))).toBe(tile);
|
||||
expect(sourceCache.getCache().order).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('Search cache for loaded sibling tiles', () => {
|
||||
const sourceCache = createSourceCache({});
|
||||
sourceCache.onAdd(undefined);
|
||||
const tr = new MercatorTransform();
|
||||
tr.resize(512, 512);
|
||||
sourceCache.updateCacheSize(tr);
|
||||
|
||||
const mockTile = id => {
|
||||
const tile = {
|
||||
tileID: id,
|
||||
hasData() { return true; }
|
||||
} as any as Tile;
|
||||
sourceCache.getTiles()[id.key] = tile;
|
||||
};
|
||||
|
||||
const tiles = [
|
||||
new OverscaledTileID(0, 0, 0, 0, 0),
|
||||
new OverscaledTileID(1, 0, 1, 1, 0),
|
||||
new OverscaledTileID(2, 0, 2, 0, 0),
|
||||
new OverscaledTileID(2, 0, 2, 1, 0),
|
||||
new OverscaledTileID(2, 0, 2, 2, 0),
|
||||
new OverscaledTileID(2, 0, 2, 1, 2)
|
||||
];
|
||||
|
||||
tiles.forEach(t => mockTile(t));
|
||||
sourceCache.updateLoadedSiblingTileCache();
|
||||
|
||||
// Loaded tiles should be in the cache
|
||||
expect(sourceCache.findLoadedSibling(tiles[0]).tileID).toBe(tiles[0]);
|
||||
expect(sourceCache.findLoadedSibling(tiles[1]).tileID).toBe(tiles[1]);
|
||||
expect(sourceCache.findLoadedSibling(tiles[2]).tileID).toBe(tiles[2]);
|
||||
expect(sourceCache.findLoadedSibling(tiles[3]).tileID).toBe(tiles[3]);
|
||||
expect(sourceCache.findLoadedSibling(tiles[4]).tileID).toBe(tiles[4]);
|
||||
expect(sourceCache.findLoadedSibling(tiles[5]).tileID).toBe(tiles[5]);
|
||||
|
||||
// Arbitrary tiles should not in the cache
|
||||
const notLoadedTiles = [
|
||||
new OverscaledTileID(2, 1, 2, 0, 0),
|
||||
new OverscaledTileID(2, 0, 2, 3, 0),
|
||||
new OverscaledTileID(2, 0, 2, 3, 3),
|
||||
new OverscaledTileID(3, 0, 3, 2, 1)
|
||||
];
|
||||
|
||||
expect(sourceCache.findLoadedSibling(notLoadedTiles[0])).toBeNull();
|
||||
expect(sourceCache.findLoadedSibling(notLoadedTiles[1])).toBeNull();
|
||||
expect(sourceCache.findLoadedSibling(notLoadedTiles[2])).toBeNull();
|
||||
expect(sourceCache.findLoadedSibling(notLoadedTiles[3])).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('SourceCache.reload', () => {
|
||||
test('before loaded', () => {
|
||||
const sourceCache = createSourceCache({noLoad: true});
|
||||
@ -2562,5 +2542,4 @@ describe('SourceCache::refreshTiles', () => {
|
||||
expect(spy.mock.calls[2][1]).toBe('expired');
|
||||
expect(spy.mock.calls[3][1]).toBe('expired');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
702
node_modules/maplibre-gl/src/source/source_cache.ts
generated
vendored
702
node_modules/maplibre-gl/src/source/source_cache.ts
generated
vendored
@ -1,16 +1,16 @@
|
||||
import {create as createSource} from './source';
|
||||
|
||||
import {Tile} from './tile';
|
||||
import {Event, ErrorEvent, Evented} from '../util/evented';
|
||||
import {Tile, FadingDirections, FadingRoles} from './tile';
|
||||
import {ErrorEvent, Event, Evented} from '../util/evented';
|
||||
import {TileCache} from './tile_cache';
|
||||
import {MercatorCoordinate} from '../geo/mercator_coordinate';
|
||||
import {keysDifference} from '../util/util';
|
||||
import {EXTENT} from '../data/extent';
|
||||
import {type Context} from '../gl/context';
|
||||
import Point from '@mapbox/point-geometry';
|
||||
import {browser} from '../util/browser';
|
||||
import {now} from '../util/time_control';
|
||||
import {OverscaledTileID} from './tile_id';
|
||||
import {SourceFeatureState} from './source_state';
|
||||
import {getEdgeTiles} from '../util/util';
|
||||
import {config} from '../util/config';
|
||||
|
||||
import type {Source} from './source';
|
||||
@ -62,30 +62,24 @@ export class SourceCache extends Evented {
|
||||
_sourceLoaded: boolean;
|
||||
|
||||
_sourceErrored: boolean;
|
||||
_tiles: {[_: string]: Tile};
|
||||
_tiles: Record<string, Tile>;
|
||||
_prevLng: number;
|
||||
_cache: TileCache;
|
||||
_timers: {
|
||||
[_ in any]: ReturnType<typeof setTimeout>;
|
||||
};
|
||||
_cacheTimers: {
|
||||
[_ in any]: ReturnType<typeof setTimeout>;
|
||||
};
|
||||
_timers: Record<string, ReturnType<typeof setTimeout>>;
|
||||
_maxTileCacheSize: number;
|
||||
_maxTileCacheZoomLevels: number;
|
||||
_paused: boolean;
|
||||
_shouldReloadOnResume: boolean;
|
||||
_coveredTiles: {[_: string]: boolean};
|
||||
transform: ITransform;
|
||||
terrain: Terrain;
|
||||
used: boolean;
|
||||
usedForTerrain: boolean;
|
||||
tileSize: number;
|
||||
_state: SourceFeatureState;
|
||||
_loadedParentTiles: {[_: string]: Tile};
|
||||
_loadedSiblingTiles: {[_: string]: Tile};
|
||||
_didEmitContent: boolean;
|
||||
_updated: boolean;
|
||||
_rasterFadeDuration: number;
|
||||
_maxFadingAncestorLevels: number;
|
||||
|
||||
static maxUnderzooming: number;
|
||||
static maxOverzooming: number;
|
||||
@ -111,12 +105,11 @@ export class SourceCache extends Evented {
|
||||
this._tiles = {};
|
||||
this._cache = new TileCache(0, (tile) => this._unloadTile(tile));
|
||||
this._timers = {};
|
||||
this._cacheTimers = {};
|
||||
this._maxTileCacheSize = null;
|
||||
this._maxTileCacheZoomLevels = null;
|
||||
this._loadedParentTiles = {};
|
||||
this._rasterFadeDuration = 0;
|
||||
this._maxFadingAncestorLevels = 5;
|
||||
|
||||
this._coveredTiles = {};
|
||||
this._state = new SourceFeatureState();
|
||||
this._didEmitContent = false;
|
||||
this._updated = false;
|
||||
@ -223,7 +216,7 @@ export class SourceCache extends Evented {
|
||||
* Return all tile ids ordered with z-order, and cast to numbers
|
||||
*/
|
||||
getIds(): Array<string> {
|
||||
return (Object.values(this._tiles) as any).map((tile: Tile) => tile.tileID).sort(compareTileId).map(id => id.key);
|
||||
return Object.values(this._tiles).map(tile => tile.tileID).sort(compareTileId).map(id => id.key);
|
||||
}
|
||||
|
||||
getRenderableIds(symbolLayer?: boolean): Array<string> {
|
||||
@ -244,18 +237,24 @@ export class SourceCache extends Evented {
|
||||
}
|
||||
|
||||
hasRenderableParent(tileID: OverscaledTileID) {
|
||||
const parentTile = this.findLoadedParent(tileID, 0);
|
||||
if (parentTile) {
|
||||
return this._isIdRenderable(parentTile.tileID.key);
|
||||
const parentZ = tileID.overscaledZ - 1;
|
||||
if (parentZ >= this._source.minzoom) {
|
||||
const parentTile = this._getLoadedTile(tileID.scaledTo(parentZ));
|
||||
if (parentTile) {
|
||||
return this._isIdRenderable(parentTile.tileID.key);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_isIdRenderable(id: string, symbolLayer?: boolean) {
|
||||
return this._tiles[id] && this._tiles[id].hasData() &&
|
||||
!this._coveredTiles[id] && (symbolLayer || !this._tiles[id].holdingForFade());
|
||||
_isIdRenderable(id: string, symbolLayer: boolean = false) {
|
||||
return this._tiles[id]?.isRenderable(symbolLayer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload tiles in this source. If source data has changed, reload all tiles using a state of 'expired',
|
||||
* otherwise reload only non-errored tiles using state of 'reloading'.
|
||||
*/
|
||||
reload(sourceDataChanged?: boolean) {
|
||||
if (this._paused) {
|
||||
this._shouldReloadOnResume = true;
|
||||
@ -292,7 +291,7 @@ export class SourceCache extends Evented {
|
||||
}
|
||||
|
||||
_tileLoaded(tile: Tile, id: string, previousState: TileState) {
|
||||
tile.timeAdded = browser.now();
|
||||
tile.timeAdded = now();
|
||||
if (previousState === 'expired') tile.refreshedUponExpiration = true;
|
||||
this._setTileReloadTimer(id, tile);
|
||||
if (this.getSource().type === 'raster-dem' && tile.dem) this._backfillDEM(tile);
|
||||
@ -379,44 +378,50 @@ export class SourceCache extends Evented {
|
||||
*/
|
||||
|
||||
_retainLoadedChildren(
|
||||
targetTiles: { [_: string]: OverscaledTileID },
|
||||
retain: { [_: string]: OverscaledTileID }
|
||||
targetTiles: Record<string, OverscaledTileID>,
|
||||
retain: Record<string, OverscaledTileID>
|
||||
) {
|
||||
const targetTileIDs = Object.values(targetTiles);
|
||||
const loadedDescendents: { [_: string]: Tile[] } = this._getLoadedDescendents(targetTileIDs);
|
||||
const loadedDescendents: Record<string, Tile[]> = this._getLoadedDescendents(targetTileIDs);
|
||||
const incomplete: Record<string, OverscaledTileID> = {};
|
||||
|
||||
// retain the uppermost descendents of target tiles
|
||||
for (const targetID of targetTileIDs) {
|
||||
const descendentTiles = loadedDescendents[targetID.key];
|
||||
if (!descendentTiles) continue;
|
||||
|
||||
const targetTileMaxCoveringZoom = targetID.overscaledZ + SourceCache.maxUnderzooming;
|
||||
|
||||
// determine the topmost zoom (overscaledZ) in the set of descendent tiles. (i.e. zoom 4 tiles are topmost relative to zoom 5)
|
||||
let topmostZoom = Infinity;
|
||||
for (const tile of descendentTiles) {
|
||||
const zoom = tile.tileID.overscaledZ;
|
||||
if (zoom <= targetTileMaxCoveringZoom && zoom < topmostZoom) {
|
||||
topmostZoom = zoom;
|
||||
}
|
||||
const descendents = loadedDescendents[targetID.key];
|
||||
if (!descendents?.length) {
|
||||
incomplete[targetID.key] = targetID;
|
||||
continue;
|
||||
}
|
||||
|
||||
// retain all uppermost descendents (with the same overscaledZ) in the topmost zoom below the target tile
|
||||
if (topmostZoom !== Infinity) {
|
||||
for (const tile of descendentTiles) {
|
||||
if (tile.tileID.overscaledZ === topmostZoom) {
|
||||
retain[tile.tileID.key] = tile.tileID;
|
||||
}
|
||||
}
|
||||
// find descendents within the max covering zoom range
|
||||
const maxCoveringZoom = targetID.overscaledZ + SourceCache.maxUnderzooming;
|
||||
const candidates = descendents.filter(t => t.tileID.overscaledZ <= maxCoveringZoom);
|
||||
if (!candidates.length) {
|
||||
incomplete[targetID.key] = targetID;
|
||||
continue;
|
||||
}
|
||||
|
||||
// retain the uppermost descendents in the topmost zoom below the target tile
|
||||
const topZoom = Math.min(...candidates.map(t => t.tileID.overscaledZ));
|
||||
const topIDs = candidates.filter(t => t.tileID.overscaledZ === topZoom).map(t => t.tileID);
|
||||
for (const tileID of topIDs) {
|
||||
retain[tileID.key] = tileID;
|
||||
}
|
||||
|
||||
//determine if the retained generation is fully covered
|
||||
if (!this._areDescendentsComplete(topIDs, topZoom, targetID.overscaledZ)) {
|
||||
incomplete[targetID.key] = targetID;
|
||||
}
|
||||
}
|
||||
|
||||
return incomplete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return dictionary of qualified loaded descendents for each provided target tile id
|
||||
*/
|
||||
_getLoadedDescendents(targetTileIDs: OverscaledTileID[]) {
|
||||
const loadedDescendents: { [_: string]: Tile[] } = {};
|
||||
const loadedDescendents: Record<string, Tile[]> = {};
|
||||
|
||||
// enumerate tiles currently in this source and find the loaded descendents of each target tile
|
||||
for (const sourceKey in this._tiles) {
|
||||
@ -435,42 +440,30 @@ export class SourceCache extends Evented {
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a loaded parent of the given tile (up to minCoveringZoom)
|
||||
* Determine if tile ids fully cover the current generation.
|
||||
* - 1st generation: need 4 children or 1 overscaled child
|
||||
* - 2nd generation: need 16 children or 1 overscaled child
|
||||
*/
|
||||
findLoadedParent(tileID: OverscaledTileID, minCoveringZoom: number): Tile {
|
||||
if (tileID.key in this._loadedParentTiles) {
|
||||
const parent = this._loadedParentTiles[tileID.key];
|
||||
if (parent && parent.tileID.overscaledZ >= minCoveringZoom) {
|
||||
return parent;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
for (let z = tileID.overscaledZ - 1; z >= minCoveringZoom; z--) {
|
||||
const parentTileID = tileID.scaledTo(z);
|
||||
const tile = this._getLoadedTile(parentTileID);
|
||||
if (tile) {
|
||||
return tile;
|
||||
}
|
||||
_areDescendentsComplete(generationIDs: OverscaledTileID[], generationZ: number, ancestorZ: number) {
|
||||
//if overscaled, seeking 1 tile at generationZ, otherwise seeking a power of 4 for each descending Z
|
||||
if (generationIDs.length === 1 && generationIDs[0].isOverscaled()) {
|
||||
return generationIDs[0].overscaledZ === generationZ;
|
||||
} else {
|
||||
const expectedTiles = Math.pow(4, generationZ - ancestorZ); //4, 16, 64 (for first 3 gens)
|
||||
return expectedTiles === generationIDs.length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a loaded sibling of the given tile
|
||||
* Get a loaded tile currently in this source.
|
||||
* - loaded tiles exist in this._tiles - a cached tile is not a loaded tile
|
||||
*/
|
||||
findLoadedSibling(tileID: OverscaledTileID): Tile {
|
||||
// If a tile with this ID already exists, return it
|
||||
return this._getLoadedTile(tileID);
|
||||
}
|
||||
|
||||
_getLoadedTile(tileID: OverscaledTileID): Tile {
|
||||
_getLoadedTile(tileID: OverscaledTileID): Tile | null {
|
||||
const tile = this._tiles[tileID.key];
|
||||
if (tile && tile.hasData()) {
|
||||
if (tile?.hasData()) {
|
||||
return tile;
|
||||
}
|
||||
// TileCache ignores wrap in lookup.
|
||||
const cachedTile = this._cache.getByKey(tileID.wrapped().key);
|
||||
return cachedTile;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -517,7 +510,7 @@ export class SourceCache extends Evented {
|
||||
this._prevLng = lng;
|
||||
|
||||
if (wrapDelta) {
|
||||
const tiles: {[_: string]: Tile} = {};
|
||||
const tiles: Record<string, Tile> = {};
|
||||
for (const key in this._tiles) {
|
||||
const tile = this._tiles[key];
|
||||
tile.tileID = tile.tileID.unwrapTo(tile.tileID.wrap + wrapDelta);
|
||||
@ -525,102 +518,7 @@ export class SourceCache extends Evented {
|
||||
}
|
||||
this._tiles = tiles;
|
||||
|
||||
// Reset tile reload timers
|
||||
for (const id in this._timers) {
|
||||
clearTimeout(this._timers[id]);
|
||||
delete this._timers[id];
|
||||
}
|
||||
for (const id in this._tiles) {
|
||||
const tile = this._tiles[id];
|
||||
this._setTileReloadTimer(id, tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_updateCoveredAndRetainedTiles(
|
||||
retain: { [_: string]: OverscaledTileID },
|
||||
minCoveringZoom: number,
|
||||
idealTileIDs: OverscaledTileID[],
|
||||
terrain?: Terrain
|
||||
) {
|
||||
const tilesForFading: { [_: string]: OverscaledTileID } = {};
|
||||
const fadingTiles = {};
|
||||
const ids = Object.keys(retain);
|
||||
const now = browser.now();
|
||||
for (const id of ids) {
|
||||
const tileID = retain[id];
|
||||
|
||||
const tile = this._tiles[id];
|
||||
|
||||
// when fadeEndTime is 0, the tile is created but registerFadeDuration
|
||||
// has not been called, therefore must be kept in fadingTiles dictionary
|
||||
// for next round of rendering
|
||||
if (!tile || (tile.fadeEndTime !== 0 && tile.fadeEndTime <= now)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the tile is loaded but still fading in, find parents to cross-fade with it
|
||||
const parentTile = this.findLoadedParent(tileID, minCoveringZoom);
|
||||
const siblingTile = this.findLoadedSibling(tileID);
|
||||
const fadeTileRef = parentTile || siblingTile || null;
|
||||
if (fadeTileRef) {
|
||||
this._addTile(fadeTileRef.tileID);
|
||||
tilesForFading[fadeTileRef.tileID.key] = fadeTileRef.tileID;
|
||||
}
|
||||
|
||||
fadingTiles[id] = tileID;
|
||||
}
|
||||
|
||||
// for tiles that are still fading in, also find children to cross-fade with
|
||||
this._retainLoadedChildren(fadingTiles, retain);
|
||||
|
||||
for (const id in tilesForFading) {
|
||||
if (!retain[id]) {
|
||||
// If a tile is only needed for fading, mark it as covered so that it isn't rendered on it's own.
|
||||
this._coveredTiles[id] = true;
|
||||
retain[id] = tilesForFading[id];
|
||||
}
|
||||
}
|
||||
|
||||
// disable fading logic in terrain3D mode to avoid rendering two tiles on the same place
|
||||
if (terrain) {
|
||||
const idealRasterTileIDs: { [_: string]: OverscaledTileID } = {};
|
||||
const missingTileIDs: { [_: string]: OverscaledTileID } = {};
|
||||
for (const tileID of idealTileIDs) {
|
||||
if (this._tiles[tileID.key].hasData())
|
||||
idealRasterTileIDs[tileID.key] = tileID;
|
||||
else
|
||||
missingTileIDs[tileID.key] = tileID;
|
||||
}
|
||||
// search for a complete set of children for each missing tile
|
||||
for (const key in missingTileIDs) {
|
||||
const children = missingTileIDs[key].children(this._source.maxzoom);
|
||||
if (this._tiles[children[0].key] && this._tiles[children[1].key] && this._tiles[children[2].key] && this._tiles[children[3].key]) {
|
||||
idealRasterTileIDs[children[0].key] = retain[children[0].key] = children[0];
|
||||
idealRasterTileIDs[children[1].key] = retain[children[1].key] = children[1];
|
||||
idealRasterTileIDs[children[2].key] = retain[children[2].key] = children[2];
|
||||
idealRasterTileIDs[children[3].key] = retain[children[3].key] = children[3];
|
||||
delete missingTileIDs[key];
|
||||
}
|
||||
}
|
||||
// search for parent or sibling for each missing tile
|
||||
for (const key in missingTileIDs) {
|
||||
const tileID = missingTileIDs[key];
|
||||
const parentTile = this.findLoadedParent(tileID, this._source.minzoom);
|
||||
const siblingTile = this.findLoadedSibling(tileID);
|
||||
const fadeTileRef = parentTile || siblingTile || null;
|
||||
if (fadeTileRef) {
|
||||
idealRasterTileIDs[fadeTileRef.tileID.key] = retain[fadeTileRef.tileID.key] = fadeTileRef.tileID;
|
||||
// remove idealTiles which would be rendered twice
|
||||
for (const key in idealRasterTileIDs) {
|
||||
if (idealRasterTileIDs[key].isChildOf(fadeTileRef.tileID)) delete idealRasterTileIDs[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
// cover all tiles which are not needed
|
||||
for (const key in this._tiles) {
|
||||
if (!idealRasterTileIDs[key]) this._coveredTiles[key] = true;
|
||||
}
|
||||
this._resetTileReloadTimers();
|
||||
}
|
||||
}
|
||||
|
||||
@ -638,10 +536,6 @@ export class SourceCache extends Evented {
|
||||
this.updateCacheSize(transform);
|
||||
this.handleWrapJump(this.transform.center.lng);
|
||||
|
||||
// Covered is a list of retained tiles who's areas are fully covered by other,
|
||||
// better, retained tiles. They are not drawn separately.
|
||||
this._coveredTiles = {};
|
||||
|
||||
let idealTileIDs: OverscaledTileID[];
|
||||
|
||||
if (!this.used && !this.usedForTerrain) {
|
||||
@ -661,27 +555,13 @@ export class SourceCache extends Evented {
|
||||
});
|
||||
|
||||
if (this._source.hasTile) {
|
||||
idealTileIDs = idealTileIDs.filter((coord) => (this._source.hasTile as any)(coord));
|
||||
idealTileIDs = idealTileIDs.filter((coord) => this._source.hasTile(coord));
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the overzooming/underzooming amounts.
|
||||
const zoom = coveringZoomLevel(transform, this._source);
|
||||
const minCoveringZoom = Math.max(zoom - SourceCache.maxOverzooming, this._source.minzoom);
|
||||
|
||||
// When sourcecache is used for terrain also load parent tiles to avoid flickering when zooming out
|
||||
// When sourcecache is used for terrain also load parent tiles for complete rendering of 3d terrain levels
|
||||
if (this.usedForTerrain) {
|
||||
const parents = {};
|
||||
for (const tileID of idealTileIDs) {
|
||||
if (tileID.canonical.z > this._source.minzoom) {
|
||||
const parent = tileID.scaledTo(tileID.canonical.z - 1);
|
||||
parents[parent.key] = parent;
|
||||
// load very low zoom to calculate tile visibility in transform.coveringTiles and high zoomlevels correct
|
||||
const parent2 = tileID.scaledTo(Math.max(this._source.minzoom, Math.min(tileID.canonical.z, 5)));
|
||||
parents[parent2.key] = parent2;
|
||||
}
|
||||
}
|
||||
idealTileIDs = idealTileIDs.concat(Object.values(parents));
|
||||
idealTileIDs = this._addTerrainIdealTiles(idealTileIDs);
|
||||
}
|
||||
|
||||
const noPendingDataEmissions = idealTileIDs.length === 0 && !this._updated && this._didEmitContent;
|
||||
@ -695,37 +575,84 @@ export class SourceCache extends Evented {
|
||||
// Retain is a list of tiles that we shouldn't delete, even if they are not
|
||||
// the most ideal tile for the current viewport. This may include tiles like
|
||||
// parent or child tiles that are *already* loaded.
|
||||
const retain = this._updateRetainedTiles(idealTileIDs, zoom);
|
||||
const zoom: number = coveringZoomLevel(transform, this._source);
|
||||
const retain: Record<string, OverscaledTileID> = this._updateRetainedTiles(idealTileIDs, zoom);
|
||||
|
||||
if (isRasterType(this._source.type)) {
|
||||
this._updateCoveredAndRetainedTiles(retain, minCoveringZoom, idealTileIDs, terrain);
|
||||
// enable fading for raster source except when using terrain which doesn't currently support fading
|
||||
const isRaster = isRasterType(this._source.type);
|
||||
if (isRaster && this._rasterFadeDuration > 0 && !terrain) {
|
||||
this._updateFadingTiles(idealTileIDs, retain);
|
||||
}
|
||||
|
||||
for (const retainedId in retain) {
|
||||
// Make sure retained tiles always clear any existing fade holds
|
||||
// so that if they're removed again their fade timer starts fresh.
|
||||
this._tiles[retainedId].clearFadeHold();
|
||||
// clean up non-retained tiles in this source
|
||||
if (isRaster) {
|
||||
this._cleanUpRasterTiles(retain);
|
||||
} else {
|
||||
this._cleanUpVectorTiles(retain);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the tiles we don't need anymore.
|
||||
const remove = keysDifference(this._tiles, retain);
|
||||
for (const tileID of remove) {
|
||||
const tile = this._tiles[tileID];
|
||||
if (tile.hasSymbolBuckets && !tile.holdingForFade()) {
|
||||
tile.setHoldDuration(this.map._fadeDuration);
|
||||
} else if (!tile.hasSymbolBuckets || tile.symbolFadeFinished()) {
|
||||
this._removeTile(tileID);
|
||||
/**
|
||||
* Remove raster tiles that are no longer retained
|
||||
*/
|
||||
_cleanUpRasterTiles(retain: Record<string, OverscaledTileID>) {
|
||||
for (const key in this._tiles) {
|
||||
if (!retain[key]) {
|
||||
this._removeTile(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove vector tiles that are no longer retained and also not needed for symbol fading
|
||||
*/
|
||||
_cleanUpVectorTiles(retain: Record<string, OverscaledTileID>) {
|
||||
for (const key in this._tiles) {
|
||||
const tile = this._tiles[key];
|
||||
|
||||
// retained - clear fade hold so if it's removed again fade timer starts fresh.
|
||||
if (retain[key]) {
|
||||
tile.clearSymbolFadeHold();
|
||||
continue;
|
||||
}
|
||||
|
||||
// remove non-retained tiles without symbols
|
||||
if (!tile.hasSymbolBuckets) {
|
||||
this._removeTile(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
// for tile with symbols - hold for fade - then remove
|
||||
if (!tile.holdingForSymbolFade()) {
|
||||
tile.setSymbolHoldDuration(this.map._fadeDuration);
|
||||
} else if (tile.symbolFadeFinished()) {
|
||||
this._removeTile(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add ideal tiles needed for 3D terrain rendering
|
||||
*/
|
||||
_addTerrainIdealTiles(idealTileIDs: OverscaledTileID[]): OverscaledTileID[] {
|
||||
const ancestors = [];
|
||||
|
||||
for (const tileID of idealTileIDs) {
|
||||
if (tileID.canonical.z > this._source.minzoom) {
|
||||
const parent = tileID.scaledTo(tileID.canonical.z - 1);
|
||||
ancestors.push(parent);
|
||||
// load very low zoom to calculate tile visibility in transform.coveringTiles and high zoom levels correct
|
||||
const parent2 = tileID.scaledTo(Math.max(this._source.minzoom, Math.min(tileID.canonical.z, 5)));
|
||||
ancestors.push(parent2);
|
||||
}
|
||||
}
|
||||
|
||||
// Construct caches of loaded parents & siblings
|
||||
this._updateLoadedParentTileCache();
|
||||
this._updateLoadedSiblingTileCache();
|
||||
return idealTileIDs.concat(ancestors);
|
||||
}
|
||||
|
||||
releaseSymbolFadeTiles() {
|
||||
for (const id in this._tiles) {
|
||||
if (this._tiles[id].holdingForFade()) {
|
||||
if (this._tiles[id].holdingForSymbolFade()) {
|
||||
this._removeTile(id);
|
||||
}
|
||||
}
|
||||
@ -736,64 +663,34 @@ export class SourceCache extends Evented {
|
||||
* children so they can be displayed as substitutes pending load of each ideal tile (to reduce flickering).
|
||||
* If no loaded children are available, fallback to seeking loaded parents as an alternative substitute.
|
||||
*/
|
||||
_updateRetainedTiles(idealTileIDs: Array<OverscaledTileID>, zoom: number): {[_: string]: OverscaledTileID} {
|
||||
const retain: {[_: string]: OverscaledTileID} = {};
|
||||
const checked: {[_: string]: boolean} = {};
|
||||
_updateRetainedTiles(idealTileIDs: Array<OverscaledTileID>, zoom: number): Record<string, OverscaledTileID> {
|
||||
const retain: Record<string, OverscaledTileID> = {};
|
||||
const checked: Record<string, boolean> = {};
|
||||
const minCoveringZoom = Math.max(zoom - SourceCache.maxOverzooming, this._source.minzoom);
|
||||
|
||||
const missingTiles = {};
|
||||
for (const tileID of idealTileIDs) {
|
||||
const tile = this._addTile(tileID);
|
||||
let missingIdealTiles = {};
|
||||
for (const idealID of idealTileIDs) {
|
||||
const idealTile = this._addTile(idealID);
|
||||
|
||||
// retain the tile even if it's not loaded because it's an ideal tile.
|
||||
retain[tileID.key] = tileID;
|
||||
retain[idealID.key] = idealID;
|
||||
|
||||
if (tile.hasData()) continue;
|
||||
|
||||
if (zoom < this._source.maxzoom) {
|
||||
// save missing tiles that potentially have loaded children
|
||||
missingTiles[tileID.key] = tileID;
|
||||
if (!idealTile.hasData()) {
|
||||
missingIdealTiles[idealID.key] = idealID;
|
||||
}
|
||||
}
|
||||
|
||||
this._retainLoadedChildren(missingTiles, retain);
|
||||
missingIdealTiles = this._retainLoadedChildren(missingIdealTiles, retain);
|
||||
|
||||
for (const tileID of idealTileIDs) {
|
||||
let tile = this._tiles[tileID.key];
|
||||
|
||||
if (tile.hasData()) continue;
|
||||
|
||||
// The tile we require is not yet loaded or does not exist;
|
||||
// Attempt to find children that fully cover it.
|
||||
|
||||
if (zoom + 1 > this._source.maxzoom) {
|
||||
// We're looking for an overzoomed child tile.
|
||||
const childCoord = tileID.children(this._source.maxzoom)[0];
|
||||
const childTile = this.getTile(childCoord);
|
||||
if (!!childTile && childTile.hasData()) {
|
||||
retain[childCoord.key] = childCoord;
|
||||
continue; // tile is covered by overzoomed child
|
||||
}
|
||||
} else {
|
||||
// check if all 4 immediate children are loaded (i.e. the missing ideal tile is covered)
|
||||
const children = tileID.children(this._source.maxzoom);
|
||||
|
||||
if (children.length === 4 &&
|
||||
retain[children[0].key] &&
|
||||
retain[children[1].key] &&
|
||||
retain[children[2].key] &&
|
||||
retain[children[3].key]) continue; // tile is covered by children
|
||||
|
||||
if (children.length === 1 &&
|
||||
retain[children[0].key]) continue; // tile is covered by overscaled child
|
||||
}
|
||||
|
||||
// We couldn't find child tiles that entirely cover the ideal tile; look for parents now.
|
||||
// for remaining missing tiles with incomplete child coverage, seek a loaded parent tile
|
||||
for (const idealKey in missingIdealTiles) {
|
||||
const tileID = missingIdealTiles[idealKey];
|
||||
let tile = this._tiles[idealKey];
|
||||
|
||||
// As we ascend up the tile pyramid of the ideal tile, we check whether the parent
|
||||
// tile has been previously requested (and errored because we only loop over tiles with no data)
|
||||
// in order to determine if we need to request its parent.
|
||||
let parentWasRequested = tile.wasRequested();
|
||||
let parentWasRequested = tile?.wasRequested();
|
||||
|
||||
for (let overscaledZ = tileID.overscaledZ - 1; overscaledZ >= minCoveringZoom; --overscaledZ) {
|
||||
const parentId = tileID.scaledTo(overscaledZ);
|
||||
@ -822,59 +719,181 @@ export class SourceCache extends Evented {
|
||||
return retain;
|
||||
}
|
||||
|
||||
_updateLoadedParentTileCache() {
|
||||
this._loadedParentTiles = {};
|
||||
/**
|
||||
* Designate fading bases and parents using a many-to-one relationship where the lower children fade in/out
|
||||
* with their parents. Raster shaders are not currently designed for a one-to-many fade relationship.
|
||||
*
|
||||
* Tiles that are candidates for fading out must be loaded and rendered tiles, as loading a tile to then
|
||||
* fade it out would not appear smoothly. The first source of truth for tile fading always starts at the
|
||||
* ideal tile, which continually changes on map adjustment. The state of the previously rendered ideal
|
||||
* tile plane indicates which direction to fade each part of the newer ideal plane (with varying z).
|
||||
*
|
||||
* For a pitched map, the back of the map can have decreasing zooms while the front can have increasing zooms.
|
||||
* Fade logic must therefore adapt dynamically based on the previously rendered ideal tile set.
|
||||
*/
|
||||
_updateFadingTiles(idealTileIDs: OverscaledTileID[], retain: Record<string, OverscaledTileID>) {
|
||||
const currentTime: number = now();
|
||||
const edgeTileIDs: Set<OverscaledTileID> = getEdgeTiles(idealTileIDs);
|
||||
|
||||
for (const tileKey in this._tiles) {
|
||||
const path = [];
|
||||
let parentTile: Tile;
|
||||
let currentId = this._tiles[tileKey].tileID;
|
||||
for (const idealID of idealTileIDs) {
|
||||
const idealTile = this._tiles[idealID.key];
|
||||
|
||||
// Find the closest loaded ancestor by traversing the tile tree towards the root and
|
||||
// caching results along the way
|
||||
while (currentId.overscaledZ > 0) {
|
||||
|
||||
// Do we have a cached result from previous traversals?
|
||||
if (currentId.key in this._loadedParentTiles) {
|
||||
parentTile = this._loadedParentTiles[currentId.key];
|
||||
break;
|
||||
}
|
||||
|
||||
path.push(currentId.key);
|
||||
|
||||
// Is the parent loaded?
|
||||
const parentId = currentId.scaledTo(currentId.overscaledZ - 1);
|
||||
parentTile = this._getLoadedTile(parentId);
|
||||
if (parentTile) {
|
||||
break;
|
||||
}
|
||||
|
||||
currentId = parentId;
|
||||
// reset any previously departing(ed) tiles that are now ideal tiles
|
||||
if (idealTile.fadingDirection === FadingDirections.Departing || idealTile.fadeOpacity === 0) {
|
||||
idealTile.resetFadeLogic();
|
||||
}
|
||||
|
||||
// Cache the result of this traversal to all newly visited tiles
|
||||
for (const key of path) {
|
||||
this._loadedParentTiles[key] = parentTile;
|
||||
}
|
||||
const parentIsFader = this._updateFadingAncestor(idealTile, retain, currentTime);
|
||||
if (parentIsFader) continue;
|
||||
|
||||
const childIsFader = this._updateFadingDescendents(idealTile, retain, currentTime);
|
||||
if (childIsFader) continue;
|
||||
|
||||
const edgeIsFader = this._updateFadingEdge(idealTile, edgeTileIDs, currentTime);
|
||||
if (edgeIsFader) continue;
|
||||
|
||||
// for all remaining non-fading ideal tiles reset the fade logic
|
||||
idealTile.resetFadeLogic();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the cache of loaded sibling tiles
|
||||
* Many-to-one cross-fade. Set 4 ideal tiles as the fading base for a rendered parent tile
|
||||
* as the fading parent. Here the parent is fading out and the ideal tile is fading in.
|
||||
*
|
||||
* Sibling tiles are tiles that share the same zoom level and
|
||||
* x/y position but have different wrap values
|
||||
* Maintaining sibling tile cache allows fading from old to new tiles
|
||||
* of the same position and zoom level
|
||||
* Parent tile - fading out ■ -- Fading Parent
|
||||
* ┌──────────────┬──────┴───────┬──────────────┐
|
||||
* Ideal tiles - fading in ■ ■ ■ ■ -- Base Role = Incoming
|
||||
* ┌───┬─┴─┬───┐ ┌───┬─┴─┬───┐ ┌───┬─┴─┬───┐ ┌───┬─┴─┬───┐
|
||||
* ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
|
||||
*/
|
||||
_updateLoadedSiblingTileCache() {
|
||||
this._loadedSiblingTiles = {};
|
||||
_updateFadingAncestor(idealTile: Tile, retain: Record<string, OverscaledTileID>, now: number): boolean {
|
||||
if (!idealTile.hasData()) return false;
|
||||
|
||||
for (const tileKey in this._tiles) {
|
||||
const currentId = this._tiles[tileKey].tileID;
|
||||
const siblingTile: Tile = this._getLoadedTile(currentId);
|
||||
this._loadedSiblingTiles[currentId.key] = siblingTile;
|
||||
const {tileID: idealID, fadingRole, fadingDirection, fadingParentID} = idealTile;
|
||||
// ideal tile already has fading parent - retain and return
|
||||
if (fadingRole === FadingRoles.Base && fadingDirection === FadingDirections.Incoming && fadingParentID) {
|
||||
retain[fadingParentID.key] = fadingParentID;
|
||||
return true;
|
||||
}
|
||||
|
||||
// find a loaded parent tile to fade with the ideal tile
|
||||
const minAncestorZ = Math.max(idealID.overscaledZ - this._maxFadingAncestorLevels, this._source.minzoom);
|
||||
for (let ancestorZ = idealID.overscaledZ - 1; ancestorZ >= minAncestorZ; ancestorZ--) {
|
||||
const ancestorID = idealID.scaledTo(ancestorZ);
|
||||
const ancestorTile = this._getLoadedTile(ancestorID);
|
||||
if (!ancestorTile) continue;
|
||||
|
||||
// ideal tile (base) is fading in
|
||||
idealTile.setCrossFadeLogic({
|
||||
fadingRole: FadingRoles.Base,
|
||||
fadingDirection: FadingDirections.Incoming,
|
||||
fadingParentID: ancestorTile.tileID, // fading out
|
||||
fadeEndTime: now + this._rasterFadeDuration
|
||||
});
|
||||
// ancestor tile (parent) is fading out
|
||||
ancestorTile.setCrossFadeLogic({
|
||||
fadingRole: FadingRoles.Parent,
|
||||
fadingDirection: FadingDirections.Departing,
|
||||
fadeEndTime: now + this._rasterFadeDuration
|
||||
});
|
||||
|
||||
retain[ancestorID.key] = ancestorID;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Many-to-one cross-fade. Search descendents of ideal tiles as the fading base with the ideal tile
|
||||
* as the fading parent. Here the children are fading out and the ideal tile is fading in.
|
||||
*
|
||||
* ■
|
||||
* ┌──────────────┬──────┴───────┬──────────────┐
|
||||
* Ideal tiles - fading in ■ ■ ■ ■ -- Fading Parent
|
||||
* ┌───┬─┴─┬───┐ ┌───┬─┴─┬───┐ ┌───┬─┴─┬───┐ ┌───┬─┴─┬───┐
|
||||
* Child tiles - fading out ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ -- Base Role = Departing
|
||||
*
|
||||
* Try direct children first. If none found, try grandchildren. Stops at the first generation that provides a fader.
|
||||
*/
|
||||
_updateFadingDescendents(idealTile: Tile, retain: Record<string, OverscaledTileID>, now: number): boolean {
|
||||
if (!idealTile.hasData()) return false;
|
||||
|
||||
// search first level of descendents (4 tiles)
|
||||
const idealChildren = idealTile.tileID.children(this._source.maxzoom);
|
||||
let hasFader = this._updateFadingChildren(idealTile, idealChildren, retain, now);
|
||||
if (hasFader) return true;
|
||||
|
||||
// search second level of descendents (16 tiles)
|
||||
for (const childID of idealChildren) {
|
||||
const grandChildIDs = childID.children(this._source.maxzoom);
|
||||
if (this._updateFadingChildren(idealTile, grandChildIDs, retain, now)) {
|
||||
hasFader = true;
|
||||
}
|
||||
}
|
||||
|
||||
return hasFader;
|
||||
}
|
||||
|
||||
_updateFadingChildren(idealTile: Tile, childIDs: OverscaledTileID[], retain: Record<string, OverscaledTileID>, now: number): boolean {
|
||||
if (childIDs[0].overscaledZ >= this._source.maxzoom) return false;
|
||||
let foundFader = false;
|
||||
|
||||
// find loaded child tiles to fade with the ideal tile
|
||||
for (const childID of childIDs) {
|
||||
const childTile = this._getLoadedTile(childID);
|
||||
if (!childTile) continue;
|
||||
|
||||
const {fadingRole, fadingDirection, fadingParentID} = childTile;
|
||||
if (fadingRole !== FadingRoles.Base || fadingDirection !== FadingDirections.Departing || !fadingParentID) {
|
||||
// child tile (base) is fading out
|
||||
childTile.setCrossFadeLogic({
|
||||
fadingRole: FadingRoles.Base,
|
||||
fadingDirection: FadingDirections.Departing,
|
||||
fadingParentID: idealTile.tileID,
|
||||
fadeEndTime: now + this._rasterFadeDuration
|
||||
});
|
||||
// ideal tile (parent) is fading in
|
||||
idealTile.setCrossFadeLogic({
|
||||
fadingRole: FadingRoles.Parent,
|
||||
fadingDirection: FadingDirections.Incoming,
|
||||
fadeEndTime: now + this._rasterFadeDuration
|
||||
});
|
||||
}
|
||||
|
||||
retain[childID.key] = childID;
|
||||
foundFader = true;
|
||||
}
|
||||
|
||||
return foundFader;
|
||||
}
|
||||
|
||||
/**
|
||||
* One-to-one self fading for unloaded edge tiles (for panning sideways on map). for loading tiles over gaps it feels
|
||||
* more natural for them to fade in, however if they are already loaded/cached then there is no need to fade as map will
|
||||
* look cohesive with no gaps. Note that draw_raster determines fade priority, as many-to-one fade supersedes edge fading.
|
||||
*/
|
||||
_updateFadingEdge(idealTile: Tile, edgeTileIDs: Set<OverscaledTileID>, now: number): boolean {
|
||||
const idealID: OverscaledTileID = idealTile.tileID;
|
||||
|
||||
// tile is already self fading
|
||||
if (idealTile.selfFading) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// fading not needed for tiles that are already loaded
|
||||
if (idealTile.hasData()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// enable fading for loading edges with no data
|
||||
if (edgeTileIDs.has(idealID)) {
|
||||
const fadeEndTime = now + this._rasterFadeDuration;
|
||||
idealTile.setSelfFadeLogic(fadeEndTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -887,15 +906,15 @@ export class SourceCache extends Evented {
|
||||
|
||||
tile = this._cache.getAndRemove(tileID);
|
||||
if (tile) {
|
||||
//reset fading logic to remove stale fading data from cache
|
||||
tile.resetFadeLogic();
|
||||
|
||||
// set timer for the reloading of the tile upon expiration
|
||||
this._setTileReloadTimer(tileID.key, tile);
|
||||
|
||||
// set the tileID because the cached tile could have had a different wrap value
|
||||
tile.tileID = tileID;
|
||||
this._state.initializeTileState(tile, this.map ? this.map.painter : null);
|
||||
if (this._cacheTimers[tileID.key]) {
|
||||
clearTimeout(this._cacheTimers[tileID.key]);
|
||||
delete this._cacheTimers[tileID.key];
|
||||
this._setTileReloadTimer(tileID.key, tile);
|
||||
}
|
||||
}
|
||||
|
||||
const cached = tile;
|
||||
@ -914,18 +933,38 @@ export class SourceCache extends Evented {
|
||||
return tile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a timeout to reload the tile after it expires
|
||||
*/
|
||||
_setTileReloadTimer(id: string, tile: Tile) {
|
||||
if (id in this._timers) {
|
||||
clearTimeout(this._timers[id]);
|
||||
delete this._timers[id];
|
||||
}
|
||||
this._clearTileReloadTimer(id);
|
||||
|
||||
const expiryTimeout = tile.getExpiryTimeout();
|
||||
if (expiryTimeout) {
|
||||
this._timers[id] = setTimeout(() => {
|
||||
const reload = () => {
|
||||
this._reloadTile(id, 'expired');
|
||||
delete this._timers[id];
|
||||
}, expiryTimeout);
|
||||
};
|
||||
this._timers[id] = setTimeout(reload, expiryTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
_clearTileReloadTimer(id: string) {
|
||||
const timeout = this._timers[id];
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
delete this._timers[id];
|
||||
}
|
||||
}
|
||||
|
||||
_resetTileReloadTimers() {
|
||||
for (const id in this._timers) {
|
||||
clearTimeout(this._timers[id]);
|
||||
delete this._timers[id];
|
||||
}
|
||||
for (const id in this._tiles) {
|
||||
const tile = this._tiles[id];
|
||||
this._setTileReloadTimer(id, tile);
|
||||
}
|
||||
}
|
||||
|
||||
@ -953,10 +992,7 @@ export class SourceCache extends Evented {
|
||||
|
||||
tile.uses--;
|
||||
delete this._tiles[id];
|
||||
if (this._timers[id]) {
|
||||
clearTimeout(this._timers[id]);
|
||||
delete this._timers[id];
|
||||
}
|
||||
this._clearTileReloadTimer(id);
|
||||
|
||||
if (tile.uses > 0)
|
||||
return;
|
||||
@ -970,24 +1006,28 @@ export class SourceCache extends Evented {
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
/** @internal
|
||||
* Handles incoming source data messages (i.e. after the source has been updated via a worker that has fired
|
||||
* to map.ts data event). For sources with mutable data, the 'content' event fires when the underlying data
|
||||
* to a source has changed. (i.e. GeoJSONSource.setData and ImageSource.setCoordinates)
|
||||
*/
|
||||
private _dataHandler(e: MapSourceDataEvent) {
|
||||
if (e.dataType !== 'source') return;
|
||||
|
||||
const eventSourceDataType = e.sourceDataType;
|
||||
if (e.dataType === 'source' && eventSourceDataType === 'metadata') {
|
||||
if (e.sourceDataType === 'metadata') {
|
||||
this._sourceLoaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// for sources with mutable data, this event fires when the underlying data
|
||||
// to a source is changed. (i.e. GeoJSONSource.setData and ImageSource.serCoordinates)
|
||||
if (this._sourceLoaded && !this._paused && e.dataType === 'source' && eventSourceDataType === 'content') {
|
||||
this.reload(e.sourceDataChanged);
|
||||
if (this.transform) {
|
||||
this.update(this.transform, this.terrain);
|
||||
}
|
||||
|
||||
this._didEmitContent = true;
|
||||
if (e.sourceDataType !== 'content' || !this._sourceLoaded || this._paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.reload(e.sourceDataChanged);
|
||||
if (this.transform) {
|
||||
this.update(this.transform, this.terrain);
|
||||
}
|
||||
this._didEmitContent = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1030,7 +1070,7 @@ export class SourceCache extends Evented {
|
||||
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
const tile = this._tiles[ids[i]];
|
||||
if (tile.holdingForFade()) {
|
||||
if (tile.holdingForSymbolFade()) {
|
||||
// Tiles held for fading are covered by tiles that are closer to ideal
|
||||
continue;
|
||||
}
|
||||
@ -1098,11 +1138,11 @@ export class SourceCache extends Evented {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isRasterType(this._source.type)) {
|
||||
const now = browser.now();
|
||||
if (isRasterType(this._source.type) && this._rasterFadeDuration > 0) {
|
||||
const currentTime = now();
|
||||
for (const id in this._tiles) {
|
||||
const tile = this._tiles[id];
|
||||
if (tile.fadeEndTime >= now) {
|
||||
if (tile.fadeEndTime >= currentTime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1111,6 +1151,10 @@ export class SourceCache extends Evented {
|
||||
return false;
|
||||
}
|
||||
|
||||
setRasterFadeDuration(fadeDuration: number) {
|
||||
this._rasterFadeDuration = fadeDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a particular state for a feature
|
||||
*/
|
||||
|
||||
6
node_modules/maplibre-gl/src/source/terrain_source_cache.ts
generated
vendored
6
node_modules/maplibre-gl/src/source/terrain_source_cache.ts
generated
vendored
@ -6,7 +6,7 @@ import {Evented} from '../util/evented';
|
||||
import type {ITransform} from '../geo/transform_interface';
|
||||
import type {SourceCache} from '../source/source_cache';
|
||||
import {type Terrain} from '../render/terrain';
|
||||
import {browser} from '../util/browser';
|
||||
import {now} from '../util/time_control';
|
||||
import {coveringTiles} from '../geo/projection/covering_tiles';
|
||||
import {createMat4f64} from '../util/util';
|
||||
import {type CanonicalTileRange} from './image_source';
|
||||
@ -57,7 +57,7 @@ export class TerrainSourceCache extends Evented {
|
||||
/**
|
||||
* used to determine whether depth & coord framebuffers need updating
|
||||
*/
|
||||
_lastTilesetChange: number = browser.now();
|
||||
_lastTilesetChange: number = now();
|
||||
|
||||
constructor(sourceCache: SourceCache) {
|
||||
super();
|
||||
@ -103,7 +103,7 @@ export class TerrainSourceCache extends Evented {
|
||||
tileID.terrainRttPosMatrix32f = new Float64Array(16) as any;
|
||||
mat4.ortho(tileID.terrainRttPosMatrix32f, 0, EXTENT, EXTENT, 0, 0, 1);
|
||||
this._tiles[tileID.key] = new Tile(tileID, this.tileSize);
|
||||
this._lastTilesetChange = browser.now();
|
||||
this._lastTilesetChange = now();
|
||||
}
|
||||
}
|
||||
// free unused tiles
|
||||
|
||||
82
node_modules/maplibre-gl/src/source/tile.ts
generated
vendored
82
node_modules/maplibre-gl/src/source/tile.ts
generated
vendored
@ -6,7 +6,7 @@ import {featureFilter} from '@maplibre/maplibre-gl-style-spec';
|
||||
import {SymbolBucket} from '../data/bucket/symbol_bucket';
|
||||
import {CollisionBoxArray} from '../data/array_types.g';
|
||||
import {Texture} from '../render/texture';
|
||||
import {browser} from '../util/browser';
|
||||
import {now} from '../util/time_control';
|
||||
import {toEvaluationFeature} from '../data/evaluation_feature';
|
||||
import {EvaluationParameters} from '../style/evaluation_parameters';
|
||||
import {type SourceFeatureState} from '../source/source_state';
|
||||
@ -33,6 +33,7 @@ import type {VectorTileLayer} from '@mapbox/vector-tile';
|
||||
import type {ExpiryData} from '../util/ajax';
|
||||
import type {QueryRenderedFeaturesOptionsStrict, QuerySourceFeatureOptionsStrict} from './query_features';
|
||||
import type {FeatureIndex, QueryResults} from '../data/feature_index';
|
||||
import type {DashEntry} from '../render/line_atlas';
|
||||
/**
|
||||
* The tile's state, can be:
|
||||
*
|
||||
@ -45,6 +46,21 @@ import type {FeatureIndex, QueryResults} from '../data/feature_index';
|
||||
*/
|
||||
export type TileState = 'loading' | 'loaded' | 'reloading' | 'unloaded' | 'errored' | 'expired';
|
||||
|
||||
/** @internal */
|
||||
type CrossFadeArgs = {
|
||||
fadingRole: FadingRoles;
|
||||
fadingDirection: FadingDirections;
|
||||
fadingParentID?: OverscaledTileID;
|
||||
fadeEndTime: number;
|
||||
};
|
||||
|
||||
export enum FadingRoles {
|
||||
Base, Parent
|
||||
}
|
||||
export enum FadingDirections {
|
||||
Departing, Incoming
|
||||
}
|
||||
|
||||
/**
|
||||
* A tile object is the combination of a Coordinate, which defines
|
||||
* its place, as well as a unique ID and data tracking for its content
|
||||
@ -59,13 +75,19 @@ export class Tile {
|
||||
latestRawTileData: ArrayBuffer;
|
||||
imageAtlas: ImageAtlas;
|
||||
imageAtlasTexture: Texture;
|
||||
dashPositions: {[_: string]: DashEntry};
|
||||
glyphAtlasImage: AlphaImage;
|
||||
glyphAtlasTexture: Texture;
|
||||
expirationTime: any;
|
||||
expiredRequestCount: number;
|
||||
state: TileState;
|
||||
fadingRole: FadingRoles;
|
||||
fadingDirection: FadingDirections;
|
||||
fadingParentID: OverscaledTileID;
|
||||
selfFading: boolean;
|
||||
timeAdded: number = 0;
|
||||
fadeEndTime: number = 0;
|
||||
fadeOpacity: number = 1;
|
||||
collisionBoxArray: CollisionBoxArray;
|
||||
redoWhenDone: boolean;
|
||||
showCollisionBoxes: boolean;
|
||||
@ -122,16 +144,47 @@ export class Tile {
|
||||
this.state = 'loading';
|
||||
}
|
||||
|
||||
registerFadeDuration(duration: number) {
|
||||
const fadeEndTime = duration + this.timeAdded;
|
||||
isRenderable(symbolLayer: boolean): boolean {
|
||||
return (
|
||||
this.hasData() &&
|
||||
(!this.fadeEndTime || this.fadeOpacity > 0) && // raster fading
|
||||
(symbolLayer || !this.holdingForSymbolFade()) // symbol fading
|
||||
);
|
||||
}
|
||||
|
||||
if (fadeEndTime < this.fadeEndTime) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
* Many-to-one crossfade between a base tile and parent/ancestor tile (when zooming)
|
||||
*/
|
||||
setCrossFadeLogic({fadingRole, fadingDirection, fadingParentID, fadeEndTime}: CrossFadeArgs) {
|
||||
this.resetFadeLogic();
|
||||
|
||||
this.fadingRole = fadingRole;
|
||||
this.fadingDirection = fadingDirection;
|
||||
this.fadingParentID = fadingParentID;
|
||||
this.fadeEndTime = fadeEndTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Self fading for edge tiles (when panning map)
|
||||
*/
|
||||
setSelfFadeLogic(fadeEndTime: number) {
|
||||
this.resetFadeLogic();
|
||||
this.selfFading = true;
|
||||
this.fadeEndTime = fadeEndTime;
|
||||
}
|
||||
|
||||
resetFadeLogic() {
|
||||
this.fadingRole = null;
|
||||
this.fadingDirection = null;
|
||||
this.fadingParentID = null;
|
||||
this.selfFading = false;
|
||||
|
||||
this.timeAdded = now();
|
||||
this.fadeEndTime = 0;
|
||||
this.fadeOpacity = 1;
|
||||
}
|
||||
|
||||
wasRequested() {
|
||||
return this.state === 'errored' || this.state === 'loaded' || this.state === 'reloading';
|
||||
}
|
||||
@ -218,6 +271,7 @@ export class Tile {
|
||||
if (data.glyphAtlasImage) {
|
||||
this.glyphAtlasImage = data.glyphAtlasImage;
|
||||
}
|
||||
this.dashPositions = data.dashPositions;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -241,6 +295,10 @@ export class Tile {
|
||||
this.glyphAtlasTexture.destroy();
|
||||
}
|
||||
|
||||
if (this.dashPositions) {
|
||||
this.dashPositions = null;
|
||||
}
|
||||
|
||||
this.latestFeatureIndex = null;
|
||||
this.state = 'unloaded';
|
||||
}
|
||||
@ -423,7 +481,7 @@ export class Tile {
|
||||
const sourceLayerStates = states[sourceLayerId];
|
||||
if (!sourceLayer || !sourceLayerStates || Object.keys(sourceLayerStates).length === 0) continue;
|
||||
|
||||
bucket.update(sourceLayerStates, sourceLayer, this.imageAtlas && this.imageAtlas.patternPositions || {});
|
||||
bucket.update(sourceLayerStates, sourceLayer, this.imageAtlas && this.imageAtlas.patternPositions || {}, this.dashPositions || {});
|
||||
const layer = painter && painter.style && painter.style.getLayer(id);
|
||||
if (layer) {
|
||||
this.queryPadding = Math.max(this.queryPadding, layer.queryRadius(bucket));
|
||||
@ -431,20 +489,20 @@ export class Tile {
|
||||
}
|
||||
}
|
||||
|
||||
holdingForFade(): boolean {
|
||||
holdingForSymbolFade(): boolean {
|
||||
return this.symbolFadeHoldUntil !== undefined;
|
||||
}
|
||||
|
||||
symbolFadeFinished(): boolean {
|
||||
return !this.symbolFadeHoldUntil || this.symbolFadeHoldUntil < browser.now();
|
||||
return !this.symbolFadeHoldUntil || this.symbolFadeHoldUntil < now();
|
||||
}
|
||||
|
||||
clearFadeHold() {
|
||||
clearSymbolFadeHold() {
|
||||
this.symbolFadeHoldUntil = undefined;
|
||||
}
|
||||
|
||||
setHoldDuration(duration: number) {
|
||||
this.symbolFadeHoldUntil = browser.now() + duration;
|
||||
setSymbolHoldDuration(duration: number) {
|
||||
this.symbolFadeHoldUntil = now() + duration;
|
||||
}
|
||||
|
||||
setDependencies(namespace: string, dependencies: Array<string>) {
|
||||
|
||||
16
node_modules/maplibre-gl/src/source/tile_id.ts
generated
vendored
16
node_modules/maplibre-gl/src/source/tile_id.ts
generated
vendored
@ -33,7 +33,9 @@ export class CanonicalTileID implements ICanonicalTileID {
|
||||
return this.z === id.z && this.x === id.x && this.y === id.y;
|
||||
}
|
||||
|
||||
// given a list of urls, choose a url template and return a tile URL
|
||||
/**
|
||||
* given a list of urls, choose a url template and return a tile URL
|
||||
*/
|
||||
url(urls: Array<string>, pixelRatio: number, scheme?: string | null) {
|
||||
const bbox = getTileBBox(this.x, this.y, this.z);
|
||||
const quadkey = getQuadkey(this.z, this.x, this.y);
|
||||
@ -113,6 +115,14 @@ export class OverscaledTileID {
|
||||
return this.overscaledZ === id.overscaledZ && this.wrap === id.wrap && this.canonical.equals(id.canonical);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new `OverscaledTileID` representing the tile at the target zoom level.
|
||||
* When targetZ is greater than the current canonical z, the canonical coordinates are unchanged.
|
||||
* When targetZ is less than the current canonical z, the canonical coordinates are updated.
|
||||
* @param targetZ - the zoom level to scale to. Must be less than or equal to this.overscaledZ
|
||||
* @returns a new OverscaledTileID representing the tile at the target zoom level
|
||||
* @throws if targetZ is greater than this.overscaledZ
|
||||
*/
|
||||
scaledTo(targetZ: number) {
|
||||
if (targetZ > this.overscaledZ) throw new Error(`targetZ > this.overscaledZ; targetZ = ${targetZ}; overscaledZ = ${this.overscaledZ}`);
|
||||
const zDifference = this.canonical.z - targetZ;
|
||||
@ -123,6 +133,10 @@ export class OverscaledTileID {
|
||||
}
|
||||
}
|
||||
|
||||
isOverscaled() {
|
||||
return (this.overscaledZ > this.canonical.z);
|
||||
}
|
||||
|
||||
/*
|
||||
* calculateScaledKey is an optimization:
|
||||
* when withWrap == true, implements the same as this.scaledTo(z).key,
|
||||
|
||||
2
node_modules/maplibre-gl/src/source/worker_source.ts
generated
vendored
2
node_modules/maplibre-gl/src/source/worker_source.ts
generated
vendored
@ -14,6 +14,7 @@ import type {RemoveSourceParams} from '../util/actor_messages';
|
||||
import type {IActor} from '../util/actor';
|
||||
import type {StyleLayerIndex} from '../style/style_layer_index';
|
||||
import type {SubdivisionGranularitySetting} from '../render/subdivision_granularity_settings';
|
||||
import type {DashEntry} from '../render/line_atlas';
|
||||
|
||||
/**
|
||||
* Parameters to identify a tile
|
||||
@ -59,6 +60,7 @@ export type WorkerDEMTileParameters = TileParameters & {
|
||||
export type WorkerTileResult = ExpiryData & {
|
||||
buckets: Array<Bucket>;
|
||||
imageAtlas: ImageAtlas;
|
||||
dashPositions: Record<string, DashEntry>;
|
||||
glyphAtlasImage: AlphaImage;
|
||||
featureIndex: FeatureIndex;
|
||||
collisionBoxArray: CollisionBoxArray;
|
||||
|
||||
32
node_modules/maplibre-gl/src/source/worker_tile.test.ts
generated
vendored
32
node_modules/maplibre-gl/src/source/worker_tile.test.ts
generated
vendored
@ -11,6 +11,7 @@ import {type PossiblyEvaluated} from '../style/properties';
|
||||
import {Color} from '@maplibre/maplibre-gl-style-spec';
|
||||
import {type CirclePaintProps, type CirclePaintPropsPossiblyEvaluated} from '../style/style_layer/circle_style_layer_properties.g';
|
||||
import {type SymbolLayoutProps, type SymbolLayoutPropsPossiblyEvaluated} from '../style/style_layer/symbol_style_layer_properties.g';
|
||||
import {MessageType} from '../util/actor_messages';
|
||||
|
||||
function createWorkerTile(params?: {globalState?: Record<string, any>}): WorkerTile {
|
||||
return new WorkerTile({
|
||||
@ -175,6 +176,14 @@ describe('worker tile', () => {
|
||||
'text-font': ['StandardFont-Bold'],
|
||||
'text-field': '{name}'
|
||||
}
|
||||
}, {
|
||||
id: 'line-layer',
|
||||
type: 'line',
|
||||
source: 'source',
|
||||
'source-layer': 'test',
|
||||
paint: {
|
||||
'line-dasharray': ['case', ['has', 'road_type'], ['literal', [2, 1]], ['literal', [1, 2]]]
|
||||
}
|
||||
}]);
|
||||
|
||||
const data = {
|
||||
@ -200,10 +209,16 @@ describe('worker tile', () => {
|
||||
} as any as VectorTile;
|
||||
|
||||
const sendAsync = vi.fn().mockImplementation((message: {type: string; data: any}) => {
|
||||
const response = message.type === 'getImages' ?
|
||||
{'hello': {width: 1, height: 1, data: new Uint8Array([0])}} :
|
||||
{'StandardFont-Bold': {width: 1, height: 1, data: new Uint8Array([0])}};
|
||||
return Promise.resolve(response);
|
||||
if (message.type === MessageType.getImages) {
|
||||
return Promise.resolve({'hello': {width: 1, height: 1, data: new Uint8Array([0])}});
|
||||
} else if (message.type === MessageType.getGlyphs) {
|
||||
return Promise.resolve({'StandardFont-Bold': {width: 1, height: 1, data: new Uint8Array([0])}});
|
||||
} else if (message.type === MessageType.getDashes) {
|
||||
return Promise.resolve({
|
||||
'2,1,false': {y: 0, height: 16, width: 256},
|
||||
'1,2,false': {y: 16, height: 16, width: 256}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const actorMock = {
|
||||
@ -211,10 +226,11 @@ describe('worker tile', () => {
|
||||
};
|
||||
const result = await tile.parse(data, layerIndex, ['hello'], actorMock, SubdivisionGranularitySetting.noSubdivision);
|
||||
expect(result).toBeDefined();
|
||||
expect(sendAsync).toHaveBeenCalledTimes(3);
|
||||
expect(sendAsync).toHaveBeenCalledWith(expect.objectContaining({data: expect.objectContaining({'icons': ['hello'], 'type': 'icons'})}), expect.any(Object));
|
||||
expect(sendAsync).toHaveBeenCalledWith(expect.objectContaining({data: expect.objectContaining({'icons': ['hello'], 'type': 'patterns'})}), expect.any(Object));
|
||||
expect(sendAsync).toHaveBeenCalledWith(expect.objectContaining({data: expect.objectContaining({'source': 'source', 'type': 'glyphs', 'stacks': {'StandardFont-Bold': [101, 115, 116]}})}), expect.any(Object));
|
||||
expect(sendAsync).toHaveBeenCalledTimes(4); // icons, patterns, glyphs, dashes
|
||||
expect(sendAsync).toHaveBeenCalledWith(expect.objectContaining({type: 'GI', data: expect.objectContaining({'icons': ['hello'], 'type': 'icons'})}), expect.any(Object));
|
||||
expect(sendAsync).toHaveBeenCalledWith(expect.objectContaining({type: 'GI', data: expect.objectContaining({'icons': ['hello'], 'type': 'patterns'})}), expect.any(Object));
|
||||
expect(sendAsync).toHaveBeenCalledWith(expect.objectContaining({type: 'GG', data: expect.objectContaining({'source': 'source', 'type': 'glyphs', 'stacks': {'StandardFont-Bold': [101, 115, 116]}})}), expect.any(Object));
|
||||
expect(sendAsync).toHaveBeenCalledWith(expect.objectContaining({type: 'GDA', data: expect.objectContaining({'dashes': expect.any(Object)})}), expect.any(Object));
|
||||
});
|
||||
|
||||
test('WorkerTile.parse would cancel and only event once on repeated reparsing', async () => {
|
||||
|
||||
27
node_modules/maplibre-gl/src/source/worker_tile.ts
generated
vendored
27
node_modules/maplibre-gl/src/source/worker_tile.ts
generated
vendored
@ -22,7 +22,7 @@ import type {
|
||||
} from '../source/worker_source';
|
||||
import type {PromoteIdSpecification} from '@maplibre/maplibre-gl-style-spec';
|
||||
import type {VectorTile} from '@mapbox/vector-tile';
|
||||
import {MessageType, type GetGlyphsResponse, type GetImagesResponse} from '../util/actor_messages';
|
||||
import {type GetDashesResponse, MessageType, type GetGlyphsResponse, type GetImagesResponse} from '../util/actor_messages';
|
||||
import type {SubdivisionGranularitySetting} from '../render/subdivision_granularity_settings';
|
||||
export class WorkerTile {
|
||||
tileID: OverscaledTileID;
|
||||
@ -77,6 +77,7 @@ export class WorkerTile {
|
||||
iconDependencies: {},
|
||||
patternDependencies: {},
|
||||
glyphDependencies: {},
|
||||
dashDependencies: {},
|
||||
availableImages,
|
||||
subdivisionGranularity
|
||||
};
|
||||
@ -107,10 +108,7 @@ export class WorkerTile {
|
||||
if (layer.source !== this.source) {
|
||||
warnOnce(`layer.source = ${layer.source} does not equal this.source = ${this.source}`);
|
||||
}
|
||||
if (layer.minzoom && this.zoom < Math.floor(layer.minzoom)) continue;
|
||||
if (layer.maxzoom && this.zoom >= layer.maxzoom) continue;
|
||||
if (layer.visibility === 'none') continue;
|
||||
|
||||
if (layer.isHidden(this.zoom, true)) continue;
|
||||
recalculateLayers(family, this.zoom, availableImages);
|
||||
|
||||
const bucket = buckets[layer.id] = layer.createBucket({
|
||||
@ -159,7 +157,16 @@ export class WorkerTile {
|
||||
getPatternsPromise = actor.sendAsync({type: MessageType.getImages, data: {icons: patterns, source: this.source, tileID: this.tileID, type: 'patterns'}}, abortController);
|
||||
}
|
||||
|
||||
const [glyphMap, iconMap, patternMap] = await Promise.all([getGlyphsPromise, getIconsPromise, getPatternsPromise]);
|
||||
const dashes = options.dashDependencies;
|
||||
let getDashesPromise = Promise.resolve<GetDashesResponse>({} as GetDashesResponse);
|
||||
if (Object.keys(dashes).length) {
|
||||
const abortController = new AbortController();
|
||||
this.inFlightDependencies.push(abortController);
|
||||
getDashesPromise = actor.sendAsync({type: MessageType.getDashes, data: {dashes}}, abortController);
|
||||
}
|
||||
|
||||
const [glyphMap, iconMap, patternMap, dashPositions] = await Promise.all([getGlyphsPromise, getIconsPromise, getPatternsPromise, getDashesPromise]);
|
||||
|
||||
const glyphAtlas = new GlyphAtlas(glyphMap);
|
||||
const imageAtlas = new ImageAtlas(iconMap, patternMap);
|
||||
|
||||
@ -177,12 +184,9 @@ export class WorkerTile {
|
||||
canonical: this.tileID.canonical,
|
||||
subdivisionGranularity: options.subdivisionGranularity
|
||||
});
|
||||
} else if (bucket.hasPattern &&
|
||||
(bucket instanceof LineBucket ||
|
||||
bucket instanceof FillBucket ||
|
||||
bucket instanceof FillExtrusionBucket)) {
|
||||
} else if (bucket.hasDependencies && (bucket instanceof FillBucket || bucket instanceof FillExtrusionBucket || bucket instanceof LineBucket)) {
|
||||
recalculateLayers(bucket.layers, this.zoom, availableImages);
|
||||
bucket.addFeatures(options, this.tileID.canonical, imageAtlas.patternPositions);
|
||||
bucket.addFeatures(options, this.tileID.canonical, imageAtlas.patternPositions, dashPositions);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,6 +197,7 @@ export class WorkerTile {
|
||||
collisionBoxArray: this.collisionBoxArray,
|
||||
glyphAtlasImage: glyphAtlas.image,
|
||||
imageAtlas,
|
||||
dashPositions,
|
||||
// Only used for benchmarking:
|
||||
glyphMap: this.returnDependencies ? glyphMap : null,
|
||||
iconMap: this.returnDependencies ? iconMap : null,
|
||||
|
||||
6
node_modules/maplibre-gl/src/style/pauseable_placement.ts
generated
vendored
6
node_modules/maplibre-gl/src/style/pauseable_placement.ts
generated
vendored
@ -1,4 +1,4 @@
|
||||
import {browser} from '../util/browser';
|
||||
import {now} from '../util/time_control';
|
||||
import {Placement} from '../symbol/placement';
|
||||
import type {ITransform} from '../geo/transform_interface';
|
||||
import type {StyleLayer} from './style_layer';
|
||||
@ -92,10 +92,10 @@ export class PauseablePlacement {
|
||||
layers: {[_: string]: StyleLayer},
|
||||
layerTiles: {[_: string]: Array<Tile>}
|
||||
) {
|
||||
const startTime = browser.now();
|
||||
const startTime = now();
|
||||
|
||||
const shouldPausePlacement = () => {
|
||||
return this._forceFullPlacement ? false : (browser.now() - startTime) > 2;
|
||||
return this._forceFullPlacement ? false : (now() - startTime) > 2;
|
||||
};
|
||||
|
||||
while (this._currentPlacementIndex >= 0) {
|
||||
|
||||
29
node_modules/maplibre-gl/src/style/style.test.ts
generated
vendored
29
node_modules/maplibre-gl/src/style/style.test.ts
generated
vendored
@ -3378,4 +3378,33 @@ describe('Style.serialize', () => {
|
||||
expect(style.sky.properties.get('fog-color').g).toBe(1);
|
||||
expect(style.sky.properties.get('fog-color').r).toBe(0);
|
||||
});
|
||||
|
||||
test('Style.getDashes returns line atlas entries for dash patterns', async () => {
|
||||
const style = createStyle();
|
||||
|
||||
const params = {
|
||||
dashes: {
|
||||
'2,1,false': {dasharray: [2, 1], round: false},
|
||||
'4,2,true': {dasharray: [4, 2], round: true}
|
||||
},
|
||||
source: 'test-source',
|
||||
tileID: {z: 1, x: 0, y: 0} as any,
|
||||
type: 'dasharray' as const
|
||||
};
|
||||
|
||||
const result = await style.getDashes('mapId', params);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(Object.keys(result)).toHaveLength(2);
|
||||
expect(result['2,1,false']).toBeDefined();
|
||||
expect(result['4,2,true']).toBeDefined();
|
||||
|
||||
// Verify the entries have the expected atlas properties
|
||||
expect(typeof result['2,1,false'].width).toBe('number');
|
||||
expect(typeof result['2,1,false'].height).toBe('number');
|
||||
expect(typeof result['2,1,false'].y).toBe('number');
|
||||
expect(typeof result['4,2,true'].width).toBe('number');
|
||||
expect(typeof result['4,2,true'].height).toBe('number');
|
||||
expect(typeof result['4,2,true'].y).toBe('number');
|
||||
});
|
||||
});
|
||||
|
||||
36
node_modules/maplibre-gl/src/style/style.ts
generated
vendored
36
node_modules/maplibre-gl/src/style/style.ts
generated
vendored
@ -1,5 +1,6 @@
|
||||
import {Event, ErrorEvent, Evented} from '../util/evented';
|
||||
import {type StyleLayer} from './style_layer';
|
||||
import {isRasterStyleLayer} from './style_layer/raster_style_layer';
|
||||
import {createStyleLayer} from './create_style_layer';
|
||||
import {loadSprite} from './load_sprite';
|
||||
import {ImageManager} from '../render/image_manager';
|
||||
@ -12,6 +13,7 @@ import {coerceSpriteToArray} from '../util/style';
|
||||
import {getJSON, getReferrer} from '../util/ajax';
|
||||
import {ResourceType} from '../util/request_manager';
|
||||
import {browser} from '../util/browser';
|
||||
import {now} from '../util/time_control';
|
||||
import {Dispatcher} from '../util/dispatcher';
|
||||
import {validateStyle, emitValidationErrors as _emitValidationErrors} from './validate_style';
|
||||
import {type Source} from '../source/source';
|
||||
@ -59,6 +61,8 @@ import type {CanvasSourceSpecification} from '../source/canvas_source';
|
||||
import type {CustomLayerInterface} from './style_layer/custom_style_layer';
|
||||
import type {Validator} from './validate_style';
|
||||
import {
|
||||
type GetDashesParameters,
|
||||
type GetDashesResponse,
|
||||
MessageType,
|
||||
type GetGlyphsParameters,
|
||||
type GetGlyphsResponse,
|
||||
@ -247,6 +251,9 @@ export class Style extends Evented {
|
||||
this.dispatcher.registerMessageHandler(MessageType.getImages, (mapId, params) => {
|
||||
return this.getImages(mapId, params);
|
||||
});
|
||||
this.dispatcher.registerMessageHandler(MessageType.getDashes, (mapId, params) => {
|
||||
return this.getDashes(mapId, params);
|
||||
});
|
||||
this.imageManager = new ImageManager();
|
||||
this.imageManager.setEventedParent(this);
|
||||
const glyphLang = map._container?.lang || (typeof document !== 'undefined' && document.documentElement?.lang) || undefined;
|
||||
@ -475,6 +482,11 @@ export class Style extends Evented {
|
||||
const styledLayer = createStyleLayer(layer, this._globalState);
|
||||
styledLayer.setEventedParent(this, {layer: {id: layer.id}});
|
||||
this._layers[layer.id] = styledLayer;
|
||||
|
||||
if (isRasterStyleLayer(styledLayer) && this.sourceCaches[styledLayer.source]) {
|
||||
const rasterFadeDuration = layer.paint?.['raster-fade-duration'] ?? styledLayer.paint.get('raster-fade-duration');
|
||||
this.sourceCaches[styledLayer.source].setRasterFadeDuration(rasterFadeDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1314,6 +1326,10 @@ export class Style extends Evented {
|
||||
this._updateLayer(layer);
|
||||
}
|
||||
|
||||
if (isRasterStyleLayer(layer) && name === 'raster-fade-duration') {
|
||||
this.sourceCaches[layer.source].setRasterFadeDuration(value);
|
||||
}
|
||||
|
||||
this._changed = true;
|
||||
this._updatedPaintProps[layer.id] = true;
|
||||
// reset serialization field, to be populated only when needed
|
||||
@ -1619,7 +1635,7 @@ export class Style extends Evented {
|
||||
if (!_update) return;
|
||||
|
||||
const parameters = {
|
||||
now: browser.now(),
|
||||
now: now(),
|
||||
transition: extend({
|
||||
duration: 300,
|
||||
delay: 0
|
||||
@ -1671,7 +1687,7 @@ export class Style extends Evented {
|
||||
if (!update) return;
|
||||
|
||||
const parameters = {
|
||||
now: browser.now(),
|
||||
now: now(),
|
||||
transition: extend({
|
||||
duration: 300,
|
||||
delay: 0
|
||||
@ -1684,7 +1700,7 @@ export class Style extends Evented {
|
||||
}
|
||||
|
||||
_setProjectionInternal(name: ProjectionSpecification['type']) {
|
||||
const projectionObjects = createProjectionFromName(name);
|
||||
const projectionObjects = createProjectionFromName(name, this.map.transformConstrain);
|
||||
this.projection = projectionObjects.projection;
|
||||
this.map.migrateProjection(projectionObjects.transform, projectionObjects.cameraHelper);
|
||||
for (const key in this.sourceCaches) {
|
||||
@ -1788,7 +1804,7 @@ export class Style extends Evented {
|
||||
// tiles will fully display symbols in their first frame
|
||||
forceFullPlacement = forceFullPlacement || this._layerOrderChanged || fadeDuration === 0;
|
||||
|
||||
if (forceFullPlacement || !this.pauseablePlacement || (this.pauseablePlacement.isDone() && !this.placement.stillRecent(browser.now(), transform.zoom))) {
|
||||
if (forceFullPlacement || !this.pauseablePlacement || (this.pauseablePlacement.isDone() && !this.placement.stillRecent(now(), transform.zoom))) {
|
||||
this.pauseablePlacement = new PauseablePlacement(transform, this.map.terrain, this._order, forceFullPlacement, showCollisionBoxes, fadeDuration, crossSourceCollisions, this.placement);
|
||||
this._layerOrderChanged = false;
|
||||
}
|
||||
@ -1803,7 +1819,7 @@ export class Style extends Evented {
|
||||
this.pauseablePlacement.continuePlacement(this._order, this._layers, layerTiles);
|
||||
|
||||
if (this.pauseablePlacement.isDone()) {
|
||||
this.placement = this.pauseablePlacement.commit(browser.now());
|
||||
this.placement = this.pauseablePlacement.commit(now());
|
||||
placementCommitted = true;
|
||||
}
|
||||
|
||||
@ -1824,7 +1840,7 @@ export class Style extends Evented {
|
||||
}
|
||||
|
||||
// needsRender is false when we have just finished a placement that didn't change the visibility of any symbols
|
||||
const needsRerender = !this.pauseablePlacement.isDone() || this.placement.hasTransitions(browser.now());
|
||||
const needsRerender = !this.pauseablePlacement.isDone() || this.placement.hasTransitions(now());
|
||||
return needsRerender;
|
||||
}
|
||||
|
||||
@ -1883,6 +1899,14 @@ export class Style extends Evented {
|
||||
this.glyphManager.setURL(glyphsUrl);
|
||||
}
|
||||
|
||||
async getDashes(mapId: string | number, params: GetDashesParameters): Promise<GetDashesResponse> {
|
||||
const result: GetDashesResponse = {};
|
||||
for (const [key, dash] of Object.entries(params.dashes)) {
|
||||
result[key] = this.lineAtlas.getDash(dash.dasharray, dash.round);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a sprite.
|
||||
*
|
||||
|
||||
4
node_modules/maplibre-gl/src/style/style_layer.ts
generated
vendored
4
node_modules/maplibre-gl/src/style/style_layer.ts
generated
vendored
@ -283,8 +283,8 @@ export abstract class StyleLayer extends Evented {
|
||||
return false;
|
||||
}
|
||||
|
||||
isHidden(zoom: number) {
|
||||
if (this.minzoom && zoom < this.minzoom) return true;
|
||||
isHidden(zoom: number, roundMinZoom: boolean = false) {
|
||||
if (this.minzoom && zoom < (roundMinZoom ? Math.floor(this.minzoom) : this.minzoom)) return true;
|
||||
if (this.maxzoom && zoom >= this.maxzoom) return true;
|
||||
return this.visibility === 'none';
|
||||
}
|
||||
|
||||
2
node_modules/maplibre-gl/src/style/style_layer/circle_style_layer.test.ts
generated
vendored
2
node_modules/maplibre-gl/src/style/style_layer/circle_style_layer.test.ts
generated
vendored
@ -47,7 +47,7 @@ describe('CircleStyleLayer.queryIntersectsFeature', () => {
|
||||
}
|
||||
|
||||
describe('Mercator projection', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(400, 300);
|
||||
|
||||
describe('map pitch alignment', () => {
|
||||
|
||||
2
node_modules/maplibre-gl/src/style/style_layer/heatmap_style_layer.test.ts
generated
vendored
2
node_modules/maplibre-gl/src/style/style_layer/heatmap_style_layer.test.ts
generated
vendored
@ -39,7 +39,7 @@ describe('HeatmapStyleLayer.queryIntersectsFeature', () => {
|
||||
}
|
||||
|
||||
describe('Mercator projection', () => {
|
||||
const transform = new MercatorTransform(0, 22, 0, 85, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 85, renderWorldCopies: true});
|
||||
transform.resize(400, 300);
|
||||
|
||||
test('returns `true` when a heatmap intersects a point', () => {
|
||||
|
||||
6
node_modules/maplibre-gl/src/style/style_layer/line_style_layer_properties.g.ts
generated
vendored
6
node_modules/maplibre-gl/src/style/style_layer/line_style_layer_properties.g.ts
generated
vendored
@ -51,7 +51,7 @@ export type LinePaintProps = {
|
||||
"line-gap-width": DataDrivenProperty<number>,
|
||||
"line-offset": DataDrivenProperty<number>,
|
||||
"line-blur": DataDrivenProperty<number>,
|
||||
"line-dasharray": CrossFadedProperty<Array<number>>,
|
||||
"line-dasharray": CrossFadedDataDrivenProperty<Array<number>>,
|
||||
"line-pattern": CrossFadedDataDrivenProperty<ResolvedImage>,
|
||||
"line-gradient": ColorRampProperty,
|
||||
};
|
||||
@ -65,7 +65,7 @@ export type LinePaintPropsPossiblyEvaluated = {
|
||||
"line-gap-width": PossiblyEvaluatedPropertyValue<number>,
|
||||
"line-offset": PossiblyEvaluatedPropertyValue<number>,
|
||||
"line-blur": PossiblyEvaluatedPropertyValue<number>,
|
||||
"line-dasharray": CrossFaded<Array<number>>,
|
||||
"line-dasharray": PossiblyEvaluatedPropertyValue<CrossFaded<Array<number>>>,
|
||||
"line-pattern": PossiblyEvaluatedPropertyValue<CrossFaded<ResolvedImage>>,
|
||||
"line-gradient": ColorRampProperty,
|
||||
};
|
||||
@ -80,7 +80,7 @@ const getPaint = () => paint = paint || new Properties({
|
||||
"line-gap-width": new DataDrivenProperty(styleSpec["paint_line"]["line-gap-width"] as any as StylePropertySpecification),
|
||||
"line-offset": new DataDrivenProperty(styleSpec["paint_line"]["line-offset"] as any as StylePropertySpecification),
|
||||
"line-blur": new DataDrivenProperty(styleSpec["paint_line"]["line-blur"] as any as StylePropertySpecification),
|
||||
"line-dasharray": new CrossFadedProperty(styleSpec["paint_line"]["line-dasharray"] as any as StylePropertySpecification),
|
||||
"line-dasharray": new CrossFadedDataDrivenProperty(styleSpec["paint_line"]["line-dasharray"] as any as StylePropertySpecification),
|
||||
"line-pattern": new CrossFadedDataDrivenProperty(styleSpec["paint_line"]["line-pattern"] as any as StylePropertySpecification),
|
||||
"line-gradient": new ColorRampProperty(styleSpec["paint_line"]["line-gradient"] as any as StylePropertySpecification),
|
||||
});
|
||||
|
||||
2
node_modules/maplibre-gl/src/symbol/collision_index.test.ts
generated
vendored
2
node_modules/maplibre-gl/src/symbol/collision_index.test.ts
generated
vendored
@ -7,7 +7,7 @@ import {mat4} from 'gl-matrix';
|
||||
describe('CollisionIndex', () => {
|
||||
test('floating point precision', () => {
|
||||
const x = 100000.123456, y = 0;
|
||||
const transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(200, 200);
|
||||
const tile = new UnwrappedTileID(0, new CanonicalTileID(0, 0, 0));
|
||||
vi.spyOn(transform, 'calculatePosMatrix').mockImplementation(() => mat4.create());
|
||||
|
||||
3
node_modules/maplibre-gl/src/symbol/collision_index.ts
generated
vendored
3
node_modules/maplibre-gl/src/symbol/collision_index.ts
generated
vendored
@ -11,6 +11,7 @@ import type {IReadonlyTransform} from '../geo/transform_interface';
|
||||
import type {SingleCollisionBox} from '../data/bucket/symbol_bucket';
|
||||
import type {
|
||||
GlyphOffsetArray,
|
||||
PlacedSymbol,
|
||||
SymbolLineVertexArray
|
||||
} from '../data/array_types.g';
|
||||
import type {OverlapMode} from '../style/style_layer/overlap_mode';
|
||||
@ -184,7 +185,7 @@ export class CollisionIndex {
|
||||
|
||||
placeCollisionCircles(
|
||||
overlapMode: OverlapMode,
|
||||
symbol: any,
|
||||
symbol: PlacedSymbol,
|
||||
lineVertexArray: SymbolLineVertexArray,
|
||||
glyphOffsetArray: GlyphOffsetArray,
|
||||
fontSize: number,
|
||||
|
||||
10
node_modules/maplibre-gl/src/symbol/get_anchors.ts
generated
vendored
10
node_modules/maplibre-gl/src/symbol/get_anchors.ts
generated
vendored
@ -78,7 +78,7 @@ function getAnchors(line: Array<Point>,
|
||||
glyphSize: number,
|
||||
boxScale: number,
|
||||
overscaling: number,
|
||||
tileExtent: number) {
|
||||
tileExtent: number): Anchor[] {
|
||||
|
||||
// Resample a line to get anchor points for labels and check that each
|
||||
// potential label passes text-max-angle check and has enough room to fit
|
||||
@ -111,15 +111,15 @@ function getAnchors(line: Array<Point>,
|
||||
return resample(line, offset, spacing, angleWindowSize, maxAngle, labelLength, isLineContinued, false, tileExtent);
|
||||
}
|
||||
|
||||
function resample(line, offset, spacing, angleWindowSize, maxAngle, labelLength, isLineContinued, placeAtMiddle, tileExtent) {
|
||||
function resample(line: Point[], offset: number, spacing: number, angleWindowSize: number, maxAngle: number, labelLength: number, isLineContinued: boolean, placeAtMiddle: boolean, tileExtent: number): Anchor[] {
|
||||
|
||||
const halfLabelLength = labelLength / 2;
|
||||
const lineLength = getLineLength(line);
|
||||
|
||||
let distance = 0,
|
||||
markedDistance = offset - spacing;
|
||||
let distance = 0;
|
||||
let markedDistance = offset - spacing;
|
||||
|
||||
let anchors = [];
|
||||
let anchors: Anchor[] = [];
|
||||
|
||||
for (let i = 0; i < line.length - 1; i++) {
|
||||
|
||||
|
||||
4
node_modules/maplibre-gl/src/symbol/placement.ts
generated
vendored
4
node_modules/maplibre-gl/src/symbol/placement.ts
generated
vendored
@ -294,7 +294,7 @@ export class Placement {
|
||||
pitchedLabelPlaneMatrix,
|
||||
scale,
|
||||
textPixelRatio,
|
||||
holdingForFade: tile.holdingForFade(),
|
||||
holdingForFade: tile.holdingForSymbolFade(),
|
||||
collisionBoxArray,
|
||||
partiallyEvaluatedTextSize: symbolSize.evaluateSizeForZoom(symbolBucket.textSizeData, this.transform.zoom),
|
||||
collisionGroup: this.collisionGroups.get(symbolBucket.sourceID)
|
||||
@ -683,7 +683,7 @@ export class Placement {
|
||||
placeText = placedGlyphBoxes && placedGlyphBoxes.placeable;
|
||||
offscreen = placedGlyphBoxes && placedGlyphBoxes.offscreen;
|
||||
|
||||
if (symbolInstance.useRuntimeCollisionCircles) {
|
||||
if (symbolInstance.useRuntimeCollisionCircles && symbolInstance.centerJustifiedTextSymbolIndex >= 0) {
|
||||
const placedSymbol = bucket.text.placedSymbolArray.get(symbolInstance.centerJustifiedTextSymbolIndex);
|
||||
const fontSize = symbolSize.evaluateSizeForFeature(bucket.textSizeData, partiallyEvaluatedTextSize, placedSymbol);
|
||||
|
||||
|
||||
7
node_modules/maplibre-gl/src/symbol/projection.ts
generated
vendored
7
node_modules/maplibre-gl/src/symbol/projection.ts
generated
vendored
@ -10,7 +10,8 @@ import type {SymbolBucket} from '../data/bucket/symbol_bucket';
|
||||
import type {
|
||||
GlyphOffsetArray,
|
||||
SymbolLineVertexArray,
|
||||
SymbolDynamicLayoutArray
|
||||
SymbolDynamicLayoutArray,
|
||||
PlacedSymbol,
|
||||
} from '../data/array_types.g';
|
||||
import {WritingMode} from '../symbol/shaping';
|
||||
import {findLineIntersection} from '../util/util';
|
||||
@ -344,7 +345,7 @@ export function placeFirstAndLastGlyph(
|
||||
lineOffsetX: number,
|
||||
lineOffsetY: number,
|
||||
flip: boolean,
|
||||
symbol: any,
|
||||
symbol: PlacedSymbol,
|
||||
rotateToLine: boolean,
|
||||
projectionContext: SymbolProjectionContext): FirstAndLastGlyphPlacement {
|
||||
const glyphEndIndex = symbol.glyphStartIndex + symbol.numGlyphs;
|
||||
@ -404,7 +405,7 @@ type GlyphLinePlacementResult = OrientationChangeType & {
|
||||
type GlyphLinePlacementArgs = {
|
||||
projectionContext: SymbolProjectionContext;
|
||||
pitchedLabelPlaneMatrixInverse: mat4;
|
||||
symbol: any; // PlacedSymbolStruct
|
||||
symbol: PlacedSymbol;
|
||||
fontSize: number;
|
||||
flip: boolean;
|
||||
keepUpright: boolean;
|
||||
|
||||
6
node_modules/maplibre-gl/src/symbol/symbol_layout.ts
generated
vendored
6
node_modules/maplibre-gl/src/symbol/symbol_layout.ts
generated
vendored
@ -321,7 +321,7 @@ function addFeature(bucket: SymbolBucket,
|
||||
textRepeatDistance = symbolMinDistance / 2;
|
||||
|
||||
const iconTextFit = layout.get('icon-text-fit');
|
||||
let verticallyShapedIcon;
|
||||
let verticallyShapedIcon: PositionedIcon | undefined;
|
||||
// Adjust shaped icon size when icon-text-fit is used.
|
||||
if (shapedIcon && iconTextFit !== 'none') {
|
||||
if (bucket.allowVerticalPlacement && shapedTextOrientations.vertical) {
|
||||
@ -509,9 +509,9 @@ function addSymbol(bucket: SymbolBucket,
|
||||
anchor: Anchor,
|
||||
line: Array<Point>,
|
||||
shapedTextOrientations: ShapedTextOrientations,
|
||||
shapedIcon: PositionedIcon | void,
|
||||
shapedIcon: PositionedIcon | undefined,
|
||||
imageMap: {[_: string]: StyleImage},
|
||||
verticallyShapedIcon: PositionedIcon | void,
|
||||
verticallyShapedIcon: PositionedIcon | undefined,
|
||||
layer: SymbolStyleLayer,
|
||||
collisionBoxArray: CollisionBoxArray,
|
||||
featureIndex: number,
|
||||
|
||||
122
node_modules/maplibre-gl/src/ui/camera.test.ts
generated
vendored
122
node_modules/maplibre-gl/src/ui/camera.test.ts
generated
vendored
@ -1,6 +1,7 @@
|
||||
import {describe, beforeEach, test, expect, vi} from 'vitest';
|
||||
import {Camera, type CameraOptions, type PointLike} from '../ui/camera';
|
||||
import {TaskQueue, type TaskID} from '../util/task_queue';
|
||||
import * as timeControl from '../util/time_control';
|
||||
import {browser} from '../util/browser';
|
||||
import {fixedLngLat, fixedNum} from '../../test/unit/lib/fixed';
|
||||
import {setMatchMedia} from '../util/test/util';
|
||||
@ -1042,7 +1043,7 @@ describe('easeTo', () => {
|
||||
|
||||
test('can be called from within a moveend event handler', async () => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
stub.mockImplementation(() => 0);
|
||||
camera.easeTo({center: [100, 0], duration: 10});
|
||||
@ -1079,7 +1080,7 @@ describe('easeTo', () => {
|
||||
|
||||
test('pans eastward across the antimeridian', async () => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([170, 0]);
|
||||
let crossedAntimeridian;
|
||||
@ -1122,7 +1123,7 @@ describe('easeTo', () => {
|
||||
|
||||
test('pans westward across the antimeridian', async () => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([-170, 0]);
|
||||
let crossedAntimeridian;
|
||||
@ -1166,7 +1167,7 @@ describe('easeTo', () => {
|
||||
test('animation occurs when prefers-reduced-motion: reduce is set but overridden by essential: true', async () => {
|
||||
const camera = createCamera();
|
||||
Object.defineProperty(browser, 'prefersReducedMotion', {value: true});
|
||||
const stubNow = vi.spyOn(browser, 'now');
|
||||
const stubNow = vi.spyOn(timeControl, 'now');
|
||||
|
||||
// camera transition expected to take in this range when prefersReducedMotion is set and essential: true,
|
||||
// when a duration of 200 is requested
|
||||
@ -1174,7 +1175,7 @@ describe('easeTo', () => {
|
||||
const max = 300;
|
||||
|
||||
let startTime;
|
||||
camera.on('movestart', () => { startTime = browser.now(); });
|
||||
camera.on('movestart', () => { startTime = timeControl.now(); });
|
||||
const promise = camera.once('moveend');
|
||||
|
||||
setTimeout(() => {
|
||||
@ -1190,7 +1191,7 @@ describe('easeTo', () => {
|
||||
}, 0);
|
||||
|
||||
await promise;
|
||||
const endTime = browser.now();
|
||||
const endTime = timeControl.now();
|
||||
const timeDiff = endTime - startTime;
|
||||
expect(timeDiff >= min && timeDiff < max).toBeTruthy();
|
||||
});
|
||||
@ -1281,12 +1282,12 @@ describe('easeTo', () => {
|
||||
|
||||
test('terrain set during easeTo', () => {
|
||||
const camera = createCamera();
|
||||
const stubNow = vi.spyOn(browser, 'now');
|
||||
const stubNow = vi.spyOn(timeControl, 'now');
|
||||
|
||||
stubNow.mockImplementation(() => 0);
|
||||
|
||||
camera.easeTo({bearing: 97, duration: 500});
|
||||
|
||||
|
||||
stubNow.mockImplementation(() => 100);
|
||||
camera.simulateFrame();
|
||||
|
||||
@ -1321,7 +1322,7 @@ describe('flyTo', () => {
|
||||
});
|
||||
|
||||
test('does not throw when cameras current zoom is above maxzoom and an offset creates infinite zoom out factor', () => {
|
||||
const transform = new MercatorTransform(0, 20.9999, 0, 60, true);
|
||||
const transform = new MercatorTransform({minZoom: 0, maxZoom: 20.9999, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
transform.resize(512, 512);
|
||||
const camera = attachSimulateFrame(new CameraMock(transform, new MercatorCameraHelper(), {} as any))
|
||||
.jumpTo({zoom: 21, center: [0, 0]});
|
||||
@ -1344,7 +1345,7 @@ describe('flyTo', () => {
|
||||
test('Zoom out from the same position to the same position with animation', async () => {
|
||||
const pos = {lng: 0, lat: 0};
|
||||
const camera = createCamera({zoom: 20, center: pos});
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
const promise = camera.once('moveend');
|
||||
|
||||
@ -1538,7 +1539,7 @@ describe('flyTo', () => {
|
||||
camera.on('pitchend', (d) => { pitchended = d.data; });
|
||||
const promise = camera.once('moveend');
|
||||
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
|
||||
camera.flyTo({center: [100, 0], duration: 10}, eventData);
|
||||
@ -1579,7 +1580,7 @@ describe('flyTo', () => {
|
||||
});
|
||||
|
||||
test('no roll when motion is interrupted', () => {
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
const camera = createCamera();
|
||||
stub.mockImplementation(() => 0);
|
||||
@ -1591,7 +1592,7 @@ describe('flyTo', () => {
|
||||
});
|
||||
|
||||
test('no roll when motion is interrupted: globe', () => {
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
const camera = createCameraGlobe();
|
||||
stub.mockImplementation(() => 0);
|
||||
@ -1603,7 +1604,7 @@ describe('flyTo', () => {
|
||||
});
|
||||
|
||||
test('angles when motion is interrupted', () => {
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
const camera = createCamera();
|
||||
stub.mockImplementation(() => 0);
|
||||
@ -1617,7 +1618,7 @@ describe('flyTo', () => {
|
||||
});
|
||||
|
||||
test('angles when motion is interrupted: globe', () => {
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
const camera = createCameraGlobe();
|
||||
stub.mockImplementation(() => 0);
|
||||
@ -1632,7 +1633,7 @@ describe('flyTo', () => {
|
||||
|
||||
test('can be called from within a moveend event handler', async () => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
|
||||
camera.flyTo({center: [100, 0], duration: 10});
|
||||
@ -1673,7 +1674,7 @@ describe('flyTo', () => {
|
||||
|
||||
const promise = camera.once('moveend');
|
||||
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
|
||||
camera.flyTo({center: [100, 0], zoom: 18, duration: 10});
|
||||
@ -1693,7 +1694,7 @@ describe('flyTo', () => {
|
||||
|
||||
test('pans eastward across the prime meridian', async () => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([-10, 0]);
|
||||
let crossedPrimeMeridian;
|
||||
@ -1725,7 +1726,7 @@ describe('flyTo', () => {
|
||||
|
||||
test('pans westward across the prime meridian', async () => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([10, 0]);
|
||||
let crossedPrimeMeridian;
|
||||
@ -1757,7 +1758,7 @@ describe('flyTo', () => {
|
||||
|
||||
test('pans eastward across the antimeridian', async () => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([170, 0]);
|
||||
let crossedAntimeridian;
|
||||
@ -1788,7 +1789,7 @@ describe('flyTo', () => {
|
||||
|
||||
test('pans westward across the antimeridian', async () => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([-170, 0]);
|
||||
let crossedAntimeridian;
|
||||
@ -1819,7 +1820,7 @@ describe('flyTo', () => {
|
||||
|
||||
test('does not pan eastward across the antimeridian if no world copies', async () => {
|
||||
const camera = createCamera({renderWorldCopies: false});
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([170, 0]);
|
||||
let crossedAntimeridian;
|
||||
@ -1851,7 +1852,7 @@ describe('flyTo', () => {
|
||||
|
||||
test('does not pan westward across the antimeridian if no world copies', async () => {
|
||||
const camera = createCamera({renderWorldCopies: false});
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([-170, 0]);
|
||||
let crossedAntimeridian;
|
||||
@ -1883,7 +1884,7 @@ describe('flyTo', () => {
|
||||
|
||||
test('jumps back to world 0 when crossing the antimeridian', async () => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([-170, 0]);
|
||||
|
||||
@ -1914,7 +1915,7 @@ describe('flyTo', () => {
|
||||
|
||||
test('peaks at the specified zoom level', async () => {
|
||||
const camera = createCamera({zoom: 20});
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
const minZoom = 1;
|
||||
let zoomed = false;
|
||||
@ -1950,7 +1951,7 @@ describe('flyTo', () => {
|
||||
});
|
||||
|
||||
test('respects transform\'s maxZoom', async () => {
|
||||
const transform = new MercatorTransform(2, 10, 0, 60, false);
|
||||
const transform = new MercatorTransform({minZoom: 2, maxZoom: 10, minPitch: 0, maxPitch: 60, renderWorldCopies: false});
|
||||
transform.resize(512, 512);
|
||||
|
||||
const camera = attachSimulateFrame(new CameraMock(transform, new MercatorCameraHelper(), {} as any));
|
||||
@ -1958,7 +1959,7 @@ describe('flyTo', () => {
|
||||
|
||||
const promise = camera.once('moveend');
|
||||
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
camera.flyTo({center: [12, 34], zoom: 30, duration: 10});
|
||||
|
||||
@ -1975,7 +1976,7 @@ describe('flyTo', () => {
|
||||
});
|
||||
|
||||
test('respects transform\'s minZoom', async () => {
|
||||
const transform = new MercatorTransform(2, 10, 0, 60, false);
|
||||
const transform = new MercatorTransform({minZoom: 2, maxZoom: 10, minPitch: 0, maxPitch: 60, renderWorldCopies: false});
|
||||
transform.resize(512, 512);
|
||||
|
||||
const camera = attachSimulateFrame(new CameraMock(transform, new MercatorCameraHelper(), {} as any));
|
||||
@ -1983,7 +1984,7 @@ describe('flyTo', () => {
|
||||
|
||||
const promise = camera.once('moveend');
|
||||
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
camera.flyTo({center: [12, 34], zoom: 1, duration: 10});
|
||||
|
||||
@ -2029,9 +2030,18 @@ describe('flyTo', () => {
|
||||
expect(timeDiff >= 0 && timeDiff < 10).toBeTruthy();
|
||||
});
|
||||
|
||||
test('applies the padding option when prefers-reduce-motion:reduce is set', async () => {
|
||||
const camera = createCamera();
|
||||
Object.defineProperty(browser, 'prefersReducedMotion', {value: true});
|
||||
|
||||
camera.flyTo({padding: {top: 50, right: 30}});
|
||||
|
||||
expect(camera.getPadding()).toEqual({top: 50, bottom: 0, left: 0, right: 30});
|
||||
});
|
||||
|
||||
test('check elevation events freezeElevation=false', async () => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
const terrainCallbacks = {prepare: 0, update: 0, finalize: 0} as any;
|
||||
camera.terrain = {} as Terrain;
|
||||
@ -2055,7 +2065,7 @@ describe('flyTo', () => {
|
||||
|
||||
test('check elevation events freezeElevation=true', async() => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
const terrainCallbacks = {prepare: 0, update: 0, finalize: 0} as any;
|
||||
camera.terrain = {} as Terrain;
|
||||
@ -2121,7 +2131,7 @@ describe('isEasing', () => {
|
||||
test('returns false when done panning', async () => {
|
||||
const camera = createCamera();
|
||||
const promise = camera.once('moveend');
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
camera.panTo([100, 0], {duration: 1});
|
||||
setTimeout(() => {
|
||||
@ -2143,7 +2153,7 @@ describe('isEasing', () => {
|
||||
test('returns false when done zooming', async () => {
|
||||
const camera = createCamera();
|
||||
const promise = camera.once('moveend');
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
camera.zoomTo(3.2, {duration: 1});
|
||||
setTimeout(() => {
|
||||
@ -2164,7 +2174,7 @@ describe('isEasing', () => {
|
||||
test('returns false when done rotating', async () => {
|
||||
const camera = createCamera();
|
||||
const promise = camera.once('moveend');
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
camera.rotateTo(90, {duration: 1});
|
||||
setTimeout(() => {
|
||||
@ -2239,7 +2249,7 @@ describe('stop', () => {
|
||||
const spy = vi.fn();
|
||||
camera.on('moveend', spy);
|
||||
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
camera.panTo([100, 0], {duration: 1}, eventData);
|
||||
|
||||
@ -2344,7 +2354,7 @@ describe('cameraForBounds', () => {
|
||||
});
|
||||
|
||||
test('asymmetrical transform using LngLatBounds instance', () => {
|
||||
const transform = new MercatorTransform(2, 10, 0, 60, false);
|
||||
const transform = new MercatorTransform({minZoom: 2, maxZoom: 10, minPitch: 0, maxPitch: 60, renderWorldCopies: false});
|
||||
transform.resize(2048, 512);
|
||||
|
||||
const camera = attachSimulateFrame(new CameraMock(transform, new MercatorCameraHelper(), {} as any));
|
||||
@ -2477,7 +2487,7 @@ describe('queryTerrainElevation', () => {
|
||||
test('Calls getElevationForLngLatZoom with correct arguments', () => {
|
||||
const getElevationForLngLatZoom = vi.fn();
|
||||
camera.terrain = {getElevationForLngLatZoom} as any as Terrain;
|
||||
camera.transform = new MercatorTransform(0, 22, 0, 60, true);
|
||||
camera.transform = new MercatorTransform({minZoom: 0, maxZoom: 22, minPitch: 0, maxPitch: 60, renderWorldCopies: true});
|
||||
|
||||
camera.queryTerrainElevation([1, 2]);
|
||||
|
||||
@ -2514,7 +2524,7 @@ describe('transformCameraUpdate', () => {
|
||||
|
||||
test('invoke transformCameraUpdate callback during easeTo', async () => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
|
||||
let callbackCount = 0;
|
||||
@ -2548,7 +2558,7 @@ describe('transformCameraUpdate', () => {
|
||||
|
||||
test('invoke transformCameraUpdate callback during flyTo', async () => {
|
||||
const camera = createCamera();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
|
||||
let callbackCount = 0;
|
||||
@ -2858,7 +2868,7 @@ describe('easeTo globe projection', () => {
|
||||
|
||||
test('smoothly sets given padding with duration > 0', async () => {
|
||||
const camera = createCameraGlobe();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
const promise = camera.once('moveend');
|
||||
|
||||
stub.mockImplementation(() => 0);
|
||||
@ -3020,7 +3030,7 @@ describe('easeTo globe projection', () => {
|
||||
|
||||
test('pans eastward across the antimeridian', async () => {
|
||||
const camera = createCameraGlobe();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([170, 0]);
|
||||
let crossedAntimeridian;
|
||||
@ -3062,7 +3072,7 @@ describe('easeTo globe projection', () => {
|
||||
|
||||
test('pans westward across the antimeridian', async () => {
|
||||
const camera = createCameraGlobe();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([-170, 0]);
|
||||
let crossedAntimeridian;
|
||||
@ -3162,7 +3172,7 @@ describe('flyTo globe projection', () => {
|
||||
test('Zoom out from the same position to the same position with animation', async () => {
|
||||
const pos = {lng: 0, lat: 0};
|
||||
const camera = createCameraGlobe({zoom: 20, center: pos});
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
const promise = camera.once('zoomend');
|
||||
|
||||
@ -3241,7 +3251,7 @@ describe('flyTo globe projection', () => {
|
||||
|
||||
test('smoothly sets given padding with duration > 0', async () => {
|
||||
const camera = createCameraGlobe();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
const promise = camera.once('moveend');
|
||||
|
||||
stub.mockImplementation(() => 0);
|
||||
@ -3408,7 +3418,7 @@ describe('flyTo globe projection', () => {
|
||||
camera.on('pitchend', (d) => { pitchended = d.data; });
|
||||
const promise = camera.once('moveend');
|
||||
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
|
||||
camera.flyTo({center: [100, 0], duration: 10}, eventData);
|
||||
@ -3455,7 +3465,7 @@ describe('flyTo globe projection', () => {
|
||||
|
||||
const promise = camera.once('moveend');
|
||||
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
|
||||
camera.flyTo({center: [100, 0], zoom: 18, duration: 10});
|
||||
@ -3476,7 +3486,7 @@ describe('flyTo globe projection', () => {
|
||||
|
||||
test('pans eastward across the prime meridian', async () => {
|
||||
const camera = createCameraGlobe();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([-10, 0]);
|
||||
let crossedPrimeMeridian;
|
||||
@ -3508,7 +3518,7 @@ describe('flyTo globe projection', () => {
|
||||
|
||||
test('pans westward across the prime meridian', async () => {
|
||||
const camera = createCameraGlobe();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([10, 0]);
|
||||
let crossedPrimeMeridian;
|
||||
@ -3540,7 +3550,7 @@ describe('flyTo globe projection', () => {
|
||||
|
||||
test('pans eastward across the antimeridian', async () => {
|
||||
const camera = createCameraGlobe();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([170, 0]);
|
||||
let crossedAntimeridian;
|
||||
@ -3572,7 +3582,7 @@ describe('flyTo globe projection', () => {
|
||||
|
||||
test('pans westward across the antimeridian', async () => {
|
||||
const camera = createCameraGlobe();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([-170, 0]);
|
||||
let crossedAntimeridian;
|
||||
@ -3604,7 +3614,7 @@ describe('flyTo globe projection', () => {
|
||||
|
||||
test('pans eastward across the antimeridian even if renderWorldCopies: false', async () => {
|
||||
const camera = createCameraGlobe({renderWorldCopies: false});
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([170, 0]);
|
||||
let crossedAntimeridian;
|
||||
@ -3636,7 +3646,7 @@ describe('flyTo globe projection', () => {
|
||||
|
||||
test('pans westward across the antimeridian even if renderWorldCopies: false', async () => {
|
||||
const camera = createCameraGlobe({renderWorldCopies: false});
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([-170, 0]);
|
||||
let crossedAntimeridian;
|
||||
@ -3668,7 +3678,7 @@ describe('flyTo globe projection', () => {
|
||||
|
||||
test('jumps back to world 0 when crossing the antimeridian', async () => {
|
||||
const camera = createCameraGlobe();
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
camera.setCenter([-170, 0]);
|
||||
|
||||
@ -3699,7 +3709,7 @@ describe('flyTo globe projection', () => {
|
||||
|
||||
test('peaks at the specified zoom level', async () => {
|
||||
const camera = createCameraGlobe({zoom: 20});
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
|
||||
const minZoom = 1;
|
||||
let zoomed = false;
|
||||
@ -3743,7 +3753,7 @@ describe('flyTo globe projection', () => {
|
||||
|
||||
const promise = camera.once('moveend');
|
||||
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
camera.flyTo({center: [12, 34], zoom: 30, duration: 10});
|
||||
|
||||
@ -3772,7 +3782,7 @@ describe('flyTo globe projection', () => {
|
||||
|
||||
const promise = camera.once('moveend');
|
||||
|
||||
const stub = vi.spyOn(browser, 'now');
|
||||
const stub = vi.spyOn(timeControl, 'now');
|
||||
stub.mockImplementation(() => 0);
|
||||
camera.flyTo({center: target, zoom: 1, duration: 10});
|
||||
|
||||
|
||||
21
node_modules/maplibre-gl/src/ui/camera.ts
generated
vendored
21
node_modules/maplibre-gl/src/ui/camera.ts
generated
vendored
@ -1,6 +1,7 @@
|
||||
import {extend, wrap, defaultEasing, pick, scaleZoom} from '../util/util';
|
||||
import {interpolates} from '@maplibre/maplibre-gl-style-spec';
|
||||
import {browser} from '../util/browser';
|
||||
import {now} from '../util/time_control';
|
||||
import {LngLat} from '../geo/lng_lat';
|
||||
import {LngLatBounds} from '../geo/lng_lat_bounds';
|
||||
import Point from '@mapbox/point-geometry';
|
||||
@ -1069,9 +1070,10 @@ export abstract class Camera extends Evented {
|
||||
* between old and new values. The map will retain its current values for any
|
||||
* details not specified in `options`.
|
||||
*
|
||||
* Note: The transition will happen instantly if the user has enabled
|
||||
* the `reduced motion` accessibility feature enabled in their operating system,
|
||||
* unless `options` includes `essential: true`.
|
||||
* !!! note "Reduced Motion"
|
||||
* The transition will happen instantly if the user has enabled
|
||||
* the `reduced motion` accessibility feature enabled in their operating system,
|
||||
* unless `options` includes `essential: true`.
|
||||
*
|
||||
* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`,
|
||||
* `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`.
|
||||
@ -1351,9 +1353,10 @@ export abstract class Camera extends Evented {
|
||||
* evokes flight. The animation seamlessly incorporates zooming and panning to help
|
||||
* the user maintain her bearings even after traversing a great distance.
|
||||
*
|
||||
* Note: The animation will be skipped, and this will behave equivalently to `jumpTo`
|
||||
* if the user has the `reduced motion` accessibility feature enabled in their operating system,
|
||||
* unless 'options' includes `essential: true`.
|
||||
* !!! note "Reduced Motion"
|
||||
* The animation will be skipped, and this will behave equivalently to `jumpTo`
|
||||
* if the user has the `reduced motion` accessibility feature enabled in their operating system,
|
||||
* unless 'options' includes `essential: true`.
|
||||
*
|
||||
* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`,
|
||||
* `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`.
|
||||
@ -1384,7 +1387,7 @@ export abstract class Camera extends Evented {
|
||||
flyTo(options: FlyToOptions, eventData?: any): this {
|
||||
// Fall through to jumpTo if user has set prefers-reduced-motion
|
||||
if (!options.essential && browser.prefersReducedMotion) {
|
||||
const coercedOptions = pick(options, ['center', 'zoom', 'bearing', 'pitch', 'roll', 'elevation']) as CameraOptions;
|
||||
const coercedOptions = pick(options, ['center', 'zoom', 'bearing', 'pitch', 'roll', 'elevation', 'padding']) as JumpToOptions;
|
||||
return this.jumpTo(coercedOptions, eventData);
|
||||
}
|
||||
|
||||
@ -1593,7 +1596,7 @@ export abstract class Camera extends Evented {
|
||||
frame(1);
|
||||
finish();
|
||||
} else {
|
||||
this._easeStart = browser.now();
|
||||
this._easeStart = now();
|
||||
this._easeOptions = options;
|
||||
this._onEaseFrame = frame;
|
||||
this._onEaseEnd = finish;
|
||||
@ -1603,7 +1606,7 @@ export abstract class Camera extends Evented {
|
||||
|
||||
// Callback for map._requestRenderFrame
|
||||
_renderFrameCallback = () => {
|
||||
const t = Math.min((browser.now() - this._easeStart) / this._easeOptions.duration, 1);
|
||||
const t = Math.min((now() - this._easeStart) / this._easeOptions.duration, 1);
|
||||
this._onEaseFrame(this._easeOptions.easing(t));
|
||||
|
||||
// if _stop is called during _onEaseFrame from _fireMoveEvents we should avoid a new _requestRenderFrame, checking it by ensuring _easeFrameId was not deleted
|
||||
|
||||
210
node_modules/maplibre-gl/src/ui/control/geolocate_control.test.ts
generated
vendored
210
node_modules/maplibre-gl/src/ui/control/geolocate_control.test.ts
generated
vendored
@ -111,10 +111,118 @@ describe('GeolocateControl with no options', () => {
|
||||
expect(geolocate._geolocateButton.disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
test('error event', async () => {
|
||||
test('error event in waiting active state', async () => {
|
||||
const geolocate = new GeolocateControl(undefined);
|
||||
map.addControl(geolocate);
|
||||
await sleep(0);
|
||||
geolocate._watchState = 'WAITING_ACTIVE';
|
||||
|
||||
const click = new window.Event('click');
|
||||
const errorPromise = geolocate.once('error');
|
||||
geolocate._geolocateButton.dispatchEvent(click);
|
||||
|
||||
geolocation.sendError({code: 2, message: 'error message'});
|
||||
const error = await errorPromise;
|
||||
|
||||
expect(error.code).toBe(2);
|
||||
expect(error.message).toBe('error message');
|
||||
expect(geolocate._watchState).toBe('ACTIVE_ERROR');
|
||||
});
|
||||
|
||||
test('error event in active lock state', async () => {
|
||||
const geolocate = new GeolocateControl(undefined);
|
||||
map.addControl(geolocate);
|
||||
await sleep(0);
|
||||
geolocate._watchState = 'ACTIVE_LOCK';
|
||||
|
||||
const click = new window.Event('click');
|
||||
const errorPromise = geolocate.once('error');
|
||||
geolocate._geolocateButton.dispatchEvent(click);
|
||||
|
||||
geolocation.sendError({code: 2, message: 'error message'});
|
||||
const error = await errorPromise;
|
||||
|
||||
expect(error.code).toBe(2);
|
||||
expect(error.message).toBe('error message');
|
||||
expect(geolocate._watchState).toBe('ACTIVE_ERROR');
|
||||
});
|
||||
|
||||
test('error event in background state', async () => {
|
||||
const geolocate = new GeolocateControl(undefined);
|
||||
map.addControl(geolocate);
|
||||
await sleep(0);
|
||||
geolocate._watchState = 'BACKGROUND';
|
||||
|
||||
const click = new window.Event('click');
|
||||
const errorPromise = geolocate.once('error');
|
||||
geolocate._geolocateButton.dispatchEvent(click);
|
||||
|
||||
geolocation.sendError({code: 2, message: 'error message'});
|
||||
const error = await errorPromise;
|
||||
|
||||
expect(error.code).toBe(2);
|
||||
expect(error.message).toBe('error message');
|
||||
expect(geolocate._watchState).toBe('BACKGROUND_ERROR');
|
||||
});
|
||||
|
||||
test('error event in active error state', async () => {
|
||||
const geolocate = new GeolocateControl(undefined);
|
||||
map.addControl(geolocate);
|
||||
await sleep(0);
|
||||
geolocate._watchState = 'ACTIVE_ERROR';
|
||||
|
||||
const click = new window.Event('click');
|
||||
const errorPromise = geolocate.once('error');
|
||||
geolocate._geolocateButton.dispatchEvent(click);
|
||||
|
||||
geolocation.sendError({code: 2, message: 'error message'});
|
||||
const error = await errorPromise;
|
||||
|
||||
expect(error.code).toBe(2);
|
||||
expect(error.message).toBe('error message');
|
||||
expect(geolocate._watchState).toBe('ACTIVE_ERROR');
|
||||
});
|
||||
|
||||
test('error event in background error state', async () => {
|
||||
const geolocate = new GeolocateControl(undefined);
|
||||
map.addControl(geolocate);
|
||||
await sleep(0);
|
||||
geolocate._watchState = 'BACKGROUND_ERROR';
|
||||
|
||||
const click = new window.Event('click');
|
||||
const errorPromise = geolocate.once('error');
|
||||
geolocate._geolocateButton.dispatchEvent(click);
|
||||
|
||||
geolocation.sendError({code: 2, message: 'error message'});
|
||||
const error = await errorPromise;
|
||||
|
||||
expect(error.code).toBe(2);
|
||||
expect(error.message).toBe('error message');
|
||||
expect(geolocate._watchState).toBe('BACKGROUND_ERROR');
|
||||
});
|
||||
|
||||
test('error event in off state', async () => {
|
||||
const geolocate = new GeolocateControl(undefined);
|
||||
map.addControl(geolocate);
|
||||
await sleep(0);
|
||||
geolocate._watchState = 'OFF';
|
||||
|
||||
const click = new window.Event('click');
|
||||
const errorPromise = geolocate.once('error');
|
||||
geolocate._geolocateButton.dispatchEvent(click);
|
||||
|
||||
geolocation.sendError({code: 2, message: 'error message'});
|
||||
const error = await errorPromise;
|
||||
|
||||
expect(error.code).toBe(2);
|
||||
expect(error.message).toBe('error message');
|
||||
expect(geolocate._watchState).toBe('OFF');
|
||||
});
|
||||
|
||||
test('error event when trackUserLocation is false', async () => {
|
||||
const geolocate = new GeolocateControl({trackUserLocation: false});
|
||||
map.addControl(geolocate);
|
||||
await sleep(0);
|
||||
const click = new window.Event('click');
|
||||
const errorPromise = geolocate.once('error');
|
||||
geolocate._geolocateButton.dispatchEvent(click);
|
||||
@ -124,6 +232,7 @@ describe('GeolocateControl with no options', () => {
|
||||
|
||||
expect(error.code).toBe(2);
|
||||
expect(error.message).toBe('error message');
|
||||
expect(geolocate._watchState).toBeUndefined();
|
||||
});
|
||||
|
||||
test('does not throw if removed quickly', () => {
|
||||
@ -137,6 +246,26 @@ describe('GeolocateControl with no options', () => {
|
||||
map.removeControl(geolocate);
|
||||
});
|
||||
|
||||
test('outofmaxbounds event in waiting active state', async () => {
|
||||
const geolocate = new GeolocateControl(undefined);
|
||||
map.addControl(geolocate);
|
||||
await sleep(0);
|
||||
map.setMaxBounds([[0, 0], [10, 10]]);
|
||||
geolocate._watchState = 'WAITING_ACTIVE';
|
||||
|
||||
const click = new window.Event('click');
|
||||
|
||||
const promise = geolocate.once('outofmaxbounds');
|
||||
geolocate._geolocateButton.dispatchEvent(click);
|
||||
geolocation.send({latitude: 10, longitude: 20, accuracy: 3, timestamp: 4});
|
||||
const position = await promise;
|
||||
expect(geolocate._watchState).toBe('ACTIVE_ERROR');
|
||||
expect(position.coords.latitude).toBe(10);
|
||||
expect(position.coords.longitude).toBe(20);
|
||||
expect(position.coords.accuracy).toBe(3);
|
||||
expect(position.timestamp).toBe(4);
|
||||
});
|
||||
|
||||
test('outofmaxbounds event in active lock state', async () => {
|
||||
const geolocate = new GeolocateControl(undefined);
|
||||
map.addControl(geolocate);
|
||||
@ -178,6 +307,85 @@ describe('GeolocateControl with no options', () => {
|
||||
expect(position.timestamp).toBe(4);
|
||||
});
|
||||
|
||||
test('outofmaxbounds event in active error state', async () => {
|
||||
const geolocate = new GeolocateControl(undefined);
|
||||
map.addControl(geolocate);
|
||||
await sleep(0);
|
||||
map.setMaxBounds([[0, 0], [10, 10]]);
|
||||
geolocate._watchState = 'ACTIVE_ERROR';
|
||||
|
||||
const click = new window.Event('click');
|
||||
|
||||
const promise = geolocate.once('outofmaxbounds');
|
||||
geolocate._geolocateButton.dispatchEvent(click);
|
||||
geolocation.send({latitude: 10, longitude: 20, accuracy: 3, timestamp: 4});
|
||||
const position = await promise;
|
||||
expect(geolocate._watchState).toBe('ACTIVE_ERROR');
|
||||
expect(position.coords.latitude).toBe(10);
|
||||
expect(position.coords.longitude).toBe(20);
|
||||
expect(position.coords.accuracy).toBe(3);
|
||||
expect(position.timestamp).toBe(4);
|
||||
});
|
||||
|
||||
test('outofmaxbounds event in background error state', async () => {
|
||||
const geolocate = new GeolocateControl(undefined);
|
||||
map.addControl(geolocate);
|
||||
await sleep(0);
|
||||
map.setMaxBounds([[0, 0], [10, 10]]);
|
||||
geolocate._watchState = 'BACKGROUND_ERROR';
|
||||
|
||||
const click = new window.Event('click');
|
||||
|
||||
const promise = geolocate.once('outofmaxbounds');
|
||||
geolocate._geolocateButton.dispatchEvent(click);
|
||||
geolocation.send({latitude: 10, longitude: 20, accuracy: 3, timestamp: 4});
|
||||
const position = await promise;
|
||||
expect(geolocate._watchState).toBe('BACKGROUND_ERROR');
|
||||
expect(position.coords.latitude).toBe(10);
|
||||
expect(position.coords.longitude).toBe(20);
|
||||
expect(position.coords.accuracy).toBe(3);
|
||||
expect(position.timestamp).toBe(4);
|
||||
});
|
||||
|
||||
test('outofmaxbounds event in off state', async () => {
|
||||
const geolocate = new GeolocateControl(undefined);
|
||||
map.addControl(geolocate);
|
||||
await sleep(0);
|
||||
map.setMaxBounds([[0, 0], [10, 10]]);
|
||||
geolocate._watchState = 'OFF';
|
||||
|
||||
const click = new window.Event('click');
|
||||
|
||||
const promise = geolocate.once('outofmaxbounds');
|
||||
geolocate._geolocateButton.dispatchEvent(click);
|
||||
geolocation.send({latitude: 10, longitude: 20, accuracy: 3, timestamp: 4});
|
||||
const position = await promise;
|
||||
expect(geolocate._watchState).toBe('OFF');
|
||||
expect(position.coords.latitude).toBe(10);
|
||||
expect(position.coords.longitude).toBe(20);
|
||||
expect(position.coords.accuracy).toBe(3);
|
||||
expect(position.timestamp).toBe(4);
|
||||
});
|
||||
|
||||
test('outofmaxbounds event when trackUserLocation = false', async () => {
|
||||
const geolocate = new GeolocateControl({trackUserLocation: false});
|
||||
map.addControl(geolocate);
|
||||
await sleep(0);
|
||||
map.setMaxBounds([[0, 0], [10, 10]]);
|
||||
|
||||
const click = new window.Event('click');
|
||||
|
||||
const promise = geolocate.once('outofmaxbounds');
|
||||
geolocate._geolocateButton.dispatchEvent(click);
|
||||
geolocation.send({latitude: 10, longitude: 20, accuracy: 3, timestamp: 4});
|
||||
const position = await promise;
|
||||
expect(geolocate._watchState).toBeUndefined();
|
||||
expect(position.coords.latitude).toBe(10);
|
||||
expect(position.coords.longitude).toBe(20);
|
||||
expect(position.coords.accuracy).toBe(3);
|
||||
expect(position.timestamp).toBe(4);
|
||||
});
|
||||
|
||||
test('geolocate event', async () => {
|
||||
const geolocate = new GeolocateControl(undefined);
|
||||
map.addControl(geolocate);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user