vendor: 38,015 bytes, lines 1-1033
1/** 2* MapLibre GL JS 3* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v6.11.1/LICENSE.txt 4*/ 5import { An as addProtocol, Br as EXTENT, D as clipGeometry, Dt as RGBAImage, Et as AlphaImage, F as potpack, Fn as isAbortError, G as GeoJSONVT, H as rtlWorkerPlugin, Ir as warnOnce, Ln as JSON_PREFIX, Mn as removeProtocol, N as ImageAtlas, On as makeRequest, Tn as getJSON, Ui as Point, Vt as CollisionBoxArray, _ as createStyleLayer, bt as DEMData, c as GeoJSONWrapper, d as OverscaledTileID, dn as createExpression, dr as isImageBitmap, dt as VectorTile, en as EvaluationParameters, g as Actor, gr as isWorker, i as MLTVectorTile, l as fromVectorTileJs, ln as groupByLayout, lr as getImageData, nn as register, o as DictionaryCoder, r as FeatureIndex, rr as extend, t as BoundedLRUCache, tr as ensureError, un as featureFilter, vr as mapObject, wn as getArrayBuffer, z as PbfReader } from "./maplibre-gl-shared-dev.mjs"; 6//#region src/style/style_layer_index.ts 7var StyleLayerIndex = class { 8 constructor(layerConfigs, globalState) { 9 this.keyCache = {}; 10 if (layerConfigs) this.replace(layerConfigs, globalState); 11 } 12 replace(layerConfigs, globalState) { 13 this._layerConfigs = {}; 14 this._layers = {}; 15 this.update(layerConfigs, [], globalState); 16 } 17 update(layerConfigs, removedIds, globalState) { 18 for (const layerConfig of layerConfigs) { 19 this._layerConfigs[layerConfig.id] = layerConfig; 20 const layer = this._layers[layerConfig.id] = createStyleLayer(layerConfig, globalState); 21 layer._featureFilter = featureFilter(layer.filter, `layers[${layerConfig.id}].filter`, globalState); 22 if (this.keyCache[layerConfig.id]) delete this.keyCache[layerConfig.id]; 23 } 24 for (const id of removedIds) { 25 delete this.keyCache[id]; 26 delete this._layerConfigs[id]; 27 delete this._layers[id]; 28 } 29 this.familiesBySource = {}; 30 const groups = groupByLayout(Object.values(this._layerConfigs), this.keyCache); 31 for (const layerConfigs of groups) { 32 const layers = layerConfigs.map((layerConfig) => this._layers[layerConfig.id]); 33 const layer = layers[0]; 34 if (layer.isHidden()) continue; 35 const sourceId = layer.source || ""; 36 let sourceGroup = this.familiesBySource[sourceId]; 37 sourceGroup ||= this.familiesBySource[sourceId] = {}; 38 const sourceLayerId = layer.sourceLayer || "_geojsonTileLayer"; 39 let sourceLayerFamilies = sourceGroup[sourceLayerId]; 40 sourceLayerFamilies ||= sourceGroup[sourceLayerId] = []; 41 sourceLayerFamilies.push(layers); 42 } 43 } 44}; 45//#endregion 46//#region src/render/glyph_atlas.ts 47const padding = 1; 48var GlyphAtlas = class { 49 constructor(stacks) { 50 const positions = {}; 51 const bins = []; 52 for (const stack in stacks) { 53 const glyphs = stacks[stack]; 54 const stackPositions = positions[stack] = {}; 55 for (const id in glyphs) { 56 const src = glyphs[id]; 57 if (!src || src.bitmap.width === 0 || src.bitmap.height === 0) continue; 58 const bin = { 59 x: 0, 60 y: 0, 61 w: src.bitmap.width + 2, 62 h: src.bitmap.height + 2 63 }; 64 bins.push(bin); 65 stackPositions[id] = { 66 rect: bin, 67 metrics: src.metrics 68 }; 69 } 70 } 71 const { w, h } = potpack(bins); 72 const image = new AlphaImage({ 73 width: w || 1, 74 height: h || 1 75 }); 76 for (const stack in stacks) { 77 const glyphs = stacks[stack]; 78 for (const id in glyphs) { 79 const src = glyphs[id]; 80 if (!src || src.bitmap.width === 0 || src.bitmap.height === 0) continue; 81 const bin = positions[stack][id].rect; 82 AlphaImage.copy(src.bitmap, image, { 83 x: 0, 84 y: 0 85 }, { 86 x: bin.x + padding, 87 y: bin.y + padding 88 }, src.bitmap); 89 } 90 } 91 this.image = image; 92 this.positions = positions; 93 } 94}; 95register("GlyphAtlas", GlyphAtlas); 96//#endregion 97//#region src/source/worker_tile.ts 98var WorkerTile = class { 99 constructor(params) { 100 this.tileID = new OverscaledTileID(params.tileID.overscaledZ, params.tileID.wrap, params.tileID.canonical.z, params.tileID.canonical.x, params.tileID.canonical.y); 101 this.uid = params.uid; 102 this.zoom = params.zoom; 103 this.pixelRatio = params.pixelRatio; 104 this.tileSize = params.tileSize; 105 this.source = params.source; 106 this.overscaling = this.tileID.overscaleFactor(); 107 this.showCollisionBoxes = params.showCollisionBoxes; 108 this.collectResourceTiming = !!params.collectResourceTiming; 109 this.returnDependencies = !!params.returnDependencies; 110 this.promoteId = params.promoteId; 111 this.inFlightDependencies = []; 112 } 113 async parse(data, layerIndex, availableImages, actor, subdivisionGranularity) { 114 this.data = data; 115 this.collisionBoxArray = new CollisionBoxArray(); 116 const sourceLayerCoder = new DictionaryCoder(Object.keys(data.layers).sort()); 117 const featureIndex = new FeatureIndex(this.tileID, this.promoteId); 118 featureIndex.bucketLayerIDs = []; 119 const buckets = {}; 120 const options = { 121 featureIndex, 122 iconDependencies: {}, 123 patternDependencies: {}, 124 glyphDependencies: {}, 125 dashDependencies: {}, 126 availableImages, 127 subdivisionGranularity 128 }; 129 const layerFamilies = layerIndex.familiesBySource[this.source]; 130 for (const sourceLayerId in layerFamilies) { 131 const sourceLayer = data.layers[sourceLayerId]; 132 if (!sourceLayer) continue; 133 if (sourceLayer.version === 1) warnOnce(`Vector tile source "${this.source}" layer "${sourceLayerId}" does not use vector tile spec v2 and therefore may have some rendering errors.`); 134 const sourceLayerIndex = sourceLayerCoder.encode(sourceLayerId); 135 const features = []; 136 for (let index = 0; index < sourceLayer.length; index++) { 137 const feature = sourceLayer.feature(index); 138 const id = featureIndex.getId(feature, sourceLayerId); 139 features.push({ 140 feature, 141 id, 142 index, 143 sourceLayerIndex 144 }); 145 } 146 for (const family of layerFamilies[sourceLayerId]) { 147 const layer = family[0]; 148 if (layer.source !== this.source) warnOnce(`layer.source = ${layer.source} does not equal this.source = ${this.source}`); 149 if (layer.isHidden(this.zoom, true)) continue; 150 recalculateLayers(family, this.zoom, availableImages); 151 (buckets[layer.id] = layer.createBucket({ 152 index: featureIndex.bucketLayerIDs.length, 153 layers: family, 154 zoom: this.zoom, 155 pixelRatio: this.pixelRatio, 156 overscaling: this.overscaling, 157 collisionBoxArray: this.collisionBoxArray, 158 sourceLayerIndex, 159 sourceID: this.source 160 })).populate(features, options, this.tileID.canonical); 161 featureIndex.bucketLayerIDs.push(family.map((l) => l.id)); 162 } 163 } 164 const stacks = mapObject(options.glyphDependencies, (glyphs) => Object.keys(glyphs)); 165 for (const request of this.inFlightDependencies) request?.abort(); 166 this.inFlightDependencies = []; 167 let getGlyphsPromise = Promise.resolve({}); 168 if (Object.keys(stacks).length) { 169 const abortController = new AbortController(); 170 this.inFlightDependencies.push(abortController); 171 getGlyphsPromise = actor.sendAsync({ 172 type: "GG", 173 data: { 174 stacks, 175 source: this.source, 176 tileID: this.tileID, 177 type: "glyphs" 178 } 179 }, abortController); 180 } 181 const icons = Object.keys(options.iconDependencies); 182 let getIconsPromise = Promise.resolve({}); 183 if (icons.length) { 184 const abortController = new AbortController(); 185 this.inFlightDependencies.push(abortController); 186 getIconsPromise = actor.sendAsync({ 187 type: "GI", 188 data: { 189 icons, 190 source: this.source, 191 tileID: this.tileID, 192 type: "icons" 193 } 194 }, abortController); 195 } 196 const patterns = Object.keys(options.patternDependencies); 197 let getPatternsPromise = Promise.resolve({}); 198 if (patterns.length) { 199 const abortController = new AbortController(); 200 this.inFlightDependencies.push(abortController); 201 getPatternsPromise = actor.sendAsync({ 202 type: "GI", 203 data: { 204 icons: patterns, 205 source: this.source, 206 tileID: this.tileID, 207 type: "patterns" 208 } 209 }, abortController); 210 } 211 const dashes = options.dashDependencies; 212 let getDashesPromise = Promise.resolve({}); 213 if (Object.keys(dashes).length) { 214 const abortController = new AbortController(); 215 this.inFlightDependencies.push(abortController); 216 getDashesPromise = actor.sendAsync({ 217 type: "GDA", 218 data: { dashes } 219 }, abortController); 220 } 221 const [glyphMap, iconMap, patternMap, dashPositions] = await Promise.all([ 222 getGlyphsPromise, 223 getIconsPromise, 224 getPatternsPromise, 225 getDashesPromise 226 ]); 227 const glyphAtlas = new GlyphAtlas(glyphMap); 228 const imageAtlas = new ImageAtlas(iconMap, patternMap); 229 for (const key in buckets) { 230 const bucket = buckets[key]; 231 if (!bucket.hasDependencies) continue; 232 recalculateLayers(bucket.layers, this.zoom, availableImages); 233 bucket.addFeatures({ 234 options, 235 canonical: this.tileID.canonical, 236 glyphMap, 237 glyphPositions: glyphAtlas.positions, 238 iconMap, 239 iconPositions: imageAtlas.iconPositions, 240 patternMap, 241 patternPositions: imageAtlas.patternPositions, 242 dashPositions, 243 showCollisionBoxes: this.showCollisionBoxes 244 }); 245 } 246 return { 247 buckets: Object.values(buckets).filter((b) => !b.isEmpty()), 248 featureIndex, 249 collisionBoxArray: this.collisionBoxArray, 250 glyphAtlasImage: glyphAtlas.image, 251 imageAtlas, 252 dashPositions, 253 glyphMap: this.returnDependencies ? glyphMap : null, 254 iconMap: this.returnDependencies ? iconMap : null, 255 glyphPositions: this.returnDependencies ? glyphAtlas.positions : null 256 }; 257 } 258}; 259function recalculateLayers(layers, zoom, availableImages) { 260 const parameters = new EvaluationParameters(zoom); 261 for (const layer of layers) layer.recalculate(parameters, availableImages); 262} 263//#endregion 264//#region src/source/worker_tile_state.ts 265var WorkerTileState = class { 266 constructor() { 267 this.loading = {}; 268 this.loaded = {}; 269 this.parsing = {}; 270 } 271 startLoading(uid, tile) { 272 this.loading[uid] = tile; 273 } 274 finishLoading(uid) { 275 delete this.loading[uid]; 276 } 277 abort(uid) { 278 const tile = this.loading[uid]; 279 if (!tile?.abort) return; 280 tile.abort.abort(); 281 delete this.loading[uid]; 282 } 283 getParsing(uid) { 284 return this.parsing[uid]; 285 } 286 setParsing(uid, state) { 287 this.parsing[uid] = state; 288 } 289 removeParsing(uid) { 290 delete this.parsing[uid]; 291 } 292 markLoaded(uid, tile) { 293 this.loaded[uid] = tile; 294 } 295 getLoaded(uid) { 296 const tile = this.loaded[uid]; 297 if (!tile) return void 0; 298 return tile; 299 } 300 removeLoaded(uid) { 301 delete this.loaded[uid]; 302 } 303 clearLoaded() { 304 this.loaded = {}; 305 } 306}; 307//#endregion 308//#region src/util/request_performance.ts 309/** 310* @internal 311* Safe wrapper for the performance resource timing API in web workers with graceful degradation 312*/ 313var RequestPerformance = class { 314 constructor(url) { 315 this.start = `${url}#start`; 316 this.end = `${url}#end`; 317 this.measure = url; 318 performance.mark(this.start); 319 } 320 finish() { 321 performance.mark(this.end); 322 let resourceTimingData = performance.getEntriesByName(this.measure); 323 if (resourceTimingData.length === 0) { 324 performance.measure(this.measure, this.start, this.end); 325 resourceTimingData = performance.getEntriesByName(this.measure); 326 performance.clearMarks(this.start); 327 performance.clearMarks(this.end); 328 performance.clearMeasures(this.measure); 329 } 330 return resourceTimingData; 331 } 332}; 333//#endregion 334//#region src/source/vector_tile_overzoomed.ts 335var VectorTileFeatureOverzoomed = class { 336 constructor(type, geometry, properties, id, extent) { 337 this.type = type; 338 this.properties = properties ? properties : {}; 339 this.extent = extent; 340 this.pointsArray = geometry; 341 this.id = id; 342 } 343 loadGeometry() { 344 return this.pointsArray.map((ring) => ring.map((point) => new Point(point.x, point.y))); 345 } 346}; 347var VectorTileLayerOverzoomed = class { 348 constructor(features, layerName, extent) { 349 this.version = 2; 350 this._myFeatures = features; 351 this.name = layerName; 352 this.length = features.length; 353 this.extent = extent; 354 } 355 feature(i) { 356 return this._myFeatures[i]; 357 } 358}; 359var VectorTileOverzoomed = class { 360 constructor() { 361 this.layers = {}; 362 } 363 addLayer(layer) { 364 this.layers[layer.name] = layer; 365 } 366}; 367/** 368* This function slices a source tile layer into an overzoomed tile layer for a target tile ID. 369* @param sourceLayer - the source tile layer to slice 370* @param maxZoomTileID - the maximum zoom tile ID 371* @param targetTileID - the target tile ID 372* @returns - the overzoomed tile layer 373*/ 374function sliceVectorTileLayer(sourceLayer, maxZoomTileID, targetTileID) { 375 const { extent } = sourceLayer; 376 const dz = targetTileID.z - maxZoomTileID.z; 377 const scale = Math.pow(2, dz); 378 const offsetX = (targetTileID.x - maxZoomTileID.x * scale) * extent; 379 const offsetY = (targetTileID.y - maxZoomTileID.y * scale) * extent; 380 const featureWrappers = []; 381 for (let index = 0; index < sourceLayer.length; index++) { 382 const feature = sourceLayer.feature(index); 383 let geometry = feature.loadGeometry(); 384 for (const ring of geometry) for (const point of ring) { 385 point.x = point.x * scale - offsetX; 386 point.y = point.y * scale - offsetY; 387 } 388 const buffer = 128; 389 geometry = clipGeometry(geometry, feature.type, -128, -128, extent + buffer, extent + buffer); 390 if (geometry.length === 0) continue; 391 featureWrappers.push(new VectorTileFeatureOverzoomed(feature.type, geometry, feature.properties, feature.id, extent)); 392 } 393 return new VectorTileLayerOverzoomed(featureWrappers, sourceLayer.name, extent); 394} 395//#endregion 396//#region src/source/vector_tile_worker_source.ts 397/** 398* The {@link WorkerSource} implementation that supports {@link VectorTileSource}. This class is 399* used by vector tile sources to perform tile processing operations in a separate worker thread. 400*/ 401var VectorTileWorkerSource = class { 402 constructor(actor, layerIndex, availableImages) { 403 this.actor = actor; 404 this.layerIndex = layerIndex; 405 this.availableImages = availableImages; 406 this.tileState = new WorkerTileState(); 407 this.overzoomedTileResultCache = new BoundedLRUCache(1e3); 408 } 409 /** 410 * Loads a vector tile 411 */ 412 loadVectorTile(params, rawData) { 413 try { 414 return { 415 vectorTile: params.encoding !== "mlt" ? new VectorTile(new PbfReader(rawData)) : new MLTVectorTile(rawData), 416 rawData 417 }; 418 } catch (ex) { 419 const bytes = new Uint8Array(rawData); 420 const isGzipped = bytes[0] === 31 && bytes[1] === 139; 421 let errorMessage = `Unable to parse the tile at ${params.request.url}, `; 422 if (isGzipped) errorMessage += "please make sure the data is not gzipped and that you have configured the relevant header in the server"; 423 else errorMessage += `got error: ${ensureError(ex).message}`; 424 throw new Error(errorMessage); 425 } 426 } 427 /** 428 * Implements {@link WorkerSource.loadTile}. 429 */ 430 async loadTile(params) { 431 const { uid, overzoomParameters } = params; 432 if (overzoomParameters) params.request = overzoomParameters.overzoomRequest; 433 const timing = this._startRequestTiming(params); 434 const workerTile = new WorkerTile(params); 435 this.tileState.startLoading(uid, workerTile); 436 const abortController = new AbortController(); 437 workerTile.abort = abortController; 438 try { 439 const tileResponse = await getArrayBuffer(params.request, abortController); 440 if (params.etag && params.etag === tileResponse.etag) { 441 this.tileState.finishLoading(uid); 442 return this._getEtagUnmodifiedResult(tileResponse, timing); 443 } 444 const tileResult = this.loadVectorTile(params, tileResponse.data); 445 this.tileState.finishLoading(uid); 446 if (!tileResult) return null; 447 let { vectorTile, rawData } = tileResult; 448 if (overzoomParameters) ({vectorTile, rawData} = this._getOverzoomTile(params, vectorTile)); 449 const cacheControl = this._getExpiryData(tileResponse); 450 const resourceTiming = this._finishRequestTiming(timing); 451 workerTile.vectorTile = vectorTile; 452 workerTile.etag = tileResponse.etag; 453 this.tileState.markLoaded(uid, workerTile); 454 const parsingState = { 455 rawData, 456 cacheControl, 457 resourceTiming 458 }; 459 this.tileState.setParsing(uid, parsingState); 460 return await this._parseWorkerTile(workerTile, params); 461 } catch (err) { 462 this.tileState.finishLoading(uid); 463 this.tileState.markLoaded(uid, workerTile); 464 throw err; 465 } 466 } 467 _getEtagUnmodifiedResult(response, timing) { 468 const cacheControl = this._getExpiryData(response); 469 const resourceTiming = this._finishRequestTiming(timing); 470 return extend({ etagUnmodified: true }, cacheControl, resourceTiming); 471 } 472 async _parseWorkerTile(workerTile, params) { 473 const parseState = this.tileState.getParsing(workerTile.uid); 474 let result = await workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, params.subdivisionGranularity); 475 if (parseState) { 476 const { rawData, cacheControl, resourceTiming } = parseState; 477 const encoding = params.overzoomParameters ? "mvt" : params.encoding; 478 result = extend({ 479 rawTileData: rawData.slice(0), 480 encoding 481 }, result, cacheControl, resourceTiming); 482 this.tileState.removeParsing(workerTile.uid); 483 } else if (workerTile.etag) result = extend(result, { etag: workerTile.etag }); 484 return result; 485 } 486 _getExpiryData({ expires, cacheControl, etag }) { 487 const data = {}; 488 if (expires) data.expires = expires; 489 if (cacheControl) data.cacheControl = cacheControl; 490 if (etag) data.etag = etag; 491 return data; 492 } 493 _startRequestTiming(params) { 494 if (!params.request?.collectResourceTiming) return; 495 return new RequestPerformance(params.request.url); 496 } 497 _finishRequestTiming(timing) { 498 const timingData = timing?.finish(); 499 if (!timingData) return {}; 500 return { resourceTiming: JSON.parse(JSON.stringify(timingData)) }; 501 } 502 /** 503 * If we are seeking a tile deeper than the source's max available canonical tile, get the overzoomed tile 504 * @param params - the worker tile parameters 505 * @param maxZoomVectorTile - the original vector tile at the source's max available canonical zoom 506 * @returns the overzoomed tile and its raw data 507 */ 508 _getOverzoomTile(params, maxZoomVectorTile) { 509 const { tileID, source, overzoomParameters } = params; 510 const { maxZoomTileID } = overzoomParameters; 511 const cacheKey = `${maxZoomTileID.key}_${tileID.key}_${params.request?.url}`; 512 const cachedOverzoomTile = this.overzoomedTileResultCache.get(cacheKey); 513 if (cachedOverzoomTile) return cachedOverzoomTile; 514 const overzoomedVectorTile = new VectorTileOverzoomed(); 515 const layerFamilies = this.layerIndex.familiesBySource[source]; 516 for (const sourceLayerId in layerFamilies) { 517 const sourceLayer = maxZoomVectorTile.layers[sourceLayerId]; 518 if (!sourceLayer) continue; 519 const slicedTileLayer = sliceVectorTileLayer(sourceLayer, maxZoomTileID, tileID.canonical); 520 if (slicedTileLayer.length > 0) overzoomedVectorTile.addLayer(slicedTileLayer); 521 } 522 const overzoomedVectorTileResult = { 523 vectorTile: overzoomedVectorTile, 524 rawData: fromVectorTileJs(overzoomedVectorTile).buffer 525 }; 526 this.overzoomedTileResultCache.set(cacheKey, overzoomedVectorTileResult); 527 return overzoomedVectorTileResult; 528 } 529 /** 530 * Implements {@link WorkerSource.reloadTile}. 531 */ 532 async reloadTile(params) { 533 const uid = params.uid; 534 const workerTile = this.tileState.getLoaded(uid); 535 if (!workerTile) throw new Error("Should not be trying to reload a tile that was never loaded or has been removed"); 536 if (!workerTile.vectorTile) return; 537 workerTile.showCollisionBoxes = params.showCollisionBoxes; 538 return await this._parseWorkerTile(workerTile, params); 539 } 540 /** 541 * Implements {@link WorkerSource.abortTile}. 542 */ 543 async abortTile(params) { 544 this.tileState.abort(params.uid); 545 } 546 /** 547 * Implements {@link WorkerSource.removeTile}. 548 */ 549 async removeTile(params) { 550 this.tileState.removeLoaded(params.uid); 551 } 552}; 553//#endregion 554//#region src/source/raster_dem_tile_worker_source.ts 555var RasterDEMTileWorkerSource = class { 556 constructor() { 557 this.loaded = {}; 558 } 559 async loadTile(params) { 560 const { uid, encoding, rawImageData, redFactor, greenFactor, blueFactor, baseShift } = params; 561 const width = rawImageData.width + 4; 562 const height = rawImageData.height + 4; 563 const imagePixels = isImageBitmap(rawImageData) ? new RGBAImage({ 564 width, 565 height 566 }, await getImageData(rawImageData, -2, -2, width, height)) : rawImageData; 567 const dem = new DEMData(uid, imagePixels, encoding, redFactor, greenFactor, blueFactor, baseShift); 568 this.loaded ||= {}; 569 this.loaded[uid] = dem; 570 return dem; 571 } 572 removeTile(params) { 573 const loaded = this.loaded, uid = params.uid; 574 if (loaded?.[uid]) delete loaded[uid]; 575 } 576}; 577//#endregion 578//#region src/source/geojson_worker_source.ts 579/** 580* The {@link WorkerSource} implementation that supports {@link GeoJSONSource}. 581* This class is designed to be easily reused to support custom source types 582* for data formats that can be parsed/converted into an in-memory GeoJSON 583* representation. To do so, create it with 584* `new GeoJSONWorkerSource(actor, layerIndex, customLoadGeoJSONFunction)`. 585* For a full example, see [mapbox-gl-topojson](https://github.com/developmentseed/mapbox-gl-topojson). 586*/ 587var GeoJSONWorkerSource = class { 588 constructor(actor, layerIndex, availableImages, createGeoJSONIndexFunc = createGeoJSONIndex) { 589 this.actor = actor; 590 this.layerIndex = layerIndex; 591 this.availableImages = availableImages; 592 this.tileState = new WorkerTileState(); 593 this._createGeoJSONIndex = createGeoJSONIndexFunc; 594 } 595 /** 596 * Retrieves and sends loaded vector tiles to the main thread. 597 */ 598 loadVectorTile(params) { 599 if (!this._geoJSONIndex) throw new Error("Unable to parse the data into a cluster or geojson"); 600 const { z, x, y } = params.tileID.canonical; 601 const geoJSONTile = this._geoJSONIndex.getTile(z, x, y); 602 if (!geoJSONTile) return null; 603 const geojsonWrapper = new GeoJSONWrapper(geoJSONTile.features, { 604 version: 2, 605 extent: EXTENT 606 }); 607 return { 608 vectorTile: geojsonWrapper, 609 rawData: fromVectorTileJs(geojsonWrapper, JSON_PREFIX).buffer 610 }; 611 } 612 /** 613 * Implements {@link WorkerSource.loadTile}. 614 */ 615 async loadTile(params) { 616 const { uid } = params; 617 const workerTile = new WorkerTile(params); 618 workerTile.abort = new AbortController(); 619 try { 620 const loadResult = this.loadVectorTile(params); 621 if (!loadResult) return null; 622 const { vectorTile, rawData } = loadResult; 623 workerTile.vectorTile = vectorTile; 624 this.tileState.markLoaded(uid, workerTile); 625 const parsingState = { rawData }; 626 this.tileState.setParsing(uid, parsingState); 627 return await this._parseWorkerTile(workerTile, params); 628 } catch (err) { 629 this.tileState.markLoaded(uid, workerTile); 630 throw err; 631 } 632 } 633 async _parseWorkerTile(workerTile, params) { 634 const parseState = this.tileState.getParsing(workerTile.uid); 635 let result = await workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, params.subdivisionGranularity); 636 if (parseState) { 637 const { rawData } = parseState; 638 result = extend({ 639 rawTileData: rawData.slice(0), 640 encoding: "mvt" 641 }, result); 642 this.tileState.removeParsing(workerTile.uid); 643 } 644 return result; 645 } 646 /** 647 * Implements {@link WorkerSource.abortTile}. 648 */ 649 async abortTile(params) { 650 this.tileState.abort(params.uid); 651 } 652 /** 653 * Implements {@link WorkerSource.removeTile}. 654 */ 655 async removeTile(params) { 656 this.tileState.removeLoaded(params.uid); 657 } 658 /** 659 * Fetches (if appropriate), parses and indexes geojson data into tiles. This 660 * preparatory method must be called before {@link GeoJSONWorkerSource.loadTile} 661 * can correctly serve up tiles. The first call to this method must contain a valid 662 * {@link params.data}, {@link params.request} or {@link params.dataDiff}. Subsequent 663 * calls may omit these parameters to reprocess the existing data (such as to update 664 * clustering options). 665 * 666 * Defers to {@link GeoJSONWorkerSource.loadAndProcessGeoJSON} for the pre-processing. 667 * 668 * When a `loadData` request comes in while a previous one is being processed, 669 * the previous one is aborted. 670 * 671 * @param params - the parameters 672 * @returns a promise that resolves when the data is loaded and parsed into a GeoJSON object 673 */ 674 async loadData(params) { 675 this._pendingRequest?.abort(); 676 const timing = this._startRequestTiming(params); 677 this._pendingRequest = new AbortController(); 678 try { 679 await this.loadAndProcessGeoJSON(params, this._pendingRequest); 680 delete this._pendingRequest; 681 this.tileState.clearLoaded(); 682 const result = {}; 683 if (params.request) result.data = params.data; 684 this._finishRequestTiming(timing, params, result); 685 return result; 686 } catch (err) { 687 delete this._pendingRequest; 688 if (!isAbortError(err)) throw err; 689 return { abandoned: true }; 690 } 691 } 692 _startRequestTiming(params) { 693 if (!params.request?.collectResourceTiming) return; 694 return new RequestPerformance(params.request.url); 695 } 696 _finishRequestTiming(timing, params, result) { 697 const timingData = timing?.finish(); 698 if (!timingData) return; 699 result.resourceTiming = { [params.source]: JSON.parse(JSON.stringify(timingData)) }; 700 } 701 /** 702 * Implements {@link WorkerSource.reloadTile}. 703 * 704 * If the tile is loaded, reload by re-parsing the already available tile data. 705 * Otherwise, such as after a setData() call, we load the tile fresh. 706 * 707 * @param params - the parameters 708 * @returns A promise that resolves when the tile is reloaded 709 */ 710 async reloadTile(params) { 711 const uid = params.uid; 712 const workerTile = this.tileState.getLoaded(uid); 713 if (!workerTile) return await this.loadTile(params); 714 if (!workerTile.vectorTile) return; 715 workerTile.showCollisionBoxes = params.showCollisionBoxes; 716 return await this._parseWorkerTile(workerTile, params); 717 } 718 /** 719 * Fetch, parse and process GeoJSON according to the given parameters. 720 * Defers to {@link GeoJSONWorkerSource._loadGeoJSONFromString} for the fetching and parsing. 721 * 722 * @param params - the parameters 723 * @param abortController - the abort controller that allows aborting this operation 724 * @returns a promise that is resolved with the processes GeoJSON 725 */ 726 async loadAndProcessGeoJSON(params, abortController) { 727 if (params.request) params.data = (await getJSON(params.request, abortController)).data; 728 if (params.data) { 729 params.data = this._filterGeoJSON(params.data, params.filter, params.source); 730 this._geoJSONIndex = this._createGeoJSONIndex(params.data, params); 731 return; 732 } 733 if (params.dataDiff) { 734 this._geoJSONIndex ??= this._createGeoJSONIndex({ 735 type: "FeatureCollection", 736 features: [] 737 }, params); 738 this._geoJSONIndex.updateData(params.dataDiff, this._getFilterPredicate(params.filter, params.source)); 739 return; 740 } 741 if (params.updateCluster) this._geoJSONIndex.updateClusterOptions(params.geojsonVtOptions.cluster, getSuperclusterOptions(params)); 742 if (this._geoJSONIndex == null) throw new Error(`Input data given to '${params.source}' is not a valid GeoJSON object.`); 743 } 744 /** 745 * Applies a filter to a GeoJSON object. 746 */ 747 _filterGeoJSON(data, filter, source) { 748 if (data.type !== "FeatureCollection") return data; 749 const predicate = this._getFilterPredicate(filter, source); 750 if (!predicate) return data; 751 return { 752 type: "FeatureCollection", 753 features: data.features.filter((feature) => predicate(feature)) 754 }; 755 } 756 /** 757 * Gets a predicate function that can be used to filter GeoJSON features. 758 */ 759 _getFilterPredicate(filter, source) { 760 if (typeof filter !== "boolean" && !filter?.length) return void 0; 761 const compiled = createExpression(filter, `sources.${source}.filter`, { 762 type: "boolean", 763 "property-type": "data-driven", 764 overridable: false, 765 transition: false 766 }); 767 if (compiled.result === "error") throw new Error(compiled.value.map((err) => `${err.key}: ${err.message}`).join(", ")); 768 return (feature) => compiled.value.evaluate({ zoom: 0 }, feature); 769 } 770 async removeSource(_params) { 771 this._pendingRequest?.abort(); 772 } 773 getClusterExpansionZoom(params) { 774 return this._geoJSONIndex.getClusterExpansionZoom(params.clusterId); 775 } 776 getClusterChildren(params) { 777 return this._geoJSONIndex.getClusterChildren(params.clusterId); 778 } 779 getClusterLeaves(params) { 780 return this._geoJSONIndex.getClusterLeaves(params.clusterId, params.limit, params.offset); 781 } 782}; 783function createGeoJSONIndex(data, params) { 784 const options = extend(params.geojsonVtOptions || {}, { 785 updateable: true, 786 clusterOptions: getSuperclusterOptions(params) 787 }); 788 return new GeoJSONVT(data, options); 789} 790function getSuperclusterOptions({ geojsonVtOptions, clusterProperties, source }) { 791 if (!clusterProperties || !geojsonVtOptions.clusterOptions) return geojsonVtOptions.clusterOptions; 792 const mapExpressions = {}; 793 const reduceExpressions = {}; 794 const globals = { 795 accumulated: null, 796 zoom: 0 797 }; 798 const feature = { properties: null }; 799 const propertyNames = Object.keys(clusterProperties); 800 for (const key of propertyNames) { 801 const [operator, mapExpression] = clusterProperties[key]; 802 const mapExpressionParsed = createExpression(mapExpression, `sources.${source}.clusterProperties.${key}[1]`); 803 const reduceExpressionParsed = createExpression(typeof operator === "string" ? [ 804 operator, 805 ["accumulated"], 806 ["get", key] 807 ] : operator, `sources.${source}.clusterProperties.${key}[0]`); 808 mapExpressions[key] = mapExpressionParsed.value; 809 reduceExpressions[key] = reduceExpressionParsed.value; 810 } 811 geojsonVtOptions.clusterOptions.map = (pointProperties) => { 812 feature.properties = pointProperties; 813 const properties = {}; 814 for (const key of propertyNames) properties[key] = mapExpressions[key].evaluate(globals, feature); 815 return properties; 816 }; 817 geojsonVtOptions.clusterOptions.reduce = (accumulated, clusterProperties) => { 818 feature.properties = clusterProperties; 819 for (const key of propertyNames) { 820 globals.accumulated = accumulated[key]; 821 accumulated[key] = reduceExpressions[key].evaluate(globals, feature); 822 } 823 }; 824 return geojsonVtOptions.clusterOptions; 825} 826//#endregion 827//#region src/source/worker.ts 828/** 829* Loads an external script into worker (global) scope. The loader picks a 830* strategy based on what the script actually is: 831* 832* - `.mjs` URLs: dynamic `import()` directly, no fetch/sniff overhead. Worker 833* CSP needs `script-src` to permit the URL. 834* 835* - Other URLs: fetch the source and sniff for ESM syntax (top-level `import` 836* or `export`). If ESM is detected, run it through a blob-URL dynamic 837* `import()` so the browser parses it as a module; this requires 838* `script-src blob:` in the worker CSP. Otherwise treat it as UMD/IIFE and 839* run it via `globalThis.eval`, which requires `script-src 'unsafe-eval'`. 840*/ 841async function loadScript(url) { 842 if (url.endsWith(".mjs")) { 843 await import( 844 /* @vite-ignore */ 845 url 846); 847 return; 848 } 849 const response = await fetch(url, { credentials: "same-origin" }); 850 if (!response.ok) throw new Error(`Failed to load ${url}: ${response.status}`); 851 const code = await response.text(); 852 if (/^[ \t]*(import|export)\s/m.test(code)) { 853 const blobUrl = URL.createObjectURL(new Blob([code], { type: "text/javascript" })); 854 try { 855 await import( 856 /* @vite-ignore */ 857 blobUrl 858); 859 } finally { 860 URL.revokeObjectURL(blobUrl); 861 } 862 return; 863 } 864 globalThis.eval(code); 865} 866/** 867* The Worker class responsible for background thread related execution 868*/ 869var Worker = class { 870 constructor(self) { 871 this.self = self; 872 this.actor = new Actor(self); 873 this.layerIndexes = {}; 874 this.availableImages = {}; 875 this.workerSources = {}; 876 this.demWorkerSources = {}; 877 this.externalWorkerSourceTypes = {}; 878 this.globalStates = /* @__PURE__ */ new Map(); 879 this.self.registerWorkerSource = (name, WorkerSource) => { 880 if (this.externalWorkerSourceTypes[name]) throw new Error(`Worker source with name "${name}" already registered.`); 881 this.externalWorkerSourceTypes[name] = WorkerSource; 882 }; 883 this.self.addProtocol = addProtocol; 884 this.self.removeProtocol = removeProtocol; 885 /** 886 * Invoked by a right-to-left text plugin once it has fetched and parsed. 887 * 888 * @deprecated MapLibre shapes Arabic and reorders bidirectional text itself. A plugin 889 * registered here still replaces the built-in implementation, but this will be removed in a 890 * future release. 891 */ 892 this.self.registerRTLTextPlugin = (rtlTextPlugin) => { 893 rtlWorkerPlugin.setMethods(rtlTextPlugin); 894 }; 895 this.self.makeRequest = makeRequest; 896 this.actor.registerMessageHandler("LDT", (mapId, params) => { 897 return this._getDEMWorkerSource(mapId, params.source).loadTile(params); 898 }); 899 this.actor.registerMessageHandler("RDT", async (mapId, params) => { 900 this._getDEMWorkerSource(mapId, params.source).removeTile(params); 901 }); 902 this.actor.registerMessageHandler("GCEZ", async (mapId, params) => { 903 return this._getWorkerSource(mapId, params.type, params.source).getClusterExpansionZoom(params); 904 }); 905 this.actor.registerMessageHandler("GCC", async (mapId, params) => { 906 return this._getWorkerSource(mapId, params.type, params.source).getClusterChildren(params); 907 }); 908 this.actor.registerMessageHandler("GCL", async (mapId, params) => { 909 return this._getWorkerSource(mapId, params.type, params.source).getClusterLeaves(params); 910 }); 911 this.actor.registerMessageHandler("LD", (mapId, params) => { 912 return this._getWorkerSource(mapId, params.type, params.source).loadData(params); 913 }); 914 this.actor.registerMessageHandler("LT", (mapId, params) => { 915 return this._getWorkerSource(mapId, params.type, params.source).loadTile(params); 916 }); 917 this.actor.registerMessageHandler("RT", (mapId, params) => { 918 return this._getWorkerSource(mapId, params.type, params.source).reloadTile(params); 919 }); 920 this.actor.registerMessageHandler("AT", (mapId, params) => { 921 return this._getWorkerSource(mapId, params.type, params.source).abortTile(params); 922 }); 923 this.actor.registerMessageHandler("RMT", (mapId, params) => { 924 return this._getWorkerSource(mapId, params.type, params.source).removeTile(params); 925 }); 926 this.actor.registerMessageHandler("RS", async (mapId, params) => { 927 if (!this.workerSources[mapId]?.[params.type]?.[params.source]) return; 928 const worker = this.workerSources[mapId][params.type][params.source]; 929 delete this.workerSources[mapId][params.type][params.source]; 930 if (worker.removeSource !== void 0) worker.removeSource(params); 931 }); 932 this.actor.registerMessageHandler("RM", async (mapId) => { 933 delete this.layerIndexes[mapId]; 934 delete this.availableImages[mapId]; 935 delete this.workerSources[mapId]; 936 delete this.demWorkerSources[mapId]; 937 this.globalStates.delete(mapId); 938 }); 939 this.actor.registerMessageHandler("SR", async (_mapId, params) => { 940 this.referrer = params; 941 }); 942 this.actor.registerMessageHandler("SRPS", (mapId, params) => { 943 return this._syncRTLPluginState(mapId, params); 944 }); 945 this.actor.registerMessageHandler("IS", async (_mapId, params) => { 946 await loadScript(params); 947 }); 948 this.actor.registerMessageHandler("SI", (mapId, params) => { 949 return this._setImages(mapId, params); 950 }); 951 this.actor.registerMessageHandler("UL", async (mapId, params) => { 952 this._getLayerIndex(mapId).update(params.layers, params.removedIds, this._getGlobalState(mapId)); 953 }); 954 this.actor.registerMessageHandler("UGS", async (mapId, params) => { 955 const globalState = this._getGlobalState(mapId); 956 for (const key in params) globalState[key] = params[key]; 957 }); 958 this.actor.registerMessageHandler("SL", async (mapId, params) => { 959 this._getLayerIndex(mapId).replace(params, this._getGlobalState(mapId)); 960 }); 961 } 962 _getGlobalState(mapId) { 963 let state = this.globalStates.get(mapId); 964 if (!state) { 965 state = {}; 966 this.globalStates.set(mapId, state); 967 } 968 return state; 969 } 970 async _setImages(mapId, images) { 971 this.availableImages[mapId] = images; 972 for (const workerSource in this.workerSources[mapId]) { 973 const ws = this.workerSources[mapId][workerSource]; 974 for (const source in ws) ws[source].availableImages = images; 975 } 976 } 977 async _syncRTLPluginState(mapId, incomingState) { 978 return await rtlWorkerPlugin.syncState(incomingState, loadScript); 979 } 980 _getAvailableImages(mapId) { 981 let availableImages = this.availableImages[mapId]; 982 availableImages ||= []; 983 return availableImages; 984 } 985 _getLayerIndex(mapId) { 986 let layerIndexes = this.layerIndexes[mapId]; 987 layerIndexes ||= this.layerIndexes[mapId] = new StyleLayerIndex(); 988 return layerIndexes; 989 } 990 /** 991 * This is basically a lazy initialization of a worker per mapId and sourceType and sourceName 992 * @param mapId - the mapId 993 * @param sourceType - the source type - 'vector' for example 994 * @param sourceName - the source name - 'osm' for example 995 * @returns a new instance or a cached one 996 */ 997 _getWorkerSource(mapId, sourceType, sourceName) { 998 this.workerSources[mapId] ||= {}; 999 this.workerSources[mapId][sourceType] ||= {}; 1000 if (!this.workerSources[mapId][sourceType][sourceName]) { 1001 const actor = { sendAsync: (message, abortController) => { 1002 message.targetMapId = mapId; 1003 return this.actor.sendAsync(message, abortController); 1004 } }; 1005 switch (sourceType) { 1006 case "vector": 1007 this.workerSources[mapId][sourceType][sourceName] = new VectorTileWorkerSource(actor, this._getLayerIndex(mapId), this._getAvailableImages(mapId)); 1008 break; 1009 case "geojson": 1010 this.workerSources[mapId][sourceType][sourceName] = new GeoJSONWorkerSource(actor, this._getLayerIndex(mapId), this._getAvailableImages(mapId)); 1011 break; 1012 default: this.workerSources[mapId][sourceType][sourceName] = new this.externalWorkerSourceTypes[sourceType](actor, this._getLayerIndex(mapId), this._getAvailableImages(mapId)); 1013 } 1014 } 1015 return this.workerSources[mapId][sourceType][sourceName]; 1016 } 1017 /** 1018 * This is basically a lazy initialization of a worker per mapId and source 1019 * @param mapId - the mapId 1020 * @param sourceType - the source type - 'raster-dem' for example 1021 * @returns a new instance or a cached one 1022 */ 1023 _getDEMWorkerSource(mapId, sourceType) { 1024 this.demWorkerSources[mapId] ||= {}; 1025 this.demWorkerSources[mapId][sourceType] ||= new RasterDEMTileWorkerSource(); 1026 return this.demWorkerSources[mapId][sourceType]; 1027 } 1028}; 1029if (isWorker(self)) self.worker = new Worker(self); 1030//#endregion 1031export { Worker as default }; 1032 1033//# sourceMappingURL=maplibre-gl-worker-dev.mjs.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.