vendor: 4,933 bytes, lines 1-119
1/** 2* MapLibre GL JS 3* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v6.4.1/LICENSE.txt 4*/ 5import { B as PbfReader, Bn as JSON_PREFIX, D as SymbolBucket, Dn as getArrayBuffer, Dt as RGBAImage, Et as AlphaImage, Fn as removeProtocol, I as potpack, K as FillExtrusionBucket, Nn as addProtocol, On as getJSON, P as ImageAtlas, Rn as isAbortError, S as createStyleLayer, U as LineBucket, Ur as EXTENT, Vt as CollisionBoxArray, W as GeoJSONVT, Wi as Point, an as register, bt as DEMData, c as FeatureIndex, d as DictionaryCoder, fr as getImageData, g as OverscaledTileID, i as clipGeometry, ir as ensureError, jn as makeRequest, l as MLTVectorTile, m as fromVectorTileJs, mn as createExpression, mr as isImageBitmap, mt as FillBucket, n as performSymbolLayout, nn as rtlWorkerPlugin, o as BoundedLRUCache, or as extend, p as GeoJSONWrapper, tn as EvaluationParameters, ut as VectorTile, vn as featureFilter, x as Actor, xr as mapObject, yn as groupByLayout, yr as isWorker, zr as warnOnce } 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).map(Number)); 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 instanceof SymbolBucket) { 232 recalculateLayers(bucket.layers, this.zoom, availableImages); 233 performSymbolLayout({ 234 bucket, 235 glyphMap, 236 glyphPositions: glyphAtlas.positions, 237 imageMap: iconMap, 238 imagePositions: imageAtlas.iconPositions, 239 showCollisionBoxes: this.showCollisionBoxes, 240 canonical: this.tileID.canonical, 241 subdivisionGranularity: options.subdivisionGranularity 242 }); 243 } else if (bucket.hasDependencies && (bucket instanceof FillBucket || bucket instanceof FillExtrusionBucket || bucket instanceof LineBucket)) { 244 recalculateLayers(bucket.layers, this.zoom, availableImages); 245 bucket.addFeatures(options, this.tileID.canonical, imageAtlas.patternPositions, dashPositions); 246 }
vendor: 6,657 bytes, lines 246-454
246 247 } 248 return { 249 buckets: Object.values(buckets).filter((b) => !b.isEmpty()), 250 featureIndex, 251 collisionBoxArray: this.collisionBoxArray, 252 glyphAtlasImage: glyphAtlas.image, 253 imageAtlas, 254 dashPositions, 255 glyphMap: this.returnDependencies ? glyphMap : null, 256 iconMap: this.returnDependencies ? iconMap : null, 257 glyphPositions: this.returnDependencies ? glyphAtlas.positions : null 258 }; 259 } 260}; 261function recalculateLayers(layers, zoom, availableImages) { 262 const parameters = new EvaluationParameters(zoom); 263 for (const layer of layers) layer.recalculate(parameters, availableImages); 264} 265//#endregion 266//#region src/source/worker_tile_state.ts 267var WorkerTileState = class { 268 constructor() { 269 this.loading = {}; 270 this.loaded = {}; 271 this.parsing = {}; 272 } 273 startLoading(uid, tile) { 274 this.loading[uid] = tile; 275 } 276 finishLoading(uid) { 277 delete this.loading[uid]; 278 } 279 abort(uid) { 280 const tile = this.loading[uid]; 281 if (!tile?.abort) return; 282 tile.abort.abort(); 283 delete this.loading[uid]; 284 } 285 getParsing(uid) { 286 return this.parsing[uid]; 287 } 288 setParsing(uid, state) { 289 this.parsing[uid] = state; 290 } 291 removeParsing(uid) { 292 delete this.parsing[uid]; 293 } 294 markLoaded(uid, tile) { 295 this.loaded[uid] = tile; 296 } 297 getLoaded(uid) { 298 const tile = this.loaded[uid]; 299 if (!tile) return void 0; 300 return tile; 301 } 302 removeLoaded(uid) { 303 delete this.loaded[uid]; 304 } 305 clearLoaded() { 306 this.loaded = {}; 307 } 308}; 309//#endregion 310//#region src/util/request_performance.ts 311/** 312* @internal 313* Safe wrapper for the performance resource timing API in web workers with graceful degradation 314*/ 315var RequestPerformance = class { 316 constructor(url) { 317 this.start = `${url}#start`; 318 this.end = `${url}#end`; 319 this.measure = url; 320 performance.mark(this.start); 321 } 322 finish() { 323 performance.mark(this.end); 324 let resourceTimingData = performance.getEntriesByName(this.measure); 325 if (resourceTimingData.length === 0) { 326 performance.measure(this.measure, this.start, this.end); 327 resourceTimingData = performance.getEntriesByName(this.measure); 328 performance.clearMarks(this.start); 329 performance.clearMarks(this.end); 330 performance.clearMeasures(this.measure); 331 } 332 return resourceTimingData; 333 } 334}; 335//#endregion 336//#region src/source/vector_tile_overzoomed.ts 337var VectorTileFeatureOverzoomed = class { 338 constructor(type, geometry, properties, id, extent) { 339 this.type = type; 340 this.properties = properties ? properties : {}; 341 this.extent = extent; 342 this.pointsArray = geometry; 343 this.id = id; 344 } 345 loadGeometry() { 346 return this.pointsArray.map((ring) => ring.map((point) => new Point(point.x, point.y))); 347 } 348}; 349var VectorTileLayerOverzoomed = class { 350 constructor(features, layerName, extent) { 351 this.version = 2; 352 this._myFeatures = features; 353 this.name = layerName; 354 this.length = features.length; 355 this.extent = extent; 356 } 357 feature(i) { 358 return this._myFeatures[i]; 359 } 360}; 361var VectorTileOverzoomed = class { 362 constructor() { 363 this.layers = {}; 364 } 365 addLayer(layer) { 366 this.layers[layer.name] = layer; 367 } 368}; 369/** 370* This function slices a source tile layer into an overzoomed tile layer for a target tile ID. 371* @param sourceLayer - the source tile layer to slice 372* @param maxZoomTileID - the maximum zoom tile ID 373* @param targetTileID - the target tile ID 374* @returns - the overzoomed tile layer 375*/ 376function sliceVectorTileLayer(sourceLayer, maxZoomTileID, targetTileID) { 377 const { extent } = sourceLayer; 378 const dz = targetTileID.z - maxZoomTileID.z; 379 const scale = Math.pow(2, dz); 380 const offsetX = (targetTileID.x - maxZoomTileID.x * scale) * extent; 381 const offsetY = (targetTileID.y - maxZoomTileID.y * scale) * extent; 382 const featureWrappers = []; 383 for (let index = 0; index < sourceLayer.length; index++) { 384 const feature = sourceLayer.feature(index); 385 let geometry = feature.loadGeometry(); 386 for (const ring of geometry) for (const point of ring) { 387 point.x = point.x * scale - offsetX; 388 point.y = point.y * scale - offsetY; 389 } 390 const buffer = 128; 391 geometry = clipGeometry(geometry, feature.type, -128, -128, extent + buffer, extent + buffer); 392 if (geometry.length === 0) continue; 393 featureWrappers.push(new VectorTileFeatureOverzoomed(feature.type, geometry, feature.properties, feature.id, extent)); 394 } 395 return new VectorTileLayerOverzoomed(featureWrappers, sourceLayer.name, extent); 396} 397//#endregion 398//#region src/source/vector_tile_worker_source.ts 399/** 400* The {@link WorkerSource} implementation that supports {@link VectorTileSource}. This class is 401* used by vector tile sources to perform tile processing operations in a separate worker thread. 402*/ 403var VectorTileWorkerSource = class { 404 constructor(actor, layerIndex, availableImages) { 405 this.actor = actor; 406 this.layerIndex = layerIndex; 407 this.availableImages = availableImages; 408 this.tileState = new WorkerTileState(); 409 this.overzoomedTileResultCache = new BoundedLRUCache(1e3); 410 } 411 /** 412 * Loads a vector tile 413 */ 414 loadVectorTile(params, rawData) { 415 try { 416 return { 417 vectorTile: params.encoding !== "mlt" ? new VectorTile(new PbfReader(rawData)) : new MLTVectorTile(rawData), 418 rawData 419 }; 420 } catch (ex) { 421 const bytes = new Uint8Array(rawData); 422 const isGzipped = bytes[0] === 31 && bytes[1] === 139; 423 let errorMessage = `Unable to parse the tile at ${params.request.url}, `; 424 if (isGzipped) errorMessage += "please make sure the data is not gzipped and that you have configured the relevant header in the server"; 425 else errorMessage += `got error: ${ensureError(ex).message}`; 426 throw new Error(errorMessage); 427 } 428 } 429 /** 430 * Implements {@link WorkerSource.loadTile}. 431 */ 432 async loadTile(params) { 433 const { uid, overzoomParameters } = params; 434 if (overzoomParameters) params.request = overzoomParameters.overzoomRequest; 435 const timing = this._startRequestTiming(params); 436 const workerTile = new WorkerTile(params); 437 this.tileState.startLoading(uid, workerTile); 438 const abortController = new AbortController(); 439 workerTile.abort = abortController; 440 try { 441 const tileResponse = await getArrayBuffer(params.request, abortController); 442 if (params.etag && params.etag === tileResponse.etag) { 443 this.tileState.finishLoading(uid); 444 return this._getEtagUnmodifiedResult(tileResponse, timing); 445 } 446 const tileResult = this.loadVectorTile(params, tileResponse.data); 447 this.tileState.finishLoading(uid); 448 if (!tileResult) return null; 449 let { vectorTile, rawData } = tileResult; 450 if (overzoomParameters) ({vectorTile, rawData} = this._getOverzoomTile(params, vectorTile)); 451 const cacheControl = this._getExpiryData(tileResponse); 452 const resourceTiming = this._finishRequestTiming(timing); 453 workerTile.vectorTile = vectorTile; 454
vendor: 4,272 bytes, lines 454-567
454this.tileState.markLoaded(uid, workerTile); 455 const parsingState = { 456 rawData, 457 cacheControl, 458 resourceTiming 459 }; 460 this.tileState.setParsing(uid, parsingState); 461 return await this._parseWorkerTile(workerTile, params); 462 } catch (err) { 463 this.tileState.finishLoading(uid); 464 this.tileState.markLoaded(uid, workerTile); 465 throw err; 466 } 467 } 468 _getEtagUnmodifiedResult(response, timing) { 469 const cacheControl = this._getExpiryData(response); 470 const resourceTiming = this._finishRequestTiming(timing); 471 return extend({ etagUnmodified: true }, cacheControl, resourceTiming); 472 } 473 async _parseWorkerTile(workerTile, params) { 474 const parseState = this.tileState.getParsing(workerTile.uid); 475 let result = await workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, params.subdivisionGranularity); 476 if (parseState) { 477 const { rawData, cacheControl, resourceTiming } = parseState; 478 const encoding = params.overzoomParameters ? "mvt" : params.encoding; 479 result = extend({ 480 rawTileData: rawData.slice(0), 481 encoding 482 }, result, cacheControl, resourceTiming); 483 this.tileState.removeParsing(workerTile.uid); 484 } 485 return result; 486 } 487 _getExpiryData({ expires, cacheControl, etag }) { 488 const data = {}; 489 if (expires) data.expires = expires; 490 if (cacheControl) data.cacheControl = cacheControl; 491 if (etag) data.etag = etag; 492 return data; 493 } 494 _startRequestTiming(params) { 495 if (!params.request?.collectResourceTiming) return; 496 return new RequestPerformance(params.request.url); 497 } 498 _finishRequestTiming(timing) { 499 const timingData = timing?.finish(); 500 if (!timingData) return {}; 501 return { resourceTiming: JSON.parse(JSON.stringify(timingData)) }; 502 } 503 /** 504 * If we are seeking a tile deeper than the source's max available canonical tile, get the overzoomed tile 505 * @param params - the worker tile parameters 506 * @param maxZoomVectorTile - the original vector tile at the source's max available canonical zoom 507 * @returns the overzoomed tile and its raw data 508 */ 509 _getOverzoomTile(params, maxZoomVectorTile) { 510 const { tileID, source, overzoomParameters } = params; 511 const { maxZoomTileID } = overzoomParameters; 512 const cacheKey = `${maxZoomTileID.key}_${tileID.key}_${params.request?.url}`; 513 const cachedOverzoomTile = this.overzoomedTileResultCache.get(cacheKey); 514 if (cachedOverzoomTile) return cachedOverzoomTile; 515 const overzoomedVectorTile = new VectorTileOverzoomed(); 516 const layerFamilies = this.layerIndex.familiesBySource[source]; 517 for (const sourceLayerId in layerFamilies) { 518 const sourceLayer = maxZoomVectorTile.layers[sourceLayerId]; 519 if (!sourceLayer) continue; 520 const slicedTileLayer = sliceVectorTileLayer(sourceLayer, maxZoomTileID, tileID.canonical); 521 if (slicedTileLayer.length > 0) overzoomedVectorTile.addLayer(slicedTileLayer); 522 } 523 const overzoomedVectorTileResult = { 524 vectorTile: overzoomedVectorTile, 525 rawData: fromVectorTileJs(overzoomedVectorTile).buffer 526 }; 527 this.overzoomedTileResultCache.set(cacheKey, overzoomedVectorTileResult); 528 return overzoomedVectorTileResult; 529 } 530 /** 531 * Implements {@link WorkerSource.reloadTile}. 532 */ 533 async reloadTile(params) { 534 const uid = params.uid; 535 const workerTile = this.tileState.getLoaded(uid); 536 if (!workerTile) throw new Error("Should not be trying to reload a tile that was never loaded or has been removed"); 537 if (!workerTile.vectorTile) return; 538 workerTile.showCollisionBoxes = params.showCollisionBoxes; 539 return await this._parseWorkerTile(workerTile, params); 540 } 541 /** 542 * Implements {@link WorkerSource.abortTile}. 543 */ 544 async abortTile(params) { 545 this.tileState.abort(params.uid); 546 } 547 /** 548 * Implements {@link WorkerSource.removeTile}. 549 */ 550 async removeTile(params) { 551 this.tileState.removeLoaded(params.uid); 552 } 553}; 554//#endregion 555//#region src/source/raster_dem_tile_worker_source.ts 556var RasterDEMTileWorkerSource = class { 557 constructor() { 558 this.loaded = {}; 559 } 560 async loadTile(params) { 561 const { uid, encoding, rawImageData, redFactor, greenFactor, blueFactor, baseShift } = params; 562 const width = rawImageData.width + 2; 563 const height = rawImageData.height + 2; 564 const imagePixels = isImageBitmap(rawImageData) ? new RGBAImage({ 565 width, 566 height 567 }, await getImageData(rawImageData, -1, -1
vendor: 17,658 bytes, lines 567-1027
567, width, height)) : rawImageData; 568 const dem = new DEMData(uid, imagePixels, encoding, redFactor, greenFactor, blueFactor, baseShift); 569 this.loaded ||= {}; 570 this.loaded[uid] = dem; 571 return dem; 572 } 573 removeTile(params) { 574 const loaded = this.loaded, uid = params.uid; 575 if (loaded?.[uid]) delete loaded[uid]; 576 } 577}; 578//#endregion 579//#region src/source/geojson_worker_source.ts 580/** 581* The {@link WorkerSource} implementation that supports {@link GeoJSONSource}. 582* This class is designed to be easily reused to support custom source types 583* for data formats that can be parsed/converted into an in-memory GeoJSON 584* representation. To do so, create it with 585* `new GeoJSONWorkerSource(actor, layerIndex, customLoadGeoJSONFunction)`. 586* For a full example, see [mapbox-gl-topojson](https://github.com/developmentseed/mapbox-gl-topojson). 587*/ 588var GeoJSONWorkerSource = class { 589 constructor(actor, layerIndex, availableImages, createGeoJSONIndexFunc = createGeoJSONIndex) { 590 this.actor = actor; 591 this.layerIndex = layerIndex; 592 this.availableImages = availableImages; 593 this.tileState = new WorkerTileState(); 594 this._createGeoJSONIndex = createGeoJSONIndexFunc; 595 } 596 /** 597 * Retrieves and sends loaded vector tiles to the main thread. 598 */ 599 loadVectorTile(params) { 600 if (!this._geoJSONIndex) throw new Error("Unable to parse the data into a cluster or geojson"); 601 const { z, x, y } = params.tileID.canonical; 602 const geoJSONTile = this._geoJSONIndex.getTile(z, x, y); 603 if (!geoJSONTile) return null; 604 const geojsonWrapper = new GeoJSONWrapper(geoJSONTile.features, { 605 version: 2, 606 extent: EXTENT 607 }); 608 return { 609 vectorTile: geojsonWrapper, 610 rawData: fromVectorTileJs(geojsonWrapper, JSON_PREFIX).buffer 611 }; 612 } 613 /** 614 * Implements {@link WorkerSource.loadTile}. 615 */ 616 async loadTile(params) { 617 const { uid } = params; 618 const workerTile = new WorkerTile(params); 619 workerTile.abort = new AbortController(); 620 try { 621 const loadResult = this.loadVectorTile(params); 622 if (!loadResult) return null; 623 const { vectorTile, rawData } = loadResult; 624 workerTile.vectorTile = vectorTile; 625 this.tileState.markLoaded(uid, workerTile); 626 const parsingState = { rawData }; 627 this.tileState.setParsing(uid, parsingState); 628 return await this._parseWorkerTile(workerTile, params); 629 } catch (err) { 630 this.tileState.markLoaded(uid, workerTile); 631 throw err; 632 } 633 } 634 async _parseWorkerTile(workerTile, params) { 635 const parseState = this.tileState.getParsing(workerTile.uid); 636 let result = await workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, params.subdivisionGranularity); 637 if (parseState) { 638 const { rawData } = parseState; 639 result = extend({ 640 rawTileData: rawData.slice(0), 641 encoding: "mvt" 642 }, result); 643 this.tileState.removeParsing(workerTile.uid); 644 } 645 return result; 646 } 647 /** 648 * Implements {@link WorkerSource.abortTile}. 649 */ 650 async abortTile(params) { 651 this.tileState.abort(params.uid); 652 } 653 /** 654 * Implements {@link WorkerSource.removeTile}. 655 */ 656 async removeTile(params) { 657 this.tileState.removeLoaded(params.uid); 658 } 659 /** 660 * Fetches (if appropriate), parses and indexes geojson data into tiles. This 661 * preparatory method must be called before {@link GeoJSONWorkerSource.loadTile} 662 * can correctly serve up tiles. The first call to this method must contain a valid 663 * {@link params.data}, {@link params.request} or {@link params.dataDiff}. Subsequent 664 * calls may omit these parameters to reprocess the existing data (such as to update 665 * clustering options). 666 * 667 * Defers to {@link GeoJSONWorkerSource.loadAndProcessGeoJSON} for the pre-processing. 668 * 669 * When a `loadData` request comes in while a previous one is being processed, 670 * the previous one is aborted. 671 * 672 * @param params - the parameters 673 * @returns a promise that resolves when the data is loaded and parsed into a GeoJSON object 674 */ 675 async loadData(params) { 676 this._pendingRequest?.abort(); 677 const timing = this._startRequestTiming(params); 678 this._pendingRequest = new AbortController(); 679 try { 680 await this.loadAndProcessGeoJSON(params, this._pendingRequest); 681 delete this._pendingRequest; 682 this.tileState.clearLoaded(); 683 const result = {}; 684 if (params.request) result.data = params.data; 685 this._finishRequestTiming(timing, params, result); 686 return result; 687 } catch (err) { 688 delete this._pendingRequest; 689 if (!isAbortError(err)) throw err; 690 return { abandoned: true }; 691 } 692 } 693 _startRequestTiming(params) { 694 if (!params.request?.collectResourceTiming) return; 695 return new RequestPerformance(params.request.url); 696 } 697 _finishRequestTiming(timing, params, result) { 698 const timingData = timing?.finish(); 699 if (!timingData) return; 700 result.resourceTiming = { [params.source]: JSON.parse(JSON.stringify(timingData)) }; 701 } 702 /** 703 * Implements {@link WorkerSource.reloadTile}. 704 * 705 * If the tile is loaded, reload by re-parsing the already available tile data. 706 * Otherwise, such as after a setData() call, we load the tile fresh. 707 * 708 * @param params - the parameters 709 * @returns A promise that resolves when the tile is reloaded 710 */ 711 async reloadTile(params) { 712 const uid = params.uid; 713 const workerTile = this.tileState.getLoaded(uid); 714 if (!workerTile) return await this.loadTile(params); 715 if (!workerTile.vectorTile) return; 716 workerTile.showCollisionBoxes = params.showCollisionBoxes; 717 return await this._parseWorkerTile(workerTile, params); 718 } 719 /** 720 * Fetch, parse and process GeoJSON according to the given parameters. 721 * Defers to {@link GeoJSONWorkerSource._loadGeoJSONFromString} for the fetching and parsing. 722 * 723 * @param params - the parameters 724 * @param abortController - the abort controller that allows aborting this operation 725 * @returns a promise that is resolved with the processes GeoJSON 726 */ 727 async loadAndProcessGeoJSON(params, abortController) { 728 if (params.request) params.data = (await getJSON(params.request, abortController)).data; 729 if (params.data) { 730 params.data = this._filterGeoJSON(params.data, params.filter, params.source); 731 this._geoJSONIndex = this._createGeoJSONIndex(params.data, params); 732 return; 733 } 734 if (params.dataDiff) { 735 this._geoJSONIndex ??= this._createGeoJSONIndex({ 736 type: "FeatureCollection", 737 features: [] 738 }, params); 739 this._geoJSONIndex.updateData(params.dataDiff, this._getFilterPredicate(params.filter, params.source)); 740 return; 741 } 742 if (params.updateCluster) this._geoJSONIndex.updateClusterOptions(params.geojsonVtOptions.cluster, getSuperclusterOptions(params)); 743 if (this._geoJSONIndex == null) throw new Error(`Input data given to '${params.source}' is not a valid GeoJSON object.`); 744 } 745 /** 746 * Applies a filter to a GeoJSON object. 747 */ 748 _filterGeoJSON(data, filter, source) { 749 if (data.type !== "FeatureCollection") return data; 750 const predicate = this._getFilterPredicate(filter, source); 751 if (!predicate) return data; 752 return { 753 type: "FeatureCollection", 754 features: data.features.filter((feature) => predicate(feature)) 755 }; 756 } 757 /** 758 * Gets a predicate function that can be used to filter GeoJSON features. 759 */ 760 _getFilterPredicate(filter, source) { 761 if (typeof filter !== "boolean" && !filter?.length) return void 0; 762 const compiled = createExpression(filter, `sources.${source}.filter`, { 763 type: "boolean", 764 "property-type": "data-driven", 765 overridable: false, 766 transition: false 767 }); 768 if (compiled.result === "error") throw new Error(compiled.value.map((err) => `${err.key}: ${err.message}`).join(", ")); 769 return (feature) => compiled.value.evaluate({ zoom: 0 }, feature); 770 } 771 async removeSource(_params) { 772 this._pendingRequest?.abort(); 773 } 774 getClusterExpansionZoom(params) { 775 return this._geoJSONIndex.getClusterExpansionZoom(params.clusterId); 776 } 777 getClusterChildren(params) { 778 return this._geoJSONIndex.getClusterChildren(params.clusterId); 779 } 780 getClusterLeaves(params) { 781 return this._geoJSONIndex.getClusterLeaves(params.clusterId, params.limit, params.offset); 782 } 783}; 784function createGeoJSONIndex(data, params) { 785 const options = extend(params.geojsonVtOptions || {}, { 786 updateable: true, 787 clusterOptions: getSuperclusterOptions(params) 788 }); 789 return new GeoJSONVT(data, options); 790} 791function getSuperclusterOptions({ geojsonVtOptions, clusterProperties, source }) { 792 if (!clusterProperties || !geojsonVtOptions.clusterOptions) return geojsonVtOptions.clusterOptions; 793 const mapExpressions = {}; 794 const reduceExpressions = {}; 795 const globals = { 796 accumulated: null, 797 zoom: 0 798 }; 799 const feature = { properties: null }; 800 const propertyNames = Object.keys(clusterProperties); 801 for (const key of propertyNames) { 802 const [operator, mapExpression] = clusterProperties[key]; 803 const mapExpressionParsed = createExpression(mapExpression, `sources.${source}.clusterProperties.${key}[1]`); 804 const reduceExpressionParsed = createExpression(typeof operator === "string" ? [ 805 operator, 806 ["accumulated"], 807 ["get", key] 808 ] : operator, `sources.${source}.clusterProperties.${key}[0]`); 809 mapExpressions[key] = mapExpressionParsed.value; 810 reduceExpressions[key] = reduceExpressionParsed.value; 811 } 812 geojsonVtOptions.clusterOptions.map = (pointProperties) => { 813 feature.properties = pointProperties; 814 const properties = {}; 815 for (const key of propertyNames) properties[key] = mapExpressions[key].evaluate(globals, feature); 816 return properties; 817 }; 818 geojsonVtOptions.clusterOptions.reduce = (accumulated, clusterProperties) => { 819 feature.properties = clusterProperties; 820 for (const key of propertyNames) { 821 globals.accumulated = accumulated[key]; 822 accumulated[key] = reduceExpressions[key].evaluate(globals, feature); 823 } 824 }; 825 return geojsonVtOptions.clusterOptions; 826} 827//#endregion 828//#region src/source/worker.ts 829/** 830* Loads an external script into worker (global) scope. The loader picks a 831* strategy based on what the script actually is: 832* 833* - `.mjs` URLs: dynamic `import()` directly, no fetch/sniff overhead. Worker 834* CSP needs `script-src` to permit the URL. 835* 836* - Other URLs: fetch the source and sniff for ESM syntax (top-level `import` 837* or `export`). If ESM is detected, run it through a blob-URL dynamic 838* `import()` so the browser parses it as a module; this requires 839* `script-src blob:` in the worker CSP. Otherwise treat it as UMD/IIFE and 840* run it via `globalThis.eval`, which requires `script-src 'unsafe-eval'`. 841*/ 842async function loadScript(url) { 843 if (url.endsWith(".mjs")) { 844 await import( 845 /* @vite-ignore */ 846 url 847); 848 return; 849 } 850 const response = await fetch(url, { credentials: "same-origin" }); 851 if (!response.ok) throw new Error(`Failed to load ${url}: ${response.status}`); 852 const code = await response.text(); 853 if (/^[ \t]*(import|export)\s/m.test(code)) { 854 const blobUrl = URL.createObjectURL(new Blob([code], { type: "text/javascript" })); 855 try { 856 await import( 857 /* @vite-ignore */ 858 blobUrl 859); 860 } finally { 861 URL.revokeObjectURL(blobUrl); 862 } 863 return; 864 } 865 globalThis.eval(code); 866} 867/** 868* The Worker class responsible for background thread related execution 869*/ 870var Worker = class { 871 constructor(self) { 872 this.self = self; 873 this.actor = new Actor(self); 874 this.layerIndexes = {}; 875 this.availableImages = {}; 876 this.workerSources = {}; 877 this.demWorkerSources = {}; 878 this.externalWorkerSourceTypes = {}; 879 this.globalStates = /* @__PURE__ */ new Map(); 880 this.self.registerWorkerSource = (name, WorkerSource) => { 881 if (this.externalWorkerSourceTypes[name]) throw new Error(`Worker source with name "${name}" already registered.`); 882 this.externalWorkerSourceTypes[name] = WorkerSource; 883 }; 884 this.self.addProtocol = addProtocol; 885 this.self.removeProtocol = removeProtocol; 886 this.self.registerRTLTextPlugin = (rtlTextPlugin) => { 887 rtlWorkerPlugin.setMethods(rtlTextPlugin); 888 }; 889 this.self.makeRequest = makeRequest; 890 this.actor.registerMessageHandler("LDT", (mapId, params) => { 891 return this._getDEMWorkerSource(mapId, params.source).loadTile(params); 892 }); 893 this.actor.registerMessageHandler("RDT", async (mapId, params) => { 894 this._getDEMWorkerSource(mapId, params.source).removeTile(params); 895 }); 896 this.actor.registerMessageHandler("GCEZ", async (mapId, params) => { 897 return this._getWorkerSource(mapId, params.type, params.source).getClusterExpansionZoom(params); 898 }); 899 this.actor.registerMessageHandler("GCC", async (mapId, params) => { 900 return this._getWorkerSource(mapId, params.type, params.source).getClusterChildren(params); 901 }); 902 this.actor.registerMessageHandler("GCL", async (mapId, params) => { 903 return this._getWorkerSource(mapId, params.type, params.source).getClusterLeaves(params); 904 }); 905 this.actor.registerMessageHandler("LD", (mapId, params) => { 906 return this._getWorkerSource(mapId, params.type, params.source).loadData(params); 907 }); 908 this.actor.registerMessageHandler("LT", (mapId, params) => { 909 return this._getWorkerSource(mapId, params.type, params.source).loadTile(params); 910 }); 911 this.actor.registerMessageHandler("RT", (mapId, params) => { 912 return this._getWorkerSource(mapId, params.type, params.source).reloadTile(params); 913 }); 914 this.actor.registerMessageHandler("AT", (mapId, params) => { 915 return this._getWorkerSource(mapId, params.type, params.source).abortTile(params); 916 }); 917 this.actor.registerMessageHandler("RMT", (mapId, params) => { 918 return this._getWorkerSource(mapId, params.type, params.source).removeTile(params); 919 }); 920 this.actor.registerMessageHandler("RS", async (mapId, params) => { 921 if (!this.workerSources[mapId]?.[params.type]?.[params.source]) return; 922 const worker = this.workerSources[mapId][params.type][params.source]; 923 delete this.workerSources[mapId][params.type][params.source]; 924 if (worker.removeSource !== void 0) worker.removeSource(params); 925 }); 926 this.actor.registerMessageHandler("RM", async (mapId) => { 927 delete this.layerIndexes[mapId]; 928 delete this.availableImages[mapId]; 929 delete this.workerSources[mapId]; 930 delete this.demWorkerSources[mapId]; 931 this.globalStates.delete(mapId); 932 }); 933 this.actor.registerMessageHandler("SR", async (_mapId, params) => { 934 this.referrer = params; 935 }); 936 this.actor.registerMessageHandler("SRPS", (mapId, params) => { 937 return this._syncRTLPluginState(mapId, params); 938 }); 939 this.actor.registerMessageHandler("IS", async (_mapId, params) => { 940 await loadScript(params); 941 }); 942 this.actor.registerMessageHandler("SI", (mapId, params) => { 943 return this._setImages(mapId, params); 944 }); 945 this.actor.registerMessageHandler("UL", async (mapId, params) => { 946 this._getLayerIndex(mapId).update(params.layers, params.removedIds, this._getGlobalState(mapId)); 947 }); 948 this.actor.registerMessageHandler("UGS", async (mapId, params) => { 949 const globalState = this._getGlobalState(mapId); 950 for (const key in params) globalState[key] = params[key]; 951 }); 952 this.actor.registerMessageHandler("SL", async (mapId, params) => { 953 this._getLayerIndex(mapId).replace(params, this._getGlobalState(mapId)); 954 }); 955 } 956 _getGlobalState(mapId) { 957 let state = this.globalStates.get(mapId); 958 if (!state) { 959 state = {}; 960 this.globalStates.set(mapId, state); 961 } 962 return state; 963 } 964 async _setImages(mapId, images) { 965 this.availableImages[mapId] = images; 966 for (const workerSource in this.workerSources[mapId]) { 967 const ws = this.workerSources[mapId][workerSource]; 968 for (const source in ws) ws[source].availableImages = images; 969 } 970 } 971 async _syncRTLPluginState(mapId, incomingState) { 972 return await rtlWorkerPlugin.syncState(incomingState, loadScript); 973 } 974 _getAvailableImages(mapId) { 975 let availableImages = this.availableImages[mapId]; 976 availableImages ||= []; 977 return availableImages; 978 } 979 _getLayerIndex(mapId) { 980 let layerIndexes = this.layerIndexes[mapId]; 981 layerIndexes ||= this.layerIndexes[mapId] = new StyleLayerIndex(); 982 return layerIndexes; 983 } 984 /** 985 * This is basically a lazy initialization of a worker per mapId and sourceType and sourceName 986 * @param mapId - the mapId 987 * @param sourceType - the source type - 'vector' for example 988 * @param sourceName - the source name - 'osm' for example 989 * @returns a new instance or a cached one 990 */ 991 _getWorkerSource(mapId, sourceType, sourceName) { 992 this.workerSources[mapId] ||= {}; 993 this.workerSources[mapId][sourceType] ||= {}; 994 if (!this.workerSources[mapId][sourceType][sourceName]) { 995 const actor = { sendAsync: (message, abortController) => { 996 message.targetMapId = mapId; 997 return this.actor.sendAsync(message, abortController); 998 } }; 999 switch (sourceType) { 1000 case "vector": 1001 this.workerSources[mapId][sourceType][sourceName] = new VectorTileWorkerSource(actor, this._getLayerIndex(mapId), this._getAvailableImages(mapId)); 1002 break; 1003 case "geojson": 1004 this.workerSources[mapId][sourceType][sourceName] = new GeoJSONWorkerSource(actor, this._getLayerIndex(mapId), this._getAvailableImages(mapId)); 1005 break; 1006 default: this.workerSources[mapId][sourceType][sourceName] = new this.externalWorkerSourceTypes[sourceType](actor, this._getLayerIndex(mapId), this._getAvailableImages(mapId)); 1007 } 1008 } 1009 return this.workerSources[mapId][sourceType][sourceName]; 1010 } 1011 /** 1012 * This is basically a lazy initialization of a worker per mapId and source 1013 * @param mapId - the mapId 1014 * @param sourceType - the source type - 'raster-dem' for example 1015 * @returns a new instance or a cached one 1016 */ 1017 _getDEMWorkerSource(mapId, sourceType) { 1018 this.demWorkerSources[mapId] ||= {}; 1019 this.demWorkerSources[mapId][sourceType] ||= new RasterDEMTileWorkerSource(); 1020 return this.demWorkerSources[mapId][sourceType]; 1021 } 1022}; 1023if (isWorker(self)) self.worker = new Worker(self); 1024//#endregion 1025export { Worker as default }; 1026 1027//# 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.