From 30dfd3022ddf925c2775b0014f7ce7664dd8fc83 Mon Sep 17 00:00:00 2001 From: Joseph Beuckman Date: Thu, 7 Apr 2022 00:47:56 -0500 Subject: [PATCH] Stub Function To Find Pixel Color --- debug/query-raster.html | 70 ++ src/source/query_raster_values.js | 80 ++ src/source/tile.js | 535 ++++++++--- src/style/style.js | 1288 ++++++++++++++++++-------- src/ui/map.js | 1433 ++++++++++++++++++++--------- 5 files changed, 2460 insertions(+), 946 deletions(-) create mode 100644 debug/query-raster.html create mode 100644 src/source/query_raster_values.js diff --git a/debug/query-raster.html b/debug/query-raster.html new file mode 100644 index 00000000000..c1dc1ce541d --- /dev/null +++ b/debug/query-raster.html @@ -0,0 +1,70 @@ + + + + Mapbox GL JS debug page + + + + + + + +
+
+ +
+ + + + + + diff --git a/src/source/query_raster_values.js b/src/source/query_raster_values.js new file mode 100644 index 00000000000..cdb166ab6ed --- /dev/null +++ b/src/source/query_raster_values.js @@ -0,0 +1,80 @@ +// @flow + +import type SourceCache from "./source_cache.js"; +import type StyleLayer from "../style/style_layer.js"; +import type CollisionIndex from "../symbol/collision_index.js"; +import type Transform from "../geo/transform.js"; +import type { RetainedQueryData } from "../symbol/placement.js"; +import type { FilterSpecification } from "../style-spec/types.js"; +import type { QueryGeometry } from "../style/query_geometry.js"; +import assert from "assert"; +import { mat4 } from "gl-matrix"; + +import type Point from "@mapbox/point-geometry"; +import type { QueryResult } from "../data/feature_index.js"; +import type { QueryFeature } from "../util/vectortile_to_geojson.js"; + +/* + * Returns a matrix that can be used to convert from tile coordinates to viewport pixel coordinates. + */ +function getPixelPosMatrix(transform, tileID) { + const t = mat4.identity([]); + mat4.scale(t, t, [transform.width * 0.5, -transform.height * 0.5, 1]); + mat4.translate(t, t, [1, -1, 0]); + mat4.multiply(t, t, transform.calculateProjMatrix(tileID.toUnwrapped())); + return Float32Array.from(t); +} + +export function queryRasterSource( + sourceCache: SourceCache, + styleLayers: { [_: string]: StyleLayer }, + serializedLayers: { [_: string]: Object }, + queryGeometry: QueryGeometry, + params: { + filter: FilterSpecification, + layers: Array, + availableImages: Array, + }, + transform: Transform, + use3DQuery: boolean, + visualizeQueryGeometry: boolean = false +): QueryResult { + const tileResults = sourceCache.tilesIn( + queryGeometry, + use3DQuery, + visualizeQueryGeometry + ); + tileResults.sort(sortTilesIn); + const results = []; + for (const tileResult of tileResults) { + results.push({ + wrappedTileID: tileResult.tile.tileID.wrapped().key, + queryResults: tileResult.tile.queryRasterValues( + styleLayers, + serializedLayers, + sourceCache._state, + tileResult, + params, + transform, + getPixelPosMatrix( + sourceCache.transform, + tileResult.tile.tileID + ), + visualizeQueryGeometry + ), + }); + } + + return results; +} + +function sortTilesIn(a, b) { + const idA = a.tileID; + const idB = b.tileID; + return ( + idA.overscaledZ - idB.overscaledZ || + idA.canonical.y - idB.canonical.y || + idA.wrap - idB.wrap || + idA.canonical.x - idB.canonical.x + ); +} diff --git a/src/source/tile.js b/src/source/tile.js index c7d709283ae..856193bc3a3 100644 --- a/src/source/tile.js +++ b/src/source/tile.js @@ -1,73 +1,87 @@ // @flow -import {uniqueId, parseCacheControl} from '../util/util.js'; -import {deserialize as deserializeBucket} from '../data/bucket.js'; -import FeatureIndex from '../data/feature_index.js'; -import GeoJSONFeature from '../util/vectortile_to_geojson.js'; -import featureFilter from '../style-spec/feature_filter/index.js'; -import SymbolBucket from '../data/bucket/symbol_bucket.js'; -import {CollisionBoxArray, TileBoundsArray, PosArray, TriangleIndexArray, LineStripIndexArray, PosGlobeExtArray} from '../data/array_types.js'; -import Texture from '../render/texture.js'; -import browser from '../util/browser.js'; -import {Debug} from '../util/debug.js'; -import toEvaluationFeature from '../data/evaluation_feature.js'; -import EvaluationParameters from '../style/evaluation_parameters.js'; -import SourceFeatureState from '../source/source_state.js'; -import {lazyLoadRTLTextPlugin} from './rtl_text_plugin.js'; -import {TileSpaceDebugBuffer} from '../data/debug_viz.js'; -import Color from '../style-spec/util/color.js'; -import loadGeometry from '../data/load_geometry.js'; -import earcut from 'earcut'; -import getTileMesh from './tile_mesh.js'; -import tileTransform from '../geo/projection/tile_transform.js'; - -import boundsAttributes from '../data/bounds_attributes.js'; -import posAttributes, {posAttributesGlobeExt} from '../data/pos_attributes.js'; - -import EXTENT from '../data/extent.js'; -import Point from '@mapbox/point-geometry'; -import SegmentVector from '../data/segment.js'; +import { uniqueId, parseCacheControl } from "../util/util.js"; +import { deserialize as deserializeBucket } from "../data/bucket.js"; +import FeatureIndex from "../data/feature_index.js"; +import GeoJSONFeature from "../util/vectortile_to_geojson.js"; +import featureFilter from "../style-spec/feature_filter/index.js"; +import SymbolBucket from "../data/bucket/symbol_bucket.js"; +import { + CollisionBoxArray, + TileBoundsArray, + PosArray, + TriangleIndexArray, + LineStripIndexArray, + PosGlobeExtArray, +} from "../data/array_types.js"; +import Texture from "../render/texture.js"; +import browser from "../util/browser.js"; +import { Debug } from "../util/debug.js"; +import toEvaluationFeature from "../data/evaluation_feature.js"; +import EvaluationParameters from "../style/evaluation_parameters.js"; +import SourceFeatureState from "../source/source_state.js"; +import { lazyLoadRTLTextPlugin } from "./rtl_text_plugin.js"; +import { TileSpaceDebugBuffer } from "../data/debug_viz.js"; +import Color from "../style-spec/util/color.js"; +import loadGeometry from "../data/load_geometry.js"; +import earcut from "earcut"; +import getTileMesh from "./tile_mesh.js"; +import tileTransform from "../geo/projection/tile_transform.js"; + +import boundsAttributes from "../data/bounds_attributes.js"; +import posAttributes, { + posAttributesGlobeExt, +} from "../data/pos_attributes.js"; + +import EXTENT from "../data/extent.js"; +import Point from "@mapbox/point-geometry"; +import SegmentVector from "../data/segment.js"; const CLOCK_SKEW_RETRY_TIMEOUT = 30000; -import type {Bucket} from '../data/bucket.js'; -import FillBucket from '../data/bucket/fill_bucket.js'; -import LineBucket from '../data/bucket/line_bucket.js'; -import type StyleLayer from '../style/style_layer.js'; -import type {WorkerTileResult} from './worker_source.js'; -import type Actor from '../util/actor.js'; -import type DEMData from '../data/dem_data.js'; -import type {AlphaImage, SpritePositions} from '../util/image.js'; -import type ImageAtlas from '../render/image_atlas.js'; -import type LineAtlas from '../render/line_atlas.js'; -import type ImageManager from '../render/image_manager.js'; -import type Context from '../gl/context.js'; -import type {CanonicalTileID, OverscaledTileID} from './tile_id.js'; -import type Framebuffer from '../gl/framebuffer.js'; -import type Transform from '../geo/transform.js'; -import type {LayerFeatureStates} from './source_state.js'; -import type {Cancelable} from '../types/cancelable.js'; -import type {FilterSpecification} from '../style-spec/types.js'; -import type {TilespaceQueryGeometry} from '../style/query_geometry.js'; -import type VertexBuffer from '../gl/vertex_buffer.js'; -import type IndexBuffer from '../gl/index_buffer.js'; -import type Projection from '../geo/projection/projection.js'; -import type {TileTransform} from '../geo/projection/tile_transform.js'; -import type {QueryResult} from '../data/feature_index.js'; -import type Painter from '../render/painter.js'; -import type {QueryFeature} from '../util/vectortile_to_geojson.js'; -import {globeTileBounds, globeNormalizeECEF, tileCoordToECEF} from '../geo/projection/globe_util.js'; -import {vec3} from 'gl-matrix'; -import type {TextureImage} from '../render/texture.js'; +import type { Bucket } from "../data/bucket.js"; +import FillBucket from "../data/bucket/fill_bucket.js"; +import LineBucket from "../data/bucket/line_bucket.js"; +import type StyleLayer from "../style/style_layer.js"; +import type { WorkerTileResult } from "./worker_source.js"; +import type Actor from "../util/actor.js"; +import type DEMData from "../data/dem_data.js"; +import type { AlphaImage, SpritePositions } from "../util/image.js"; +import type ImageAtlas from "../render/image_atlas.js"; +import type LineAtlas from "../render/line_atlas.js"; +import type ImageManager from "../render/image_manager.js"; +import type Context from "../gl/context.js"; +import type { CanonicalTileID, OverscaledTileID } from "./tile_id.js"; +import type Framebuffer from "../gl/framebuffer.js"; +import type Transform from "../geo/transform.js"; +import type { LayerFeatureStates } from "./source_state.js"; +import type { Cancelable } from "../types/cancelable.js"; +import type { FilterSpecification } from "../style-spec/types.js"; +import type { TilespaceQueryGeometry } from "../style/query_geometry.js"; +import type VertexBuffer from "../gl/vertex_buffer.js"; +import type IndexBuffer from "../gl/index_buffer.js"; +import type Projection from "../geo/projection/projection.js"; +import type { TileTransform } from "../geo/projection/tile_transform.js"; +import type { QueryResult } from "../data/feature_index.js"; +import type Painter from "../render/painter.js"; +import type { QueryFeature } from "../util/vectortile_to_geojson.js"; +import { + globeTileBounds, + globeNormalizeECEF, + tileCoordToECEF, +} from "../geo/projection/globe_util.js"; +import { vec3 } from "gl-matrix"; +import type { TextureImage } from "../render/texture.js"; export type TileState = - | 'loading' // Tile data is in the process of loading. - | 'loaded' // Tile data has been loaded. Tile can be rendered. - | 'reloading' // Tile data has been loaded and is being updated. Tile can be rendered. - | 'unloaded' // Tile data has been deleted. - | 'errored' // Tile data was not loaded because of an error. - | 'expired'; /* Tile data was previously loaded, but has expired per its - * HTTP headers and is in the process of refreshing. */ + | "loading" // Tile data is in the process of loading. + | "loaded" // Tile data has been loaded. Tile can be rendered. + | "reloading" // Tile data has been loaded and is being updated. Tile can be rendered. + | "unloaded" // Tile data has been deleted. + | "errored" // Tile data was not loaded because of an error. + | "expired"; +/* Tile data was previously loaded, but has expired per its + * HTTP headers and is in the process of refreshing. */ // a tile bounds outline used for getting reprojected tile geometry in non-mercator projections const BOUNDS_FEATURE = (() => { @@ -75,14 +89,16 @@ const BOUNDS_FEATURE = (() => { type: 2, extent: EXTENT, loadGeometry() { - return [[ - new Point(0, 0), - new Point(EXTENT + 1, 0), - new Point(EXTENT + 1, EXTENT + 1), - new Point(0, EXTENT + 1), - new Point(0, 0) - ]]; - } + return [ + [ + new Point(0, 0), + new Point(EXTENT + 1, 0), + new Point(EXTENT + 1, EXTENT + 1), + new Point(0, EXTENT + 1), + new Point(0, 0), + ], + ]; + }, }; })(); @@ -98,7 +114,7 @@ class Tile { uses: number; tileSize: number; tileZoom: number; - buckets: {[_: string]: Bucket}; + buckets: { [_: string]: Bucket }; latestFeatureIndex: ?FeatureIndex; latestRawTileData: ?ArrayBuffer; imageAtlas: ?ImageAtlas; @@ -117,7 +133,7 @@ class Tile { showCollisionBoxes: boolean; placementSource: any; actor: ?Actor; - vtLayers: {[_: string]: VectorTileLayer}; + vtLayers: { [_: string]: VectorTileLayer }; isSymbolTile: ?boolean; isRaster: ?boolean; _tileTransform: TileTransform; @@ -162,7 +178,13 @@ class Tile { * @param size * @private */ - constructor(tileID: OverscaledTileID, size: number, tileZoom: number, painter: any, isRaster?: boolean) { + constructor( + tileID: OverscaledTileID, + size: number, + tileZoom: number, + painter: any, + isRaster?: boolean + ) { this.tileID = tileID; this.uid = uniqueId(); this.uses = 0; @@ -182,7 +204,7 @@ class Tile { // serving expired tiles. this.expiredRequestCount = 0; - this.state = 'loading'; + this.state = "loading"; if (painter && painter.transform) { this.projection = painter.transform.projection; @@ -198,12 +220,19 @@ class Tile { } wasRequested(): boolean { - return this.state === 'errored' || this.state === 'loaded' || this.state === 'reloading'; + return ( + this.state === "errored" || + this.state === "loaded" || + this.state === "reloading" + ); } get tileTransform(): TileTransform { if (!this._tileTransform) { - this._tileTransform = tileTransform(this.tileID.canonical, this.projection); + this._tileTransform = tileTransform( + this.tileID.canonical, + this.projection + ); } return this._tileTransform; } @@ -218,10 +247,14 @@ class Tile { * @returns {undefined} * @private */ - loadVectorData(data: ?WorkerTileResult, painter: any, justReloaded: ?boolean) { + loadVectorData( + data: ?WorkerTileResult, + painter: any, + justReloaded: ?boolean + ) { this.unloadVectorData(); - this.state = 'loaded'; + this.state = "loaded"; // empty GeoJSON tile if (!data) { @@ -275,7 +308,10 @@ class Tile { this.queryPadding = 0; for (const id in this.buckets) { const bucket = this.buckets[id]; - this.queryPadding = Math.max(this.queryPadding, painter.style.getLayer(id).queryRadius(bucket)); + this.queryPadding = Math.max( + this.queryPadding, + painter.style.getLayer(id).queryRadius(bucket) + ); } if (data.imageAtlas) { @@ -364,7 +400,7 @@ class Tile { } }); this.latestFeatureIndex = null; - this.state = 'unloaded'; + this.state = "unloaded"; } getBucket(layer: StyleLayer): Bucket { @@ -381,46 +417,96 @@ class Tile { const gl = context.gl; if (this.imageAtlas && !this.imageAtlas.uploaded) { - this.imageAtlasTexture = new Texture(context, this.imageAtlas.image, gl.RGBA); + this.imageAtlasTexture = new Texture( + context, + this.imageAtlas.image, + gl.RGBA + ); this.imageAtlas.uploaded = true; } if (this.glyphAtlasImage) { - this.glyphAtlasTexture = new Texture(context, this.glyphAtlasImage, gl.ALPHA); + this.glyphAtlasTexture = new Texture( + context, + this.glyphAtlasImage, + gl.ALPHA + ); this.glyphAtlasImage = null; } if (this.lineAtlas && !this.lineAtlas.uploaded) { - this.lineAtlasTexture = new Texture(context, this.lineAtlas.image, gl.ALPHA); + this.lineAtlasTexture = new Texture( + context, + this.lineAtlas.image, + gl.ALPHA + ); this.lineAtlas.uploaded = true; } } prepare(imageManager: ImageManager) { if (this.imageAtlas) { - this.imageAtlas.patchUpdatedImages(imageManager, this.imageAtlasTexture); + this.imageAtlas.patchUpdatedImages( + imageManager, + this.imageAtlasTexture + ); } } + // Find raster value at a given location + queryRasterValues( + layers: { [_: string]: StyleLayer }, + serializedLayers: { [string]: Object }, + sourceFeatureState: SourceFeatureState, + tileResult: TilespaceQueryGeometry, + params: { + filter: FilterSpecification, + layers: Array, + availableImages: Array, + }, + transform: Transform, + pixelPosMatrix: Float32Array, + visualizeQueryGeometry: boolean + ): Color { + console.log("queryRasterValues", tileResult, pixelPosMatrix); + + // const texturePos = pixelPosMatrix.getTexturePos() + + // const color = tileResult.tile.texture.readPixels(texturePos.x, texturePos.y, 1, 1, type, ???) + + // return color + } + // Queries non-symbol features rendered for this tile. // Symbol features are queried globally - queryRenderedFeatures(layers: {[_: string]: StyleLayer}, - serializedLayers: {[string]: Object}, - sourceFeatureState: SourceFeatureState, - tileResult: TilespaceQueryGeometry, - params: { filter: FilterSpecification, layers: Array, availableImages: Array }, - transform: Transform, - pixelPosMatrix: Float32Array, - visualizeQueryGeometry: boolean): QueryResult { + queryRenderedFeatures( + layers: { [_: string]: StyleLayer }, + serializedLayers: { [string]: Object }, + sourceFeatureState: SourceFeatureState, + tileResult: TilespaceQueryGeometry, + params: { + filter: FilterSpecification, + layers: Array, + availableImages: Array, + }, + transform: Transform, + pixelPosMatrix: Float32Array, + visualizeQueryGeometry: boolean + ): QueryResult { Debug.run(() => { if (visualizeQueryGeometry) { let geometryViz = this.queryGeometryDebugViz; let boundsViz = this.queryBoundsDebugViz; if (!geometryViz) { - geometryViz = this.queryGeometryDebugViz = new TileSpaceDebugBuffer(this.tileSize); + geometryViz = this.queryGeometryDebugViz = new TileSpaceDebugBuffer( + this.tileSize + ); } if (!boundsViz) { - boundsViz = this.queryBoundsDebugViz = new TileSpaceDebugBuffer(this.tileSize, Color.blue); + boundsViz = this.queryBoundsDebugViz = new TileSpaceDebugBuffer( + this.tileSize, + Color.blue + ); } geometryViz.addPoints(tileResult.tilespaceGeometry); @@ -431,13 +517,18 @@ class Tile { if (!this.latestFeatureIndex || !this.latestFeatureIndex.rawTileData) return {}; - return this.latestFeatureIndex.query({ - tileResult, - pixelPosMatrix, - transform, - params, - tileTransform: this.tileTransform - }, layers, serializedLayers, sourceFeatureState); + return this.latestFeatureIndex.query( + { + tileResult, + pixelPosMatrix, + transform, + params, + tileTransform: this.tileTransform, + }, + layers, + serializedLayers, + sourceFeatureState + ); } querySourceFeatures(result: Array, params: any) { @@ -446,21 +537,33 @@ class Tile { const vtLayers = featureIndex.loadVTLayers(); - const sourceLayer = params ? params.sourceLayer : ''; + const sourceLayer = params ? params.sourceLayer : ""; const layer = vtLayers._geojsonTileLayer || vtLayers[sourceLayer]; if (!layer) return; const filter = featureFilter(params && params.filter); - const {z, x, y} = this.tileID.canonical; - const coord = {z, x, y}; + const { z, x, y } = this.tileID.canonical; + const coord = { z, x, y }; for (let i = 0; i < layer.length; i++) { const feature = layer.feature(i); if (filter.needGeometry) { const evaluationFeature = toEvaluationFeature(feature, true); - if (!filter.filter(new EvaluationParameters(this.tileID.overscaledZ), evaluationFeature, this.tileID.canonical)) continue; - } else if (!filter.filter(new EvaluationParameters(this.tileID.overscaledZ), feature)) { + if ( + !filter.filter( + new EvaluationParameters(this.tileID.overscaledZ), + evaluationFeature, + this.tileID.canonical + ) + ) + continue; + } else if ( + !filter.filter( + new EvaluationParameters(this.tileID.overscaledZ), + feature + ) + ) { continue; } const id = featureIndex.getId(feature, sourceLayer); @@ -472,11 +575,18 @@ class Tile { } hasData(): boolean { - return this.state === 'loaded' || this.state === 'reloading' || this.state === 'expired'; + return ( + this.state === "loaded" || + this.state === "reloading" || + this.state === "expired" + ); } patternsLoaded(): boolean { - return !!this.imageAtlas && !!Object.keys(this.imageAtlas.patternPositions).length; + return ( + !!this.imageAtlas && + !!Object.keys(this.imageAtlas.patternPositions).length + ); } setExpiryData(data: any) { @@ -484,7 +594,8 @@ class Tile { if (data.cacheControl) { const parsedCC = parseCacheControl(data.cacheControl); - if (parsedCC['max-age']) this.expirationTime = Date.now() + parsedCC['max-age'] * 1000; + if (parsedCC["max-age"]) + this.expirationTime = Date.now() + parsedCC["max-age"] * 1000; } else if (data.expires) { this.expirationTime = new Date(data.expires).getTime(); } @@ -501,7 +612,6 @@ class Tile { // Expiring date is going backwards: // fall back to exponential backoff isExpired = true; - } else { const delta = this.expirationTime - prior; @@ -509,19 +619,18 @@ class Tile { // Server is serving the same expired resource over and over: fall // back to exponential backoff. isExpired = true; - } else { // Assume that either the client or the server clock is wrong and // try to interpolate a valid expiration date (from the client POV) // observing a minimum timeout. - this.expirationTime = now + Math.max(delta, CLOCK_SKEW_RETRY_TIMEOUT); - + this.expirationTime = + now + Math.max(delta, CLOCK_SKEW_RETRY_TIMEOUT); } } if (isExpired) { this.expiredRequestCount++; - this.state = 'expired'; + this.state = "expired"; } else { this.expiredRequestCount = 0; } @@ -534,16 +643,21 @@ class Tile { return 1000 * (1 << Math.min(this.expiredRequestCount - 1, 31)); } else { // Max value for `setTimeout` implementations is a 32 bit integer; cap this accordingly - return Math.min(this.expirationTime - new Date().getTime(), Math.pow(2, 31) - 1); + return Math.min( + this.expirationTime - new Date().getTime(), + Math.pow(2, 31) - 1 + ); } } } setFeatureState(states: LayerFeatureStates, painter: ?Painter) { - if (!this.latestFeatureIndex || + if ( + !this.latestFeatureIndex || !this.latestFeatureIndex.rawTileData || Object.keys(states).length === 0 || - !painter) { + !painter + ) { return; } @@ -555,23 +669,49 @@ class Tile { const bucket = this.buckets[id]; // Buckets are grouped by common source-layer - const sourceLayerId = bucket.layers[0]['sourceLayer'] || '_geojsonTileLayer'; + const sourceLayerId = + bucket.layers[0]["sourceLayer"] || "_geojsonTileLayer"; const sourceLayer = vtLayers[sourceLayerId]; const sourceLayerStates = states[sourceLayerId]; - if (!sourceLayer || !sourceLayerStates || Object.keys(sourceLayerStates).length === 0) continue; + if ( + !sourceLayer || + !sourceLayerStates || + Object.keys(sourceLayerStates).length === 0 + ) + continue; // $FlowFixMe[incompatible-type] Flow can't interpret ImagePosition as SpritePosition for some reason here - const imagePositions: SpritePositions = (this.imageAtlas && this.imageAtlas.patternPositions) || {}; - bucket.update(sourceLayerStates, sourceLayer, availableImages, imagePositions); + const imagePositions: SpritePositions = + (this.imageAtlas && this.imageAtlas.patternPositions) || {}; + bucket.update( + sourceLayerStates, + sourceLayer, + availableImages, + imagePositions + ); if (bucket instanceof LineBucket || bucket instanceof FillBucket) { - const sourceCache = painter.style._getSourceCache(bucket.layers[0].source); - if (painter._terrain && painter._terrain.enabled && sourceCache && bucket.programConfigurations.needsUpload) { - painter._terrain._clearRenderCacheForTile(sourceCache.id, this.tileID); + const sourceCache = painter.style._getSourceCache( + bucket.layers[0].source + ); + if ( + painter._terrain && + painter._terrain.enabled && + sourceCache && + bucket.programConfigurations.needsUpload + ) { + painter._terrain._clearRenderCacheForTile( + sourceCache.id, + this.tileID + ); } } - const layer = painter && painter.style && painter.style.getLayer(id); + const layer = + painter && painter.style && painter.style.getLayer(id); if (layer) { - this.queryPadding = Math.max(this.queryPadding, layer.queryRadius(bucket)); + this.queryPadding = Math.max( + this.queryPadding, + layer.queryRadius(bucket) + ); } } } @@ -581,7 +721,10 @@ class Tile { } symbolFadeFinished(): boolean { - return !this.symbolFadeHoldUntil || this.symbolFadeHoldUntil < browser.now(); + return ( + !this.symbolFadeHoldUntil || + this.symbolFadeHoldUntil < browser.now() + ); } clearFadeHold() { @@ -597,13 +740,20 @@ class Tile { const gl = context.gl; this.texture = painter.getTileTexture(img.width); if (this.texture) { - this.texture.update(img, {useMipmap: true}); + this.texture.update(img, { useMipmap: true }); } else { - this.texture = new Texture(context, img, gl.RGBA, {useMipmap: true}); + this.texture = new Texture(context, img, gl.RGBA, { + useMipmap: true, + }); this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE); if (context.extTextureFilterAnisotropic) { - gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, context.extTextureFilterAnisotropicMax); + gl.texParameterf( + gl.TEXTURE_2D, + context.extTextureFilterAnisotropic + .TEXTURE_MAX_ANISOTROPY_EXT, + context.extTextureFilterAnisotropicMax + ); } } } @@ -642,32 +792,58 @@ class Tile { } _makeDebugTileBoundsBuffers(context: Context, projection: Projection) { - if (!projection || projection.name === 'mercator' || this._tileDebugBuffer) return; + if ( + !projection || + projection.name === "mercator" || + this._tileDebugBuffer + ) + return; // reproject tile outline with adaptive resampling - const boundsLine = loadGeometry(BOUNDS_FEATURE, this.tileID.canonical, this.tileTransform)[0]; + const boundsLine = loadGeometry( + BOUNDS_FEATURE, + this.tileID.canonical, + this.tileTransform + )[0]; // generate vertices for debugging tile boundaries const debugVertices = new PosArray(); const debugIndices = new LineStripIndexArray(); for (let i = 0; i < boundsLine.length; i++) { - const {x, y} = boundsLine[i]; + const { x, y } = boundsLine[i]; debugVertices.emplaceBack(x, y); debugIndices.emplaceBack(i); } debugIndices.emplaceBack(0); this._tileDebugIndexBuffer = context.createIndexBuffer(debugIndices); - this._tileDebugBuffer = context.createVertexBuffer(debugVertices, posAttributes.members); - this._tileDebugSegments = SegmentVector.simpleSegment(0, 0, debugVertices.length, debugIndices.length); + this._tileDebugBuffer = context.createVertexBuffer( + debugVertices, + posAttributes.members + ); + this._tileDebugSegments = SegmentVector.simpleSegment( + 0, + 0, + debugVertices.length, + debugIndices.length + ); } _makeTileBoundsBuffers(context: Context, projection: Projection) { - if (this._tileBoundsBuffer || !projection || projection.name === 'mercator') return; + if ( + this._tileBoundsBuffer || + !projection || + projection.name === "mercator" + ) + return; // reproject tile outline with adaptive resampling - const boundsLine = loadGeometry(BOUNDS_FEATURE, this.tileID.canonical, this.tileTransform)[0]; + const boundsLine = loadGeometry( + BOUNDS_FEATURE, + this.tileID.canonical, + this.tileTransform + )[0]; let boundsVertices, boundsIndices; if (this.isRaster) { @@ -675,27 +851,44 @@ class Tile { const mesh = getTileMesh(this.tileID.canonical, projection); boundsVertices = mesh.vertices; boundsIndices = mesh.indices; - } else { // for vector tiles, generate an Earcut triangulation of the outline boundsVertices = new TileBoundsArray(); boundsIndices = new TriangleIndexArray(); - for (const {x, y} of boundsLine) { + for (const { x, y } of boundsLine) { boundsVertices.emplaceBack(x, y, 0, 0); } const indices = earcut(boundsVertices.int16, undefined, 4); for (let i = 0; i < indices.length; i += 3) { - boundsIndices.emplaceBack(indices[i], indices[i + 1], indices[i + 2]); + boundsIndices.emplaceBack( + indices[i], + indices[i + 1], + indices[i + 2] + ); } } - this._tileBoundsBuffer = context.createVertexBuffer(boundsVertices, boundsAttributes.members); + this._tileBoundsBuffer = context.createVertexBuffer( + boundsVertices, + boundsAttributes.members + ); this._tileBoundsIndexBuffer = context.createIndexBuffer(boundsIndices); - this._tileBoundsSegments = SegmentVector.simpleSegment(0, 0, boundsVertices.length, boundsIndices.length); + this._tileBoundsSegments = SegmentVector.simpleSegment( + 0, + 0, + boundsVertices.length, + boundsIndices.length + ); } _makeGlobeTileDebugBuffers(context: Context, projection: Projection) { - if (this._globeTileDebugBorderBuffer || this._globeTileDebugTextBuffer || !projection || projection.name !== 'globe') return; + if ( + this._globeTileDebugBorderBuffer || + this._globeTileDebugTextBuffer || + !projection || + projection.name !== "globe" + ) + return; const id = this.tileID.canonical; const bounds = globeTileBounds(id); @@ -705,12 +898,22 @@ class Tile { this._makeGlobeTileDebugTextBuffer(context, id, normalizationMatrix); } - _makeGlobeTileDebugBorderBuffer(context: Context, id: CanonicalTileID, normalizationMatrix: Float64Array) { + _makeGlobeTileDebugBorderBuffer( + context: Context, + id: CanonicalTileID, + normalizationMatrix: Float64Array + ) { const vertices = new PosArray(); const indices = new LineStripIndexArray(); const extraGlobe = new PosGlobeExtArray(); - const addLine = (sx: number, sy: number, ex: number, ey: number, pointCount: number) => { + const addLine = ( + sx: number, + sy: number, + ex: number, + ey: number, + pointCount: number + ) => { const stepX = (ex - sx) / (pointCount - 1); const stepY = (ey - sy) / (pointCount - 1); @@ -738,12 +941,27 @@ class Tile { addLine(0, e, 0, 0, 16); this._tileDebugIndexBuffer = context.createIndexBuffer(indices); - this._tileDebugBuffer = context.createVertexBuffer(vertices, posAttributes.members); - this._globeTileDebugBorderBuffer = context.createVertexBuffer(extraGlobe, posAttributesGlobeExt.members); - this._tileDebugSegments = SegmentVector.simpleSegment(0, 0, vertices.length, indices.length); + this._tileDebugBuffer = context.createVertexBuffer( + vertices, + posAttributes.members + ); + this._globeTileDebugBorderBuffer = context.createVertexBuffer( + extraGlobe, + posAttributesGlobeExt.members + ); + this._tileDebugSegments = SegmentVector.simpleSegment( + 0, + 0, + vertices.length, + indices.length + ); } - _makeGlobeTileDebugTextBuffer(context: Context, id: CanonicalTileID, normalizationMatrix: Float64Array) { + _makeGlobeTileDebugTextBuffer( + context: Context, + id: CanonicalTileID, + normalizationMatrix: Float64Array + ) { const SEGMENTS = 4; const numVertices = SEGMENTS + 1; const step = EXTENT / SEGMENTS; @@ -792,9 +1010,20 @@ class Tile { } this._tileDebugTextIndexBuffer = context.createIndexBuffer(indices); - this._tileDebugTextBuffer = context.createVertexBuffer(vertices, posAttributes.members); - this._globeTileDebugTextBuffer = context.createVertexBuffer(extraGlobe, posAttributesGlobeExt.members); - this._tileDebugTextSegments = SegmentVector.simpleSegment(0, 0, totalVertices, totalTriangles); + this._tileDebugTextBuffer = context.createVertexBuffer( + vertices, + posAttributes.members + ); + this._globeTileDebugTextBuffer = context.createVertexBuffer( + extraGlobe, + posAttributesGlobeExt.members + ); + this._tileDebugTextSegments = SegmentVector.simpleSegment( + 0, + 0, + totalVertices, + totalTriangles + ); } } diff --git a/src/style/style.js b/src/style/style.js index 18280274cab..06028c2f91e 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -1,71 +1,86 @@ // @flow -import assert from 'assert'; - -import {Event, ErrorEvent, Evented} from '../util/evented.js'; -import StyleLayer from './style_layer.js'; -import createStyleLayer from './create_style_layer.js'; -import loadSprite from './load_sprite.js'; -import ImageManager from '../render/image_manager.js'; -import GlyphManager, {LocalGlyphMode} from '../render/glyph_manager.js'; -import Light from './light.js'; -import Terrain, {DrapeRenderMode} from './terrain.js'; -import Fog from './fog.js'; -import LineAtlas from '../render/line_atlas.js'; -import {pick, clone, extend, deepEqual, filterObject} from '../util/util.js'; -import {getJSON, getReferrer, makeRequest, ResourceType} from '../util/ajax.js'; -import {isMapboxURL} from '../util/mapbox.js'; -import browser from '../util/browser.js'; -import Dispatcher from '../util/dispatcher.js'; +import assert from "assert"; + +import { Event, ErrorEvent, Evented } from "../util/evented.js"; +import StyleLayer from "./style_layer.js"; +import createStyleLayer from "./create_style_layer.js"; +import loadSprite from "./load_sprite.js"; +import ImageManager from "../render/image_manager.js"; +import GlyphManager, { LocalGlyphMode } from "../render/glyph_manager.js"; +import Light from "./light.js"; +import Terrain, { DrapeRenderMode } from "./terrain.js"; +import Fog from "./fog.js"; +import LineAtlas from "../render/line_atlas.js"; +import { pick, clone, extend, deepEqual, filterObject } from "../util/util.js"; +import { + getJSON, + getReferrer, + makeRequest, + ResourceType, +} from "../util/ajax.js"; +import { isMapboxURL } from "../util/mapbox.js"; +import browser from "../util/browser.js"; +import Dispatcher from "../util/dispatcher.js"; import { validateStyle, validateSource, validateLayer, validateFilter, validateTerrain, - emitValidationErrors as _emitValidationErrors -} from './validate_style.js'; -import {QueryGeometry} from '../style/query_geometry.js'; + emitValidationErrors as _emitValidationErrors, +} from "./validate_style.js"; +import { QueryGeometry } from "../style/query_geometry.js"; import { create as createSource, getType as getSourceType, setType as setSourceType, - type SourceClass -} from '../source/source.js'; -import {queryRenderedFeatures, queryRenderedSymbols, querySourceFeatures} from '../source/query_features.js'; -import SourceCache from '../source/source_cache.js'; -import GeoJSONSource from '../source/geojson_source.js'; -import styleSpec from '../style-spec/reference/latest.js'; -import getWorkerPool from '../util/global_worker_pool.js'; -import deref from '../style-spec/deref.js'; -import emptyStyle from '../style-spec/empty.js'; -import diffStyles, {operations as diffOperations} from '../style-spec/diff.js'; + type SourceClass, +} from "../source/source.js"; +import { + queryRenderedFeatures, + queryRenderedSymbols, + querySourceFeatures, +} from "../source/query_features.js"; +import { queryRasterSource } from "../source/query_raster_values.js"; +import SourceCache from "../source/source_cache.js"; +import GeoJSONSource from "../source/geojson_source.js"; +import styleSpec from "../style-spec/reference/latest.js"; +import getWorkerPool from "../util/global_worker_pool.js"; +import deref from "../style-spec/deref.js"; +import emptyStyle from "../style-spec/empty.js"; +import diffStyles, { + operations as diffOperations, +} from "../style-spec/diff.js"; import { registerForPluginStateChange, evented as rtlTextPluginEvented, - triggerPluginCompletionEvent -} from '../source/rtl_text_plugin.js'; -import PauseablePlacement from './pauseable_placement.js'; -import ZoomHistory from './zoom_history.js'; -import CrossTileSymbolIndex from '../symbol/cross_tile_symbol_index.js'; -import {validateCustomStyleLayer} from './style_layer/custom_style_layer.js'; + triggerPluginCompletionEvent, +} from "../source/rtl_text_plugin.js"; +import PauseablePlacement from "./pauseable_placement.js"; +import ZoomHistory from "./zoom_history.js"; +import CrossTileSymbolIndex from "../symbol/cross_tile_symbol_index.js"; +import { validateCustomStyleLayer } from "./style_layer/custom_style_layer.js"; // We're skipping validation errors with the `source.canvas` identifier in order // to continue to allow canvas sources to be added at runtime/updated in // smart setStyle (see https://github.com/mapbox/mapbox-gl-js/pull/6424): const emitValidationErrors = (evented: Evented, errors: ?ValidationErrors) => - _emitValidationErrors(evented, errors && errors.filter(error => error.identifier !== 'source.canvas')); - -import type Map from '../ui/map.js'; -import type Transform from '../geo/transform.js'; -import type {StyleImage} from './style_image.js'; -import type {StyleGlyph} from './style_glyph.js'; -import type {Callback} from '../types/callback.js'; -import type EvaluationParameters from './evaluation_parameters.js'; -import type {Placement} from '../symbol/placement.js'; -import type {Cancelable} from '../types/cancelable.js'; -import type {RequestParameters, ResponseCallback} from '../util/ajax.js'; -import type {GeoJSON} from '@mapbox/geojson-types'; + _emitValidationErrors( + evented, + errors && errors.filter((error) => error.identifier !== "source.canvas") + ); + +import type Map from "../ui/map.js"; +import type Transform from "../geo/transform.js"; +import type { StyleImage } from "./style_image.js"; +import type { StyleGlyph } from "./style_glyph.js"; +import type { Callback } from "../types/callback.js"; +import type EvaluationParameters from "./evaluation_parameters.js"; +import type { Placement } from "../symbol/placement.js"; +import type { Cancelable } from "../types/cancelable.js"; +import type { RequestParameters, ResponseCallback } from "../util/ajax.js"; +import type { GeoJSON } from "@mapbox/geojson-types"; import type { LayerSpecification, FilterSpecification, @@ -76,41 +91,41 @@ import type { FogSpecification, ProjectionSpecification, TransitionSpecification, - PropertyValueSpecification -} from '../style-spec/types.js'; -import type {CustomLayerInterface} from './style_layer/custom_style_layer.js'; -import type {Validator, ValidationErrors} from './validate_style.js'; -import type {OverscaledTileID} from '../source/tile_id.js'; -import type {QueryResult} from '../data/feature_index.js'; -import type {QueryFeature} from '../util/vectortile_to_geojson.js'; -import type {FeatureStates} from '../source/source_state.js'; -import type {PointLike} from '@mapbox/point-geometry'; -import type {Source} from '../source/source.js'; + PropertyValueSpecification, +} from "../style-spec/types.js"; +import type { CustomLayerInterface } from "./style_layer/custom_style_layer.js"; +import type { Validator, ValidationErrors } from "./validate_style.js"; +import type { OverscaledTileID } from "../source/tile_id.js"; +import type { QueryResult } from "../data/feature_index.js"; +import type { QueryFeature } from "../util/vectortile_to_geojson.js"; +import type { FeatureStates } from "../source/source_state.js"; +import type { PointLike } from "@mapbox/point-geometry"; +import type { Source } from "../source/source.js"; const supportedDiffOperations = pick(diffOperations, [ - 'addLayer', - 'removeLayer', - 'setPaintProperty', - 'setLayoutProperty', - 'setFilter', - 'addSource', - 'removeSource', - 'setLayerZoomRange', - 'setLight', - 'setTransition', - 'setGeoJSONSourceData', - 'setTerrain', - 'setFog', - 'setProjection' + "addLayer", + "removeLayer", + "setPaintProperty", + "setLayoutProperty", + "setFilter", + "addSource", + "removeSource", + "setLayerZoomRange", + "setLight", + "setTransition", + "setGeoJSONSourceData", + "setTerrain", + "setFog", + "setProjection", // 'setGlyphs', // 'setSprite', ]); const ignoredDiffOperations = pick(diffOperations, [ - 'setCenter', - 'setZoom', - 'setBearing', - 'setPitch' + "setCenter", + "setZoom", + "setBearing", + "setPitch", ]); const empty = emptyStyle(); @@ -118,15 +133,21 @@ const empty = emptyStyle(); export type StyleOptions = { validate?: boolean, localFontFamily?: string, - localIdeographFontFamily?: string + localIdeographFontFamily?: string, }; export type StyleSetterOptions = { - validate?: boolean + validate?: boolean, }; // Symbols are draped only for specific cases: see isLayerDraped -const drapedLayers = {'fill': true, 'line': true, 'background': true, "hillshade": true, "raster": true}; +const drapedLayers = { + fill: true, + line: true, + background: true, + hillshade: true, + raster: true, +}; /** * @private @@ -144,25 +165,25 @@ class Style extends Evented { _request: ?Cancelable; _spriteRequest: ?Cancelable; - _layers: {[_: string]: StyleLayer}; + _layers: { [_: string]: StyleLayer }; _num3DLayers: number; _numSymbolLayers: number; _numCircleLayers: number; - _serializedLayers: {[_: string]: Object}; + _serializedLayers: { [_: string]: Object }; _order: Array; _drapedFirstOrder: Array; - _sourceCaches: {[_: string]: SourceCache}; - _otherSourceCaches: {[_: string]: SourceCache}; - _symbolSourceCaches: {[_: string]: SourceCache}; + _sourceCaches: { [_: string]: SourceCache }; + _otherSourceCaches: { [_: string]: SourceCache }; + _symbolSourceCaches: { [_: string]: SourceCache }; zoomHistory: ZoomHistory; _loaded: boolean; _rtlTextPluginCallback: Function; _changed: boolean; - _updatedSources: {[_: string]: 'clear' | 'reload'}; - _updatedLayers: {[_: string]: true}; - _removedLayers: {[_: string]: StyleLayer}; - _changedImages: {[_: string]: true}; - _updatedPaintProps: {[layer: string]: true}; + _updatedSources: { [_: string]: "clear" | "reload" }; + _updatedLayers: { [_: string]: true }; + _removedLayers: { [_: string]: StyleLayer }; + _changedImages: { [_: string]: true }; + _updatedPaintProps: { [layer: string]: true }; _layerOrderChanged: boolean; _availableImages: Array; _markersNeedUpdate: boolean; @@ -184,11 +205,15 @@ class Style extends Evented { this.dispatcher = new Dispatcher(getWorkerPool(), this); this.imageManager = new ImageManager(); this.imageManager.setEventedParent(this); - this.glyphManager = new GlyphManager(map._requestManager, - options.localFontFamily ? - LocalGlyphMode.all : - (options.localIdeographFontFamily ? LocalGlyphMode.ideographs : LocalGlyphMode.none), - options.localFontFamily || options.localIdeographFontFamily); + this.glyphManager = new GlyphManager( + map._requestManager, + options.localFontFamily + ? LocalGlyphMode.all + : options.localIdeographFontFamily + ? LocalGlyphMode.ideographs + : LocalGlyphMode.none, + options.localFontFamily || options.localIdeographFontFamily + ); this.lineAtlas = new LineAtlas(256, 512); this.crossTileSymbolIndex = new CrossTileSymbolIndex(); @@ -203,40 +228,52 @@ class Style extends Evented { this.zoomHistory = new ZoomHistory(); this._loaded = false; this._availableImages = []; - this._order = []; + this._order = []; this._drapedFirstOrder = []; this._markersNeedUpdate = false; this._resetUpdates(); - this.dispatcher.broadcast('setReferrer', getReferrer()); + this.dispatcher.broadcast("setReferrer", getReferrer()); const self = this; - this._rtlTextPluginCallback = Style.registerForPluginStateChange((event) => { - const state = { - pluginStatus: event.pluginStatus, - pluginURL: event.pluginURL - }; - self.dispatcher.broadcast('syncRTLPluginState', state, (err, results) => { - triggerPluginCompletionEvent(err); - if (results) { - const allComplete = results.every((elem) => elem); - if (allComplete) { - for (const id in self._sourceCaches) { - const sourceCache = self._sourceCaches[id]; - const sourceCacheType = sourceCache.getSource().type; - if (sourceCacheType === 'vector' || sourceCacheType === 'geojson') { - sourceCache.reload(); // Should be a no-op if the plugin loads before any tiles load + this._rtlTextPluginCallback = Style.registerForPluginStateChange( + (event) => { + const state = { + pluginStatus: event.pluginStatus, + pluginURL: event.pluginURL, + }; + self.dispatcher.broadcast( + "syncRTLPluginState", + state, + (err, results) => { + triggerPluginCompletionEvent(err); + if (results) { + const allComplete = results.every((elem) => elem); + if (allComplete) { + for (const id in self._sourceCaches) { + const sourceCache = self._sourceCaches[id]; + const sourceCacheType = sourceCache.getSource() + .type; + if ( + sourceCacheType === "vector" || + sourceCacheType === "geojson" + ) { + sourceCache.reload(); // Should be a no-op if the plugin loads before any tiles load + } + } } } } - } - - }); - }); + ); + } + ); - this.on('data', (event) => { - if (event.dataType !== 'source' || event.sourceDataType !== 'metadata') { + this.on("data", (event) => { + if ( + event.dataType !== "source" || + event.sourceDataType !== "metadata" + ) { return; } @@ -254,17 +291,28 @@ class Style extends Evented { }); } - loadURL(url: string, options: { - validate?: boolean, - accessToken?: string - } = {}) { - this.fire(new Event('dataloading', {dataType: 'style'})); - - const validate = typeof options.validate === 'boolean' ? - options.validate : !isMapboxURL(url); - - url = this.map._requestManager.normalizeStyleURL(url, options.accessToken); - const request = this.map._requestManager.transformRequest(url, ResourceType.Style); + loadURL( + url: string, + options: { + validate?: boolean, + accessToken?: string, + } = {} + ) { + this.fire(new Event("dataloading", { dataType: "style" })); + + const validate = + typeof options.validate === "boolean" + ? options.validate + : !isMapboxURL(url); + + url = this.map._requestManager.normalizeStyleURL( + url, + options.accessToken + ); + const request = this.map._requestManager.transformRequest( + url, + ResourceType.Style + ); this._request = getJSON(request, (error: ?Error, json: ?Object) => { this._request = null; if (error) { @@ -276,7 +324,7 @@ class Style extends Evented { } loadJSON(json: StyleSpecification, options: StyleSetterOptions = {}) { - this.fire(new Event('dataloading', {dataType: 'style'})); + this.fire(new Event("dataloading", { dataType: "style" })); this._request = browser.frame(() => { this._request = null; @@ -285,7 +333,7 @@ class Style extends Evented { } loadEmpty() { - this.fire(new Event('dataloading', {dataType: 'style'})); + this.fire(new Event("dataloading", { dataType: "style" })); this._load(empty, false); } @@ -295,10 +343,10 @@ class Style extends Evented { if (layer.is3D()) { this._num3DLayers += count; } - if (layer.type === 'circle') { + if (layer.type === "circle") { this._numCircleLayers += count; } - if (layer.type === 'symbol') { + if (layer.type === "symbol") { this._numSymbolLayers += count; } } @@ -313,14 +361,14 @@ class Style extends Evented { this._updateMapProjection(); for (const id in json.sources) { - this.addSource(id, json.sources[id], {validate: false}); + this.addSource(id, json.sources[id], { validate: false }); } this._changed = false; // avoid triggering redundant style update after adding initial sources if (json.sprite) { this._loadSprite(json.sprite); } else { this.imageManager.setLoaded(true); - this.dispatcher.broadcast('spriteLoaded', true); + this.dispatcher.broadcast("spriteLoaded", true); } this.glyphManager.setURL(json.glyphs); @@ -333,29 +381,38 @@ class Style extends Evented { this._serializedLayers = {}; for (let layer of layers) { layer = createStyleLayer(layer); - layer.setEventedParent(this, {layer: {id: layer.id}}); + layer.setEventedParent(this, { layer: { id: layer.id } }); this._layers[layer.id] = layer; this._serializedLayers[layer.id] = layer.serialize(); this._updateLayerCount(layer, true); } - this.dispatcher.broadcast('setLayers', this._serializeLayers(this._order)); + this.dispatcher.broadcast( + "setLayers", + this._serializeLayers(this._order) + ); this.light = new Light(this.stylesheet.light); if (this.stylesheet.terrain && !this.terrainSetForDrapingOnly()) { - this._createTerrain(this.stylesheet.terrain, DrapeRenderMode.elevated); + this._createTerrain( + this.stylesheet.terrain, + DrapeRenderMode.elevated + ); } if (this.stylesheet.fog) { this._createFog(this.stylesheet.fog); } this._updateDrapeFirstLayers(); - this.fire(new Event('data', {dataType: 'style'})); - this.fire(new Event('style.load')); + this.fire(new Event("data", { dataType: "style" })); + this.fire(new Event("style.load")); } terrainSetForDrapingOnly(): boolean { - return !!this.terrain && this.terrain.drapeRenderMode === DrapeRenderMode.deferred; + return ( + !!this.terrain && + this.terrain.drapeRenderMode === DrapeRenderMode.deferred + ); } setProjection(projection?: ?ProjectionSpecification) { @@ -370,16 +427,21 @@ class Style extends Evented { } _updateMapProjection() { - if (!this.map._explicitProjection) { // Update the visible projection if map's is null + if (!this.map._explicitProjection) { + // Update the visible projection if map's is null this.map._updateProjection(); - } else { // Ensure that style is consistent with current projection on style load + } else { + // Ensure that style is consistent with current projection on style load this.applyProjectionUpdate(); } } applyProjectionUpdate() { if (!this._loaded) return; - this.dispatcher.broadcast('setProjection', this.map.transform.projectionOptions); + this.dispatcher.broadcast( + "setProjection", + this.map.transform.projectionOptions + ); if (this.map.transform.projection.requiresDraping) { const hasTerrain = this.getTerrain() || this.stylesheet.terrain; @@ -392,22 +454,26 @@ class Style extends Evented { } _loadSprite(url: string) { - this._spriteRequest = loadSprite(url, this.map._requestManager, (err, images) => { - this._spriteRequest = null; - if (err) { - this.fire(new ErrorEvent(err)); - } else if (images) { - for (const id in images) { - this.imageManager.addImage(id, images[id]); + this._spriteRequest = loadSprite( + url, + this.map._requestManager, + (err, images) => { + this._spriteRequest = null; + if (err) { + this.fire(new ErrorEvent(err)); + } else if (images) { + for (const id in images) { + this.imageManager.addImage(id, images[id]); + } } - } - this.imageManager.setLoaded(true); - this._availableImages = this.imageManager.listImages(); - this.dispatcher.broadcast('setImages', this._availableImages); - this.dispatcher.broadcast('spriteLoaded', true); - this.fire(new Event('data', {dataType: 'style'})); - }); + this.imageManager.setLoaded(true); + this._availableImages = this.imageManager.listImages(); + this.dispatcher.broadcast("setImages", this._availableImages); + this.dispatcher.broadcast("spriteLoaded", true); + this.fire(new Event("data", { dataType: "style" })); + } + ); } _validateLayer(layer: StyleLayer) { @@ -421,28 +487,32 @@ class Style extends Evented { return; } - if (source.type === 'geojson' || (source.vectorLayerIds && source.vectorLayerIds.indexOf(sourceLayer) === -1)) { - this.fire(new ErrorEvent(new Error( - `Source layer "${sourceLayer}" ` + - `does not exist on source "${source.id}" ` + - `as specified by style layer "${layer.id}"` - ))); + if ( + source.type === "geojson" || + (source.vectorLayerIds && + source.vectorLayerIds.indexOf(sourceLayer) === -1) + ) { + this.fire( + new ErrorEvent( + new Error( + `Source layer "${sourceLayer}" ` + + `does not exist on source "${source.id}" ` + + `as specified by style layer "${layer.id}"` + ) + ) + ); } } loaded(): boolean { - if (!this._loaded) - return false; + if (!this._loaded) return false; - if (Object.keys(this._updatedSources).length) - return false; + if (Object.keys(this._updatedSources).length) return false; for (const id in this._sourceCaches) - if (!this._sourceCaches[id].loaded()) - return false; + if (!this._sourceCaches[id].loaded()) return false; - if (!this.imageManager.isLoaded()) - return false; + if (!this.imageManager.isLoaded()) return false; return true; } @@ -451,7 +521,7 @@ class Style extends Evented { const serializedLayers = []; for (const id of ids) { const layer = this._layers[id]; - if (layer.type !== 'custom') { + if (layer.type !== "custom") { serializedLayers.push(layer.serialize()); } } @@ -497,7 +567,7 @@ class Style extends Evented { _checkLoaded() { if (!this._loaded) { - throw new Error('Style is not done loading'); + throw new Error("Style is not done loading"); } } @@ -520,10 +590,10 @@ class Style extends Evented { } for (const id in this._updatedSources) { const action = this._updatedSources[id]; - assert(action === 'reload' || action === 'clear'); - if (action === 'reload') { + assert(action === "reload" || action === "clear"); + if (action === "reload") { this._reloadSource(id); - } else if (action === 'clear') { + } else if (action === "clear") { this._clearSource(id); } } @@ -564,7 +634,9 @@ class Style extends Evented { const programIds = layer.getProgramIds(); if (!programIds) continue; - const programConfiguration = layer.getProgramConfiguration(parameters.zoom); + const programConfiguration = layer.getProgramConfiguration( + parameters.zoom + ); for (const programId of programIds) { painter.useProgram(programId, programConfiguration); @@ -575,7 +647,13 @@ class Style extends Evented { for (const sourceId in sourcesUsedBefore) { const sourceCache = this._sourceCaches[sourceId]; if (sourcesUsedBefore[sourceId] !== sourceCache.used) { - sourceCache.getSource().fire(new Event('data', {sourceDataType: 'visibility', dataType:'source', sourceId: sourceCache.getSource().id})); + sourceCache.getSource().fire( + new Event("data", { + sourceDataType: "visibility", + dataType: "source", + sourceId: sourceCache.getSource().id, + }) + ); } } @@ -594,7 +672,7 @@ class Style extends Evented { } if (changed) { - this.fire(new Event('data', {dataType: 'style'})); + this.fire(new Event("data", { dataType: "style" })); } } @@ -605,16 +683,19 @@ class Style extends Evented { const changedImages = Object.keys(this._changedImages); if (changedImages.length) { for (const name in this._sourceCaches) { - this._sourceCaches[name].reloadTilesForDependencies(['icons', 'patterns'], changedImages); + this._sourceCaches[name].reloadTilesForDependencies( + ["icons", "patterns"], + changedImages + ); } this._changedImages = {}; } } _updateWorkerLayers(updatedIds: Array, removedIds: Array) { - this.dispatcher.broadcast('updateLayers', { + this.dispatcher.broadcast("updateLayers", { layers: this._serializeLayers(updatedIds), - removedIds + removedIds, }); } @@ -648,20 +729,27 @@ class Style extends Evented { nextState = clone(nextState); nextState.layers = deref(nextState.layers); - const changes = diffStyles(this.serialize(), nextState) - .filter(op => !(op.command in ignoredDiffOperations)); + const changes = diffStyles(this.serialize(), nextState).filter( + (op) => !(op.command in ignoredDiffOperations) + ); if (changes.length === 0) { return false; } - const unimplementedOps = changes.filter(op => !(op.command in supportedDiffOperations)); + const unimplementedOps = changes.filter( + (op) => !(op.command in supportedDiffOperations) + ); if (unimplementedOps.length > 0) { - throw new Error(`Unimplemented: ${unimplementedOps.map(op => op.command).join(', ')}.`); + throw new Error( + `Unimplemented: ${unimplementedOps + .map((op) => op.command) + .join(", ")}.` + ); } changes.forEach((op) => { - if (op.command === 'setTransition') { + if (op.command === "setTransition") { // `transition` is always read directly off of // `this.stylesheet`, which we update below return; @@ -677,7 +765,11 @@ class Style extends Evented { addImage(id: string, image: StyleImage): this { if (this.getImage(id)) { - return this.fire(new ErrorEvent(new Error('An image with this name already exists.'))); + return this.fire( + new ErrorEvent( + new Error("An image with this name already exists.") + ) + ); } this.imageManager.addImage(id, image); this._afterImageUpdated(id); @@ -694,7 +786,9 @@ class Style extends Evented { removeImage(id: string): this { if (!this.getImage(id)) { - return this.fire(new ErrorEvent(new Error('No image with this name exists.'))); + return this.fire( + new ErrorEvent(new Error("No image with this name exists.")) + ); } this.imageManager.removeImage(id); this._afterImageUpdated(id); @@ -705,8 +799,8 @@ class Style extends Evented { this._availableImages = this.imageManager.listImages(); this._changedImages[id] = true; this._changed = true; - this.dispatcher.broadcast('setImages', this._availableImages); - this.fire(new Event('data', {dataType: 'style'})); + this.dispatcher.broadcast("setImages", this._availableImages); + this.fire(new Event("data", { dataType: "style" })); } listImages(): Array { @@ -714,42 +808,65 @@ class Style extends Evented { return this._availableImages.slice(); } - addSource(id: string, source: SourceSpecification, options: StyleSetterOptions = {}) { + addSource( + id: string, + source: SourceSpecification, + options: StyleSetterOptions = {} + ) { this._checkLoaded(); if (this.getSource(id) !== undefined) { - throw new Error('There is already a source with this ID'); + throw new Error("There is already a source with this ID"); } if (!source.type) { - throw new Error(`The type property must be defined, but only the following properties were given: ${Object.keys(source).join(', ')}.`); + throw new Error( + `The type property must be defined, but only the following properties were given: ${Object.keys( + source + ).join(", ")}.` + ); } - const builtIns = ['vector', 'raster', 'geojson', 'video', 'image']; + const builtIns = ["vector", "raster", "geojson", "video", "image"]; const shouldValidate = builtIns.indexOf(source.type) >= 0; - if (shouldValidate && this._validate(validateSource, `sources.${id}`, source, null, options)) return; + if ( + shouldValidate && + this._validate( + validateSource, + `sources.${id}`, + source, + null, + options + ) + ) + return; - if (this.map && this.map._collectResourceTiming) (source: any).collectResourceTiming = true; + if (this.map && this.map._collectResourceTiming) + (source: any).collectResourceTiming = true; const sourceInstance = createSource(id, source, this.dispatcher, this); sourceInstance.setEventedParent(this, () => ({ isSourceLoaded: this._isSourceCacheLoaded(id), source: sourceInstance.serialize(), - sourceId: id + sourceId: id, })); const addSourceCache = (onlySymbols) => { - const sourceCacheId = (onlySymbols ? 'symbol:' : 'other:') + id; - const sourceCache = this._sourceCaches[sourceCacheId] = new SourceCache(sourceCacheId, sourceInstance, onlySymbols); - (onlySymbols ? this._symbolSourceCaches : this._otherSourceCaches)[id] = sourceCache; + const sourceCacheId = (onlySymbols ? "symbol:" : "other:") + id; + const sourceCache = (this._sourceCaches[ + sourceCacheId + ] = new SourceCache(sourceCacheId, sourceInstance, onlySymbols)); + (onlySymbols ? this._symbolSourceCaches : this._otherSourceCaches)[ + id + ] = sourceCache; sourceCache.style = this; sourceCache.onAdd(this.map); }; addSourceCache(false); - if (source.type === 'vector' || source.type === 'geojson') { + if (source.type === "vector" || source.type === "geojson") { addSourceCache(true); } @@ -769,22 +886,40 @@ class Style extends Evented { const source = this.getSource(id); if (!source) { - throw new Error('There is no source with this ID'); + throw new Error("There is no source with this ID"); } for (const layerId in this._layers) { if (this._layers[layerId].source === id) { - return this.fire(new ErrorEvent(new Error(`Source "${id}" cannot be removed while layer "${layerId}" is using it.`))); + return this.fire( + new ErrorEvent( + new Error( + `Source "${id}" cannot be removed while layer "${layerId}" is using it.` + ) + ) + ); } } if (this.terrain && this.terrain.get().source === id) { - return this.fire(new ErrorEvent(new Error(`Source "${id}" cannot be removed while terrain is using it.`))); + return this.fire( + new ErrorEvent( + new Error( + `Source "${id}" cannot be removed while terrain is using it.` + ) + ) + ); } const sourceCaches = this._getSourceCaches(id); for (const sourceCache of sourceCaches) { delete this._sourceCaches[sourceCache.id]; delete this._updatedSources[sourceCache.id]; - sourceCache.fire(new Event('data', {sourceDataType: 'metadata', dataType:'source', sourceId: sourceCache.getSource().id})); + sourceCache.fire( + new Event("data", { + sourceDataType: "metadata", + dataType: "source", + sourceId: sourceCache.getSource().id, + }) + ); sourceCache.setEventedParent(null); sourceCache.clearTiles(); } @@ -800,16 +935,19 @@ class Style extends Evented { } /** - * Set the data of a GeoJSON source, given its ID. - * @param {string} id ID of the source. - * @param {GeoJSON|string} data GeoJSON source. - */ + * Set the data of a GeoJSON source, given its ID. + * @param {string} id ID of the source. + * @param {GeoJSON|string} data GeoJSON source. + */ setGeoJSONSourceData(id: string, data: GeoJSON | string) { this._checkLoaded(); - assert(this.getSource(id) !== undefined, 'There is no source with this ID'); + assert( + this.getSource(id) !== undefined, + "There is no source with this ID" + ); const geojsonSource: GeoJSONSource = (this.getSource(id): any); - assert(geojsonSource.type === 'geojson'); + assert(geojsonSource.type === "geojson"); geojsonSource.setData(data); this._changed = true; @@ -833,45 +971,73 @@ class Style extends Evented { * @param {Object} options Style setter options. * @returns {Map} The {@link Map} object. */ - addLayer(layerObject: LayerSpecification | CustomLayerInterface, before?: string, options: StyleSetterOptions = {}) { + addLayer( + layerObject: LayerSpecification | CustomLayerInterface, + before?: string, + options: StyleSetterOptions = {} + ) { this._checkLoaded(); const id = layerObject.id; if (this.getLayer(id)) { - this.fire(new ErrorEvent(new Error(`Layer with id "${id}" already exists on this map`))); + this.fire( + new ErrorEvent( + new Error( + `Layer with id "${id}" already exists on this map` + ) + ) + ); return; } let layer; - if (layerObject.type === 'custom') { - - if (emitValidationErrors(this, validateCustomStyleLayer(layerObject))) return; + if (layerObject.type === "custom") { + if ( + emitValidationErrors( + this, + validateCustomStyleLayer(layerObject) + ) + ) + return; layer = createStyleLayer(layerObject); - } else { - if (typeof layerObject.source === 'object') { + if (typeof layerObject.source === "object") { this.addSource(id, layerObject.source); layerObject = clone(layerObject); - layerObject = (extend(layerObject, {source: id}): any); + layerObject = (extend(layerObject, { source: id }): any); } // this layer is not in the style.layers array, so we pass an impossible array index - if (this._validate(validateLayer, - `layers.${id}`, layerObject, {arrayIndex: -1}, options)) return; + if ( + this._validate( + validateLayer, + `layers.${id}`, + layerObject, + { arrayIndex: -1 }, + options + ) + ) + return; layer = createStyleLayer(layerObject); this._validateLayer(layer); - layer.setEventedParent(this, {layer: {id}}); + layer.setEventedParent(this, { layer: { id } }); this._serializedLayers[layer.id] = layer.serialize(); this._updateLayerCount(layer, true); } const index = before ? this._order.indexOf(before) : this._order.length; if (before && index === -1) { - this.fire(new ErrorEvent(new Error(`Layer with id "${before}" does not exist on this map.`))); + this.fire( + new ErrorEvent( + new Error( + `Layer with id "${before}" does not exist on this map.` + ) + ) + ); return; } @@ -881,7 +1047,12 @@ class Style extends Evented { this._layers[id] = layer; const sourceCache = this._getLayerSourceCache(layer); - if (this._removedLayers[id] && layer.source && sourceCache && layer.type !== 'custom') { + if ( + this._removedLayers[id] && + layer.source && + sourceCache && + layer.type !== "custom" + ) { // If, in the current batch, we have already removed this layer // and we are now re-adding it with a different `type`, then we // need to clear (rather than just reload) the underyling source's @@ -892,9 +1063,9 @@ class Style extends Evented { const removed = this._removedLayers[id]; delete this._removedLayers[id]; if (removed.type !== layer.type) { - this._updatedSources[layer.source] = 'clear'; + this._updatedSources[layer.source] = "clear"; } else { - this._updatedSources[layer.source] = 'reload'; + this._updatedSources[layer.source] = "reload"; sourceCache.pause(); } } @@ -919,7 +1090,13 @@ class Style extends Evented { const layer = this._layers[id]; if (!layer) { - this.fire(new ErrorEvent(new Error(`The layer '${id}' does not exist in the map's style and cannot be moved.`))); + this.fire( + new ErrorEvent( + new Error( + `The layer '${id}' does not exist in the map's style and cannot be moved.` + ) + ) + ); return; } @@ -930,9 +1107,17 @@ class Style extends Evented { const index = this._order.indexOf(id); this._order.splice(index, 1); - const newIndex = before ? this._order.indexOf(before) : this._order.length; + const newIndex = before + ? this._order.indexOf(before) + : this._order.length; if (before && newIndex === -1) { - this.fire(new ErrorEvent(new Error(`Layer with id "${before}" does not exist on this map.`))); + this.fire( + new ErrorEvent( + new Error( + `Layer with id "${before}" does not exist on this map.` + ) + ) + ); return; } this._order.splice(newIndex, 0, id); @@ -955,7 +1140,13 @@ class Style extends Evented { const layer = this._layers[id]; if (!layer) { - this.fire(new ErrorEvent(new Error(`The layer '${id}' does not exist in the map's style and cannot be removed.`))); + this.fire( + new ErrorEvent( + new Error( + `The layer '${id}' does not exist in the map's style and cannot be removed.` + ) + ) + ); return; } @@ -1022,7 +1213,13 @@ class Style extends Evented { const layer = this.getLayer(layerId); if (!layer) { - this.fire(new ErrorEvent(new Error(`The layer '${layerId}' does not exist in the map's style and cannot have zoom extent.`))); + this.fire( + new ErrorEvent( + new Error( + `The layer '${layerId}' does not exist in the map's style and cannot have zoom extent.` + ) + ) + ); return; } @@ -1037,12 +1234,22 @@ class Style extends Evented { this._updateLayer(layer); } - setFilter(layerId: string, filter: ?FilterSpecification, options: StyleSetterOptions = {}) { + setFilter( + layerId: string, + filter: ?FilterSpecification, + options: StyleSetterOptions = {} + ) { this._checkLoaded(); const layer = this.getLayer(layerId); if (!layer) { - this.fire(new ErrorEvent(new Error(`The layer '${layerId}' does not exist in the map's style and cannot be filtered.`))); + this.fire( + new ErrorEvent( + new Error( + `The layer '${layerId}' does not exist in the map's style and cannot be filtered.` + ) + ) + ); return; } @@ -1056,7 +1263,15 @@ class Style extends Evented { return; } - if (this._validate(validateFilter, `layers.${layer.id}.filter`, filter, {layerType: layer.type}, options)) { + if ( + this._validate( + validateFilter, + `layers.${layer.id}.filter`, + filter, + { layerType: layer.type }, + options + ) + ) { return; } @@ -1074,12 +1289,23 @@ class Style extends Evented { return layer && clone(layer.filter); } - setLayoutProperty(layerId: string, name: string, value: any, options: StyleSetterOptions = {}) { + setLayoutProperty( + layerId: string, + name: string, + value: any, + options: StyleSetterOptions = {} + ) { this._checkLoaded(); const layer = this.getLayer(layerId); if (!layer) { - this.fire(new ErrorEvent(new Error(`The layer '${layerId}' does not exist in the map's style and cannot be styled.`))); + this.fire( + new ErrorEvent( + new Error( + `The layer '${layerId}' does not exist in the map's style and cannot be styled.` + ) + ) + ); return; } @@ -1095,22 +1321,42 @@ class Style extends Evented { * @param {string} name The name of the layout property. * @returns {*} The property value. */ - getLayoutProperty(layerId: string, name: string): ?PropertyValueSpecification { + getLayoutProperty( + layerId: string, + name: string + ): ?PropertyValueSpecification { const layer = this.getLayer(layerId); if (!layer) { - this.fire(new ErrorEvent(new Error(`The layer '${layerId}' does not exist in the map's style.`))); + this.fire( + new ErrorEvent( + new Error( + `The layer '${layerId}' does not exist in the map's style.` + ) + ) + ); return; } return layer.getLayoutProperty(name); } - setPaintProperty(layerId: string, name: string, value: any, options: StyleSetterOptions = {}) { + setPaintProperty( + layerId: string, + name: string, + value: any, + options: StyleSetterOptions = {} + ) { this._checkLoaded(); const layer = this.getLayer(layerId); if (!layer) { - this.fire(new ErrorEvent(new Error(`The layer '${layerId}' does not exist in the map's style and cannot be styled.`))); + this.fire( + new ErrorEvent( + new Error( + `The layer '${layerId}' does not exist in the map's style and cannot be styled.` + ) + ) + ); return; } @@ -1125,32 +1371,60 @@ class Style extends Evented { this._updatedPaintProps[layerId] = true; } - getPaintProperty(layerId: string, name: string): void | TransitionSpecification | PropertyValueSpecification { + getPaintProperty( + layerId: string, + name: string + ): void | TransitionSpecification | PropertyValueSpecification { const layer = this.getLayer(layerId); return layer && layer.getPaintProperty(name); } - setFeatureState(target: { source: string; sourceLayer?: string; id: string | number; }, state: Object) { + setFeatureState( + target: { source: string, sourceLayer?: string, id: string | number }, + state: Object + ) { this._checkLoaded(); const sourceId = target.source; const sourceLayer = target.sourceLayer; const source = this.getSource(sourceId); if (!source) { - this.fire(new ErrorEvent(new Error(`The source '${sourceId}' does not exist in the map's style.`))); + this.fire( + new ErrorEvent( + new Error( + `The source '${sourceId}' does not exist in the map's style.` + ) + ) + ); return; } const sourceType = source.type; - if (sourceType === 'geojson' && sourceLayer) { - this.fire(new ErrorEvent(new Error(`GeoJSON sources cannot have a sourceLayer parameter.`))); + if (sourceType === "geojson" && sourceLayer) { + this.fire( + new ErrorEvent( + new Error( + `GeoJSON sources cannot have a sourceLayer parameter.` + ) + ) + ); return; } - if (sourceType === 'vector' && !sourceLayer) { - this.fire(new ErrorEvent(new Error(`The sourceLayer parameter must be provided for vector source types.`))); + if (sourceType === "vector" && !sourceLayer) { + this.fire( + new ErrorEvent( + new Error( + `The sourceLayer parameter must be provided for vector source types.` + ) + ) + ); return; } if (target.id === undefined) { - this.fire(new ErrorEvent(new Error(`The feature id parameter must be provided.`))); + this.fire( + new ErrorEvent( + new Error(`The feature id parameter must be provided.`) + ) + ); } const sourceCaches = this._getSourceCaches(sourceId); @@ -1159,26 +1433,52 @@ class Style extends Evented { } } - removeFeatureState(target: { source: string; sourceLayer?: string; id?: string | number; }, key?: string) { + removeFeatureState( + target: { source: string, sourceLayer?: string, id?: string | number }, + key?: string + ) { this._checkLoaded(); const sourceId = target.source; const source = this.getSource(sourceId); if (!source) { - this.fire(new ErrorEvent(new Error(`The source '${sourceId}' does not exist in the map's style.`))); + this.fire( + new ErrorEvent( + new Error( + `The source '${sourceId}' does not exist in the map's style.` + ) + ) + ); return; } const sourceType = source.type; - const sourceLayer = sourceType === 'vector' ? target.sourceLayer : undefined; - - if (sourceType === 'vector' && !sourceLayer) { - this.fire(new ErrorEvent(new Error(`The sourceLayer parameter must be provided for vector source types.`))); + const sourceLayer = + sourceType === "vector" ? target.sourceLayer : undefined; + + if (sourceType === "vector" && !sourceLayer) { + this.fire( + new ErrorEvent( + new Error( + `The sourceLayer parameter must be provided for vector source types.` + ) + ) + ); return; } - if (key && (typeof target.id !== 'string' && typeof target.id !== 'number')) { - this.fire(new ErrorEvent(new Error(`A feature id is required to remove its specific state property.`))); + if ( + key && + typeof target.id !== "string" && + typeof target.id !== "number" + ) { + this.fire( + new ErrorEvent( + new Error( + `A feature id is required to remove its specific state property.` + ) + ) + ); return; } @@ -1188,23 +1488,43 @@ class Style extends Evented { } } - getFeatureState(target: { source: string; sourceLayer?: string; id: string | number; }): ?FeatureStates { + getFeatureState(target: { + source: string, + sourceLayer?: string, + id: string | number, + }): ?FeatureStates { this._checkLoaded(); const sourceId = target.source; const sourceLayer = target.sourceLayer; const source = this.getSource(sourceId); if (!source) { - this.fire(new ErrorEvent(new Error(`The source '${sourceId}' does not exist in the map's style.`))); + this.fire( + new ErrorEvent( + new Error( + `The source '${sourceId}' does not exist in the map's style.` + ) + ) + ); return; } const sourceType = source.type; - if (sourceType === 'vector' && !sourceLayer) { - this.fire(new ErrorEvent(new Error(`The sourceLayer parameter must be provided for vector source types.`))); + if (sourceType === "vector" && !sourceLayer) { + this.fire( + new ErrorEvent( + new Error( + `The sourceLayer parameter must be provided for vector source types.` + ) + ) + ); return; } if (target.id === undefined) { - this.fire(new ErrorEvent(new Error(`The feature id parameter must be provided.`))); + this.fire( + new ErrorEvent( + new Error(`The feature id parameter must be provided.`) + ) + ); } const sourceCaches = this._getSourceCaches(sourceId); @@ -1212,7 +1532,10 @@ class Style extends Evented { } getTransition(): TransitionSpecification { - return extend({duration: 300, delay: 0}, this.stylesheet && this.stylesheet.transition); + return extend( + { duration: 300, delay: 0 }, + this.stylesheet && this.stylesheet.transition + ); } serialize(): StyleSpecification { @@ -1223,39 +1546,46 @@ class Style extends Evented { sources[source.id] = source.serialize(); } } - return filterObject({ - version: this.stylesheet.version, - name: this.stylesheet.name, - metadata: this.stylesheet.metadata, - light: this.stylesheet.light, - terrain: this.stylesheet.terrain, - fog: this.stylesheet.fog, - center: this.stylesheet.center, - zoom: this.stylesheet.zoom, - bearing: this.stylesheet.bearing, - pitch: this.stylesheet.pitch, - sprite: this.stylesheet.sprite, - glyphs: this.stylesheet.glyphs, - transition: this.stylesheet.transition, - projection: this.stylesheet.projection, - sources, - layers: this._serializeLayers(this._order) - }, (value) => { return value !== undefined; }); + return filterObject( + { + version: this.stylesheet.version, + name: this.stylesheet.name, + metadata: this.stylesheet.metadata, + light: this.stylesheet.light, + terrain: this.stylesheet.terrain, + fog: this.stylesheet.fog, + center: this.stylesheet.center, + zoom: this.stylesheet.zoom, + bearing: this.stylesheet.bearing, + pitch: this.stylesheet.pitch, + sprite: this.stylesheet.sprite, + glyphs: this.stylesheet.glyphs, + transition: this.stylesheet.transition, + projection: this.stylesheet.projection, + sources, + layers: this._serializeLayers(this._order), + }, + (value) => { + return value !== undefined; + } + ); } _updateLayer(layer: StyleLayer) { this._updatedLayers[layer.id] = true; const sourceCache = this._getLayerSourceCache(layer); - if (layer.source && !this._updatedSources[layer.source] && + if ( + layer.source && + !this._updatedSources[layer.source] && //Skip for raster layers (https://github.com/mapbox/mapbox-gl-js/issues/7865) sourceCache && - sourceCache.getSource().type !== 'raster') { - this._updatedSources[layer.source] = 'reload'; + sourceCache.getSource().type !== "raster" + ) { + this._updatedSources[layer.source] = "reload"; sourceCache.pause(); } this._changed = true; layer.invalidateCompiledFilter(); - } _flattenAndSortRenderedFeatures(sourceResults: Array): Array { @@ -1276,7 +1606,8 @@ class Style extends Evented { // This means that that the line_layer feature is above the extrusion_layer_b feature despite // it being in an earlier layer. - const isLayer3D = layerId => this._layers[layerId].type === 'fill-extrusion'; + const isLayer3D = (layerId) => + this._layers[layerId].type === "fill-extrusion"; const layerIndex = {}; const features3D = []; @@ -1326,22 +1657,70 @@ class Style extends Evented { return features; } - queryRenderedFeatures(queryGeometry: PointLike | [PointLike, PointLike], params: any, transform: Transform): Array { + queryRasterSource( + sourceId: string, + geometry: PointLike, + transform: Transform + ) { + const sourceCache = map.style._getSourceCache(sourceId); + + if (!sourceCache) { + return undefined; + } + + const queryGeometryStruct = QueryGeometry.createFromScreenPoints( + geometry, + transform + ); + + return queryRasterSource( + sourceCache, + this._layers, + this._serializedLayers, + queryGeometryStruct, + {}, // params + transform, + false, // has3DLayer, + !!this.map._showQueryGeometry + ); + } + + queryRenderedFeatures( + queryGeometry: PointLike | [PointLike, PointLike], + params: any, + transform: Transform + ): Array { if (params && params.filter) { - this._validate(validateFilter, 'queryRenderedFeatures.filter', params.filter, null, params); + this._validate( + validateFilter, + "queryRenderedFeatures.filter", + params.filter, + null, + params + ); } const includedSources = {}; if (params && params.layers) { if (!Array.isArray(params.layers)) { - this.fire(new ErrorEvent(new Error('parameters.layers must be an Array.'))); + this.fire( + new ErrorEvent( + new Error("parameters.layers must be an Array.") + ) + ); return []; } for (const layerId of params.layers) { const layer = this._layers[layerId]; if (!layer) { // this layer is not in the style.layers array - this.fire(new ErrorEvent(new Error(`The layer '${layerId}' does not exist in the map's style and cannot be queried for features.`))); + this.fire( + new ErrorEvent( + new Error( + `The layer '${layerId}' does not exist in the map's style and cannot be queried for features.` + ) + ) + ); return []; } includedSources[layer.source] = true; @@ -1352,13 +1731,17 @@ class Style extends Evented { params.availableImages = this._availableImages; - const has3DLayer = (params && params.layers) ? - params.layers.some((layerId) => { - const layer = this.getLayer(layerId); - return layer && layer.is3D(); - }) : - this.has3DLayers(); - const queryGeometryStruct = QueryGeometry.createFromScreenPoints(queryGeometry, transform); + const has3DLayer = + params && params.layers + ? params.layers.some((layerId) => { + const layer = this.getLayer(layerId); + return layer && layer.is3D(); + }) + : this.has3DLayers(); + const queryGeometryStruct = QueryGeometry.createFromScreenPoints( + queryGeometry, + transform + ); for (const id in this._sourceCaches) { const sourceId = this._sourceCaches[id].getSource().id; @@ -1372,7 +1755,8 @@ class Style extends Evented { params, transform, has3DLayer, - !!this.map._showQueryGeometry) + !!this.map._showQueryGeometry + ) ); } @@ -1387,16 +1771,30 @@ class Style extends Evented { queryGeometryStruct.screenGeometry, params, this.placement.collisionIndex, - this.placement.retainedQueryData) + this.placement.retainedQueryData + ) ); } return (this._flattenAndSortRenderedFeatures(sourceResults): any); } - querySourceFeatures(sourceID: string, params: ?{sourceLayer: ?string, filter: ?Array, validate?: boolean}): Array { + querySourceFeatures( + sourceID: string, + params: ?{ + sourceLayer: ?string, + filter: ?Array, + validate?: boolean, + } + ): Array { if (params && params.filter) { - this._validate(validateFilter, 'querySourceFeatures.filter', params.filter, null, params); + this._validate( + validateFilter, + "querySourceFeatures.filter", + params.filter, + null, + params + ); } const sourceCaches = this._getSourceCaches(sourceID); let results = []; @@ -1406,9 +1804,15 @@ class Style extends Evented { return results; } - addSourceType(name: string, SourceType: SourceClass, callback: Callback): void { + addSourceType( + name: string, + SourceType: SourceClass, + callback: Callback + ): void { if (Style.getSourceType(name)) { - return callback(new Error(`A source type called "${name}" already exists.`)); + return callback( + new Error(`A source type called "${name}" already exists.`) + ); } Style.setSourceType(name, SourceType); @@ -1417,17 +1821,24 @@ class Style extends Evented { return callback(null, null); } - this.dispatcher.broadcast('loadWorkerSource', { - name, - url: SourceType.workerSourceURL - }, callback); + this.dispatcher.broadcast( + "loadWorkerSource", + { + name, + url: SourceType.workerSourceURL, + }, + callback + ); } getLight(): LightSpecification { return this.light.getLight(); } - setLight(lightOptions: LightSpecification, options: StyleSetterOptions = {}) { + setLight( + lightOptions: LightSpecification, + options: StyleSetterOptions = {} + ) { this._checkLoaded(); const light = this.light.getLight(); @@ -1442,10 +1853,13 @@ class Style extends Evented { const parameters = { now: browser.now(), - transition: extend({ - duration: 300, - delay: 0 - }, this.stylesheet.transition) + transition: extend( + { + duration: 300, + delay: 0, + }, + this.stylesheet.transition + ), }; this.light.setLight(lightOptions, options); @@ -1453,25 +1867,31 @@ class Style extends Evented { } getTerrain(): ?TerrainSpecification { - return this.terrain && this.terrain.drapeRenderMode === DrapeRenderMode.elevated ? this.terrain.get() : null; + return this.terrain && + this.terrain.drapeRenderMode === DrapeRenderMode.elevated + ? this.terrain.get() + : null; } setTerrainForDraping() { - const mockTerrainOptions = {source: '', exaggeration: 0}; + const mockTerrainOptions = { source: "", exaggeration: 0 }; this.setTerrain(mockTerrainOptions, DrapeRenderMode.deferred); } // eslint-disable-next-line no-warning-comments // TODO: generic approach for root level property: light, terrain, skybox. // It is not done here to prevent rebasing issues. - setTerrain(terrainOptions: ?TerrainSpecification, drapeRenderMode: number = DrapeRenderMode.elevated) { + setTerrain( + terrainOptions: ?TerrainSpecification, + drapeRenderMode: number = DrapeRenderMode.elevated + ) { this._checkLoaded(); // Disabling if (!terrainOptions) { delete this.terrain; delete this.stylesheet.terrain; - this.dispatcher.broadcast('enableTerrain', false); + this.dispatcher.broadcast("enableTerrain", false); this._force3DLayerUpdate(); this._markersNeedUpdate = true; return; @@ -1479,22 +1899,26 @@ class Style extends Evented { if (drapeRenderMode === DrapeRenderMode.elevated) { // Input validation and source object unrolling - if (typeof terrainOptions.source === 'object') { - const id = 'terrain-dem-src'; - this.addSource(id, ((terrainOptions.source): any)); + if (typeof terrainOptions.source === "object") { + const id = "terrain-dem-src"; + this.addSource(id, (terrainOptions.source: any)); terrainOptions = clone(terrainOptions); - terrainOptions = (extend(terrainOptions, {source: id}): any); + terrainOptions = (extend(terrainOptions, { source: id }): any); } - if (this._validate(validateTerrain, 'terrain', terrainOptions)) { + if (this._validate(validateTerrain, "terrain", terrainOptions)) { return; } } // Enabling - if (!this.terrain || (this.terrain && drapeRenderMode !== this.terrain.drapeRenderMode)) { + if ( + !this.terrain || + (this.terrain && drapeRenderMode !== this.terrain.drapeRenderMode) + ) { this._createTerrain(terrainOptions, drapeRenderMode); - } else { // Updating + } else { + // Updating const terrain = this.terrain; const currSpec = terrain.get(); for (const key in terrainOptions) { @@ -1503,9 +1927,12 @@ class Style extends Evented { this.stylesheet.terrain = terrainOptions; const parameters = { now: browser.now(), - transition: extend({ - duration: 0 - }, this.stylesheet.transition) + transition: extend( + { + duration: 0, + }, + this.stylesheet.transition + ), }; terrain.updateTransitions(parameters); @@ -1519,13 +1946,16 @@ class Style extends Evented { } _createFog(fogOptions: FogSpecification) { - const fog = this.fog = new Fog(fogOptions, this.map.transform); + const fog = (this.fog = new Fog(fogOptions, this.map.transform)); this.stylesheet.fog = fogOptions; const parameters = { now: browser.now(), - transition: extend({ - duration: 0 - }, this.stylesheet.transition) + transition: extend( + { + duration: 0, + }, + this.stylesheet.transition + ), }; fog.updateTransitions(parameters); @@ -1570,9 +2000,12 @@ class Style extends Evented { this.stylesheet.fog = fogOptions; const parameters = { now: browser.now(), - transition: extend({ - duration: 0 - }, this.stylesheet.transition) + transition: extend( + { + duration: 0, + }, + this.stylesheet.transition + ), }; fog.updateTransitions(parameters); @@ -1601,16 +2034,28 @@ class Style extends Evented { this._drapedFirstOrder.push(...nonDraped); } - _createTerrain(terrainOptions: TerrainSpecification, drapeRenderMode: number) { - const terrain = this.terrain = new Terrain(terrainOptions, drapeRenderMode); + _createTerrain( + terrainOptions: TerrainSpecification, + drapeRenderMode: number + ) { + const terrain = (this.terrain = new Terrain( + terrainOptions, + drapeRenderMode + )); this.stylesheet.terrain = terrainOptions; - this.dispatcher.broadcast('enableTerrain', !this.terrainSetForDrapingOnly()); + this.dispatcher.broadcast( + "enableTerrain", + !this.terrainSetForDrapingOnly() + ); this._force3DLayerUpdate(); const parameters = { now: browser.now(), - transition: extend({ - duration: 0 - }, this.stylesheet.transition) + transition: extend( + { + duration: 0, + }, + this.stylesheet.transition + ), }; terrain.updateTransitions(parameters); @@ -1619,7 +2064,7 @@ class Style extends Evented { _force3DLayerUpdate() { for (const layerId in this._layers) { const layer = this._layers[layerId]; - if (layer.type === 'fill-extrusion') { + if (layer.type === "fill-extrusion") { this._updateLayer(layer); } } @@ -1628,22 +2073,37 @@ class Style extends Evented { _forceSymbolLayerUpdate() { for (const layerId in this._layers) { const layer = this._layers[layerId]; - if (layer.type === 'symbol') { + if (layer.type === "symbol") { this._updateLayer(layer); } } } - _validate(validate: Validator, key: string, value: any, props: any, options: { validate?: boolean } = {}): boolean { + _validate( + validate: Validator, + key: string, + value: any, + props: any, + options: { validate?: boolean } = {} + ): boolean { if (options && options.validate === false) { return false; } - return emitValidationErrors(this, validate.call(validateStyle, extend({ - key, - style: this.serialize(), - value, - styleSpec - }, props))); + return emitValidationErrors( + this, + validate.call( + validateStyle, + extend( + { + key, + style: this.serialize(), + value, + styleSpec, + }, + props + ) + ) + ); } _remove() { @@ -1655,7 +2115,10 @@ class Style extends Evented { this._spriteRequest.cancel(); this._spriteRequest = null; } - rtlTextPluginEvented.off('pluginStateChange', this._rtlTextPluginCallback); + rtlTextPluginEvented.off( + "pluginStateChange", + this._rtlTextPluginCallback + ); for (const layerId in this._layers) { const layer: StyleLayer = this._layers[layerId]; layer.setEventedParent(null); @@ -1698,7 +2161,13 @@ class Style extends Evented { } } - _updatePlacement(transform: Transform, showCollisionBoxes: boolean, fadeDuration: number, crossSourceCollisions: boolean, forceFullPlacement: boolean = false): boolean { + _updatePlacement( + transform: Transform, + showCollisionBoxes: boolean, + fadeDuration: number, + crossSourceCollisions: boolean, + forceFullPlacement: boolean = false + ): boolean { let symbolBucketsChanged = false; let placementCommitted = false; @@ -1706,17 +2175,27 @@ class Style extends Evented { for (const layerID of this._order) { const styleLayer = this._layers[layerID]; - if (styleLayer.type !== 'symbol') continue; + if (styleLayer.type !== "symbol") continue; if (!layerTiles[styleLayer.source]) { const sourceCache = this._getLayerSourceCache(styleLayer); if (!sourceCache) continue; - layerTiles[styleLayer.source] = sourceCache.getRenderableIds(true) + layerTiles[styleLayer.source] = sourceCache + .getRenderableIds(true) .map((id) => sourceCache.getTileByID(id)) - .sort((a, b) => (b.tileID.overscaledZ - a.tileID.overscaledZ) || (a.tileID.isLessThan(b.tileID) ? -1 : 1)); + .sort( + (a, b) => + b.tileID.overscaledZ - a.tileID.overscaledZ || + (a.tileID.isLessThan(b.tileID) ? -1 : 1) + ); } - const layerBucketsChanged = this.crossTileSymbolIndex.addLayer(styleLayer, layerTiles[styleLayer.source], transform.center.lng, transform.projection); + const layerBucketsChanged = this.crossTileSymbolIndex.addLayer( + styleLayer, + layerTiles[styleLayer.source], + transform.center.lng, + transform.projection + ); symbolBucketsChanged = symbolBucketsChanged || layerBucketsChanged; } this.crossTileSymbolIndex.pruneUnusedLayers(this._order); @@ -1727,15 +2206,33 @@ class Style extends Evented { // We need to restart placement to keep layer indices in sync. // Also force full placement when fadeDuration === 0 to ensure that newly loaded // tiles will fully display symbols in their first frame - forceFullPlacement = forceFullPlacement || this._layerOrderChanged || fadeDuration === 0; + forceFullPlacement = + forceFullPlacement || this._layerOrderChanged || fadeDuration === 0; if (this._layerOrderChanged) { - this.fire(new Event('neworder')); - } - - if (forceFullPlacement || !this.pauseablePlacement || (this.pauseablePlacement.isDone() && !this.placement.stillRecent(browser.now(), transform.zoom))) { - const fogState = this.fog && transform.projection.supportsFog ? this.fog.state : null; - this.pauseablePlacement = new PauseablePlacement(transform, this._order, forceFullPlacement, showCollisionBoxes, fadeDuration, crossSourceCollisions, this.placement, fogState); + this.fire(new Event("neworder")); + } + + if ( + forceFullPlacement || + !this.pauseablePlacement || + (this.pauseablePlacement.isDone() && + !this.placement.stillRecent(browser.now(), transform.zoom)) + ) { + const fogState = + this.fog && transform.projection.supportsFog + ? this.fog.state + : null; + this.pauseablePlacement = new PauseablePlacement( + transform, + this._order, + forceFullPlacement, + showCollisionBoxes, + fadeDuration, + crossSourceCollisions, + this.placement, + fogState + ); this._layerOrderChanged = false; } @@ -1746,7 +2243,11 @@ class Style extends Evented { // render frame this.placement.setStale(); } else { - this.pauseablePlacement.continuePlacement(this._order, this._layers, layerTiles); + this.pauseablePlacement.continuePlacement( + this._order, + this._layers, + layerTiles + ); if (this.pauseablePlacement.isDone()) { this.placement = this.pauseablePlacement.commit(browser.now()); @@ -1764,13 +2265,18 @@ class Style extends Evented { if (placementCommitted || symbolBucketsChanged) { for (const layerID of this._order) { const styleLayer = this._layers[layerID]; - if (styleLayer.type !== 'symbol') continue; - this.placement.updateLayerOpacities(styleLayer, layerTiles[styleLayer.source]); + if (styleLayer.type !== "symbol") continue; + this.placement.updateLayerOpacities( + styleLayer, + layerTiles[styleLayer.source] + ); } } // 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(browser.now()); return needsRerender; } @@ -1782,8 +2288,16 @@ class Style extends Evented { // Callbacks from web workers - getImages(mapId: string, params: {icons: Array, source: string, tileID: OverscaledTileID, type: string}, callback: Callback<{[_: string]: StyleImage}>) { - + getImages( + mapId: string, + params: { + icons: Array, + source: string, + tileID: OverscaledTileID, + type: string, + }, + callback: Callback<{ [_: string]: StyleImage }> + ) { this.imageManager.getImages(params.icons, callback); // Apply queued image changes before setting the tile's dependencies so that the tile @@ -1798,18 +2312,36 @@ class Style extends Evented { const setDependencies = (sourceCache: SourceCache) => { if (sourceCache) { - sourceCache.setDependencies(params.tileID.key, params.type, params.icons); + sourceCache.setDependencies( + params.tileID.key, + params.type, + params.icons + ); } }; setDependencies(this._otherSourceCaches[params.source]); setDependencies(this._symbolSourceCaches[params.source]); } - getGlyphs(mapId: string, params: {stacks: {[_: string]: Array}}, callback: Callback<{[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}}>) { + getGlyphs( + mapId: string, + params: { stacks: { [_: string]: Array } }, + callback: Callback<{ + [_: string]: { + glyphs: { [_: number]: ?StyleGlyph }, + ascender?: number, + descender?: number, + }, + }> + ) { this.glyphManager.getGlyphs(params.stacks, callback); } - getResource(mapId: string, params: RequestParameters, callback: ResponseCallback): Cancelable { + getResource( + mapId: string, + params: RequestParameters, + callback: ResponseCallback + ): Cancelable { return makeRequest(params, callback); } @@ -1818,9 +2350,9 @@ class Style extends Evented { } _getLayerSourceCache(layer: StyleLayer): SourceCache | void { - return layer.type === 'symbol' ? - this._symbolSourceCaches[layer.source] : - this._otherSourceCaches[layer.source]; + return layer.type === "symbol" + ? this._symbolSourceCaches[layer.source] + : this._otherSourceCaches[layer.source]; } _getSourceCaches(source: string): Array { @@ -1837,10 +2369,14 @@ class Style extends Evented { _isSourceCacheLoaded(source: string): boolean { const sourceCaches = this._getSourceCaches(source); if (sourceCaches.length === 0) { - this.fire(new ErrorEvent(new Error(`There is no source with ID '${source}'`))); + this.fire( + new ErrorEvent( + new Error(`There is no source with ID '${source}'`) + ) + ); return false; } - return sourceCaches.every(sc => sc.loaded()); + return sourceCaches.every((sc) => sc.loaded()); } has3DLayers(): boolean { @@ -1856,7 +2392,7 @@ class Style extends Evented { } _clearWorkerCaches() { - this.dispatcher.broadcast('clearCaches'); + this.dispatcher.broadcast("clearCaches"); } destroy() { diff --git a/src/ui/map.js b/src/ui/map.js index 9af38147d47..a961e41f3fe 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -1,59 +1,78 @@ // @flow -import {version} from '../../package.json'; -import {asyncAll, extend, bindAll, warnOnce, uniqueId, isSafariWithAntialiasingBug} from '../util/util.js'; -import browser from '../util/browser.js'; -import window from '../util/window.js'; -import * as DOM from '../util/dom.js'; -import {getImage, getJSON, ResourceType} from '../util/ajax.js'; -import {RequestManager, getMapSessionAPI, postMapLoadEvent, AUTH_ERR_MSG, storeAuthState, removeAuthState} from '../util/mapbox.js'; -import Style from '../style/style.js'; -import EvaluationParameters from '../style/evaluation_parameters.js'; -import Painter from '../render/painter.js'; -import Transform from '../geo/transform.js'; -import Hash from './hash.js'; -import HandlerManager from './handler_manager.js'; -import Camera from './camera.js'; -import LngLat from '../geo/lng_lat.js'; -import MercatorCoordinate from '../geo/mercator_coordinate.js'; -import LngLatBounds from '../geo/lng_lat_bounds.js'; -import Point from '@mapbox/point-geometry'; -import AttributionControl from './control/attribution_control.js'; -import LogoControl from './control/logo_control.js'; -import {supported} from '@mapbox/mapbox-gl-supported'; -import {RGBAImage} from '../util/image.js'; -import {Event, ErrorEvent} from '../util/evented.js'; -import {MapMouseEvent} from './events.js'; -import TaskQueue from '../util/task_queue.js'; -import webpSupported from '../util/webp_supported.js'; -import {PerformanceMarkers, PerformanceUtils} from '../util/performance.js'; -import Marker from '../ui/marker.js'; -import EasedVariable from '../util/eased_variable.js'; -import SourceCache from '../source/source_cache.js'; -import {GLOBE_ZOOM_THRESHOLD_MAX} from '../geo/projection/globe_util.js'; - -import {setCacheLimits} from '../util/tile_request_cache.js'; - -import type {PointLike} from '@mapbox/point-geometry'; -import type {RequestTransformFunction} from '../util/mapbox.js'; -import type {LngLatLike} from '../geo/lng_lat.js'; -import type {LngLatBoundsLike} from '../geo/lng_lat_bounds.js'; -import type {StyleOptions, StyleSetterOptions} from '../style/style.js'; -import type {MapEvent, MapDataEvent} from './events.js'; -import type {CustomLayerInterface} from '../style/style_layer/custom_style_layer.js'; -import type {StyleImageInterface, StyleImageMetadata} from '../style/style_image.js'; - -import type ScrollZoomHandler from './handler/scroll_zoom.js'; -import type BoxZoomHandler from './handler/box_zoom.js'; -import type {TouchPitchHandler} from './handler/touch_zoom_rotate.js'; -import type DragRotateHandler from './handler/shim/drag_rotate.js'; -import type DragPanHandler, {DragPanOptions} from './handler/shim/drag_pan.js'; -import type KeyboardHandler from './handler/keyboard.js'; -import type DoubleClickZoomHandler from './handler/shim/dblclick_zoom.js'; -import type TouchZoomRotateHandler from './handler/shim/touch_zoom_rotate.js'; -import defaultLocale from './default_locale.js'; -import type {TaskID} from '../util/task_queue.js'; -import type {Cancelable} from '../types/cancelable.js'; +import { version } from "../../package.json"; +import { + asyncAll, + extend, + bindAll, + warnOnce, + uniqueId, + isSafariWithAntialiasingBug, +} from "../util/util.js"; +import browser from "../util/browser.js"; +import window from "../util/window.js"; +import * as DOM from "../util/dom.js"; +import { getImage, getJSON, ResourceType } from "../util/ajax.js"; +import { + RequestManager, + getMapSessionAPI, + postMapLoadEvent, + AUTH_ERR_MSG, + storeAuthState, + removeAuthState, +} from "../util/mapbox.js"; +import Style from "../style/style.js"; +import EvaluationParameters from "../style/evaluation_parameters.js"; +import Painter from "../render/painter.js"; +import Transform from "../geo/transform.js"; +import Hash from "./hash.js"; +import HandlerManager from "./handler_manager.js"; +import Camera from "./camera.js"; +import LngLat from "../geo/lng_lat.js"; +import MercatorCoordinate from "../geo/mercator_coordinate.js"; +import LngLatBounds from "../geo/lng_lat_bounds.js"; +import Point from "@mapbox/point-geometry"; +import AttributionControl from "./control/attribution_control.js"; +import LogoControl from "./control/logo_control.js"; +import { supported } from "@mapbox/mapbox-gl-supported"; +import { RGBAImage } from "../util/image.js"; +import { Event, ErrorEvent } from "../util/evented.js"; +import { MapMouseEvent } from "./events.js"; +import TaskQueue from "../util/task_queue.js"; +import webpSupported from "../util/webp_supported.js"; +import { PerformanceMarkers, PerformanceUtils } from "../util/performance.js"; +import Marker from "../ui/marker.js"; +import EasedVariable from "../util/eased_variable.js"; +import SourceCache from "../source/source_cache.js"; +import { GLOBE_ZOOM_THRESHOLD_MAX } from "../geo/projection/globe_util.js"; + +import { setCacheLimits } from "../util/tile_request_cache.js"; + +import type { PointLike } from "@mapbox/point-geometry"; +import type { RequestTransformFunction } from "../util/mapbox.js"; +import type { LngLatLike } from "../geo/lng_lat.js"; +import type { LngLatBoundsLike } from "../geo/lng_lat_bounds.js"; +import type { StyleOptions, StyleSetterOptions } from "../style/style.js"; +import type { MapEvent, MapDataEvent } from "./events.js"; +import type { CustomLayerInterface } from "../style/style_layer/custom_style_layer.js"; +import type { + StyleImageInterface, + StyleImageMetadata, +} from "../style/style_image.js"; + +import type ScrollZoomHandler from "./handler/scroll_zoom.js"; +import type BoxZoomHandler from "./handler/box_zoom.js"; +import type { TouchPitchHandler } from "./handler/touch_zoom_rotate.js"; +import type DragRotateHandler from "./handler/shim/drag_rotate.js"; +import type DragPanHandler, { + DragPanOptions, +} from "./handler/shim/drag_pan.js"; +import type KeyboardHandler from "./handler/keyboard.js"; +import type DoubleClickZoomHandler from "./handler/shim/dblclick_zoom.js"; +import type TouchZoomRotateHandler from "./handler/shim/touch_zoom_rotate.js"; +import defaultLocale from "./default_locale.js"; +import type { TaskID } from "../util/task_queue.js"; +import type { Cancelable } from "../types/cancelable.js"; import type { LayerSpecification, FilterSpecification, @@ -64,22 +83,26 @@ import type { SourceSpecification, ProjectionSpecification, PropertyValueSpecification, - TransitionSpecification -} from '../style-spec/types.js'; -import type StyleLayer from '../style/style_layer.js'; -import type {ElevationQueryOptions} from '../terrain/elevation.js'; -import type {Source} from '../source/source.js'; -import type {QueryFeature} from '../util/vectortile_to_geojson.js'; -import type {QueryResult} from '../data/feature_index.js'; - -export type ControlPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; + TransitionSpecification, +} from "../style-spec/types.js"; +import type StyleLayer from "../style/style_layer.js"; +import type { ElevationQueryOptions } from "../terrain/elevation.js"; +import type { Source } from "../source/source.js"; +import type { QueryFeature } from "../util/vectortile_to_geojson.js"; +import type { QueryResult } from "../data/feature_index.js"; + +export type ControlPosition = + | "top-left" + | "top-right" + | "bottom-left" + | "bottom-right"; /* eslint-disable no-use-before-define */ type IControl = { - onAdd(map: Map): HTMLElement; - onRemove(map: Map): void; + onAdd(map: Map): HTMLElement, + onRemove(map: Map): void, - +getDefaultPosition?: () => ControlPosition; -} + +getDefaultPosition?: () => ControlPosition, +}; /* eslint-enable no-use-before-define */ export const AVERAGE_ELEVATION_SAMPLING_INTERVAL = 500; // ms @@ -126,7 +149,7 @@ type MapOptions = { accessToken: string, testMode: ?boolean, locale?: Object, - projection?: ProjectionSpecification | string + projection?: ProjectionSpecification | string, }; const defaultMinZoom = -2; @@ -174,12 +197,12 @@ const defaultOptions = { refreshExpiredTiles: true, minTileCacheSize: null, maxTileCacheSize: null, - localIdeographFontFamily: 'sans-serif', + localIdeographFontFamily: "sans-serif", localFontFamily: null, transformRequest: null, accessToken: null, fadeDuration: 300, - crossSourceCollisions: true + crossSourceCollisions: true, }; /** @@ -318,7 +341,7 @@ class Map extends Camera { _missingCSSCanary: HTMLElement; _canvasContainer: HTMLElement; _controlContainer: HTMLElement; - _controlPositions: {[_: string]: HTMLElement}; + _controlPositions: { [_: string]: HTMLElement }; _interactive: ?boolean; _showTileBoundaries: ?boolean; _showTerrainWireframe: ?boolean; @@ -435,35 +458,58 @@ class Map extends Camera { options = extend({}, defaultOptions, options); - if (options.minZoom != null && options.maxZoom != null && options.minZoom > options.maxZoom) { + if ( + options.minZoom != null && + options.maxZoom != null && + options.minZoom > options.maxZoom + ) { throw new Error(`maxZoom must be greater than or equal to minZoom`); } - if (options.minPitch != null && options.maxPitch != null && options.minPitch > options.maxPitch) { - throw new Error(`maxPitch must be greater than or equal to minPitch`); + if ( + options.minPitch != null && + options.maxPitch != null && + options.minPitch > options.maxPitch + ) { + throw new Error( + `maxPitch must be greater than or equal to minPitch` + ); } if (options.minPitch != null && options.minPitch < defaultMinPitch) { - throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`); + throw new Error( + `minPitch must be greater than or equal to ${defaultMinPitch}` + ); } if (options.maxPitch != null && options.maxPitch > defaultMaxPitch) { - throw new Error(`maxPitch must be less than or equal to ${defaultMaxPitch}`); + throw new Error( + `maxPitch must be less than or equal to ${defaultMaxPitch}` + ); } // disable antialias with OS/iOS 15.4 and 15.5 due to rendering bug if (options.antialias && isSafariWithAntialiasingBug(window)) { options.antialias = false; - warnOnce('Antialiasing is disabled for this WebGL context to avoid browser bug: https://github.com/mapbox/mapbox-gl-js/issues/11609'); + warnOnce( + "Antialiasing is disabled for this WebGL context to avoid browser bug: https://github.com/mapbox/mapbox-gl-js/issues/11609" + ); } - const transform = new Transform(options.minZoom, options.maxZoom, options.minPitch, options.maxPitch, options.renderWorldCopies); + const transform = new Transform( + options.minZoom, + options.maxZoom, + options.minPitch, + options.maxPitch, + options.renderWorldCopies + ); super(transform, options); this._interactive = options.interactive; this._minTileCacheSize = options.minTileCacheSize; this._maxTileCacheSize = options.maxTileCacheSize; - this._failIfMajorPerformanceCaveat = options.failIfMajorPerformanceCaveat; + this._failIfMajorPerformanceCaveat = + options.failIfMajorPerformanceCaveat; this._preserveDrawingBuffer = options.preserveDrawingBuffer; this._antialias = options.antialias; this._trackResize = options.trackResize; @@ -492,10 +538,14 @@ class Map extends Camera { this._explicitProjection = null; // Fallback to stylesheet by default - this._requestManager = new RequestManager(options.transformRequest, options.accessToken, options.testMode); + this._requestManager = new RequestManager( + options.transformRequest, + options.accessToken, + options.testMode + ); this._silenceAuthErrors = !!options.testMode; - if (typeof options.container === 'string') { + if (typeof options.container === "string") { this._container = window.document.getElementById(options.container); if (!this._container) { @@ -504,24 +554,31 @@ class Map extends Camera { } else if (options.container instanceof window.HTMLElement) { this._container = options.container; } else { - throw new Error(`Invalid type: 'container' must be a String or HTMLElement.`); + throw new Error( + `Invalid type: 'container' must be a String or HTMLElement.` + ); } if (this._container.childNodes.length > 0) { - warnOnce(`The map container element should be empty, otherwise the map's interactivity will be negatively impacted. If you want to display a message when WebGL is not supported, use the Mapbox GL Supported plugin instead.`); + warnOnce( + `The map container element should be empty, otherwise the map's interactivity will be negatively impacted. If you want to display a message when WebGL is not supported, use the Mapbox GL Supported plugin instead.` + ); } if (options.maxBounds) { this.setMaxBounds(options.maxBounds); } - bindAll([ - '_onWindowOnline', - '_onWindowResize', - '_onMapScroll', - '_contextLost', - '_contextRestored' - ], this); + bindAll( + [ + "_onWindowOnline", + "_onWindowResize", + "_onMapScroll", + "_contextLost", + "_contextRestored", + ], + this + ); this._setupContainer(); this._setupPainter(); @@ -529,15 +586,23 @@ class Map extends Camera { throw new Error(`Failed to initialize WebGL.`); } - this.on('move', () => this._update(false)); - this.on('moveend', () => this._update(false)); - this.on('zoom', () => this._update(true)); - - if (typeof window !== 'undefined') { - window.addEventListener('online', this._onWindowOnline, false); - window.addEventListener('resize', this._onWindowResize, false); - window.addEventListener('orientationchange', this._onWindowResize, false); - window.addEventListener('webkitfullscreenchange', this._onWindowResize, false); + this.on("move", () => this._update(false)); + this.on("moveend", () => this._update(false)); + this.on("zoom", () => this._update(true)); + + if (typeof window !== "undefined") { + window.addEventListener("online", this._onWindowOnline, false); + window.addEventListener("resize", this._onWindowResize, false); + window.addEventListener( + "orientationchange", + this._onWindowResize, + false + ); + window.addEventListener( + "webkitfullscreenchange", + this._onWindowResize, + false + ); } this.handlers = new HandlerManager(this, options); @@ -546,58 +611,69 @@ class Map extends Camera { this._localIdeographFontFamily = options.localIdeographFontFamily; if (options.style) { - this.setStyle(options.style, {localFontFamily: this._localFontFamily, localIdeographFontFamily: this._localIdeographFontFamily}); + this.setStyle(options.style, { + localFontFamily: this._localFontFamily, + localIdeographFontFamily: this._localIdeographFontFamily, + }); } if (options.projection) { this.setProjection(options.projection); } - const hashName = (typeof options.hash === 'string' && options.hash) || undefined; - this._hash = options.hash && (new Hash(hashName)).addTo(this); + const hashName = + (typeof options.hash === "string" && options.hash) || undefined; + this._hash = options.hash && new Hash(hashName).addTo(this); // don't set position from options if set through hash if (!this._hash || !this._hash._onHashChange()) { this.jumpTo({ center: options.center, zoom: options.zoom, bearing: options.bearing, - pitch: options.pitch + pitch: options.pitch, }); if (options.bounds) { this.resize(); - this.fitBounds(options.bounds, extend({}, options.fitBoundsOptions, {duration: 0})); + this.fitBounds( + options.bounds, + extend({}, options.fitBoundsOptions, { duration: 0 }) + ); } } this.resize(); if (options.attributionControl) - this.addControl(new AttributionControl({customAttribution: options.customAttribution})); + this.addControl( + new AttributionControl({ + customAttribution: options.customAttribution, + }) + ); this._logoControl = new LogoControl(); this.addControl(this._logoControl, options.logoPosition); - this.on('style.load', () => { + this.on("style.load", () => { if (this.transform.unmodified) { this.jumpTo((this.style.stylesheet: any)); } }); - this.on('data', (event: MapDataEvent) => { - this._update(event.dataType === 'style'); + this.on("data", (event: MapDataEvent) => { + this._update(event.dataType === "style"); this.fire(new Event(`${event.dataType}data`, event)); }); - this.on('dataloading', (event: MapDataEvent) => { + this.on("dataloading", (event: MapDataEvent) => { this.fire(new Event(`${event.dataType}dataloading`, event)); }); } /* - * Returns a unique number for this map instance which is used for the MapLoadEvent - * to make sure we only fire one event per instantiated map object. - * @private - * @returns {number} - */ + * Returns a unique number for this map instance which is used for the MapLoadEvent + * to make sure we only fire one event per instantiated map object. + * @private + * @returns {number} + */ _getMapId(): number { return this._mapId; } @@ -621,19 +697,27 @@ class Map extends Camera { if (control.getDefaultPosition) { position = control.getDefaultPosition(); } else { - position = 'top-right'; + position = "top-right"; } } if (!control || !control.onAdd) { - return this.fire(new ErrorEvent(new Error( - 'Invalid argument to map.addControl(). Argument must be a control with onAdd and onRemove methods.'))); + return this.fire( + new ErrorEvent( + new Error( + "Invalid argument to map.addControl(). Argument must be a control with onAdd and onRemove methods." + ) + ) + ); } const controlElement = control.onAdd(this); this._controls.push(control); const positionContainer = this._controlPositions[position]; - if (position.indexOf('bottom') !== -1) { - positionContainer.insertBefore(controlElement, positionContainer.firstChild); + if (position.indexOf("bottom") !== -1) { + positionContainer.insertBefore( + controlElement, + positionContainer.firstChild + ); } else { positionContainer.appendChild(controlElement); } @@ -655,8 +739,13 @@ class Map extends Camera { */ removeControl(control: IControl): this { if (!control || !control.onRemove) { - return this.fire(new ErrorEvent(new Error( - 'Invalid argument to map.removeControl(). Argument must be a control with onAdd and onRemove methods.'))); + return this.fire( + new ErrorEvent( + new Error( + "Invalid argument to map.removeControl(). Argument must be a control with onAdd and onRemove methods." + ) + ) + ); } const ci = this._controls.indexOf(control); if (ci > -1) this._controls.splice(ci, 1); @@ -750,22 +839,30 @@ class Map extends Camera { this._updateContainerDimensions(); // do nothing if container remained the same size - if (this._containerWidth === this.transform.width && this._containerHeight === this.transform.height) return this; + if ( + this._containerWidth === this.transform.width && + this._containerHeight === this.transform.height + ) + return this; this._resizeCanvas(this._containerWidth, this._containerHeight); this.transform.resize(this._containerWidth, this._containerHeight); - this.painter.resize(Math.ceil(this._containerWidth), Math.ceil(this._containerHeight)); + this.painter.resize( + Math.ceil(this._containerWidth), + Math.ceil(this._containerHeight) + ); const fireMoving = !this._moving; if (fireMoving) { - this.fire(new Event('movestart', eventData)) - .fire(new Event('move', eventData)); + this.fire(new Event("movestart", eventData)).fire( + new Event("move", eventData) + ); } - this.fire(new Event('resize', eventData)); + this.fire(new Event("resize", eventData)); - if (fireMoving) this.fire(new Event('moveend', eventData)); + if (fireMoving) this.fire(new Event("moveend", eventData)); return this; } @@ -838,8 +935,10 @@ class Map extends Camera { * map.setMinZoom(12.25); */ setMinZoom(minZoom?: ?number): this { - - minZoom = minZoom === null || minZoom === undefined ? defaultMinZoom : minZoom; + minZoom = + minZoom === null || minZoom === undefined + ? defaultMinZoom + : minZoom; if (minZoom >= defaultMinZoom && minZoom <= this.transform.maxZoom) { this.transform.minZoom = minZoom; @@ -848,14 +947,16 @@ class Map extends Camera { if (this.getZoom() < minZoom) { this.setZoom(minZoom); } else { - this.fire(new Event('zoomstart')) - .fire(new Event('zoom')) - .fire(new Event('zoomend')); + this.fire(new Event("zoomstart")) + .fire(new Event("zoom")) + .fire(new Event("zoomend")); } return this; - - } else throw new Error(`minZoom must be between ${defaultMinZoom} and the current maxZoom, inclusive`); + } else + throw new Error( + `minZoom must be between ${defaultMinZoom} and the current maxZoom, inclusive` + ); } /** @@ -865,7 +966,9 @@ class Map extends Camera { * @example * const minZoom = map.getMinZoom(); */ - getMinZoom(): number { return this.transform.minZoom; } + getMinZoom(): number { + return this.transform.minZoom; + } /** * Sets or clears the map's maximum zoom level. @@ -879,8 +982,10 @@ class Map extends Camera { * map.setMaxZoom(18.75); */ setMaxZoom(maxZoom?: ?number): this { - - maxZoom = maxZoom === null || maxZoom === undefined ? defaultMaxZoom : maxZoom; + maxZoom = + maxZoom === null || maxZoom === undefined + ? defaultMaxZoom + : maxZoom; if (maxZoom >= this.transform.minZoom) { this.transform.maxZoom = maxZoom; @@ -889,14 +994,14 @@ class Map extends Camera { if (this.getZoom() > maxZoom) { this.setZoom(maxZoom); } else { - this.fire(new Event('zoomstart')) - .fire(new Event('zoom')) - .fire(new Event('zoomend')); + this.fire(new Event("zoomstart")) + .fire(new Event("zoom")) + .fire(new Event("zoomend")); } return this; - - } else throw new Error(`maxZoom must be greater than the current minZoom`); + } else + throw new Error(`maxZoom must be greater than the current minZoom`); } /** @@ -906,7 +1011,9 @@ class Map extends Camera { * @example * const maxZoom = map.getMaxZoom(); */ - getMaxZoom(): number { return this.transform.maxZoom; } + getMaxZoom(): number { + return this.transform.maxZoom; + } /** * Sets or clears the map's minimum pitch. @@ -919,28 +1026,37 @@ class Map extends Camera { * map.setMinPitch(5); */ setMinPitch(minPitch?: ?number): this { - - minPitch = minPitch === null || minPitch === undefined ? defaultMinPitch : minPitch; + minPitch = + minPitch === null || minPitch === undefined + ? defaultMinPitch + : minPitch; if (minPitch < defaultMinPitch) { - throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`); + throw new Error( + `minPitch must be greater than or equal to ${defaultMinPitch}` + ); } - if (minPitch >= defaultMinPitch && minPitch <= this.transform.maxPitch) { + if ( + minPitch >= defaultMinPitch && + minPitch <= this.transform.maxPitch + ) { this.transform.minPitch = minPitch; this._update(); if (this.getPitch() < minPitch) { this.setPitch(minPitch); } else { - this.fire(new Event('pitchstart')) - .fire(new Event('pitch')) - .fire(new Event('pitchend')); + this.fire(new Event("pitchstart")) + .fire(new Event("pitch")) + .fire(new Event("pitchend")); } return this; - - } else throw new Error(`minPitch must be between ${defaultMinPitch} and the current maxPitch, inclusive`); + } else + throw new Error( + `minPitch must be between ${defaultMinPitch} and the current maxPitch, inclusive` + ); } /** @@ -950,7 +1066,9 @@ class Map extends Camera { * @example * const minPitch = map.getMinPitch(); */ - getMinPitch(): number { return this.transform.minPitch; } + getMinPitch(): number { + return this.transform.minPitch; + } /** * Sets or clears the map's maximum pitch. @@ -964,11 +1082,15 @@ class Map extends Camera { * map.setMaxPitch(70); */ setMaxPitch(maxPitch?: ?number): this { - - maxPitch = maxPitch === null || maxPitch === undefined ? defaultMaxPitch : maxPitch; + maxPitch = + maxPitch === null || maxPitch === undefined + ? defaultMaxPitch + : maxPitch; if (maxPitch > defaultMaxPitch) { - throw new Error(`maxPitch must be less than or equal to ${defaultMaxPitch}`); + throw new Error( + `maxPitch must be less than or equal to ${defaultMaxPitch}` + ); } if (maxPitch >= this.transform.minPitch) { @@ -978,14 +1100,16 @@ class Map extends Camera { if (this.getPitch() > maxPitch) { this.setPitch(maxPitch); } else { - this.fire(new Event('pitchstart')) - .fire(new Event('pitch')) - .fire(new Event('pitchend')); + this.fire(new Event("pitchstart")) + .fire(new Event("pitch")) + .fire(new Event("pitchend")); } return this; - - } else throw new Error(`maxPitch must be greater than the current minPitch`); + } else + throw new Error( + `maxPitch must be greater than the current minPitch` + ); } /** @@ -995,7 +1119,9 @@ class Map extends Camera { * @example * const maxPitch = map.getMaxPitch(); */ - getMaxPitch(): number { return this.transform.maxPitch; } + getMaxPitch(): number { + return this.transform.maxPitch; + } /** * Returns the state of `renderWorldCopies`. If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`: @@ -1009,7 +1135,9 @@ class Map extends Camera { * const worldCopiesRendered = map.getRenderWorldCopies(); * @see [Example: Render world copies](https://docs.mapbox.com/mapbox-gl-js/example/render-world-copies/) */ - getRenderWorldCopies(): boolean { return this.transform.renderWorldCopies; } + getRenderWorldCopies(): boolean { + return this.transform.renderWorldCopies; + } /** * Sets the state of `renderWorldCopies`. @@ -1044,10 +1172,14 @@ class Map extends Camera { if (this._explicitProjection) { return this._explicitProjection; } - if (this.style && this.style.stylesheet && this.style.stylesheet.projection) { + if ( + this.style && + this.style.stylesheet && + this.style.stylesheet.projection + ) { return this.style.stylesheet.projection; } - return {name: "mercator", center:[0, 0]}; + return { name: "mercator", center: [0, 0] }; } /** @@ -1059,7 +1191,9 @@ class Map extends Camera { * // do globe things here * } */ - _usingGlobe(): boolean { return this.transform.projection.name === 'globe'; } + _usingGlobe(): boolean { + return this.transform.projection.name === "globe"; + } /** * Sets the map's projection. If called with `null` or `undefined`, the map will reset to Mercator. @@ -1081,34 +1215,50 @@ class Map extends Camera { this._lazyInitEmptyStyle(); if (!projection) { projection = null; - } else if (typeof projection === 'string') { - projection = (({name: projection}: any): ProjectionSpecification); + } else if (typeof projection === "string") { + projection = (({ name: projection }: any): ProjectionSpecification); } return this._updateProjection(projection); } - _updateProjection(explicitProjection?: ProjectionSpecification | null): this { + _updateProjection( + explicitProjection?: ProjectionSpecification | null + ): this { const prevProjection = this.getProjection(); - if (explicitProjection === null) { this._explicitProjection = null; } + if (explicitProjection === null) { + this._explicitProjection = null; + } const projection = explicitProjection || this.getProjection(); // At high zoom on globe, set transform projection to Mercator while _explicitProjection stays globe. - const newProjection = this.transform.setProjection(projection && projection.name === 'globe' ? - {name: (this.transform.zoom >= GLOBE_ZOOM_THRESHOLD_MAX ? 'mercator' : 'globe')} : - projection); + const newProjection = this.transform.setProjection( + projection && projection.name === "globe" + ? { + name: + this.transform.zoom >= GLOBE_ZOOM_THRESHOLD_MAX + ? "mercator" + : "globe", + } + : projection + ); // When called through setProjection, update _explicitProjection if (explicitProjection) { - this._explicitProjection = (explicitProjection.name === "globe" ? - {name:'globe', center:[0, 0]} : - this.transform.getProjection()); + this._explicitProjection = + explicitProjection.name === "globe" + ? { name: "globe", center: [0, 0] } + : this.transform.getProjection(); } if (newProjection) { // If a zoom transition on globe - if (prevProjection.name === 'globe' && this.getProjection().name === 'globe') { + if ( + prevProjection.name === "globe" && + this.getProjection().name === "globe" + ) { this.style._forceSymbolLayerUpdate(); - } else { // If a switch between different projections + } else { + // If a switch between different projections this.painter.clearBackgroundTiles(); for (const id in this.style._sourceCaches) { this.style._sourceCaches[id].clearTiles(); @@ -1167,7 +1317,9 @@ class Map extends Camera { * const isMoving = map.isMoving(); */ isMoving(): boolean { - return this._moving || (this.handlers && this.handlers.isMoving()) || false; + return ( + this._moving || (this.handlers && this.handlers.isMoving()) || false + ); } /** @@ -1178,7 +1330,11 @@ class Map extends Camera { * const isZooming = map.isZooming(); */ isZooming(): boolean { - return this._zooming || (this.handlers && this.handlers.isZooming()) || false; + return ( + this._zooming || + (this.handlers && this.handlers.isZooming()) || + false + ); } /** @@ -1189,51 +1345,96 @@ class Map extends Camera { * map.isRotating(); */ isRotating(): boolean { - return this._rotating || (this.handlers && this.handlers.isRotating()) || false; + return ( + this._rotating || + (this.handlers && this.handlers.isRotating()) || + false + ); } - _createDelegatedListener(type: MapEvent, layers: Array, listener: any): any { - if (type === 'mouseenter' || type === 'mouseover') { + _createDelegatedListener( + type: MapEvent, + layers: Array, + listener: any + ): any { + if (type === "mouseenter" || type === "mouseover") { let mousein = false; const mousemove = (e) => { - const filteredLayers = layers.filter(layerId => this.getLayer(layerId)); - const features = filteredLayers.length ? this.queryRenderedFeatures(e.point, {layers: filteredLayers}) : []; + const filteredLayers = layers.filter((layerId) => + this.getLayer(layerId) + ); + const features = filteredLayers.length + ? this.queryRenderedFeatures(e.point, { + layers: filteredLayers, + }) + : []; if (!features.length) { mousein = false; } else if (!mousein) { mousein = true; - listener.call(this, new MapMouseEvent(type, this, e.originalEvent, {features})); + listener.call( + this, + new MapMouseEvent(type, this, e.originalEvent, { + features, + }) + ); } }; const mouseout = () => { mousein = false; }; - return {layers: new Set(layers), listener, delegates: {mousemove, mouseout}}; - } else if (type === 'mouseleave' || type === 'mouseout') { + return { + layers: new Set(layers), + listener, + delegates: { mousemove, mouseout }, + }; + } else if (type === "mouseleave" || type === "mouseout") { let mousein = false; const mousemove = (e) => { - const filteredLayers = layers.filter(layerId => this.getLayer(layerId)); - const features = filteredLayers.length ? this.queryRenderedFeatures(e.point, {layers: filteredLayers}) : []; + const filteredLayers = layers.filter((layerId) => + this.getLayer(layerId) + ); + const features = filteredLayers.length + ? this.queryRenderedFeatures(e.point, { + layers: filteredLayers, + }) + : []; if (features.length) { mousein = true; } else if (mousein) { mousein = false; - listener.call(this, new MapMouseEvent(type, this, e.originalEvent)); + listener.call( + this, + new MapMouseEvent(type, this, e.originalEvent) + ); } }; const mouseout = (e) => { if (mousein) { mousein = false; - listener.call(this, new MapMouseEvent(type, this, e.originalEvent)); + listener.call( + this, + new MapMouseEvent(type, this, e.originalEvent) + ); } }; - return {layers: new Set(layers), listener, delegates: {mousemove, mouseout}}; + return { + layers: new Set(layers), + listener, + delegates: { mousemove, mouseout }, + }; } else { const delegate = (e) => { - const filteredLayers = layers.filter(layerId => this.getLayer(layerId)); - const features = filteredLayers.length ? this.queryRenderedFeatures(e.point, {layers: filteredLayers}) : []; + const filteredLayers = layers.filter((layerId) => + this.getLayer(layerId) + ); + const features = filteredLayers.length + ? this.queryRenderedFeatures(e.point, { + layers: filteredLayers, + }) + : []; if (features.length) { // Here we need to mutate the original event, so that preventDefault works as expected. e.features = features; @@ -1242,7 +1443,11 @@ class Map extends Camera { } }; - return {layers: new Set(layers), listener, delegates: {[(type: string)]: delegate}}; + return { + layers: new Set(layers), + listener, + delegates: { [(type: string)]: delegate }, + }; } } @@ -1368,7 +1573,11 @@ class Map extends Camera { if (!Array.isArray(layerIds)) { layerIds = [layerIds]; } - const delegatedListener = this._createDelegatedListener(type, layerIds, listener); + const delegatedListener = this._createDelegatedListener( + type, + layerIds, + listener + ); this._delegatedListeners = this._delegatedListeners || {}; this._delegatedListeners[type] = this._delegatedListeners[type] || []; @@ -1421,7 +1630,6 @@ class Map extends Camera { * @see [Example: Play map locations as a slideshow](https://docs.mapbox.com/mapbox-gl-js/example/playback-locations/) */ once(type: MapEvent, layerIds: any, listener: any): this | Promise { - if (listener === undefined) { return super.once(type, layerIds); } @@ -1429,7 +1637,11 @@ class Map extends Camera { if (!Array.isArray(layerIds)) { layerIds = [layerIds]; } - const delegatedListener = this._createDelegatedListener(type, layerIds, listener); + const delegatedListener = this._createDelegatedListener( + type, + layerIds, + listener + ); for (const event in delegatedListener.delegates) { this.once((event: any), delegatedListener.delegates[event]); @@ -1484,9 +1696,15 @@ class Map extends Camera { const removeDelegatedListeners = (listeners: Array) => { for (let i = 0; i < listeners.length; i++) { const delegatedListener = listeners[i]; - if (delegatedListener.listener === listener && areLayerArraysEqual(delegatedListener.layers, layerIds)) { + if ( + delegatedListener.listener === listener && + areLayerArraysEqual(delegatedListener.layers, layerIds) + ) { for (const event in delegatedListener.delegates) { - this.off((event: any), delegatedListener.delegates[event]); + this.off( + (event: any), + delegatedListener.delegates[event] + ); } listeners.splice(i, 1); return this; @@ -1494,7 +1712,9 @@ class Map extends Camera { } }; - const delegatedListeners = this._delegatedListeners ? this._delegatedListeners[type] : undefined; + const delegatedListeners = this._delegatedListeners + ? this._delegatedListeners[type] + : undefined; if (delegatedListeners) { removeDelegatedListeners(delegatedListeners); } @@ -1502,6 +1722,16 @@ class Map extends Camera { return this; } + /** @section {Querying raster values} */ + + queryRasterSource(sourceId: string, geometry: PointLike) { + if (!this.style) { + return undefined; + } + + return this.style.queryRasterSource(sourceId, geometry, this.transform); + } + /** @section {Querying features} */ /** @@ -1584,7 +1814,10 @@ class Map extends Camera { * @see [Example: Highlight features within a bounding box](https://www.mapbox.com/mapbox-gl-js/example/using-box-queryrenderedfeatures/) * @see [Example: Filter features within map view](https://www.mapbox.com/mapbox-gl-js/example/filter-features-within-map-view/) */ - queryRenderedFeatures(geometry?: PointLike | [PointLike, PointLike], options?: Object): Array { + queryRenderedFeatures( + geometry?: PointLike | [PointLike, PointLike], + options?: Object + ): Array { // The first parameter can be omitted entirely, making this effectively an overloaded method // with two signatures: // @@ -1598,15 +1831,27 @@ class Map extends Camera { return []; } - if (options === undefined && geometry !== undefined && !(geometry instanceof Point) && !Array.isArray(geometry)) { + if ( + options === undefined && + geometry !== undefined && + !(geometry instanceof Point) && + !Array.isArray(geometry) + ) { options = (geometry: Object); geometry = undefined; } options = options || {}; - geometry = geometry || [[0, 0], [this.transform.width, this.transform.height]]; - - return this.style.queryRenderedFeatures(geometry, options, this.transform); + geometry = geometry || [ + [0, 0], + [this.transform.width, this.transform.height], + ]; + + return this.style.queryRenderedFeatures( + geometry, + options, + this.transform + ); } /** @@ -1646,7 +1891,14 @@ class Map extends Camera { * * @see [Example: Highlight features containing similar data](https://www.mapbox.com/mapbox-gl-js/example/query-similar-features/) */ - querySourceFeatures(sourceId: string, parameters: ?{sourceLayer: ?string, filter: ?Array, validate?: boolean}): Array { + querySourceFeatures( + sourceId: string, + parameters: ?{ + sourceLayer: ?string, + filter: ?Array, + validate?: boolean, + } + ): Array { return this.style.querySourceFeatures(sourceId, parameters); } @@ -1666,11 +1918,18 @@ class Map extends Camera { * const elevation = map.queryTerrainElevation(coordinate); * @see [Example: Query terrain elevation](https://docs.mapbox.com/mapbox-gl-js/example/query-terrain-elevation/) */ - queryTerrainElevation(lnglat: LngLatLike, options: ElevationQueryOptions): number | null { + queryTerrainElevation( + lnglat: LngLatLike, + options: ElevationQueryOptions + ): number | null { const elevation = this.transform.elevation; if (elevation) { - options = extend({}, {exaggerated: true}, options); - return elevation.getAtPoint(MercatorCoordinate.fromLngLat(lnglat), null, options.exaggerated); + options = extend({}, { exaggerated: true }, options); + return elevation.getAtPoint( + MercatorCoordinate.fromLngLat(lnglat), + null, + options.exaggerated + ); } return null; } @@ -1703,12 +1962,27 @@ class Map extends Camera { * * @see [Example: Change a map's style](https://www.mapbox.com/mapbox-gl-js/example/setstyle/) */ - setStyle(style: StyleSpecification | string | null, options?: {diff?: boolean} & StyleOptions): this { - options = extend({}, {localIdeographFontFamily: this._localIdeographFontFamily, localFontFamily: this._localFontFamily}, options); - - if ((options.diff !== false && - options.localIdeographFontFamily === this._localIdeographFontFamily && - options.localFontFamily === this._localFontFamily) && this.style && style) { + setStyle( + style: StyleSpecification | string | null, + options?: { diff?: boolean } & StyleOptions + ): this { + options = extend( + {}, + { + localIdeographFontFamily: this._localIdeographFontFamily, + localFontFamily: this._localFontFamily, + }, + options + ); + + if ( + options.diff !== false && + options.localIdeographFontFamily === + this._localIdeographFontFamily && + options.localFontFamily === this._localFontFamily && + this.style && + style + ) { this._diffStyle(style, options); return this; } else { @@ -1727,7 +2001,10 @@ class Map extends Camera { return str; } - _updateStyle(style: StyleSpecification | string | null, options?: {diff?: boolean} & StyleOptions): this { + _updateStyle( + style: StyleSpecification | string | null, + options?: { diff?: boolean } & StyleOptions + ): this { if (this.style) { this.style.setEventedParent(null); this.style._remove(); @@ -1736,9 +2013,9 @@ class Map extends Camera { if (style) { this.style = new Style(this, options || {}); - this.style.setEventedParent(this, {style: this.style}); + this.style.setEventedParent(this, { style: this.style }); - if (typeof style === 'string') { + if (typeof style === "string") { this.style.loadURL(style); } else { this.style.loadJSON(style); @@ -1751,15 +2028,21 @@ class Map extends Camera { _lazyInitEmptyStyle() { if (!this.style) { this.style = new Style(this, {}); - this.style.setEventedParent(this, {style: this.style}); + this.style.setEventedParent(this, { style: this.style }); this.style.loadEmpty(); } } - _diffStyle(style: StyleSpecification | string, options?: {diff?: boolean} & StyleOptions) { - if (typeof style === 'string') { + _diffStyle( + style: StyleSpecification | string, + options?: { diff?: boolean } & StyleOptions + ) { + if (typeof style === "string") { const url = this._requestManager.normalizeStyleURL(style); - const request = this._requestManager.transformRequest(url, ResourceType.Style); + const request = this._requestManager.transformRequest( + url, + ResourceType.Style + ); getJSON(request, (error: ?Error, json: ?Object) => { if (error) { this.fire(new ErrorEvent(error)); @@ -1767,19 +2050,24 @@ class Map extends Camera { this._updateDiff(json, options); } }); - } else if (typeof style === 'object') { + } else if (typeof style === "object") { this._updateDiff(style, options); } } - _updateDiff(style: StyleSpecification, options?: {diff?: boolean} & StyleOptions) { + _updateDiff( + style: StyleSpecification, + options?: { diff?: boolean } & StyleOptions + ) { try { if (this.style.setState(style)) { this._update(true); } } catch (e) { warnOnce( - `Unable to perform style diff: ${e.message || e.error || e}. Rebuilding the style from scratch.` + `Unable to perform style diff: ${ + e.message || e.error || e + }. Rebuilding the style from scratch.` ); this._updateStyle(style, options); } @@ -1812,7 +2100,7 @@ class Map extends Camera { */ isStyleLoaded(): boolean { if (!this.style) { - warnOnce('There is no style added to the map.'); + warnOnce("There is no style added to the map."); return false; } return this.style.loaded(); @@ -1887,7 +2175,8 @@ class Map extends Camera { const tiles = source._tiles; for (const t in tiles) { const tile = tiles[t]; - if (!(tile.state === 'loaded' || tile.state === 'errored')) return false; + if (!(tile.state === "loaded" || tile.state === "errored")) + return false; } } return true; @@ -1991,33 +2280,66 @@ class Map extends Camera { * @see Example: Use `HTMLImageElement`: [Add an icon to the map](https://www.mapbox.com/mapbox-gl-js/example/add-image/) * @see Example: Use `ImageData`: [Add a generated icon to the map](https://www.mapbox.com/mapbox-gl-js/example/add-image-generated/) */ - addImage(id: string, - image: HTMLImageElement | ImageBitmap | ImageData | {width: number, height: number, data: Uint8Array | Uint8ClampedArray} | StyleImageInterface, - {pixelRatio = 1, sdf = false, stretchX, stretchY, content}: $Shape = {}) { + addImage( + id: string, + image: + | HTMLImageElement + | ImageBitmap + | ImageData + | { + width: number, + height: number, + data: Uint8Array | Uint8ClampedArray, + } + | StyleImageInterface, + { + pixelRatio = 1, + sdf = false, + stretchX, + stretchY, + content, + }: $Shape = {} + ) { this._lazyInitEmptyStyle(); const version = 0; - if (image instanceof window.HTMLImageElement || (window.ImageBitmap && image instanceof window.ImageBitmap)) { - const {width, height, data} = browser.getImageData(image); - this.style.addImage(id, {data: new RGBAImage({width, height}, data), pixelRatio, stretchX, stretchY, content, sdf, version}); + if ( + image instanceof window.HTMLImageElement || + (window.ImageBitmap && image instanceof window.ImageBitmap) + ) { + const { width, height, data } = browser.getImageData(image); + this.style.addImage(id, { + data: new RGBAImage({ width, height }, data), + pixelRatio, + stretchX, + stretchY, + content, + sdf, + version, + }); } else if (image.width === undefined || image.height === undefined) { - this.fire(new ErrorEvent(new Error( - 'Invalid arguments to map.addImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, ' + - 'or object with `width`, `height`, and `data` properties with the same format as `ImageData`'))); + this.fire( + new ErrorEvent( + new Error( + "Invalid arguments to map.addImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, " + + "or object with `width`, `height`, and `data` properties with the same format as `ImageData`" + ) + ) + ); } else { - const {width, height} = image; + const { width, height } = image; const userImage = ((image: any): StyleImageInterface); const data = userImage.data; this.style.addImage(id, { - data: new RGBAImage({width, height}, new Uint8Array(data)), + data: new RGBAImage({ width, height }, new Uint8Array(data)), pixelRatio, stretchX, stretchY, content, sdf, version, - userImage + userImage, }); if (userImage.onAdd) { @@ -2044,34 +2366,69 @@ class Map extends Camera { * // replace that image with a new image, 'other-cat-icon.png'. * if (map.hasImage('cat')) map.updateImage('cat', './other-cat-icon.png'); */ - updateImage(id: string, - image: HTMLImageElement | ImageBitmap | ImageData | {width: number, height: number, data: Uint8Array | Uint8ClampedArray} | StyleImageInterface) { - + updateImage( + id: string, + image: + | HTMLImageElement + | ImageBitmap + | ImageData + | { + width: number, + height: number, + data: Uint8Array | Uint8ClampedArray, + } + | StyleImageInterface + ) { const existingImage = this.style.getImage(id); if (!existingImage) { - this.fire(new ErrorEvent(new Error( - 'The map has no image with that id. If you are adding a new image use `map.addImage(...)` instead.'))); + this.fire( + new ErrorEvent( + new Error( + "The map has no image with that id. If you are adding a new image use `map.addImage(...)` instead." + ) + ) + ); return; } - const imageData = (image instanceof window.HTMLImageElement || (window.ImageBitmap && image instanceof window.ImageBitmap)) ? browser.getImageData(image) : image; - const {width, height} = imageData; + const imageData = + image instanceof window.HTMLImageElement || + (window.ImageBitmap && image instanceof window.ImageBitmap) + ? browser.getImageData(image) + : image; + const { width, height } = imageData; // Flow can't refine the type enough to exclude ImageBitmap const data = ((imageData: any).data: Uint8Array | Uint8ClampedArray); if (width === undefined || height === undefined) { - this.fire(new ErrorEvent(new Error( - 'Invalid arguments to map.updateImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, ' + - 'or object with `width`, `height`, and `data` properties with the same format as `ImageData`'))); + this.fire( + new ErrorEvent( + new Error( + "Invalid arguments to map.updateImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, " + + "or object with `width`, `height`, and `data` properties with the same format as `ImageData`" + ) + ) + ); return; } - if (width !== existingImage.data.width || height !== existingImage.data.height) { - this.fire(new ErrorEvent(new Error( - 'The width and height of the updated image must be that same as the previous version of the image'))); + if ( + width !== existingImage.data.width || + height !== existingImage.data.height + ) { + this.fire( + new ErrorEvent( + new Error( + "The width and height of the updated image must be that same as the previous version of the image" + ) + ) + ); return; } - const copy = !(image instanceof window.HTMLImageElement || (window.ImageBitmap && image instanceof window.ImageBitmap)); + const copy = !( + image instanceof window.HTMLImageElement || + (window.ImageBitmap && image instanceof window.ImageBitmap) + ); existingImage.data.replace(data, copy); this.style.updateImage(id, existingImage); @@ -2092,7 +2449,7 @@ class Map extends Camera { */ hasImage(id: string): boolean { if (!id) { - this.fire(new ErrorEvent(new Error('Missing required image id'))); + this.fire(new ErrorEvent(new Error("Missing required image id"))); return false; } @@ -2133,22 +2490,30 @@ class Map extends Camera { * @see [Example: Add an icon to the map](https://www.mapbox.com/mapbox-gl-js/example/add-image/) */ loadImage(url: string, callback: Function) { - getImage(this._requestManager.transformRequest(url, ResourceType.Image), (err, img) => { - callback(err, img instanceof window.HTMLImageElement ? browser.getImageData(img) : img); - }); + getImage( + this._requestManager.transformRequest(url, ResourceType.Image), + (err, img) => { + callback( + err, + img instanceof window.HTMLImageElement + ? browser.getImageData(img) + : img + ); + } + ); } /** - * Returns an Array of strings containing the IDs of all images currently available in the map. - * This includes both images from the style's original [sprite](https://docs.mapbox.com/help/glossary/sprite/) - * and any images that have been added at runtime using {@link Map#addImage}. - * - * @returns {Array} An Array of strings containing the names of all sprites/images currently available in the map. - * - * @example - * const allImages = map.listImages(); - * - */ + * Returns an Array of strings containing the IDs of all images currently available in the map. + * This includes both images from the style's original [sprite](https://docs.mapbox.com/help/glossary/sprite/) + * and any images that have been added at runtime using {@link Map#addImage}. + * + * @returns {Array} An Array of strings containing the names of all sprites/images currently available in the map. + * + * @example + * const allImages = map.listImages(); + * + */ listImages(): Array { return this.style.listImages(); } @@ -2264,7 +2629,10 @@ class Map extends Camera { * @see [Example: Add a vector tile source](https://docs.mapbox.com/mapbox-gl-js/example/vector-source/) * @see [Example: Add a WMS source](https://docs.mapbox.com/mapbox-gl-js/example/wms/) */ - addLayer(layer: LayerSpecification | CustomLayerInterface, beforeId?: string): this { + addLayer( + layer: LayerSpecification | CustomLayerInterface, + beforeId?: string + ): this { this._lazyInitEmptyStyle(); this.style.addLayer(layer, beforeId); return this._update(true); @@ -2379,7 +2747,11 @@ class Map extends Camera { * @see [Example: Create a timeline animation](https://www.mapbox.com/mapbox-gl-js/example/timeline-animation/) * @see [Tutorial: Show changes over time](https://docs.mapbox.com/help/tutorials/show-changes-over-time/) */ - setFilter(layerId: string, filter: ?FilterSpecification, options: StyleSetterOptions = {}): this { + setFilter( + layerId: string, + filter: ?FilterSpecification, + options: StyleSetterOptions = {} + ): this { this.style.setFilter(layerId, filter, options); return this._update(true); } @@ -2412,7 +2784,12 @@ class Map extends Camera { * @see [Example: Adjust a layer's opacity](https://www.mapbox.com/mapbox-gl-js/example/adjust-layer-opacity/) * @see [Example: Create a draggable point](https://www.mapbox.com/mapbox-gl-js/example/drag-a-point/) */ - setPaintProperty(layerId: string, name: string, value: any, options: StyleSetterOptions = {}): this { + setPaintProperty( + layerId: string, + name: string, + value: any, + options: StyleSetterOptions = {} + ): this { this.style.setPaintProperty(layerId, name, value, options); return this._update(true); } @@ -2426,7 +2803,10 @@ class Map extends Camera { * @example * const paintProperty = map.getPaintProperty('mySymbolLayer', 'icon-color'); */ - getPaintProperty(layerId: string, name: string): void | TransitionSpecification | PropertyValueSpecification { + getPaintProperty( + layerId: string, + name: string + ): void | TransitionSpecification | PropertyValueSpecification { return this.style.getPaintProperty(layerId, name); } @@ -2443,7 +2823,12 @@ class Map extends Camera { * map.setLayoutProperty('my-layer', 'visibility', 'none'); * @see [Example: Show and hide layers](https://docs.mapbox.com/mapbox-gl-js/example/toggle-layers/) */ - setLayoutProperty(layerId: string, name: string, value: any, options: StyleSetterOptions = {}): this { + setLayoutProperty( + layerId: string, + name: string, + value: any, + options: StyleSetterOptions = {} + ): this { this.style.setLayoutProperty(layerId, name, value, options); return this._update(true); } @@ -2457,7 +2842,10 @@ class Map extends Camera { * @example * const layoutProperty = map.getLayoutProperty('mySymbolLayer', 'icon-anchor'); */ - getLayoutProperty(layerId: string, name: string): ?PropertyValueSpecification { + getLayoutProperty( + layerId: string, + name: string + ): ?PropertyValueSpecification { return this.style.getLayoutProperty(layerId, name); } @@ -2474,7 +2862,10 @@ class Map extends Camera { * const layerVisibility = map.getLayoutProperty('my-layer', 'visibility'); * @see [Example: Show and hide layers](https://docs.mapbox.com/mapbox-gl-js/example/toggle-layers/) */ - setLight(light: LightSpecification, options: StyleSetterOptions = {}): this { + setLight( + light: LightSpecification, + options: StyleSetterOptions = {} + ): this { this._lazyInitEmptyStyle(); this.style.setLight(light, options); return this._update(true); @@ -2575,7 +2966,10 @@ class Map extends Camera { */ _queryFogOpacity(lnglat: LngLatLike): number { if (!this.style || !this.style.fog) return 0.0; - return this.style.fog.getOpacityAtLatLng(LngLat.convert(lnglat), this.transform); + return this.style.fog.getOpacityAtLatLng( + LngLat.convert(lnglat), + this.transform + ); } /** @section {Feature state} */ @@ -2618,7 +3012,10 @@ class Map extends Camera { * @see [Example: Create a hover effect](https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/) * @see [Tutorial: Create interactive hover effects with Mapbox GL JS](https://docs.mapbox.com/help/tutorials/create-interactive-hover-effects-with-mapbox-gl-js/) */ - setFeatureState(feature: { source: string; sourceLayer?: string; id: string | number; }, state: Object): this { + setFeatureState( + feature: { source: string, sourceLayer?: string, id: string | number }, + state: Object + ): this { this.style.setFeatureState(feature, state); return this._update(); } @@ -2669,8 +3066,11 @@ class Map extends Camera { * }, 'hover'); * }); * - */ - removeFeatureState(feature: { source: string; sourceLayer?: string; id?: string | number; }, key?: string): this { + */ + removeFeatureState( + feature: { source: string, sourceLayer?: string, id?: string | number }, + key?: string + ): this { this.style.removeFeatureState(feature, key); return this._update(); } @@ -2704,7 +3104,11 @@ class Map extends Camera { * }); * */ - getFeatureState(feature: { source: string; sourceLayer?: string; id: string | number; }): any { + getFeatureState(feature: { + source: string, + sourceLayer?: string, + id: string | number, + }): any { return this.style.getFeatureState(feature); } @@ -2720,58 +3124,104 @@ class Map extends Camera { let el = this._container; while (el && (!transformScaleWidth || !transformScaleHeight)) { const transformMatrix = window.getComputedStyle(el).transform; - if (transformMatrix && transformMatrix !== 'none') { - transformValues = transformMatrix.match(/matrix.*\((.+)\)/)[1].split(', '); - if (transformValues[0] && transformValues[0] !== '0' && transformValues[0] !== '1') transformScaleWidth = transformValues[0]; - if (transformValues[3] && transformValues[3] !== '0' && transformValues[3] !== '1') transformScaleHeight = transformValues[3]; + if (transformMatrix && transformMatrix !== "none") { + transformValues = transformMatrix + .match(/matrix.*\((.+)\)/)[1] + .split(", "); + if ( + transformValues[0] && + transformValues[0] !== "0" && + transformValues[0] !== "1" + ) + transformScaleWidth = transformValues[0]; + if ( + transformValues[3] && + transformValues[3] !== "0" && + transformValues[3] !== "1" + ) + transformScaleHeight = transformValues[3]; } el = el.parentElement; } - this._containerWidth = transformScaleWidth ? Math.abs(width / transformScaleWidth) : width; - this._containerHeight = transformScaleHeight ? Math.abs(height / transformScaleHeight) : height; + this._containerWidth = transformScaleWidth + ? Math.abs(width / transformScaleWidth) + : width; + this._containerHeight = transformScaleHeight + ? Math.abs(height / transformScaleHeight) + : height; } _detectMissingCSS(): void { - const computedColor = window.getComputedStyle(this._missingCSSCanary).getPropertyValue('background-color'); - if (computedColor !== 'rgb(250, 128, 114)') { - warnOnce('This page appears to be missing CSS declarations for ' + - 'Mapbox GL JS, which may cause the map to display incorrectly. ' + - 'Please ensure your page includes mapbox-gl.css, as described ' + - 'in https://www.mapbox.com/mapbox-gl-js/api/.'); + const computedColor = window + .getComputedStyle(this._missingCSSCanary) + .getPropertyValue("background-color"); + if (computedColor !== "rgb(250, 128, 114)") { + warnOnce( + "This page appears to be missing CSS declarations for " + + "Mapbox GL JS, which may cause the map to display incorrectly. " + + "Please ensure your page includes mapbox-gl.css, as described " + + "in https://www.mapbox.com/mapbox-gl-js/api/." + ); } } _setupContainer() { const container = this._container; - container.classList.add('mapboxgl-map'); - - const missingCSSCanary = this._missingCSSCanary = DOM.create('div', 'mapboxgl-canary', container); - missingCSSCanary.style.visibility = 'hidden'; + container.classList.add("mapboxgl-map"); + + const missingCSSCanary = (this._missingCSSCanary = DOM.create( + "div", + "mapboxgl-canary", + container + )); + missingCSSCanary.style.visibility = "hidden"; this._detectMissingCSS(); - const canvasContainer = this._canvasContainer = DOM.create('div', 'mapboxgl-canvas-container', container); + const canvasContainer = (this._canvasContainer = DOM.create( + "div", + "mapboxgl-canvas-container", + container + )); if (this._interactive) { - canvasContainer.classList.add('mapboxgl-interactive'); + canvasContainer.classList.add("mapboxgl-interactive"); } - this._canvas = DOM.create('canvas', 'mapboxgl-canvas', canvasContainer); - this._canvas.addEventListener('webglcontextlost', this._contextLost, false); - this._canvas.addEventListener('webglcontextrestored', this._contextRestored, false); - this._canvas.setAttribute('tabindex', '0'); - this._canvas.setAttribute('aria-label', this._getUIString('Map.Title')); - this._canvas.setAttribute('role', 'region'); + this._canvas = DOM.create("canvas", "mapboxgl-canvas", canvasContainer); + this._canvas.addEventListener( + "webglcontextlost", + this._contextLost, + false + ); + this._canvas.addEventListener( + "webglcontextrestored", + this._contextRestored, + false + ); + this._canvas.setAttribute("tabindex", "0"); + this._canvas.setAttribute("aria-label", this._getUIString("Map.Title")); + this._canvas.setAttribute("role", "region"); this._updateContainerDimensions(); this._resizeCanvas(this._containerWidth, this._containerHeight); - const controlContainer = this._controlContainer = DOM.create('div', 'mapboxgl-control-container', container); - const positions = this._controlPositions = {}; - ['top-left', 'top-right', 'bottom-left', 'bottom-right'].forEach((positionName) => { - positions[positionName] = DOM.create('div', `mapboxgl-ctrl-${positionName}`, controlContainer); - }); + const controlContainer = (this._controlContainer = DOM.create( + "div", + "mapboxgl-control-container", + container + )); + const positions = (this._controlPositions = {}); + ["top-left", "top-right", "bottom-left", "bottom-right"].forEach( + (positionName) => { + positions[positionName] = DOM.create( + "div", + `mapboxgl-ctrl-${positionName}`, + controlContainer + ); + } + ); - this._container.addEventListener('scroll', this._onMapScroll, false); + this._container.addEventListener("scroll", this._onMapScroll, false); } _resizeCanvas(width: number, height: number) { @@ -2801,22 +3251,23 @@ class Map extends Camera { const attributes = extend({}, supported.webGLContextAttributes, { failIfMajorPerformanceCaveat: this._failIfMajorPerformanceCaveat, preserveDrawingBuffer: this._preserveDrawingBuffer, - antialias: this._antialias || false + antialias: this._antialias || false, }); - const gl = this._canvas.getContext('webgl', attributes) || - this._canvas.getContext('experimental-webgl', attributes); + const gl = + this._canvas.getContext("webgl", attributes) || + this._canvas.getContext("experimental-webgl", attributes); if (!gl) { - this.fire(new ErrorEvent(new Error('Failed to initialize WebGL'))); + this.fire(new ErrorEvent(new Error("Failed to initialize WebGL"))); return; } storeAuthState(gl, true); this.painter = new Painter(gl, this.transform); - this.on('data', (event: MapDataEvent) => { - if (event.dataType === 'source') { + this.on("data", (event: MapDataEvent) => { + if (event.dataType === "source") { this.painter.setTileLoadedFlag(true); } }); @@ -2830,14 +3281,14 @@ class Map extends Camera { this._frame.cancel(); this._frame = null; } - this.fire(new Event('webglcontextlost', {originalEvent: event})); + this.fire(new Event("webglcontextlost", { originalEvent: event })); } _contextRestored(event: *) { this._setupPainter(); this.resize(); this._update(); - this.fire(new Event('webglcontextrestored', {originalEvent: event})); + this.fire(new Event("webglcontextrestored", { originalEvent: event })); } _onMapScroll(event: *): ?boolean { @@ -2863,7 +3314,12 @@ class Map extends Camera { * const isLoaded = map.loaded(); */ loaded(): boolean { - return !this._styleDirty && !this._sourcesDirty && !!this.style && this.style.loaded(); + return ( + !this._styleDirty && + !this._sourcesDirty && + !!this.style && + this.style.loaded() + ); } /** @@ -2930,12 +3386,15 @@ class Map extends Camera { let gpuTimer; const extTimerQuery = this.painter.context.extTimerQuery; const frameStartTime = browser.now(); - if (this.listens('gpu-timing-frame')) { + if (this.listens("gpu-timing-frame")) { gpuTimer = extTimerQuery.createQueryEXT(); - extTimerQuery.beginQueryEXT(extTimerQuery.TIME_ELAPSED_EXT, gpuTimer); + extTimerQuery.beginQueryEXT( + extTimerQuery.TIME_ELAPSED_EXT, + gpuTimer + ); } - const m = PerformanceUtils.beginMeasure('render'); + const m = PerformanceUtils.beginMeasure("render"); // A custom layer may have used the context asynchronously. Mark the state as dirty. this.painter.context.setDirty(); @@ -2947,12 +3406,12 @@ class Map extends Camera { if (this._removed) return; // In globe view, change to/from Mercator when zoom threshold is crossed. - if (this.getProjection().name === 'globe') { + if (this.getProjection().name === "globe") { if (this.transform.zoom >= GLOBE_ZOOM_THRESHOLD_MAX) { - if (this.transform.projection.name === 'globe') { + if (this.transform.projection.name === "globe") { this._updateProjection(); } - } else if (this.transform.projection.name === 'mercator') { + } else if (this.transform.projection.name === "mercator") { this._updateProjection(); } } @@ -2976,7 +3435,7 @@ class Map extends Camera { fadeDuration, pitch, zoomHistory: this.style.zoomHistory, - transition: this.style.getTransition() + transition: this.style.getTransition(), }); const factor = parameters.crossFadingFactor(); @@ -2988,7 +3447,8 @@ class Map extends Camera { this.style.update(parameters); } - const fogIsTransitioning = this.style && this.style.fog && this.style.fog.hasTransition(); + const fogIsTransitioning = + this.style && this.style.fog && this.style.fog.hasTransition(); if (fogIsTransitioning) { this.style._markersNeedUpdate = true; @@ -3003,15 +3463,26 @@ class Map extends Camera { this._sourcesDirty = false; this.painter._updateFog(this.style); this._updateTerrain(); // Terrain DEM source updates here and skips update in style._updateSources. - averageElevationChanged = this._updateAverageElevation(frameStartTime); + averageElevationChanged = this._updateAverageElevation( + frameStartTime + ); this.style._updateSources(this.transform); // Update positions of markers on enabling/disabling terrain this._forceMarkerUpdate(); } else { - averageElevationChanged = this._updateAverageElevation(frameStartTime); + averageElevationChanged = this._updateAverageElevation( + frameStartTime + ); } - this._placementDirty = this.style && this.style._updatePlacement(this.painter.transform, this.showCollisionBoxes, fadeDuration, this._crossSourceCollisions); + this._placementDirty = + this.style && + this.style._updatePlacement( + this.painter.transform, + this.showCollisionBoxes, + fadeDuration, + this._crossSourceCollisions + ); // Actually draw if (this.style) { @@ -3026,18 +3497,20 @@ class Map extends Camera { fadeDuration, isInitialLoad: this._isInitialLoad, showPadding: this.showPadding, - gpuTiming: !!this.listens('gpu-timing-layer'), - gpuTimingDeferredRender: !!this.listens('gpu-timing-deferred-render'), + gpuTiming: !!this.listens("gpu-timing-layer"), + gpuTimingDeferredRender: !!this.listens( + "gpu-timing-deferred-render" + ), speedIndexTiming: this.speedIndexTiming, }); } - this.fire(new Event('render')); + this.fire(new Event("render")); if (this.loaded() && !this._loaded) { this._loaded = true; PerformanceUtils.mark(PerformanceMarkers.load); - this.fire(new Event('load')); + this.fire(new Event("load")); } if (this.style && (this.style.hasTransitions() || crossFading)) { @@ -3051,41 +3524,54 @@ class Map extends Camera { this.style._releaseSymbolFadeTiles(); } - if (this.listens('gpu-timing-frame')) { + if (this.listens("gpu-timing-frame")) { const renderCPUTime = browser.now() - frameStartTime; extTimerQuery.endQueryEXT(extTimerQuery.TIME_ELAPSED_EXT, gpuTimer); setTimeout(() => { - const renderGPUTime = extTimerQuery.getQueryObjectEXT(gpuTimer, extTimerQuery.QUERY_RESULT_EXT) / (1000 * 1000); + const renderGPUTime = + extTimerQuery.getQueryObjectEXT( + gpuTimer, + extTimerQuery.QUERY_RESULT_EXT + ) / + (1000 * 1000); extTimerQuery.deleteQueryEXT(gpuTimer); - this.fire(new Event('gpu-timing-frame', { - cpuTime: renderCPUTime, - gpuTime: renderGPUTime - })); + this.fire( + new Event("gpu-timing-frame", { + cpuTime: renderCPUTime, + gpuTime: renderGPUTime, + }) + ); }, 50); // Wait 50ms to give time for all GPU calls to finish before querying } PerformanceUtils.endMeasure(m); - if (this.listens('gpu-timing-layer')) { + if (this.listens("gpu-timing-layer")) { // Resetting the Painter's per-layer timing queries here allows us to isolate // the queries to individual frames. const frameLayerQueries = this.painter.collectGpuTimers(); setTimeout(() => { - const renderedLayerTimes = this.painter.queryGpuTimers(frameLayerQueries); - - this.fire(new Event('gpu-timing-layer', { - layerTimes: renderedLayerTimes - })); + const renderedLayerTimes = this.painter.queryGpuTimers( + frameLayerQueries + ); + + this.fire( + new Event("gpu-timing-layer", { + layerTimes: renderedLayerTimes, + }) + ); }, 50); // Wait 50ms to give time for all GPU calls to finish before querying } - if (this.listens('gpu-timing-deferred-render')) { + if (this.listens("gpu-timing-deferred-render")) { const deferredRenderQueries = this.painter.collectDeferredRenderGpuQueries(); setTimeout(() => { - const gpuTime = this.painter.queryGpuTimeDeferredRender(deferredRenderQueries); - this.fire(new Event('gpu-timing-deferred-render', {gpuTime})); + const gpuTime = this.painter.queryGpuTimeDeferredRender( + deferredRenderQueries + ); + this.fire(new Event("gpu-timing-deferred-render", { gpuTime })); }, 50); // Wait 50ms to give time for all GPU calls to finish before querying } @@ -3094,7 +3580,11 @@ class Map extends Camera { // Even though `_styleDirty` and `_sourcesDirty` are reset in this // method, synchronous events fired during Style#update or // Style#_updateSources could have caused them to be set again. - const somethingDirty = this._sourcesDirty || this._styleDirty || this._placementDirty || averageElevationChanged; + const somethingDirty = + this._sourcesDirty || + this._styleDirty || + this._placementDirty || + averageElevationChanged; if (somethingDirty || this._repaint) { this.triggerRepaint(); } else { @@ -3102,7 +3592,10 @@ class Map extends Camera { if (willIdle) { // Before idling, we perform one last sample so that if the average elevation // does not exactly match the terrain, we skip idle and ease it to its final state. - averageElevationChanged = this._updateAverageElevation(frameStartTime, true); + averageElevationChanged = this._updateAverageElevation( + frameStartTime, + true + ); } if (averageElevationChanged) { @@ -3110,12 +3603,16 @@ class Map extends Camera { } else { this._triggerFrame(false); if (willIdle) { - this.fire(new Event('idle')); + this.fire(new Event("idle")); this._isInitialLoad = false; // check the options to see if need to calculate the speed index if (this.speedIndexTiming) { const speedIndexNumber = this._calculateSpeedIndex(); - this.fire(new Event('speedindexcompleted', {speedIndex: speedIndexNumber})); + this.fire( + new Event("speedindexcompleted", { + speedIndex: speedIndexNumber, + }) + ); this.speedIndexTiming = false; } } @@ -3142,8 +3639,11 @@ class Map extends Camera { * @returns {boolean} true if elevation has changed from the last sampling * @private */ - _updateAverageElevation(timeStamp: number, ignoreTimeout: boolean = false): boolean { - const applyUpdate = value => { + _updateAverageElevation( + timeStamp: number, + ignoreTimeout: boolean = false + ): boolean { + const applyUpdate = (value) => { this.transform.averageElevation = value; this._update(false); return true; @@ -3154,14 +3654,19 @@ class Map extends Camera { return false; } - const timeoutElapsed = ignoreTimeout || timeStamp - this._averageElevationLastSampledAt > AVERAGE_ELEVATION_SAMPLING_INTERVAL; + const timeoutElapsed = + ignoreTimeout || + timeStamp - this._averageElevationLastSampledAt > + AVERAGE_ELEVATION_SAMPLING_INTERVAL; if (timeoutElapsed && !this._averageElevation.isEasing(timeStamp)) { const currentElevation = this.transform.averageElevation; let newElevation = this.transform.sampleAverageElevation(); let exaggerationChanged = false; if (this.transform.elevation) { - exaggerationChanged = this.transform.elevation.exaggeration() !== this._averageElevationExaggeration; + exaggerationChanged = + this.transform.elevation.exaggeration() !== + this._averageElevationExaggeration; // $FlowIgnore[incompatible-use] this._averageElevationExaggeration = this.transform.elevation.exaggeration(); } @@ -3180,7 +3685,11 @@ class Map extends Camera { this._averageElevation.jumpTo(newElevation); return applyUpdate(newElevation); } else { - this._averageElevation.easeTo(newElevation, timeStamp, AVERAGE_ELEVATION_EASE_TIME); + this._averageElevation.easeTo( + newElevation, + timeStamp, + AVERAGE_ELEVATION_EASE_TIME + ); } } else if (elevationChange > AVERAGE_ELEVATION_CHANGE_THRESHOLD) { this._averageElevation.jumpTo(newElevation); @@ -3196,37 +3705,61 @@ class Map extends Camera { } /***** START WARNING - REMOVAL OR MODIFICATION OF THE - * FOLLOWING CODE VIOLATES THE MAPBOX TERMS OF SERVICE ****** - * The following code is used to access Mapbox's APIs. Removal or modification - * of this code can result in higher fees and/or - * termination of your account with Mapbox. - * - * Under the Mapbox Terms of Service, you may not use this code to access Mapbox - * Mapping APIs other than through Mapbox SDKs. - * - * The Mapping APIs documentation is available at https://docs.mapbox.com/api/maps/#maps - * and the Mapbox Terms of Service are available at https://www.mapbox.com/tos/ - ******************************************************************************/ + * FOLLOWING CODE VIOLATES THE MAPBOX TERMS OF SERVICE ****** + * The following code is used to access Mapbox's APIs. Removal or modification + * of this code can result in higher fees and/or + * termination of your account with Mapbox. + * + * Under the Mapbox Terms of Service, you may not use this code to access Mapbox + * Mapping APIs other than through Mapbox SDKs. + * + * The Mapping APIs documentation is available at https://docs.mapbox.com/api/maps/#maps + * and the Mapbox Terms of Service are available at https://www.mapbox.com/tos/ + ******************************************************************************/ _authenticate() { - getMapSessionAPI(this._getMapId(), this._requestManager._skuToken, this._requestManager._customAccessToken, (err) => { - if (err) { - // throwing an error here will cause the callback to be called again unnecessarily - if (err.message === AUTH_ERR_MSG || (err: any).status === 401) { - const gl = this.painter.context.gl; - storeAuthState(gl, false); - if (this._logoControl instanceof LogoControl) { - this._logoControl._updateLogo(); - } - if (gl) gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); - - if (!this._silenceAuthErrors) { - this.fire(new ErrorEvent(new Error('A valid Mapbox access token is required to use Mapbox GL JS. To create an account or a new access token, visit https://account.mapbox.com/'))); + getMapSessionAPI( + this._getMapId(), + this._requestManager._skuToken, + this._requestManager._customAccessToken, + (err) => { + if (err) { + // throwing an error here will cause the callback to be called again unnecessarily + if ( + err.message === AUTH_ERR_MSG || + (err: any).status === 401 + ) { + const gl = this.painter.context.gl; + storeAuthState(gl, false); + if (this._logoControl instanceof LogoControl) { + this._logoControl._updateLogo(); + } + if (gl) + gl.clear( + gl.DEPTH_BUFFER_BIT | + gl.COLOR_BUFFER_BIT | + gl.STENCIL_BUFFER_BIT + ); + + if (!this._silenceAuthErrors) { + this.fire( + new ErrorEvent( + new Error( + "A valid Mapbox access token is required to use Mapbox GL JS. To create an account or a new access token, visit https://account.mapbox.com/" + ) + ) + ); + } } } } - }); - postMapLoadEvent(this._getMapId(), this._requestManager._skuToken, this._requestManager._customAccessToken, () => {}); + ); + postMapLoadEvent( + this._getMapId(), + this._requestManager._skuToken, + this._requestManager._customAccessToken, + () => {} + ); } /***** END WARNING - REMOVAL OR MODIFICATION OF THE @@ -3235,7 +3768,10 @@ class Map extends Camera { _updateTerrain() { // Recalculate if enabled/disabled and calculate elevation cover. As camera is using elevation tiles before // render (and deferred update after zoom recalculation), this needs to be called when removing terrain source. - this.painter.updateTerrain(this.style, this.isMoving() || this.isRotating() || this.isZooming()); + this.painter.updateTerrain( + this.style, + this.isMoving() || this.isRotating() || this.isZooming() + ); } _calculateSpeedIndex(): number { @@ -3248,16 +3784,40 @@ class Map extends Camera { gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); function read(texture) { - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); - const pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4); - gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + gl.framebufferTexture2D( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, + texture, + 0 + ); + const pixels = new Uint8Array( + gl.drawingBufferWidth * gl.drawingBufferHeight * 4 + ); + gl.readPixels( + 0, + 0, + gl.drawingBufferWidth, + gl.drawingBufferHeight, + gl.RGBA, + gl.UNSIGNED_BYTE, + pixels + ); return pixels; } - return this._canvasPixelComparison(read(finalFrame), canvasCopyInstances.canvasCopies.map(read), canvasCopyInstances.timeStamps); + return this._canvasPixelComparison( + read(finalFrame), + canvasCopyInstances.canvasCopies.map(read), + canvasCopyInstances.timeStamps + ); } - _canvasPixelComparison(finalFrame: Uint8Array, allFrames: Uint8Array[], timeStamps: number[]): number { + _canvasPixelComparison( + finalFrame: Uint8Array, + allFrames: Uint8Array[], + timeStamps: number[] + ): number { let finalScore = timeStamps[1] - timeStamps[0]; const numPixels = finalFrame.length / 4; @@ -3265,17 +3825,19 @@ class Map extends Camera { const frame = allFrames[i]; let cnt = 0; for (let j = 0; j < frame.length; j += 4) { - if (frame[j] === finalFrame[j] && + if ( + frame[j] === finalFrame[j] && frame[j + 1] === finalFrame[j + 1] && frame[j + 2] === finalFrame[j + 2] && - frame[j + 3] === finalFrame[j + 3]) { + frame[j + 3] === finalFrame[j + 3] + ) { cnt = cnt + 1; } } //calculate the % visual completeness const interval = timeStamps[i + 2] - timeStamps[i + 1]; const visualCompletness = cnt / numPixels; - finalScore += interval * (1 - visualCompletness); + finalScore += interval * (1 - visualCompletness); } return finalScore; } @@ -3312,24 +3874,34 @@ class Map extends Camera { this.handlers = undefined; this.setStyle(null); - if (typeof window !== 'undefined') { - window.removeEventListener('resize', this._onWindowResize, false); - window.removeEventListener('orientationchange', this._onWindowResize, false); - window.removeEventListener('webkitfullscreenchange', this._onWindowResize, false); - window.removeEventListener('online', this._onWindowOnline, false); + if (typeof window !== "undefined") { + window.removeEventListener("resize", this._onWindowResize, false); + window.removeEventListener( + "orientationchange", + this._onWindowResize, + false + ); + window.removeEventListener( + "webkitfullscreenchange", + this._onWindowResize, + false + ); + window.removeEventListener("online", this._onWindowOnline, false); } - const extension = this.painter.context.gl.getExtension('WEBGL_lose_context'); + const extension = this.painter.context.gl.getExtension( + "WEBGL_lose_context" + ); if (extension) extension.loseContext(); removeNode(this._canvasContainer); removeNode(this._controlContainer); removeNode(this._missingCSSCanary); - this._container.classList.remove('mapboxgl-map'); + this._container.classList.remove("mapboxgl-map"); PerformanceUtils.clearMetrics(); removeAuthState(this.painter.context.gl); this._removed = true; - this.fire(new Event('remove')); + this.fire(new Event("remove")); } /** @@ -3369,10 +3941,16 @@ class Map extends Camera { * @returns {Object} Returns `this` | Promise. */ _preloadTiles(transform: Transform | Array): this { - const sources: Array = this.style ? (Object.values(this.style._sourceCaches): any) : []; - asyncAll(sources, (source, done) => source._preloadTiles(transform, done), () => { - this.triggerRepaint(); - }); + const sources: Array = this.style + ? (Object.values(this.style._sourceCaches): any) + : []; + asyncAll( + sources, + (source, done) => source._preloadTiles(transform, done), + () => { + this.triggerRepaint(); + } + ); return this; } @@ -3383,7 +3961,7 @@ class Map extends Camera { _onWindowResize(event: Event) { if (this._trackResize) { - this.resize({originalEvent: event})._update(); + this.resize({ originalEvent: event })._update(); } } @@ -3404,7 +3982,9 @@ class Map extends Camera { * @example * map.showTileBoundaries = true; */ - get showTileBoundaries(): boolean { return !!this._showTileBoundaries; } + get showTileBoundaries(): boolean { + return !!this._showTileBoundaries; + } set showTileBoundaries(value: boolean) { if (this._showTileBoundaries === value) return; this._showTileBoundaries = value; @@ -3424,7 +4004,9 @@ class Map extends Camera { * @example * map.showTerrainWireframe = true; */ - get showTerrainWireframe(): boolean { return !!this._showTerrainWireframe; } + get showTerrainWireframe(): boolean { + return !!this._showTerrainWireframe; + } set showTerrainWireframe(value: boolean) { if (this._showTerrainWireframe === value) return; this._showTerrainWireframe = value; @@ -3442,7 +4024,9 @@ class Map extends Camera { * @example * map.speedIndexTiming = true; */ - get speedIndexTiming(): boolean { return !!this._speedIndexTiming; } + get speedIndexTiming(): boolean { + return !!this._speedIndexTiming; + } set speedIndexTiming(value: boolean) { if (this._speedIndexTiming === value) return; this._speedIndexTiming = value; @@ -3458,7 +4042,9 @@ class Map extends Camera { * @instance * @memberof Map */ - get showPadding(): boolean { return !!this._showPadding; } + get showPadding(): boolean { + return !!this._showPadding; + } set showPadding(value: boolean) { if (this._showPadding === value) return; this._showPadding = value; @@ -3476,7 +4062,9 @@ class Map extends Camera { * @instance * @memberof Map */ - get showCollisionBoxes(): boolean { return !!this._showCollisionBoxes; } + get showCollisionBoxes(): boolean { + return !!this._showCollisionBoxes; + } set showCollisionBoxes(value: boolean) { if (this._showCollisionBoxes === value) return; this._showCollisionBoxes = value; @@ -3502,7 +4090,9 @@ class Map extends Camera { * @instance * @memberof Map */ - get showOverdrawInspector(): boolean { return !!this._showOverdrawInspector; } + get showOverdrawInspector(): boolean { + return !!this._showOverdrawInspector; + } set showOverdrawInspector(value: boolean) { if (this._showOverdrawInspector === value) return; this._showOverdrawInspector = value; @@ -3518,7 +4108,9 @@ class Map extends Camera { * @instance * @memberof Map */ - get repaint(): boolean { return !!this._repaint; } + get repaint(): boolean { + return !!this._repaint; + } set repaint(value: boolean) { if (this._repaint !== value) { this._repaint = value; @@ -3526,8 +4118,13 @@ class Map extends Camera { } } // show vertices - get vertices(): boolean { return !!this._vertices; } - set vertices(value: boolean) { this._vertices = value; this._update(); } + get vertices(): boolean { + return !!this._vertices; + } + set vertices(value: boolean) { + this._vertices = value; + this._update(); + } // for cache browser tests _setCacheLimits(limit: number, checkThreshold: number) { @@ -3543,7 +4140,9 @@ class Map extends Camera { * @var {string} version */ - get version(): string { return version; } + get version(): string { + return version; + } } export default Map;