PageSourceSearch

https://favorite.app/_next/static/media/maplibre-gl-dev.2790ibatt4woy.mjs

js favorite.app collected 2026-10-02 01:14:29 UTC 1,241,398 bytes, 30,142 lines download raw bytes

1/**
2* MapLibre GL JS
3* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v6.11.0/LICENSE.txt
4*/
5import { $ as projectToWorldCoordinates, $n as differenceOfAnglesDegrees, $r as add, $t as Transitionable, A as evaluateSizeForZoom, Ai as rotateX, An as addProtocol, Ar as scaleZoom, At as toEvaluationFeature, B as isCluster, Bi as rotate, Bn as arrayBufferToImage, Br as EXTENT, Bt as SegmentVector, C as addDynamicAttributes, Ci as exactEquals, Cn as GLOBAL_DISPATCHER_ID, Cr as pointPlaneSignedDistance, Ct as HEATMAP_FULL_RENDER_FBO_KEY, Di as multiply, Dn as getVideo, Dr as remapSaturate, Dt as RGBAImage, E as TextAnchorEnum, Ei as invert$1, En as getReferrer, Er as readImageUsingVideoFrame, Et as AlphaImage, F as potpack, Fi as create$4, Fn as isAbortError, Fr as uniqueId, Ft as Uniform4f, Gn as createIdentityMat4f32, Gr as zero, Gt as PosArray, Hi as offscreenCanvasSupported, Hn as bezier, Hr as length$1, Ht as CollisionCircleLayoutArray, I as isStyleImageWebGLData, Ii as fromRotation, In as throwIfAborted, Ir as warnOnce, It as UniformColor, J as cameraDirectionFromPitchBearing, Jn as createVec3f64, Jr as multiply$1, Jt as TriangleIndexArray, K as isFillExtrusionStyleLayer, Kn as createIdentityMat4f64, Kr as fromEuler, Kt as QuadTriangleArray, L as renderStyleImage, Li as create$2, Lr as wrap, Lt as UniformColorArray, M as getAnchorAlignment, Mi as rotateZ, Mn as removeProtocol, Mr as subscribe, Mt as Uniform1i, Ni as scale, Nn as config, Nr as threePlaneIntersection, Nt as Uniform2f, O as clipLine, Oi as ortho, On as makeRequest, Or as rollPitchBearingEqual, Ot as isCircleStyleLayer, P as ImagePosition, Pi as translate, Pn as AbortError, Pr as translatePosition, Pt as Uniform3f, Q as maxMercatorHorizonAngle, Qn as degreesToRadians, Qr as transformMat4, Qt as Properties, R as parseGlyphPbf, Ri as determinant, Rn as MAX_VALID_LATITUDE, Rr as zoomScale, Rt as UniformFloatArray, S as SymbolBucket, Si as equals, Sn as AJAXError, Sr as pick, St as isHillshadeStyleLayer, T as getAnchorJustification, Ti as identity, Tn as getJSON, Tr as rayPlaneIntersection, Tt as renderColorRamp, U as collisionCircleLayout, Ui as Point, Un as clamp, Ur as scale$3, Ut as LineStripIndexArray, V as codePointUsesLocalIdeographFontFamily, Vi as isOffscreenCanvasDistorted, Vn as arrayBufferToImageBitmap, Vr as create$3, Vt as CollisionBoxArray, W as isLineStyleLayer, Wn as clone$2, Wr as sqrLen, Wt as Pos3dArray, X as cameraMercatorCoordinateFromCenterAndRotation, Xn as deepEqual, Xr as mul, Xt as isRasterStyleLayer, Y as cameraMercatorCoordinate, Yn as createVec4f64, Yr as slerp, Yt as createLayout, Z as getMercatorHorizon, Zn as defaultEasing, Zr as scale$2, Zt as DataConstantProperty, _ as createStyleLayer, _i as transformQuat, _n as derefLayers, _r as lerp, _t as SubdivisionGranularityExpression, a as GeoJSONFeature, ai as length, an as validateAndEmit, ar as findLineIntersection, at as lngFromMercatorX, b as isBackgroundStyleLayer, bi as copy, bn as Event, br as nextPowerOfTwo, ci as rotateX$1, cn as emptyStyle, cr as getEdgeTiles, ct as mercatorZfromAltitude, d as OverscaledTileID, di as scale$1, dr as isImageBitmap, ei as clone, en as EvaluationParameters, er as distanceOfAnglesRadians, et as tileCoordinatesToMercatorCoordinates, f as UnwrappedTileID, fi as scaleAndAdd, fn as interpolateFactory, fr as isPointableEvent, ft as EXTENT_BOUNDS, g as Actor, gi as transformMat4$1, gn as diff, gr as isWorker, gt as SOUTH_POLE_Y, h as isInBoundsForZoomLngLat, hi as transformMat3, hn as ValidationError, hr as isTouchableOrPointableType, ht as NORTH_POLE_Y, ii as len, in as emitValidationErrors, ir as filterObject, it as latFromMercatorY, ji as rotateY$1, jn as getProtocol, jr as sphericalToCartesian, jt as Uniform1f, k as evaluateSizeForFeature, ki as perspective, kn as sameOrigin, kr as rollPitchBearingToQuat, kt as polygonIntersectsPolygon, li as rotateY, lt as LngLat, m as compareTileId, mi as subtract, mn as Color, mr as isTouchableEvent, mt as isFillStyleLayer, n as TileCache, ni as distance, nr as evaluateZoomSnap, nt as MercatorCoordinate, oi as negate, on as validateStyle, or as getAABB, ot as mercatorXfromLng, p as calculateTileKey, pi as sub, pn as ProjectionDefinition, pr as isSafari, pt as Bounds, q as calculateTileMatrix, qn as createMat4f64, qr as fromValues, qt as RasterBoundsArray, ri as dot, rn as SPEC_SOURCE_TYPES, rr as extend, rt as altitudeFromMercatorZ, s as GEOJSON_TILE_LAYER_NAME, si as normalize, sn as validateStyleAndEmit, sr as getAngleDelta, st as mercatorYfromLat, ti as cross, tn as ZoomHistory, tr as ensureError, tt as unprojectFromWorldCoordinates, u as CanonicalTileID, ui as rotateZ$1, un as featureFilter, ur as getRollPitchBearing, ut as earthRadius, v as isCustomStyleLayer, vi as zero$1, vn as latest, vr as mapObject, vt as SubdivisionGranularitySetting, w as getOverlapMode, wi as fromScaling, wn as getArrayBuffer, wr as radiansToDegrees, wt as isHeatmapStyleLayer, x as isSymbolStyleLayer, xi as create$1, xn as Evented, xr as parseCacheControl, xt as Texture, y as validateCustomStyleLayer, yi as clone$1, yn as ErrorEvent, yr as mod, yt as isColorReliefStyleLayer, zi as invert, zn as angleToRotateBetweenVectors2D, zr as pixelsToTileUnits, zt as UniformMatrix4f } from "./maplibre-gl-shared-dev.mjs";
6//#region package.json
7var version$2 = "6.11.0
vendor: 5,834 bytes, lines 7-200
7";
8//#endregion
9//#region src/util/browser.ts
10let linkEl;
11let reducedMotionQuery;
12let reducedMotionOverride;
13/** */
14const browser = {
15	/**
16	* Schedules a callback to be invoked on the next animation frame.
17	* @param abortController - Controller to abort the scheduled frame.
18	* @param fn - Callback to invoke with the paint start timestamp.
19	* @param reject - Callback to invoke if the frame is aborted.
20	* @param targetWindow - Optional window to use for requestAnimationFrame.
21	*   When the map is rendered in a popup window or iframe, pass the owning
22	*   window to ensure animation frames continue even when the main window
23	*   is not focused.
24	*/
25	frame(abortController, fn, reject, targetWindow) {
26		const win = targetWindow || window;
27		const frameId = win.requestAnimationFrame((paintStartTimestamp) => {
28			unsubscribe();
29			fn(paintStartTimestamp);
30		});
31		const { unsubscribe } = subscribe(abortController.signal, "abort", () => {
32			unsubscribe();
33			win.cancelAnimationFrame(frameId);
34			reject(new AbortError(abortController.signal.reason));
35		}, false);
36	},
37	/**
38	* Returns a promise that resolves on the next animation frame.
39	* @param abortController - Controller to abort the scheduled frame.
40	* @param targetWindow - Optional window to use for requestAnimationFrame.
41	* @see {@link browser.frame}
42	*/
43	frameAsync(abortController, targetWindow) {
44		return new Promise((resolve, reject) => {
45			this.frame(abortController, resolve, reject, targetWindow);
46		});
47	},
48	getImageData(img, padding = 0) {
49		return this.getImageCanvasContext(img).getImageData(-padding, -padding, img.width + 2 * padding, img.height + 2 * padding);
50	},
51	/**
52	* Draws an image into a 2D canvas whose pixels can be read back cheaply. An `OffscreenCanvas`
53	* is used where the browser has one that reads back faithfully: drawing a GPU-backed
54	* `ImageBitmap` into a document canvas opened with `willReadFrequently` stalls the main thread
55	* for tens of milliseconds on GPU-accelerated browsers, an `OffscreenCanvas` does not.
56	*/
57	getImageCanvasContext(img) {
58		let context;
59		if (offscreenCanvasSupported() && !isOffscreenCanvasDistorted()) context = new OffscreenCanvas(img.width, img.height).getContext("2d", { willReadFrequently: true });
60		else {
61			const canvas = window.document.createElement("canvas");
62			canvas.width = img.width;
63			canvas.height = img.height;
64			context = canvas.getContext("2d", { willReadFrequently: true });
65		}
66		if (!context) throw new Error("failed to create canvas 2d context");
67		context.drawImage(img, 0, 0, img.width, img.height);
68		return context;
69	},
70	resolveURL(path) {
71		linkEl ||= document.createElement("a");
72		linkEl.href = path;
73		return linkEl.href;
74	},
75	get hardwareConcurrency() {
76		return typeof navigator !== "undefined" && navigator.hardwareConcurrency || 4;
77	},
78	get prefersReducedMotion() {
79		if (reducedMotionOverride !== void 0) return reducedMotionOverride;
80		if (!matchMedia) return false;
81		reducedMotionQuery ??= matchMedia("(prefers-reduced-motion: reduce)");
82		return reducedMotionQuery.matches;
83	},
84	set prefersReducedMotion(value) {
85		reducedMotionOverride = value;
86	}
87};
88//#endregion
89//#region src/util/time_control.ts
90/**
91* Manages time flow with optional freezing capability for deterministic rendering.
92*/
93var TimeManager = class {
94	constructor() {
95		this._frozenAt = null;
96	}
97	/**
98	* Gets the current time, either real or frozen.
99	* @returns Current time in milliseconds
100	*/
101	getCurrentTime() {
102		return this._frozenAt !== null ? this._frozenAt : performance.now();
103	}
104	/**
105	* Sets time at a specific timestamp.
106	* @param timestamp - Time in milliseconds to set
107	*/
108	setNow(timestamp) {
109		this._frozenAt = timestamp;
110	}
111	/**
112	* Restores normal time flow.
113	*/
114	restoreNow() {
115		this._frozenAt = null;
116	}
117	/**
118	* Returns whether time is currently frozen.
119	* @returns True if time is frozen, false otherwise
120	*/
121	isFrozen() {
122		return this._frozenAt !== null;
123	}
124};
125const timeManager = new TimeManager();
126/**
127* Returns the current time in milliseconds.
128* When time is frozen via setNow(), returns the frozen timestamp.
129* Otherwise returns real browser time via performance.now().
130*
131* @returns Current time in milliseconds
132* @example
133* ```ts
134* // Measure elapsed time
135* const start = maplibregl.now();
136* // ... later ...
137* const elapsed = maplibregl.now() - start;
138*
139* // During frozen time
140* maplibregl.setNow(16.67);
141* console.log(maplibregl.now()); // 16.67
142* maplibregl.restoreNow();
143* console.log(maplibregl.now()); // real time
144* ```
145*/
146function now() {
147	return timeManager.getCurrentTime();
148}
149/**
150* Freezes time at a specific timestamp for deterministic rendering.
151* Useful for frame-by-frame video capture where each frame needs
152* a consistent time value.
153*
154* @param timestamp - Time in milliseconds to freeze at
155* @example
156* ```ts
157* // Freeze time for video export at 60fps
158* setNow(0);           // First frame
159* // ... render frame ...
160* setNow(16.67);       // Second frame
161* // ... render frame ...
162* setNow(33.34);       // Third frame
163* // ... done ...
164* restoreNow();        // Resume normal time
165* ```
166*/
167function setNow(timestamp) {
168	timeManager.setNow(timestamp);
169}
170/**
171* Restores normal time flow after freezing with setNow().
172* Call this after finishing deterministic rendering operations.
173*
174* @example
175* ```ts
176* // After video export, resume normal time
177* setNow(0);
178* // ... export frames ...
179* restoreNow(); // Map animations resume normally
180* ```
181*/
182function restoreNow() {
183	timeManager.restoreNow();
184}
185/**
186* Returns whether time is currently frozen.
187* @returns True if time is frozen via setNow(), false otherwise
188* @example
189* ```ts
190* setNow(1000);
191* console.log(isTimeFrozen()); // true
192* restoreNow();
193* console.log(isTimeFrozen()); // false
194* ```
195*/
196function isTimeFrozen() {
197	return timeManager.isFrozen();
198}
199//#endregion
200//#region src/util/dom.ts
201var DOM = class DOM {
202	static {
203		this.docStyle = typeof window !== "undefined" && window.document?.documentElement.style;
204	}
205	static {
206		this.selectProp = !DOM.docStyle || "userSelect" in DOM.docStyle ? "userSelect" : "webkitUserSelect";
207	}
208	static create(tagName, className, container) {
209		const el = window.document.createElement(tagName);
210		if (className !== void 0) el.className = className;
211		if (container) container.appendChild(el);
212		return el;
213	}
214	static createNS(namespaceURI, tagName) {
215		return window.document.createElementNS(namespaceURI, tagName);
216	}
217	static disableDrag() {
218		if (DOM.docStyle && DOM.selectProp) {
219			DOM.userSelect = DOM.docStyle[DOM.selectProp];
220			DOM.docStyle[DOM.selectProp] = "none";
221		}
222	}
223	static enableDrag() {
224		if (DOM.docStyle && DOM.selectProp) DOM.docStyle[DOM.selectProp] = DOM.userSelect;
225	}
226	static suppressClickInternal(e) {
227		e.preventDefault();
228		e.stopPropagation();
229		window.removeEventListener("click", DOM.suppressClickInternal, true);
230	}
231	static suppressClick() {
232		window.addEventListener("click", DOM.suppressClickInternal, true);
233		window.setTimeout(() => {
234			window.removeEventListener("click", DOM.suppressClickInternal, true);
235		}, 0);
236	}
237	static getScale(element) {
238		const rect = element.getBoundingClientRect();
239		return {
240			x: rect.width / element.offsetWidth || 1,
241			y: rect.height / element.offsetHeight || 1,
242			boundingClientRect: rect
243		};
244	}
245	static getPoint(el, scale, e) {
246		const rect = scale.boundingClientRect;
247		return new Point((e.clientX - rect.left) / scale.x - el.clientLeft, (e.clientY - rect.top) / scale.y - el.clientTop);
248	}
249	static mousePos(el, e) {
250		const scale = DOM.getScale(el);
251		return DOM.getPoint(el, scale, e);
252	}
253	static touchPos(el, touches) {
254		const points = [];
255		const scale = DOM.getScale(el);
256		for (const touch of touches) points.push(DOM.getPoint(el, scale, touch));
257		return points;
258	}
259	/**
260	* Sanitize an HTML string - this might not be enough to prevent all XSS attacks
261	* Base on https://javascriptsource.com/sanitize-an-html-string-to-reduce-the-risk-of-xss-attacks/
262	* (c) 2021 Chris Ferdinandi, MIT License, https://gomakethings.com
263	*
264	* Returns the sanitized nodes rather than a string, since re-parsing serialized HTML can produce a different tree.
265	*/
266	static sanitize(str) {
267		const html = new DOMParser().parseFromString(str, "text/html").body || document.createElement("body");
268		const dangerousElements = html.querySelectorAll("script, iframe");
269		for (const element of dangerousElements) element.remove();
270		DOM.clean(html);
271		const fragment = document.createDocumentFragment();
272		fragment.append(...html.childNodes);
273		return fragment;
274	}
275	/**
276	* Check if the attribute is potentially dangerous
277	*/
278	static isPossiblyDangerous(name, value) {
279		const val = value.replace(/\s+/g, "").toLowerCase();
280		if ([
281			"src",
282			"href",
283			"xlink:href"
284		].includes(name)) {
285			if (val.includes("javascript:") || val.includes("data:")) return true;
286		}
287		if (name === "srcdoc") return true;
288		if (name.startsWith("on")) return true;
289	}
290	/**
291	* Remove dangerous stuff from the HTML document's nodes
292	* @param html - The HTML document
293	*/
294	static clean(html) {
295		const nodes = html.children;
296		for (const node of nodes) {
297			DOM.removeAttributes(node);
298			DOM.clean(node);
299		}
300	}
301	/**
302	* Remove potentially dangerous attributes from an element
303	* @param elem - The element
304	*/
305	static removeAttributes(elem) {
306		for (const { name, value } of Array.from(elem.attributes)) {
307			if (!DOM.isPossiblyDangerous(name, value)) continue;
308			elem.removeAttribute(name)
vendor: 40,366 bytes, lines 308-1526
308;
309		}
310	}
311};
312//#endregion
313//#region src/util/image_request.ts
314let ImageRequest;
315(function(_ImageRequest) {
316	let imageRequestQueue;
317	let currentParallelImageRequests;
318	let throttleControlCallbackHandleCounter;
319	let throttleControlCallbacks;
320	_ImageRequest.resetRequestQueue = () => {
321		imageRequestQueue = [];
322		currentParallelImageRequests = 0;
323		throttleControlCallbackHandleCounter = 0;
324		throttleControlCallbacks = {};
325	};
326	_ImageRequest.addThrottleControl = (callback) => {
327		const handle = throttleControlCallbackHandleCounter++;
328		throttleControlCallbacks[handle] = callback;
329		return handle;
330	};
331	_ImageRequest.removeThrottleControl = (callbackHandle) => {
332		delete throttleControlCallbacks[callbackHandle];
333		processQueue();
334	};
335	/**
336	* Check to see if any of the installed callbacks are requesting the queue
337	* to be throttled.
338	* @returns `true` if any callback is causing the queue to be throttled.
339	*/
340	const isThrottled = () => {
341		for (const key of Object.keys(throttleControlCallbacks)) if (throttleControlCallbacks[key]()) return true;
342		return false;
343	};
344	async function transformAndGetImage(requestManager, url, resourceType, abortController, supportImageRefresh = true, imageBitmapOptions) {
345		const requestParameters = await requestManager.transformRequest(url, resourceType);
346		throwIfAborted(abortController.signal);
347		return ImageRequest.getImage(requestParameters, abortController, supportImageRefresh, imageBitmapOptions);
348	}
349	_ImageRequest.transformAndGetImage = transformAndGetImage;
350	_ImageRequest.getImage = (requestParameters, abortController, supportImageRefresh = true, imageBitmapOptions) => {
351		return new Promise((resolve, reject) => {
352			requestParameters.headers ||= {};
353			requestParameters.headers.accept = "image/webp,*/*";
354			extend(requestParameters, { type: "image" });
355			const request = {
356				abortController,
357				requestParameters,
358				supportImageRefresh,
359				imageBitmapOptions,
360				state: "queued",
361				onError: (error) => {
362					reject(error);
363				},
364				onSuccess: (response) => {
365					resolve(response);
366				}
367			};
368			imageRequestQueue.push(request);
369			processQueue();
370		});
371	};
372	const arrayBufferToCanvasImageSource = (data, imageBitmapOptions) => {
373		if (typeof createImageBitmap === "function") return arrayBufferToImageBitmap(data, imageBitmapOptions);
374		else return arrayBufferToImage(data);
375	};
376	/**
377	* Runs one queued image request.
378	*
379	* An empty response body (e.g. HTTP 204 for a tile without content) resolves with
380	* `data: null`, keeping the expiry headers, so callers can handle the absence
381	* explicitly and re-request when the headers say so.
382	*/
383	const doImageRequest = async (itemInQueue) => {
384		itemInQueue.state = "running";
385		const { requestParameters, supportImageRefresh, imageBitmapOptions, onError, onSuccess, abortController } = itemInQueue;
386		const canUseHTMLImageElement = supportImageRefresh === false && !imageBitmapOptions && !isWorker(self) && !getProtocol(requestParameters.url) && (!requestParameters.headers || Object.keys(requestParameters.headers).reduce((acc, item) => acc && item === "accept", true));
387		currentParallelImageRequests++;
388		const getImagePromise = canUseHTMLImageElement ? getImageUsingHtmlImage(requestParameters, abortController) : makeRequest(requestParameters, abortController);
389		try {
390			const response = await getImagePromise;
391			delete itemInQueue.abortController;
392			itemInQueue.state = "completed";
393			if (response.data instanceof HTMLImageElement || isImageBitmap(response.data)) onSuccess(response);
394			else if (!response.data || response.data.byteLength === 0) onSuccess({
395				data: null,
396				cacheControl: response.cacheControl,
397				expires: response.expires
398			});
399			else onSuccess({
400				data: await arrayBufferToCanvasImageSource(response.data, imageBitmapOptions),
401				cacheControl: response.cacheControl,
402				expires: response.expires
403			});
404		} catch (err) {
405			delete itemInQueue.abortController;
406			onError(ensureError(err));
407		} finally {
408			currentParallelImageRequests--;
409			processQueue();
410		}
411	};
412	/**
413	* Process some number of items in the image request queue.
414	*/
415	const processQueue = () => {
416		const maxImageRequests = isThrottled() ? config.MAX_PARALLEL_IMAGE_REQUESTS_PER_FRAME : config.MAX_PARALLEL_IMAGE_REQUESTS;
417		for (let numImageRequests = currentParallelImageRequests; numImageRequests < maxImageRequests && imageRequestQueue.length > 0; numImageRequests++) {
418			const topItemInQueue = imageRequestQueue.shift();
419			if (topItemInQueue.abortController.signal.aborted) {
420				numImageRequests--;
421				continue;
422			}
423			doImageRequest(topItemInQueue);
424		}
425	};
426	const getImageUsingHtmlImage = (requestParameters, abortController) => {
427		return new Promise((resolve, reject) => {
428			const image = new Image();
429			const url = requestParameters.url;
430			const credentials = requestParameters.credentials;
431			if (credentials && credentials === "include") image.crossOrigin = "use-credentials";
432			else if (credentials && credentials === "same-origin" || !sameOrigin(url)) image.crossOrigin = "anonymous";
433			abortController.signal.addEventListener("abort", () => {
434				image.src = "";
435				reject(new AbortError(abortController.signal.reason));
436			});
437			image.fetchPriority = "high";
438			image.onload = () => {
439				image.onerror = image.onload = null;
440				resolve({ data: image });
441			};
442			image.onerror = () => {
443				image.onerror = image.onload = null;
444				if (abortController.signal.aborted) return;
445				reject(/* @__PURE__ */ new Error("Could not load image. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported."));
446			};
447			image.src = url;
448		});
449	};
450})(ImageRequest || (ImageRequest = {}));
451ImageRequest.resetRequestQueue();
452var RequestManager = class {
453	constructor(transformRequestFn) {
454		this._transformRequestFn = transformRequestFn ?? null;
455	}
456	transformRequest(url, type) {
457		if (this._transformRequestFn) return this._transformRequestFn(url, type) || { url };
458		return { url };
459	}
460	setTransformRequest(transformRequest) {
461		this._transformRequestFn = transformRequest;
462	}
463};
464//#endregion
465//#region src/ui/events.ts
466/**
467* The base event for MapLibre
468*
469* @group Event Related
470*/
471var MapLibreEvent = class extends Event {};
472/**
473* `MapMovementEvent` is the event type for the camera-transition map events:
474* `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `rotatestart`, `rotate`, `rotateend`,
475* `dragstart`, `drag`, `dragend`, `pitchstart`, `pitch`, `pitchend`, `rollstart`, `roll` and `rollend`.
476* These are fired as the map's view changes, as a result of either user interaction or methods such
477* as {@link Map.jumpTo} / {@link Map.flyTo}.
478*
479* @group Event Related
480*/
481var MapMovementEvent = class extends MapLibreEvent {};
482/**
483* The `style.load` event, fired once the map's style has fully loaded or changed.
484*
485* @group Event Related
486*/
487var MapStyleLoadEvent = class extends MapLibreEvent {
488	constructor(data = {}) {
489		super("style.load", data);
490	}
491};
492/**
493* The style data event
494*
495* @group Event Related
496*/
497var MapStyleDataEvent = class extends MapLibreEvent {
498	constructor(type, data = {}) {
499		super(type, data);
500		this.dataType = "style";
501	}
502};
503/**
504* A `MapSourceDataEvent` is emitted with the source-related `data`, `dataloading`, `dataabort`,
505* `sourcedata`, `sourcedataloading` and `sourcedataabort` events. Its `dataType` is always `'source'`.
506*
507* Possible values for `sourceDataType`s are:
508*
509* - `'metadata'`: indicates that any necessary source metadata has been loaded (such as TileJSON) and it is ok to start loading tiles
510* - `'content'`: indicates the source data has changed (such as when source.setData() has been called on GeoJSONSource)
511* - `'visibility'`: send when the source becomes used when at least one of its layers becomes visible in style sense (inside the layer's zoom range and with layout.visibility set to 'visible')
512* - `'idle'`: indicates that no new source data has been fetched (but the source has done loading)
513*
514* @group Event Related
515*
516* @example
517* ```ts
518* // The sourcedata event is an example of a MapSourceDataEvent.
519* // Set up an event listener on the map.
520* map.on('sourcedata', (e) => {
521*    if (e.isSourceLoaded) {
522*        // Do something when the source has finished loading
523*    }
524* });
525* ```
526*/
527var MapSourceDataEvent = class extends MapLibreEvent {
528	constructor(type, data = {}) {
529		super(type, data);
530		this.dataType = "source";
531	}
532};
533/**
534* `MapMouseEvent` is the event type for mouse-related map events.
535*
536* @group Event Related
537*
538* @example
539* ```ts
540* // The `click` event is an example of a `MapMouseEvent`.
541* // Set up an event listener on the map.
542* map.on('click', (e) => {
543*   // The event object (e) contains information like the
544*   // coordinates of the point on the map that was clicked.
545*   console.log('A click event has occurred at ' + e.lngLat);
546* });
547* ```
548*/
549var MapMouseEvent = class extends MapLibreEvent {
550	/**
551	* Prevents subsequent default processing of the event by the map.
552	*
553	* Calling this method will prevent the following default map behaviors:
554	*
555	*   * On `mousedown` events, the behavior of {@link DragPanHandler}
556	*   * On `mousedown` events, the behavior of {@link DragRotateHandler}
557	*   * On `mousedown` events, the behavior of {@link BoxZoomHandler}
558	*   * On `dblclick` events, the behavior of {@link DoubleClickZoomHandler}
559	*
560	*/
561	preventDefault() {
562		this._defaultPrevented = true;
563	}
564	/**
565	* `true` if `preventDefault` has been called.
566	*/
567	get defaultPrevented() {
568		return this._defaultPrevented;
569	}
570	constructor(type, map, originalEvent, data = {}) {
571		originalEvent = originalEvent instanceof MouseEvent ? originalEvent : new MouseEvent(type, originalEvent);
572		const point = DOM.mousePos(map.getCanvas(), originalEvent);
573		const lngLat = map.unproject(point);
574		super(type, extend({
575			point,
576			lngLat,
577			originalEvent
578		}, data));
579		this._defaultPrevented = false;
580		this.target = map;
581	}
582};
583/**
584* `MapTouchEvent` is the event type for touch-related map events.
585*
586* @group Event Related
587*/
588var MapTouchEvent = class extends MapLibreEvent {
589	/**
590	* Prevents subsequent default processing of the event by the map.
591	*
592	* Calling this method will prevent the following default map behaviors:
593	*
594	*   * On `touchstart` events, the behavior of {@link DragPanHandler}
595	*   * On `touchstart` events, the behavior of {@link TwoFingersTouchZoomRotateHandler}
596	*
597	*/
598	preventDefault() {
599		this._defaultPrevented = true;
600	}
601	/**
602	* `true` if `preventDefault` has been called.
603	*/
604	get defaultPrevented() {
605		return this._defaultPrevented;
606	}
607	constructor(type, map, originalEvent) {
608		const touches = type === "touchend" ? originalEvent.changedTouches : originalEvent.touches;
609		const points = DOM.touchPos(map.getCanvasContainer(), touches);
610		const lngLats = points.map((t) => map.unproject(t));
611		const point = points.reduce((prev, curr, i, arr) => {
612			return prev.add(curr.div(arr.length));
613		}, new Point(0, 0));
614		const lngLat = map.unproject(point);
615		super(type, {
616			points,
617			point,
618			lngLats,
619			lngLat,
620			originalEvent
621		});
622		this._defaultPrevented = false;
623	}
624};
625/**
626* `MapWheelEvent` is the event type for the `wheel` map event.
627*
628* @group Event Related
629*/
630var MapWheelEvent = class extends MapLibreEvent {
631	/**
632	* Prevents subsequent default processing of the event by the map.
633	*
634	* Calling this method will prevent the behavior of {@link ScrollZoomHandler}.
635	*/
636	preventDefault() {
637		this._defaultPrevented = true;
638	}
639	/**
640	* `true` if `preventDefault` has been called.
641	*/
642	get defaultPrevented() {
643		return this._defaultPrevented;
644	}
645	/** */
646	constructor(map, originalEvent) {
647		super("wheel", { originalEvent });
648		this._defaultPrevented = false;
649	}
650};
651/**
652* A `MapBoxZoomEvent` is the event type for the boxzoom-related map events emitted by the {@link BoxZoomHandler}.
653*
654* @group Event Related
655*/
656var MapBoxZoomEvent = class extends MapLibreEvent {};
657/**
658* The terrain event
659*
660* @group Event Related
661*/
662var MapTerrainEvent = class extends MapLibreEvent {
663	constructor(data = {}) {
664		super("terrain", data);
665	}
666};
667/**
668* The map projection event
669*
670* @group Event Related
671*/
672var MapProjectionEvent = class extends MapLibreEvent {
673	constructor(data = {}) {
674		super("projectiontransition", data);
675	}
676};
677/**
678* An event related to the web gl context
679*
680* @group Event Related
681*/
682var MapContextEvent = class extends MapLibreEvent {};
683/**
684* The style image missing event, fired when an image is still missing after the missing style image
685* resolver has been given a chance to supply it. To load or generate images on demand,
686* use {@link Map.setMissingStyleImageResolver}.
687* Event listeners cannot resolve the missing image for the current request.
688*
689* @group Event Related
690*
691* @see [Generate and add a missing icon to the map](https://maplibre.org/maplibre-gl-js/docs/examples/generate-and-add-a-missing-icon-to-the-map/)
692*/
693var MapStyleImageMissingEvent = class extends MapLibreEvent {
694	constructor(data = {}) {
695		super("styleimagemissing", data);
696	}
697};
698//#endregion
699//#region src/util/style.ts
700/**
701* Takes a SpriteSpecification value and returns it in its array form. If `undefined` is passed as an input value, an
702* empty array is returned.
703* duplicated entries with identical id/url will be removed in returned array
704* @param sprite - optional sprite to coerce
705* @returns an empty array in case `undefined` is passed; id-url pairs otherwise
706*/
707function coerceSpriteToArray(sprite) {
708	const resultArray = [];
709	if (typeof sprite === "string") resultArray.push({
710		id: "default",
711		url: sprite
712	});
713	else if (sprite && sprite.length > 0) {
714		const dedupArray = [];
715		for (const { id, url } of sprite) {
716			const key = `${id}${url}`;
717			if (!dedupArray.includes(key)) {
718				dedupArray.push(key);
719				resultArray.push({
720					id,
721					url
722				});
723			}
724		}
725	}
726	return resultArray;
727}
728//#endregion
729//#region src/style/load_sprite.ts
730function normalizeSpriteURL(url, format, extension) {
731	try {
732		const parsed = new URL(url);
733		parsed.pathname += `${format}${extension}`;
734		return parsed.toString();
735	} catch {
736		throw new Error(`Invalid sprite URL "${url}", must be absolute. Modify style specification directly or use TransformStyleFunction to correct the issue dynamically`);
737	}
738}
739async function loadSprite(originalSprite, requestManager, pixelRatio, abortController) {
740	const spriteArray = coerceSpriteToArray(originalSprite);
741	const format = pixelRatio > 1 ? "@2x" : "";
742	const jsonsMap = {};
743	const imagesMap = {};
744	for (const { id, url } of spriteArray) {
745		const jsonRequestParameters = await requestManager.transformRequest(normalizeSpriteURL(url, format, ".json"), "SpriteJSON");
746		jsonsMap[id] = getJSON(jsonRequestParameters, abortController);
747		const imageRequestParameters = await requestManager.transformRequest(normalizeSpriteURL(url, format, ".png"), "SpriteImage");
748		imagesMap[id] = ImageRequest.getImage(imageRequestParameters, abortController);
749	}
750	await Promise.all([...Object.values(jsonsMap), ...Object.values(imagesMap)]);
751	return doOnceCompleted(jsonsMap, imagesMap);
752}
753/**
754* @param jsonsMap - JSON data map
755* @param imagesMap - image data map
756*/
757async function doOnceCompleted(jsonsMap, imagesMap) {
758	const result = {};
759	for (const spriteName in jsonsMap) {
760		result[spriteName] = {};
761		const image = (await imagesMap[spriteName]).data;
762		if (!image) throw new Error(`Could not load sprite image for ${spriteName}: the response is empty`);
763		const context = browser.getImageCanvasContext(image);
764		const json = (await jsonsMap[spriteName]).data;
765		for (const id in json) {
766			const { width, height, x, y, sdf, pixelRatio, stretchX, stretchY, content, textFitWidth, textFitHeight } = json[id];
767			const spriteData = {
768				width,
769				height,
770				x,
771				y,
772				context
773			};
774			result[spriteName][id] = {
775				data: null,
776				pixelRatio,
777				sdf,
778				stretchX,
779				stretchY,
780				content,
781				textFitWidth,
782				textFitHeight,
783				spriteData
784			};
785		}
786	}
787	return result;
788}
789//#endregion
790//#region src/render/image_manager.ts
791/**
792* Owns every image of the style - the ones the sprites bring in as well as the ones added at
793* runtime - and tracks requests for them from the tile workers, sending responses when the
794* requests are fulfilled.
795*/
796var ImageManager = class extends Evented {
797	constructor() {
798		super();
799		this.images = {};
800		this.updateVersion = 0;
801		this.loaded = false;
802		this.requestors = [];
803		this.missingImageResolver = null;
804		this._spriteImagesIds = {};
805		this._imagesIds = null;
806		this._renderCallbacksDispatchedThisFrame = {};
807	}
808	destroy() {
809		for (const id of Object.keys(this.images)) this.removeImage(id);
810		this._spriteImagesIds = {};
811	}
812	isLoaded() {
813		return this.loaded;
814	}
815	setLoaded(loaded) {
816		if (this.loaded === loaded) return;
817		this.loaded = loaded;
818		if (loaded) {
819			for (const { ids, promiseResolve } of this.requestors) promiseResolve(this._getImagesForIds(ids));
820			this.requestors = [];
821		}
822	}
823	getImage(id) {
824		const image = this.images[id];
825		if (image && !image.data && image.spriteData) {
826			const spriteData = image.spriteData;
827			image.data = new RGBAImage({
828				width: spriteData.width,
829				height: spriteData.height
830			}, spriteData.context.getImageData(spriteData.x, spriteData.y, spriteData.width, spriteData.height).data);
831			image.spriteData = null;
832		}
833		return image;
834	}
835	addImage(id, image) {
836		if (this.images[id]) throw new Error(`Image id ${id} already exist, use updateImage instead`);
837		if (this._validate(id, image)) {
838			this.images[id] = image;
839			this._imagesIds = null;
840			if (image.isWebGLImage) this.updateImage(id, image, false);
841		}
842	}
843	_validate(id, image) {
844		let valid = true;
845		const data = image.data || image.spriteData;
846		if (!this._validateStretch(image.stretchX, data?.width)) {
847			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Image "${id}" has invalid "stretchX" value`)));
848			valid = false;
849		}
850		if (!this._validateStretch(image.stretchY, data?.height)) {
851			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Image "${id}" has invalid "stretchY" value`)));
852			valid = false;
853		}
854		if (!this._validateContent(image.content, image)) {
855			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Image "${id}" has invalid "content" value`)));
856			valid = false;
857		}
858		return valid;
859	}
860	_validateStretch(stretch, size) {
861		if (!stretch) return true;
862		let last = 0;
863		for (const part of stretch) {
864			if (part[0] < last || part[1] < part[0] || size < part[1]) return false;
865			last = part[1];
866		}
867		return true;
868	}
869	_validateContent(content, image) {
870		if (!content) return true;
871		if (content.length !== 4) return false;
872		const spriteData = image.spriteData;
873		const width = spriteData?.width || image.data.width;
874		const height = spriteData?.height || image.data.height;
875		if (content[0] < 0 || width < content[0]) return false;
876		if (content[1] < 0 || height < content[1]) return false;
877		if (content[2] < 0 || width < content[2]) return false;
878		if (content[3] < 0 || height < content[3]) return false;
879		if (content[2] < content[0]) return false;
880		return content[3] >= content[1];
881	}
882	/**
883	* Replaces an image that is already known under this id, bumping its version so that the
884	* atlases holding it notice.
885	*
886	* The old record is read directly rather than through {@link ImageManager.getImage}, which
887	* would force a synchronous decode of the sprite data of the very image being replaced. Its
888	* size therefore comes from whichever of the two the pixels are still in, since an image that
889	* came from a sprite and was never rendered has not been decoded yet, and its version is
890	* defaulted, since images start out without one.
891	*
892	* @param validate - whether to reject an image of a different size than the one it replaces
893	*/
894	updateImage(id, image, validate = true) {
895		const oldImage = this.images[id];
896		if (validate) {
897			const oldData = oldImage.data || oldImage.spriteData;
898			if (oldData.width !== image.data.width || oldData.height !== image.data.height) throw new Error(`size mismatch between old image (${oldData.width}x${oldData.height}) and new image (${image.data.width}x${image.data.height}).`);
899		}
900		image.version = (oldImage.version ?? 0) + 1;
901		this.images[id] = image;
902		this.updateVersion++;
903	}
904	removeImage(id) {
905		const image = this.images[id];
906		if (!image) return;
907		delete this.images[id];
908		this._imagesIds = null;
909		if (image.userImage?.onRemove) image.userImage.onRemove();
910	}
911	/**
912	* @returns the ids of every image currently held, sprite images and runtime ones alike. The
913	* returned array is shared between callers and must not be modified.
914	*/
915	listImages() {
916		this._imagesIds ??= Object.keys(this.images);
917		return this._imagesIds;
918	}
919	/**
920	* Images of the `default` sprite keep their plain id, the ones of any other sprite are
921	* namespaced by their sprite's id.
922	*/
923	_getSpriteImageId(spriteId, imageId) {
924		return spriteId === "default" ? imageId : `${spriteId}:${imageId}`;
925	}
926	/**
927	* Takes over the images of a single sprite: adds the new ones, updates the ones that are
928	* already known, and removes the ones that a previous load of this same sprite had brought in
929	* but that are not part of it anymore.
930	*
931	* @param spriteId - the id of the sprite the images belong to
932	* @param images - the sprite's images, keyed by their id within the sprite
933	* @returns the ids of the images that this sprite now provides, and the ids of the ones that
934	* were removed because they are no longer part of it
935	*/
936	setSpriteImages(spriteId, images) {
937		const previousIds = this._spriteImagesIds[spriteId] ?? [];
938		const loaded = [];
939		for (const id in images) {
940			const imageId = this._getSpriteImageId(spriteId, id);
941			loaded.push(imageId);
942			if (imageId in this.images) this.updateImage(imageId, images[id], false);
943			else this.addImage(imageId, images[id]);
944		}
945		const loadedIds = new Set(loaded);
946		const removed = previousIds.filter((imageId) => !loadedIds.has(imageId));
947		for (const imageId of removed) this.removeImage(imageId);
948		this._spriteImagesIds[spriteId] = loaded;
949		return {
950			loaded,
951			removed
952		};
953	}
954	/**
955	* Removes all the images a single sprite has brought in.
956	* @returns the ids of the removed images
957	*/
958	removeSpriteImages(spriteId) {
959		const removed = this._spriteImagesIds[spriteId] ?? [];
960		for (const imageId of removed) this.removeImage(imageId);
961		delete this._spriteImagesIds[spriteId];
962		return removed;
963	}
964	/**
965	* Removes the images of every sprite, leaving the images added at runtime alone.
966	* @returns the ids of the removed images
967	*/
968	removeAllSpriteImages() {
969		const removed = Object.values(this._spriteImagesIds).flat();
970		for (const imageId of removed) this.removeImage(imageId);
971		this._spriteImagesIds = {};
972		return removed;
973	}
974	setMissingImageResolver(resolver) {
975		this.missingImageResolver = resolver;
976	}
977	getImages(ids) {
978		return new Promise((resolve, _reject) => {
979			let hasAllDependencies = true;
980			if (!this.isLoaded()) {
981				for (const id of ids) if (!this.images[id]) hasAllDependencies = false;
982			}
983			if (this.isLoaded() || hasAllDependencies) resolve(this._getImagesForIds(ids));
984			else this.requestors.push({
985				ids,
986				promiseResolve: resolve
987			});
988		});
989	}
990	async _getImagesForIds(ids) {
991		const unresolvedIds = new Set(ids.filter((id) => !this.getImage(id)));
992		const resolver = this.missingImageResolver;
993		if (resolver) await Promise.allSettled(Array.from(unresolvedIds, (id) => resolver(id)));
994		const response = {};
995		for (const id of ids) {
996			const image = this.getImage(id);
997			if (image) {
998				unresolvedIds.delete(id);
999				response[id] = {
1000					data: image.data.clone(),
1001					pixelRatio: image.pixelRatio,
1002					sdf: image.sdf,
1003					version: image.version,
1004					stretchX: image.stretchX,
1005					stretchY: image.stretchY,
1006					content: image.content,
1007					textFitWidth: image.textFitWidth,
1008					textFitHeight: image.textFitHeight,
1009					hasRenderCallback: Boolean(image.userImage?.render),
1010					isWebGLImage: image.isWebGLImage
1011				};
1012			}
1013		}
1014		for (const id of unresolvedIds) {
1015			this.fire(new MapStyleImageMissingEvent({ id }));
1016			warnOnce(`Image "${id}" could not be loaded. Please make sure you have added the image before it is needed with map.addImage(), resolved it with map.setMissingStyleImageResolver(), or included it in a "sprite" property in your style.`);
1017		}
1018		return response;
1019	}
1020	beginFrame() {
1021		this._renderCallbacksDispatchedThisFrame = {};
1022	}
1023	/**
1024	* Re-renders the images among `ids` that were added with a `render` callback (see
1025	* `StyleImageInterface`), at most once per frame each.
1026	*/
1027	dispatchRenderCallbacks(ids) {
1028		for (const id of ids) {
1029			if (this._renderCallbacksDispatchedThisFrame[id]) continue;
1030			this._renderCallbacksDispatchedThisFrame[id] = true;
1031			const image = this.getImage(id);
1032			if (!image) warnOnce(`Image with ID: "${id}" was not found`);
1033			if (renderStyleImage(image)) this.updateImage(id, image);
1034		}
1035	}
1036	cloneImages() {
1037		const clonedImages = {};
1038		for (const id in this.images) {
1039			const image = this.images[id];
1040			clonedImages[id] = {
1041				...image,
1042				data: image.data ? image.data.clone() : null
1043			};
1044		}
1045		return clonedImages;
1046	}
1047};
1048//#endregion
1049//#region src/render/pattern_atlas.ts
1050/**
1051* When copied into the atlas texture, pattern images are padded by one pixel on each side with a
1052* copy of the image data wrapped from the opposite side. This ensures the correct behavior of
1053* GL_LINEAR texture sampling mode.
1054*/
1055const PADDING = 1;
1056/**
1057* A texture atlas of the pattern images that are drawn without a tile's own {@link ImageAtlas},
1058* i.e. `background-pattern`. Entries are packed on demand and re-packed whenever one of them is
1059* added or changes, which is rare enough for the repacking cost not to matter.
1060*/
1061var PatternAtlas = class {
1062	constructor(imageManager) {
1063		this._imageManager = imageManager;
1064		this._entries = {};
1065		this._image = new RGBAImage({
1066			width: 1,
1067			height: 1
1068		});
1069		this._dirty = true;
1070	}
1071	destroy() {
1072		if (this._texture) {
1073			this._texture.destroy();
1074			this._texture = null;
1075		}
1076		this._entries = {};
1077		this._image = new RGBAImage({
1078			width: 1,
1079			height: 1
1080		});
1081		this._dirty = true;
1082	}
1083	getPixelSize() {
1084		const { width, height } = this._image;
1085		return {
1086			width,
1087			height
1088		};
1089	}
1090	/**
1091	* @returns the position of the pattern within the atlas, or `null` if there is no such image
1092	*/
1093	getPattern(id) {
1094		const image = this._imageManager.getImage(id);
1095		if (!image) return null;
1096		const entry = this._entries[id];
1097		if (entry?.image !== image) {
1098			const bin = {
1099				w: image.data.width + 2,
1100				h: image.data.height + 2,
1101				x: 0,
1102				y: 0
1103			};
1104			this._entries[id] = {
1105				bin,
1106				position: new ImagePosition(bin, image),
1107				image
1108			};
1109		} else if (entry.position.version !== image.version) entry.position.version = image.version;
1110		else return entry.position;
1111		this._update();
1112		return this._entries[id].position;
1113	}
1114	bind(context) {
1115		const gl = context.gl;
1116		if (!this._texture) {
1117			this._texture = new Texture(context, this._image, gl.RGBA);
1118			this._dirty = false;
1119		} else if (this._dirty) {
1120			this._texture.update(this._image);
1121			this._dirty = false;
1122		}
1123		this._texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
1124	}
1125	_update() {
1126		for (const id in this._entries) if (!this._imageManager.getImage(id)) delete this._entries[id];
1127		const bins = [];
1128		for (const id in this._entries) bins.push(this._entries[id].bin);
1129		const { w, h } = potpack(bins);
1130		const dst = this._image;
1131		dst.resize({
1132			width: w || 1,
1133			height: h || 1
1134		});
1135		for (const id in this._entries) {
1136			const { bin } = this._entries[id];
1137			const x = bin.x + PADDING;
1138			const y = bin.y + PADDING;
1139			const src = this._entries[id].image.data;
1140			const w = src.width;
1141			const h = src.height;
1142			RGBAImage.copy(src, dst, {
1143				x: 0,
1144				y: 0
1145			}, {
1146				x,
1147				y
1148			}, {
1149				width: w,
1150				height: h
1151			});
1152			RGBAImage.copy(src, dst, {
1153				x: 0,
1154				y: h - 1
1155			}, {
1156				x,
1157				y: y - 1
1158			}, {
1159				width: w,
1160				height: 1
1161			});
1162			RGBAImage.copy(src, dst, {
1163				x: 0,
1164				y: 0
1165			}, {
1166				x,
1167				y: y + h
1168			}, {
1169				width: w,
1170				height: 1
1171			});
1172			RGBAImage.copy(src, dst, {
1173				x: w - 1,
1174				y: 0
1175			}, {
1176				x: x - 1,
1177				y
1178			}, {
1179				width: 1,
1180				height: h
1181			});
1182			RGBAImage.copy(src, dst, {
1183				x: 0,
1184				y: 0
1185			}, {
1186				x: x + w,
1187				y
1188			}, {
1189				width: 1,
1190				height: h
1191			});
1192		}
1193		this._dirty = true;
1194	}
1195};
1196//#endregion
1197//#region src/render/font_face_manager.ts
1198const MAX_CODE_POINT = 1114111;
1199/**
1200* What a font file covers when the style does not narrow it down with `unicode-range`.
1201*/
1202const DEFAULT_UNICODE_RANGE = {
1203	start: 0,
1204	end: MAX_CODE_POINT
1205};
1206/**
1207* Makes the family names unique across every map on the page, since `document.fonts` is shared.
1208*/
1209let nextFamilyId = 0;
1210/**
1211* Parses one `unicode-range` entry, in the CSS grammar the style specification borrows: `U+A5`,
1212* `U+0-10FFFF` or `U+4??`.
1213* @param value - a single `unicode-range` entry
1214* @returns the codepoints it covers, or `null` if it is not a range this can make sense of
1215*/
1216function parseUnicodeRange(value) {
1217	const wildcard = /^u\+([0-9a-f]*)(\?+)$/i.exec(value);
1218	if (wildcard) {
1219		const [, prefix, questionMarks] = wildcard;
1220		if (prefix.length + questionMarks.length > 6) return null;
1221		return clampRange(parseInt(`${prefix}${"0".repeat(questionMarks.length)}`, 16), parseInt(`${prefix}${"f".repeat(questionMarks.length)}`, 16));
1222	}
1223	const explicit = /^u\+([0-9a-f]{1,6})(?:-([0-9a-f]{1,6}))?$/i.exec(value);
1224	if (!explicit) return null;
1225	const start = parseInt(explicit[1], 16);
1226	return clampRange(start, explicit[2] === void 0 ? start : parseInt(explicit[2], 16));
1227}
1228function clampRange(start, end) {
1229	if (start > end || start > MAX_CODE_POINT) return null;
1230	return {
1231		start,
1232		end: Math.min(end, MAX_CODE_POINT)
1233	};
1234}
1235/**
1236* Whether the style lets this file cover the given codepoint.
1237*/
1238function covers(face, codePoint) {
1239	return face.ranges.some(({ start, end }) => codePoint >= start && codePoint <= end);
1240}
1241/**
1242* Keeps the font files a style declares in its
1243* [`font-faces`](https://maplibre.org/maplibre-style-spec/root/#font-faces) property, and hands the
1244* {@link GlyphManager} the CSS family to draw a given codepoint with.
1245*
1246* The files go to the browser's CSS Font Loading API, so any format it can render text with works.
1247* Each is registered under a family name of our own making, so that a style cannot restyle the
1248* surrounding page and a codepoint can be pinned to one file rather than to whatever font matching
1249* picks.
1250*
1251* Rasterizing happens a grapheme cluster at a time, so a letter reaches the browser's text engine
1252* with its marks. Shaping across cluster boundaries still needs the RTL text plugin.
1253*/
1254var FontFaceManager = class {
1255	constructor(requestManager) {
1256		this.requestManager = requestManager;
1257		this._faces = {};
1258		this._registered = /* @__PURE__ */ new Set();
1259	}
1260	/**
1261	* Replaces the declared font faces. Nothing is downloaded here: each file waits until a
1262	* codepoint it covers is actually drawn.
1263	*/
1264	setFontFaces(fontFaces) {
1265		this._unregisterAll();
1266		this._faces = {};
1267		for (const [fontName, declaration] of Object.entries(fontFaces ?? {})) {
1268			const declarations = Array.isArray(declaration) ? declaration : [declaration];
1269			this._faces[fontName] = declarations.map((entry) => this._declareFontFace(fontName, entry)).filter((face) => face !== null);
1270		}
1271	}
1272	/**
1273	* Whether the style declared any font face at all, so that the common case of a style without
1274	* `font-faces` costs nothing.
1275	*/
1276	hasFontFaces() {
1277		return Object.keys(this._faces).length > 0;
1278	}
1279	/**
1280	* Finds the font file to draw a codepoint with: each name of the `text-font` stack in turn, and
1281	* within a name each declared file, until one covers it. A file that fails to load is skipped,
1282	* the specification asking for unsupported fonts to be ignored.
1283	*
1284	* @returns the CSS family to draw with, or `null` to leave the codepoint to the `glyphs` URL
1285	*/
1286	async getFontFamily(fontStack, codePoint) {
1287		for (const fontName of fontStack.split(",")) for (const face of this._faces[fontName.trim()] ?? []) {
1288			if (!covers(face, codePoint)) continue;
1289			face.loaded ??= this._loadFontFace(face);
1290			if (await face.loaded) return face.family;
1291		}
1292		return null;
1293	}
1294	/**
1295	* Turns one declaration into a face to draw with, or `null` if there is nothing usable in it.
1296	*
1297	* A bare URL is the same as a face that names no `unicode-range`: it covers every codepoint.
1298	*/
1299	_declareFontFace(fontName, declaration) {
1300		const face = typeof declaration === "string" ? { url: declaration } : declaration;
1301		if (typeof face?.url !== "string") {
1302			warnOnce(`Ignoring the font face declared for "${fontName}": it has no URL.`);
1303			return null;
1304		}
1305		const family = `maplibre-gl-font-face-${nextFamilyId++}`;
1306		const unicodeRange = face["unicode-range"];
1307		if (!unicodeRange?.length) return {
1308			url: face.url,
1309			ranges: [DEFAULT_UNICODE_RANGE],
1310			family
1311		};
1312		const ranges = [];
1313		for (const entry of unicodeRange) {
1314			const range = parseUnicodeRange(entry);
1315			if (!range) {
1316				warnOnce(`Ignoring the unicode range "${entry}" of the font face at ${face.url}: it is not a valid range.`);
1317				continue;
1318			}
1319			ranges.push(range);
1320		}
1321		if (!ranges.length) return null;
1322		return {
1323			url: face.url,
1324			ranges,
1325			family
1326		};
1327	}
1328	/**
1329	* Downloads a declared file and hands it to the browser, reporting whether it can be drawn with.
1330	* A failure is not an error: its codepoints fall through to the next file, then to `glyphs`.
1331	*/
1332	async _loadFontFace(face) {
1333		if (typeof FontFace === "undefined" || typeof document === "undefined" || !document.fonts) {
1334			warnOnce(`Ignoring the font face at ${face.url}: this environment has no CSS Font Loading API.`);
1335			return false;
1336		}
1337		let fontFace;
1338		try {
1339			fontFace = new FontFace(face.family, await this._downloadFontFile(face.url));
1340			if (!Object.values(this._faces).some((faces) => faces.includes(face))) return false;
1341			document.fonts.add(fontFace);
1342			this._registered.add(fontFace);
1343			await fontFace.load();
1344			return true;
1345		} catch (e) {
1346			if (fontFace) this._unregister(fontFace);
1347			warnOnce(`Ignoring the font face at ${face.url}: ${ensureError(e).message}`);
1348			return false;
1349		}
1350	}
1351	/**
1352	* Downloads a font file, giving the map's `transformRequest` a chance to rewrite the request the
1353	* same way it gets one for a glyph range.
1354	*/
1355	async _downloadFontFile(url) {
1356		const request = await this.requestManager.transformRequest(url, "Glyphs");
1357		const response = await getArrayBuffer(request, new AbortController());
1358		if (!response?.data) throw new Error(`the response was empty for the font file at ${url}`);
1359		return response.data;
1360	}
1361	_unregister(fontFace) {
1362		document.fonts?.delete(fontFace);
1363		this._registered.delete(fontFace);
1364	}
1365	_unregisterAll() {
1366		for (const fontFace of this._registered) document.fonts?.delete(fontFace);
1367		this._registered.clear();
1368	}
1369	destroy() {
1370		this._unregisterAll();
1371		this._faces = {};
1372	}
1373};
1374//#endregion
1375//#region node_modules/@mapbox/tiny-sdf/index.js
1376const INF = 0x56bc75e2d63100000;
1377const alphaTable = /* @__PURE__ */ new Float64Array(256);
1378for (let i = 0; i < 256; i++) {
1379	const d = .5 - Math.pow(i / 255, 1 / 2.2);
1380	alphaTable[i] = d * Math.abs(d);
1381}
1382alphaTable[255] = -0x56bc75e2d63100000;
1383var TinySDF = class {
1384	constructor({ fontSize = 24, buffer = 3, radius = 8, cutoff = .25, fontFamily = "sans-serif", fontWeight = "normal", fontStyle = "normal", lang = null } = {}) {
1385		this.buffer = buffer;
1386		this.radius = radius;
1387		this.cutoff = cutoff;
1388		this.lang = lang;
1389		const size = this.size = fontSize + buffer * 4;
1390		const canvas = this._createCanvas(size);
1391		const ctx = this.ctx = canvas.getContext("2d", { willReadFrequently: true });
1392		ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;
1393		ctx.textBaseline = "alphabetic";
1394		ctx.textAlign = "left";
1395		ctx.fillStyle = "black";
1396		this.gridOuter = new Float64Array(size * size);
1397		this.gridInner = new Float64Array(size * size);
1398		this.f = new Float64Array(size);
1399		this.z = new Float64Array(size + 1);
1400		this.v = new Uint16Array(size);
1401	}
1402	_createCanvas(size) {
1403		if (typeof OffscreenCanvas !== "undefined") return new OffscreenCanvas(size, size);
1404		const canvas = document.createElement("canvas");
1405		canvas.width = canvas.height = size;
1406		return canvas;
1407	}
1408	draw(char) {
1409		const { width: glyphAdvance, actualBoundingBoxAscent, actualBoundingBoxDescent, actualBoundingBoxLeft, actualBoundingBoxRight } = this.ctx.measureText(char);
1410		const glyphTop = Math.ceil(actualBoundingBoxAscent);
1411		const glyphLeft = Math.floor(-actualBoundingBoxLeft);
1412		const glyphWidth = Math.max(0, Math.min(this.size - this.buffer, Math.ceil(actualBoundingBoxRight) - glyphLeft));
1413		const glyphHeight = Math.max(0, Math.min(this.size - this.buffer, glyphTop + Math.ceil(actualBoundingBoxDescent)));
1414		const width = glyphWidth + 2 * this.buffer;
1415		const height = glyphHeight + 2 * this.buffer;
1416		const len = Math.max(width * height, 0);
1417		const data = new Uint8ClampedArray(len);
1418		const glyph = {
1419			data,
1420			width,
1421			height,
1422			glyphWidth,
1423			glyphHeight,
1424			glyphTop,
1425			glyphLeft,
1426			glyphAdvance
1427		};
1428		if (glyphWidth === 0 || glyphHeight === 0) return glyph;
1429		const { ctx, buffer, gridInner, gridOuter } = this;
1430		if (this.lang) ctx.lang = this.lang;
1431		ctx.clearRect(buffer, buffer, glyphWidth, glyphHeight);
1432		ctx.fillText(char, buffer - glyphLeft, buffer + glyphTop);
1433		const imgData = ctx.getImageData(buffer, buffer, glyphWidth, glyphHeight);
1434		gridOuter.fill(INF, 0, len);
1435		gridInner.fill(0, 0, len);
1436		let imgIdx = 3;
1437		for (let y = 0; y < glyphHeight; y++) {
1438			let j = (y + buffer) * width + buffer;
1439			for (let x = 0; x < glyphWidth; x++, imgIdx += 4, j++) {
1440				const a = imgData.data[imgIdx];
1441				if (a === 0) continue;
1442				const t = alphaTable[a];
1443				gridOuter[j] = Math.max(0, t);
1444				gridInner[j] = Math.max(0, -t);
1445			}
1446		}
1447		edt(gridOuter, 0, 0, width, height, width, this.f, this.v, this.z);
1448		const pad = Math.min(buffer, 1);
1449		edt(gridInner, buffer - pad, buffer - pad, glyphWidth + 2 * pad, glyphHeight + 2 * pad, width, this.f, this.v, this.z);
1450		const scale = 255 / this.radius;
1451		const base = 255 * (1 - this.cutoff);
1452		for (let i = 0; i < len; i++) {
1453			const d = Math.sqrt(gridOuter[i]) - Math.sqrt(gridInner[i]);
1454			data[i] = Math.round(base - scale * d);
1455		}
1456		return glyph;
1457	}
1458};
1459function edt(data, x0, y0, width, height, gridSize, f, v, z) {
1460	for (let x = x0; x < x0 + width; x++) edt1d(data, y0 * gridSize + x, gridSize, height, f, v, z);
1461	for (let y = y0; y < y0 + height; y++) edt1d(data, y * gridSize + x0, 1, width, f, v, z);
1462}
1463function edt1d(grid, offset, stride, length, f, v, z) {
1464	v[0] = 0;
1465	z[0] = -0x56bc75e2d63100000;
1466	z[1] = INF;
1467	f[0] = grid[offset];
1468	for (let q = 1, k = 0, s = 0; q < length; q++) {
1469		f[q] = grid[offset + q * stride];
1470		const q2 = q * q;
1471		do {
1472			const r = v[k];
1473			s = (f[q] - f[r] + q2 - r * r) / (q - r) / 2;
1474		} while (s <= z[k] && --k > -1);
1475		k++;
1476		v[k] = q;
1477		z[k] = s;
1478		z[k + 1] = INF;
1479	}
1480	for (let q = 0, k = 0; q < length; q++) {
1481		while (z[k + 1] < q) k++;
1482		const r = v[k];
1483		const qr = q - r;
1484		grid[offset + q * stride] = f[r] + qr * qr;
1485	}
1486}
1487//#endregion
1488//#region src/render/glyph_manager.ts
1489/**
1490* The style specification hard-codes some last resort fonts as a default fontstack.
1491*/
1492const defaultStack = latest.layout_symbol["text-font"].default.join(",");
1493/**
1494* The CSS generic font family closest to `defaultStack`.
1495*/
1496const defaultGenericFontFamily = "sans-serif";
1497/**
1498* Scale factor for client-generated glyphs.
1499*
1500* Client-generated glyphs are rendered at 2× because CJK glyphs are more detailed than others.
1501*/
1502const textureScale = 2;
1503const defaultCreateRasterizer = (options, padding) => {
1504	const tinySDF = new TinySDF(options);
1505	tinySDF.buffer = padding;
1506	return tinySDF;
1507};
1508/**
1509* How wide a grapheme cluster may be drawn, in multiples of the font size.
1510*
1511* TinySDF sizes its canvas for a single character and cuts off the rest, but a cluster is a whole
1512* syllable: Burmese `လား` is nearly twice the font size. Three ems fits the ones that occur.
1513*/
1514const clusterEmsWide = 3;
1515var GlyphManager = class {
1516	constructor(requestManager, localIdeographFontFamily, lang, createRasterizer = defaultCreateRasterizer) {
1517		this.requestManager = requestManager;
1518		this.localIdeographFontFamily = localIdeographFontFamily;
1519		this.entries = {};
1520		this.lang = lang;
1521		this.fontFaceManager = new FontFaceManager(requestManager);
1522		this.createRasterizer = createRasterizer;
1523	}
1524	setURL(url) {
1525		this.url = url;
1526	}
vendor: 19,389 bytes, lines 1527-2066
1527	/**
1528	* Replaces the font files the style declares in its `font-faces` property, dropping every glyph
1529	* drawn with the previous ones.
1530	*/
1531	setFontFaces(fontFaces) {
1532		this.fontFaceManager.setFontFaces(fontFaces);
1533		this.entries = {};
1534	}
1535	async getGlyphs(glyphs) {
1536		const glyphsPromises = [];
1537		for (const stack in glyphs) for (const id of glyphs[stack]) glyphsPromises.push(this._getAndCacheGlyphsPromise(stack, id));
1538		const updatedGlyphs = await Promise.all(glyphsPromises);
1539		const result = {};
1540		for (const { stack, id, glyph } of updatedGlyphs) {
1541			result[stack] ||= {};
1542			result[stack][id] = glyph && {
1543				id: glyph.id,
1544				bitmap: glyph.bitmap.clone(),
1545				metrics: glyph.metrics
1546			};
1547		}
1548		return result;
1549	}
1550	/**
1551	* Gets one glyph, asked for by grapheme cluster so that a letter and its marks are drawn as the
1552	* one shape they are written as.
1553	*
1554	* A cluster is drawn from a font rather than fetched, because a glyphs URL serves codepoints and
1555	* has no way to serve the one shape they are written as. A file the style pinned with
1556	* `font-faces` draws it where one covers it, and the local fonts otherwise. For a single
1557	* codepoint a declared file still wins over the glyphs URL and the local fallbacks.
1558	*/
1559	async _getAndCacheGlyphsPromise(stack, id) {
1560		this.entries[stack] ??= {
1561			glyphs: {},
1562			requests: {},
1563			ranges: {}
1564		};
1565		const entry = this.entries[stack];
1566		let glyph = entry.glyphs[id];
1567		if (glyph !== void 0) return {
1568			stack,
1569			id,
1570			glyph
1571		};
1572		const codePoint = id.codePointAt(0);
1573		const fontFaceFamily = this.fontFaceManager.hasFontFaces() ? await this.fontFaceManager.getFontFamily(stack, codePoint) : null;
1574		if (fontFaceFamily) {
1575			glyph = entry.glyphs[id] = await this._drawGlyph(entry, stack, id, fontFaceFamily);
1576			return {
1577				stack,
1578				id,
1579				glyph
1580			};
1581		}
1582		if (!this.url || isCluster(id) || this._charUsesLocalIdeographFontFamily(codePoint)) {
1583			glyph = entry.glyphs[id] = await this._drawGlyph(entry, stack, id);
1584			return {
1585				stack,
1586				id,
1587				glyph
1588			};
1589		}
1590		return await this._downloadAndCacheRangePromise(stack, id);
1591	}
1592	/**
1593	* Gets a glyph from the server-side cache, downloading the PBF range it falls in if need be.
1594	*
1595	* Only reached for a single codepoint. What comes back is keyed by codepoint, as the file is,
1596	* and is cached by cluster -- which for one codepoint is the character itself.
1597	*/
1598	async _downloadAndCacheRangePromise(stack, id) {
1599		const codePoint = id.codePointAt(0);
1600		const entry = this.entries[stack];
1601		const range = Math.floor(codePoint / 256);
1602		if (entry.ranges[range]) return {
1603			stack,
1604			id,
1605			glyph: null
1606		};
1607		entry.requests[range] ||= this._loadGlyphRange(stack, range);
1608		try {
1609			const response = await entry.requests[range];
1610			for (const responseId in response) entry.glyphs[String.fromCodePoint(+responseId)] = response[+responseId];
1611			entry.ranges[range] = true;
1612			return {
1613				stack,
1614				id,
1615				glyph: response[codePoint] || null
1616			};
1617		} catch (e) {
1618			const glyph = entry.glyphs[id] = await this._drawGlyph(entry, stack, id);
1619			this._warnOnMissingGlyphRange(glyph, range, codePoint, ensureError(e));
1620			return {
1621				stack,
1622				id,
1623				glyph
1624			};
1625		}
1626	}
1627	/**
1628	* Downloads one range of 256 codepoints from the glyphs URL and parses the glyphs out of it.
1629	*/
1630	async _loadGlyphRange(fontstack, range) {
1631		const begin = range * 256;
1632		const end = begin + 255;
1633		const request = await this.requestManager.transformRequest(this.url.replace("{fontstack}", fontstack).replace("{range}", `${begin}-${end}`), "Glyphs");
1634		const response = await getArrayBuffer(request, new AbortController());
1635		if (!response?.data) throw new Error(`Could not load glyph range. range: ${range}, ${begin}-${end}`);
1636		const glyphs = {};
1637		for (const glyph of parseGlyphPbf(response.data)) glyphs[glyph.id] = glyph;
1638		return glyphs;
1639	}
1640	_warnOnMissingGlyphRange(glyph, range, id, err) {
1641		const begin = range * 256;
1642		const end = begin + 255;
1643		const codePoint = id.toString(16).padStart(4, "0").toUpperCase();
1644		warnOnce(`Unable to load glyph range ${range}, ${begin}-${end}. Rendering codepoint U+${codePoint} locally instead. ${err}`);
1645	}
1646	/**
1647	* Returns whether the given codepoint should be rendered locally.
1648	*/
1649	_charUsesLocalIdeographFontFamily(id) {
1650		return !!this.localIdeographFontFamily && codePointUsesLocalIdeographFontFamily(id);
1651	}
1652	/**
1653	* Draws a glyph offscreen using TinySDF, created lazily. The whole cluster goes to TinySDF, which
1654	* is what lets the browser's text engine place a letter's marks on it.
1655	*
1656	* @param fontFaceFamily - the CSS family of the `font-faces` file covering this codepoint, if any
1657	*/
1658	async _drawGlyph(entry, stack, id, fontFaceFamily) {
1659		const char = (await this._getTinySDF(entry, stack, id, fontFaceFamily)).draw(id);
1660		/**
1661		* TinySDF's "top" is the distance from the alphabetic baseline to the top of the glyph.
1662		* Server-generated fonts specify "top" relative to an origin above the em box (the origin
1663		* comes from FreeType, but I'm unclear on exactly how it's derived)
1664		* ref: https://github.com/mapbox/sdf-glyph-foundry
1665		*
1666		* Server fonts don't yet include baseline information, so we can't line up exactly with them
1667		* (and they don't line up with each other)
1668		* ref: https://github.com/mapbox/node-fontnik/pull/160
1669		*
1670		* To approximately align TinySDF glyphs with server-provided glyphs, we use this baseline adjustment
1671		* factor calibrated to be in between DIN Pro and Arial Unicode (but closer to Arial Unicode)
1672		*/
1673		const topAdjustment = 27.5;
1674		const leftAdjustment = .5;
1675		const isControl = /^\p{gc=Cf}+$/u.test(id);
1676		return {
1677			id: id.codePointAt(0),
1678			bitmap: new AlphaImage({
1679				width: char.width || 60,
1680				height: char.height || 60
1681			}, char.data),
1682			metrics: {
1683				width: isControl ? 0 : char.glyphWidth / textureScale || 24,
1684				height: char.glyphHeight / textureScale || 24,
1685				left: char.glyphLeft / textureScale + leftAdjustment || 0,
1686				top: char.glyphTop / textureScale - topAdjustment || -8,
1687				advance: isControl ? 0 : char.glyphAdvance / textureScale || 24,
1688				isDoubleResolution: true
1689			}
1690		};
1691	}
1692	/**
1693	* Returns the TinySDF that draws this grapheme, created lazily. A stack keeps one per font
1694	* selection, so a fallback cannot bleed into the rest of the text, and one more per font file
1695	* for the clusters drawn from it, which need a wider canvas.
1696	*
1697	* A font file carries its own weight and style, so neither is sniffed out of the family name.
1698	* Where no file covers the grapheme, `localIdeographFontFamily` beats the last resort fontstack.
1699	*/
1700	_getTinySDF(entry, stack, id, fontFaceFamily) {
1701		const cluster = isCluster(id);
1702		if (fontFaceFamily) {
1703			const cache = cluster ? "clusterTinySDFs" : "fontFaceTinySDFs";
1704			entry[cache] ??= {};
1705			entry[cache][fontFaceFamily] ||= this._createTinySDF(fontFaceFamily, false, cluster ? clusterEmsWide : 1);
1706			return entry[cache][fontFaceFamily];
1707		}
1708		const usesLocalIdeographFontFamily = stack === defaultStack && this.localIdeographFontFamily !== "" && this._charUsesLocalIdeographFontFamily(id.codePointAt(0));
1709		const family = usesLocalIdeographFontFamily ? this.localIdeographFontFamily : stack;
1710		if (cluster) {
1711			entry.clusterTinySDFs ??= {};
1712			entry.clusterTinySDFs[family] ||= this._createTinySDF(family, true, clusterEmsWide);
1713			return entry.clusterTinySDFs[family];
1714		}
1715		const cache = usesLocalIdeographFontFamily ? "ideographTinySDF" : "tinySDF";
1716		entry[cache] ||= this._createTinySDF(family);
1717		return entry[cache];
1718	}
1719	/**
1720	* Builds the TinySDF that draws with a given font selection.
1721	*
1722	* TinySDF derives its canvas from `fontSize + buffer * 4`, so the buffer is the only way in to a
1723	* wider one. It also stands for the padding the atlas expects to be `GLYPH_PBF_BORDER`, so the
1724	* two are passed separately.
1725	*
1726	* @param emsWide - how wide the glyphs may be, in font sizes, before TinySDF cuts them off
1727	*/
1728	async _createTinySDF(stack, sniffFontStyles = true, emsWide = 1) {
1729		const fontFamilies = stack ? stack.split(",") : [];
1730		fontFamilies.push(defaultGenericFontFamily);
1731		const fontFamily = fontFamilies.map((fontName) => /[-\w]+/.test(fontName) ? fontName : `'${CSS.escape(fontName)}'`).join(",");
1732		const fontSize = 48;
1733		const fontWeight = sniffFontStyles ? this._fontWeight(fontFamilies[0]) : void 0;
1734		const fontStyle = sniffFontStyles ? this._fontStyle(fontFamilies[0]) : "normal";
1735		if (typeof document !== "undefined" && document.fonts?.load) try {
1736			await document.fonts.load(`${fontStyle} ${fontWeight || "normal"} ${fontSize}px ${fontFamily}`);
1737		} catch (e) {
1738			warnOnce(`Failed to load font "${fontFamily}": ${ensureError(e).message}`);
1739		}
1740		const padding = 6;
1741		return this.createRasterizer({
1742			fontSize,
1743			buffer: Math.max(padding, Math.ceil(fontSize * (emsWide - 1) / 4)),
1744			radius: 16,
1745			cutoff: .25,
1746			fontFamily,
1747			fontWeight,
1748			fontStyle,
1749			lang: this.lang
1750		}, padding);
1751	}
1752	/**
1753	* Sniffs the font style out of a font family name.
1754	*/
1755	_fontStyle(fontFamily) {
1756		if (/italic/i.test(fontFamily)) return "italic";
1757		else if (/oblique/i.test(fontFamily)) return "oblique";
1758		return "normal";
1759	}
1760	/**
1761	* Sniffs the font weight out of a font family name.
1762	*/
1763	_fontWeight(fontFamily) {
1764		const weightsByName = {
1765			thin: 100,
1766			hairline: 100,
1767			"extra light": 200,
1768			"ultra light": 200,
1769			light: 300,
1770			normal: 400,
1771			regular: 400,
1772			medium: 500,
1773			semibold: 600,
1774			demibold: 600,
1775			bold: 700,
1776			"extra bold": 800,
1777			"ultra bold": 800,
1778			black: 900,
1779			heavy: 900,
1780			"extra black": 950,
1781			"ultra black": 950
1782		};
1783		let match;
1784		for (const [name, weight] of Object.entries(weightsByName)) if (new RegExp(`\\b${name}\\b`, "i").test(fontFamily)) match = `${weight}`;
1785		return match;
1786	}
1787	destroy() {
1788		for (const stack in this.entries) {
1789			const entry = this.entries[stack];
1790			entry.tinySDF = null;
1791			entry.ideographTinySDF = null;
1792			entry.fontFaceTinySDFs = {};
1793			entry.glyphs = {};
1794			entry.requests = {};
1795			entry.ranges = {};
1796		}
1797		this.entries = {};
1798		this.fontFaceManager.destroy();
1799	}
1800};
1801//#endregion
1802//#region src/style/light_properties.g.ts
1803let properties$2;
1804const getProperties$2 = () => properties$2 = properties$2 || new Properties({
1805	"anchor": new DataConstantProperty(latest["light"]["anchor"], "anchor"),
1806	"position": new DataConstantProperty(latest["light"]["position"], "position"),
1807	"color": new DataConstantProperty(latest["light"]["color"], "color"),
1808	"intensity": new DataConstantProperty(latest["light"]["intensity"], "intensity")
1809});
1810//#endregion
1811//#region src/style/light.ts
1812var Light = class extends Evented {
1813	constructor(lightOptions, globalState) {
1814		super();
1815		this._transitionable = new Transitionable(getProperties$2(), "light", globalState);
1816		this.setLight(lightOptions);
1817		this._transitioning = this._transitionable.untransitioned();
1818	}
1819	getLight() {
1820		return this._transitionable.serialize();
1821	}
1822	/**
1823	* Gets the light position in cartesian coordinates.
1824	*/
1825	getCartesianPosition() {
1826		return sphericalToCartesian(this.properties.get("position"));
1827	}
1828	setLight(light, options = {}) {
1829		if (this._validate(validateStyle.light, light, options)) return;
1830		this._transitionable.setValues(light);
1831	}
1832	updateTransitions(parameters) {
1833		this._transitioning = this._transitionable.transitioned(parameters, this._transitioning);
1834	}
1835	hasTransition() {
1836		return this._transitioning.hasTransition();
1837	}
1838	applyGlobalStateChange(refs, priorGlobalState, parameters) {
1839		this._transitioning = this._transitionable.applyGlobalStateChange(refs, priorGlobalState, this._transitioning, parameters);
1840	}
1841	recalculate(parameters) {
1842		this.properties = this._transitioning.possiblyEvaluate(parameters);
1843	}
1844	_validate(validate, value, options) {
1845		return validateAndEmit(this, validate, { value }, options);
1846	}
1847};
1848//#endregion
1849//#region src/style/sky_properties.g.ts
1850let properties$1;
1851const getProperties$1 = () => properties$1 = properties$1 || new Properties({
1852	"sky-color": new DataConstantProperty(latest["sky"]["sky-color"], "sky-color"),
1853	"horizon-color": new DataConstantProperty(latest["sky"]["horizon-color"], "horizon-color"),
1854	"fog-color": new DataConstantProperty(latest["sky"]["fog-color"], "fog-color"),
1855	"fog-ground-blend": new DataConstantProperty(latest["sky"]["fog-ground-blend"], "fog-ground-blend"),
1856	"horizon-fog-blend": new DataConstantProperty(latest["sky"]["horizon-fog-blend"], "horizon-fog-blend"),
1857	"sky-horizon-blend": new DataConstantProperty(latest["sky"]["sky-horizon-blend"], "sky-horizon-blend"),
1858	"atmosphere-blend": new DataConstantProperty(latest["sky"]["atmosphere-blend"], "atmosphere-blend")
1859});
1860//#endregion
1861//#region src/style/sky.ts
1862var Sky = class extends Evented {
1863	constructor(sky, globalState) {
1864		super();
1865		this._transitionable = new Transitionable(getProperties$1(), "sky", globalState);
1866		this.setSky(sky);
1867		this._transitioning = this._transitionable.untransitioned();
1868		this.recalculate(new EvaluationParameters(0));
1869	}
1870	/**
1871	* Validates and applies the sky. Returns false, after firing `error`, when
1872	* the value was rejected and nothing changed.
1873	*/
1874	setSky(sky, options = {}) {
1875		if (this._validate(validateStyle.sky, sky, options)) return false;
1876		sky ||= {
1877			"sky-color": "transparent",
1878			"horizon-color": "transparent",
1879			"fog-color": "transparent",
1880			"fog-ground-blend": 1,
1881			"atmosphere-blend": 0
1882		};
1883		this._transitionable.setValues(sky);
1884		return true;
1885	}
1886	getSky() {
1887		return this._transitionable.serialize();
1888	}
1889	updateTransitions(parameters) {
1890		this._transitioning = this._transitionable.transitioned(parameters, this._transitioning);
1891	}
1892	hasTransition() {
1893		return this._transitioning.hasTransition();
1894	}
1895	applyGlobalStateChange(refs, priorGlobalState, parameters) {
1896		this._transitioning = this._transitionable.applyGlobalStateChange(refs, priorGlobalState, this._transitioning, parameters);
1897	}
1898	recalculate(parameters) {
1899		this.properties = this._transitioning.possiblyEvaluate(parameters);
1900	}
1901	_validate(validate, value, options = {}) {
1902		return validateAndEmit(this, validate, { value }, options);
1903	}
1904	/**
1905	* Currently fog is a very simple implementation, and should only used
1906	* to create an atmosphere near the horizon.
1907	* But because the fog is drawn from the far-clipping-plane to
1908	* map-center, and because the fog does nothing know about the horizon,
1909	* this method does a fadeout in respect of pitch. So, when the horizon
1910	* gets out of view, which is at about pitch 70, this methods calculates
1911	* the corresponding opacity values. Below pitch 60 the fog is completely
1912	* invisible.
1913	*/
1914	calculateFogBlendOpacity(pitch) {
1915		if (pitch < 60) return 0;
1916		if (pitch < 70) return (pitch - 60) / 10;
1917		return 1;
1918	}
1919};
1920//#endregion
1921//#region src/render/line_atlas.ts
1922/**
1923* @internal
1924* A LineAtlas lets us reuse rendered dashed lines
1925* by writing many of them to a texture and then fetching their positions
1926* using {@link LineAtlas.getDash}.
1927*
1928* @param width - the width
1929* @param height - the height
1930*/
1931var LineAtlas = class {
1932	constructor(width, height) {
1933		this.width = width;
1934		this.height = height;
1935		this.nextRow = 0;
1936		this.data = new Uint8Array(this.width * this.height);
1937		this.dashEntry = {};
1938	}
1939	/**
1940	* Get or create a dash line pattern.
1941	*
1942	* @param dasharray - the key (represented by numbers) to get the dash texture
1943	* @param round - whether to add circle caps in between dash segments
1944	* @returns position of dash texture in {@link DashEntry}
1945	*/
1946	getDash(dasharray, round) {
1947		const key = dasharray.join(",") + String(round);
1948		this.dashEntry[key] ||= this.addDash(dasharray, round);
1949		return this.dashEntry[key];
1950	}
1951	getDashRanges(dasharray, lineAtlasWidth, stretch) {
1952		const oddDashArray = dasharray.length % 2 === 1;
1953		const ranges = [];
1954		let left = oddDashArray ? -dasharray[dasharray.length - 1] * stretch : 0;
1955		let right = dasharray[0] * stretch;
1956		let isDash = true;
1957		ranges.push({
1958			left,
1959			right,
1960			isDash,
1961			zeroLength: dasharray[0] === 0
1962		});
1963		let currentDashLength = dasharray[0];
1964		for (let i = 1; i < dasharray.length; i++) {
1965			isDash = !isDash;
1966			const dashLength = dasharray[i];
1967			left = currentDashLength * stretch;
1968			currentDashLength += dashLength;
1969			right = currentDashLength * stretch;
1970			ranges.push({
1971				left,
1972				right,
1973				isDash,
1974				zeroLength: dashLength === 0
1975			});
1976		}
1977		return ranges;
1978	}
1979	addRoundDash(ranges, stretch, n) {
1980		const halfStretch = stretch / 2;
1981		for (let y = -n; y <= n; y++) {
1982			const row = this.nextRow + n + y;
1983			const index = this.width * row;
1984			let currIndex = 0;
1985			let range = ranges[currIndex];
1986			for (let x = 0; x < this.width; x++) {
1987				if (x / range.right > 1) range = ranges[++currIndex];
1988				const distLeft = Math.abs(x - range.left);
1989				const distRight = Math.abs(x - range.right);
1990				const minDist = Math.min(distLeft, distRight);
1991				let signedDistance;
1992				const distMiddle = y / n * (halfStretch + 1);
1993				if (range.isDash) {
1994					const distEdge = halfStretch - Math.abs(distMiddle);
1995					signedDistance = Math.sqrt(minDist * minDist + distEdge * distEdge);
1996				} else signedDistance = halfStretch - Math.sqrt(minDist * minDist + distMiddle * distMiddle);
1997				this.data[index + x] = Math.max(0, Math.min(255, signedDistance + 128));
1998			}
1999		}
2000	}
2001	addRegularDash(ranges) {
2002		for (let i = ranges.length - 1; i >= 0; --i) {
2003			const part = ranges[i];
2004			const next = ranges[i + 1];
2005			if (part.zeroLength) ranges.splice(i, 1);
2006			else if (next?.isDash === part.isDash) {
2007				next.left = part.left;
2008				ranges.splice(i, 1);
2009			}
2010		}
2011		const first = ranges[0];
2012		const last = ranges[ranges.length - 1];
2013		if (first.isDash === last.isDash) {
2014			first.left = last.left - this.width;
2015			last.right = first.right + this.width;
2016		}
2017		const index = this.width * this.nextRow;
2018		let currIndex = 0;
2019		let range = ranges[currIndex];
2020		for (let x = 0; x < this.width; x++) {
2021			if (x / range.right > 1) range = ranges[++currIndex];
2022			const distLeft = Math.abs(x - range.left);
2023			const distRight = Math.abs(x - range.right);
2024			const minDist = Math.min(distLeft, distRight);
2025			const signedDistance = range.isDash ? minDist : -minDist;
2026			this.data[index + x] = Math.max(0, Math.min(255, signedDistance + 128));
2027		}
2028	}
2029	addDash(dasharray, round) {
2030		const n = round ? 7 : 0;
2031		const height = 2 * n + 1;
2032		if (this.nextRow + height > this.height) {
2033			warnOnce("LineAtlas out of space");
2034			return null;
2035		}
2036		let length = 0;
2037		for (const dash of dasharray) length += dash;
2038		if (length !== 0) {
2039			const stretch = this.width / length;
2040			const ranges = this.getDashRanges(dasharray, this.width, stretch);
2041			if (round) this.addRoundDash(ranges, stretch, n);
2042			else this.addRegularDash(ranges);
2043		}
2044		const dashEntry = {
2045			y: this.nextRow + n,
2046			height: 2 * n,
2047			width: length
2048		};
2049		this.nextRow += height;
2050		this.dirty = true;
2051		return dashEntry;
2052	}
2053	bind(context) {
2054		const gl = context.gl;
2055		if (!this.texture) {
2056			this.texture = gl.createTexture();
2057			gl.bindTexture(gl.TEXTURE_2D, this.texture);
2058			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
2059			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
2060			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
2061			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
2062			gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, this.width, this.height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, this.data);
2063		} else {
2064			gl.bindTexture(gl.TEXTURE_2D, this.texture);
2065			if (this.dirty) {
2066				this.dirty = false;
vendor: 48,123 bytes, lines 2067-3477
2067				gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, this.width, this.height, gl.ALPHA, gl.UNSIGNED_BYTE, this.data);
2068			}
2069		}
2070	}
2071};
2072//#endregion
2073//#region src/util/web_worker.ts
2074function isCrossOrigin(url) {
2075	if (!url) return false;
2076	const loc = globalThis.location;
2077	if (!loc) return false;
2078	try {
2079		return new URL(url, loc.href).origin !== loc.origin;
2080	} catch {
2081		return false;
2082	}
2083}
2084function defaultWorkerUrl() {
2085	const moduleUrl = import.meta.url;
2086	if (!/^https?:/.test(moduleUrl)) return "";
2087	const workerName = moduleUrl.endsWith("-dev.mjs") ? "maplibre-gl-worker-dev.mjs" : "maplibre-gl-worker.mjs";
2088	return new URL(`./${workerName}`, moduleUrl).href;
2089}
2090function createWorker(url, asModule) {
2091	if (asModule) try {
2092		return new Worker(url, { type: "module" });
2093	} catch (e) {
2094		console.warn("Module worker not supported, falling back to classic worker", e);
2095	}
2096	return new Worker(url);
2097}
2098async function fetchAsBlobUrl(url) {
2099	const response = await fetch(url);
2100	if (!response.ok) throw new Error(`Failed to fetch worker script (${response.status}): ${url}`);
2101	const code = await response.text();
2102	const blob = new Blob([code], { type: "text/javascript" });
2103	return URL.createObjectURL(blob);
2104}
2105function importAsBlobUrl(url) {
2106	const blob = new Blob([`import ${JSON.stringify(new URL(url, import.meta.url).href)}`], { type: "text/javascript" });
2107	return URL.createObjectURL(blob);
2108}
2109async function workerFactory() {
2110	const url = config.WORKER_URL || defaultWorkerUrl();
2111	const asModule = url?.endsWith(".cjs") ? false : true;
2112	if (!isCrossOrigin(url)) return createWorker(url, asModule);
2113	if (asModule) {
2114		const blobUrl = importAsBlobUrl(url);
2115		try {
2116			return createWorker(blobUrl, asModule);
2117		} finally {
2118			URL.revokeObjectURL(blobUrl);
2119		}
2120	}
2121	const blobUrl = await fetchAsBlobUrl(url);
2122	try {
2123		return createWorker(blobUrl, asModule);
2124	} finally {
2125		URL.revokeObjectURL(blobUrl);
2126	}
2127}
2128//#endregion
2129//#region src/util/worker_pool.ts
2130const PRELOAD_POOL_ID = "maplibre_preloaded_worker_pool";
2131/**
2132* Constructs a worker pool.
2133*/
2134var WorkerPool = class WorkerPool {
2135	constructor() {
2136		this.active = {};
2137		this.workersPromise = null;
2138	}
2139	async acquire(mapId) {
2140		this.active[mapId] = true;
2141		if (!this.workersPromise) {
2142			const promises = [];
2143			while (promises.length < WorkerPool.workerCount) promises.push(workerFactory());
2144			this.workersPromise = Promise.all(promises);
2145		}
2146		return (await this.workersPromise).slice();
2147	}
2148	release(mapId) {
2149		delete this.active[mapId];
2150		if (this.numActive() === 0 && this.workersPromise) {
2151			const promise = this.workersPromise;
2152			this.workersPromise = null;
2153			promise.then((workers) => {
2154				for (const w of workers) w.terminate();
2155			});
2156		}
2157	}
2158	isPreloaded() {
2159		return !!this.active[PRELOAD_POOL_ID];
2160	}
2161	numActive() {
2162		return Object.keys(this.active).length;
2163	}
2164};
2165const availableLogicalProcessors = Math.floor(browser.hardwareConcurrency / 2);
2166WorkerPool.workerCount = isSafari(globalThis) ? Math.max(Math.min(availableLogicalProcessors, 3), 1) : 1;
2167//#endregion
2168//#region src/util/global_worker_pool.ts
2169let globalWorkerPool;
2170/**
2171* Creates (if necessary) and returns the single, global WorkerPool instance
2172* to be shared across each Map
2173*/
2174function getGlobalWorkerPool() {
2175	globalWorkerPool ||= new WorkerPool();
2176	return globalWorkerPool;
2177}
2178/**
2179* Initializes resources like WebWorkers that can be shared across maps to lower load
2180* times in some situations. `setWorkerUrl()` and `setWorkerCount()`, if being
2181* used, must be set before `prewarm()` is called to have an effect.
2182*
2183* By default, the lifecycle of these resources is managed automatically, and they are
2184* lazily initialized when a Map is first created. By invoking `prewarm()`, these
2185* resources will be created ahead of time, and will not be cleared when the last Map
2186* is removed from the page. This allows them to be re-used by new Map instances that
2187* are created later. They can be manually cleared by calling
2188* `clearPrewarmedResources()`. This is only necessary if your web page remains
2189* active but stops using maps altogether.
2190*
2191* This is primarily useful when using GL-JS maps in a single page app, wherein a user
2192* would navigate between various views that can cause Map instances to constantly be
2193* created and destroyed.
2194*
2195* @example
2196* ```ts
2197* prewarm()
2198* ```
2199*/
2200function prewarm() {
2201	getGlobalWorkerPool().acquire(PRELOAD_POOL_ID);
2202}
2203/**
2204* Clears up resources that have previously been created by `prewarm()`.
2205* Note that this is typically not necessary. You should only call this function
2206* if you expect the user of your app to not return to a Map view at any point
2207* in your application.
2208*
2209* @example
2210* ```ts
2211* clearPrewarmedResources()
2212* ```
2213*/
2214function clearPrewarmedResources() {
2215	const pool = globalWorkerPool;
2216	if (pool) {
2217		if (pool.isPreloaded() && pool.numActive() === 1) {
2218			pool.release(PRELOAD_POOL_ID);
2219			globalWorkerPool = null;
2220		} else console.warn("Could not clear WebWorkers since there are active Map instances that still reference it. The pre-warmed WebWorker pool can only be cleared when all map instances have been removed with map.remove()");
2221	}
2222}
2223//#endregion
2224//#region src/util/dispatcher.ts
2225/**
2226* Responsible for sending messages from a {@link Source} to an associated worker source (usually with the same name).
2227*/
2228var Dispatcher = class extends Evented {
2229	constructor(workerPool, mapId) {
2230		super();
2231		this.workerPool = workerPool;
2232		this.actors = [];
2233		this.currentActor = 0;
2234		this.id = mapId;
2235		this.removed = false;
2236		this.workerErrorSubscriptions = [];
2237		this.actorsPromise = this.initActors(mapId);
2238	}
2239	async initActors(mapId) {
2240		const workers = await this.workerPool.acquire(mapId);
2241		if (this.removed) return [];
2242		this.actors = workers.map((worker, i) => {
2243			this.workerErrorSubscriptions.push(subscribe(worker, "error", () => {
2244				this.fire(new ErrorEvent(/* @__PURE__ */ new Error("Worker failed to load. Check that the worker URL is correct.")));
2245			}, false));
2246			const actor = new Actor(worker, mapId);
2247			actor.name = `Worker ${i}`;
2248			return actor;
2249		});
2250		if (!this.actors.length) throw new Error("No actors found");
2251		return this.actors;
2252	}
2253	/**
2254	* Broadcast a message to all Workers.
2255	*/
2256	async broadcast(type, data) {
2257		const actors = await this.actorsPromise;
2258		return Promise.all(actors.map((actor) => actor.sendAsync({
2259			type,
2260			data
2261		})));
2262	}
2263	/**
2264	* Acquires an actor to dispatch messages to. The actors are distributed in round-robin fashion.
2265	* @returns An actor object backed by a web worker for processing messages.
2266	*/
2267	async getActor() {
2268		const actors = await this.actorsPromise;
2269		this.currentActor = (this.currentActor + 1) % actors.length;
2270		return actors[this.currentActor];
2271	}
2272	async waitForInitComplete() {
2273		if (this.actors.length === 0) await this.actorsPromise;
2274	}
2275	getReadyActor() {
2276		this.currentActor = (this.currentActor + 1) % this.actors.length;
2277		return this.actors[this.currentActor];
2278	}
2279	remove(mapRemoved = true) {
2280		this.removed = true;
2281		for (const actor of this.actors) actor.remove();
2282		for (const subscription of this.workerErrorSubscriptions) subscription.unsubscribe();
2283		this.actors = [];
2284		this.workerErrorSubscriptions = [];
2285		this.setEventedParent(null);
2286		if (mapRemoved) this.workerPool.release(this.id);
2287	}
2288	async registerMessageHandler(type, handler) {
2289		const actors = await this.actorsPromise;
2290		for (const actor of actors) actor.registerMessageHandler(type, handler);
2291	}
2292	async unregisterMessageHandler(type) {
2293		const actors = await this.actorsPromise;
2294		for (const actor of actors) actor.unregisterMessageHandler(type);
2295	}
2296};
2297let globalDispatcher;
2298/**
2299* This function is used to get the global dispatcher that is shared across all maps instances.
2300* It is used by the main thread to send messages to the workers, and by the workers to send messages back to the main thread.
2301* If you import a script into the worker and need to send a message to the workers to pass some parameters for example,
2302* you can use this function to get the global dispatcher and send a message to the workers.
2303* @returns The global dispatcher instance.
2304*/
2305function getGlobalDispatcher() {
2306	if (!globalDispatcher) {
2307		globalDispatcher = new Dispatcher(getGlobalWorkerPool(), GLOBAL_DISPATCHER_ID);
2308		globalDispatcher.registerMessageHandler("GR", (_mapId, params, abortController) => {
2309			return makeRequest(params, abortController);
2310		});
2311	}
2312	return globalDispatcher;
2313}
2314//#endregion
2315//#region src/source/query_features.ts
2316function getPixelPosMatrix(transform, tileID) {
2317	const t = create$1();
2318	translate(t, t, [
2319		1,
2320		1,
2321		0
2322	]);
2323	scale(t, t, [
2324		transform.width * .5,
2325		transform.height * .5,
2326		1
2327	]);
2328	if (transform.calculatePosMatrix) return multiply(t, t, transform.calculatePosMatrix(tileID.toUnwrapped()));
2329	else return t;
2330}
2331function queryIncludes3DLayer(layers, styleLayers, sourceID) {
2332	if (layers) for (const layerID of layers) {
2333		const layer = styleLayers[layerID];
2334		if (layer?.source === sourceID && layer.type === "fill-extrusion") return true;
2335	}
2336	else for (const key in styleLayers) {
2337		const layer = styleLayers[key];
2338		if (layer.source === sourceID && layer.type === "fill-extrusion") return true;
2339	}
2340	return false;
2341}
2342function queryRenderedFeatures(tileManager, styleLayers, serializedLayers, queryGeometry, params, transform, getElevation) {
2343	const has3DLayer = queryIncludes3DLayer(params?.layers ?? null, styleLayers, tileManager.id);
2344	const maxPitchScaleFactor = transform.maxPitchScaleFactor();
2345	const tilesIn = tileManager.tilesIn(queryGeometry, maxPitchScaleFactor, has3DLayer);
2346	tilesIn.sort(sortTilesIn);
2347	const renderedFeatureLayers = [];
2348	for (const tileIn of tilesIn) renderedFeatureLayers.push({
2349		wrappedTileID: tileIn.tileID.wrapped().key,
2350		queryResults: tileIn.tile.queryRenderedFeatures(styleLayers, serializedLayers, tileManager.getState(), tileIn.queryGeometry, tileIn.cameraQueryGeometry, tileIn.scale, params, transform, maxPitchScaleFactor, getPixelPosMatrix(transform, tileIn.tileID), getElevation ? (x, y) => getElevation(tileIn.tileID, x, y) : void 0)
2351	});
2352	return convertFeaturesToMapFeatures(mergeRenderedFeatureLayers(renderedFeatureLayers), tileManager);
2353}
2354function queryRenderedSymbols(styleLayers, serializedLayers, tileManagers, queryGeometry, params, collisionIndex, retainedQueryData) {
2355	const result = {};
2356	const renderedSymbols = collisionIndex.queryRenderedSymbols(queryGeometry);
2357	const bucketQueryData = [];
2358	for (const bucketInstanceId of Object.keys(renderedSymbols).map(Number)) bucketQueryData.push(retainedQueryData[bucketInstanceId]);
2359	bucketQueryData.sort(sortTilesIn);
2360	for (const queryData of bucketQueryData) {
2361		const bucketSymbols = queryData.featureIndex.lookupSymbolFeatures(renderedSymbols[queryData.bucketInstanceId], serializedLayers, queryData.bucketIndex, queryData.sourceLayerIndex, {
2362			filterSpec: params.filter,
2363			globalState: params.globalState
2364		}, params.layers, params.availableImages, styleLayers);
2365		for (const layerID in bucketSymbols) {
2366			result[layerID] ||= [];
2367			const layerSymbols = bucketSymbols[layerID];
2368			layerSymbols.sort((a, b) => {
2369				const featureSortOrder = queryData.featureSortOrder;
2370				if (featureSortOrder) {
2371					const sortedA = featureSortOrder.indexOf(a.featureIndex);
2372					return featureSortOrder.indexOf(b.featureIndex) - sortedA;
2373				} else return b.featureIndex - a.featureIndex;
2374			});
2375			for (const symbolFeature of layerSymbols) result[layerID].push(symbolFeature);
2376		}
2377	}
2378	return convertFeaturesToMapFeaturesMultiple(result, styleLayers, tileManagers);
2379}
2380function querySourceFeatures(tileManager, params) {
2381	const tiles = tileManager.getRenderableIds().map((id) => {
2382		return tileManager.getTileByID(id);
2383	});
2384	const result = [];
2385	const dataTiles = {};
2386	for (const tile of tiles) {
2387		const dataID = tile.tileID.canonical.key;
2388		if (!dataTiles[dataID]) {
2389			dataTiles[dataID] = true;
2390			tile.querySourceFeatures(result, params);
2391		}
2392	}
2393	return result;
2394}
2395function sortTilesIn(a, b) {
2396	const idA = a.tileID;
2397	const idB = b.tileID;
2398	return idA.overscaledZ - idB.overscaledZ || idA.canonical.y - idB.canonical.y || idA.wrap - idB.wrap || idA.canonical.x - idB.canonical.x;
2399}
2400function mergeRenderedFeatureLayers(tiles) {
2401	const result = {};
2402	const wrappedIDLayerMap = {};
2403	for (const { queryResults, wrappedTileID } of tiles) {
2404		wrappedIDLayerMap[wrappedTileID] ||= {};
2405		const wrappedIDLayers = wrappedIDLayerMap[wrappedTileID];
2406		for (const layerID in queryResults) {
2407			const tileFeatures = queryResults[layerID];
2408			wrappedIDLayers[layerID] ||= {};
2409			const wrappedIDFeatures = wrappedIDLayers[layerID];
2410			result[layerID] ||= [];
2411			for (const tileFeature of tileFeatures) if (!wrappedIDFeatures[tileFeature.featureIndex]) {
2412				wrappedIDFeatures[tileFeature.featureIndex] = true;
2413				result[layerID].push(tileFeature);
2414			}
2415		}
2416	}
2417	return result;
2418}
2419function convertFeaturesToMapFeatures(result, tileManager) {
2420	for (const layerID in result) for (const featureWrapper of result[layerID]) convertFeatureToMapFeature(featureWrapper, tileManager);
2421	return result;
2422}
2423function convertFeaturesToMapFeaturesMultiple(result, styleLayers, tileManagers) {
2424	for (const layerName in result) for (const featureWrapper of result[layerName]) {
2425		const tileManager = tileManagers[styleLayers[layerName].source];
2426		convertFeatureToMapFeature(featureWrapper, tileManager);
2427	}
2428	return result;
2429}
2430function convertFeatureToMapFeature(featureWrapper, tileManager) {
2431	const feature = featureWrapper.feature;
2432	const state = tileManager.getFeatureState(feature.layer["source-layer"], feature.id);
2433	feature.source = feature.layer.source;
2434	if (feature.layer["source-layer"]) feature.sourceLayer = feature.layer["source-layer"];
2435	feature.state = state;
2436}
2437//#endregion
2438//#region src/source/load_tilejson.ts
2439async function loadTileJson(options, requestManager, abortController, targetWindow) {
2440	let tileJSON = options;
2441	if (options.url) tileJSON = (await getJSON(await requestManager.transformRequest(options.url, "Source"), abortController)).data;
2442	else await browser.frameAsync(abortController, targetWindow);
2443	if (!tileJSON) return null;
2444	const result = pick(extend(tileJSON, options), [
2445		"tiles",
2446		"minzoom",
2447		"maxzoom",
2448		"attribution",
2449		"bounds",
2450		"scheme",
2451		"tileSize",
2452		"encoding"
2453	]);
2454	if ("vector_layers" in tileJSON && tileJSON.vector_layers) result.vectorLayerIds = tileJSON.vector_layers.map((layer) => layer.id);
2455	return result;
2456}
2457//#endregion
2458//#region src/geo/lng_lat_bounds.ts
2459/**
2460* A `LngLatBounds` object represents a geographical bounding box,
2461* defined by its southwest and northeast points in longitude and latitude.
2462*
2463* If no arguments are provided to the constructor, a `null` bounding box is created.
2464*
2465* Note that any MapLibre GL method that accepts a `LngLatBounds` object as an argument or option
2466* can also accept an `Array` of two {@link LngLatLike} constructs and will perform an implicit conversion.
2467* This flexible type is documented as {@link LngLatBoundsLike}.
2468*
2469* @group Geography and Geometry
2470*
2471* @example
2472* ```ts
2473* let sw = new LngLat(-73.9876, 40.7661);
2474* let ne = new LngLat(-73.9397, 40.8002);
2475* let llb = new LngLatBounds(sw, ne);
2476* ```
2477* @see [Fit to the bounds of a LineString](https://maplibre.org/maplibre-gl-js/docs/examples/fit-to-the-bounds-of-a-linestring/)
2478*/
2479var LngLatBounds = class LngLatBounds {
2480	/**
2481	* @param sw - The southwest corner of the bounding box.
2482	* OR array of 4 numbers in the order of  west, south, east, north
2483	* OR array of 2 LngLatLike: `[sw, ne]`
2484	* @param ne - The northeast corner of the bounding box.
2485	* @example
2486	* ```ts
2487	* let sw = new LngLat(-73.9876, 40.7661);
2488	* let ne = new LngLat(-73.9397, 40.8002);
2489	* let llb = new LngLatBounds(sw, ne);
2490	* ```
2491	* OR
2492	* ```ts
2493	* let llb = new LngLatBounds([-73.9876, 40.7661, -73.9397, 40.8002]);
2494	* ```
2495	* OR
2496	* ```ts
2497	* let llb = new LngLatBounds([sw, ne]);
2498	* ```
2499	*/
2500	constructor(sw, ne) {
2501		if (!sw) {} else if (ne) this.setSouthWest(sw).setNorthEast(ne);
2502		else if (Array.isArray(sw)) {
2503			if (sw.length === 4) this.setSouthWest([sw[0], sw[1]]).setNorthEast([sw[2], sw[3]]);
2504			else this.setSouthWest(sw[0]).setNorthEast(sw[1]);
2505		}
2506	}
2507	/**
2508	* Set the northeast corner of the bounding box
2509	*
2510	* @param ne - a {@link LngLatLike} object describing the northeast corner of the bounding box.
2511	*/
2512	setNorthEast(ne) {
2513		this._ne = ne instanceof LngLat ? new LngLat(ne.lng, ne.lat) : LngLat.convert(ne);
2514		return this;
2515	}
2516	/**
2517	* Set the southwest corner of the bounding box
2518	*
2519	* @param sw - a {@link LngLatLike} object describing the southwest corner of the bounding box.
2520	*/
2521	setSouthWest(sw) {
2522		this._sw = sw instanceof LngLat ? new LngLat(sw.lng, sw.lat) : LngLat.convert(sw);
2523		return this;
2524	}
2525	/**
2526	* Extend the bounds to include a given LngLatLike or LngLatBoundsLike.
2527	*
2528	* @param obj - object to extend to
2529	*/
2530	extend(obj) {
2531		const sw = this._sw, ne = this._ne;
2532		let sw2, ne2;
2533		if (obj instanceof LngLat) {
2534			sw2 = obj;
2535			ne2 = obj;
2536		} else if (obj instanceof LngLatBounds) {
2537			sw2 = obj._sw;
2538			ne2 = obj._ne;
2539			if (!sw2 || !ne2) return this;
2540		} else {
2541			if (Array.isArray(obj)) {
2542				if (obj.length === 4 || obj.every(Array.isArray)) {
2543					const lngLatBoundsObj = obj;
2544					return this.extend(LngLatBounds.convert(lngLatBoundsObj));
2545				} else {
2546					const lngLatObj = obj;
2547					return this.extend(LngLat.convert(lngLatObj));
2548				}
2549			} else if (obj && ("lng" in obj || "lon" in obj) && "lat" in obj) return this.extend(LngLat.convert(obj));
2550			return this;
2551		}
2552		if (!sw && !ne) {
2553			this._sw = new LngLat(sw2.lng, sw2.lat);
2554			this._ne = new LngLat(ne2.lng, ne2.lat);
2555		} else {
2556			sw.lng = Math.min(sw2.lng, sw.lng);
2557			sw.lat = Math.min(sw2.lat, sw.lat);
2558			ne.lng = Math.max(ne2.lng, ne.lng);
2559			ne.lat = Math.max(ne2.lat, ne.lat);
2560		}
2561		return this;
2562	}
2563	/**
2564	* Returns the geographical coordinate equidistant from the bounding box's corners.
2565	*
2566	* @returns The bounding box's center.
2567	* @example
2568	* ```ts
2569	* let llb = new LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]);
2570	* llb.getCenter(); // = LngLat {lng: -73.96365, lat: 40.78315}
2571	* ```
2572	*/
2573	getCenter() {
2574		return new LngLat((this._sw.lng + this._ne.lng) / 2, (this._sw.lat + this._ne.lat) / 2);
2575	}
2576	/**
2577	* Returns the southwest corner of the bounding box.
2578	*
2579	* @returns The southwest corner of the bounding box.
2580	*/
2581	getSouthWest() {
2582		return this._sw;
2583	}
2584	/**
2585	* Returns the northeast corner of the bounding box.
2586	*
2587	* @returns The northeast corner of the bounding box.
2588	*/
2589	getNorthEast() {
2590		return this._ne;
2591	}
2592	/**
2593	* Returns the northwest corner of the bounding box.
2594	*
2595	* @returns The northwest corner of the bounding box.
2596	*/
2597	getNorthWest() {
2598		return new LngLat(this.getWest(), this.getNorth());
2599	}
2600	/**
2601	* Returns the southeast corner of the bounding box.
2602	*
2603	* @returns The southeast corner of the bounding box.
2604	*/
2605	getSouthEast() {
2606		return new LngLat(this.getEast(), this.getSouth());
2607	}
2608	/**
2609	* Returns the west edge of the bounding box.
2610	*
2611	* @returns The west edge of the bounding box.
2612	*/
2613	getWest() {
2614		return this._sw.lng;
2615	}
2616	/**
2617	* Returns the south edge of the bounding box.
2618	*
2619	* @returns The south edge of the bounding box.
2620	*/
2621	getSouth() {
2622		return this._sw.lat;
2623	}
2624	/**
2625	* Returns the east edge of the bounding box.
2626	*
2627	* @returns The east edge of the bounding box.
2628	*/
2629	getEast() {
2630		return this._ne.lng;
2631	}
2632	/**
2633	* Returns the north edge of the bounding box.
2634	*
2635	* @returns The north edge of the bounding box.
2636	*/
2637	getNorth() {
2638		return this._ne.lat;
2639	}
2640	/**
2641	* Returns the bounding box represented as an array.
2642	*
2643	* @returns The bounding box represented as an array, consisting of the
2644	* southwest and northeast coordinates of the bounding represented as arrays of numbers.
2645	* @example
2646	* ```ts
2647	* let llb = new LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]);
2648	* llb.toArray(); // = [[-73.9876, 40.7661], [-73.9397, 40.8002]]
2649	* ```
2650	*/
2651	toArray() {
2652		return [this._sw.toArray(), this._ne.toArray()];
2653	}
2654	/**
2655	* Return the bounding box represented as a string.
2656	*
2657	* @returns The bounding box represents as a string of the format
2658	* `'LngLatBounds(LngLat(lng, lat), LngLat(lng, lat))'`.
2659	* @example
2660	* ```ts
2661	* let llb = new LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]);
2662	* llb.toString(); // = "LngLatBounds(LngLat(-73.9876, 40.7661), LngLat(-73.9397, 40.8002))"
2663	* ```
2664	*/
2665	toString() {
2666		return `LngLatBounds(${this._sw.toString()}, ${this._ne.toString()})`;
2667	}
2668	/**
2669	* Check if the bounding box is an empty/`null`-type box.
2670	*
2671	* @returns True if bounds have been defined, otherwise false.
2672	*/
2673	isEmpty() {
2674		return !(this._sw && this._ne);
2675	}
2676	/**
2677	* Check if the point is within the bounding box.
2678	*
2679	* @param lnglat - geographic point to check against.
2680	* @returns `true` if the point is within the bounding box.
2681	* @example
2682	* ```ts
2683	* let llb = new LngLatBounds(
2684	*   new LngLat(-73.9876, 40.7661),
2685	*   new LngLat(-73.9397, 40.8002)
2686	* );
2687	*
2688	* let ll = new LngLat(-73.9567, 40.7789);
2689	*
2690	* console.log(llb.contains(ll)); // = true
2691	* ```
2692	*/
2693	contains(lnglat) {
2694		const { lng, lat } = LngLat.convert(lnglat);
2695		const containsLatitude = this._sw.lat <= lat && lat <= this._ne.lat;
2696		let containsLongitude = this._sw.lng <= lng && lng <= this._ne.lng;
2697		if (this._sw.lng > this._ne.lng) containsLongitude = this._sw.lng >= lng && lng >= this._ne.lng;
2698		return containsLatitude && containsLongitude;
2699	}
2700	/**
2701	* Checks if this bounding box intersects with another bounding box.
2702	*
2703	* Returns true if the bounding boxes share any area, including cases where
2704	* they only touch along an edge or at a corner.
2705	*
2706	* This method properly handles cases where either or both bounding boxes cross
2707	* the antimeridian (date line).
2708	*/
2709	intersects(other) {
2710		other = LngLatBounds.convert(other);
2711		if (!(other.getNorth() >= this.getSouth() && other.getSouth() <= this.getNorth())) return false;
2712		const thisSpan = Math.abs(this.getEast() - this.getWest());
2713		const otherSpan = Math.abs(other.getEast() - other.getWest());
2714		if (thisSpan >= 360 || otherSpan >= 360) return true;
2715		const thisWest = wrap(this.getWest(), -180, 180);
2716		const thisEast = wrap(this.getEast(), -180, 180);
2717		const otherWest = wrap(other.getWest(), -180, 180);
2718		const otherEast = wrap(other.getEast(), -180, 180);
2719		const thisWraps = thisWest > thisEast;
2720		const otherWraps = otherWest > otherEast;
2721		if (thisWraps && otherWraps) return true;
2722		if (thisWraps) return otherEast >= thisWest || otherWest <= thisEast;
2723		if (otherWraps) return thisEast >= otherWest || thisWest <= otherEast;
2724		return otherWest <= thisEast && otherEast >= thisWest;
2725	}
2726	/**
2727	* Converts an array to a `LngLatBounds` object.
2728	*
2729	* If a `LngLatBounds` object is passed in, the function returns it unchanged.
2730	*
2731	* Internally, the function calls {@link LngLat.convert} to convert arrays to `LngLat` values.
2732	*
2733	* @param input - An array of two coordinates to convert, or a `LngLatBounds` object to return.
2734	* @returns A new `LngLatBounds` object, if a conversion occurred, or the original `LngLatBounds` object.
2735	* @example
2736	* ```ts
2737	* let arr = [[-73.9876, 40.7661], [-73.9397, 40.8002]];
2738	* let llb = LngLatBounds.convert(arr); // = LngLatBounds {_sw: LngLat {lng: -73.9876, lat: 40.7661}, _ne: LngLat {lng: -73.9397, lat: 40.8002}}
2739	* ```
2740	*/
2741	static convert(input) {
2742		if (input instanceof LngLatBounds) return input;
2743		if (!input) return input;
2744		return new LngLatBounds(input);
2745	}
2746	/**
2747	* Returns a `LngLatBounds` from the coordinates extended by a given `radius`. The returned `LngLatBounds` completely contains the `radius`.
2748	*
2749	* @param center - center coordinates of the new bounds.
2750	* @param radius - Distance in meters from the coordinates to extend the bounds.
2751	* @returns A new `LngLatBounds` object representing the coordinates extended by the `radius`.
2752	* @example
2753	* ```ts
2754	* let center = new LngLat(-73.9749, 40.7736);
2755	* LngLatBounds.fromLngLat(100).toArray(); // = [[-73.97501862141328, 40.77351016847229], [-73.97478137858673, 40.77368983152771]]
2756	* ```
2757	*/
2758	static fromLngLat(center, radius = 0) {
2759		const latAccuracy = 360 * radius / 40075017, lngAccuracy = latAccuracy / Math.cos(Math.PI / 180 * center.lat);
2760		return new LngLatBounds(new LngLat(center.lng - lngAccuracy, center.lat - latAccuracy), new LngLat(center.lng + lngAccuracy, center.lat + latAccuracy));
2761	}
2762	/**
2763	* Adjusts the given bounds to handle the case where the bounds cross the 180th meridian (antimeridian).
2764	*
2765	* @returns The adjusted LngLatBounds
2766	* @example
2767	* ```ts
2768	* let bounds = new LngLatBounds([175.813127, -20.157768], [-178. 340903, -15.449124]);
2769	* let adjustedBounds = bounds.adjustAntiMeridian();
2770	* // adjustedBounds will be: [[175.813127, -20.157768], [181.659097, -15.449124]]
2771	* ```
2772	*/
2773	adjustAntiMeridian() {
2774		const sw = new LngLat(this._sw.lng, this._sw.lat);
2775		const ne = new LngLat(this._ne.lng, this._ne.lat);
2776		if (sw.lng > ne.lng) return new LngLatBounds(sw, new LngLat(ne.lng + 360, ne.lat));
2777		return new LngLatBounds(sw, ne);
2778	}
2779};
2780//#endregion
2781//#region src/tile/tile_bounds.ts
2782var TileBounds = class {
2783	constructor(bounds, minzoom, maxzoom) {
2784		this.bounds = LngLatBounds.convert(this.validateBounds(bounds));
2785		this.minzoom = minzoom || 0;
2786		this.maxzoom = maxzoom || 24;
2787	}
2788	validateBounds(bounds) {
2789		if (!Array.isArray(bounds) || bounds.length !== 4) return [
2790			-180,
2791			-90,
2792			180,
2793			90
2794		];
2795		return [
2796			Math.max(-180, bounds[0]),
2797			Math.max(-90, bounds[1]),
2798			Math.min(180, bounds[2]),
2799			Math.min(90, bounds[3])
2800		];
2801	}
2802	contains(tileID) {
2803		const worldSize = Math.pow(2, tileID.z);
2804		const level = {
2805			minX: Math.floor(mercatorXfromLng(this.bounds.getWest()) * worldSize),
2806			minY: Math.floor(mercatorYfromLat(this.bounds.getNorth()) * worldSize),
2807			maxX: Math.ceil(mercatorXfromLng(this.bounds.getEast()) * worldSize),
2808			maxY: Math.ceil(mercatorYfromLat(this.bounds.getSouth()) * worldSize)
2809		};
2810		return tileID.x >= level.minX && tileID.x < level.maxX && tileID.y >= level.minY && tileID.y < level.maxY;
2811	}
2812};
2813//#endregion
2814//#region src/source/vector_tile_source.ts
2815/**
2816* A source containing vector tiles in [Maplibre Vector Tile format](https://maplibre.org/maplibre-tile-spec/) or [Mapbox Vector Tile format](https://docs.mapbox.com/vector-tiles/reference/).
2817* (See the [Style Specification](https://maplibre.org/maplibre-style-spec/) for detailed documentation of options.)
2818*
2819* @group Sources
2820*
2821* @example
2822* ```ts
2823* map.addSource('some id', {
2824*     type: 'vector',
2825*     url: 'https://demotiles.maplibre.org/tiles/tiles.json'
2826* });
2827* ```
2828*
2829* @example
2830* ```ts
2831* map.addSource('some id', {
2832*     type: 'vector',
2833*     tiles: ['https://d25uarhxywzl1j.cloudfront.net/v0.1/{z}/{x}/{y}.mvt'],
2834*     minzoom: 6,
2835*     maxzoom: 14
2836* });
2837* ```
2838*
2839* @example
2840* ```ts
2841* map.getSource('some id').setUrl("https://demotiles.maplibre.org/tiles/tiles.json");
2842* ```
2843*
2844* @example
2845* ```ts
2846* map.getSource('some id').setTiles(['https://d25uarhxywzl1j.cloudfront.net/v0.1/{z}/{x}/{y}.mvt']);
2847* ```
2848* @see [Add a vector tile source](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-vector-tile-source/)
2849*/
2850var VectorTileSource = class extends Evented {
2851	constructor(id, options, dispatcher, eventedParent) {
2852		super();
2853		this.id = id;
2854		this.dispatcher = dispatcher;
2855		this.type = "vector";
2856		this.minzoom = 0;
2857		this.maxzoom = 22;
2858		this.scheme = "xyz";
2859		this.tileSize = 512;
2860		this.reparseOverscaled = true;
2861		this.isTileClipped = true;
2862		this._loaded = false;
2863		extend(this, pick(options, [
2864			"url",
2865			"scheme",
2866			"tileSize",
2867			"promoteId",
2868			"encoding"
2869		]));
2870		this._options = extend({ type: "vector" }, options);
2871		this._collectResourceTiming = options.collectResourceTiming;
2872		if (this.tileSize !== 512) throw new Error("vector tile sources must have a tileSize of 512");
2873		this.setEventedParent(eventedParent);
2874	}
2875	async load(sourceDataChanged = false) {
2876		this._loaded = false;
2877		this.fire(new MapSourceDataEvent("dataloading"));
2878		this._tileJSONRequest = new AbortController();
2879		try {
2880			const tileJSON = await loadTileJson(this._options, this.map._requestManager, this._tileJSONRequest, this.map._ownerWindow);
2881			this._tileJSONRequest = null;
2882			this._loaded = true;
2883			if (tileJSON) {
2884				extend(this, tileJSON);
2885				if (tileJSON.bounds) this.tileBounds = new TileBounds(tileJSON.bounds, this.minzoom, this.maxzoom);
2886				this.fire(new MapSourceDataEvent("data", { sourceDataType: "metadata" }));
2887				this.fire(new MapSourceDataEvent("data", {
2888					sourceDataType: "content",
2889					sourceDataChanged
2890				}));
2891			}
2892		} catch (err) {
2893			this._tileJSONRequest = null;
2894			this._loaded = true;
2895			if (!isAbortError(err)) this.fire(new ErrorEvent(ensureError(err)));
2896		}
2897	}
2898	loaded() {
2899		return this._loaded;
2900	}
2901	hasTile(tileID) {
2902		return !this.tileBounds || this.tileBounds.contains(tileID.canonical);
2903	}
2904	onAdd(map) {
2905		this.map = map;
2906		this.load();
2907	}
2908	setSourceProperty(callback) {
2909		if (this._tileJSONRequest) this._tileJSONRequest.abort();
2910		callback();
2911		this.load(true);
2912	}
2913	/**
2914	* Sets the source `tiles` property and re-renders the map.
2915	*
2916	* @param tiles - An array of one or more tile source URLs, as in the TileJSON spec.
2917	*/
2918	setTiles(tiles) {
2919		this.setSourceProperty(() => {
2920			this.tiles = tiles;
2921			this._options.tiles = tiles;
2922		});
2923		return this;
2924	}
2925	/**
2926	* Sets the source `url` property and re-renders the map.
2927	*
2928	* @param url - A URL to a TileJSON resource. Supported protocols are `http:` and `https:`.
2929	*/
2930	setUrl(url) {
2931		this.setSourceProperty(() => {
2932			this.url = url;
2933			this._options.url = url;
2934		});
2935		return this;
2936	}
2937	onRemove() {
2938		if (this._tileJSONRequest) {
2939			this._tileJSONRequest.abort();
2940			this._tileJSONRequest = null;
2941		}
2942	}
2943	serialize() {
2944		return extend({}, this._options);
2945	}
2946	async loadTile(tile) {
2947		const url = tile.tileID.canonical.url(this.tiles, this.map.getPixelRatio(), this.scheme);
2948		const params = {
2949			request: await this.map._requestManager.transformRequest(url, "Tile"),
2950			uid: tile.uid,
2951			tileID: tile.tileID,
2952			zoom: tile.tileID.overscaledZ,
2953			tileSize: this.tileSize * tile.tileID.overscaleFactor(),
2954			type: this.type,
2955			source: this.id,
2956			pixelRatio: this.map.getPixelRatio(),
2957			showCollisionBoxes: this.map.showCollisionBoxes,
2958			promoteId: this.promoteId,
2959			subdivisionGranularity: this.map.style.projection.subdivisionGranularity,
2960			encoding: this.encoding,
2961			overzoomParameters: await this._getOverzoomParameters(tile),
2962			etag: tile.etag
2963		};
2964		params.request.collectResourceTiming = this._collectResourceTiming;
2965		await this.dispatcher.waitForInitComplete();
2966		let messageType = "RT";
2967		if (!tile.actor || tile.state === "expired") {
2968			tile.actor = this.dispatcher.getReadyActor();
2969			messageType = "LT";
2970		} else if (tile.state === "loading") return new Promise((resolve, reject) => {
2971			tile.reloadPromise = {
2972				resolve,
2973				reject
2974			};
2975		});
2976		tile.abortController = new AbortController();
2977		try {
2978			const data = await tile.actor.sendAsync({
2979				type: messageType,
2980				data: params
2981			}, tile.abortController);
2982			delete tile.abortController;
2983			if (tile.aborted) return;
2984			this._afterTileLoadWorkerResponse(tile, data);
2985			const result = {};
2986			if (data?.etagUnmodified) result.unmodified = true;
2987			return result;
2988		} catch (err) {
2989			delete tile.abortController;
2990			if (tile.aborted || isAbortError(err)) return;
2991			if (err && err.status !== 404) throw err;
2992			this._afterTileLoadWorkerResponse(tile, null);
2993		}
2994	}
2995	/**
2996	* When the requested tile has a higher canonical Z than source maxzoom, pass overzoom parameters so worker can load the
2997	* deepest tile at source max zoom to generate sub tiles using geojsonvt for highest performance on vector overscaling
2998	*/
2999	async _getOverzoomParameters(tile) {
3000		if (tile.tileID.canonical.z <= this.maxzoom) return;
3001		if (this.map._zoomLevelsToOverscale === void 0) return;
3002		const maxZoomTileID = tile.tileID.scaledTo(this.maxzoom).canonical;
3003		const maxZoomTileUrl = maxZoomTileID.url(this.tiles, this.map.getPixelRatio(), this.scheme);
3004		return {
3005			maxZoomTileID,
3006			overzoomRequest: await this.map._requestManager.transformRequest(maxZoomTileUrl, "Tile")
3007		};
3008	}
3009	_afterTileLoadWorkerResponse(tile, data) {
3010		if (data?.resourceTiming) tile.resourceTiming = data.resourceTiming;
3011		if (data && this.map._refreshExpiredTiles) tile.setExpiryData(data);
3012		tile.etag = data?.etag;
3013		tile.loadVectorData(data, this.map.painter);
3014		if (tile.reloadPromise) {
3015			const reloadPromise = tile.reloadPromise;
3016			tile.reloadPromise = null;
3017			this.loadTile(tile).then(reloadPromise.resolve).catch(reloadPromise.reject);
3018		}
3019	}
3020	async abortTile(tile) {
3021		if (tile.abortController) {
3022			tile.abortController.abort();
3023			delete tile.abortController;
3024		}
3025		if (tile.actor) await tile.actor.sendAsync({
3026			type: "AT",
3027			data: {
3028				uid: tile.uid,
3029				type: this.type,
3030				source: this.id
3031			}
3032		});
3033	}
3034	async unloadTile(tile) {
3035		tile.unloadVectorData();
3036		if (tile.actor) await tile.actor.sendAsync({
3037			type: "RMT",
3038			data: {
3039				uid: tile.uid,
3040				type: this.type,
3041				source: this.id
3042			}
3043		});
3044	}
3045	hasTransition() {
3046		return false;
3047	}
3048};
3049//#endregion
3050//#region src/source/raster_tile_source.ts
3051/**
3052* A source containing raster tiles (See the [raster source documentation](https://maplibre.org/maplibre-style-spec/sources/#raster) for detailed documentation of options.)
3053*
3054* @group Sources
3055*
3056* \> ℹ️ **Note:** The default `tileSize` is `512`. If your tile provider (such as OpenStreetMap or Stadia Maps) serves 256px tiles, set `tileSize: 256` manually to avoid blurry rendering due to upscaling.
3057*
3058* @example
3059* ```ts
3060* map.addSource('raster-source', {
3061*     'type': 'raster',
3062*     'tiles': ['https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg'],
3063*     'tileSize': 256, // Set this to match tile server output to avoid blurry rendering
3064* });
3065* ```
3066*
3067* @example
3068* ```ts
3069* map.addSource('wms-test-source', {
3070*      'type': 'raster',
3071* // use the tiles option to specify a WMS tile source URL
3072*      'tiles': [
3073*          'https://img.nj.gov/imagerywms/Natural2015?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&transparent=true&width=256&height=256&layers=Natural2015'
3074*      ],
3075*      'tileSize': 256 // Important for WMS if tiles are 256px
3076* });
3077* ```
3078* @see [Add a raster tile source](https://maplibre.org/maplibre-gl-js/docs/examples/map-tiles/)
3079* @see [Add a WMS source](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-wms-source/)
3080* @see [Display a satellite map](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-satellite-map/)
3081*/
3082var RasterTileSource = class extends Evented {
3083	constructor(id, options, dispatcher, eventedParent) {
3084		super();
3085		this.id = id;
3086		this.dispatcher = dispatcher;
3087		this.setEventedParent(eventedParent);
3088		this.type = "raster";
3089		this.minzoom = 0;
3090		this.maxzoom = 22;
3091		this.roundZoom = true;
3092		this.scheme = "xyz";
3093		this.tileSize = 512;
3094		this._loaded = false;
3095		this._premultiplyAlpha = true;
3096		this._options = extend({ type: "raster" }, options);
3097		extend(this, pick(options, [
3098			"url",
3099			"scheme",
3100			"tileSize"
3101		]));
3102	}
3103	async load(sourceDataChanged = false) {
3104		this._loaded = false;
3105		this.fire(new MapSourceDataEvent("dataloading"));
3106		this._tileJSONRequest = new AbortController();
3107		try {
3108			const tileJSON = await loadTileJson(this._options, this.map._requestManager, this._tileJSONRequest, this.map._ownerWindow);
3109			this._tileJSONRequest = null;
3110			this._loaded = true;
3111			if (tileJSON) {
3112				extend(this, tileJSON);
3113				if (tileJSON.bounds) this.tileBounds = new TileBounds(tileJSON.bounds, this.minzoom, this.maxzoom);
3114				this.fire(new MapSourceDataEvent("data", { sourceDataType: "metadata" }));
3115				this.fire(new MapSourceDataEvent("data", {
3116					sourceDataType: "content",
3117					sourceDataChanged
3118				}));
3119			}
3120		} catch (err) {
3121			this._tileJSONRequest = null;
3122			this._loaded = true;
3123			if (!isAbortError(err)) this.fire(new ErrorEvent(ensureError(err)));
3124		}
3125	}
3126	loaded() {
3127		return this._loaded;
3128	}
3129	onAdd(map) {
3130		this.map = map;
3131		this.load();
3132	}
3133	onRemove() {
3134		if (this._tileJSONRequest) {
3135			this._tileJSONRequest.abort();
3136			this._tileJSONRequest = null;
3137		}
3138	}
3139	setSourceProperty(callback) {
3140		if (this._tileJSONRequest) {
3141			this._tileJSONRequest.abort();
3142			this._tileJSONRequest = null;
3143		}
3144		callback();
3145		this.load(true);
3146	}
3147	/**
3148	* Sets the source `tiles` property and re-renders the map.
3149	*
3150	* @param tiles - An array of one or more tile source URLs, as in the raster tiles spec (See the [Style Specification](https://maplibre.org/maplibre-style-spec/)
3151	*/
3152	setTiles(tiles) {
3153		this.setSourceProperty(() => {
3154			this.tiles = tiles;
3155			this._options.tiles = tiles;
3156		});
3157		return this;
3158	}
3159	/**
3160	* Sets the source `url` property and re-renders the map.
3161	*
3162	* @param url - A URL to a TileJSON resource. Supported protocols are `http:` and `https:`.
3163	*/
3164	setUrl(url) {
3165		this.setSourceProperty(() => {
3166			this.url = url;
3167			this._options.url = url;
3168		});
3169		return this;
3170	}
3171	serialize() {
3172		return extend({}, this._options);
3173	}
3174	/**
3175	* Sets whether alpha premultiplication is applied to raster tile images.
3176	* Set to `false` to preserve exact RGBA byte values when alpha carries data instead of opacity.
3177	*
3178	* @param premultiplyAlpha - If `false`, disables alpha premultiplication for raster tile image decode and texture upload.
3179	* @example
3180	* ```ts
3181	* map.getSource<RasterTileSource>('raster-source').setPremultiplyAlpha(false);
3182	* ```
3183	*/
3184	setPremultiplyAlpha(premultiplyAlpha) {
3185		if (this._premultiplyAlpha === premultiplyAlpha) return this;
3186		this.setSourceProperty(() => {
3187			this._premultiplyAlpha = premultiplyAlpha;
3188		});
3189		return this;
3190	}
3191	hasTile(tileID) {
3192		return !this.tileBounds || this.tileBounds.contains(tileID.canonical);
3193	}
3194	async loadTile(tile) {
3195		const url = tile.tileID.canonical.url(this.tiles, this.map.getPixelRatio(), this.scheme);
3196		const premultiply = this._premultiplyAlpha;
3197		const imageBitmapOptions = premultiply ? void 0 : { premultiplyAlpha: "none" };
3198		tile.abortController = new AbortController();
3199		try {
3200			const response = await ImageRequest.transformAndGetImage(this.map._requestManager, url, "Tile", tile.abortController, this.map._refreshExpiredTiles, imageBitmapOptions);
3201			delete tile.abortController;
3202			if (tile.aborted) {
3203				tile.state = "unloaded";
3204				return;
3205			}
3206			if (response) {
3207				if (this.map._refreshExpiredTiles && (response.cacheControl || response.expires)) tile.setExpiryData({
3208					cacheControl: response.cacheControl,
3209					expires: response.expires
3210				});
3211				const context = this.map.painter.context;
3212				const gl = context.gl;
3213				const img = response.data ?? new RGBAImage({
3214					width: 1,
3215					height: 1
3216				}, /* @__PURE__ */ new Uint8Array(4));
3217				tile.texture = this.map.painter.getTileTexture(img.width);
3218				if (tile.texture) tile.texture.update(img, {
3219					useMipmap: true,
3220					premultiply
3221				});
3222				else {
3223					tile.texture = new Texture(context, img, gl.RGBA, {
3224						useMipmap: true,
3225						premultiply
3226					});
3227					tile.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
3228				}
3229				tile.state = "loaded";
3230			}
3231		} catch (err) {
3232			delete tile.abortController;
3233			if (tile.aborted) tile.state = "unloaded";
3234			else if (err) {
3235				tile.state = "errored";
3236				throw err;
3237			}
3238		}
3239	}
3240	async abortTile(tile) {
3241		if (tile.abortController) {
3242			tile.abortController.abort();
3243			delete tile.abortController;
3244		}
3245	}
3246	async unloadTile(tile) {
3247		if (tile.texture) this.map.painter.saveTileTexture(tile.texture);
3248	}
3249	hasTransition() {
3250		return false;
3251	}
3252};
3253//#endregion
3254//#region src/source/raster_dem_tile_source.ts
3255/**
3256* A source containing raster DEM tiles (See the [Style Specification](https://maplibre.org/maplibre-style-spec/) for detailed documentation of options.)
3257* This source can be used to show hillshading and 3D terrain
3258*
3259* @group Sources
3260*
3261* @example
3262* ```ts
3263* map.addSource('raster-dem-source', {
3264*      type: 'raster-dem',
3265*      url: 'https://demotiles.maplibre.org/terrain-tiles/tiles.json',
3266*      tileSize: 256
3267* });
3268* ```
3269* @see [3D Terrain](https://maplibre.org/maplibre-gl-js/docs/examples/3d-terrain/)
3270*/
3271var RasterDEMTileSource = class extends RasterTileSource {
3272	constructor(id, options, dispatcher, eventedParent) {
3273		super(id, options, dispatcher, eventedParent);
3274		this.type = "raster-dem";
3275		this.maxzoom = 22;
3276		this._options = extend({ type: "raster-dem" }, options);
3277		this.encoding = options.encoding || "mapbox";
3278		this.redFactor = options.redFactor;
3279		this.greenFactor = options.greenFactor;
3280		this.blueFactor = options.blueFactor;
3281		this.baseShift = options.baseShift;
3282	}
3283	async loadTile(tile) {
3284		const url = tile.tileID.canonical.url(this.tiles, this.map.getPixelRatio(), this.scheme);
3285		tile.neighboringTiles = this._getNeighboringTiles(tile.tileID);
3286		tile.abortController = new AbortController();
3287		try {
3288			const response = await ImageRequest.transformAndGetImage(this.map._requestManager, url, "Tile", tile.abortController, this.map._refreshExpiredTiles, { colorSpaceConversion: "none" });
3289			delete tile.abortController;
3290			if (tile.aborted) {
3291				tile.state = "unloaded";
3292				return;
3293			}
3294			if (response) {
3295				if (this.map._refreshExpiredTiles && (response.cacheControl || response.expires)) tile.setExpiryData({
3296					cacheControl: response.cacheControl,
3297					expires: response.expires
3298				});
3299				if (!response.data) {
3300					tile.state = "loaded";
3301					return;
3302				}
3303				const img = response.data;
3304				const rawImageData = isImageBitmap(img) && offscreenCanvasSupported() ? img : await this.readImageNow(img);
3305				const params = {
3306					type: this.type,
3307					uid: tile.uid,
3308					source: this.id,
3309					rawImageData,
3310					encoding: this.encoding,
3311					redFactor: this.redFactor,
3312					greenFactor: this.greenFactor,
3313					blueFactor: this.blueFactor,
3314					baseShift: this.baseShift
3315				};
3316				if (tile.actor && tile.state !== "expired" && tile.state !== "reloading") return;
3317				await this.dispatcher.waitForInitComplete();
3318				if (!tile.actor || tile.state === "expired") tile.actor = this.dispatcher.getReadyActor();
3319				tile.dem = await tile.actor.sendAsync({
3320					type: "LDT",
3321					data: params
3322				});
3323				tile.needsHillshadePrepare = true;
3324				tile.needsTerrainPrepare = true;
3325				tile.needsColorReliefPrepare = true;
3326				tile.state = "loaded";
3327			}
3328		} catch (err) {
3329			delete tile.abortController;
3330			if (tile.aborted) tile.state = "unloaded";
3331			else if (err) {
3332				tile.state = "errored";
3333				throw err;
3334			}
3335		}
3336	}
3337	async readImageNow(img) {
3338		if (typeof VideoFrame !== "undefined" && isOffscreenCanvasDistorted()) {
3339			const width = img.width + 4;
3340			const height = img.height + 4;
3341			try {
3342				return new RGBAImage({
3343					width,
3344					height
3345				}, await readImageUsingVideoFrame(img, -2, -2, width, height));
3346			} catch {}
3347		}
3348		return browser.getImageData(img, 2);
3349	}
3350	_getNeighboringTiles(tileID) {
3351		const canonical = tileID.canonical;
3352		const dim = Math.pow(2, canonical.z);
3353		const px = (canonical.x - 1 + dim) % dim;
3354		const pxw = canonical.x === 0 ? tileID.wrap - 1 : tileID.wrap;
3355		const nx = (canonical.x + 1 + dim) % dim;
3356		const nxw = canonical.x + 1 === dim ? tileID.wrap + 1 : tileID.wrap;
3357		const neighboringTiles = {};
3358		neighboringTiles[new OverscaledTileID(tileID.overscaledZ, pxw, canonical.z, px, canonical.y).key] = { backfilled: false };
3359		neighboringTiles[new OverscaledTileID(tileID.overscaledZ, nxw, canonical.z, nx, canonical.y).key] = { backfilled: false };
3360		if (canonical.y > 0) {
3361			neighboringTiles[new OverscaledTileID(tileID.overscaledZ, pxw, canonical.z, px, canonical.y - 1).key] = { backfilled: false };
3362			neighboringTiles[new OverscaledTileID(tileID.overscaledZ, tileID.wrap, canonical.z, canonical.x, canonical.y - 1).key] = { backfilled: false };
3363			neighboringTiles[new OverscaledTileID(tileID.overscaledZ, nxw, canonical.z, nx, canonical.y - 1).key] = { backfilled: false };
3364		}
3365		if (canonical.y + 1 < dim) {
3366			neighboringTiles[new OverscaledTileID(tileID.overscaledZ, pxw, canonical.z, px, canonical.y + 1).key] = { backfilled: false };
3367			neighboringTiles[new OverscaledTileID(tileID.overscaledZ, tileID.wrap, canonical.z, canonical.x, canonical.y + 1).key] = { backfilled: false };
3368			neighboringTiles[new OverscaledTileID(tileID.overscaledZ, nxw, canonical.z, nx, canonical.y + 1).key] = { backfilled: false };
3369		}
3370		return neighboringTiles;
3371	}
3372	async unloadTile(tile) {
3373		if (tile.demTexture) this.map.painter.saveTileTexture(tile.demTexture);
3374		if (tile.fbo) {
3375			tile.fbo.destroy();
3376			delete tile.fbo;
3377		}
3378		if (tile.dem) delete tile.dem;
3379		delete tile.neighboringTiles;
3380		tile.state = "unloaded";
3381		if (tile.actor) await tile.actor.sendAsync({
3382			type: "RDT",
3383			data: {
3384				type: this.type,
3385				uid: tile.uid,
3386				source: this.id
3387			}
3388		});
3389	}
3390};
3391//#endregion
3392//#region src/source/geojson_source_diff.ts
3393function getFeatureId(feature, promoteId) {
3394	return promoteId ? feature.properties[promoteId] : feature.id;
3395}
3396/**
3397* Converts a GeoJSON object into a map of feature IDs to GeoJSON features.
3398* @param data - The GeoJSON object to convert.
3399* @param promoteId - If set, the feature id will be set to the promoteId property value.
3400* @returns A map of feature IDs to GeoJSON features, or `undefined` if the GeoJSON object is not a valid updateable object.
3401*
3402* Features must have unique identifiers to be updateable. IDs can come from:
3403* - The feature's `id` property (standard GeoJSON)
3404* - A promoted property specified by `promoteId` (e.g., a "name" property)
3405*/
3406function toUpdateable(data, promoteId) {
3407	const updateable = /* @__PURE__ */ new Map();
3408	if (data == null) return updateable;
3409	if (data.type == null) return updateable;
3410	if (data.type === "Feature") {
3411		const id = getFeatureId(data, promoteId);
3412		if (id == null) return void 0;
3413		updateable.set(id, data);
3414		return updateable;
3415	}
3416	if (data.type === "FeatureCollection") {
3417		const seenIds = /* @__PURE__ */ new Set();
3418		for (const feature of data.features) {
3419			const id = getFeatureId(feature, promoteId);
3420			if (id == null) return void 0;
3421			if (seenIds.has(id)) return void 0;
3422			seenIds.add(id);
3423			updateable.set(id, feature);
3424		}
3425		return updateable;
3426	}
3427}
3428/**
3429* Mutates updateable and applies a {@link GeoJSONSourceDiff}. Operations are processed in a specific order to ensure predictable behavior:
3430* 1. Remove operations (removeAll, remove)
3431* 2. Add operations (add)
3432* 3. Update operations (update)
3433* @returns an array of geometries that were affected by the diff - with the exception of removeAll which does not track any affected geometries.
3434*/
3435function applySourceDiff(updateable, diff, promoteId) {
3436	const affectedGeometries = [];
3437	if (diff.removeAll) updateable.clear();
3438	else if (diff.remove) for (const id of diff.remove) {
3439		const existing = updateable.get(id);
3440		if (!existing) continue;
3441		affectedGeometries.push(existing.geometry);
3442		updateable.delete(id);
3443	}
3444	if (diff.add) for (const feature of diff.add) {
3445		const id = getFeatureId(feature, promoteId);
3446		if (id == null) continue;
3447		const existing = updateable.get(id);
3448		if (existing) affectedGeometries.push(existing.geometry);
3449		affectedGeometries.push(feature.geometry);
3450		updateable.set(id, feature);
3451	}
3452	if (diff.update) for (const update of diff.update) {
3453		const existing = updateable.get(update.id);
3454		if (!existing) continue;
3455		const changeGeometry = !!update.newGeometry;
3456		const changeProps = update.removeAllProperties || update.removeProperties?.length > 0 || update.addOrUpdateProperties?.length > 0;
3457		if (!changeGeometry && !changeProps) continue;
3458		affectedGeometries.push(existing.geometry);
3459		const feature = { ...existing };
3460		updateable.set(update.id, feature);
3461		if (changeGeometry) {
3462			affectedGeometries.push(update.newGeometry);
3463			feature.geometry = update.newGeometry;
3464		}
3465		if (changeProps) {
3466			if (update.removeAllProperties) feature.properties = {};
3467			else feature.properties = { ...feature.properties || {} };
3468			if (update.removeProperties) for (const key of update.removeProperties) delete feature.properties[key];
3469			if (update.addOrUpdateProperties) for (const { key, value } of update.addOrUpdateProperties) feature.properties[key] = value;
3470		}
3471	}
3472	return affectedGeometries;
3473}
3474/**
3475* Merge two GeoJSONSourceDiffs, considering the order of operations as specified above (remove, add, update).
3476*
3477* 
3477For `add` features that use promoteId, the feature id will be set to the promoteId value temporarily so that
3478* the merge can be completed, then reverted to the original promoteId state after the merge.
3479*/
3480function mergeSourceDiffs(prevDiff, nextDiff, promoteId) {
3481	if (!prevDiff) return nextDiff || {};
3482	if (!nextDiff) return prevDiff || {};
3483	if (promoteId) {
3484		promoteFeatureIds(prevDiff.add, promoteId);
3485		promoteFeatureIds(nextDiff.add, promoteId);
3486	}
3487	const prev = diffToHashed(prevDiff);
3488	const next = diffToHashed(nextDiff);
3489	resolveMergeConflicts(prev, next);
3490	const merged = {};
3491	if (prev.removeAll || next.removeAll) merged.removeAll = true;
3492	merged.remove = /* @__PURE__ */ new Set([...prev.remove, ...next.remove]);
3493	merged.add = new Map([...prev.add, ...next.add]);
3494	merged.update = new Map([...prev.update, ...next.update]);
3495	if (merged.remove.size && merged.add.size) for (const id of merged.add.keys()) merged.remove.delete(id);
3496	const mergedDiff = hashedToDiff(merged);
3497	if (promoteId) demoteFeatureIds(mergedDiff.add, promoteId);
3498	return mergedDiff;
3499}
3500/**
3501* Resolve merge conflicts between two GeoJSONSourceDiffs considering the ordering above (remove/add/update).
3502*
3503* - If you `removeAll` and then `add` features in the same diff, the added features will be kept.
3504* - Updates only apply to features that exist after removes and adds have been processed.
3505*/
3506function resolveMergeConflicts(prev, next) {
3507	if (next.removeAll) {
3508		prev.add.clear();
3509		prev.update.clear();
3510		prev.remove.clear();
3511		next.remove.clear();
3512	}
3513	for (const id of next.remove) {
3514		prev.add.delete(id);
3515		prev.update.delete(id);
3516	}
3517	for (const [id, nextUpdate] of next.update) {
3518		const prevUpdate = prev.update.get(id);
3519		if (!prevUpdate) continue;
3520		next.update.set(id, mergeFeatureDiffs(prevUpdate, nextUpdate));
3521		prev.update.delete(id);
3522	}
3523}
3524/**
3525* Merge two feature diffs for the same feature id, considering the order of operations as specified above (remove, add/update).
3526*/
3527function mergeFeatureDiffs(prev, next) {
3528	const merged = { id: prev.id };
3529	if (next.removeAllProperties) {
3530		delete prev.removeProperties;
3531		delete prev.addOrUpdateProperties;
3532		delete next.removeProperties;
3533	}
3534	if (next.removeProperties && prev.addOrUpdateProperties) {
3535		const removedProperties = new Set(next.removeProperties);
3536		prev.addOrUpdateProperties = prev.addOrUpdateProperties.filter((prop) => !removedProperties.has(prop.key));
3537	}
3538	if (prev.removeAllProperties || next.removeAllProperties) merged.removeAllProperties = true;
3539	if (prev.removeProperties || next.removeProperties) merged.removeProperties = [...prev.removeProperties || [], ...next.removeProperties || []];
3540	if (prev.addOrUpdateProperties || next.addOrUpdateProperties) merged.addOrUpdateProperties = [...prev.addOrUpdateProperties || [], ...next.addOrUpdateProperties || []];
3541	if (prev.newGeometry || next.newGeometry) merged.newGeometry = next.newGeometry || prev.newGeometry;
3542	return merged;
3543}
3544/**
3545* Mutates diff.add and applies a feature id using the promoteId property
3546*/
3547function promoteFeatureIds(add, promoteId) {
3548	if (!add) return;
3549	for (const feature of add) {
3550		const id = getFeatureId(feature, promoteId);
3551		if (id != null) feature.id = id;
3552	}
3553}
3554/**
3555* Mutates diff.add and removes the feature id if using the promoteId property
3556*/
3557function demoteFeatureIds(add, promoteId) {
3558	if (!add) return;
3559	for (const feature of add) if (getFeatureId(feature, promoteId) != null) delete feature.id;
3560}
3561/**
3562* @internal
3563* Convert a GeoJSONSourceDiff to an idempotent hashed representation using Sets and Maps
3564*/
3565function diffToHashed(diff) {
3566	if (!diff) return {};
3567	const hashed = {};
3568	hashed.removeAll = diff.removeAll;
3569	hashed.remove = new Set(diff.remove || []);
3570	hashed.add = new Map(diff.add?.map((feature) => [feature.id
vendor: 5,623 bytes, lines 3570-3721
3570, feature]));
3571	hashed.update = new Map(diff.update?.map((update) => [update.id, update]));
3572	return hashed;
3573}
3574/**
3575* @internal
3576* Convert a hashed GeoJSONSourceDiff back to the array-based representation
3577*/
3578function hashedToDiff(hashed) {
3579	const diff = {};
3580	if (hashed.removeAll) diff.removeAll = hashed.removeAll;
3581	if (hashed.remove) diff.remove = Array.from(hashed.remove);
3582	if (hashed.add) diff.add = Array.from(hashed.add.values());
3583	if (hashed.update) diff.update = Array.from(hashed.update.values());
3584	return diff;
3585}
3586//#endregion
3587//#region src/util/geojson_bounds.ts
3588function extractCoordinates(coords) {
3589	if (!coords || coords.length === 0) return [];
3590	if (typeof coords[0] === "number") return [coords];
3591	return coords.flatMap((c) => extractCoordinates(c));
3592}
3593function getCoordinatesFromGeometry(geometry) {
3594	if (geometry.type === "GeometryCollection") return geometry.geometries.flatMap((g) => getCoordinatesFromGeometry(g));
3595	return extractCoordinates(geometry.coordinates);
3596}
3597/**
3598* Calculates the bounding box of GeoJSON data.
3599* @param data - The GeoJSON data to calculate bounds for
3600* @returns The bounding box of the GeoJSON data
3601*/
3602function getGeoJSONBounds(data) {
3603	const bounds = new LngLatBounds();
3604	let coordinates;
3605	switch (data.type) {
3606		case "FeatureCollection":
3607			coordinates = data.features.flatMap((f) => getCoordinatesFromGeometry(f.geometry));
3608			break;
3609		case "Feature":
3610			coordinates = getCoordinatesFromGeometry(data.geometry);
3611			break;
3612		default: coordinates = getCoordinatesFromGeometry(data);
3613	}
3614	if (coordinates.length === 0) return bounds;
3615	for (const coordinate of coordinates) {
3616		const [lng, lat] = coordinate;
3617		bounds.extend([lng, lat]);
3618	}
3619	return bounds;
3620}
3621//#endregion
3622//#region src/tile/tile_id_to_lng_lat_bounds.ts
3623function tileIdToLngLatBounds({ x, y, z }, buffer = 0) {
3624	const lngMin = lngFromMercatorX((x - buffer) / Math.pow(2, z));
3625	const latMin = latFromMercatorY((y + 1 + buffer) / Math.pow(2, z));
3626	const lngMax = lngFromMercatorX((x + 1 + buffer) / Math.pow(2, z));
3627	const latMax = latFromMercatorY((y - buffer) / Math.pow(2, z));
3628	return new LngLatBounds([lngMin, latMin], [lngMax, latMax]);
3629}
3630//#endregion
3631//#region src/source/geojson_source.ts
3632/**
3633* A source containing GeoJSON.
3634* (See the [Style Specification](https://maplibre.org/maplibre-style-spec/#sources-geojson) for detailed documentation of options.)
3635*
3636* GeoJSON is tiled internally for rendering. Features exposed from rendered tiles and related events come from vector-tile data, so GeoJSON foreign members that cannot be represented by the vector-tile format are not preserved there. Keep that data separately, or map it to supported feature properties, if you need it after tiling.
3637*
3638* @group Sources
3639*
3640* @example
3641* ```ts
3642* map.addSource('some id', {
3643*     type: 'geojson',
3644*     data: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_ports.geojson'
3645* });
3646* ```
3647*
3648* @example
3649* ```ts
3650* map.addSource('some id', {
3651*    type: 'geojson',
3652*    data: {
3653*        "type": "FeatureCollection",
3654*        "features": [{
3655*            "type": "Feature",
3656*            "properties": {},
3657*            "geometry": {
3658*                "type": "Point",
3659*                "coordinates": [
3660*                    -76.53063297271729,
3661*                    39.18174077994108
3662*                ]
3663*            }
3664*        }]
3665*    }
3666* });
3667* ```
3668*
3669* @example
3670* ```ts
3671* map.getSource('some id').setData({
3672*   "type": "FeatureCollection",
3673*   "features": [{
3674*       "type": "Feature",
3675*       "properties": { "name": "Null Island" },
3676*       "geometry": {
3677*           "type": "Point",
3678*           "coordinates": [ 0, 0 ]
3679*       }
3680*   }]
3681* });
3682* ```
3683* @see [Draw GeoJSON points](https://maplibre.org/maplibre-gl-js/docs/examples/draw-geojson-points/)
3684* @see [Add a GeoJSON line](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-geojson-line/)
3685* @see [Create a heatmap from points](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-heatmap-layer/)
3686* @see [Create and style clusters](https://maplibre.org/maplibre-gl-js/docs/examples/create-and-style-clusters/)
3687*/
3688var GeoJSONSource = class extends Evented {
3689	/** @internal */
3690	constructor(id, options, dispatcher, eventedParent) {
3691		super();
3692		this.id = id;
3693		this.type = "geojson";
3694		this.minzoom = 0;
3695		this.maxzoom = 18;
3696		this.tileSize = 512;
3697		this.isTileClipped = true;
3698		this.reparseOverscaled = true;
3699		this._removed = false;
3700		this._isUpdatingWorker = false;
3701		this._pendingWorkerUpdate = { data: options.data };
3702		this.actorPromise = dispatcher.getActor();
3703		this.setEventedParent(eventedParent);
3704		this._data = typeof options.data === "string" ? { url: options.data } : { geojson: options.data };
3705		this._options = extend({}, options);
3706		this._collectResourceTiming = options.collectResourceTiming;
3707		if (options.maxzoom !== void 0) this.maxzoom = options.maxzoom;
3708		if (options.type) this.type = options.type;
3709		if (options.attribution) this.attribution = options.attribution;
3710		this.promoteId = options.promoteId;
3711		if (options.clusterMaxZoom !== void 0 && this.maxzoom <= options.clusterMaxZoom) warnOnce(`The maxzoom value "${this.maxzoom}" is expected to be greater than the clusterMaxZoom value "${options.clusterMaxZoom}".`);
3712		this.workerOptions = extend({
3713			source: this.id,
3714			geojsonVtOptions: {
3715				buffer: this._pixelsToTileUnits(options.buffer !== void 0 ? options.buffer : 128),
3716				tolerance: this._pixelsToTileUnits(options.tolerance !== void 0 ? options.tolerance : .375),
3717				extent: EXTENT,
3718				maxZoom: this.maxzoom,
3719				lineMetrics: options.lineMetrics || false,
3720				generateId: options.generateId || false,
3721				promoteId: t
3721ypeof options.promoteId === "string" ? options.promoteId : void 0,
3722				cluster: options.cluster || false,
3723				clusterOptions: {
3724					maxZoom: this._getClusterMaxZoom(options.clusterMaxZoom),
3725					minPoints: Math.max(2, options.clusterMinPoints || 2),
3726					extent: EXTENT,
3727					radius: this._pixelsToTileUnits(options.clusterRadius || 50),
3728					log: false,
3729					generateId: options.generateId || false
3730				}
3731			},
3732			clusterProperties: options.clusterProperties,
3733			filter: options.filter
3734		}, options.workerOptions);
3735	}
3736	_hasPendingWorkerUpdate() {
3737		return this._pendingWorkerUpdate.data !== void 0 || this._pendingWorkerUpdate.diff !== void 0 || this._pendingWorkerUpdate.updateCluster;
3738	}
3739	_pixelsToTileUnits(pixelValue) {
3740		return pixelValue * (EXTENT / this.tileSize);
3741	}
3742	_tileUnitsToPixels(tileUnitValue) {
3743		return tileUnitValue / (EXTENT / this.tileSize);
3744	}
3745	_getClusterMaxZoom(clusterMaxZoom) {
3746		const effectiveClusterMaxZoom = clusterMaxZoom ? Math.round(clusterMaxZoom) : this.maxzoom - 1;
3747		if (!(Number.isInteger(clusterMaxZoom) || clusterMaxZoom === void 0)) warnOnce(`Integer expected for option 'clusterMaxZoom': provided value "${clusterMaxZoom}" rounded to "${effectiveClusterMaxZoom}"`);
3748		return effectiveClusterMaxZoom;
3749	}
3750	async load() {
3751		await this._updateWorkerData();
3752	}
3753	onAdd(map) {
3754		this.map = map;
3755		this.load();
3756	}
3757	/**
3758	* Sets the GeoJSON data and re-renders the map.
3759	*
3760	* @param data - A GeoJSON data object or a URL to one. The latter is preferable in the case of large GeoJSON files.
3761	*/
3762	setData(data) {
3763		this._data = typeof data === "string" ? { url: data } : { geojson: data };
3764		this._pendingWorkerUpdate = { data };
3765		return this._updateWorkerData();
3766	}
3767	/**
3768	* Updates the source's GeoJSON, and re-renders the map.
3769	*
3770	* For sources with lots of features, this method can be used to make updates more quickly.
3771	*
3772	* This approach requires unique IDs for every feature in the source. The IDs can either be specified on the feature,
3773	* or by using the promoteId option to specify which property should be used as the ID.
3774	*
3775	* It is an error to call updateData on a source that did not have unique IDs for each of its features already.
3776	*
3777	* Updates are applied on a best-effort basis, updating an ID that does not exist will not result in an error.
3778	*
3779	* @param diff - The changes that need to be applied.
3780	*/
3781	updateData(diff) {
3782		this._pendingWorkerUpdate.diff = mergeSourceDiffs(this._pendingWorkerUpdate.diff, diff
vendor: 7,643 bytes, lines 3782-4014
3782);
3783		return this._updateWorkerData();
3784	}
3785	/**
3786	* Allows to get the source's actual GeoJSON data.
3787	*
3788	* @returns a promise which resolves to the source's actual GeoJSON data
3789	*/
3790	async getData() {
3791		if (this._data.url) await this.once("data");
3792		if (this._data.geojson) return this._data.geojson;
3793		return {
3794			type: "FeatureCollection",
3795			features: Array.from(this._data.updateable.values())
3796		};
3797	}
3798	/**
3799	* Allows getting the source's boundaries.
3800	* If there's a problem with the source's data, it will return an empty {@link LngLatBounds}.
3801	* @returns a promise which resolves to the source's boundaries
3802	*/
3803	async getBounds() {
3804		return getGeoJSONBounds(await this.getData());
3805	}
3806	/**
3807	* To disable/enable clustering on the source options
3808	* @param options - The options to set
3809	* @example
3810	* ```ts
3811	* map.getSource('some id').setClusterOptions({cluster: false});
3812	* map.getSource('some id').setClusterOptions({cluster: false, clusterRadius: 50, clusterMaxZoom: 14});
3813	* ```
3814	*/
3815	setClusterOptions(options) {
3816		this.workerOptions.geojsonVtOptions.cluster = options.cluster;
3817		if (options.clusterRadius !== void 0) this.workerOptions.geojsonVtOptions.clusterOptions.radius = this._pixelsToTileUnits(options.clusterRadius);
3818		if (options.clusterMaxZoom !== void 0) this.workerOptions.geojsonVtOptions.clusterOptions.maxZoom = this._getClusterMaxZoom(options.clusterMaxZoom);
3819		this._pendingWorkerUpdate.updateCluster = true;
3820		return this._updateWorkerData();
3821	}
3822	/**
3823	* Gets the cluster options currently configured on the source.
3824	* The returned values mirror the options accepted by `setClusterOptions`.
3825	*
3826	* @returns the source's current cluster options
3827	* @example
3828	* ```ts
3829	* const {cluster, clusterMaxZoom, clusterRadius} = map.getSource('some id').getClusterOptions();
3830	* ```
3831	*/
3832	getClusterOptions() {
3833		const { cluster, clusterOptions } = this.workerOptions.geojsonVtOptions;
3834		return {
3835			cluster,
3836			clusterMaxZoom: clusterOptions.maxZoom,
3837			clusterRadius: this._tileUnitsToPixels(clusterOptions.radius)
3838		};
3839	}
3840	/**
3841	* For clustered sources, fetches the zoom at which the given cluster expands.
3842	*
3843	* @param clusterId - The value of the cluster's `cluster_id` property.
3844	* @returns a promise that is resolved with the zoom number
3845	*/
3846	async getClusterExpansionZoom(clusterId) {
3847		return (await this.actorPromise).sendAsync({
3848			type: "GCEZ",
3849			data: {
3850				type: this.type,
3851				clusterId,
3852				source: this.id
3853			}
3854		});
3855	}
3856	/**
3857	* For clustered sources, fetches the children of the given cluster on the next zoom level (as an array of GeoJSON features).
3858	*
3859	* @param clusterId - The value of the cluster's `cluster_id` property.
3860	* @returns a promise that is resolved when the features are retrieved
3861	*/
3862	async getClusterChildren(clusterId) {
3863		return (await this.actorPromise).sendAsync({
3864			type: "GCC",
3865			data: {
3866				type: this.type,
3867				clusterId,
3868				source: this.id
3869			}
3870		});
3871	}
3872	/**
3873	* For clustered sources, fetches the original points that belong to the cluster (as an array of GeoJSON features).
3874	*
3875	* @param clusterId - The value of the cluster's `cluster_id` property.
3876	* @param limit - The maximum number of features to return.
3877	* @param offset - The number of features to skip (e.g. for pagination).
3878	* @returns a promise that is resolved when the features are retrieved
3879	* @example
3880	* Retrieve cluster leaves on click
3881	* ```ts
3882	* map.on('click', 'clusters', (e) => {
3883	*   let features = map.queryRenderedFeatures(e.point, {
3884	*     layers: ['clusters']
3885	*   });
3886	*
3887	*   let clusterId = features[0].properties.cluster_id;
3888	*   let pointCount = features[0].properties.point_count;
3889	*   let clusterSource = map.getSource('clusters');
3890	*
3891	*   const features = await clusterSource.getClusterLeaves(clusterId, pointCount);
3892	*   // Print cluster leaves in the console
3893	*   console.log('Cluster leaves:', features);
3894	* });
3895	* ```
3896	*/
3897	async getClusterLeaves(clusterId, limit, offset) {
3898		return (await this.actorPromise).sendAsync({
3899			type: "GCL",
3900			data: {
3901				type: this.type,
3902				source: this.id,
3903				clusterId,
3904				limit,
3905				offset
3906			}
3907		});
3908	}
3909	/**
3910	* Responsible for invoking WorkerSource's geojson.loadData target, which
3911	* handles loading the geojson data and preparing to serve it up as tiles,
3912	* using geojson-vt or supercluster as appropriate.
3913	*/
3914	async _updateWorkerData() {
3915		if (this._isUpdatingWorker) return this._updatePromise;
3916		if (!this._hasPendingWorkerUpdate()) {
3917			warnOnce(`No pending worker updates for GeoJSONSource ${this.id}.`);
3918			return;
3919		}
3920		const { data, diff, updateCluster } = this._pendingWorkerUpdate;
3921		const params = this._getLoadGeoJSONParameters(data, diff, updateCluster);
3922		if (data !== void 0) this._pendingWorkerUpdate.data = void 0;
3923		else if (diff) this._pendingWorkerUpdate.diff = void 0;
3924		else if (updateCluster) this._pendingWorkerUpdate.updateCluster = void 0;
3925		this._updatePromise = this._dispatchWorkerUpdate(params);
3926		await this._updatePromise;
3927	}
3928	/**
3929	* Create the parameters object that will be sent to the worker and used to load GeoJSON.
3930	*/
3931	async _getLoadGeoJSONParameters(data, diff, updateCluster) {
3932		const params = extend({
3933			type: this.type,
3934			source: this.id
3935		}, this.workerOptions);
3936		if (typeof data === "string") {
3937			params.request = await this.map._requestManager.transformRequest(browser.resolveURL(data), "Source");
3938			params.request.collectResourceTiming = this._collectResourceTiming;
3939			return params;
3940		}
3941		if (data !== void 0) {
3942			params.data = data;
3943			return params;
3944		}
3945		if (diff) {
3946			params.dataDiff = diff;
3947			return params;
3948		}
3949		if (updateCluster) {
3950			params.updateCluster = true;
3951			return params;
3952		}
3953	}
3954	/**
3955	* Send the worker update data from the main thread to the worker
3956	*/
3957	async _dispatchWorkerUpdate(optionsPromise) {
3958		this._isUpdatingWorker = true;
3959		this.fire(new MapSourceDataEvent("dataloading"));
3960		try {
3961			const options = await optionsPromise;
3962			const result = await (await this.actorPromise).sendAsync({
3963				type: "LD",
3964				data: options
3965			});
3966			this._isUpdatingWorker = false;
3967			if (this._removed || result.abandoned) {
3968				this.fire(new MapSourceDataEvent("dataabort"));
3969				return;
3970			}
3971			if (result.data) this._data = { geojson: result.data };
3972			const affectedGeometries = this._applyDiffToSource(options.dataDiff);
3973			const shouldReloadTileOptions = this._getShouldReloadTileOptions(affectedGeometries);
3974			const eventData = {};
3975			this._applyResourceTiming(eventData, result);
3976			this.fire(new MapSourceDataEvent("data", {
3977				...eventData,
3978				sourceDataType: "metadata"
3979			}));
3980			this.fire(new MapSourceDataEvent("data", {
3981				...eventData,
3982				sourceDataType: "content",
3983				shouldReloadTileOptions
3984			}));
3985		} catch (err) {
3986			this._isUpdatingWorker = false;
3987			if (this._removed) {
3988				this.fire(new MapSourceDataEvent("dataabort"));
3989				return;
3990			}
3991			this.fire(new ErrorEvent(ensureError(err)));
3992		} finally {
3993			if (this._hasPendingWorkerUpdate()) await this._updateWorkerData();
3994		}
3995	}
3996	/**
3997	* Apply resource timing data to the event object.
3998	*/
3999	_applyResourceTiming(eventData, result) {
4000		if (!this._collectResourceTiming) return;
4001		const timingData = result.resourceTiming?.[this.id];
4002		if (!timingData) return;
4003		const resourceTiming = timingData.slice(0);
4004		if (!resourceTiming?.length) return;
4005		extend(eventData, { resourceTiming });
4006	}
4007	/**
4008	* Apply a diff to this source's data and return the affected feature geometries.
4009	* @param diff - The {@link GeoJSONSourceDiff} to apply.
4010	* @returns The affected geometries, or undefined if the diff is not applicable or all geometries are affected.
4011	*/
4012	_applyDiffToSource(diff) {
4013		if (!diff) return;
4014		const promoteId = t
4014ypeof this.promoteId === "string" ? this.promoteId : void 0
vendor: 210,934 bytes, lines 4014-9134
4014;
4015		if (!this._data.url && !this._data.updateable) {
4016			const updateable = toUpdateable(this._data.geojson, promoteId);
4017			if (!updateable) throw new Error(`GeoJSONSource "${this.id}": GeoJSON data is not compatible with updateData`);
4018			this._data = { updateable };
4019		}
4020		if (!this._data.updateable) return;
4021		const affectedGeometries = applySourceDiff(this._data.updateable, diff, promoteId);
4022		if (diff.removeAll || this._options.cluster) return;
4023		return affectedGeometries;
4024	}
4025	/**
4026	* Get options for use in determining whether to reload a tile based on the modified features.
4027	* @param affectedGeometries - The feature geometries affected by the update.
4028	* @returns A {@link GeoJSONSourceShouldReloadTileOptions} object which contains an array of affected bounds caused by the update.
4029	*/
4030	_getShouldReloadTileOptions(affectedGeometries) {
4031		if (!affectedGeometries) return void 0;
4032		return { affectedBounds: affectedGeometries.filter(Boolean).map((g) => getGeoJSONBounds(g)) };
4033	}
4034	/**
4035	* Determine whether a tile should be reloaded based on a set of options associated with a {@link MapSourceDataChangedEvent}.
4036	* @internal
4037	*/
4038	shouldReloadTile(tile, { affectedBounds }) {
4039		if (tile.state === "loading") return true;
4040		if (tile.state === "unloaded") return false;
4041		const { buffer, extent } = this.workerOptions.geojsonVtOptions;
4042		const tileBounds = tileIdToLngLatBounds(tile.tileID.canonical, buffer / extent);
4043		for (const bounds of affectedBounds) if (tileBounds.intersects(bounds)) return true;
4044		return false;
4045	}
4046	loaded() {
4047		return !this._isUpdatingWorker && !this._hasPendingWorkerUpdate();
4048	}
4049	async loadTile(tile) {
4050		const message = !tile.actor ? "LT" : "RT";
4051		tile.actor = await this.actorPromise;
4052		const params = {
4053			type: this.type,
4054			uid: tile.uid,
4055			tileID: tile.tileID,
4056			zoom: tile.tileID.overscaledZ,
4057			maxZoom: this.maxzoom,
4058			tileSize: this.tileSize,
4059			source: this.id,
4060			pixelRatio: this.map.getPixelRatio(),
4061			showCollisionBoxes: this.map.showCollisionBoxes,
4062			promoteId: this.promoteId,
4063			subdivisionGranularity: this.map.style.projection.subdivisionGranularity
4064		};
4065		tile.abortController = new AbortController();
4066		try {
4067			const data = await (await this.actorPromise).sendAsync({
4068				type: message,
4069				data: params
4070			}, tile.abortController);
4071			delete tile.abortController;
4072			tile.unloadVectorData();
4073			if (!tile.aborted) tile.loadVectorData(data, this.map.painter, message === "RT");
4074		} catch (err) {
4075			delete tile.abortController;
4076			if (tile.aborted || isAbortError(err)) return;
4077			throw err;
4078		}
4079	}
4080	async abortTile(tile) {
4081		if (tile.abortController) {
4082			tile.abortController.abort();
4083			delete tile.abortController;
4084		}
4085		tile.aborted = true;
4086	}
4087	async unloadTile(tile) {
4088		tile.unloadVectorData();
4089		await (await this.actorPromise).sendAsync({
4090			type: "RMT",
4091			data: {
4092				uid: tile.uid,
4093				type: this.type,
4094				source: this.id
4095			}
4096		});
4097	}
4098	onRemove() {
4099		this._removed = true;
4100		this.actorPromise.then((actor) => actor.sendAsync({
4101			type: "RS",
4102			data: {
4103				type: this.type,
4104				source: this.id
4105			}
4106		}));
4107	}
4108	serialize() {
4109		return extend({}, this._options, {
4110			type: this.type,
4111			data: this._data.updateable ? {
4112				type: "FeatureCollection",
4113				features: Array.from(this._data.updateable.values())
4114			} : this._data.url || this._data.geojson
4115		});
4116	}
4117	hasTransition() {
4118		return false;
4119	}
4120};
4121//#endregion
4122//#region src/webgl/program/raster_program.ts
4123/** The bilinear warp, which is also the affine one that a rectangle or a parallelogram needs. */
4124const bilinearImageWarp = [
4125	0,
4126	0,
4127	1
4128];
4129const rasterUniforms = (context, locations) => ({
4130	"u_tl_parent": new Uniform2f(context, locations.u_tl_parent),
4131	"u_scale_parent": new Uniform1f(context, locations.u_scale_parent),
4132	"u_buffer_scale": new Uniform1f(context, locations.u_buffer_scale),
4133	"u_image_warp": new Uniform3f(context, locations.u_image_warp),
4134	"u_fade_t": new Uniform1f(context, locations.u_fade_t),
4135	"u_opacity": new Uniform1f(context, locations.u_opacity),
4136	"u_image0": new Uniform1i(context, locations.u_image0),
4137	"u_image1": new Uniform1i(context, locations.u_image1),
4138	"u_brightness_low": new Uniform1f(context, locations.u_brightness_low),
4139	"u_brightness_high": new Uniform1f(context, locations.u_brightness_high),
4140	"u_saturation_factor": new Uniform1f(context, locations.u_saturation_factor),
4141	"u_contrast_factor": new Uniform1f(context, locations.u_contrast_factor),
4142	"u_spin_weights": new Uniform3f(context, locations.u_spin_weights),
4143	"u_coords_top": new Uniform4f(context, locations.u_coords_top),
4144	"u_coords_bottom": new Uniform4f(context, locations.u_coords_bottom)
4145});
4146const rasterUniformValues = (parentTL, parentScaleBy, fade, layer, cornerCoords, imageWarp) => ({
4147	"u_tl_parent": parentTL,
4148	"u_scale_parent": parentScaleBy,
4149	"u_buffer_scale": 1,
4150	"u_image_warp": imageWarp,
4151	"u_fade_t": fade.mix,
4152	"u_opacity": fade.opacity * layer.paint.get("raster-opacity"),
4153	"u_image0": 0,
4154	"u_image1": 1,
4155	"u_brightness_low": layer.paint.get("raster-brightness-min"),
4156	"u_brightness_high": layer.paint.get("raster-brightness-max"),
4157	"u_saturation_factor": saturationFactor(layer.paint.get("raster-saturation")),
4158	"u_contrast_factor": contrastFactor(layer.paint.get("raster-contrast")),
4159	"u_spin_weights": spinWeights(layer.paint.get("raster-hue-rotate")),
4160	"u_coords_top": [
4161		cornerCoords[0].x,
4162		cornerCoords[0].y,
4163		cornerCoords[1].x,
4164		cornerCoords[1].y
4165	],
4166	"u_coords_bottom": [
4167		cornerCoords[3].x,
4168		cornerCoords[3].y,
4169		cornerCoords[2].x,
4170		cornerCoords[2].y
4171	]
4172});
4173function spinWeights(angle) {
4174	angle *= Math.PI / 180;
4175	const s = Math.sin(angle);
4176	const c = Math.cos(angle);
4177	return [
4178		(2 * c + 1) / 3,
4179		(-Math.sqrt(3) * s - c + 1) / 3,
4180		(Math.sqrt(3) * s - c + 1) / 3
4181	];
4182}
4183function contrastFactor(contrast) {
4184	return contrast > 0 ? 1 / (1 - contrast) : 1 + contrast;
4185}
4186function saturationFactor(saturation) {
4187	return saturation > 0 ? 1 - 1 / (1.001 - saturation) : -saturation;
4188}
4189//#endregion
4190//#region src/render/mesh.ts
4191var Mesh = class {
4192	constructor(vertexBuffer, indexBuffer, segments) {
4193		this.vertexBuffer = vertexBuffer;
4194		this.indexBuffer = indexBuffer;
4195		this.segments = segments;
4196	}
4197	destroy() {
4198		this.vertexBuffer.destroy();
4199		this.indexBuffer.destroy();
4200		this.segments.destroy();
4201		this.vertexBuffer = null;
4202		this.indexBuffer = null;
4203		this.segments = null;
4204	}
4205};
4206//#endregion
4207//#region src/data/pos_attributes.ts
4208const posAttributes = createLayout([{
4209	name: "a_pos",
4210	type: "Int16",
4211	components: 2
4212}]);
4213//#endregion
4214//#region src/util/create_tile_mesh.ts
4215/**
4216* The size of border region for stencil masks, in internal tile coordinates.
4217* Used for globe rendering.
4218*/
4219const EXTENT_STENCIL_BORDER = EXTENT / 128;
4220/**
4221* @internal
4222* Creates a mesh of a quad that covers the entire tile (covering positions in range 0..EXTENT),
4223* is optionally subdivided into finer quads, optionally includes a border
4224* and optionally extends to the north and/or special pole vertices.
4225* Also allocates and populates WebGL buffers for the mesh.
4226* Forces 16 bit indices that are used throughout MapLibre.
4227* @param context - The WebGL context wrapper.
4228* @param options - Specify options for tile mesh creation such as granularity or border.
4229* @returns The mesh vertices and indices, already allocated and uploaded into WebGL buffers.
4230*/
4231function createTileMeshWithBuffers(context, options) {
4232	const tileMesh = createTileMesh(options, "16bit");
4233	const vertices = PosArray.deserialize({
4234		arrayBuffer: tileMesh.vertices,
4235		length: tileMesh.vertices.byteLength / 2 / 2
4236	});
4237	const indices = TriangleIndexArray.deserialize({
4238		arrayBuffer: tileMesh.indices,
4239		length: tileMesh.indices.byteLength / 2 / 3
4240	});
4241	return new Mesh(context.createVertexBuffer(vertices, posAttributes.members), context.createIndexBuffer(indices), SegmentVector.simpleSegment(0, 0, vertices.length, indices.length));
4242}
4243/**
4244* Creates a mesh of a quad that covers the entire tile (covering positions in range 0..EXTENT),
4245* is optionally subdivided into finer quads, optionally includes a border
4246* and optionally extends to the north and/or special pole vertices.
4247* Additionally the resulting mesh indices type can be specified using `forceIndicesSize`.
4248* @example
4249* ```
4250* // Creating a mesh for a tile that can be used for raster layers, hillshade, etc.
4251* const meshBuffers = createTileMesh({
4252*     granularity: map.style.projection.subdivisionGranularity.tile.getGranularityForZoomLevel(tileID.z),
4253*     generateBorders: true,
4254*     extendToNorthPole: tileID.y === 0,
4255*     extendToSouthPole: tileID.y === (1 << tileID.z) - 1,
4256* }, '16bit');
4257* ```
4258* @see [Add a custom layer with tiles to a globe](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-custom-layer-with-tiles-to-a-globe/)
4259* @param options - Specify options for tile mesh creation such as granularity or border.
4260* @param forceIndicesSize - Specifies what indices type to use. The values '32bit' and '16bit' force their respective indices size. If undefined, the mesh may use either size, and will pick 16 bit indices if possible. If '16bit' is specified and the mesh exceeds 65536 vertices, an exception is thrown.
4261* @returns Typed arrays of the mesh vertices and indices.
4262*/
4263function createTileMesh(options, forceIndicesSize) {
4264	const granularity = options.granularity !== void 0 ? Math.max(options.granularity, 1) : 1;
4265	const quadsPerAxisX = granularity + (options.generateBorders ? 2 : 0);
4266	const quadsPerAxisY = granularity + (options.extendToNorthPole || options.generateBorders ? 1 : 0) + (options.extendToSouthPole || options.generateBorders ? 1 : 0);
4267	const verticesPerAxisX = quadsPerAxisX + 1;
4268	const verticesPerAxisY = quadsPerAxisY + 1;
4269	const offsetX = options.generateBorders ? -1 : 0;
4270	const offsetY = options.generateBorders || options.extendToNorthPole ? -1 : 0;
4271	const endX = granularity + (options.generateBorders ? 1 : 0);
4272	const endY = granularity + (options.generateBorders || options.extendToSouthPole ? 1 : 0);
4273	const vertexCount = verticesPerAxisX * verticesPerAxisY;
4274	const indexCount = quadsPerAxisX * quadsPerAxisY * 6;
4275	const overflows16bitIndices = verticesPerAxisX * verticesPerAxisY > 65536;
4276	if (overflows16bitIndices && forceIndicesSize === "16bit") throw new Error("Granularity is too large and meshes would not fit inside 16 bit vertex indices.");
4277	const use32bitIndices = overflows16bitIndices || forceIndicesSize === "32bit";
4278	const vertices = new Int16Array(vertexCount * 2);
4279	let vertexId = 0;
4280	for (let y = offsetY; y <= endY; y++) for (let x = offsetX; x <= endX; x++) {
4281		let vx = x / granularity * EXTENT;
4282		if (x === -1) vx = -EXTENT_STENCIL_BORDER;
4283		if (x === granularity + 1) vx = EXTENT + EXTENT_STENCIL_BORDER;
4284		let vy = y / granularity * EXTENT;
4285		if (y === -1) vy = options.extendToNorthPole ? NORTH_POLE_Y : -EXTENT_STENCIL_BORDER;
4286		if (y === granularity + 1) vy = options.extendToSouthPole ? SOUTH_POLE_Y : EXTENT + EXTENT_STENCIL_BORDER;
4287		vertices[vertexId++] = vx;
4288		vertices[vertexId++] = vy;
4289	}
4290	const indices = use32bitIndices ? new Uint32Array(indexCount) : new Uint16Array(indexCount);
4291	let indexId = 0;
4292	for (let y = 0; y < quadsPerAxisY; y++) for (let x = 0; x < quadsPerAxisX; x++) {
4293		const v0 = x + y * verticesPerAxisX;
4294		const v1 = x + 1 + y * verticesPerAxisX;
4295		const v2 = x + (y + 1) * verticesPerAxisX;
4296		const v3 = x + 1 + (y + 1) * verticesPerAxisX;
4297		indices[indexId++] = v0;
4298		indices[indexId++] = v2;
4299		indices[indexId++] = v1;
4300		indices[indexId++] = v1;
4301		indices[indexId++] = v2;
4302		indices[indexId++] = v3;
4303	}
4304	return {
4305		vertices: vertices.buffer.slice(0),
4306		indices: indices.buffer.slice(0),
4307		uses32bitIndices: use32bitIndices
4308	};
4309}
4310//#endregion
4311//#region src/source/image_source.ts
4312/**
4313* How many grid cells per axis the mesh of a subdivided quad has. Within a cell the bilinear warp
4314* is still approximated by one mapping per triangle, so the error falls off with the cell size.
4315*/
4316const SUBDIVIDED_QUAD_GRANULARITY = 16;
4317/**
4318* How much a warp may foreshorten the image, as the ratio between the largest and the smallest
4319* homogeneous denominator over the quad, while staying purely projective. Ordinary oblique imagery
4320* stays well below it.
4321*/
4322const PROJECTIVE_FORESHORTENING = 4;
4323/**
4324* The foreshortening at which a warp is taken to be purely bilinear.
4325*
4326* The denominator vanishes on the warp's vanishing line, and that line touches the quad exactly when
4327* the quad degenerates into a triangle, so foreshortening grows without bound as a corner approaches
4328* the diagonal between its neighbours. Left alone, such a quad keeps a valid but wildly lopsided
4329* warp, squeezing nearly the whole image into a sliver along one edge and magnifying a handful of
4330* texels over the rest of the quad.
4331*/
4332const BILINEAR_FORESHORTENING = 512;
4333/**
4334* A data source containing an image.
4335* (See the [Style Specification](https://maplibre.org/maplibre-style-spec/#sources-image) for detailed documentation of options.)
4336*
4337* @group Sources
4338*
4339* @example
4340* ```ts
4341* // add to map
4342* map.addSource('some id', {
4343*    type: 'image',
4344*    url: 'https://www.maplibre.org/images/foo.png',
4345*    coordinates: [
4346*        [-76.54, 39.18],
4347*        [-76.52, 39.18],
4348*        [-76.52, 39.17],
4349*        [-76.54, 39.17]
4350*    ]
4351* });
4352*
4353* // update coordinates
4354* let mySource = map.getSource('some id');
4355* mySource.setCoordinates([
4356*     [-76.54335737228394, 39.18579907229748],
4357*     [-76.52803659439087, 39.1838364847587],
4358*     [-76.5295386314392, 39.17683392507606],
4359*     [-76.54520273208618, 39.17876344106642]
4360* ]);
4361*
4362* // update url and coordinates simultaneously
4363* mySource.updateImage({
4364*    url: 'https://www.maplibre.org/images/bar.png',
4365*    coordinates: [
4366*        [-76.54335737228394, 39.18579907229748],
4367*        [-76.52803659439087, 39.1838364847587],
4368*        [-76.5295386314392, 39.17683392507606],
4369*        [-76.54520273208618, 39.17876344106642]
4370*    ]
4371* })
4372*
4373* // update with an already-decoded image (no network request)
4374* const bitmap = await createImageBitmap(myCanvas);
4375* mySource.updateImage({image: bitmap});
4376*
4377* // create an empty source (no url), then feed it entirely via updateImage
4378* map.addSource('empty id', {
4379*    type: 'image',
4380*    coordinates: [
4381*        [-76.54, 39.18],
4382*        [-76.52, 39.18],
4383*        [-76.52, 39.17],
4384*        [-76.54, 39.17]
4385*    ]
4386* });
4387* (map.getSource('empty id') as ImageSource).updateImage({image: bitmap});
4388*
4389* map.removeSource('some id');  // remove
4390* ```
4391*/
4392var ImageSource = class extends Evented {
4393	/** @internal */
4394	constructor(id, options, dispatcher, eventedParent) {
4395		super();
4396		this.imageWarp = bilinearImageWarp;
4397		this.flippedWindingOrder = false;
4398		this._warp = "auto";
4399		this._imageDirty = false;
4400		this._subdividedQuad = false;
4401		this._subdividedMesh = null;
4402		this.id = id;
4403		this.dispatcher = dispatcher;
4404		this.coordinates = options.coordinates;
4405		this.type = "image";
4406		this.minzoom = 0;
4407		this.maxzoom = 22;
4408		this.tileSize = 512;
4409		this.tiles = {};
4410		this._loaded = false;
4411		this.setEventedParent(eventedParent);
4412		this.options = options;
4413	}
4414	async load(newCoordinates) {
4415		if (this.options.url === void 0) {
4416			this._loaded = true;
4417			this._finishLoading();
4418			return;
4419		}
4420		this._loaded = false;
4421		this.fire(new MapSourceDataEvent("dataloading"));
4422		this.url = this.options.url;
4423		this._abortController = new AbortController();
4424		try {
4425			const image = await ImageRequest.transformAndGetImage(this.map._requestManager, this.url, "Image", this._abortController);
4426			this._abortController = null;
4427			this._loaded = true;
4428			if (image?.data) {
4429				this._setImage(image.data);
4430				if (newCoordinates) this.coordinates = newCoordinates;
4431				this._finishLoading();
4432			} else this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Could not load image ${this.url}: the response is empty`)));
4433		} catch (err) {
4434			if (isAbortError(err)) return;
4435			this._abortController = null;
4436			this._loaded = true;
4437			this.fire(new ErrorEvent(ensureError(err)));
4438		}
4439	}
4440	loaded() {
4441		return this._loaded;
4442	}
4443	/**
4444	* Updates the image and, optionally, the coordinates. To avoid having the image flash after changing,
4445	* set the `raster-fade-duration` paint property on the raster layer to 0.
4446	*
4447	* Provide exactly one of `url` (to fetch a new image over the network) or `image` (an
4448	* already-decoded `HTMLImageElement`, `HTMLCanvasElement`, `ImageBitmap` or `ImageData` to
4449	* display directly, without a network request).
4450	*
4451	* @param options - The options object.
4452	*/
4453	updateImage(options) {
4454		if (this._abortController) {
4455			this._abortController.abort();
4456			this._abortController = null;
4457		}
4458		if ("image" in options) {
4459			this._loaded = true;
4460			this._setImage(options.image);
4461			if (options.coordinates) this.coordinates = options.coordinates;
4462			this._finishLoading();
4463			return this;
4464		}
4465		if (!options.url) return this;
4466		this.options.url = options.url;
4467		this.load(options.coordinates);
4468		return this;
4469	}
4470	/** Loaded tiles hold `this.texture`, so the wrapper has to outlive the images in it. */
4471	_setImage(image) {
4472		this.image = image;
4473		this._imageDirty = true;
4474	}
4475	/**
4476	* @internal
4477	* The mesh the image is drawn with, or null to use the tile mesh of the current projection.
4478	*
4479	* The raster vertex shader evaluates {@link imageWarp} per vertex, so a pair of triangles only
4480	* samples it at the four corners. That is enough for the affine mapping of a parallelogram, and
4481	* for a purely projective mapping, whose straight lines survive linear interpolation across the
4482	* diagonal. Anything in between is neither, and would be textured as two independently warped
4483	* halves with a visible seam along that diagonal, so it needs the warp evaluated per grid cell.
4484	*
4485	* A projection that subdivides its own tile meshes already does this, so it keeps them.
4486	*/
4487	getMesh(context, projectionSubdividesTiles) {
4488		if (!this._subdividedQuad || projectionSubdividesTiles) return null;
4489		this._subdividedMesh ??= createTileMeshWithBuffers(context, { granularity: SUBDIVIDED_QUAD_GRANULARITY });
4490		return this._subdividedMesh;
4491	}
4492	/** Teardown only: dropping the reference alone leaves the allocation to the GC. */
4493	_disposeTexture() {
4494		this.texture?.destroy();
4495		this.texture = null;
4496	}
4497	_finishLoading() {
4498		if (this.map) {
4499			this.setCoordinates(this.coordinates);
4500			this.fire(new MapSourceDataEvent("data", { sourceDataType: "metadata" }));
4501		}
4502	}
4503	onAdd(map) {
4504		this.map = map;
4505		this.load();
4506	}
4507	onRemove() {
4508		if (this._abortController) {
4509			this._abortController.abort();
4510			this._abortController = null;
4511		}
4512		this._disposeTexture();
4513		this._subdividedMesh?.destroy();
4514		this._subdividedMesh = null;
4515		this.image = null;
4516		this.tiles = {};
4517	}
4518	/**
4519	* @experimental
4520	* Sets how the image is warped onto its coordinates and re-renders the map.
4521	*
4522	* This only has an effect while the coordinates do not form a rectangle, and it is not part of
4523	* the style specification, so it does not survive `map.setStyle`.
4524	*
4525	* @param warp - The warp to use, see {@link ImageSourceWarp}.
4526	*
4527	* @example
4528	* ```ts
4529	* // Keep the image pinned to its corners while the user drags them around.
4530	* map.getSource('some id').setWarp('flat');
4531	* ```
4532	*/
4533	setWarp(warp) {
4534		if (this._warp === warp) return this;
4535		this._warp = warp;
4536		if (this.tileCoords) this.setCoordinates(this.coordinates);
4537		return this;
4538	}
4539	/**
4540	* @experimental
4541	* Returns how the image is warped onto its coordinates.
4542	*
4543	* @returns The warp in use, see {@link ImageSourceWarp}.
4544	*/
4545	getWarp() {
4546		return this._warp;
4547	}
4548	/**
4549	* Sets the image's coordinates and re-renders the map.
4550	*
4551	* @param coordinates - Four geographical coordinates,
4552	* represented as arrays of longitude and latitude numbers, which define the corners of the image.
4553	* The coordinates start at the top left corner of the image and proceed in clockwise order.
4554	* They do not have to represent a rectangle.
4555	*/
4556	setCoordinates(coordinates) {
4557		this.coordinates = coordinates;
4558		const cornerCoords = coordinates.map(MercatorCoordinate.fromLngLat);
4559		this.tileID = getCoordinatesCenterTileID(cornerCoords);
4560		this.terrainTileRanges = this._getOverlappingTileRanges(cornerCoords);
4561		this.minzoom = this.maxzoom = this.tileID.z;
4562		this.tileCoords = cornerCoords.map((coord) => this.tileID.getTilePoint(coord)._round());
4563		this.imageWarp = calculateImageWarp(this.tileCoords, this._warp);
4564		this._subdividedQuad = this.imageWarp[2] > 0 && !isParallelogram(this.tileCoords);
4565		this.flippedWindingOrder = hasWrongWindingOrder(this.tileCoords);
4566		this.fire(new MapSourceDataEvent("data", { sourceDataType: "content" }));
4567		return this;
4568	}
4569	prepare() {
4570		if (Object.keys(this.tiles).length === 0 || !this.image) return;
4571		const context = this.map.painter.context;
4572		const gl = context.gl;
4573		if (!this.texture) {
4574			this.texture = new Texture(context, this.image, gl.RGBA);
4575			this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
4576		} else if (this._imageDirty) {
4577			this.texture.update(this.image);
4578			this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
4579		}
4580		this._imageDirty = false;
4581		let newTilesLoaded = false;
4582		for (const w in this.tiles) {
4583			const tile = this.tiles[w];
4584			if (tile.state !== "loaded") {
4585				tile.state = "loaded";
4586				tile.texture = this.texture;
4587				newTilesLoaded = true;
4588			}
4589		}
4590		if (newTilesLoaded) this.fire(new MapSourceDataEvent("data", {
4591			sourceDataType: "idle",
4592			sourceId: this.id
4593		}));
4594	}
4595	async loadTile(tile) {
4596		if (this.tileID?.equals(tile.tileID.canonical)) {
4597			this.tiles[String(tile.tileID.wrap)] = tile;
4598			tile.buckets = {};
4599		} else tile.state = "errored";
4600	}
4601	serialize() {
4602		const spec = {
4603			type: "image",
4604			coordinates: this.coordinates
4605		};
4606		if (this.options.url !== void 0) spec.url = this.options.url;
4607		return spec;
4608	}
4609	hasTransition() {
4610		return false;
4611	}
4612	/**
4613	* Given a list of coordinates, determine overlapping tile ranges for all zoom levels.
4614	*
4615	* @returns Overlapping tile ranges for all zoom levels.
4616	* @internal
4617	*/
4618	_getOverlappingTileRanges(coords) {
4619		const { minX, minY, maxX, maxY } = Bounds.fromPoints(coords);
4620		const ranges = {};
4621		for (let z = 0; z <= 25; z++) {
4622			const tilesAtZoom = Math.pow(2, z);
4623			const minTileX = Math.floor(minX * tilesAtZoom);
4624			const minTileY = Math.floor(minY * tilesAtZoom);
4625			const maxTileX = Math.floor(maxX * tilesAtZoom);
4626			const maxTileY = Math.floor(maxY * tilesAtZoom);
4627			const minTileXWrapped = (minTileX % tilesAtZoom + tilesAtZoom) % tilesAtZoom;
4628			const maxTileXWrapped = maxTileX % tilesAtZoom;
4629			const minWrap = Math.floor(minTileX / tilesAtZoom);
4630			const maxWrap = Math.floor(maxTileX / tilesAtZoom);
4631			ranges[z] = {
4632				minWrap,
4633				maxWrap,
4634				minTileXWrapped,
4635				maxTileXWrapped,
4636				minTileY,
4637				maxTileY
4638			};
4639		}
4640		return ranges;
4641	}
4642};
4643/**
4644* Given a list of coordinates, get their center as a coordinate.
4645*
4646* @returns centerpoint
4647* @internal
4648*/
4649function getCoordinatesCenterTileID(coords) {
4650	const bounds = Bounds.fromPoints(coords);
4651	const dx = bounds.width();
4652	const dy = bounds.height();
4653	const zoom = Math.max(0, Math.floor(-Math.log(Math.max(dx, dy)) / Math.LN2));
4654	const tilesAtZoom = Math.pow(2, zoom);
4655	return new CanonicalTileID(zoom, Math.floor((bounds.minX + bounds.maxX) / 2 * tilesAtZoom), Math.floor((bounds.minY + bounds.maxY) / 2 * tilesAtZoom));
4656}
4657function hasWrongWindingOrder(coords) {
4658	const e0x = coords[1].x - coords[0].x;
4659	const e0y = coords[1].y - coords[0].y;
4660	const e1x = coords[2].x - coords[0].x;
4661	return e0x * (coords[2].y - coords[0].y) - e0y * e1x < 0;
4662}
4663/**
4664* How the image is warped onto its coordinates, given the corners in tile space and what the user
4665* asked for. The projective warp is the perspective view of a plane, so it is the one a photograph
4666* wants; the bilinear warp is a rubber sheet pinned at the corners, and is the only one left once
4667* the corners stop describing a perspective view at all.
4668*
4669* Based on Paul S. Heckbert, "Fundamentals of Texture Mapping and Image
4670* Warping", UCB/CSD-89-516, 1989, section 2.2.3 and appendix A.2.
4671*
4672* @see https://www2.eecs.berkeley.edu/Pubs/TechRpts/1989/5504.html
4673* @see https://www.cs.cmu.edu/~ph/texfund/texfund.pdf
4674*/
4675function calculateImageWarp(cornerCoords, warp) {
4676	if (warp === "flat" || isParallelogram(cornerCoords)) return bilinearImageWarp;
4677	const [topLeft, topRight, bottomRight, bottomLeft] = cornerCoords;
4678	const sumX = topLeft.x - topRight.x + bottomRight.x - bottomLeft.x;
4679	const sumY = topLeft.y - topRight.y + bottomRight.y - bottomLeft.y;
4680	const basis = [
4681		topRight.x - bottomRight.x,
4682		topRight.y - bottomRight.y,
4683		bottomLeft.x - bottomRight.x,
4684		bottomLeft.y - bottomRight.y
4685	];
4686	const [rightX, rightY, downX, downY] = basis;
4687	const determinant$1 = determinant(basis);
4688	const perspectiveX = (sumX * downY - downX * sumY) / determinant$1;
4689	const perspectiveY = (rightX * sumY - sumX * rightY) / determinant$1;
4690	const denominators = [
4691		1,
4692		1 + perspectiveX,
4693		1 + perspectiveX + perspectiveY,
4694		1 + perspectiveY
4695	];
4696	const foreshortening = Math.max(...denominators) / Math.min(...denominators);
4697	const blend = warp === "perspective" ? 0 : bilinearBlend(foreshortening);
4698	if (!(foreshortening >= 1 && foreshortening <= BILINEAR_FORESHORTENING) || blend >= 1) return bilinearImageWarp;
4699	return [
4700		perspectiveX,
4701		perspectiveY,
4702		blend
4703	];
4704}
4705/**
4706* How far a warp of the given foreshortening is blended towards the bilinear one, ramping from
4707* purely projective at {@link PROJECTIVE_FORESHORTENING} to purely bilinear at
4708* {@link BILINEAR_FORESHORTENING}. Blending rather than switching keeps the image continuous as the
4709* quad is reshaped: the two warps only agree for a parallelogram, and diverge by roughly a tenth of
4710* the quad per unit of foreshortening, so a switch would visibly displace the image.
4711*/
4712function bilinearBlend(foreshortening) {
4713	const ramp = (1 - PROJECTIVE_FORESHORTENING / foreshortening) / (1 - PROJECTIVE_FORESHORTENING / BILINEAR_FORESHORTENING);
4714	return Math.max(0, ramp);
4715}
4716function isParallelogram(cornerCoords) {
4717	const [topLeft, topRight, bottomRight, bottomLeft] = cornerCoords;
4718	return topLeft.x + bottomRight.x === topRight.x + bottomLeft.x && topLeft.y + bottomRight.y === topRight.y + bottomLeft.y;
4719}
4720//#endregion
4721//#region src/source/video_source.ts
4722/**
4723* A data source containing video.
4724* (See the [Style Specification](https://maplibre.org/maplibre-style-spec/#sources-video) for detailed documentation of options.)
4725*
4726* @group Sources
4727*
4728* @example
4729* ```ts
4730* // add to map
4731* map.addSource('some id', {
4732*    type: 'video',
4733*    url: [
4734*        'https://www.mapbox.com/blog/assets/baltimore-smoke.mp4',
4735*        'https://www.mapbox.com/blog/assets/baltimore-smoke.webm'
4736*    ],
4737*    coordinates: [
4738*        [-76.54, 39.18],
4739*        [-76.52, 39.18],
4740*        [-76.52, 39.17],
4741*        [-76.54, 39.17]
4742*    ]
4743* });
4744*
4745* // update
4746* let mySource = map.getSource('some id');
4747* mySource.setCoordinates([
4748*     [-76.54335737228394, 39.18579907229748],
4749*     [-76.52803659439087, 39.1838364847587],
4750*     [-76.5295386314392, 39.17683392507606],
4751*     [-76.54520273208618, 39.17876344106642]
4752* ]);
4753*
4754* map.removeSource('some id');  // remove
4755* ```
4756* @see [Add a video](https://maplibre.org/maplibre-gl-js/docs/examples/video-on-a-map/)
4757*
4758* Note that when rendered as a raster layer, the layer's `raster-fade-duration` property will cause the video to fade in.
4759* This happens when playback is started, paused and resumed, or when the video's coordinates are updated. To avoid this behavior,
4760* set the layer's `raster-fade-duration` property to `0`.
4761*/
4762var VideoSource = class extends ImageSource {
4763	constructor(id, options, dispatcher, eventedParent) {
4764		super(id, options, dispatcher, eventedParent);
4765		this._onPlayingHandler = () => {
4766			this.map?.triggerRepaint();
4767		};
4768		this.roundZoom = true;
4769		this.type = "video";
4770		this.options = options;
4771	}
4772	async load() {
4773		this._loaded = false;
4774		const options = this.options;
4775		this.urls = [];
4776		for (const url of options.urls) this.urls.push((await this.map._requestManager.transformRequest(url, "Source")).url);
4777		try {
4778			const video = await getVideo(this.urls);
4779			this._loaded = true;
4780			if (!video) return;
4781			this.video = video;
4782			this.video.loop = true;
4783			this.video.addEventListener("playing", this._onPlayingHandler);
4784			if (this.map) this.video.play();
4785			this._finishLoading();
4786		} catch (err) {
4787			this.fire(new ErrorEvent(ensureError(err)));
4788		}
4789	}
4790	/**
4791	* Pauses the video.
4792	*/
4793	pause() {
4794		if (this.video) this.video.pause();
4795	}
4796	/**
4797	* Plays the video.
4798	*/
4799	play() {
4800		if (this.video) this.video.play();
4801	}
4802	/**
4803	* Sets playback to a timestamp, in seconds.
4804	*/
4805	seek(seconds) {
4806		if (this.video) {
4807			const seekableRange = this.video.seekable;
4808			if (seconds < seekableRange.start(0) || seconds > seekableRange.end(0)) this.fire(new ErrorEvent(new ValidationError(`sources.${this.id}`, null, `Playback for this video can be set only between the ${seekableRange.start(0)} and ${seekableRange.end(0)}-second mark.`)));
4809			else this.video.currentTime = seconds;
4810		}
4811	}
4812	/**
4813	* Returns the HTML `video` element.
4814	*
4815	* @returns The HTML `video` element.
4816	*/
4817	getVideo() {
4818		return this.video;
4819	}
4820	onAdd(map) {
4821		if (this.map) return;
4822		this.map = map;
4823		this.load();
4824		if (this.video) {
4825			this.video.play();
4826			this.setCoordinates(this.coordinates);
4827		}
4828	}
4829	onRemove() {
4830		super.onRemove();
4831		if (this.video) {
4832			this.video.removeEventListener("playing", this._onPlayingHandler);
4833			this.video.pause();
4834		}
4835	}
4836	/**
4837	* Sets the video's coordinates and re-renders the map.
4838	*/
4839	prepare() {
4840		if (Object.keys(this.tiles).length === 0 || this.video.readyState < 2) return;
4841		const context = this.map.painter.context;
4842		const gl = context.gl;
4843		if (!this.texture) {
4844			this.texture = new Texture(context, this.video, gl.RGBA);
4845			this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
4846		} else if (!this.video.paused) {
4847			this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
4848			gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.video);
4849		}
4850		let newTilesLoaded = false;
4851		for (const w in this.tiles) {
4852			const tile = this.tiles[w];
4853			if (tile.state !== "loaded") {
4854				tile.state = "loaded";
4855				tile.texture = this.texture;
4856				newTilesLoaded = true;
4857			}
4858		}
4859		if (newTilesLoaded) this.fire(new MapSourceDataEvent("data", {
4860			sourceDataType: "idle",
4861			sourceId: this.id
4862		}));
4863	}
4864	serialize() {
4865		return {
4866			type: "video",
4867			urls: this.urls,
4868			coordinates: this.coordinates
4869		};
4870	}
4871	hasTransition() {
4872		return this.video && !this.video.paused;
4873	}
4874};
4875//#endregion
4876//#region src/source/canvas_source.ts
4877/**
4878* A data source containing the contents of an HTML canvas. See {@link CanvasSourceSpecification} for detailed documentation of options.
4879*
4880* @group Sources
4881*
4882* @example
4883* ```ts
4884* // add to map
4885* map.addSource('some id', {
4886*    type: 'canvas',
4887*    canvas: 'idOfMyHTMLCanvas',
4888*    animate: true,
4889*    coordinates: [
4890*        [-76.54, 39.18],
4891*        [-76.52, 39.18],
4892*        [-76.52, 39.17],
4893*        [-76.54, 39.17]
4894*    ]
4895* });
4896*
4897* // update
4898* let mySource = map.getSource('some id');
4899* mySource.setCoordinates([
4900*     [-76.54335737228394, 39.18579907229748],
4901*     [-76.52803659439087, 39.1838364847587],
4902*     [-76.5295386314392, 39.17683392507606],
4903*     [-76.54520273208618, 39.17876344106642]
4904* ]);
4905*
4906* map.removeSource('some id');  // remove
4907* ```
4908*/
4909var CanvasSource = class extends ImageSource {
4910	/** @internal */
4911	constructor(id, options, dispatcher, eventedParent) {
4912		super(id, options, dispatcher, eventedParent);
4913		if (!options.coordinates) this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, "missing required property \"coordinates\"")));
4914		else if (!Array.isArray(options.coordinates) || options.coordinates.length !== 4 || options.coordinates.some((c) => !Array.isArray(c) || c.length !== 2 || c.some((l) => typeof l !== "number"))) this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, "\"coordinates\" property must be an array of 4 longitude/latitude array pairs")));
4915		if (options.animate && typeof options.animate !== "boolean") this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, "optional \"animate\" property must be a boolean value")));
4916		if (!options.canvas) this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, "missing required property \"canvas\"")));
4917		else if (typeof options.canvas !== "string" && !(options.canvas instanceof HTMLCanvasElement)) this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, "\"canvas\" must be either a string representing the ID of the canvas element from which to read, or an HTMLCanvasElement instance")));
4918		this.options = options;
4919		this.animate = options.animate !== void 0 ? options.animate : true;
4920	}
4921	async load() {
4922		this._loaded = true;
4923		this.canvas ||= this.options.canvas instanceof HTMLCanvasElement ? this.options.canvas : document.getElementById(this.options.canvas);
4924		this.width = this.canvas.width;
4925		this.height = this.canvas.height;
4926		if (this._hasInvalidDimensions()) {
4927			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("Canvas dimensions cannot be less than or equal to zero.")));
4928			return;
4929		}
4930		this.play = function() {
4931			this._playing = true;
4932			this.map.triggerRepaint();
4933		};
4934		this.pause = function() {
4935			if (this._playing) {
4936				this.prepare();
4937				this._playing = false;
4938			}
4939		};
4940		this._finishLoading();
4941	}
4942	/**
4943	* Returns the HTML `canvas` element.
4944	*
4945	* @returns The HTML `canvas` element.
4946	*/
4947	getCanvas() {
4948		return this.canvas;
4949	}
4950	onAdd(map) {
4951		this.map = map;
4952		this.load();
4953		if (this.canvas) {
4954			if (this.animate) this.play();
4955		}
4956	}
4957	onRemove() {
4958		this._playing = false;
4959		super.onRemove();
4960	}
4961	prepare() {
4962		let resize = false;
4963		if (this.canvas.width !== this.width) {
4964			this.width = this.canvas.width;
4965			resize = true;
4966		}
4967		if (this.canvas.height !== this.height) {
4968			this.height = this.canvas.height;
4969			resize = true;
4970		}
4971		if (this._hasInvalidDimensions()) return;
4972		if (Object.keys(this.tiles).length === 0) return;
4973		const context = this.map.painter.context;
4974		const gl = context.gl;
4975		if (!this.texture) {
4976			this.texture = new Texture(context, this.canvas, gl.RGBA, { premultiply: true });
4977			this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
4978		} else if (resize || this._playing) this.texture.update(this.canvas, { premultiply: true });
4979		let newTilesLoaded = false;
4980		for (const w in this.tiles) {
4981			const tile = this.tiles[w];
4982			if (tile.state !== "loaded") {
4983				tile.state = "loaded";
4984				tile.texture = this.texture;
4985				newTilesLoaded = true;
4986			}
4987		}
4988		if (newTilesLoaded) this.fire(new MapSourceDataEvent("data", {
4989			sourceDataType: "idle",
4990			sourceId: this.id
4991		}));
4992	}
4993	serialize() {
4994		return {
4995			type: "canvas",
4996			animate: this.animate,
4997			canvas: this.options.canvas,
4998			coordinates: this.coordinates
4999		};
5000	}
5001	hasTransition() {
5002		return this._playing;
5003	}
5004	_hasInvalidDimensions() {
5005		for (const x of [this.canvas.width, this.canvas.height]) if (isNaN(x) || x <= 0) return true;
5006		return false;
5007	}
5008};
5009//#endregion
5010//#region src/source/source.ts
5011const registeredSources = {};
5012/**
5013* Creates a tiled data source instance given an options object.
5014*
5015* @param id - The id for the source. Must not be used by any existing source.
5016* @param specification - Source options, specific to the source type (except for `options.type`, which is always required).
5017* @param source - A source definition object compliant with
5018* [`maplibre-gl-style-spec`](https://maplibre.org/maplibre-style-spec/#sources) or, for a third-party source type,
5019* with that type's requirements.
5020* @param dispatcher - A {@link Dispatcher} instance, which can be used to send messages to the workers.
5021* @returns a newly created source
5022*/
5023const create = (id, specification, dispatcher, eventedParent) => {
5024	const source = new (getSourceType(specification.type))(id, specification, dispatcher, eventedParent);
5025	if (source.id !== id) throw new Error(`Expected Source id to be ${id} instead of ${source.id}`);
5026	return source;
5027};
5028const getSourceType = (name) => {
5029	switch (name) {
5030		case "geojson": return GeoJSONSource;
5031		case "image": return ImageSource;
5032		case "raster": return RasterTileSource;
5033		case "raster-dem": return RasterDEMTileSource;
5034		case "vector": return VectorTileSource;
5035		case "video": return VideoSource;
5036		case "canvas": return CanvasSource;
5037	}
5038	return registeredSources[name];
5039};
5040const setSourceType = (name, type) => {
5041	registeredSources[name] = type;
5042};
5043/**
5044* Adds a custom source type, making it available for use with {@link Map.addSource}.
5045* @param name - The name of the source type; source definition objects use this name in the `{type: ...}` field.
5046* @param SourceType - A {@link SourceClass} - which is a constructor for the `Source` interface.
5047* @returns a promise that is resolved when the source type is ready or rejected with an error.
5048*/
5049const addSourceType = async (name, SourceType) => {
5050	if (getSourceType(name)) throw new Error(`A source type called "${name}" already exists.`);
5051	setSourceType(name, SourceType);
5052};
5053//#endregion
5054//#region src/data/bucket.ts
5055function deserialize(input, style) {
5056	const output = {};
5057	if (!style) return output;
5058	for (const bucket of input) {
5059		const layers = bucket.layerIds.map((id) => style.getLayer(id)).filter(Boolean);
5060		if (layers.length === 0) continue;
5061		bucket.layers = layers;
5062		if (bucket.stateDependentLayerIds) bucket.stateDependentLayers = bucket.stateDependentLayerIds.map((lId) => layers.filter((l) => l.id === lId)[0]);
5063		for (const layer of layers) output[layer.id] = bucket;
5064	}
5065	return output;
5066}
5067//#endregion
5068//#region src/source/rtl_text_plugin_status.ts
5069/**
5070* Fired once a plugin set through {@link setRTLTextPlugin} has loaded, so that tiles drawn before it
5071* arrived can be drawn again.
5072*
5073* @deprecated Right-to-left text is drawn without a plugin, so nothing has to be waited for.
5074*/
5075const RTLPluginLoadedEventName = "RTLPluginLoaded";
5076//#endregion
5077//#region src/source/rtl_text_plugin_main_thread.ts
5078var RTLMainThreadPlugin = class extends Evented {
5079	constructor(..._args) {
5080		super(..._args);
5081		this.status = "unavailable";
5082		this.url = null;
5083		this.dispatcher = getGlobalDispatcher();
5084	}
5085	/** Sync RTL plugin state by broadcasting a message to the worker */
5086	_syncState(statusToSend) {
5087		this.status = statusToSend;
5088		return this.dispatcher.broadcast("SRPS", {
5089			pluginStatus: statusToSend,
5090			pluginURL: this.url
5091		}).catch((e) => {
5092			this.status = "error";
5093			throw e;
5094		});
5095	}
5096	/** This one is exposed to outside */
5097	getRTLTextPluginStatus() {
5098		return this.status;
5099	}
5100	clearRTLTextPlugin() {
5101		this.status = "unavailable";
5102		this.url = null;
5103	}
5104	async setRTLTextPlugin(url, deferred = false) {
5105		if (this.url) throw new Error("setRTLTextPlugin cannot be called multiple times.");
5106		this.url = browser.resolveURL(url);
5107		if (!this.url) throw new Error(`requested url ${url} is invalid`);
5108		if (this.status === "unavailable") {
5109			if (deferred) {
5110				this.status = "deferred";
5111				this._syncState(this.status);
5112			} else return this._requestImport();
5113		} else if (this.status === "requested") return this._requestImport();
5114	}
5115	/** Send a message to worker which will import the RTL plugin script */
5116	async _requestImport() {
5117		await this._syncState("loading");
5118		this.status = "loaded";
5119		this.fire(new Event(RTLPluginLoadedEventName));
5120	}
5121	/** Start a lazy loading process of RTL plugin */
5122	lazyLoad() {
5123		if (this.status === "unavailable") this.status = "requested";
5124		else if (this.status === "deferred") this._requestImport();
5125	}
5126};
5127let rtlMainThreadPlugin = null;
5128function rtlMainThreadPluginFactory() {
5129	rtlMainThreadPlugin ||= new RTLMainThreadPlugin();
5130	return rtlMainThreadPlugin;
5131}
5132//#endregion
5133//#region src/tile/tile.ts
5134const CLOCK_SKEW_RETRY_TIMEOUT = 3e4;
5135/**
5136* A tile object is the combination of a Coordinate, which defines
5137* its place, as well as a unique ID and data tracking for its content
5138*/
5139var Tile = class {
5140	/**
5141	* @param tileID - the tile ID
5142	* @param size - The tile size
5143	*/
5144	constructor(tileID, size) {
5145		this.timeAdded = 0;
5146		this.fadeEndTime = 0;
5147		this.fadeOpacity = 1;
5148		this.tileID = tileID;
5149		this.uid = uniqueId();
5150		this.uses = 0;
5151		this.tileSize = size;
5152		this.buckets = {};
5153		this.expirationTime = null;
5154		this.queryPadding = 0;
5155		this.hasSymbolBuckets = false;
5156		this.hasRTLText = false;
5157		this.dependencies = {};
5158		this.rttObjects = [];
5159		this.rttFingerprint = {};
5160		this.expiredRequestCount = 0;
5161		this.state = "loading";
5162		this.featureStateRevision = -1;
5163	}
5164	/**
5165	* Incoming and self-fading raster tiles must remain renderable at zero opacity because
5166	* drawing advances their opacity. Only transparent departing tiles have finished fading.
5167	*/
5168	isRenderable(symbolLayer) {
5169		return this.hasData() && (!this.fadeEndTime || this.fadingDirection !== 0 || this.fadeOpacity > 0) && (symbolLayer || !this.holdingForSymbolFade());
5170	}
5171	/**
5172	* @internal
5173	* Many-to-one crossfade between a base tile and parent/ancestor tile (when zooming)
5174	*/
5175	setCrossFadeLogic({ fadingRole, fadingDirection, fadingParentID, fadeEndTime }) {
5176		this.resetFadeLogic();
5177		this.fadingRole = fadingRole;
5178		this.fadingDirection = fadingDirection;
5179		this.fadingParentID = fadingParentID;
5180		this.fadeEndTime = fadeEndTime;
5181	}
5182	/**
5183	* Self fading for edge tiles (when panning map)
5184	*/
5185	setSelfFadeLogic(fadeEndTime) {
5186		this.resetFadeLogic();
5187		this.selfFading = true;
5188		this.fadeEndTime = fadeEndTime;
5189	}
5190	resetFadeLogic() {
5191		this.fadingRole = null;
5192		this.fadingDirection = null;
5193		this.fadingParentID = null;
5194		this.selfFading = false;
5195		this.timeAdded = now();
5196		this.fadeEndTime = 0;
5197		this.fadeOpacity = 1;
5198	}
5199	wasRequested() {
5200		return this.state === "errored" || this.state === "loaded" || this.state === "reloading";
5201	}
5202	clearTextures(painter) {
5203		if (this.demTexture) painter.saveTileTexture(this.demTexture);
5204		this.demTexture = null;
5205	}
5206	/**
5207	* @internal
5208	* Returns the cached RTT object for this stack, or undefined on a cache miss.
5209	*/
5210	getRTT(stack) {
5211		return this.rttObjects[stack];
5212	}
5213	/**
5214	* @internal
5215	* Allocates a fresh RTT object from the painter's pool and stores it at the
5216	* given stack slot. Callers should check {@link getRTT} first; calling this
5217	* over an existing slot leaks the previous object.
5218	*/
5219	acquireRTT(painter, stack, size) {
5220		return this.rttObjects[stack] = painter.acquireRTT(size);
5221	}
5222	/**
5223	* @internal
5224	* Returns all cached RTT slots to the painter's pool.
5225	*/
5226	releaseRTT(painter) {
5227		if (this.rttObjects.length === 0) return;
5228		for (const obj of this.rttObjects) if (obj) painter.releaseRTT(obj);
5229		this.rttObjects.length = 0;
5230	}
5231	/**
5232	* Given a data object with a 'buffers' property, load it into
5233	* this tile's elementGroups and buffers properties and set loaded
5234	* to true. If the data is null, like in the case of an empty
5235	* GeoJSON tile, no-op but still set loaded to true.
5236	* @param data - The data from the worker
5237	* @param painter - the painter
5238	* @param justReloaded - `true` to just reload
5239	*/
5240	loadVectorData(data, painter, justReloaded) {
5241		if (data?.etagUnmodified === true) {
5242			this.state = "loaded";
5243			return;
5244		}
5245		if (this.hasData()) this.unloadVectorData();
5246		this.state = "loaded";
5247		if (!data) {
5248			this.collisionBoxArray = new CollisionBoxArray();
5249			return;
5250		}
5251		if (data.featureIndex) {
5252			this.latestFeatureIndex = data.featureIndex;
5253			if (data.rawTileData) {
5254				this.latestRawTileData = data.rawTileData;
5255				this.latestEncoding = data.encoding;
5256				this.latestFeatureIndex.rawTileData = data.rawTileData;
5257				this.latestFeatureIndex.encoding = data.encoding;
5258			} else if (this.latestRawTileData) {
5259				this.latestFeatureIndex.rawTileData = this.latestRawTileData;
5260				this.latestFeatureIndex.encoding = this.latestEncoding;
5261			}
5262		}
5263		this.collisionBoxArray = data.collisionBoxArray;
5264		this.buckets = deserialize(data.buckets, painter?.style);
5265		this.hasSymbolBuckets = false;
5266		for (const id in this.buckets) {
5267			const bucket = this.buckets[id];
5268			if (bucket instanceof SymbolBucket) {
5269				this.hasSymbolBuckets = true;
5270				if (justReloaded) bucket.justReloaded = true;
5271				else break;
5272			}
5273		}
5274		this.hasRTLText = false;
5275		if (this.hasSymbolBuckets) for (const id in this.buckets) {
5276			const bucket = this.buckets[id];
5277			if (bucket instanceof SymbolBucket) {
5278				if (bucket.hasRTLText) {
5279					this.hasRTLText = true;
5280					rtlMainThreadPluginFactory().lazyLoad();
5281					break;
5282				}
5283			}
5284		}
5285		this.queryPadding = 0;
5286		for (const id in this.buckets) {
5287			const bucket = this.buckets[id];
5288			this.queryPadding = Math.max(this.queryPadding, painter.style.getLayer(id).queryRadius(bucket));
5289		}
5290		if (data.imageAtlas) this.imageAtlas = data.imageAtlas;
5291		if (data.glyphAtlasImage) this.glyphAtlasImage = data.glyphAtlasImage;
5292		this.dashPositions = data.dashPositions;
5293	}
5294	/**
5295	* Release any data or WebGL resources referenced by this tile.
5296	*/
5297	unloadVectorData() {
5298		for (const id in this.buckets) this.buckets[id].destroy();
5299		this.buckets = {};
5300		if (this.imageAtlasTexture) this.imageAtlasTexture.destroy();
5301		if (this.glyphAtlasTexture) this.glyphAtlasTexture.destroy();
5302		this.imageAtlas = null;
5303		this.dashPositions = null;
5304		this.latestFeatureIndex = null;
5305		this.state = "unloaded";
5306	}
5307	getBucket(layer) {
5308		return this.buckets[layer.id];
5309	}
5310	upload(context) {
5311		for (const id in this.buckets) {
5312			const bucket = this.buckets[id];
5313			if (bucket.uploadPending()) bucket.upload(context);
5314		}
5315		const gl = context.gl;
5316		if (this.imageAtlas && !this.imageAtlas.uploaded) {
5317			this.imageAtlasTexture = new Texture(context, this.imageAtlas.image, gl.RGBA);
5318			this.imageAtlas.uploaded = true;
5319		}
5320		if (this.glyphAtlasImage) {
5321			this.glyphAtlasTexture = new Texture(context, this.glyphAtlasImage, gl.ALPHA);
5322			this.glyphAtlasImage = null;
5323		}
5324	}
5325	prepare(imageManager) {
5326		if (this.imageAtlas) this.imageAtlas.patchUpdatedImages(imageManager, this.imageAtlasTexture);
5327	}
5328	queryRenderedFeatures(layers, serializedLayers, sourceFeatureState, queryGeometry, cameraQueryGeometry, scale, params, transform, maxPitchScaleFactor, pixelPosMatrix, getElevation) {
5329		if (!this.latestFeatureIndex?.rawTileData) return {};
5330		return this.latestFeatureIndex.query({
5331			queryGeometry,
5332			cameraQueryGeometry,
5333			scale,
5334			tileSize: this.tileSize,
5335			pixelPosMatrix,
5336			transform,
5337			params,
5338			queryPadding: this.queryPadding * maxPitchScaleFactor,
5339			getElevation
5340		}, layers, serializedLayers, sourceFeatureState);
5341	}
5342	querySourceFeatures(result, params) {
5343		const featureIndex = this.latestFeatureIndex;
5344		if (!featureIndex?.rawTileData) return;
5345		const vtLayers = featureIndex.loadVTLayers();
5346		const sourceLayer = params?.sourceLayer ? params.sourceLayer : "";
5347		const layer = vtLayers["_geojsonTileLayer"] || vtLayers[sourceLayer];
5348		if (!layer) return;
5349		const filter = featureFilter(params?.filter, `querySourceFeatures[${sourceLayer}].filter`, params?.globalState);
5350		const { z, x, y } = this.tileID.canonical;
5351		const coord = {
5352			z,
5353			x,
5354			y
5355		};
5356		for (let i = 0; i < layer.length; i++) {
5357			const feature = layer.feature(i);
5358			if (filter.needGeometry) {
5359				const evaluationFeature = toEvaluationFeature(feature, true);
5360				if (!filter.filter(new EvaluationParameters(this.tileID.overscaledZ), evaluationFeature, this.tileID.canonical)) continue;
5361			} else if (!filter.filter(new EvaluationParameters(this.tileID.overscaledZ), feature)) continue;
5362			const id = featureIndex.getId(feature, sourceLayer);
5363			const geojsonFeature = new GeoJSONFeature(feature, z, x, y, id);
5364			geojsonFeature.tile = coord;
5365			result.push(geojsonFeature);
5366		}
5367	}
5368	hasData() {
5369		return this.state === "loaded" || this.state === "reloading" || this.state === "expired";
5370	}
5371	patternsLoaded() {
5372		return this.imageAtlas && !!Object.keys(this.imageAtlas.patternPositions).length;
5373	}
5374	setExpiryData(data) {
5375		const prior = this.expirationTime;
5376		if (data.cacheControl) {
5377			const parsedCC = parseCacheControl(data.cacheControl);
5378			if (parsedCC["max-age"]) this.expirationTime = Date.now() + parsedCC["max-age"] * 1e3;
5379		} else if (data.expires) this.expirationTime = new Date(data.expires).getTime();
5380		if (this.expirationTime) {
5381			const now = Date.now();
5382			let isExpired = false;
5383			if (this.expirationTime > now) isExpired = false;
5384			else if (!prior) isExpired = true;
5385			else if (this.expirationTime < prior) isExpired = true;
5386			else {
5387				const delta = this.expirationTime - prior;
5388				if (!delta) isExpired = true;
5389				else this.expirationTime = now + Math.max(delta, CLOCK_SKEW_RETRY_TIMEOUT);
5390			}
5391			if (isExpired) {
5392				this.expiredRequestCount++;
5393				this.state = "expired";
5394			} else this.expiredRequestCount = 0;
5395		}
5396	}
5397	getExpiryTimeout() {
5398		if (this.expirationTime) {
5399			if (this.expiredRequestCount) return 1e3 * (1 << Math.min(this.expiredRequestCount - 1, 31));
5400			else return Math.min(this.expirationTime - (/* @__PURE__ */ new Date()).getTime(), Math.pow(2, 31) - 1);
5401		}
5402	}
5403	setFeatureState(states, painter, revision) {
5404		if (!this.latestFeatureIndex?.rawTileData || Object.keys(states).length === 0) return;
5405		if (this.featureStateRevision === revision) return;
5406		this.featureStateRevision = revision;
5407		const vtLayers = this.latestFeatureIndex.loadVTLayers();
5408		for (const id in this.buckets) {
5409			if (!painter.style.hasLayer(id)) continue;
5410			const bucket = this.buckets[id];
5411			const sourceLayerId = bucket.layers[0]["sourceLayer"] || "_geojsonTileLayer";
5412			const sourceLayer = vtLayers[sourceLayerId];
5413			const sourceLayerStates = states[sourceLayerId];
5414			if (!sourceLayer || !sourceLayerStates || sourceLayerStates.length === 0) continue;
5415			bucket.update(sourceLayerStates, sourceLayer, this.imageAtlas?.patternPositions || {}, this.dashPositions || {});
5416			const layer = painter?.style?.getLayer(id);
5417			if (layer) this.queryPadding = Math.max(this.queryPadding, layer.queryRadius(bucket));
5418		}
5419	}
5420	holdingForSymbolFade() {
5421		return this.symbolFadeHoldUntil !== void 0;
5422	}
5423	symbolFadeFinished() {
5424		return !this.symbolFadeHoldUntil || this.symbolFadeHoldUntil < now();
5425	}
5426	clearSymbolFadeHold() {
5427		this.symbolFadeHoldUntil = void 0;
5428	}
5429	setSymbolHoldDuration(duration) {
5430		this.symbolFadeHoldUntil = now() + duration;
5431	}
5432	setDependencies(namespace, dependencies) {
5433		const index = {};
5434		for (const dep of dependencies) index[dep] = true;
5435		this.dependencies[namespace] = index;
5436	}
5437	hasDependency(namespaces, keys) {
5438		for (const namespace of namespaces) {
5439			const dependencies = this.dependencies[namespace];
5440			if (dependencies) {
5441				for (const key of keys) if (dependencies[key]) return true;
5442			}
5443		}
5444		return false;
5445	}
5446};
5447//#endregion
5448//#region src/source/source_state.ts
5449function featureStatesMapToArray(map) {
5450	const result = [];
5451	for (const id in map) result.push({
5452		id,
5453		state: map[id]
5454	});
5455	return result;
5456}
5457/**
5458* @internal
5459* SourceFeatureState manages the state and pending changes
5460* to features in a source, separated by source layer.
5461* stateChanges and deletedStates batch all changes to the tile (updates and removes, respectively)
5462* between coalesce() events. addFeatureState() and removeFeatureState() also update their counterpart's
5463* list of changes, such that coalesce() can apply the proper state changes while agnostic to the order of operations.
5464* In deletedStates, all null's denote complete removal of state at that scope
5465*/
5466var SourceFeatureState = class {
5467	constructor() {
5468		this.state = {};
5469		this.stateChanges = {};
5470		this.deletedStates = {};
5471		this.revision = 0;
5472	}
5473	updateState(sourceLayer, featureId, newState) {
5474		const feature = String(featureId);
5475		this.stateChanges[sourceLayer] ||= {};
5476		this.stateChanges[sourceLayer][feature] ||= {};
5477		extend(this.stateChanges[sourceLayer][feature], newState);
5478		if (this.deletedStates[sourceLayer] === null) {
5479			this.deletedStates[sourceLayer] = {};
5480			for (const ft in this.state[sourceLayer]) if (ft === feature) {
5481				this.deletedStates[sourceLayer][feature] = {};
5482				for (const prop in this.state[sourceLayer][ft]) if (newState[prop] === void 0) this.deletedStates[sourceLayer][feature][prop] = null;
5483			} else this.deletedStates[sourceLayer][ft] = null;
5484		} else if (this.deletedStates[sourceLayer]?.[feature] === null) {
5485			this.deletedStates[sourceLayer][feature] = {};
5486			for (const prop in this.state[sourceLayer][feature]) if (newState[prop] === void 0) this.deletedStates[sourceLayer][feature][prop] = null;
5487		} else for (const key in newState) if (this.deletedStates[sourceLayer]?.[feature]?.[key] === null) delete this.deletedStates[sourceLayer][feature][key];
5488	}
5489	removeFeatureState(sourceLayer, featureId, key) {
5490		if (this.deletedStates[sourceLayer] === null) return;
5491		const feature = String(featureId);
5492		this.deletedStates[sourceLayer] ||= {};
5493		if (key && featureId !== void 0) {
5494			if (this.deletedStates[sourceLayer][feature] !== null) {
5495				this.deletedStates[sourceLayer][feature] ||= {};
5496				this.deletedStates[sourceLayer][feature][key] = null;
5497			}
5498		} else if (featureId !== void 0) {
5499			if (this.stateChanges[sourceLayer]?.[feature]) {
5500				this.deletedStates[sourceLayer][feature] = {};
5501				for (key in this.stateChanges[sourceLayer][feature]) this.deletedStates[sourceLayer][feature][key] = null;
5502			} else this.deletedStates[sourceLayer][feature] = null;
5503		} else this.deletedStates[sourceLayer] = null;
5504	}
5505	getState(sourceLayer, featureId) {
5506		const feature = String(featureId);
5507		const base = this.state[sourceLayer] || {};
5508		const changes = this.stateChanges[sourceLayer] || {};
5509		const reconciledState = extend({}, base[feature], changes[feature]);
5510		if (this.deletedStates[sourceLayer] === null) return {};
5511		else if (this.deletedStates[sourceLayer]) {
5512			const featureDeletions = this.deletedStates[sourceLayer][featureId];
5513			if (featureDeletions === null) return {};
5514			for (const prop in featureDeletions) delete reconciledState[prop];
5515		}
5516		return reconciledState;
5517	}
5518	initializeTileState(tile, painter) {
5519		const layerStates = {};
5520		for (const sourceLayer in this.state) layerStates[sourceLayer] = featureStatesMapToArray(this.state[sourceLayer]);
5521		tile.setFeatureState(layerStates, painter, this.revision);
5522	}
5523	coalesceChanges(inViewTiles, painter) {
5524		const featuresChangedMap = {};
5525		for (const sourceLayer in this.stateChanges) {
5526			this.state[sourceLayer] ||= {};
5527			featuresChangedMap[sourceLayer] ||= {};
5528			for (const feature in this.stateChanges[sourceLayer]) {
5529				this.state[sourceLayer][feature] ||= {};
5530				extend(this.state[sourceLayer][feature], this.stateChanges[sourceLayer][feature]);
5531				featuresChangedMap[sourceLayer][feature] = this.state[sourceLayer][feature];
5532			}
5533		}
5534		for (const sourceLayer in this.deletedStates) {
5535			this.state[sourceLayer] ||= {};
5536			featuresChangedMap[sourceLayer] ||= {};
5537			if (this.deletedStates[sourceLayer] === null) for (const ft in this.state[sourceLayer]) {
5538				this.state[sourceLayer][ft] = {};
5539				featuresChangedMap[sourceLayer][ft] = {};
5540			}
5541			else for (const feature in this.deletedStates[sourceLayer]) {
5542				if (this.deletedStates[sourceLayer][feature] === null) this.state[sourceLayer][feature] = {};
5543				else for (const key of Object.keys(this.deletedStates[sourceLayer][feature])) delete this.state[sourceLayer][feature][key];
5544				featuresChangedMap[sourceLayer][feature] = this.state[sourceLayer][feature];
5545			}
5546		}
5547		this.stateChanges = {};
5548		this.deletedStates = {};
5549		if (Object.keys(featuresChangedMap).length === 0) return;
5550		this.revision++;
5551		const featuresChanged = {};
5552		for (const sourceLayer in featuresChangedMap) featuresChanged[sourceLayer] = featureStatesMapToArray(featuresChangedMap[sourceLayer]);
5553		inViewTiles.setFeatureState(featuresChanged, painter, this.revision);
5554	}
5555};
5556//#endregion
5557//#region src/util/primitives/aabb.ts
5558var Aabb = class Aabb {
5559	constructor(min_, max_) {
5560		this.min = min_;
5561		this.max = max_;
5562		this.center = scale$1([], add([], this.min, this.max), .5);
5563	}
5564	quadrant(index) {
5565		const split = [index % 2 === 0, index < 2];
5566		const qMin = clone(this.min);
5567		const qMax = clone(this.max);
5568		for (let axis = 0; axis < split.length; axis++) {
5569			qMin[axis] = split[axis] ? this.min[axis] : this.center[axis];
5570			qMax[axis] = split[axis] ? this.center[axis] : this.max[axis];
5571		}
5572		qMax[2] = this.max[2];
5573		return new Aabb(qMin, qMax);
5574	}
5575	distanceX(point) {
5576		return Math.max(Math.min(this.max[0], point[0]), this.min[0]) - point[0];
5577	}
5578	distanceY(point) {
5579		return Math.max(Math.min(this.max[1], point[1]), this.min[1]) - point[1];
5580	}
5581	/**
5582	* Performs a frustum-aabb intersection test.
5583	*/
5584	intersectsFrustum(frustum) {
5585		let fullyInside = true;
5586		for (const plane of frustum.planes) {
5587			const planeIntersection = this.intersectsPlane(plane);
5588			if (planeIntersection === 0) return 0;
5589			if (planeIntersection === 1) fullyInside = false;
5590		}
5591		if (fullyInside) return 2;
5592		if (frustum.aabb.min[0] > this.max[0] || frustum.aabb.min[1] > this.max[1] || frustum.aabb.min[2] > this.max[2] || frustum.aabb.max[0] < this.min[0] || frustum.aabb.max[1] < this.min[1] || frustum.aabb.max[2] < this.min[2]) return 0;
5593		return 1;
5594	}
5595	/**
5596	* Performs a halfspace-aabb intersection test.
5597	*/
5598	intersectsPlane(plane) {
5599		let distMin = plane[3];
5600		let distMax = plane[3];
5601		for (let i = 0; i < 3; i++) if (plane[i] > 0) {
5602			distMin += plane[i] * this.min[i];
5603			distMax += plane[i] * this.max[i];
5604		} else {
5605			distMax += plane[i] * this.min[i];
5606			distMin += plane[i] * this.max[i];
5607		}
5608		if (distMin >= 0) return 2;
5609		if (distMax < 0) return 0;
5610		return 1;
5611	}
5612};
5613//#endregion
5614//#region src/util/primitives/frustum.ts
5615var Frustum = class Frustum {
5616	constructor(points, planes, aabb) {
5617		this.points = points;
5618		this.planes = planes;
5619		this.aabb = aabb;
5620	}
5621	static fromInvProjectionMatrix(invProj, worldSize = 1, zoom = 0, horizonPlane, flippedNearFar) {
5622		const clipSpaceCorners = [
5623			[
5624				-1,
5625				1,
5626				-1,
5627				1
5628			],
5629			[
5630				1,
5631				1,
5632				-1,
5633				1
5634			],
5635			[
5636				1,
5637				-1,
5638				-1,
5639				1
5640			],
5641			[
5642				-1,
5643				-1,
5644				-1,
5645				1
5646			],
5647			[
5648				-1,
5649				1,
5650				1,
5651				1
5652			],
5653			[
5654				1,
5655				1,
5656				1,
5657				1
5658			],
5659			[
5660				1,
5661				-1,
5662				1,
5663				1
5664			],
5665			[
5666				-1,
5667				-1,
5668				1,
5669				1
5670			]
5671		];
5672		const frustumPlanePointIndices = flippedNearFar ? [
5673			[
5674				6,
5675				5,
5676				4
5677			],
5678			[
5679				0,
5680				1,
5681				2
5682			],
5683			[
5684				0,
5685				3,
5686				7
5687			],
5688			[
5689				2,
5690				1,
5691				5
5692			],
5693			[
5694				3,
5695				2,
5696				6
5697			],
5698			[
5699				0,
5700				4,
5701				5
5702			]
5703		] : [
5704			[
5705				0,
5706				1,
5707				2
5708			],
5709			[
5710				6,
5711				5,
5712				4
5713			],
5714			[
5715				0,
5716				3,
5717				7
5718			],
5719			[
5720				2,
5721				1,
5722				5
5723			],
5724			[
5725				3,
5726				2,
5727				6
5728			],
5729			[
5730				0,
5731				4,
5732				5
5733			]
5734		];
5735		const scale = Math.pow(2, zoom);
5736		const frustumCoords = clipSpaceCorners.map((v) => unprojectClipSpacePoint(v, invProj, worldSize, scale));
5737		if (horizonPlane) adjustFarPlaneByHorizonPlane(frustumCoords, frustumPlanePointIndices[0], horizonPlane, flippedNearFar);
5738		const frustumPlanes = frustumPlanePointIndices.map((p) => {
5739			const a = sub([], frustumCoords[p[0]], frustumCoords[p[1]]);
5740			const b = sub([], frustumCoords[p[2]], frustumCoords[p[1]]);
5741			const n = normalize([], cross([], a, b));
5742			const d = -dot(n, frustumCoords[p[1]]);
5743			return n.concat(d);
5744		});
5745		const min = [
5746			Number.POSITIVE_INFINITY,
5747			Number.POSITIVE_INFINITY,
5748			Number.POSITIVE_INFINITY
5749		];
5750		const max = [
5751			Number.NEGATIVE_INFINITY,
5752			Number.NEGATIVE_INFINITY,
5753			Number.NEGATIVE_INFINITY
5754		];
5755		for (const p of frustumCoords) for (let i = 0; i < 3; i++) {
5756			min[i] = Math.min(min[i], p[i]);
5757			max[i] = Math.max(max[i], p[i]);
5758		}
5759		return new Frustum(frustumCoords, frustumPlanes, new Aabb(min, max));
5760	}
5761};
5762function unprojectClipSpacePoint(point, invProj, worldSize, scale) {
5763	const v = transformMat4([], point, invProj);
5764	const s = 1 / v[3] / worldSize * scale;
5765	return mul(v, v, [
5766		s,
5767		s,
5768		1 / v[3],
5769		s
5770	]);
5771}
5772/**
5773* Modifies points in the supplied `frustumCoords` array so that the frustum's far plane only lies as far as the horizon,
5774* which improves frustum culling effectiveness.
5775* @param frustumCoords - Points of the frustum.
5776* @param nearPlanePointsIndices - Which indices in the `frustumCoords` form the near plane.
5777* @param horizonPlane - The horizon plane.
5778*/
5779function adjustFarPlaneByHorizonPlane(frustumCoords, nearPlanePointsIndices, horizonPlane, flippedNearFar) {
5780	const nearPlanePointsOffset = flippedNearFar ? 4 : 0;
5781	const farPlanePointsOffset = flippedNearFar ? 0 : 4;
5782	let maxDist = 0;
5783	const cornerRayLengths = [];
5784	const cornerRayNormalizedDirections = [];
5785	for (let i = 0; i < 4; i++) {
5786		const dir = sub([], frustumCoords[i + farPlanePointsOffset], frustumCoords[i + nearPlanePointsOffset]);
5787		const len = length(dir);
5788		scale$1(dir, dir, 1 / len);
5789		cornerRayLengths.push(len);
5790		cornerRayNormalizedDirections.push(dir);
5791	}
5792	for (let i = 0; i < 4; i++) {
5793		const dist = rayPlaneIntersection(frustumCoords[i + nearPlanePointsOffset], cornerRayNormalizedDirections[i], horizonPlane);
5794		if (dist !== null && dist >= 0) maxDist = Math.max(maxDist, dist);
5795		else maxDist = Math.max(maxDist, cornerRayLengths[i]);
5796	}
5797	const nearPlaneNormalized = getNormalizedNearPlane(frustumCoords, nearPlanePointsIndices);
5798	const idealFarPlaneDistanceFromNearPlane = getIdealNearFarPlaneDistance(horizonPlane, nearPlaneNormalized);
5799	if (idealFarPlaneDistanceFromNearPlane !== null) {
5800		const idealCornerRayLength = idealFarPlaneDistanceFromNearPlane / dot(cornerRayNormalizedDirections[0], nearPlaneNormalized);
5801		maxDist = Math.min(maxDist, idealCornerRayLength);
5802	}
5803	for (let i = 0; i < 4; i++) {
5804		const targetLength = Math.min(maxDist, cornerRayLengths[i]);
5805		frustumCoords[i + farPlanePointsOffset] = [
5806			frustumCoords[i + nearPlanePointsOffset][0] + cornerRayNormalizedDirections[i][0] * targetLength,
5807			frustumCoords[i + nearPlanePointsOffset][1] + cornerRayNormalizedDirections[i][1] * targetLength,
5808			frustumCoords[i + nearPlanePointsOffset][2] + cornerRayNormalizedDirections[i][2] * targetLength,
5809			1
5810		];
5811	}
5812}
5813/**
5814* Returns the near plane equation with unit length direction.
5815* @param frustumCoords - Points of the frustum.
5816* @param nearPlanePointsIndices - Which indices in the `frustumCoords` form the near plane.
5817*/
5818function getNormalizedNearPlane(frustumCoords, nearPlanePointsIndices) {
5819	const nearPlaneA = sub([], frustumCoords[nearPlanePointsIndices[0]], frustumCoords[nearPlanePointsIndices[1]]);
5820	const nearPlaneB = sub([], frustumCoords[nearPlanePointsIndices[2]], frustumCoords[nearPlanePointsIndices[1]]);
5821	const nearPlaneNormalized = [
5822		0,
5823		0,
5824		0,
5825		0
5826	];
5827	normalize(nearPlaneNormalized, cross([], nearPlaneA, nearPlaneB));
5828	nearPlaneNormalized[3] = -dot(nearPlaneNormalized, frustumCoords[nearPlanePointsIndices[0]]);
5829	return nearPlaneNormalized;
5830}
5831/**
5832* Returns the ideal distance between the frustum's near and far plane so that the far plane only lies as far as the horizon.
5833*/
5834function getIdealNearFarPlaneDistance(horizonPlane, nearPlaneNormalized) {
5835	const horizonPlaneLen = len(horizonPlane);
5836	const normalizedHorizonPlane = scale$2([], horizonPlane, 1 / horizonPlaneLen);
5837	const projectedViewDirection = sub([], nearPlaneNormalized, scale$1([], normalizedHorizonPlane, dot(nearPlaneNormalized, normalizedHorizonPlane)));
5838	const projectedViewLength = len(projectedViewDirection);
5839	if (projectedViewLength > 0) {
5840		const horizonCircleRadius = Math.sqrt(1 - normalizedHorizonPlane[3] * normalizedHorizonPlane[3]);
5841		const horizonCircleCenter = scale$1([], normalizedHorizonPlane, -normalizedHorizonPlane[3]);
5842		const pointFurthestOnHorizonCircle = add([], horizonCircleCenter, scale$1([], projectedViewDirection, horizonCircleRadius / projectedViewLength));
5843		return pointPlaneSignedDistance(nearPlaneNormalized, pointFurthestOnHorizonCircle);
5844	} else return null;
5845}
5846//#endregion
5847//#region src/geo/projection/covering_tiles.ts
5848/**
5849* A simple/heuristic function that returns whether the tile is visible under the current transform.
5850* @returns an {@link IntersectionResult}.
5851*/
5852function isTileVisible(frustum, tileBoundingVolume, plane) {
5853	const frustumTest = tileBoundingVolume.intersectsFrustum(frustum);
5854	if (!plane || frustumTest === 0) return frustumTest;
5855	const planeTest = tileBoundingVolume.intersectsPlane(plane);
5856	if (planeTest === 0) return 0;
5857	if (frustumTest === 2 && planeTest === 2) return 2;
5858	return 1;
5859}
5860/**
5861* Definite integral of cos(x)^p. The analytical solution is described in `developer-guides/covering-tiles.md`,
5862* but here the integral is evaluated numerically.
5863* @param p - the power to raise cos(x) to inside the integral
5864* @param x1 - the starting point of the integral.
5865* @param x2 - the ending point of the integral.
5866* @return the integral of cos(x)^p from x=x1 to x=x2
5867*/
5868function integralOfCosXByP(p, x1, x2) {
5869	const numPoints = 10;
5870	let sum = 0;
5871	const dx = (x2 - x1) / numPoints;
5872	for (let i = 0; i < numPoints; i++) {
5873		const x = x1 + (i + .5) / numPoints * (x2 - x1);
5874		sum += dx * Math.pow(Math.cos(x), p);
5875	}
5876	return sum;
5877}
5878function createCalculateTileZoomFunction(maxZoomLevelsOnScreen, tileCountMaxMinRatio) {
5879	return function(requestedCenterZoom, distanceToTile2D, distanceToTileZ, distanceToCenter3D, cameraVerticalFOV) {
5880		/**
5881		* Controls how tiles are loaded at high pitch angles. Higher numbers cause fewer, lower resolution
5882		* tiles to be loaded. Calculate the value that will result in the selected number of zoom levels in
5883		* the worst-case condition (when the horizon is at the top of the screen). For more information, see
5884		* `developer-guides/covering-tiles.md`
5885		*/
5886		const pitchTileLoadingBehavior = 2 * ((maxZoomLevelsOnScreen - 1) / scaleZoom(Math.cos(degreesToRadians(maxMercatorHorizonAngle - cameraVerticalFOV)) / Math.cos(degreesToRadians(maxMercatorHorizonAngle))) - 1);
5887		const centerPitch = Math.acos(distanceToTileZ / distanceToCenter3D);
5888		const tileCountPitch0 = 2 * integralOfCosXByP(pitchTileLoadingBehavior - 1, 0, degreesToRadians(cameraVerticalFOV / 2));
5889		const highestPitch = Math.min(degreesToRadians(maxMercatorHorizonAngle), centerPitch + degreesToRadians(cameraVerticalFOV / 2));
5890		const lowestPitch = Math.min(highestPitch, centerPitch - degreesToRadians(cameraVerticalFOV / 2));
5891		const tileCount = integralOfCosXByP(pitchTileLoadingBehavior - 1, lowestPitch, highestPitch);
5892		const thisTilePitch = Math.atan(distanceToTile2D / distanceToTileZ);
5893		const distanceToTile3D = Math.hypot(distanceToTile2D, distanceToTileZ);
5894		let thisTileDesiredZ = requestedCenterZoom;
5895		thisTileDesiredZ = thisTileDesiredZ + scaleZoom(distanceToCenter3D / distanceToTile3D / Math.max(.5, Math.cos(degreesToRadians(cameraVerticalFOV / 2))));
5896		thisTileDesiredZ += pitchTileLoadingBehavior * scaleZoom(Math.cos(thisTilePitch)) / 2;
5897		thisTileDesiredZ -= scaleZoom(Math.max(1, tileCount / tileCountPitch0 / tileCountMaxMinRatio)) / 2;
5898		return thisTileDesiredZ;
5899	};
5900}
5901const defaultCalculateTileZoom = createCalculateTileZoomFunction(9.314, 3);
5902/**
5903* Return what zoom level of a tile source would most closely cover the tiles displayed by this transform.
5904* @param options - The options, most importantly the source's tile size.
5905* @returns An integer zoom level at which all tiles will be visible.
5906*/
5907function coveringZoomLevel(transform, options) {
5908	const z = (options.roundZoom ? Math.round : Math.floor)(transform.zoom + scaleZoom(transform.tileSize / options.tileSize));
5909	return Math.max(0, z);
5910}
5911/**
5912* Without terrain, `getTileBoundingVolume` has no knowledge of extruded features
5913* (eg. 3D buildings): every tile's bounding box is flat at the camera's `elevation`.
5914* That's a fine approximation of what's visible for most views, but it breaks down
5915* as the frustum's bottom edge approaches horizontal (which depends on both `pitch`
5916* and `fov`, not pitch alone) - a tall building can still be poking up into view
5917* long after its tile's ground point has dropped out of the frustum.
5918*
5919* This is an assumed upper bound on real-world feature height (in metres), used to
5920* grow the elevation used for tile culling as that bottom edge nears horizontal, so
5921* such tiles are not dropped too early. It has no effect away from that edge case.
5922*/
5923const ASSUMED_MAX_FEATURE_HEIGHT_METERS = 500;
5924/**
5925* Angle between the frustum's bottom edge and the mercator horizon below which
5926* the culling elevation starts to grow.
5927*/
5928const TILE_CULLING_HORIZON_ONSET_DEGREES = 15;
5929/**
5930* Returns the elevation to use when computing tile bounding volumes for culling:
5931* `transform.elevation`, growing by up to the greater of
5932* `ASSUMED_MAX_FEATURE_HEIGHT_METERS` and `maxContentElevation` (when provided)
5933* as the frustum's bottom edge approaches the horizon, where a ground-level
5934* bounding box would cull tiles whose extruded features are still visible.
5935*/
5936function getElevationForTileCulling(transform, maxContentElevation) {
5937	const bottomEdgeDegreesAboveHorizontal = maxMercatorHorizonAngle - transform.pitch - transform.fov / 2;
5938	const proximityToHorizon = clamp((TILE_CULLING_HORIZON_ONSET_DEGREES - bottomEdgeDegreesAboveHorizontal) / TILE_CULLING_HORIZON_ONSET_DEGREES, 0, 1);
5939	const maxFeatureHeight = Math.max(ASSUMED_MAX_FEATURE_HEIGHT_METERS, maxContentElevation ?? 0);
5940	return transform.elevation + proximityToHorizon * maxFeatureHeight;
5941}
5942/**
5943* Returns a copy of the frustum with its far plane moved away from the camera by `distance`.
5944* Each far corner travels along its own camera ray until it reaches the pushed plane, so the
5945* result stays a pyramid and the corners stay consistent with the side planes. The globe
5946* frustum's far plane is fitted to the surface horizon, so elevated content beyond it would
5947* otherwise be culled while still visible.
5948*/
5949function pushFrustumFarPlane(frustum, distance, cameraPos) {
5950	const far = frustum.planes[1];
5951	const points = frustum.points.map((p) => {
5952		const distanceToFar = far[0] * p[0] + far[1] * p[1] + far[2] * p[2] + far[3];
5953		if (Math.abs(distanceToFar) > 1e-6) return p;
5954		const ray = [
5955			p[0] - cameraPos[0],
5956			p[1] - cameraPos[1],
5957			p[2] - cameraPos[2]
5958		];
5959		const rayDotNormal = far[0] * ray[0] + far[1] * ray[1] + far[2] * ray[2];
5960		if (rayDotNormal >= -1e-9) return p;
5961		const scale = distance / -rayDotNormal;
5962		return [
5963			p[0] + ray[0] * scale,
5964			p[1] + ray[1] * scale,
5965			p[2] + ray[2] * scale,
5966			p[3]
5967		];
5968	});
5969	const planes = frustum.planes.map((p, i) => i === 1 ? [
5970		p[0],
5971		p[1],
5972		p[2],
5973		p[3] + distance
5974	] : p);
5975	const min = [
5976		Number.POSITIVE_INFINITY,
5977		Number.POSITIVE_INFINITY,
5978		Number.POSITIVE_INFINITY
5979	];
5980	const max = [
5981		Number.NEGATIVE_INFINITY,
5982		Number.NEGATIVE_INFINITY,
5983		Number.NEGATIVE_INFINITY
5984	];
5985	for (const p of points) for (let i = 0; i < 3; i++) {
5986		min[i] = Math.min(min[i], p[i]);
5987		max[i] = Math.max(max[i], p[i]);
5988	}
5989	return new Frustum(points, planes, new Aabb(min, max));
5990}
5991/**
5992* The horizon culling plane and the frustum's far plane both assume content sits on the
5993* surface. Content elevated to radius r (in planet radii) stays visible up to acos(1/r)
5994* beyond the surface horizon and up to sqrt(r^2-1) farther away, so both are pushed back
5995* accordingly; without this, tiles under highly elevated symbols would be dropped while
5996* the symbols are still in view.
5997* @param frustum - The covering frustum, fitted to the surface horizon.
5998* @param plane - The horizon culling plane used by globe transform.
5999* @param cameraPos - The camera position, in the frustum's space.
6000* @param maxContentElevation - The highest elevation the content may reach, in meters.
6001*/
6002function expandCullingToContentElevation(frustum, plane, cameraPos, maxContentElevation) {
6003	const len = Math.hypot(plane[0], plane[1], plane[2]);
6004	if (len === 0) return {
6005		frustum,
6006		plane
6007	};
6008	const shellRadius = 1 + maxContentElevation / earthRadius;
6009	const horizonCos = clamp(-plane[3] / len, -1, 1);
6010	const shellCos = Math.cos(Math.acos(horizonCos) + Math.acos(1 / shellRadius));
6011	return {
6012		frustum: pushFrustumFarPlane(frustum, Math.sqrt(shellRadius * shellRadius - 1), cameraPos),
6013		plane: [
6014			plane[0],
6015			plane[1],
6016			plane[2],
6017			-shellCos * len
6018		]
6019	};
6020}
6021/**
6022* Returns a list of tiles that optimally covers the screen. Adapted for globe projection.
6023* Correctly handles LOD when moving over the antimeridian.
6024* @param transform - The transform instance.
6025* @param frustum - The covering frustum.
6026* @param plane - The clipping plane used by globe transform, or null.
6027* @param cameraCoord - The x, y, z position of the camera in MercatorCoordinates.
6028* @param centerCoord - The x, y, z position of the center point in MercatorCoordinates.
6029* @param options - Additional coveringTiles options.
6030* @param details - Interface to define required helper functions.
6031* @returns A list of tile coordinates, ordered by ascending distance from camera.
6032*/
6033function coveringTiles(transform, options) {
6034	let frustum = transform.getCameraFrustum();
6035	let plane = transform.getClippingPlane();
6036	if (plane && options.maxContentElevation > 0) ({frustum, plane} = expandCullingToContentElevation(frustum, plane, transform.cameraPosition, options.maxContentElevation));
6037	const cameraCoord = cameraMercatorCoordinate(transform);
6038	const centerCoord = MercatorCoordinate.fromLngLat(transform.center, transform.elevation);
6039	const elevationForTileCulling = getElevationForTileCulling(transform, options.maxContentElevation);
6040	const detailsProvider = transform.getCoveringTilesDetailsProvider();
6041	const allowVariableZoom = detailsProvider.allowVariableZoom(transform, options);
6042	const desiredZ = coveringZoomLevel(transform, options);
6043	const minZoom = options.minzoom || 0;
6044	const maxZoom = options.maxzoom !== void 0 ? options.maxzoom : transform.maxZoom;
6045	const nominalZ = Math.min(Math.max(0, desiredZ), maxZoom);
6046	const numTiles = Math.pow(2, nominalZ);
6047	const cameraPoint = [
6048		numTiles * cameraCoord.x,
6049		numTiles * cameraCoord.y,
6050		0
6051	];
6052	const centerPoint = [
6053		numTiles * centerCoord.x,
6054		numTiles * centerCoord.y,
6055		0
6056	];
6057	const distanceToCenter2d = Math.hypot(centerCoord.x - cameraCoord.x, centerCoord.y - cameraCoord.y);
6058	const distanceZ = Math.abs(centerCoord.z - cameraCoord.z);
6059	const distanceToCenter3d = Math.hypot(distanceToCenter2d, distanceZ);
6060	const newRootTile = (wrap) => {
6061		return {
6062			zoom: 0,
6063			x: 0,
6064			y: 0,
6065			wrap,
6066			fullyVisible: false
6067		};
6068	};
6069	const stack = [];
6070	const result = [];
6071	if (transform.renderWorldCopies && detailsProvider.allowWorldCopies()) for (let i = 1; i <= 3; i++) {
6072		stack.push(newRootTile(-i));
6073		stack.push(newRootTile(i));
6074	}
6075	stack.push(newRootTile(0));
6076	while (stack.length > 0) {
6077		const it = stack.pop();
6078		const x = it.x;
6079		const y = it.y;
6080		let fullyVisible = it.fullyVisible;
6081		const tileID = {
6082			x,
6083			y,
6084			z: it.zoom
6085		};
6086		const boundingVolume = detailsProvider.getTileBoundingVolume(tileID, it.wrap, elevationForTileCulling, options);
6087		if (!fullyVisible) {
6088			const intersectResult = isTileVisible(frustum, boundingVolume, plane);
6089			if (intersectResult === 0) continue;
6090			fullyVisible = intersectResult === 2;
6091		}
6092		const distToTile2d = detailsProvider.distanceToTile2d(cameraCoord.x, cameraCoord.y, tileID, boundingVolume);
6093		let thisTileDesiredZ = desiredZ;
6094		if (allowVariableZoom) thisTileDesiredZ = (options.calculateTileZoom || defaultCalculateTileZoom)(transform.zoom + scaleZoom(transform.tileSize / options.tileSize), distToTile2d, distanceZ, distanceToCenter3d, transform.fov);
6095		thisTileDesiredZ = (options.roundZoom ? Math.round : Math.floor)(thisTileDesiredZ);
6096		thisTileDesiredZ = Math.max(0, thisTileDesiredZ);
6097		const z = Math.min(thisTileDesiredZ, maxZoom);
6098		it.wrap = detailsProvider.getWrap(centerCoord, tileID, it.wrap);
6099		if (it.zoom >= z) {
6100			if (it.zoom < minZoom) continue;
6101			const dz = nominalZ - it.zoom;
6102			const dx = cameraPoint[0] - .5 - (x << dz);
6103			const dy = cameraPoint[1] - .5 - (y << dz);
6104			const overscaledZ = options.reparseOverscaled ? Math.max(it.zoom, thisTileDesiredZ) : it.zoom;
6105			result.push({
6106				tileID: new OverscaledTileID(it.zoom === maxZoom ? overscaledZ : it.zoom, it.wrap, it.zoom, x, y),
6107				distanceSq: sqrLen([centerPoint[0] - .5 - x, centerPoint[1] - .5 - y]),
6108				tileDistanceToCamera: Math.sqrt(dx * dx + dy * dy)
6109			});
6110			continue;
6111		}
6112		for (let i = 0; i < 4; i++) {
6113			const childX = (x << 1) + i % 2;
6114			const childY = (y << 1) + (i >> 1);
6115			const childZ = it.zoom + 1;
6116			stack.push({
6117				zoom: childZ,
6118				x: childX,
6119				y: childY,
6120				wrap: it.wrap,
6121				fullyVisible
6122			});
6123		}
6124	}
6125	return result.sort((a, b) => a.distanceSq - b.distanceSq).map((a) => a.tileID);
6126}
6127//#endregion
6128//#region src/tile/tile_manager_raster.ts
6129function isRasterType(type) {
6130	return type === "raster" || type === "image" || type === "video";
6131}
6132/**
6133* Designate fading bases and parents using a many-to-one relationship where the lower children fade in/out
6134* with their parents. Raster shaders are not currently designed for a one-to-many fade relationship.
6135*
6136* Tiles that are candidates for fading out must be loaded and rendered tiles, as loading a tile to then
6137* fade it out would not appear smoothly. The first source of truth for tile fading always starts at the
6138* ideal tile, which continually changes on map adjustment. The state of the previously rendered ideal
6139* tile plane indicates which direction to fade each part of the newer ideal plane (with varying z).
6140*
6141* For a pitched map, the back of the map can have decreasing zooms while the front can have increasing zooms.
6142* Fade logic must therefore adapt dynamically based on the previously rendered ideal tile set.
6143*/
6144function updateFadingTiles(inViewTiles, idealTileIDs, retain, maxFadingAncestorLevels, sourceMinZoom, sourceMaxZoom, rasterFadeDuration) {
6145	const currentTime = now();
6146	const edgeTileIDs = getEdgeTiles(idealTileIDs);
6147	for (const idealID of idealTileIDs) {
6148		const idealTile = inViewTiles.getTileById(idealID.key);
6149		if (idealTile.fadingDirection === 0 || idealTile.fadeOpacity === 0) idealTile.resetFadeLogic();
6150		if (updateFadingAncestor(inViewTiles, idealTile, retain, currentTime, maxFadingAncestorLevels, sourceMinZoom, rasterFadeDuration)) continue;
6151		if (updateFadingDescendents(inViewTiles, idealTile, retain, currentTime, sourceMaxZoom, rasterFadeDuration)) continue;
6152		if (updateFadingEdge(idealTile, edgeTileIDs, currentTime, rasterFadeDuration)) continue;
6153		idealTile.resetFadeLogic();
6154	}
6155}
6156/**
6157* Many-to-one cross-fade. Set 4 ideal tiles as the fading base for a rendered parent tile
6158* as the fading parent. Here the parent is fading out and the ideal tile is fading in.
6159*
6160* Parent tile - fading out                                ■                                -- Fading Parent
6161*                                   ┌──────────────┬──────┴───────┬──────────────┐
6162* Ideal tiles - fading in           ■              ■              ■              ■         -- Base Role = Incoming
6163*                             ┌───┬─┴─┬───┐  ┌───┬─┴─┬───┐  ┌───┬─┴─┬───┐  ┌───┬─┴─┬───┐
6164*                             ■   ■   ■   ■  ■   ■   ■   ■  ■   ■   ■   ■  ■   ■   ■   ■
6165*/
6166function updateFadingAncestor(inViewTiles, idealTile, retain, now, maxFadingAncestorLevels, sourceMinZoom, rasterFadeDuration) {
6167	if (!idealTile.hasData()) return false;
6168	const { tileID: idealID, fadingRole, fadingDirection, fadingParentID } = idealTile;
6169	if (fadingRole === 0 && fadingDirection === 1 && fadingParentID) {
6170		retain[fadingParentID.key] = fadingParentID;
6171		return true;
6172	}
6173	const minAncestorZ = Math.max(idealID.overscaledZ - maxFadingAncestorLevels, sourceMinZoom);
6174	for (let ancestorZ = idealID.overscaledZ - 1; ancestorZ >= minAncestorZ; ancestorZ--) {
6175		const ancestorID = idealID.scaledTo(ancestorZ);
6176		const ancestorTile = inViewTiles.getLoadedTile(ancestorID);
6177		if (!ancestorTile) continue;
6178		idealTile.setCrossFadeLogic({
6179			fadingRole: 0,
6180			fadingDirection: 1,
6181			fadingParentID: ancestorTile.tileID,
6182			fadeEndTime: now + rasterFadeDuration
6183		});
6184		ancestorTile.setCrossFadeLogic({
6185			fadingRole: 1,
6186			fadingDirection: 0,
6187			fadeEndTime: now + rasterFadeDuration
6188		});
6189		retain[ancestorID.key] = ancestorID;
6190		return true;
6191	}
6192	return false;
6193}
6194/**
6195* Many-to-one cross-fade. Search descendents of ideal tiles as the fading base with the ideal tile
6196* as the fading parent. Here the children are fading out and the ideal tile is fading in.
6197*
6198*                                                         ■
6199*                                   ┌──────────────┬──────┴───────┬──────────────┐
6200* Ideal tiles - fading in           ■              ■              ■              ■          -- Fading Parent
6201*                             ┌───┬─┴─┬───┐  ┌───┬─┴─┬───┐  ┌───┬─┴─┬───┐  ┌───┬─┴─┬───┐
6202* Child tiles - fading out    ■   ■   ■   ■  ■   ■   ■   ■  ■   ■   ■   ■  ■   ■   ■   ■    -- Base Role = Departing
6203*
6204* Try direct children first. If none found, try grandchildren. Stops at the first generation that provides a fader.
6205*/
6206function updateFadingDescendents(inViewTiles, idealTile, retain, now, sourceMaxZoom, rasterFadeDuration) {
6207	if (!idealTile.hasData()) return false;
6208	const idealChildren = idealTile.tileID.children(sourceMaxZoom);
6209	let hasFader = updateFadingChildren(inViewTiles, idealTile, idealChildren, retain, now, sourceMaxZoom, rasterFadeDuration);
6210	if (hasFader) return true;
6211	for (const childID of idealChildren) if (updateFadingChildren(inViewTiles, idealTile, childID.children(sourceMaxZoom), retain, now, sourceMaxZoom, rasterFadeDuration)) hasFader = true;
6212	return hasFader;
6213}
6214function updateFadingChildren(inViewTiles, idealTile, childIDs, retain, now, sourceMaxZoom, rasterFadeDuration) {
6215	if (childIDs[0].overscaledZ >= sourceMaxZoom) return false;
6216	let foundFader = false;
6217	for (const childID of childIDs) {
6218		const childTile = inViewTiles.getLoadedTile(childID);
6219		if (!childTile) continue;
6220		const { fadingRole, fadingDirection, fadingParentID } = childTile;
6221		if (fadingRole !== 0 || fadingDirection !== 0 || !fadingParentID) {
6222			childTile.setCrossFadeLogic({
6223				fadingRole: 0,
6224				fadingDirection: 0,
6225				fadingParentID: idealTile.tileID,
6226				fadeEndTime: now + rasterFadeDuration
6227			});
6228			idealTile.setCrossFadeLogic({
6229				fadingRole: 1,
6230				fadingDirection: 1,
6231				fadeEndTime: now + rasterFadeDuration
6232			});
6233		}
6234		retain[childID.key] = childID;
6235		foundFader = true;
6236	}
6237	return foundFader;
6238}
6239/**
6240* One-to-one self fading for unloaded edge tiles (for panning sideways on map). for loading tiles over gaps it feels
6241* more natural for them to fade in, however if they are already loaded/cached then there is no need to fade as map will
6242* look cohesive with no gaps. Note that draw_raster determines fade priority, as many-to-one fade supersedes edge fading.
6243*/
6244function updateFadingEdge(idealTile, edgeTileIDs, now, rasterFadeDuration) {
6245	const idealID = idealTile.tileID;
6246	if (idealTile.selfFading) return true;
6247	if (idealTile.hasData()) return false;
6248	if (edgeTileIDs.has(idealID)) {
6249		const fadeEndTime = now + rasterFadeDuration;
6250		idealTile.setSelfFadeLogic(fadeEndTime);
6251		return true;
6252	}
6253	return false;
6254}
6255function hasRasterTransition(inViewTiles, rasterFadeDuration) {
6256	if (rasterFadeDuration <= 0) return false;
6257	const currentTime = now();
6258	for (const tile of inViewTiles.getAllTiles()) if (tile.fadeEndTime >= currentTime) return true;
6259	return false;
6260}
6261//#endregion
6262//#region src/tile/tile_manager_raster_dem.ts
6263/**
6264* For raster terrain source, backfill DEM to eliminate visible tile boundaries
6265*/
6266function backfillDEM(tile, inViewTiles) {
6267	const renderables = inViewTiles.getRenderableIds();
6268	for (const borderId of renderables) {
6269		if (!tile.neighboringTiles?.[borderId]) continue;
6270		const borderTile = inViewTiles.getTileById(borderId);
6271		if (!tile.neighboringTiles[borderId].backfilled) fillBorder(tile, borderTile);
6272		if (borderTile.neighboringTiles?.[tile.tileID.key]?.backfilled) continue;
6273		fillBorder(borderTile, tile);
6274	}
6275}
6276function fillBorder(tile, borderTile) {
6277	tile.needsHillshadePrepare = true;
6278	tile.needsTerrainPrepare = true;
6279	tile.needsColorReliefPrepare = true;
6280	let dx = borderTile.tileID.canonical.x - tile.tileID.canonical.x;
6281	const dy = borderTile.tileID.canonical.y - tile.tileID.canonical.y;
6282	const dim = Math.pow(2, tile.tileID.canonical.z);
6283	const borderId = borderTile.tileID.key;
6284	if (dx === 0 && dy === 0) return;
6285	if (Math.abs(dy) > 1) return;
6286	if (Math.abs(dx) > 1) {
6287		if (Math.abs(dx + dim) === 1) dx += dim;
6288		else if (Math.abs(dx - dim) === 1) dx -= dim;
6289	}
6290	if (!borderTile.dem || !tile.dem) return;
6291	tile.dem.backfillBorder(borderTile.dem, dx, dy);
6292	if (tile.neighboringTiles?.[borderId]) tile.neighboringTiles[borderId].backfilled = true;
6293}
6294//#endregion
6295//#region src/tile/tile_manager_in_view_tiles.ts
6296var InViewTiles = class {
6297	constructor() {
6298		this._tiles = {};
6299	}
6300	handleWrapJump(wrapDelta) {
6301		const tiles = {};
6302		for (const id in this._tiles) {
6303			const tile = this._tiles[id];
6304			tile.tileID = tile.tileID.unwrapTo(tile.tileID.wrap + wrapDelta);
6305			tiles[tile.tileID.key] = tile;
6306		}
6307		this._tiles = tiles;
6308	}
6309	setFeatureState(featuresChanged, painter, revision) {
6310		for (const id in this._tiles) this._tiles[id].setFeatureState(featuresChanged, painter, revision);
6311	}
6312	getAllTiles() {
6313		return Object.values(this._tiles);
6314	}
6315	getAllIds(sorted = false) {
6316		if (sorted) return Object.values(this._tiles).map((tile) => tile.tileID).sort(compareTileId).map((id) => id.key);
6317		return Object.keys(this._tiles);
6318	}
6319	getTileById(key) {
6320		return this._tiles[key];
6321	}
6322	setTile(key, tile) {
6323		this._tiles[key] = tile;
6324	}
6325	deleteTileById(key) {
6326		delete this._tiles[key];
6327	}
6328	/**
6329	* Get a currently loaded tile.
6330	* - a cached tile is not a loaded tile
6331	* @returns the tile if it's in view and had data, null otherwise.
6332	*/
6333	getLoadedTile(tileID) {
6334		const tile = this.getTileById(tileID.key);
6335		if (tile?.hasData()) return tile;
6336		return null;
6337	}
6338	isIdRenderable(id, symbolLayer = false) {
6339		return this.getTileById(id)?.isRenderable(symbolLayer);
6340	}
6341	getRenderableIds(bearingInRadians = 0, symbolLayer) {
6342		const renderables = [];
6343		for (const id of this.getAllIds()) if (this.isIdRenderable(id, symbolLayer)) renderables.push(this.getTileById(id));
6344		if (symbolLayer) return renderables.sort((a_, b_) => {
6345			const a = a_.tileID;
6346			const b = b_.tileID;
6347			const rotatedA = new Point(a.canonical.x, a.canonical.y)._rotate(-bearingInRadians);
6348			const rotatedB = new Point(b.canonical.x, b.canonical.y)._rotate(-bearingInRadians);
6349			return a.overscaledZ - b.overscaledZ || rotatedB.y - rotatedA.y || rotatedB.x - rotatedA.x;
6350		}).map((tile) => tile.tileID.key);
6351		return renderables.map((tile) => tile.tileID).sort(compareTileId).map((id) => id.key);
6352	}
6353};
6354//#endregion
6355//#region src/tile/tile_manager.ts
6356/**
6357* @internal
6358* `TileManager` is responsible for
6359*
6360*  - creating an instance of `Source`
6361*  - caching tiles loaded from an instance of `Source`
6362*  - handling incoming source data events events from `Map` and coordinating updates
6363*  - providing the current renderable tile coordinates to the `Painter`
6364*  - loading the tiles needed to render a given viewport
6365*  - retaining the tiles needed as substitutes for pending loading tiles
6366*  - retaining the tiles needed for fading between parents and children (for raster sources)
6367*  - reloading tiles when source data or dependencies change
6368*  - handling tile expiration and refresh timers
6369*  - unloading cached tiles not needed to render a given viewport
6370*  - managing tile state and feature state
6371*/
6372var TileManager = class TileManager extends Evented {
6373	static {
6374		this.maxUnderzooming = 10;
6375	}
6376	static {
6377		this.maxOverzooming = 3;
6378	}
6379	constructor(id, options, dispatcher) {
6380		super();
6381		this._maxContentElevationSeen = 0;
6382		this.id = id;
6383		this.dispatcher = dispatcher;
6384		this.on("data", (e) => {
6385			this._dataHandler(e);
6386		});
6387		this.on("dataloading", () => {
6388			this._sourceErrored = false;
6389		});
6390		this.on("error", () => {
6391			this._sourceErrored = this._source.loaded();
6392		});
6393		this._source = create(id, options, dispatcher, this);
6394		this._inViewTiles = new InViewTiles();
6395		this._outOfViewCache = new TileCache(0, (tile) => this._unloadTile(tile));
6396		this._timers = {};
6397		this._maxTileCacheSize = null;
6398		this._maxTileCacheZoomLevels = null;
6399		this._rasterFadeDuration = 0;
6400		this._maxFadingAncestorLevels = 5;
6401		this._state = new SourceFeatureState();
6402		this._didEmitContent = false;
6403		this._updated = false;
6404	}
6405	onAdd(map) {
6406		this.map = map;
6407		this._maxTileCacheSize = map ? map._maxTileCacheSize : null;
6408		this._maxTileCacheZoomLevels = map ? map._maxTileCacheZoomLevels : null;
6409		if (this._source?.onAdd) this._source.onAdd(map);
6410	}
6411	onRemove(map) {
6412		for (const tile of this._inViewTiles.getAllTiles()) tile.unloadVectorData();
6413		this.clearTiles();
6414		if (this._source?.onRemove) this._source.onRemove(map);
6415		this._inViewTiles = new InViewTiles();
6416	}
6417	/**
6418	* Return true if no tile data is pending, tiles will not change unless
6419	* an additional API call is received.
6420	*/
6421	loaded() {
6422		if (this._sourceErrored) return true;
6423		if (!this._sourceLoaded) return false;
6424		if (!this._source.loaded()) return false;
6425		if ((this.used !== void 0 || this.usedForTerrain !== void 0) && !this.used && !this.usedForTerrain) return true;
6426		if (!this._updated) return false;
6427		for (const tile of this._inViewTiles.getAllTiles()) if (tile.state !== "loaded" && tile.state !== "errored") return false;
6428		return true;
6429	}
6430	getSource() {
6431		return this._source;
6432	}
6433	getState() {
6434		return this._state;
6435	}
6436	pause() {
6437		this._paused = true;
6438	}
6439	resume() {
6440		if (!this._paused) return;
6441		const shouldReload = this._shouldReloadOnResume;
6442		this._paused = false;
6443		this._shouldReloadOnResume = false;
6444		if (shouldReload) this.reload();
6445		if (this.transform) this.update(this.transform, this.terrain);
6446	}
6447	async _loadTile(tile, id, state, hadData) {
6448		try {
6449			const result = await this._source.loadTile(tile);
6450			this._tileLoaded(tile, id, state, hadData, result);
6451		} catch (err) {
6452			tile.state = "errored";
6453			if (err.status !== 404) this._source.fire(new ErrorEvent(ensureError(err), { tile }));
6454			else this.update(this.transform, this.terrain);
6455		}
6456	}
6457	_unloadTile(tile) {
6458		if (this._source.unloadTile) this._source.unloadTile(tile);
6459	}
6460	_abortTile(tile) {
6461		if (this._source.abortTile) this._source.abortTile(tile);
6462		this._source.fire(new MapSourceDataEvent("dataabort", {
6463			tile,
6464			coord: tile.tileID
6465		}));
6466	}
6467	serialize() {
6468		return this._source.serialize();
6469	}
6470	prepare(context) {
6471		if (this._source.prepare) this._source.prepare();
6472		this._state.coalesceChanges(this._inViewTiles, this.map ? this.map.painter : null);
6473		for (const tile of this._inViewTiles.getAllTiles()) {
6474			tile.upload(context);
6475			tile.prepare(this.map.style.imageManager);
6476		}
6477	}
6478	/**
6479	* Return all tile ids ordered with z-order, and cast to numbers
6480	*/
6481	getIds() {
6482		return this._inViewTiles.getAllIds(true);
6483	}
6484	getRenderableIds(symbolLayer) {
6485		return this._inViewTiles.getRenderableIds(this.transform?.bearingInRadians, symbolLayer);
6486	}
6487	hasRenderableParent(tileID) {
6488		const parentZ = tileID.overscaledZ - 1;
6489		if (parentZ >= this._source.minzoom) {
6490			const parentTile = this.getLoadedTile(tileID.scaledTo(parentZ));
6491			if (parentTile) return this._inViewTiles.isIdRenderable(parentTile.tileID.key);
6492		}
6493		return false;
6494	}
6495	/**
6496	* Reload tiles based on the current state of the source.
6497	* @param sourceDataChanged - If `true`, reload all tiles using a state of 'expired' (errored tiles use 'loading' since they have nothing to show yet), otherwise reload only non-errored tiles using state of 'reloading'.
6498	* @param shouldReloadTileOptions - Set of options associated with a `MapSourceDataChangedEvent` that can be passed back to the associated `Source` determine whether a tile should be reloaded.
6499	*/
6500	reload(sourceDataChanged, shouldReloadTileOptions = void 0) {
6501		if (this._paused) {
6502			this._shouldReloadOnResume = true;
6503			return;
6504		}
6505		this._outOfViewCache.reset();
6506		for (const id of this._inViewTiles.getAllIds()) {
6507			const tile = this._inViewTiles.getTileById(id);
6508			if (shouldReloadTileOptions && !this._source.shouldReloadTile(tile, shouldReloadTileOptions)) continue;
6509			else if (sourceDataChanged) this._reloadTile(id, tile.state === "errored" ? "loading" : "expired");
6510			else if (tile.state !== "errored") this._reloadTile(id, "reloading");
6511		}
6512	}
6513	async _reloadTile(id, state) {
6514		const tile = this._inViewTiles.getTileById(id);
6515		if (!tile) return;
6516		const hadData = tile.hasData();
6517		if (tile.state !== "loading") tile.state = state;
6518		await this._loadTile(tile, id, state, hadData);
6519	}
6520	_tileLoaded(tile, id, previousState, hadData, result) {
6521		if (!hadData) {
6522			tile.timeAdded = now();
6523			if (tile.selfFading) tile.fadeEndTime = tile.timeAdded + this._rasterFadeDuration;
6524		}
6525		if (previousState === "expired") tile.refreshedUponExpiration = true;
6526		this._setTileReloadTimer(id, tile);
6527		if (result?.unmodified) return;
6528		if (this.getSource().type === "raster-dem" && tile.dem) backfillDEM(tile, this._inViewTiles);
6529		tile.featureStateRevision = -1;
6530		this._state.initializeTileState(tile, this.map ? this.map.painter : null);
6531		if (!tile.aborted) this._source.fire(new MapSourceDataEvent("data", {
6532			tile,
6533			coord: tile.tileID
6534		}));
6535	}
6536	/**
6537	* Get a specific tile by TileID
6538	*/
6539	getTile(tileID) {
6540		return this.getTileByID(tileID.key);
6541	}
6542	/**
6543	* Get a specific tile by id
6544	*/
6545	getTileByID(id) {
6546		return this._inViewTiles.getTileById(id);
6547	}
6548	/**
6549	* Retain the uppermost loaded children of each provided target tile, within a variable covering zoom range.
6550	*
6551	* On pitched maps, different parts of the screen show different zoom levels simultaneously.
6552	* Ideal tiles are generated using coveringTiles() above, which returns the ideal tile set for
6553	* the current pitched plane, which can carry tiles of varying zooms (overscaledZ).
6554	* See: https://maplibre.org/maplibre-gl-js/docs/examples/level-of-detail-control/
6555	*
6556	* A fixed `maxCoveringZoom` on a pitched map would incorrectly intersect with some
6557	* ideal tiles and cause distant high-pitch tiles to skip their uppermost children.
6558	*
6559	* To solve this, we calculate the max covering zoom for each ideal tile separately using its
6560	* `overscaledZ`. This effectively makes the "max covering zoom plane" parallel to the
6561	* "ideal tile plane," ensuring that we correctly capture the uppermost children
6562	* of each ideal tile across the pitched view.
6563	*
6564	* Analogy: imagine two sheets of paper in 3D space:
6565	*   - one sheet = ideal tiles at varying overscaledZ
6566	*   - the second sheet = maxCoveringZoom
6567	*
6568	* @param retainTileMap - this parameters will be updated with the child tiles to keep
6569	* @param idealTilesWithoutData - which of the ideal tiles currently does not have loaded data
6570	* @return a set of tiles that need to be loaded
6571	*/
6572	_retainLoadedChildren(retainTileMap, idealTilesWithoutData) {
6573		const loadedDescendents = this._getLoadedDescendents(idealTilesWithoutData);
6574		const incomplete = /* @__PURE__ */ new Set();
6575		for (const targetID of idealTilesWithoutData) {
6576			const descendents = loadedDescendents[targetID.key];
6577			if (!descendents?.length) {
6578				incomplete.add(targetID);
6579				continue;
6580			}
6581			const maxCoveringZoom = targetID.overscaledZ + TileManager.maxOverzooming;
6582			const candidates = descendents.filter((t) => t.tileID.overscaledZ <= maxCoveringZoom);
6583			if (!candidates.length) {
6584				incomplete.add(targetID);
6585				continue;
6586			}
6587			const topZoom = Math.min(...candidates.map((t) => t.tileID.overscaledZ));
6588			const topIDs = candidates.filter((t) => t.tileID.overscaledZ === topZoom).map((t) => t.tileID);
6589			for (const tileID of topIDs) retainTileMap[tileID.key] = tileID;
6590			if (!this._areDescendentsComplete(topIDs, topZoom, targetID.overscaledZ)) incomplete.add(targetID);
6591		}
6592		return incomplete;
6593	}
6594	/**
6595	* Return dictionary of qualified loaded descendents for each provided target tile id
6596	*/
6597	_getLoadedDescendents(targetTileIDs) {
6598		const loadedDescendents = {};
6599		for (const tile of this._inViewTiles.getAllTiles().filter((tile) => tile.hasData())) for (const targetID of targetTileIDs) if (tile.tileID.isChildOf(targetID)) {
6600			loadedDescendents[targetID.key] ||= [];
6601			loadedDescendents[targetID.key].push(tile);
6602		}
6603		return loadedDescendents;
6604	}
6605	/**
6606	* Determine if tile ids fully cover the current generation.
6607	* - 1st generation: need 4 children or 1 overscaled child
6608	* - 2nd generation: need 16 children or 1 overscaled child
6609	*/
6610	_areDescendentsComplete(generationIDs, generationZ, ancestorZ) {
6611		if (generationIDs.length === 1 && generationIDs[0].isOverscaled()) return generationIDs[0].overscaledZ === generationZ;
6612		else return Math.pow(4, generationZ - ancestorZ) === generationIDs.length;
6613	}
6614	/**
6615	* Get a currently loaded tile.
6616	* - a cached tile is not a loaded tile
6617	* @returns the tile if it's in view and had data, null otherwise.
6618	*/
6619	getLoadedTile(tileID) {
6620		return this._inViewTiles.getLoadedTile(tileID);
6621	}
6622	/**
6623	* Resizes the tile cache based on the current viewport's size
6624	* or the maxTileCacheSize option passed during map creation
6625	*
6626	* Larger viewports use more tiles and need larger caches. Larger viewports
6627	* are more likely to be found on devices with more memory and on pages where
6628	* the map is more important.
6629	*/
6630	updateCacheSize(transform) {
6631		const approxTilesInView = (Math.ceil(transform.width / this._source.tileSize) + 1) * (Math.ceil(transform.height / this._source.tileSize) + 1);
6632		const commonZoomRange = this._maxTileCacheZoomLevels === null ? config.MAX_TILE_CACHE_ZOOM_LEVELS : this._maxTileCacheZoomLevels;
6633		const viewDependentMaxSize = Math.floor(approxTilesInView * commonZoomRange);
6634		const maxSize = typeof this._maxTileCacheSize === "number" ? Math.min(this._maxTileCacheSize, viewDependentMaxSize) : viewDependentMaxSize;
6635		this._outOfViewCache.setMaxSize(maxSize);
6636	}
6637	handleWrapJump(lng) {
6638		const worldDifference = (lng - (this._prevLng === void 0 ? lng : this._prevLng)) / 360;
6639		const wrapDelta = Math.round(worldDifference);
6640		this._prevLng = lng;
6641		if (wrapDelta) {
6642			this._inViewTiles.handleWrapJump(wrapDelta);
6643			this._resetTileReloadTimers();
6644		}
6645	}
6646	/**
6647	* The highest elevation this source's symbols may reach, in meters: `symbol-height-offset`
6648	* constants read from the visible symbol layers, data-driven maxima tracked by the loaded
6649	* buckets at layout time. Only data-driven layers scan the loaded tiles, so styles without
6650	* them pay nothing beyond the layer loop. The value is a high-water mark: a maximum seen once is kept even
6651	* after its tile unloads, otherwise dropping the tile would also drop the reason to keep it,
6652	* and the tile could not come back while its content is still visible. The mark resets with
6653	* the tiles in `clearTiles`.
6654	*/
6655	_updateMaxContentElevation() {
6656		let maxElevation = this._maxContentElevationSeen;
6657		const layers = this.map?.style?._layers;
6658		if (!layers) return maxElevation;
6659		const tiles = this._inViewTiles.getAllTiles();
6660		for (const layerId in layers) {
6661			const layer = layers[layerId];
6662			if (layer.type !== "symbol" || layer.source !== this.id || layer.isHidden(this.transform.zoom)) continue;
6663			const symbolLayer = layer;
6664			if (!symbolLayer.layout) continue;
6665			const heightOffset = symbolLayer.layout.get("symbol-height-offset");
6666			maxElevation = Math.max(maxElevation, heightOffset.constantOr(0));
6667			if (heightOffset.isConstant()) continue;
6668			for (const tile of tiles) {
6669				const bucket = tile.getBucket(layer);
6670				if (bucket && bucket.maxHeightOffset > maxElevation) maxElevation = bucket.maxHeightOffset;
6671			}
6672		}
6673		this._maxContentElevationSeen = maxElevation;
6674		return maxElevation;
6675	}
6676	/**
6677	* Removes tiles that are outside the viewport and adds new tiles that
6678	* are inside the viewport.
6679	*/
6680	update(transform, terrain) {
6681		if (!this._sourceLoaded || this._paused) return;
6682		this.transform = transform;
6683		this.terrain = terrain;
6684		this.updateCacheSize(transform);
6685		this.handleWrapJump(this.transform.center.lng);
6686		let idealTileIDs;
6687		if (!this.used && !this.usedForTerrain) idealTileIDs = [];
6688		else if (this._source.tileID) idealTileIDs = transform.getVisibleUnwrappedCoordinates(this._source.tileID).map((unwrapped) => new OverscaledTileID(unwrapped.canonical.z, unwrapped.wrap, unwrapped.canonical.z, unwrapped.canonical.x, unwrapped.canonical.y));
6689		else {
6690			idealTileIDs = coveringTiles(transform, {
6691				tileSize: this.usedForTerrain ? this.tileSize : this._source.tileSize,
6692				minzoom: this._source.minzoom,
6693				maxzoom: this._source.type === "vector" && this.map._zoomLevelsToOverscale !== void 0 ? Math.max(this._source.maxzoom, transform.maxZoom - this.map._zoomLevelsToOverscale) : this._source.maxzoom,
6694				roundZoom: this.usedForTerrain ? false : this._source.roundZoom,
6695				reparseOverscaled: this._source.reparseOverscaled,
6696				terrain,
6697				calculateTileZoom: this._source.calculateTileZoom,
6698				maxContentElevation: this._updateMaxContentElevation()
6699			});
6700			if (this._source.hasTile) idealTileIDs = idealTileIDs.filter((coord) => this._source.hasTile(coord));
6701		}
6702		if (this.usedForTerrain) idealTileIDs = this._addTerrainIdealTiles(idealTileIDs);
6703		const noPendingDataEmissions = idealTileIDs.length === 0 && !this._updated && this._didEmitContent;
6704		this._updated = true;
6705		if (noPendingDataEmissions) this.fire(new MapSourceDataEvent("data", {
6706			sourceDataType: "idle",
6707			sourceId: this.id
6708		}));
6709		const zoom = coveringZoomLevel(transform, this._source);
6710		const retain = this._updateRetainedTiles(idealTileIDs, zoom);
6711		const isRaster = isRasterType(this._source.type);
6712		if (isRaster && this._rasterFadeDuration > 0 && !terrain) updateFadingTiles(this._inViewTiles, idealTileIDs, retain, this._maxFadingAncestorLevels, this._source.minzoom, this._source.maxzoom, this._rasterFadeDuration);
6713		if (isRaster) this._cleanUpRasterTiles(retain);
6714		else this._cleanUpVectorTiles(retain);
6715	}
6716	/**
6717	* Remove raster tiles that are no longer retained
6718	*/
6719	_cleanUpRasterTiles(retain) {
6720		for (const id of this._inViewTiles.getAllIds()) if (!retain[id]) this._removeTile(id);
6721	}
6722	/**
6723	* Remove vector tiles that are no longer retained and also not needed for symbol fading
6724	*/
6725	_cleanUpVectorTiles(retain) {
6726		for (const id of this._inViewTiles.getAllIds()) {
6727			const tile = this._inViewTiles.getTileById(id);
6728			if (retain[id]) {
6729				tile.clearSymbolFadeHold();
6730				continue;
6731			}
6732			if (!tile.hasSymbolBuckets) {
6733				this._removeTile(id);
6734				continue;
6735			}
6736			if (!tile.holdingForSymbolFade()) tile.setSymbolHoldDuration(this.map._fadeDuration);
6737			else if (tile.symbolFadeFinished()) this._removeTile(id);
6738		}
6739	}
6740	/**
6741	* Add ideal tiles needed for 3D terrain rendering
6742	*/
6743	_addTerrainIdealTiles(idealTileIDs) {
6744		const ancestors = [];
6745		for (const tileID of idealTileIDs) if (tileID.canonical.z > this._source.minzoom) {
6746			const parent = tileID.scaledTo(tileID.canonical.z - 1);
6747			ancestors.push(parent);
6748			const parent2 = tileID.scaledTo(Math.max(this._source.minzoom, Math.min(tileID.canonical.z, 5)));
6749			ancestors.push(parent2);
6750		}
6751		return idealTileIDs.concat(ancestors);
6752	}
6753	releaseSymbolFadeTiles() {
6754		for (const id of this._inViewTiles.getAllIds()) if (this._inViewTiles.getTileById(id).holdingForSymbolFade()) this._removeTile(id);
6755	}
6756	/**
6757	* Set tiles to be retained on update of the source. For ideal tiles that do not have data, retain their loaded
6758	* children so they can be displayed as substitutes pending load of each ideal tile (to reduce flickering).
6759	* If no loaded children are available, fallback to seeking loaded parents as an alternative substitute.
6760	*/
6761	_updateRetainedTiles(idealTileIDs, zoom) {
6762		const idealTilesWithoutData = /* @__PURE__ */ new Set();
6763		for (const idealID of idealTileIDs) if (!this._addTile(idealID).hasData()) idealTilesWithoutData.add(idealID);
6764		const retainTileMap = idealTileIDs.reduce((acc, t) => {
6765			acc[t.key] = t;
6766			return acc;
6767		}, {});
6768		const tileIdsWithoutData = this._retainLoadedChildren(retainTileMap, idealTilesWithoutData);
6769		const checked = {};
6770		const minCoveringZoom = Math.max(zoom - TileManager.maxUnderzooming, this._source.minzoom);
6771		for (const tileID of tileIdsWithoutData) {
6772			let tile = this._inViewTiles.getTileById(tileID.key);
6773			let parentWasRequested = tile?.wasRequested();
6774			for (let overscaledZ = tileID.overscaledZ - 1; overscaledZ >= minCoveringZoom; --overscaledZ) {
6775				const parentId = tileID.scaledTo(overscaledZ);
6776				if (checked[parentId.key]) break;
6777				checked[parentId.key] = true;
6778				tile = this.getTile(parentId);
6779				if (!tile && parentWasRequested) tile = this._addTile(parentId);
6780				if (tile) {
6781					const hasData = tile.hasData();
6782					if (hasData || !this.map?.cancelPendingTileRequestsWhileZooming || parentWasRequested) retainTileMap[parentId.key] = parentId;
6783					parentWasRequested = tile.wasRequested();
6784					if (hasData) break;
6785				}
6786			}
6787		}
6788		return retainTileMap;
6789	}
6790	/**
6791	* Add a tile, given its coordinate, to the pyramid.
6792	*/
6793	_addTile(tileID) {
6794		let tile = this._inViewTiles.getTileById(tileID.key);
6795		if (tile) return tile;
6796		tile = this._outOfViewCache.getAndRemove(tileID);
6797		if (tile) {
6798			tile.resetFadeLogic();
6799			this._setTileReloadTimer(tileID.key, tile);
6800			tile.tileID = tileID;
6801			this._state.initializeTileState(tile, this.map ? this.map.painter : null);
6802		}
6803		const cached = tile;
6804		if (!tile) {
6805			tile = new Tile(tileID, this._source.tileSize * tileID.overscaleFactor());
6806			this._loadTile(tile, tileID.key, tile.state, false);
6807		}
6808		tile.uses++;
6809		this._inViewTiles.setTile(tileID.key, tile);
6810		if (!cached) this._source.fire(new MapSourceDataEvent("dataloading", {
6811			tile,
6812			coord: tile.tileID
6813		}));
6814		return tile;
6815	}
6816	/**
6817	* Set a timeout to reload the tile after it expires
6818	*/
6819	_setTileReloadTimer(id, tile) {
6820		this._clearTileReloadTimer(id);
6821		const expiryTimeout = tile.getExpiryTimeout();
6822		if (expiryTimeout) {
6823			const reload = () => {
6824				this._reloadTile(id, "expired");
6825				delete this._timers[id];
6826			};
6827			this._timers[id] = setTimeout(reload, expiryTimeout);
6828		}
6829	}
6830	_clearTileReloadTimer(id) {
6831		const timeout = this._timers[id];
6832		if (timeout) {
6833			clearTimeout(timeout);
6834			delete this._timers[id];
6835		}
6836	}
6837	_resetTileReloadTimers() {
6838		for (const id in this._timers) {
6839			clearTimeout(this._timers[id]);
6840			delete this._timers[id];
6841		}
6842		for (const id of this._inViewTiles.getAllIds()) {
6843			const tile = this._inViewTiles.getTileById(id);
6844			this._setTileReloadTimer(id, tile);
6845		}
6846	}
6847	/**
6848	* Reload any currently renderable tiles that are match one of the incoming `tileId` x/y/z
6849	*/
6850	refreshTiles(tileIds) {
6851		for (const id of this._inViewTiles.getAllIds()) {
6852			const tile = this._inViewTiles.getTileById(id);
6853			if (!this._inViewTiles.isIdRenderable(id) && tile.state != "errored") continue;
6854			if (tileIds.some((tid) => tid.equals(tile.tileID.canonical))) this._reloadTile(id, "expired");
6855		}
6856	}
6857	/**
6858	* Remove a tile, given its id, from the pyramid
6859	*/
6860	_removeTile(id) {
6861		const tile = this._inViewTiles.getTileById(id);
6862		if (!tile) return;
6863		tile.uses--;
6864		this._inViewTiles.deleteTileById(id);
6865		this._clearTileReloadTimer(id);
6866		if (tile.uses > 0) return;
6867		if (tile.hasData() && tile.state !== "reloading") this._outOfViewCache.add(tile.tileID, tile, tile.getExpiryTimeout());
6868		else {
6869			tile.aborted = true;
6870			this._abortTile(tile);
6871			this._unloadTile(tile);
6872		}
6873	}
6874	/** @internal
6875	* Handles incoming source data messages (i.e. after the source has been updated via a worker that has fired
6876	* to map.ts data event). For sources with mutable data, the 'content' event fires when the underlying data
6877	* to a source has changed. (i.e. GeoJSONSource.setData and ImageSource.setCoordinates)
6878	*/
6879	_dataHandler(e) {
6880		if (e.dataType !== "source") return;
6881		if (e.sourceDataType === "metadata") {
6882			this._sourceLoaded = true;
6883			return;
6884		}
6885		if (e.sourceDataType !== "content" || !this._sourceLoaded || this._paused) return;
6886		this.reload(e.sourceDataChanged, e.shouldReloadTileOptions);
6887		if (this.transform) this.update(this.transform, this.terrain);
6888		this._didEmitContent = true;
6889	}
6890	/**
6891	* Forgets the recorded maximum content elevation, so the next update recomputes it from the
6892	* current layers and loaded tiles. Called when a symbol layer is removed, since the removed
6893	* layer's heights would otherwise keep expanding tile coverage.
6894	*/
6895	resetMaxContentElevation() {
6896		this._maxContentElevationSeen = 0;
6897	}
6898	/**
6899	* Remove all tiles from this pyramid
6900	*/
6901	clearTiles() {
6902		this._shouldReloadOnResume = false;
6903		this._paused = false;
6904		this.resetMaxContentElevation();
6905		for (const id of this._inViewTiles.getAllIds()) this._removeTile(id);
6906		this._outOfViewCache.reset();
6907	}
6908	/**
6909	* Search through our current tiles and attempt to find the tiles that
6910	* cover the given bounds.
6911	* @param pointQueryGeometry - coordinates of the corners of bounding rectangle
6912	* @returns result items have `{tile, minX, maxX, minY, maxY}`, where min/max bounding values are the given bounds transformed in into the coordinate space of this tile.
6913	*/
6914	tilesIn(pointQueryGeometry, maxPitchScaleFactor, has3DLayer) {
6915		const tileResults = [];
6916		const transform = this.transform;
6917		if (!transform) return tileResults;
6918		const allowWorldCopies = transform.getCoveringTilesDetailsProvider().allowWorldCopies();
6919		const cameraPointQueryGeometry = has3DLayer ? transform.getCameraQueryGeometry(pointQueryGeometry) : pointQueryGeometry;
6920		const project = (point) => transform.screenPointToMercatorCoordinate(point, this.terrain);
6921		const queryGeometry = this.transformBbox(pointQueryGeometry, project, !allowWorldCopies);
6922		const cameraQueryGeometry = this.transformBbox(cameraPointQueryGeometry, project, !allowWorldCopies);
6923		const ids = this.getIds();
6924		const cameraBounds = Bounds.fromPoints(cameraQueryGeometry);
6925		for (const id of ids) {
6926			const tile = this._inViewTiles.getTileById(id);
6927			if (tile.holdingForSymbolFade()) continue;
6928			const tileIDs = allowWorldCopies ? [tile.tileID] : [tile.tileID.unwrapTo(-1), tile.tileID.unwrapTo(0)];
6929			const scale = Math.pow(2, transform.zoom - tile.tileID.overscaledZ);
6930			const queryPadding = maxPitchScaleFactor * tile.queryPadding * EXTENT / tile.tileSize / scale;
6931			for (const tileID of tileIDs) {
6932				const tileSpaceBounds = cameraBounds.map((point) => tileID.getTilePoint(new MercatorCoordinate(point.x, point.y)));
6933				tileSpaceBounds.expandBy(queryPadding);
6934				if (tileSpaceBounds.intersects(EXTENT_BOUNDS)) {
6935					const tileSpaceQueryGeometry = queryGeometry.map((c) => tileID.getTilePoint(c));
6936					const tileSpaceCameraQueryGeometry = cameraQueryGeometry.map((c) => tileID.getTilePoint(c));
6937					tileResults.push({
6938						tile,
6939						tileID: allowWorldCopies ? tileID : tileID.unwrapTo(0),
6940						queryGeometry: tileSpaceQueryGeometry,
6941						cameraQueryGeometry: tileSpaceCameraQueryGeometry,
6942						scale
6943					});
6944				}
6945			}
6946		}
6947		return tileResults;
6948	}
6949	transformBbox(geom, project, checkWrap) {
6950		let transformed = geom.map(project);
6951		if (checkWrap) {
6952			const bounds = Bounds.fromPoints(geom);
6953			bounds.shrinkBy(Math.min(bounds.width(), bounds.height()) * .001);
6954			const projected = bounds.map(project);
6955			if (!Bounds.fromPoints(transformed).covers(projected)) transformed = transformed.map((coord) => coord.x > .5 ? new MercatorCoordinate(coord.x - 1, coord.y, coord.z) : coord);
6956		}
6957		return transformed;
6958	}
6959	getVisibleCoordinates(symbolLayer) {
6960		const coords = this.getRenderableIds(symbolLayer).map((id) => this._inViewTiles.getTileById(id).tileID);
6961		if (this.transform) this.transform.populateCache(coords);
6962		return coords;
6963	}
6964	hasTransition() {
6965		if (this._source.hasTransition()) return true;
6966		return isRasterType(this._source.type) && hasRasterTransition(this._inViewTiles, this._rasterFadeDuration);
6967	}
6968	setRasterFadeDuration(fadeDuration) {
6969		this._rasterFadeDuration = fadeDuration;
6970	}
6971	/**
6972	* Set the value of a particular state for a feature
6973	*/
6974	setFeatureState(sourceLayer, featureId, state) {
6975		sourceLayer ||= GEOJSON_TILE_LAYER_NAME;
6976		this._state.updateState(sourceLayer, featureId, state);
6977	}
6978	/**
6979	* Resets the value of a particular state key for a feature
6980	*/
6981	removeFeatureState(sourceLayer, featureId, key) {
6982		sourceLayer ||= GEOJSON_TILE_LAYER_NAME;
6983		this._state.removeFeatureState(sourceLayer, featureId, key);
6984	}
6985	/**
6986	* Get the entire state object for a feature
6987	*/
6988	getFeatureState(sourceLayer, featureId) {
6989		sourceLayer ||= GEOJSON_TILE_LAYER_NAME;
6990		return this._state.getState(sourceLayer, featureId);
6991	}
6992	/**
6993	* Sets the set of keys that the tile depends on. This allows tiles to
6994	* be reloaded when their dependencies change.
6995	*/
6996	setDependencies(tileKey, namespace, dependencies) {
6997		const tile = this._inViewTiles.getTileById(tileKey);
6998		if (tile) tile.setDependencies(namespace, dependencies);
6999	}
7000	/**
7001	* Reloads all tiles that depend on the given keys.
7002	*/
7003	reloadTilesForDependencies(namespaces, keys) {
7004		for (const id of this._inViewTiles.getAllIds()) if (this._inViewTiles.getTileById(id).hasDependency(namespaces, keys)) this._reloadTile(id, "reloading");
7005		this._outOfViewCache.filter((tile) => !tile.hasDependency(namespaces, keys));
7006	}
7007	areTilesLoaded() {
7008		for (const tile of this._inViewTiles.getAllTiles()) if (!(tile.state === "loaded" || tile.state === "errored")) return false;
7009		return true;
7010	}
7011};
7012//#endregion
7013//#region src/symbol/path_interpolator.ts
7014var PathInterpolator = class {
7015	constructor(points_, padding_) {
7016		this.reset(points_, padding_);
7017	}
7018	reset(points_, padding_) {
7019		this.points = points_ || [];
7020		this._distances = [0];
7021		for (let i = 1; i < this.points.length; i++) this._distances[i] = this._distances[i - 1] + this.points[i].dist(this.points[i - 1]);
7022		this.length = this._distances[this._distances.length - 1];
7023		this.padding = Math.min(padding_ || 0, this.length * .5);
7024		this.paddedLength = this.length - this.padding * 2;
7025	}
7026	lerp(t) {
7027		if (this.points.length === 1) return this.points[0];
7028		t = clamp(t, 0, 1);
7029		let currentIndex = 1;
7030		let distOfCurrentIdx = this._distances[currentIndex];
7031		const distToTarget = t * this.paddedLength + this.padding;
7032		while (distOfCurrentIdx < distToTarget && currentIndex < this._distances.length) distOfCurrentIdx = this._distances[++currentIndex];
7033		const idxOfPrevPoint = currentIndex - 1;
7034		const distOfPrevIdx = this._distances[idxOfPrevPoint];
7035		const segmentLength = distOfCurrentIdx - distOfPrevIdx;
7036		const segmentT = segmentLength > 0 ? (distToTarget - distOfPrevIdx) / segmentLength : 0;
7037		return this.points[idxOfPrevPoint].mult(1 - segmentT).add(this.points[currentIndex].mult(segmentT));
7038	}
7039};
7040//#endregion
7041//#region src/symbol/symbol_elevation.ts
7042/**
7043* Resolves the elevation of a symbol at a tile coordinate, combining the terrain elevation with the
7044* symbol's `symbol-height-offset`. `symbol-height-anchor` selects the datum the offset is measured
7045* from: the terrain surface below the symbol (`ground`) or the zero elevation datum (`absolute`),
7046* in which case the terrain below the symbol is ignored.
7047*
7048* Call this where the elevation is needed, so that `getElevation` keeps returning the plain terrain
7049* elevation and never has to be substituted with a wrapper.
7050*
7051* @param getElevation - the terrain elevation function, absent when the map has no terrain
7052* @param x - the tile x coordinate to evaluate the elevation at
7053* @param y - the tile y coordinate to evaluate the elevation at
7054* @param heightOffset - the evaluated `symbol-height-offset` of the symbol, in meters
7055* @param heightAnchorGround - whether `symbol-height-anchor` is `ground`
7056* @returns the elevation in meters, or undefined when the symbol sits at the zero elevation datum,
7057* which lets the projection take its fast path that ignores the z coordinate
7058*/
7059function getSymbolElevation(getElevation, x, y, heightOffset, heightAnchorGround) {
7060	if (!heightAnchorGround) return heightOffset;
7061	if (!getElevation) return heightOffset !== 0 ? heightOffset : void 0;
7062	return getElevation(x, y) + heightOffset;
7063}
7064//#endregion
7065//#region src/symbol/grid_index.ts
7066function overlapAllowed(overlapA, overlapB) {
7067	let allowed = true;
7068	if (overlapA === "always") {} else if (overlapA === "never" || overlapB === "never") allowed = false;
7069	return allowed;
7070}
7071/**
7072* @internal
7073* GridIndex is a data structure for testing the intersection of
7074* circles and rectangles in a 2d plane.
7075* It is optimized for rapid insertion and querying.
7076* GridIndex splits the plane into a set of "cells" and keeps track
7077* of which geometries intersect with each cell. At query time,
7078* full geometry comparisons are only done for items that share
7079* at least one cell. As long as the geometries are relatively
7080* uniformly distributed across the plane, this greatly reduces
7081* the number of comparisons necessary.
7082*/
7083var GridIndex = class {
7084	constructor(width, height, cellSize) {
7085		const boxCells = this.boxCells = [];
7086		const circleCells = this.circleCells = [];
7087		this.xCellCount = Math.ceil(width / cellSize);
7088		this.yCellCount = Math.ceil(height / cellSize);
7089		for (let i = 0; i < this.xCellCount * this.yCellCount; i++) {
7090			boxCells.push([]);
7091			circleCells.push([]);
7092		}
7093		this.circleKeys = [];
7094		this.boxKeys = [];
7095		this.bboxes = [];
7096		this.circles = [];
7097		this.width = width;
7098		this.height = height;
7099		this.xScale = this.xCellCount / width;
7100		this.yScale = this.yCellCount / height;
7101		this.boxUid = 0;
7102		this.circleUid = 0;
7103	}
7104	keysLength() {
7105		return this.boxKeys.length + this.circleKeys.length;
7106	}
7107	insert(key, x1, y1, x2, y2) {
7108		this._forEachCell(x1, y1, x2, y2, this._insertBoxCell, this.boxUid++);
7109		this.boxKeys.push(key);
7110		this.bboxes.push(x1);
7111		this.bboxes.push(y1);
7112		this.bboxes.push(x2);
7113		this.bboxes.push(y2);
7114	}
7115	insertCircle(key, x, y, radius) {
7116		this._forEachCell(x - radius, y - radius, x + radius, y + radius, this._insertCircleCell, this.circleUid++);
7117		this.circleKeys.push(key);
7118		this.circles.push(x);
7119		this.circles.push(y);
7120		this.circles.push(radius);
7121	}
7122	_insertBoxCell(x1, y1, x2, y2, cellIndex, uid) {
7123		this.boxCells[cellIndex].push(uid);
7124	}
7125	_insertCircleCell(x1, y1, x2, y2, cellIndex, uid) {
7126		this.circleCells[cellIndex].push(uid);
7127	}
7128	_query(x1, y1, x2, y2, hitTest, overlapMode, predicate) {
7129		if (x2 < 0 || x1 > this.width || y2 < 0 || y1 > this.height) return [];
7130		const result = [];
7131		if (x1 <= 0 && y1 <= 0 && this.width <= x2 && this.height <= y2) {
7132			if (hitTest) return [{
7133				key: null,
7134				x1,
7135				y1,
7136				x2,
7137				y2
7138			}];
7139			for (let boxUid = 0; boxUid < this.boxKeys.length; boxUid++) result.push({
7140				key: this.boxKeys[boxUid],
7141				x1: this.bboxes[boxUid * 4],
7142				y1: this.bboxes[boxUid * 4 + 1],
7143				x2: this.bboxes[boxUid * 4 + 2],
7144				y2: this.bboxes[boxUid * 4 + 3]
7145			});
7146			for (let circleUid = 0; circleUid < this.circleKeys.length; circleUid++) {
7147				const x = this.circles[circleUid * 3];
7148				const y = this.circles[circleUid * 3 + 1];
7149				const radius = this.circles[circleUid * 3 + 2];
7150				result.push({
7151					key: this.circleKeys[circleUid],
7152					x1: x - radius,
7153					y1: y - radius,
7154					x2: x + radius,
7155					y2: y + radius
7156				});
7157			}
7158		} else {
7159			const queryArgs = {
7160				hitTest,
7161				overlapMode,
7162				seenUids: {
7163					box: {},
7164					circle: {}
7165				}
7166			};
7167			this._forEachCell(x1, y1, x2, y2, this._queryCell, result, queryArgs, predicate);
7168		}
7169		return result;
7170	}
7171	query(x1, y1, x2, y2) {
7172		return this._query(x1, y1, x2, y2, false, null);
7173	}
7174	hitTest(x1, y1, x2, y2, overlapMode, predicate) {
7175		return this._query(x1, y1, x2, y2, true, overlapMode, predicate).length > 0;
7176	}
7177	hitTestCircle(x, y, radius, overlapMode, predicate) {
7178		const x1 = x - radius;
7179		const x2 = x + radius;
7180		const y1 = y - radius;
7181		const y2 = y + radius;
7182		if (x2 < 0 || x1 > this.width || y2 < 0 || y1 > this.height) return false;
7183		const result = [];
7184		const queryArgs = {
7185			hitTest: true,
7186			overlapMode,
7187			circle: {
7188				x,
7189				y,
7190				radius
7191			},
7192			seenUids: {
7193				box: {},
7194				circle: {}
7195			}
7196		};
7197		this._forEachCell(x1, y1, x2, y2, this._queryCellCircle, result, queryArgs, predicate);
7198		return result.length > 0;
7199	}
7200	_queryCell(x1, y1, x2, y2, cellIndex, result, queryArgs, predicate) {
7201		const { seenUids, hitTest, overlapMode } = queryArgs;
7202		const boxCell = this.boxCells[cellIndex];
7203		const epsilon = 1e-6;
7204		if (boxCell !== null) {
7205			const bboxes = this.bboxes;
7206			for (const boxUid of boxCell) if (!seenUids.box[boxUid]) {
7207				seenUids.box[boxUid] = true;
7208				const offset = boxUid * 4;
7209				const key = this.boxKeys[boxUid];
7210				if (x1 <= bboxes[offset + 2] + epsilon && y1 <= bboxes[offset + 3] + epsilon && x2 >= bboxes[offset + 0] - epsilon && y2 >= bboxes[offset + 1] - epsilon && (!predicate || predicate(key))) {
7211					if (!hitTest || !overlapAllowed(overlapMode, key.overlapMode)) {
7212						result.push({
7213							key,
7214							x1: bboxes[offset],
7215							y1: bboxes[offset + 1],
7216							x2: bboxes[offset + 2],
7217							y2: bboxes[offset + 3]
7218						});
7219						if (hitTest) return true;
7220					}
7221				}
7222			}
7223		}
7224		const circleCell = this.circleCells[cellIndex];
7225		if (circleCell !== null) {
7226			const circles = this.circles;
7227			for (const circleUid of circleCell) if (!seenUids.circle[circleUid]) {
7228				seenUids.circle[circleUid] = true;
7229				const offset = circleUid * 3;
7230				const key = this.circleKeys[circleUid];
7231				if (this._circleAndRectCollide(circles[offset], circles[offset + 1], circles[offset + 2], x1, y1, x2, y2) && (!predicate || predicate(key))) {
7232					if (!hitTest || !overlapAllowed(overlapMode, key.overlapMode)) {
7233						const x = circles[offset];
7234						const y = circles[offset + 1];
7235						const radius = circles[offset + 2];
7236						result.push({
7237							key,
7238							x1: x - radius,
7239							y1: y - radius,
7240							x2: x + radius,
7241							y2: y + radius
7242						});
7243						if (hitTest) return true;
7244					}
7245				}
7246			}
7247		}
7248		return false;
7249	}
7250	_queryCellCircle(x1, y1, x2, y2, cellIndex, result, queryArgs, predicate) {
7251		const { circle, seenUids, overlapMode } = queryArgs;
7252		const boxCell = this.boxCells[cellIndex];
7253		if (boxCell !== null) {
7254			const bboxes = this.bboxes;
7255			for (const boxUid of boxCell) if (!seenUids.box[boxUid]) {
7256				seenUids.box[boxUid] = true;
7257				const offset = boxUid * 4;
7258				const key = this.boxKeys[boxUid];
7259				if (this._circleAndRectCollide(circle.x, circle.y, circle.radius, bboxes[offset + 0], bboxes[offset + 1], bboxes[offset + 2], bboxes[offset + 3]) && (!predicate || predicate(key)) && !overlapAllowed(overlapMode, key.overlapMode)) {
7260					result.push(true);
7261					return true;
7262				}
7263			}
7264		}
7265		const circleCell = this.circleCells[cellIndex];
7266		if (circleCell !== null) {
7267			const circles = this.circles;
7268			for (const circleUid of circleCell) if (!seenUids.circle[circleUid]) {
7269				seenUids.circle[circleUid] = true;
7270				const offset = circleUid * 3;
7271				const key = this.circleKeys[circleUid];
7272				if (this._circlesCollide(circles[offset], circles[offset + 1], circles[offset + 2], circle.x, circle.y, circle.radius) && (!predicate || predicate(key)) && !overlapAllowed(overlapMode, key.overlapMode)) {
7273					result.push(true);
7274					return true;
7275				}
7276			}
7277		}
7278	}
7279	_forEachCell(x1, y1, x2, y2, fn, arg1, arg2, predicate) {
7280		const cx1 = this._convertToXCellCoord(x1);
7281		const cy1 = this._convertToYCellCoord(y1);
7282		const cx2 = this._convertToXCellCoord(x2);
7283		const cy2 = this._convertToYCellCoord(y2);
7284		for (let x = cx1; x <= cx2; x++) for (let y = cy1; y <= cy2; y++) {
7285			const cellIndex = this.xCellCount * y + x;
7286			if (fn.call(this, x1, y1, x2, y2, cellIndex, arg1, arg2, predicate)) return;
7287		}
7288	}
7289	_convertToXCellCoord(x) {
7290		return Math.max(0, Math.min(this.xCellCount - 1, Math.floor(x * this.xScale)));
7291	}
7292	_convertToYCellCoord(y) {
7293		return Math.max(0, Math.min(this.yCellCount - 1, Math.floor(y * this.yScale)));
7294	}
7295	_circlesCollide(x1, y1, r1, x2, y2, r2) {
7296		const dx = x2 - x1;
7297		const dy = y2 - y1;
7298		const bothRadii = r1 + r2;
7299		return bothRadii * bothRadii > dx * dx + dy * dy;
7300	}
7301	_circleAndRectCollide(circleX, circleY, radius, x1, y1, x2, y2) {
7302		const halfRectWidth = (x2 - x1) / 2;
7303		const distX = Math.abs(circleX - (x1 + halfRectWidth));
7304		if (distX > halfRectWidth + radius) return false;
7305		const halfRectHeight = (y2 - y1) / 2;
7306		const distY = Math.abs(circleY - (y1 + halfRectHeight));
7307		if (distY > halfRectHeight + radius) return false;
7308		if (distX <= halfRectWidth || distY <= halfRectHeight) return true;
7309		const dx = distX - halfRectWidth;
7310		const dy = distY - halfRectHeight;
7311		return dx * dx + dy * dy <= radius * radius;
7312	}
7313};
7314//#endregion
7315//#region src/util/fast_maths.ts
7316function fastInvertTransformMat4(dst, src) {
7317	const sXYSqInv = 1 / (src[0] * src[0] + src[1] * src[1] + src[2] * src[2]);
7318	const sZSqInv = 1 / (src[8] * src[8] + src[9] * src[9] + src[10] * src[10]);
7319	const s0 = src[0] * sXYSqInv;
7320	const s4 = src[4] * sXYSqInv;
7321	const s8 = src[8] * sZSqInv;
7322	const s1 = src[1] * sXYSqInv;
7323	const s5 = src[5] * sXYSqInv;
7324	const s9 = src[9] * sZSqInv;
7325	const s2 = src[2] * sXYSqInv;
7326	const s6 = src[6] * sXYSqInv;
7327	const s10 = src[10] * sZSqInv;
7328	dst[0] = s0;
7329	dst[1] = s4;
7330	dst[2] = s8;
7331	dst[4] = s1;
7332	dst[5] = s5;
7333	dst[6] = s9;
7334	dst[8] = s2;
7335	dst[9] = s6;
7336	dst[10] = s10;
7337	const t0 = src[12];
7338	const t1 = src[13];
7339	const t2 = src[14];
7340	dst[12] = -s0 * t0 - s1 * t1 - s2 * t2;
7341	dst[13] = -s4 * t0 - s5 * t1 - s6 * t2;
7342	dst[14] = -s8 * t0 - s9 * t1 - s10 * t2;
7343	dst[3] = 0;
7344	dst[7] = 0;
7345	dst[11] = 0;
7346	dst[15] = 1;
7347	return dst;
7348}
7349function fastInvertProjMat4(dst, src) {
7350	dst[0] = 1 / src[0];
7351	dst[1] = 0;
7352	dst[2] = 0;
7353	dst[3] = 0;
7354	dst[4] = 0;
7355	dst[5] = 1 / src[5];
7356	dst[6] = 0;
7357	dst[7] = 0;
7358	dst[8] = 0;
7359	dst[9] = 0;
7360	dst[10] = 0;
7361	dst[11] = 1 / src[14];
7362	dst[12] = 0;
7363	dst[13] = 0;
7364	dst[14] = -1;
7365	dst[15] = src[10] / src[14];
7366	return dst;
7367}
7368function fastInvertSkewMat4(dst, src) {
7369	const invDetXY = 1 / (src[0] * src[5] - src[1] * src[4]);
7370	dst[0] = src[5] * invDetXY;
7371	dst[1] = -src[1] * invDetXY;
7372	dst[2] = 0;
7373	dst[3] = 0;
7374	dst[4] = -src[4] * invDetXY;
7375	dst[5] = src[0] * invDetXY;
7376	dst[6] = 0;
7377	dst[7] = 0;
7378	dst[8] = 0;
7379	dst[9] = 0;
7380	dst[10] = 1 / src[10];
7381	dst[11] = 0;
7382	dst[12] = 0;
7383	dst[13] = 0;
7384	dst[14] = 0;
7385	dst[15] = 1 / src[15];
7386	return dst;
7387}
7388//#endregion
7389//#region src/symbol/projection.ts
7390/**
7391* Pre-allocate objects to avoid online allocation
7392*/
7393const tmpMat4 = create$1();
7394function getPitchedLabelPlaneMatrix(rotateWithMap, transform, pixelsToTileUnits) {
7395	const m = create$1();
7396	if (!rotateWithMap) {
7397		const { vecSouth, vecEast } = getTileSkewVectors(transform);
7398		const skew = create$2();
7399		skew[0] = vecEast[0];
7400		skew[1] = vecEast[1];
7401		skew[2] = vecSouth[0];
7402		skew[3] = vecSouth[1];
7403		invert(skew, skew);
7404		m[0] = skew[0];
7405		m[1] = skew[1];
7406		m[4] = skew[2];
7407		m[5] = skew[3];
7408	}
7409	scale(m, m, [
7410		1 / pixelsToTileUnits,
7411		1 / pixelsToTileUnits,
7412		1
7413	]);
7414	return m;
7415}
7416function getGlCoordMatrix(pitchWithMap, rotateWithMap, transform, pixelsToTileUnits) {
7417	if (pitchWithMap) {
7418		const m = create$1();
7419		if (!rotateWithMap) {
7420			const { vecSouth, vecEast } = getTileSkewVectors(transform);
7421			m[0] = vecEast[0];
7422			m[1] = vecEast[1];
7423			m[4] = vecSouth[0];
7424			m[5] = vecSouth[1];
7425		}
7426		scale(m, m, [
7427			pixelsToTileUnits,
7428			pixelsToTileUnits,
7429			1
7430		]);
7431		return m;
7432	} else return transform.pixelsToClipSpaceMatrix;
7433}
7434function getTileSkewVectors(transform) {
7435	const cosRoll = Math.cos(transform.rollInRadians);
7436	const sinRoll = Math.sin(transform.rollInRadians);
7437	const cosPitch = Math.cos(transform.pitchInRadians);
7438	const cosBearing = Math.cos(transform.bearingInRadians);
7439	const sinBearing = Math.sin(transform.bearingInRadians);
7440	const vecSouth = create$3();
7441	vecSouth[0] = -cosBearing * cosPitch * sinRoll - sinBearing * cosRoll;
7442	vecSouth[1] = -sinBearing * cosPitch * sinRoll + cosBearing * cosRoll;
7443	const vecSouthLen = length$1(vecSouth);
7444	if (vecSouthLen < 1e-9) zero(vecSouth);
7445	else scale$3(vecSouth, vecSouth, 1 / vecSouthLen);
7446	const vecEast = create$3();
7447	vecEast[0] = cosBearing * cosPitch * cosRoll - sinBearing * sinRoll;
7448	vecEast[1] = sinBearing * cosPitch * cosRoll + cosBearing * sinRoll;
7449	const vecEastLen = length$1(vecEast);
7450	if (vecEastLen < 1e-9) zero(vecEast);
7451	else scale$3(vecEast, vecEast, 1 / vecEastLen);
7452	return {
7453		vecEast,
7454		vecSouth
7455	};
7456}
7457/**
7458* Resolves the elevation of the symbol described by `projectionContext` at a tile coordinate.
7459*/
7460function elevationAt(projectionContext, x, y) {
7461	return getSymbolElevation(projectionContext.getElevation, x, y, projectionContext.heightOffset ?? 0, projectionContext.heightAnchorGround ?? true);
7462}
7463/**
7464* Projects a point using a specified matrix, including the perspective divide.
7465* Uses a fast path if `elevation` is undefined.
7466*/
7467function projectWithMatrix(x, y, matrix, elevation) {
7468	let pos;
7469	if (elevation != null) {
7470		pos = [
7471			x,
7472			y,
7473			elevation,
7474			1
7475		];
7476		transformMat4(pos, pos, matrix);
7477	} else {
7478		pos = [
7479			x,
7480			y,
7481			0,
7482			1
7483		];
7484		xyTransformMat4(pos, pos, matrix);
7485	}
7486	const w = pos[3];
7487	return {
7488		point: new Point(pos[0] / w, pos[1] / w),
7489		signedDistanceFromCamera: w,
7490		isOccluded: false
7491	};
7492}
7493function getPerspectiveRatio(cameraToCenterDistance, signedDistanceFromCamera) {
7494	return .5 + .5 * (cameraToCenterDistance / signedDistanceFromCamera);
7495}
7496function isVisible(p, clippingBuffer) {
7497	return p.x >= -clippingBuffer[0] && p.x <= clippingBuffer[0] && p.y >= -clippingBuffer[1] && p.y <= clippingBuffer[1];
7498}
7499function updateLineLabels(bucket, painter, isText, pitchedLabelPlaneMatrix, pitchedLabelPlaneMatrixInverse, pitchWithMap, keepUpright, rotateToLine, unwrappedTileID, viewportWidth, viewportHeight, translation, getElevation) {
7500	const sizeData = isText ? bucket.textSizeData : bucket.iconSizeData;
7501	const partiallyEvaluatedSize = evaluateSizeForZoom(sizeData, painter.transform.zoom);
7502	const clippingBuffer = [256 / painter.width * 2 + 1, 256 / painter.height * 2 + 1];
7503	const dynamicLayoutVertexArray = isText ? bucket.text.dynamicLayoutVertexArray : bucket.icon.dynamicLayoutVertexArray;
7504	dynamicLayoutVertexArray.clear();
7505	const lineVertexArray = bucket.lineVertexArray;
7506	const placedSymbols = isText ? bucket.text.placedSymbolArray : bucket.icon.placedSymbolArray;
7507	const aspectRatio = painter.transform.width / painter.transform.height;
7508	let useVertical = false;
7509	for (let s = 0; s < placedSymbols.length; s++) {
7510		const symbol = placedSymbols.get(s);
7511		if (symbol.hidden || symbol.writingMode === 2 && !useVertical) {
7512			hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
7513			continue;
7514		}
7515		useVertical = false;
7516		const tileAnchorPoint = new Point(symbol.anchorX, symbol.anchorY);
7517		const projectionContext = {
7518			getElevation,
7519			pitchedLabelPlaneMatrix,
7520			lineVertexArray,
7521			pitchWithMap,
7522			projectionCache: {
7523				projections: {},
7524				offsets: {},
7525				cachedAnchorPoint: void 0,
7526				anyProjectionOccluded: false
7527			},
7528			transform: painter.transform,
7529			tileAnchorPoint,
7530			unwrappedTileID,
7531			width: viewportWidth,
7532			height: viewportHeight,
7533			translation
7534		};
7535		const anchorPos = projectTileCoordinatesToClipSpace(symbol.anchorX, symbol.anchorY, projectionContext);
7536		if (!isVisible(anchorPos.point, clippingBuffer)) {
7537			hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
7538			continue;
7539		}
7540		const cameraToAnchorDistance = anchorPos.signedDistanceFromCamera;
7541		const perspectiveRatio = getPerspectiveRatio(painter.transform.cameraToCenterDistance, cameraToAnchorDistance);
7542		const fontSize = evaluateSizeForFeature(sizeData, partiallyEvaluatedSize, symbol);
7543		const pitchScaledFontSize = pitchWithMap ? fontSize * painter.transform.getPitchedTextCorrection(symbol.anchorX, symbol.anchorY, unwrappedTileID) / perspectiveRatio : fontSize * perspectiveRatio;
7544		const placeUnflipped = placeGlyphsAlongLine({
7545			projectionContext,
7546			pitchedLabelPlaneMatrixInverse,
7547			symbol,
7548			fontSize: pitchScaledFontSize,
7549			flip: false,
7550			keepUpright,
7551			glyphOffsetArray: bucket.glyphOffsetArray,
7552			dynamicLayoutVertexArray,
7553			aspectRatio,
7554			rotateToLine
7555		});
7556		useVertical = placeUnflipped.useVertical;
7557		if (placeUnflipped.notEnoughRoom || useVertical || placeUnflipped.needsFlipping && placeGlyphsAlongLine({
7558			projectionContext,
7559			pitchedLabelPlaneMatrixInverse,
7560			symbol,
7561			fontSize: pitchScaledFontSize,
7562			flip: true,
7563			keepUpright,
7564			glyphOffsetArray: bucket.glyphOffsetArray,
7565			dynamicLayoutVertexArray,
7566			aspectRatio,
7567			rotateToLine
7568		}).notEnoughRoom) hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
7569	}
7570	if (isText) bucket.text.dynamicLayoutVertexBuffer.updateData(dynamicLayoutVertexArray);
7571	else bucket.icon.dynamicLayoutVertexBuffer.updateData(dynamicLayoutVertexArray);
7572}
7573function placeFirstAndLastGlyph(fontScale, glyphOffsetArray, lineOffsetX, lineOffsetY, flip, symbol, rotateToLine, projectionContext) {
7574	const glyphEndIndex = symbol.glyphStartIndex + symbol.numGlyphs;
7575	const lineStartIndex = symbol.lineStartIndex;
7576	const lineEndIndex = symbol.lineStartIndex + symbol.lineLength;
7577	const firstGlyphOffset = glyphOffsetArray.getoffsetX(symbol.glyphStartIndex);
7578	const lastGlyphOffset = glyphOffsetArray.getoffsetX(glyphEndIndex - 1);
7579	const firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, symbol.segment, lineStartIndex, lineEndIndex, projectionContext, rotateToLine);
7580	if (!firstPlacedGlyph) return null;
7581	const lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, symbol.segment, lineStartIndex, lineEndIndex, projectionContext, rotateToLine);
7582	if (!lastPlacedGlyph) return null;
7583	if (projectionContext.projectionCache.anyProjectionOccluded) return null;
7584	return {
7585		first: firstPlacedGlyph,
7586		last: lastPlacedGlyph
7587	};
7588}
7589function requiresOrientationChange(writingMode, firstPoint, lastPoint, aspectRatio) {
7590	if (writingMode === 1) {
7591		if (Math.abs(lastPoint.y - firstPoint.y) > Math.abs(lastPoint.x - firstPoint.x) * aspectRatio) return { useVertical: true };
7592	}
7593	if (writingMode === 2 ? firstPoint.y < lastPoint.y : firstPoint.x > lastPoint.x) return { needsFlipping: true };
7594	return null;
7595}
7596function placeGlyphsAlongLine(args) {
7597	const { projectionContext, pitchedLabelPlaneMatrixInverse, symbol, fontSize, flip, keepUpright, glyphOffsetArray, dynamicLayoutVertexArray, aspectRatio, rotateToLine } = args;
7598	const fontScale = fontSize / 24;
7599	const lineOffsetX = symbol.lineOffsetX * fontScale;
7600	const lineOffsetY = symbol.lineOffsetY * fontScale;
7601	let placedGlyphs;
7602	if (symbol.numGlyphs > 1) {
7603		const glyphEndIndex = symbol.glyphStartIndex + symbol.numGlyphs;
7604		const lineStartIndex = symbol.lineStartIndex;
7605		const lineEndIndex = symbol.lineStartIndex + symbol.lineLength;
7606		const firstAndLastGlyph = placeFirstAndLastGlyph(fontScale, glyphOffsetArray, lineOffsetX, lineOffsetY, flip, symbol, rotateToLine, projectionContext);
7607		if (!firstAndLastGlyph) return { notEnoughRoom: true };
7608		const firstPoint = projectFromLabelPlaneToClipSpace(firstAndLastGlyph.first.point.x, firstAndLastGlyph.first.point.y, projectionContext, pitchedLabelPlaneMatrixInverse);
7609		const lastPoint = projectFromLabelPlaneToClipSpace(firstAndLastGlyph.last.point.x, firstAndLastGlyph.last.point.y, projectionContext, pitchedLabelPlaneMatrixInverse);
7610		if (keepUpright && !flip) {
7611			const orientationChange = requiresOrientationChange(symbol.writingMode, firstPoint, lastPoint, aspectRatio);
7612			if (orientationChange) return orientationChange;
7613		}
7614		placedGlyphs = [firstAndLastGlyph.first];
7615		for (let glyphIndex = symbol.glyphStartIndex + 1; glyphIndex < glyphEndIndex - 1; glyphIndex++) {
7616			const placedGlyph = placeGlyphAlongLine(fontScale * glyphOffsetArray.getoffsetX(glyphIndex), lineOffsetX, lineOffsetY, flip, symbol.segment, lineStartIndex, lineEndIndex, projectionContext, rotateToLine);
7617			if (!placedGlyph) return { notEnoughRoom: true };
7618			placedGlyphs.push(placedGlyph);
7619		}
7620		placedGlyphs.push(firstAndLastGlyph.last);
7621	} else {
7622		if (keepUpright && !flip) {
7623			const a = projectTileCoordinatesToLabelPlane(projectionContext.tileAnchorPoint.x, projectionContext.tileAnchorPoint.y, projectionContext).point;
7624			const tileVertexIndex = symbol.lineStartIndex + symbol.segment + 1;
7625			const tileSegmentEnd = new Point(projectionContext.lineVertexArray.getx(tileVertexIndex), projectionContext.lineVertexArray.gety(tileVertexIndex));
7626			const projectedVertex = projectTileCoordinatesToLabelPlane(tileSegmentEnd.x, tileSegmentEnd.y, projectionContext);
7627			const b = projectedVertex.signedDistanceFromCamera > 0 ? projectedVertex.point : projectTruncatedLineSegmentToLabelPlane(projectionContext.tileAnchorPoint, tileSegmentEnd, a, 1, projectionContext);
7628			const clipSpaceA = projectFromLabelPlaneToClipSpace(a.x, a.y, projectionContext, pitchedLabelPlaneMatrixInverse);
7629			const clipSpaceB = projectFromLabelPlaneToClipSpace(b.x, b.y, projectionContext, pitchedLabelPlaneMatrixInverse);
7630			const orientationChange = requiresOrientationChange(symbol.writingMode, clipSpaceA, clipSpaceB, aspectRatio);
7631			if (orientationChange) return orientationChange;
7632		}
7633		const singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetArray.getoffsetX(symbol.glyphStartIndex), lineOffsetX, lineOffsetY, flip, symbol.segment, symbol.lineStartIndex, symbol.lineStartIndex + symbol.lineLength, projectionContext, rotateToLine);
7634		if (!singleGlyph || projectionContext.projectionCache.anyProjectionOccluded) return { notEnoughRoom: true };
7635		placedGlyphs = [singleGlyph];
7636	}
7637	for (const glyph of placedGlyphs) addDynamicAttributes(dynamicLayoutVertexArray, glyph.point, glyph.angle);
7638	return {};
7639}
7640/**
7641* Takes a line and direction from `previousTilePoint` to `currentTilePoint`, projects it to the correct label plane,
7642* and returns a projected point along this projected line that is `minimumLength` distance away from `previousProjectedPoint`.
7643* Projects a "virtual" vertex along a line segment.
7644* @param previousTilePoint - Line start point, in tile coordinates.
7645* @param currentTilePoint - Line end point, in tile coordinates.
7646* @param previousProjectedPoint - Projection of `previousTilePoint` into label plane
7647* @param minimumLength - Distance in the projected space along the line for the returned point.
7648* @param projectionContext - Projection context, used to get terrain's `getElevation`, and to project the points to screen pixels.
7649*/
7650function projectTruncatedLineSegmentToLabelPlane(previousTilePoint, currentTilePoint, previousProjectedPoint, minimumLength, projectionContext) {
7651	const unitVertexToBeProjected = previousTilePoint.add(previousTilePoint.sub(currentTilePoint)._unit());
7652	const projectedUnitVertex = projectTileCoordinatesToLabelPlane(unitVertexToBeProjected.x, unitVertexToBeProjected.y, projectionContext).point;
7653	const projectedUnitSegment = previousProjectedPoint.sub(projectedUnitVertex);
7654	return previousProjectedPoint.add(projectedUnitSegment._mult(minimumLength / projectedUnitSegment.mag()));
7655}
7656/**
7657* Transform a vertex from tile coordinates to label plane coordinates
7658* @param index - index of vertex to project
7659* @param projectionContext - necessary data to project a vertex
7660* @returns the vertex projected to the label plane
7661*/
7662function projectLineVertexToLabelPlane(index, projectionContext, syntheticVertexArgs) {
7663	const cache = projectionContext.projectionCache;
7664	if (cache.projections[index]) return cache.projections[index];
7665	const currentVertex = new Point(projectionContext.lineVertexArray.getx(index), projectionContext.lineVertexArray.gety(index));
7666	const projection = projectTileCoordinatesToLabelPlane(currentVertex.x, currentVertex.y, projectionContext);
7667	if (projection.signedDistanceFromCamera > 0) {
7668		cache.projections[index] = projection.point;
7669		cache.anyProjectionOccluded ||= projection.isOccluded;
7670		return projection.point;
7671	}
7672	const previousLineVertexIndex = index - syntheticVertexArgs.direction;
7673	const previousTilePoint = syntheticVertexArgs.distanceFromAnchor === 0 ? projectionContext.tileAnchorPoint : new Point(projectionContext.lineVertexArray.getx(previousLineVertexIndex), projectionContext.lineVertexArray.gety(previousLineVertexIndex));
7674	const minimumLength = syntheticVertexArgs.absOffsetX - syntheticVertexArgs.distanceFromAnchor + 1;
7675	return projectTruncatedLineSegmentToLabelPlane(previousTilePoint, currentVertex, syntheticVertexArgs.previousVertex, minimumLength, projectionContext);
7676}
7677/**
7678* Projects the given point in tile coordinates to the correct label plane.
7679* If pitchWithMap is true, the (rotated) map plane in pixels is used,
7680* otherwise screen pixels are used.
7681*/
7682function projectTileCoordinatesToLabelPlane(x, y, projectionContext) {
7683	const translatedX = x + projectionContext.translation[0];
7684	const translatedY = y + projectionContext.translation[1];
7685	let projection;
7686	if (projectionContext.pitchWithMap) {
7687		projection = projectWithMatrix(translatedX, translatedY, projectionContext.pitchedLabelPlaneMatrix, elevationAt(projectionContext, translatedX, translatedY));
7688		projection.isOccluded = false;
7689	} else {
7690		projection = projectionContext.transform.projectTileCoordinates(translatedX, translatedY, projectionContext.unwrappedTileID, elevationAt(projectionContext, translatedX, translatedY));
7691		projection.point.x = (projection.point.x * .5 + .5) * projectionContext.width;
7692		projection.point.y = (-projection.point.y * .5 + .5) * projectionContext.height;
7693	}
7694	return projection;
7695}
7696function projectFromLabelPlaneToClipSpace(x, y, projectionContext, pitchedLabelPlaneMatrixInverse) {
7697	if (projectionContext.pitchWithMap) {
7698		const pos = [
7699			x,
7700			y,
7701			0,
7702			1
7703		];
7704		transformMat4(pos, pos, pitchedLabelPlaneMatrixInverse);
7705		const tileX = pos[0] / pos[3];
7706		const tileY = pos[1] / pos[3];
7707		return projectionContext.transform.projectTileCoordinates(tileX, tileY, projectionContext.unwrappedTileID, elevationAt(projectionContext, tileX, tileY)).point;
7708	} else return {
7709		x: x / projectionContext.width * 2 - 1,
7710		y: 1 - y / projectionContext.height * 2
7711	};
7712}
7713/**
7714* Projects the given point in tile coordinates to the GL clip space (-1..1).
7715*/
7716function projectTileCoordinatesToClipSpace(x, y, projectionContext) {
7717	return projectionContext.transform.projectTileCoordinates(x, y, projectionContext.unwrappedTileID, elevationAt(projectionContext, x, y));
7718}
7719/**
7720* Calculate the normal vector for a line segment
7721* @param segmentVector - will be mutated as a tiny optimization
7722* @param offset - magnitude of resulting vector
7723* @param direction - direction of line traversal
7724* @returns a normal vector from the segment, with magnitude equal to offset amount
7725*/
7726function transformToOffsetNormal(segmentVector, offset, direction) {
7727	return segmentVector._unit()._perp()._mult(offset * direction);
7728}
7729/**
7730* Construct offset line segments for the current segment and the next segment, then extend/shrink
7731* the segments until they intersect. If the segments are parallel, then they will touch with no modification.
7732*
7733* @param index - Index of the current vertex
7734* @param prevToCurrentOffsetNormal - Normal vector of the line segment from the previous vertex to the current vertex
7735* @param currentVertex - Current (non-offset) vertex projected to the label plane
7736* @param lineStartIndex - Beginning index for the line this label is on
7737* @param lineEndIndex - End index for the line this label is on
7738* @param offsetPreviousVertex - The previous vertex projected to the label plane, and then offset along the previous segments normal
7739* @param lineOffsetY - Magnitude of the offset
7740* @param projectionContext - Necessary data for tile-to-label-plane projection
7741* @returns The point at which the current and next line segments intersect, once offset and extended/shrunk to their meeting point
7742*/
7743function findOffsetIntersectionPoint(index, prevToCurrentOffsetNormal, currentVertex, lineStartIndex, lineEndIndex, offsetPreviousVertex, lineOffsetY, projectionContext, syntheticVertexArgs) {
7744	if (projectionContext.projectionCache.offsets[index]) return projectionContext.projectionCache.offsets[index];
7745	const offsetCurrentVertex = currentVertex.add(prevToCurrentOffsetNormal);
7746	if (index + syntheticVertexArgs.direction < lineStartIndex || index + syntheticVertexArgs.direction >= lineEndIndex) {
7747		projectionContext.projectionCache.offsets[index] = offsetCurrentVertex;
7748		return offsetCurrentVertex;
7749	}
7750	const nextVertex = projectLineVertexToLabelPlane(index + syntheticVertexArgs.direction, projectionContext, syntheticVertexArgs);
7751	const currentToNextOffsetNormal = transformToOffsetNormal(nextVertex.sub(currentVertex), lineOffsetY, syntheticVertexArgs.direction);
7752	const offsetNextSegmentBegin = currentVertex.add(currentToNextOffsetNormal);
7753	const offsetNextSegmentEnd = nextVertex.add(currentToNextOffsetNormal);
7754	projectionContext.projectionCache.offsets[index] = findLineIntersection(offsetPreviousVertex, offsetCurrentVertex, offsetNextSegmentBegin, offsetNextSegmentEnd) || offsetCurrentVertex;
7755	return projectionContext.projectionCache.offsets[index];
7756}
7757function placeGlyphAlongLine(offsetX, lineOffsetX, lineOffsetY, flip, anchorSegment, lineStartIndex, lineEndIndex, projectionContext, rotateToLine) {
7758	const combinedOffsetX = flip ? offsetX - lineOffsetX : offsetX + lineOffsetX;
7759	let direction = combinedOffsetX > 0 ? 1 : -1;
7760	let angle = 0;
7761	if (flip) {
7762		direction *= -1;
7763		angle = Math.PI;
7764	}
7765	if (direction < 0) angle += Math.PI;
7766	let currentIndex = direction > 0 ? lineStartIndex + anchorSegment : lineStartIndex + anchorSegment + 1;
7767	let anchorPoint;
7768	if (projectionContext.projectionCache.cachedAnchorPoint) anchorPoint = projectionContext.projectionCache.cachedAnchorPoint;
7769	else {
7770		anchorPoint = projectTileCoordinatesToLabelPlane(projectionContext.tileAnchorPoint.x, projectionContext.tileAnchorPoint.y, projectionContext).point;
7771		projectionContext.projectionCache.cachedAnchorPoint = anchorPoint;
7772	}
7773	let currentVertex = anchorPoint;
7774	let previousVertex = anchorPoint;
7775	let offsetIntersectionPoint;
7776	let offsetPreviousVertex;
7777	let distanceFromAnchor = 0;
7778	let currentSegmentDistance = 0;
7779	const absOffsetX = Math.abs(combinedOffsetX);
7780	const pathVertices = [];
7781	let currentLineSegment;
7782	while (distanceFromAnchor + currentSegmentDistance <= absOffsetX) {
7783		currentIndex += direction;
7784		if (currentIndex < lineStartIndex || currentIndex >= lineEndIndex) return null;
7785		distanceFromAnchor += currentSegmentDistance;
7786		previousVertex = currentVertex;
7787		offsetPreviousVertex = offsetIntersectionPoint;
7788		const syntheticVertexArgs = {
7789			absOffsetX,
7790			direction,
7791			distanceFromAnchor,
7792			previousVertex
7793		};
7794		currentVertex = projectLineVertexToLabelPlane(currentIndex, projectionContext, syntheticVertexArgs);
7795		if (lineOffsetY === 0) {
7796			pathVertices.push(previousVertex);
7797			currentLineSegment = currentVertex.sub(previousVertex);
7798		} else {
7799			let prevToCurrentOffsetNormal;
7800			const prevToCurrent = currentVertex.sub(previousVertex);
7801			if (prevToCurrent.mag() === 0) prevToCurrentOffsetNormal = transformToOffsetNormal(projectLineVertexToLabelPlane(currentIndex + direction, projectionContext, syntheticVertexArgs).sub(currentVertex), lineOffsetY, direction);
7802			else prevToCurrentOffsetNormal = transformToOffsetNormal(prevToCurrent, lineOffsetY, direction);
7803			offsetPreviousVertex ||= previousVertex.add(prevToCurrentOffsetNormal);
7804			offsetIntersectionPoint = findOffsetIntersectionPoint(currentIndex, prevToCurrentOffsetNormal, currentVertex, lineStartIndex, lineEndIndex, offsetPreviousVertex, lineOffsetY, projectionContext, syntheticVertexArgs);
7805			pathVertices.push(offsetPreviousVertex);
7806			currentLineSegment = offsetIntersectionPoint.sub(offsetPreviousVertex);
7807		}
7808		currentSegmentDistance = currentLineSegment.mag();
7809	}
7810	const segmentInterpolationT = (absOffsetX - distanceFromAnchor) / currentSegmentDistance;
7811	const p = currentLineSegment._mult(segmentInterpolationT)._add(offsetPreviousVertex || previousVertex);
7812	const segmentAngle = angle + Math.atan2(currentVertex.y - previousVertex.y, currentVertex.x - previousVertex.x);
7813	pathVertices.push(p);
7814	return {
7815		point: p,
7816		angle: rotateToLine ? segmentAngle : 0,
7817		path: pathVertices
7818	};
7819}
7820const hiddenGlyphAttributes = new Float32Array([
7821	-Infinity,
7822	-Infinity,
7823	0,
7824	-Infinity,
7825	-Infinity,
7826	0,
7827	-Infinity,
7828	-Infinity,
7829	0,
7830	-Infinity,
7831	-Infinity,
7832	0
7833]);
7834function hideGlyphs(num, dynamicLayoutVertexArray) {
7835	for (let i = 0; i < num; i++) {
7836		const offset = dynamicLayoutVertexArray.length;
7837		dynamicLayoutVertexArray.resize(offset + 4);
7838		dynamicLayoutVertexArray.float32.set(hiddenGlyphAttributes, offset * 3);
7839	}
7840}
7841function xyTransformMat4(out, a, m) {
7842	const x = a[0], y = a[1];
7843	out[0] = m[0] * x + m[4] * y + m[12];
7844	out[1] = m[1] * x + m[5] * y + m[13];
7845	out[3] = m[3] * x + m[7] * y + m[15];
7846	return out;
7847}
7848/**
7849* Takes a path of points that was previously projected using the `pitchedLabelPlaneMatrix`
7850* and projects it using the map projection's (mercator/globe...) `projectTileCoordinates` function.
7851* Returns a new array of the projected points.
7852* Does not modify the input array.
7853*/
7854function projectPathSpecialProjection(projectedPath, projectionContext) {
7855	const inverseLabelPlaneMatrix = tmpMat4;
7856	fastInvertSkewMat4(inverseLabelPlaneMatrix, projectionContext.pitchedLabelPlaneMatrix);
7857	return projectedPath.map((p) => {
7858		const backProjected = projectWithMatrix(p.x, p.y, inverseLabelPlaneMatrix, elevationAt(projectionContext, p.x, p.y));
7859		const projected = projectionContext.transform.projectTileCoordinates(backProjected.point.x, backProjected.point.y, projectionContext.unwrappedTileID, elevationAt(projectionContext, backProjected.point.x, backProjected.point.y));
7860		projected.point.x = (projected.point.x * .5 + .5) * projectionContext.width;
7861		projected.point.y = (-projected.point.y * .5 + .5) * projectionContext.height;
7862		return projected;
7863	});
7864}
7865/**
7866* Takes a path of points projected to screenspace, finds the longest continuous unoccluded segment of that path
7867* and returns it.
7868* Does not modify the input array.
7869*/
7870function pathSlicedToLongestUnoccluded(path) {
7871	let longestUnoccludedStart = 0;
7872	let longestUnoccludedLength = 0;
7873	let currentUnoccludedStart = 0;
7874	let currentUnoccludedLength = 0;
7875	for (let i = 0; i < path.length; i++) if (path[i].isOccluded) {
7876		currentUnoccludedStart = i + 1;
7877		currentUnoccludedLength = 0;
7878	} else {
7879		currentUnoccludedLength++;
7880		if (currentUnoccludedLength > longestUnoccludedLength) {
7881			longestUnoccludedLength = currentUnoccludedLength;
7882			longestUnoccludedStart = currentUnoccludedStart;
7883		}
7884	}
7885	return path.slice(longestUnoccludedStart, longestUnoccludedStart + longestUnoccludedLength);
7886}
7887/**
7888* @internal
7889* A collision index used to prevent symbols from overlapping. It keep tracks of
7890* where previous symbols have been placed and is used to check if a new
7891* symbol overlaps with any previously added symbols.
7892*
7893* There are two steps to insertion: first placeCollisionBox/Circles checks if
7894* there's room for a symbol, then insertCollisionBox/Circles actually puts the
7895* symbol in the index. The two step process allows paired symbols to be inserted
7896* together even if they overlap.
7897*/
7898var CollisionIndex = class {
7899	constructor(transform, grid = new GridIndex(transform.width + 200, transform.height + 200, 25), ignoredGrid = new GridIndex(transform.width + 200, transform.height + 200, 25)) {
7900		this.transform = transform;
7901		this.grid = grid;
7902		this.ignoredGrid = ignoredGrid;
7903		this.pitchFactor = Math.cos(transform.pitch * Math.PI / 180) * transform.cameraToCenterDistance;
7904		this.screenRightBoundary = transform.width + 100;
7905		this.screenBottomBoundary = transform.height + 100;
7906		this.gridRightBoundary = transform.width + 200;
7907		this.gridBottomBoundary = transform.height + 200;
7908		this.perspectiveRatioCutoff = .6;
7909	}
7910	placeCollisionBox(collisionBox, overlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, collisionGroupPredicate, getElevation, shift, simpleProjectionMatrix, heightOffset = 0, heightAnchorGround = true) {
7911		const x = collisionBox.anchorPointX + translation[0];
7912		const y = collisionBox.anchorPointY + translation[1];
7913		const projectedPoint = this.projectAndGetPerspectiveRatio(x, y, unwrappedTileID, getSymbolElevation(getElevation, x, y, heightOffset, heightAnchorGround), simpleProjectionMatrix);
7914		const tileToViewport = textPixelRatio * projectedPoint.perspectiveRatio;
7915		let projectedBox;
7916		if (!pitchWithMap && !rotateWithMap) {
7917			const pointX = projectedPoint.x + (shift ? shift.x * tileToViewport : 0);
7918			const pointY = projectedPoint.y + (shift ? shift.y * tileToViewport : 0);
7919			projectedBox = {
7920				allPointsOccluded: false,
7921				box: [
7922					pointX + collisionBox.x1 * tileToViewport,
7923					pointY + collisionBox.y1 * tileToViewport,
7924					pointX + collisionBox.x2 * tileToViewport,
7925					pointY + collisionBox.y2 * tileToViewport
7926				]
7927			};
7928		} else projectedBox = this._projectCollisionBox(collisionBox, tileToViewport, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, projectedPoint, getElevation, shift, simpleProjectionMatrix, heightOffset, heightAnchorGround);
7929		const [tlX, tlY, brX, brY] = projectedBox.box;
7930		const occluded = pitchWithMap ? projectedBox.allPointsOccluded : projectedPoint.isOccluded;
7931		let unplaceable = occluded;
7932		unplaceable ||= projectedPoint.perspectiveRatio < this.perspectiveRatioCutoff;
7933		unplaceable ||= !this.isInsideGrid(tlX, tlY, brX, brY);
7934		if (unplaceable || overlapMode !== "always" && this.grid.hitTest(tlX, tlY, brX, brY, overlapMode, collisionGroupPredicate)) return {
7935			box: [
7936				tlX,
7937				tlY,
7938				brX,
7939				brY
7940			],
7941			placeable: false,
7942			offscreen: false,
7943			occluded
7944		};
7945		return {
7946			box: [
7947				tlX,
7948				tlY,
7949				brX,
7950				brY
7951			],
7952			placeable: true,
7953			offscreen: this.isOffscreen(tlX, tlY, brX, brY),
7954			occluded
7955		};
7956	}
7957	placeCollisionCircles(overlapMode, symbol, lineVertexArray, glyphOffsetArray, fontSize, unwrappedTileID, pitchedLabelPlaneMatrix, showCollisionCircles, pitchWithMap, collisionGroupPredicate, circlePixelDiameter, textPixelPadding, translation, getElevation) {
7958		const placedCollisionCircles = [];
7959		const tileUnitAnchorPoint = new Point(symbol.anchorX, symbol.anchorY);
7960		const perspectiveRatio = this.getPerspectiveRatio(tileUnitAnchorPoint.x, tileUnitAnchorPoint.y, unwrappedTileID, getElevation?.(tileUnitAnchorPoint.x, tileUnitAnchorPoint.y));
7961		const labelPlaneFontScale = (pitchWithMap ? fontSize * this.transform.getPitchedTextCorrection(symbol.anchorX, symbol.anchorY, unwrappedTileID) / perspectiveRatio : fontSize * perspectiveRatio) / 24;
7962		const projectionCache = {
7963			projections: {},
7964			offsets: {},
7965			cachedAnchorPoint: void 0,
7966			anyProjectionOccluded: false
7967		};
7968		const lineOffsetX = symbol.lineOffsetX * labelPlaneFontScale;
7969		const lineOffsetY = symbol.lineOffsetY * labelPlaneFontScale;
7970		const projectionContext = {
7971			getElevation,
7972			pitchedLabelPlaneMatrix,
7973			lineVertexArray,
7974			pitchWithMap,
7975			projectionCache,
7976			transform: this.transform,
7977			tileAnchorPoint: tileUnitAnchorPoint,
7978			unwrappedTileID,
7979			width: this.transform.width,
7980			height: this.transform.height,
7981			translation
7982		};
7983		const firstAndLastGlyph = placeFirstAndLastGlyph(labelPlaneFontScale, glyphOffsetArray, lineOffsetX, lineOffsetY, false, symbol, false, projectionContext);
7984		let collisionDetected = false;
7985		let inGrid = false;
7986		let entirelyOffscreen = true;
7987		if (firstAndLastGlyph) {
7988			const radius = circlePixelDiameter * .5 * perspectiveRatio + textPixelPadding;
7989			const screenPlaneMin = new Point(-100, -100);
7990			const screenPlaneMax = new Point(this.screenRightBoundary, this.screenBottomBoundary);
7991			const interpolator = new PathInterpolator();
7992			const first = firstAndLastGlyph.first;
7993			const last = firstAndLastGlyph.last;
7994			let projectedPath = [];
7995			for (let i = first.path.length - 1; i >= 1; i--) projectedPath.push(first.path[i]);
7996			for (let i = 1; i < last.path.length; i++) projectedPath.push(last.path[i]);
7997			const circleDist = radius * 2.5;
7998			if (pitchWithMap) {
7999				const screenSpacePath = this.projectPathToScreenSpace(projectedPath, projectionContext);
8000				if (screenSpacePath.some((point) => point.signedDistanceFromCamera <= 0)) projectedPath = [];
8001				else projectedPath = screenSpacePath.map((p) => p.point);
8002			}
8003			let segments = [];
8004			if (projectedPath.length > 0) {
8005				const minPoint = projectedPath[0].clone();
8006				const maxPoint = projectedPath[0].clone();
8007				for (let i = 1; i < projectedPath.length; i++) {
8008					minPoint.x = Math.min(minPoint.x, projectedPath[i].x);
8009					minPoint.y = Math.min(minPoint.y, projectedPath[i].y);
8010					maxPoint.x = Math.max(maxPoint.x, projectedPath[i].x);
8011					maxPoint.y = Math.max(maxPoint.y, projectedPath[i].y);
8012				}
8013				if (minPoint.x >= screenPlaneMin.x && maxPoint.x <= screenPlaneMax.x && minPoint.y >= screenPlaneMin.y && maxPoint.y <= screenPlaneMax.y) segments = [projectedPath];
8014				else if (maxPoint.x < screenPlaneMin.x || minPoint.x > screenPlaneMax.x || maxPoint.y < screenPlaneMin.y || minPoint.y > screenPlaneMax.y) segments = [];
8015				else segments = clipLine([projectedPath], screenPlaneMin.x, screenPlaneMin.y, screenPlaneMax.x, screenPlaneMax.y);
8016			}
8017			for (const seg of segments) {
8018				interpolator.reset(seg, radius * .25);
8019				let numCircles = 0;
8020				if (interpolator.length <= .5 * radius) numCircles = 1;
8021				else numCircles = Math.ceil(interpolator.paddedLength / circleDist) + 1;
8022				for (let i = 0; i < numCircles; i++) {
8023					const t = i / Math.max(numCircles - 1, 1);
8024					const circlePosition = interpolator.lerp(t);
8025					const centerX = circlePosition.x + 100;
8026					const centerY = circlePosition.y + 100;
8027					placedCollisionCircles.push(centerX, centerY, radius, 0);
8028					const x1 = centerX - radius;
8029					const y1 = centerY - radius;
8030					const x2 = centerX + radius;
8031					const y2 = centerY + radius;
8032					entirelyOffscreen &&= this.isOffscreen(x1, y1, x2, y2);
8033					inGrid ||= this.isInsideGrid(x1, y1, x2, y2);
8034					if (overlapMode !== "always" && this.grid.hitTestCircle(centerX, centerY, radius, overlapMode, collisionGroupPredicate)) {
8035						collisionDetected = true;
8036						if (!showCollisionCircles) return {
8037							circles: [],
8038							offscreen: false,
8039							collisionDetected
8040						};
8041					}
8042				}
8043			}
8044		}
8045		return {
8046			circles: !showCollisionCircles && collisionDetected || !inGrid || perspectiveRatio < this.perspectiveRatioCutoff ? [] : placedCollisionCircles,
8047			offscreen: entirelyOffscreen,
8048			collisionDetected
8049		};
8050	}
8051	projectPathToScreenSpace(projectedPath, projectionContext) {
8052		return pathSlicedToLongestUnoccluded(projectPathSpecialProjection(projectedPath, projectionContext));
8053	}
8054	/**
8055	* Because the geometries in the CollisionIndex are an approximation of the shape of
8056	* symbols on the map, we use the CollisionIndex to look up the symbol part of
8057	* `queryRenderedFeatures`.
8058	*/
8059	queryRenderedSymbols(viewportQueryGeometry) {
8060		if (viewportQueryGeometry.length === 0 || this.grid.keysLength() === 0 && this.ignoredGrid.keysLength() === 0) return {};
8061		const query = [];
8062		const bounds = new Bounds();
8063		for (const point of viewportQueryGeometry) {
8064			const gridPoint = new Point(point.x + 100, point.y + 100);
8065			bounds.extend(gridPoint);
8066			query.push(gridPoint);
8067		}
8068		const { minX, minY, maxX, maxY } = bounds;
8069		const features = this.grid.query(minX, minY, maxX, maxY).concat(this.ignoredGrid.query(minX, minY, maxX, maxY));
8070		const seenFeatures = {};
8071		const result = {};
8072		for (const feature of features) {
8073			const featureKey = feature.key;
8074			if (seenFeatures[featureKey.bucketInstanceId] === void 0) seenFeatures[featureKey.bucketInstanceId] = {};
8075			if (seenFeatures[featureKey.bucketInstanceId][featureKey.featureIndex]) continue;
8076			const bbox = [
8077				new Point(feature.x1, feature.y1),
8078				new Point(feature.x2, feature.y1),
8079				new Point(feature.x2, feature.y2),
8080				new Point(feature.x1, feature.y2)
8081			];
8082			if (!polygonIntersectsPolygon(query, bbox)) continue;
8083			seenFeatures[featureKey.bucketInstanceId][featureKey.featureIndex] = true;
8084			if (result[featureKey.bucketInstanceId] === void 0) result[featureKey.bucketInstanceId] = [];
8085			result[featureKey.bucketInstanceId].push(featureKey.featureIndex);
8086		}
8087		return result;
8088	}
8089	insertCollisionBox(collisionBox, overlapMode, ignorePlacement, bucketInstanceId, featureIndex, collisionGroupID) {
8090		const grid = ignorePlacement ? this.ignoredGrid : this.grid;
8091		const key = {
8092			bucketInstanceId,
8093			featureIndex,
8094			collisionGroupID,
8095			overlapMode
8096		};
8097		grid.insert(key, collisionBox[0], collisionBox[1], collisionBox[2], collisionBox[3]);
8098	}
8099	insertCollisionCircles(collisionCircles, overlapMode, ignorePlacement, bucketInstanceId, featureIndex, collisionGroupID) {
8100		const grid = ignorePlacement ? this.ignoredGrid : this.grid;
8101		const key = {
8102			bucketInstanceId,
8103			featureIndex,
8104			collisionGroupID,
8105			overlapMode
8106		};
8107		for (let k = 0; k < collisionCircles.length; k += 4) grid.insertCircle(key, collisionCircles[k], collisionCircles[k + 1], collisionCircles[k + 2]);
8108	}
8109	projectAndGetPerspectiveRatio(x, y, unwrappedTileID, elevation, simpleProjectionMatrix) {
8110		if (simpleProjectionMatrix) {
8111			let pos;
8112			if (elevation != null) {
8113				pos = [
8114					x,
8115					y,
8116					elevation,
8117					1
8118				];
8119				transformMat4(pos, pos, simpleProjectionMatrix);
8120			} else {
8121				pos = [
8122					x,
8123					y,
8124					0,
8125					1
8126				];
8127				xyTransformMat4(pos, pos, simpleProjectionMatrix);
8128			}
8129			const w = pos[3];
8130			return {
8131				x: (pos[0] / w + 1) / 2 * this.transform.width + 100,
8132				y: (-pos[1] / w + 1) / 2 * this.transform.height + 100,
8133				perspectiveRatio: .5 + .5 * (this.transform.cameraToCenterDistance / w),
8134				isOccluded: false,
8135				signedDistanceFromCamera: w
8136			};
8137		} else {
8138			const projected = this.transform.projectTileCoordinates(x, y, unwrappedTileID, elevation);
8139			return {
8140				x: (projected.point.x + 1) / 2 * this.transform.width + 100,
8141				y: (-projected.point.y + 1) / 2 * this.transform.height + 100,
8142				perspectiveRatio: .5 + .5 * (this.transform.cameraToCenterDistance / projected.signedDistanceFromCamera),
8143				isOccluded: projected.isOccluded,
8144				signedDistanceFromCamera: projected.signedDistanceFromCamera
8145			};
8146		}
8147	}
8148	getPerspectiveRatio(x, y, unwrappedTileID, elevation) {
8149		const projected = this.transform.projectTileCoordinates(x, y, unwrappedTileID, elevation);
8150		return .5 + .5 * (this.transform.cameraToCenterDistance / projected.signedDistanceFromCamera);
8151	}
8152	isOffscreen(x1, y1, x2, y2) {
8153		return x2 < 100 || x1 >= this.screenRightBoundary || y2 < 100 || y1 > this.screenBottomBoundary;
8154	}
8155	isInsideGrid(x1, y1, x2, y2) {
8156		return x2 >= 0 && x1 < this.gridRightBoundary && y2 >= 0 && y1 < this.gridBottomBoundary;
8157	}
8158	getViewportMatrix() {
8159		const m = identity([]);
8160		translate(m, m, [
8161			-100,
8162			-100,
8163			0
8164		]);
8165		return m;
8166	}
8167	/**
8168	* Applies all layout+paint properties of the given box in order to find as good approximation of its screen-space bounding box as possible.
8169	*/
8170	_projectCollisionBox(collisionBox, tileToViewport, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, projectedPoint, getElevation, shift, simpleProjectionMatrix, heightOffset = 0, heightAnchorGround = true) {
8171		let vecEastX = 1;
8172		let vecEastY = 0;
8173		let vecSouthX = 0;
8174		let vecSouthY = 1;
8175		const translatedAnchorX = collisionBox.anchorPointX + translation[0];
8176		const translatedAnchorY = collisionBox.anchorPointY + translation[1];
8177		if (rotateWithMap && !pitchWithMap) {
8178			const projectedEast = this.projectAndGetPerspectiveRatio(translatedAnchorX + 1, translatedAnchorY, unwrappedTileID, getSymbolElevation(getElevation, translatedAnchorX + 1, translatedAnchorY, heightOffset, heightAnchorGround), simpleProjectionMatrix);
8179			const toEastX = projectedEast.x - projectedPoint.x;
8180			const toEastY = projectedEast.y - projectedPoint.y;
8181			const angle = Math.atan(toEastY / toEastX) + (toEastX < 0 ? Math.PI : 0);
8182			const sin = Math.sin(angle);
8183			const cos = Math.cos(angle);
8184			vecEastX = cos;
8185			vecEastY = sin;
8186			vecSouthX = -sin;
8187			vecSouthY = cos;
8188		} else if (!rotateWithMap && pitchWithMap) {
8189			const skew = getTileSkewVectors(this.transform);
8190			vecEastX = skew.vecEast[0];
8191			vecEastY = skew.vecEast[1];
8192			vecSouthX = skew.vecSouth[0];
8193			vecSouthY = skew.vecSouth[1];
8194		}
8195		let basePointX = projectedPoint.x;
8196		let basePointY = projectedPoint.y;
8197		let distanceMultiplier = tileToViewport;
8198		if (pitchWithMap) {
8199			basePointX = translatedAnchorX;
8200			basePointY = translatedAnchorY;
8201			const zoomFraction = this.transform.zoom - tileID.overscaledZ;
8202			distanceMultiplier = Math.pow(2, -zoomFraction);
8203			distanceMultiplier *= this.transform.getPitchedTextCorrection(translatedAnchorX, translatedAnchorY, unwrappedTileID);
8204			if (!shift) {
8205				const distanceRatio = projectedPoint.signedDistanceFromCamera / this.transform.cameraToCenterDistance;
8206				const perspectiveRatio = clamp(.5 + .5 * distanceRatio, 0, 4);
8207				distanceMultiplier *= perspectiveRatio;
8208			}
8209		}
8210		if (shift) {
8211			basePointX += vecEastX * shift.x * distanceMultiplier + vecSouthX * shift.y * distanceMultiplier;
8212			basePointY += vecEastY * shift.x * distanceMultiplier + vecSouthY * shift.y * distanceMultiplier;
8213		}
8214		const offsetXmin = collisionBox.x1 * distanceMultiplier;
8215		const offsetXmax = collisionBox.x2 * distanceMultiplier;
8216		const offsetXhalf = (offsetXmin + offsetXmax) / 2;
8217		const offsetYmin = collisionBox.y1 * distanceMultiplier;
8218		const offsetYmax = collisionBox.y2 * distanceMultiplier;
8219		const offsetYhalf = (offsetYmin + offsetYmax) / 2;
8220		const offsetsArray = [
8221			{
8222				offsetX: offsetXmin,
8223				offsetY: offsetYmin
8224			},
8225			{
8226				offsetX: offsetXhalf,
8227				offsetY: offsetYmin
8228			},
8229			{
8230				offsetX: offsetXmax,
8231				offsetY: offsetYmin
8232			},
8233			{
8234				offsetX: offsetXmax,
8235				offsetY: offsetYhalf
8236			},
8237			{
8238				offsetX: offsetXmax,
8239				offsetY: offsetYmax
8240			},
8241			{
8242				offsetX: offsetXhalf,
8243				offsetY: offsetYmax
8244			},
8245			{
8246				offsetX: offsetXmin,
8247				offsetY: offsetYmax
8248			},
8249			{
8250				offsetX: offsetXmin,
8251				offsetY: offsetYhalf
8252			}
8253		];
8254		let points = [];
8255		for (const { offsetX, offsetY } of offsetsArray) points.push(new Point(basePointX + vecEastX * offsetX + vecSouthX * offsetY, basePointY + vecEastY * offsetX + vecSouthY * offsetY));
8256		let anyPointVisible = false;
8257		if (pitchWithMap) {
8258			const projected = points.map((p) => this.projectAndGetPerspectiveRatio(p.x, p.y, unwrappedTileID, getSymbolElevation(getElevation, p.x, p.y, heightOffset, heightAnchorGround), simpleProjectionMatrix));
8259			anyPointVisible = projected.some((p) => !p.isOccluded);
8260			points = projected.map((p) => new Point(p.x, p.y));
8261		} else anyPointVisible = true;
8262		return {
8263			box: getAABB(points),
8264			allPointsOccluded: !anyPointVisible
8265		};
8266	}
8267};
8268//#endregion
8269//#region src/symbol/placement.ts
8270var OpacityState = class {
8271	constructor(prevState, increment, placed, skipFade) {
8272		if (prevState) this.opacity = Math.max(0, Math.min(1, prevState.opacity + (prevState.placed ? increment : -increment)));
8273		else this.opacity = skipFade && placed ? 1 : 0;
8274		this.placed = placed;
8275	}
8276	isHidden() {
8277		return this.opacity === 0 && !this.placed;
8278	}
8279};
8280var JointOpacityState = class {
8281	constructor(prevState, increment, placedText, placedIcon, skipFade) {
8282		this.text = new OpacityState(prevState ? prevState.text : null, increment, placedText, skipFade);
8283		this.icon = new OpacityState(prevState ? prevState.icon : null, increment, placedIcon, skipFade);
8284	}
8285	isHidden() {
8286		return this.text.isHidden() && this.icon.isHidden();
8287	}
8288};
8289var JointPlacement = class {
8290	constructor(text, icon, skipFade) {
8291		this.text = text;
8292		this.icon = icon;
8293		this.skipFade = skipFade;
8294	}
8295};
8296var RetainedQueryData = class {
8297	constructor(bucketInstanceId, featureIndex, sourceLayerIndex, bucketIndex, tileID) {
8298		this.bucketInstanceId = bucketInstanceId;
8299		this.featureIndex = featureIndex;
8300		this.sourceLayerIndex = sourceLayerIndex;
8301		this.bucketIndex = bucketIndex;
8302		this.tileID = tileID;
8303	}
8304};
8305var CollisionGroups = class {
8306	constructor(crossSourceCollisions) {
8307		this.crossSourceCollisions = crossSourceCollisions;
8308		this.maxGroupID = 0;
8309		this.collisionGroups = {};
8310	}
8311	get(sourceID) {
8312		if (!this.crossSourceCollisions) {
8313			if (!this.collisionGroups[sourceID]) {
8314				const nextGroupID = ++this.maxGroupID;
8315				this.collisionGroups[sourceID] = {
8316					ID: nextGroupID,
8317					predicate: (key) => {
8318						return key.collisionGroupID === nextGroupID;
8319					}
8320				};
8321			}
8322			return this.collisionGroups[sourceID];
8323		} else return {
8324			ID: 0,
8325			predicate: null
8326		};
8327	}
8328};
8329function calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale) {
8330	const { horizontalAlign, verticalAlign } = getAnchorAlignment(anchor);
8331	const shiftX = -(horizontalAlign - .5) * width;
8332	const shiftY = -(verticalAlign - .5) * height;
8333	return new Point(shiftX + textOffset[0] * textBoxScale, shiftY + textOffset[1] * textBoxScale);
8334}
8335var Placement = class {
8336	constructor(transform, terrain, fadeDuration, crossSourceCollisions, prevPlacement) {
8337		this.transform = transform.clone();
8338		this.terrain = terrain;
8339		this.collisionIndex = new CollisionIndex(this.transform);
8340		this.placements = {};
8341		this.opacities = {};
8342		this.variableOffsets = {};
8343		this.stale = false;
8344		this.commitTime = 0;
8345		this.fadeDuration = fadeDuration;
8346		this.retainedQueryData = {};
8347		this.collisionGroups = new CollisionGroups(crossSourceCollisions);
8348		this.collisionCircleArrays = {};
8349		this.collisionBoxArrays = /* @__PURE__ */ new Map();
8350		this.prevPlacement = prevPlacement;
8351		if (prevPlacement) prevPlacement.prevPlacement = void 0;
8352		this.placedOrientations = {};
8353	}
8354	_getTerrainElevationFunc(tileID) {
8355		const terrain = this.terrain;
8356		if (!terrain) return void 0;
8357		return (x, y) => terrain.getElevation(tileID, x, y);
8358	}
8359	getBucketParts(results, styleLayer, tile, sortAcrossTiles) {
8360		const symbolBucket = tile.getBucket(styleLayer);
8361		const bucketFeatureIndex = tile.latestFeatureIndex;
8362		if (!symbolBucket || !bucketFeatureIndex || styleLayer.id !== symbolBucket.layerIds[0]) return;
8363		const collisionBoxArray = tile.collisionBoxArray;
8364		const layout = symbolBucket.layers[0].layout;
8365		const paint = symbolBucket.layers[0].paint;
8366		const scale = Math.pow(2, this.transform.zoom - tile.tileID.overscaledZ);
8367		const textPixelRatio = tile.tileSize / EXTENT;
8368		const unwrappedTileID = tile.tileID.toUnwrapped();
8369		const rotateWithMap = layout.get("text-rotation-alignment") === "map";
8370		const pixelsToTiles = pixelsToTileUnits(tile, 1, this.transform.zoom);
8371		const translationText = translatePosition(this.collisionIndex.transform, tile, paint.get("text-translate"), paint.get("text-translate-anchor"));
8372		const translationIcon = translatePosition(this.collisionIndex.transform, tile, paint.get("icon-translate"), paint.get("icon-translate-anchor"));
8373		const pitchedLabelPlaneMatrix = getPitchedLabelPlaneMatrix(rotateWithMap, this.transform, pixelsToTiles);
8374		this.retainedQueryData[symbolBucket.bucketInstanceId] = new RetainedQueryData(symbolBucket.bucketInstanceId, bucketFeatureIndex, symbolBucket.sourceLayerIndex, symbolBucket.index, tile.tileID);
8375		const parameters = {
8376			bucket: symbolBucket,
8377			layout,
8378			translationText,
8379			translationIcon,
8380			unwrappedTileID,
8381			pitchedLabelPlaneMatrix,
8382			scale,
8383			textPixelRatio,
8384			holdingForFade: tile.holdingForSymbolFade(),
8385			collisionBoxArray,
8386			partiallyEvaluatedTextSize: evaluateSizeForZoom(symbolBucket.textSizeData, this.transform.zoom),
8387			collisionGroup: this.collisionGroups.get(symbolBucket.sourceID)
8388		};
8389		if (sortAcrossTiles) for (const range of symbolBucket.sortKeyRanges) {
8390			const { sortKey, symbolInstanceStart, symbolInstanceEnd } = range;
8391			results.push({
8392				sortKey,
8393				symbolInstanceStart,
8394				symbolInstanceEnd,
8395				parameters
8396			});
8397		}
8398		else results.push({
8399			symbolInstanceStart: 0,
8400			symbolInstanceEnd: symbolBucket.symbolInstances.length,
8401			parameters
8402		});
8403	}
8404	attemptAnchorPlacement(textAnchorOffset, textBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, tileID, unwrappedTileID, collisionGroup, textOverlapMode, symbolInstance, bucket, orientation, translationText, translationIcon, iconBox, getElevation, simpleProjectionMatrix, heightOffset, heightAnchorGround) {
8405		const anchor = TextAnchorEnum[textAnchorOffset.textAnchor];
8406		const textOffset = [textAnchorOffset.textOffset0, textAnchorOffset.textOffset1];
8407		const shift = calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale);
8408		const placedGlyphBoxes = this.collisionIndex.placeCollisionBox(textBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, shift, simpleProjectionMatrix, heightOffset, heightAnchorGround);
8409		if (iconBox) {
8410			if (!this.collisionIndex.placeCollisionBox(iconBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationIcon, collisionGroup.predicate, getElevation, shift, simpleProjectionMatrix, heightOffset, heightAnchorGround).placeable) return;
8411		}
8412		if (placedGlyphBoxes.placeable) {
8413			let prevAnchor;
8414			if (this.prevPlacement?.variableOffsets[symbolInstance.crossTileID] && this.prevPlacement?.placements[symbolInstance.crossTileID]?.text) prevAnchor = this.prevPlacement.variableOffsets[symbolInstance.crossTileID].anchor;
8415			if (symbolInstance.crossTileID === 0) throw new Error("symbolInstance.crossTileID can't be 0");
8416			this.variableOffsets[symbolInstance.crossTileID] = {
8417				textOffset,
8418				width,
8419				height,
8420				anchor,
8421				textBoxScale,
8422				prevAnchor
8423			};
8424			this.markUsedJustification(bucket, anchor, symbolInstance, orientation);
8425			if (bucket.allowVerticalPlacement) {
8426				this.markUsedOrientation(bucket, orientation, symbolInstance);
8427				this.placedOrientations[symbolInstance.crossTileID] = orientation;
8428			}
8429			return {
8430				shift,
8431				placedGlyphBoxes
8432			};
8433		}
8434	}
8435	placeLayerBucketPart(bucketPart, seenCrossTileIDs, showCollisionBoxes) {
8436		const { bucket, layout, translationText, translationIcon, unwrappedTileID, pitchedLabelPlaneMatrix, textPixelRatio, holdingForFade, collisionBoxArray, partiallyEvaluatedTextSize, collisionGroup } = bucketPart.parameters;
8437		const textOptional = layout.get("text-optional");
8438		const iconOptional = layout.get("icon-optional");
8439		const textOverlapMode = getOverlapMode(layout, "text-overlap", "text-allow-overlap");
8440		const textAlwaysOverlap = textOverlapMode === "always";
8441		const iconOverlapMode = getOverlapMode(layout, "icon-overlap", "icon-allow-overlap");
8442		const iconAlwaysOverlap = iconOverlapMode === "always";
8443		const rotateWithMap = layout.get("text-rotation-alignment") === "map";
8444		const pitchWithMap = layout.get("text-pitch-alignment") === "map";
8445		const hasIconTextFit = layout.get("icon-text-fit") !== "none";
8446		const zOrderByViewportY = layout.get("symbol-z-order") === "viewport-y";
8447		const heightAnchorGround = layout.get("symbol-height-anchor") === "ground";
8448		const alwaysShowText = textAlwaysOverlap && (iconAlwaysOverlap || !bucket.hasIconData() || iconOptional);
8449		const alwaysShowIcon = iconAlwaysOverlap && (textAlwaysOverlap || !bucket.hasTextData() || textOptional);
8450		if (!bucket.collisionArrays && collisionBoxArray) bucket.deserializeCollisionBoxes(collisionBoxArray);
8451		const tileID = this.retainedQueryData[bucket.bucketInstanceId].tileID;
8452		const getElevation = this._getTerrainElevationFunc(tileID);
8453		const simpleProjectionMatrix = this.transform.getFastPathSimpleProjectionMatrix(tileID);
8454		const placeSymbol = (symbolInstance, collisionArrays, symbolIndex) => {
8455			if (seenCrossTileIDs[symbolInstance.crossTileID]) return;
8456			if (holdingForFade) {
8457				this.placements[symbolInstance.crossTileID] = new JointPlacement(false, false, false);
8458				return;
8459			}
8460			const symbolHeightOffset = symbolInstance.heightOffset;
8461			let placeText = false;
8462			let placeIcon = false;
8463			let offscreen = true;
8464			let shift = null;
8465			let placed = {
8466				box: null,
8467				placeable: false,
8468				offscreen: null,
8469				occluded: false
8470			};
8471			let placedVerticalText = {
8472				box: null,
8473				placeable: false,
8474				offscreen: null
8475			};
8476			let placedGlyphBoxes = null;
8477			let placedGlyphCircles = null;
8478			let placedIconBoxes = null;
8479			let textFeatureIndex = 0;
8480			let verticalTextFeatureIndex = 0;
8481			let iconFeatureIndex = 0;
8482			if (collisionArrays.textFeatureIndex) textFeatureIndex = collisionArrays.textFeatureIndex;
8483			else if (symbolInstance.useRuntimeCollisionCircles) textFeatureIndex = symbolInstance.featureIndex;
8484			if (collisionArrays.verticalTextFeatureIndex) verticalTextFeatureIndex = collisionArrays.verticalTextFeatureIndex;
8485			const textBox = collisionArrays.textBox;
8486			if (textBox) {
8487				const updatePreviousOrientationIfNotPlaced = (isPlaced) => {
8488					let previousOrientation = 1;
8489					if (bucket.allowVerticalPlacement && !isPlaced && this.prevPlacement) {
8490						const prevPlacedOrientation = this.prevPlacement.placedOrientations[symbolInstance.crossTileID];
8491						if (prevPlacedOrientation) {
8492							this.placedOrientations[symbolInstance.crossTileID] = prevPlacedOrientation;
8493							previousOrientation = prevPlacedOrientation;
8494							this.markUsedOrientation(bucket, previousOrientation, symbolInstance);
8495						}
8496					}
8497					return previousOrientation;
8498				};
8499				const placeTextForPlacementModes = (placeHorizontalFn, placeVerticalFn) => {
8500					if (bucket.allowVerticalPlacement && symbolInstance.numVerticalGlyphVertices > 0 && collisionArrays.verticalTextBox) for (const placementMode of bucket.writingModes) {
8501						if (placementMode === 2) {
8502							placed = placeVerticalFn();
8503							placedVerticalText = placed;
8504						} else placed = placeHorizontalFn();
8505						if (placed?.placeable) break;
8506					}
8507					else placed = placeHorizontalFn();
8508				};
8509				const textAnchorOffsetStart = symbolInstance.textAnchorOffsetStartIndex;
8510				const textAnchorOffsetEnd = symbolInstance.textAnchorOffsetEndIndex;
8511				if (textAnchorOffsetEnd === textAnchorOffsetStart) {
8512					const placeBox = (collisionTextBox, orientation) => {
8513						const placedFeature = this.collisionIndex.placeCollisionBox(collisionTextBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, void 0, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround);
8514						if (placedFeature?.placeable) {
8515							this.markUsedOrientation(bucket, orientation, symbolInstance);
8516							this.placedOrientations[symbolInstance.crossTileID] = orientation;
8517						}
8518						return placedFeature;
8519					};
8520					const placeHorizontal = () => {
8521						return placeBox(textBox, 1);
8522					};
8523					const placeVertical = () => {
8524						const verticalTextBox = collisionArrays.verticalTextBox;
8525						if (bucket.allowVerticalPlacement && symbolInstance.numVerticalGlyphVertices > 0 && verticalTextBox) return placeBox(verticalTextBox, 2);
8526						return {
8527							box: null,
8528							offscreen: null
8529						};
8530					};
8531					placeTextForPlacementModes(placeHorizontal, placeVertical);
8532					updatePreviousOrientationIfNotPlaced(placed?.placeable);
8533				} else {
8534					let prevAnchor = TextAnchorEnum[this.prevPlacement?.variableOffsets[symbolInstance.crossTileID]?.anchor];
8535					const placeBoxForVariableAnchors = (collisionTextBox, collisionIconBox, orientation) => {
8536						const width = collisionTextBox.x2 - collisionTextBox.x1;
8537						const height = collisionTextBox.y2 - collisionTextBox.y1;
8538						const textBoxScale = symbolInstance.textBoxScale;
8539						const variableIconBox = hasIconTextFit && iconOverlapMode === "never" ? collisionIconBox : null;
8540						let placedBox = null;
8541						let placementPasses = textOverlapMode === "never" ? 1 : 2;
8542						let overlapMode = "never";
8543						if (prevAnchor) placementPasses++;
8544						for (let pass = 0; pass < placementPasses; pass++) {
8545							for (let i = textAnchorOffsetStart; i < textAnchorOffsetEnd; i++) {
8546								const textAnchorOffset = bucket.textAnchorOffsets.get(i);
8547								if (prevAnchor && textAnchorOffset.textAnchor !== prevAnchor) continue;
8548								const result = this.attemptAnchorPlacement(textAnchorOffset, collisionTextBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, tileID, unwrappedTileID, collisionGroup, overlapMode, symbolInstance, bucket, orientation, translationText, translationIcon, variableIconBox, getElevation, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround);
8549								if (result) {
8550									placedBox = result.placedGlyphBoxes;
8551									if (placedBox?.placeable) {
8552										placeText = true;
8553										shift = result.shift;
8554										return placedBox;
8555									}
8556								}
8557							}
8558							if (prevAnchor) prevAnchor = null;
8559							else overlapMode = textOverlapMode;
8560						}
8561						if (showCollisionBoxes && !placedBox) placedBox = {
8562							box: this.collisionIndex.placeCollisionBox(textBox, "always", textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, void 0, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround).box,
8563							offscreen: false,
8564							placeable: false,
8565							occluded: false
8566						};
8567						return placedBox;
8568					};
8569					const placeHorizontal = () => {
8570						return placeBoxForVariableAnchors(textBox, collisionArrays.iconBox, 1);
8571					};
8572					const placeVertical = () => {
8573						const verticalTextBox = collisionArrays.verticalTextBox;
8574						const wasPlaced = placed?.placeable;
8575						if (bucket.allowVerticalPlacement && !wasPlaced && symbolInstance.numVerticalGlyphVertices > 0 && verticalTextBox) return placeBoxForVariableAnchors(verticalTextBox, collisionArrays.verticalIconBox, 2);
8576						return {
8577							box: null,
8578							occluded: true,
8579							offscreen: null
8580						};
8581					};
8582					placeTextForPlacementModes(placeHorizontal, placeVertical);
8583					if (placed) {
8584						placeText = placed.placeable;
8585						offscreen = placed.offscreen;
8586					}
8587					const prevOrientation = updatePreviousOrientationIfNotPlaced(placed?.placeable);
8588					if (!placeText && this.prevPlacement) {
8589						const prevOffset = this.prevPlacement.variableOffsets[symbolInstance.crossTileID];
8590						if (prevOffset) {
8591							this.variableOffsets[symbolInstance.crossTileID] = prevOffset;
8592							this.markUsedJustification(bucket, prevOffset.anchor, symbolInstance, prevOrientation);
8593						}
8594					}
8595				}
8596			}
8597			placedGlyphBoxes = placed;
8598			placeText = placedGlyphBoxes?.placeable;
8599			offscreen = placedGlyphBoxes?.offscreen;
8600			if (symbolInstance.useRuntimeCollisionCircles && symbolInstance.centerJustifiedTextSymbolIndex >= 0) {
8601				const placedSymbol = bucket.text.placedSymbolArray.get(symbolInstance.centerJustifiedTextSymbolIndex);
8602				const fontSize = evaluateSizeForFeature(bucket.textSizeData, partiallyEvaluatedTextSize, placedSymbol);
8603				const textPixelPadding = layout.get("text-padding");
8604				const circlePixelDiameter = symbolInstance.collisionCircleDiameter;
8605				placedGlyphCircles = this.collisionIndex.placeCollisionCircles(textOverlapMode, placedSymbol, bucket.lineVertexArray, bucket.glyphOffsetArray, fontSize, unwrappedTileID, pitchedLabelPlaneMatrix, showCollisionBoxes, pitchWithMap, collisionGroup.predicate, circlePixelDiameter, textPixelPadding, translationText, getElevation);
8606				if (placedGlyphCircles.circles.length && placedGlyphCircles.collisionDetected && !showCollisionBoxes) warnOnce("Collisions detected, but collision boxes are not shown");
8607				placeText = textAlwaysOverlap || placedGlyphCircles.circles.length > 0 && !placedGlyphCircles.collisionDetected;
8608				offscreen &&= placedGlyphCircles.offscreen;
8609			}
8610			if (collisionArrays.iconFeatureIndex) iconFeatureIndex = collisionArrays.iconFeatureIndex;
8611			if (collisionArrays.iconBox) {
8612				const placeIconFeature = (iconBox) => {
8613					return this.collisionIndex.placeCollisionBox(iconBox, iconOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationIcon, collisionGroup.predicate, getElevation, hasIconTextFit && shift ? shift : void 0, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround);
8614				};
8615				if (placedVerticalText && placedVerticalText.placeable && collisionArrays.verticalIconBox) {
8616					placedIconBoxes = placeIconFeature(collisionArrays.verticalIconBox);
8617					placeIcon = placedIconBoxes.placeable;
8618				} else {
8619					placedIconBoxes = placeIconFeature(collisionArrays.iconBox);
8620					placeIcon = placedIconBoxes.placeable;
8621				}
8622				offscreen &&= placedIconBoxes.offscreen;
8623			}
8624			const iconWithoutText = textOptional || symbolInstance.numHorizontalGlyphVertices === 0 && symbolInstance.numVerticalGlyphVertices === 0;
8625			const textWithoutIcon = iconOptional || symbolInstance.numIconVertices === 0;
8626			if (!iconWithoutText && !textWithoutIcon) placeIcon = placeText = placeIcon && placeText;
8627			else if (!textWithoutIcon) placeText = placeIcon && placeText;
8628			else if (!iconWithoutText) placeIcon &&= placeText;
8629			const hasTextBox = placeText && placedGlyphBoxes.placeable;
8630			const hasIconBox = placeIcon && placedIconBoxes.placeable;
8631			if (hasTextBox) {
8632				if (placedVerticalText && placedVerticalText.placeable && verticalTextFeatureIndex) this.collisionIndex.insertCollisionBox(placedGlyphBoxes.box, textOverlapMode, layout.get("text-ignore-placement"), bucket.bucketInstanceId, verticalTextFeatureIndex, collisionGroup.ID);
8633				else this.collisionIndex.insertCollisionBox(placedGlyphBoxes.box, textOverlapMode, layout.get("text-ignore-placement"), bucket.bucketInstanceId, textFeatureIndex, collisionGroup.ID);
8634			}
8635			if (hasIconBox) this.collisionIndex.insertCollisionBox(placedIconBoxes.box, iconOverlapMode, layout.get("icon-ignore-placement"), bucket.bucketInstanceId, iconFeatureIndex, collisionGroup.ID);
8636			if (placedGlyphCircles) {
8637				if (placeText) this.collisionIndex.insertCollisionCircles(placedGlyphCircles.circles, textOverlapMode, layout.get("text-ignore-placement"), bucket.bucketInstanceId, textFeatureIndex, collisionGroup.ID);
8638			}
8639			if (showCollisionBoxes) this.storeCollisionData(bucket.bucketInstanceId, symbolIndex, collisionArrays, placedGlyphBoxes, placedIconBoxes, placedGlyphCircles);
8640			if (symbolInstance.crossTileID === 0) throw new Error("symbolInstance.crossTileID can't be 0");
8641			if (bucket.bucketInstanceId === 0) throw new Error("bucket.bucketInstanceId can't be 0");
8642			const textVisible = (placeText || alwaysShowText) && !placedGlyphBoxes?.occluded;
8643			const iconVisible = (placeIcon || alwaysShowIcon) && !placedIconBoxes?.occluded;
8644			this.placements[symbolInstance.crossTileID] = new JointPlacement(textVisible, iconVisible, offscreen || bucket.justReloaded);
8645			seenCrossTileIDs[symbolInstance.crossTileID] = true;
8646		};
8647		if (zOrderByViewportY) {
8648			if (bucketPart.symbolInstanceStart !== 0) throw new Error("bucket.bucketInstanceId should be 0");
8649			const symbolIndexes = bucket.getSortedSymbolIndexes(-this.transform.bearingInRadians);
8650			for (let i = symbolIndexes.length - 1; i >= 0; --i) {
8651				const symbolIndex = symbolIndexes[i];
8652				placeSymbol(bucket.symbolInstances.get(symbolIndex), bucket.collisionArrays[symbolIndex], symbolIndex);
8653			}
8654		} else for (let i = bucketPart.symbolInstanceStart; i < bucketPart.symbolInstanceEnd; i++) placeSymbol(bucket.symbolInstances.get(i), bucket.collisionArrays[i], i);
8655		bucket.justReloaded = false;
8656	}
8657	storeCollisionData(bucketInstanceId, symbolIndex, collisionArrays, placedGlyphBoxes, placedIconBoxes, placedGlyphCircles) {
8658		if (collisionArrays.textBox || collisionArrays.iconBox) {
8659			let boxArray;
8660			if (this.collisionBoxArrays.has(bucketInstanceId)) boxArray = this.collisionBoxArrays.get(bucketInstanceId);
8661			else {
8662				boxArray = /* @__PURE__ */ new Map();
8663				this.collisionBoxArrays.set(bucketInstanceId, boxArray);
8664			}
8665			let realCollisionBox;
8666			if (boxArray.has(symbolIndex)) realCollisionBox = boxArray.get(symbolIndex);
8667			else {
8668				realCollisionBox = {
8669					text: null,
8670					icon: null
8671				};
8672				boxArray.set(symbolIndex, realCollisionBox);
8673			}
8674			if (collisionArrays.textBox) realCollisionBox.text = placedGlyphBoxes.box;
8675			if (collisionArrays.iconBox) realCollisionBox.icon = placedIconBoxes.box;
8676		}
8677		if (placedGlyphCircles) {
8678			let circleArray = this.collisionCircleArrays[bucketInstanceId];
8679			if (circleArray === void 0) circleArray = this.collisionCircleArrays[bucketInstanceId] = [];
8680			for (let i = 0; i < placedGlyphCircles.circles.length; i += 4) {
8681				circleArray.push(placedGlyphCircles.circles[i + 0] - 100);
8682				circleArray.push(placedGlyphCircles.circles[i + 1] - 100);
8683				circleArray.push(placedGlyphCircles.circles[i + 2]);
8684				circleArray.push(placedGlyphCircles.collisionDetected ? 1 : 0);
8685			}
8686		}
8687	}
8688	markUsedJustification(bucket, placedAnchor, symbolInstance, orientation) {
8689		const justifications = {
8690			"left": symbolInstance.leftJustifiedTextSymbolIndex,
8691			"center": symbolInstance.centerJustifiedTextSymbolIndex,
8692			"right": symbolInstance.rightJustifiedTextSymbolIndex
8693		};
8694		let autoIndex;
8695		if (orientation === 2) autoIndex = symbolInstance.verticalPlacedTextSymbolIndex;
8696		else autoIndex = justifications[getAnchorJustification(placedAnchor)];
8697		const indexes = [
8698			symbolInstance.leftJustifiedTextSymbolIndex,
8699			symbolInstance.centerJustifiedTextSymbolIndex,
8700			symbolInstance.rightJustifiedTextSymbolIndex,
8701			symbolInstance.verticalPlacedTextSymbolIndex
8702		];
8703		for (const index of indexes) if (index >= 0) {
8704			if (autoIndex >= 0 && index !== autoIndex) bucket.text.placedSymbolArray.get(index).crossTileID = 0;
8705			else bucket.text.placedSymbolArray.get(index).crossTileID = symbolInstance.crossTileID;
8706		}
8707	}
8708	markUsedOrientation(bucket, orientation, symbolInstance) {
8709		const horizontal = orientation === 1 || orientation === 3 ? orientation : 0;
8710		const vertical = orientation === 2 ? orientation : 0;
8711		const horizontalIndexes = [
8712			symbolInstance.leftJustifiedTextSymbolIndex,
8713			symbolInstance.centerJustifiedTextSymbolIndex,
8714			symbolInstance.rightJustifiedTextSymbolIndex
8715		];
8716		for (const index of horizontalIndexes) bucket.text.placedSymbolArray.get(index).placedOrientation = horizontal;
8717		if (symbolInstance.verticalPlacedTextSymbolIndex) bucket.text.placedSymbolArray.get(symbolInstance.verticalPlacedTextSymbolIndex).placedOrientation = vertical;
8718	}
8719	commit(now) {
8720		this.commitTime = now;
8721		this.zoomAtLastRecencyCheck = this.transform.zoom;
8722		const prevPlacement = this.prevPlacement;
8723		let placementChanged = false;
8724		this.prevZoomAdjustment = prevPlacement ? prevPlacement.zoomAdjustment(this.transform.zoom) : 0;
8725		const increment = prevPlacement ? prevPlacement.symbolFadeChange(now) : 1;
8726		const prevOpacities = prevPlacement ? prevPlacement.opacities : {};
8727		const prevOffsets = prevPlacement ? prevPlacement.variableOffsets : {};
8728		const prevOrientations = prevPlacement ? prevPlacement.placedOrientations : {};
8729		for (const crossTileID in this.placements) {
8730			const jointPlacement = this.placements[crossTileID];
8731			const prevOpacity = prevOpacities[crossTileID];
8732			if (prevOpacity) {
8733				this.opacities[crossTileID] = new JointOpacityState(prevOpacity, increment, jointPlacement.text, jointPlacement.icon);
8734				placementChanged ||= jointPlacement.text !== prevOpacity.text.placed;
8735				placementChanged ||= jointPlacement.icon !== prevOpacity.icon.placed;
8736			} else {
8737				this.opacities[crossTileID] = new JointOpacityState(null, increment, jointPlacement.text, jointPlacement.icon, jointPlacement.skipFade);
8738				placementChanged ||= jointPlacement.text || jointPlacement.icon;
8739			}
8740		}
8741		for (const crossTileID in prevOpacities) {
8742			const prevOpacity = prevOpacities[crossTileID];
8743			if (!this.opacities[crossTileID]) {
8744				const jointOpacity = new JointOpacityState(prevOpacity, increment, false, false);
8745				if (!jointOpacity.isHidden()) {
8746					this.opacities[crossTileID] = jointOpacity;
8747					placementChanged ||= prevOpacity.text.placed;
8748					placementChanged ||= prevOpacity.icon.placed;
8749				}
8750			}
8751		}
8752		for (const crossTileID in prevOffsets) if (!this.variableOffsets[crossTileID] && this.opacities[crossTileID] && !this.opacities[crossTileID].isHidden()) this.variableOffsets[crossTileID] = prevOffsets[crossTileID];
8753		for (const crossTileID in prevOrientations) if (!this.placedOrientations[crossTileID] && this.opacities[crossTileID] && !this.opacities[crossTileID].isHidden()) this.placedOrientations[crossTileID] = prevOrientations[crossTileID];
8754		if (prevPlacement && prevPlacement.lastPlacementChangeTime === void 0) throw new Error("Last placement time for previous placement is not defined");
8755		if (placementChanged) this.lastPlacementChangeTime = now;
8756		else if (typeof this.lastPlacementChangeTime !== "number") this.lastPlacementChangeTime = prevPlacement ? prevPlacement.lastPlacementChangeTime : now;
8757	}
8758	updateLayerOpacities(styleLayer, tiles) {
8759		const seenCrossTileIDs = {};
8760		for (const tile of tiles) {
8761			const symbolBucket = tile.getBucket(styleLayer);
8762			if (symbolBucket && tile.latestFeatureIndex && styleLayer.id === symbolBucket.layerIds[0]) this.updateBucketOpacities(symbolBucket, tile.tileID, seenCrossTileIDs, tile.collisionBoxArray);
8763		}
8764	}
8765	updateBucketOpacities(bucket, tileID, seenCrossTileIDs, collisionBoxArray) {
8766		if (bucket.hasTextData()) {
8767			bucket.text.opacityVertexArray.clear();
8768			bucket.text.hasVisibleVertices = false;
8769		}
8770		if (bucket.hasIconData()) {
8771			bucket.icon.opacityVertexArray.clear();
8772			bucket.icon.hasVisibleVertices = false;
8773		}
8774		if (bucket.hasIconCollisionBoxData()) bucket.iconCollisionBox.collisionVertexArray.clear();
8775		if (bucket.hasTextCollisionBoxData()) bucket.textCollisionBox.collisionVertexArray.clear();
8776		const layer = bucket.layers[0];
8777		const layout = layer.layout;
8778		const duplicateOpacityState = new JointOpacityState(null, 0, false, false, true);
8779		const textAllowOverlap = layout.get("text-allow-overlap");
8780		const iconAllowOverlap = layout.get("icon-allow-overlap");
8781		const hasVariablePlacement = layer._unevaluatedLayout.hasValue("text-variable-anchor") || layer._unevaluatedLayout.hasValue("text-variable-anchor-offset");
8782		const rotateWithMap = layout.get("text-rotation-alignment") === "map";
8783		const pitchWithMap = layout.get("text-pitch-alignment") === "map";
8784		const hasIconTextFit = layout.get("icon-text-fit") !== "none";
8785		const defaultOpacityState = new JointOpacityState(null, 0, textAllowOverlap && (iconAllowOverlap || !bucket.hasIconData() || layout.get("icon-optional")), iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || layout.get("text-optional")), true);
8786		if (!bucket.collisionArrays && collisionBoxArray && (bucket.hasIconCollisionBoxData() || bucket.hasTextCollisionBoxData())) bucket.deserializeCollisionBoxes(collisionBoxArray);
8787		const addOpacities = (iconOrText, numVertices, opacity) => {
8788			for (let i = 0; i < numVertices / 4; i++) iconOrText.opacityVertexArray.emplaceBack(opacity);
8789			iconOrText.hasVisibleVertices ||= opacity !== PACKED_HIDDEN_OPACITY;
8790		};
8791		const boxArrays = this.collisionBoxArrays.get(bucket.bucketInstanceId);
8792		for (let s = 0; s < bucket.symbolInstances.length; s++) {
8793			const symbolInstance = bucket.symbolInstances.get(s);
8794			const { numHorizontalGlyphVertices, numVerticalGlyphVertices, crossTileID } = symbolInstance;
8795			const isDuplicate = seenCrossTileIDs[crossTileID];
8796			let opacityState = this.opacities[crossTileID];
8797			if (isDuplicate) opacityState = duplicateOpacityState;
8798			else if (!opacityState) {
8799				opacityState = defaultOpacityState;
8800				this.opacities[crossTileID] = opacityState;
8801			}
8802			seenCrossTileIDs[crossTileID] = true;
8803			const hasText = numHorizontalGlyphVertices > 0 || numVerticalGlyphVertices > 0;
8804			const hasIcon = symbolInstance.numIconVertices > 0;
8805			const placedOrientation = this.placedOrientations[symbolInstance.crossTileID];
8806			const horizontalHidden = placedOrientation === 2;
8807			const verticalHidden = placedOrientation === 1 || placedOrientation === 3;
8808			if (hasText) {
8809				const packedOpacity = packOpacity(opacityState.text);
8810				const horizontalOpacity = horizontalHidden ? PACKED_HIDDEN_OPACITY : packedOpacity;
8811				addOpacities(bucket.text, numHorizontalGlyphVertices, horizontalOpacity);
8812				const verticalOpacity = verticalHidden ? PACKED_HIDDEN_OPACITY : packedOpacity;
8813				addOpacities(bucket.text, numVerticalGlyphVertices, verticalOpacity);
8814				const symbolHidden = opacityState.text.isHidden();
8815				const textSymbolIndexes = [
8816					symbolInstance.rightJustifiedTextSymbolIndex,
8817					symbolInstance.centerJustifiedTextSymbolIndex,
8818					symbolInstance.leftJustifiedTextSymbolIndex
8819				];
8820				for (const index of textSymbolIndexes) if (index >= 0) bucket.text.placedSymbolArray.get(index).hidden = symbolHidden || horizontalHidden ? 1 : 0;
8821				if (symbolInstance.verticalPlacedTextSymbolIndex >= 0) bucket.text.placedSymbolArray.get(symbolInstance.verticalPlacedTextSymbolIndex).hidden = symbolHidden || verticalHidden ? 1 : 0;
8822				const prevOffset = this.variableOffsets[symbolInstance.crossTileID];
8823				if (prevOffset) this.markUsedJustification(bucket, prevOffset.anchor, symbolInstance, placedOrientation);
8824				const prevOrientation = this.placedOrientations[symbolInstance.crossTileID];
8825				if (prevOrientation) {
8826					this.markUsedJustification(bucket, "left", symbolInstance, prevOrientation);
8827					this.markUsedOrientation(bucket, prevOrientation, symbolInstance);
8828				}
8829			}
8830			if (hasIcon) {
8831				const packedOpacity = packOpacity(opacityState.icon);
8832				const useHorizontal = !(hasIconTextFit && symbolInstance.verticalPlacedIconSymbolIndex && horizontalHidden);
8833				if (symbolInstance.placedIconSymbolIndex >= 0) {
8834					const horizontalOpacity = useHorizontal ? packedOpacity : PACKED_HIDDEN_OPACITY;
8835					addOpacities(bucket.icon, symbolInstance.numIconVertices, horizontalOpacity);
8836					bucket.icon.placedSymbolArray.get(symbolInstance.placedIconSymbolIndex).hidden = opacityState.icon.isHidden();
8837				}
8838				if (symbolInstance.verticalPlacedIconSymbolIndex >= 0) {
8839					const verticalOpacity = !useHorizontal ? packedOpacity : PACKED_HIDDEN_OPACITY;
8840					addOpacities(bucket.icon, symbolInstance.numVerticalIconVertices, verticalOpacity);
8841					bucket.icon.placedSymbolArray.get(symbolInstance.verticalPlacedIconSymbolIndex).hidden = opacityState.icon.isHidden();
8842				}
8843			}
8844			const realBoxes = boxArrays?.has(s) ? boxArrays.get(s) : {
8845				text: null,
8846				icon: null
8847			};
8848			if (bucket.hasIconCollisionBoxData() || bucket.hasTextCollisionBoxData()) {
8849				const collisionArrays = bucket.collisionArrays[s];
8850				if (collisionArrays) {
8851					let shift = new Point(0, 0);
8852					if (collisionArrays.textBox || collisionArrays.verticalTextBox) {
8853						let used = true;
8854						if (hasVariablePlacement) {
8855							const variableOffset = this.variableOffsets[crossTileID];
8856							if (variableOffset) {
8857								shift = calculateVariableLayoutShift(variableOffset.anchor, variableOffset.width, variableOffset.height, variableOffset.textOffset, variableOffset.textBoxScale);
8858								if (rotateWithMap) shift._rotate(pitchWithMap ? -this.transform.bearingInRadians : this.transform.bearingInRadians);
8859							} else used = false;
8860						}
8861						if (collisionArrays.textBox || collisionArrays.verticalTextBox) {
8862							let hidden;
8863							if (collisionArrays.textBox) hidden = horizontalHidden;
8864							if (collisionArrays.verticalTextBox) hidden = verticalHidden;
8865							updateCollisionVertices(bucket.textCollisionBox.collisionVertexArray, opacityState.text.placed, !used || hidden, realBoxes.text, shift.x, shift.y);
8866						}
8867					}
8868					if (collisionArrays.iconBox || collisionArrays.verticalIconBox) {
8869						const verticalIconUsed = Boolean(!verticalHidden && collisionArrays.verticalIconBox);
8870						let hidden;
8871						if (collisionArrays.iconBox) hidden = verticalIconUsed;
8872						if (collisionArrays.verticalIconBox) hidden = !verticalIconUsed;
8873						updateCollisionVertices(bucket.iconCollisionBox.collisionVertexArray, opacityState.icon.placed, hidden, realBoxes.icon, hasIconTextFit ? shift.x : 0, hasIconTextFit ? shift.y : 0);
8874					}
8875				}
8876			}
8877		}
8878		bucket.sortFeatures(-this.transform.bearingInRadians);
8879		if (this.retainedQueryData[bucket.bucketInstanceId]) this.retainedQueryData[bucket.bucketInstanceId].featureSortOrder = bucket.featureSortOrder;
8880		if (bucket.hasTextData() && bucket.text.opacityVertexBuffer) bucket.text.opacityVertexBuffer.updateData(bucket.text.opacityVertexArray);
8881		if (bucket.hasIconData() && bucket.icon.opacityVertexBuffer) bucket.icon.opacityVertexBuffer.updateData(bucket.icon.opacityVertexArray);
8882		if (bucket.hasIconCollisionBoxData() && bucket.iconCollisionBox.collisionVertexBuffer) bucket.iconCollisionBox.collisionVertexBuffer.updateData(bucket.iconCollisionBox.collisionVertexArray);
8883		if (bucket.hasTextCollisionBoxData() && bucket.textCollisionBox.collisionVertexBuffer) bucket.textCollisionBox.collisionVertexBuffer.updateData(bucket.textCollisionBox.collisionVertexArray);
8884		if (bucket.text.opacityVertexArray.length !== bucket.text.layoutVertexArray.length / 4) throw new Error(`bucket.text.opacityVertexArray.length (= ${bucket.text.opacityVertexArray.length}) !== bucket.text.layoutVertexArray.length (= ${bucket.text.layoutVertexArray.length}) / 4`);
8885		if (bucket.icon.opacityVertexArray.length !== bucket.icon.layoutVertexArray.length / 4) throw new Error(`bucket.icon.opacityVertexArray.length (= ${bucket.icon.opacityVertexArray.length}) !== bucket.icon.layoutVertexArray.length (= ${bucket.icon.layoutVertexArray.length}) / 4`);
8886		if (bucket.bucketInstanceId in this.collisionCircleArrays) {
8887			bucket.collisionCircleArray = this.collisionCircleArrays[bucket.bucketInstanceId];
8888			delete this.collisionCircleArrays[bucket.bucketInstanceId];
8889		}
8890	}
8891	symbolFadeChange(now) {
8892		return this.fadeDuration === 0 ? 1 : (now - this.commitTime) / this.fadeDuration + this.prevZoomAdjustment;
8893	}
8894	zoomAdjustment(zoom) {
8895		return Math.max(0, (this.transform.zoom - zoom) / 1.5);
8896	}
8897	hasTransitions(now) {
8898		return this.stale || now - this.lastPlacementChangeTime < this.fadeDuration;
8899	}
8900	stillRecent(now, zoom) {
8901		const durationAdjustment = this.zoomAtLastRecencyCheck === zoom ? 1 - this.zoomAdjustment(zoom) : 1;
8902		this.zoomAtLastRecencyCheck = zoom;
8903		return this.commitTime + this.fadeDuration * durationAdjustment > now;
8904	}
8905	setStale() {
8906		this.stale = true;
8907	}
8908};
8909function updateCollisionVertices(collisionVertexArray, placed, notUsed, realBox, shiftX, shiftY) {
8910	if (!realBox || realBox.length === 0) realBox = [
8911		0,
8912		0,
8913		0,
8914		0
8915	];
8916	const tlX = realBox[0] - 100;
8917	const tlY = realBox[1] - 100;
8918	const brX = realBox[2] - 100;
8919	const brY = realBox[3] - 100;
8920	collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0, tlX, tlY);
8921	collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0, brX, tlY);
8922	collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0, brX, brY);
8923	collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0, tlX, brY);
8924}
8925const shift25 = Math.pow(2, 25);
8926const shift24 = Math.pow(2, 24);
8927const shift17 = Math.pow(2, 17);
8928const shift16 = Math.pow(2, 16);
8929const shift9 = Math.pow(2, 9);
8930const shift8 = Math.pow(2, 8);
8931const shift1 = Math.pow(2, 1);
8932function packOpacity(opacityState) {
8933	if (opacityState.opacity === 0 && !opacityState.placed) return 0;
8934	else if (opacityState.opacity === 1 && opacityState.placed) return 4294967295;
8935	const targetBit = opacityState.placed ? 1 : 0;
8936	const opacityBits = Math.floor(opacityState.opacity * 127);
8937	return opacityBits * shift25 + targetBit * shift24 + opacityBits * shift17 + targetBit * shift16 + opacityBits * shift9 + targetBit * shift8 + opacityBits * shift1 + targetBit;
8938}
8939const PACKED_HIDDEN_OPACITY = 0;
8940//#endregion
8941//#region src/style/pauseable_placement.ts
8942var LayerPlacement = class {
8943	constructor(styleLayer) {
8944		this._sortAcrossTiles = styleLayer.layout.get("symbol-z-order") !== "viewport-y" && !styleLayer.layout.get("symbol-sort-key").isConstant();
8945		this._currentTileIndex = 0;
8946		this._currentPartIndex = 0;
8947		this._seenCrossTileIDs = {};
8948		this._bucketParts = [];
8949	}
8950	continuePlacement(tiles, placement, showCollisionBoxes, styleLayer, shouldPausePlacement) {
8951		const bucketParts = this._bucketParts;
8952		while (this._currentTileIndex < tiles.length) {
8953			const tile = tiles[this._currentTileIndex];
8954			placement.getBucketParts(bucketParts, styleLayer, tile, this._sortAcrossTiles);
8955			this._currentTileIndex++;
8956			if (shouldPausePlacement()) return true;
8957		}
8958		if (this._sortAcrossTiles) {
8959			this._sortAcrossTiles = false;
8960			bucketParts.sort((a, b) => a.sortKey - b.sortKey);
8961		}
8962		while (this._currentPartIndex < bucketParts.length) {
8963			const bucketPart = bucketParts[this._currentPartIndex];
8964			placement.placeLayerBucketPart(bucketPart, this._seenCrossTileIDs, showCollisionBoxes);
8965			this._currentPartIndex++;
8966			if (shouldPausePlacement()) return true;
8967		}
8968		return false;
8969	}
8970};
8971var PauseablePlacement = class {
8972	constructor(transform, terrain, order, forceFullPlacement, showCollisionBoxes, fadeDuration, crossSourceCollisions, prevPlacement) {
8973		this.placement = new Placement(transform, terrain, fadeDuration, crossSourceCollisions, prevPlacement);
8974		this._currentPlacementIndex = order.length - 1;
8975		this._forceFullPlacement = forceFullPlacement;
8976		this._showCollisionBoxes = showCollisionBoxes;
8977		this._done = false;
8978	}
8979	isDone() {
8980		return this._done;
8981	}
8982	continuePlacement(order, layers, layerTiles) {
8983		const startTime = now();
8984		const shouldPausePlacement = () => {
8985			return this._forceFullPlacement ? false : now() - startTime > 2;
8986		};
8987		while (this._currentPlacementIndex >= 0) {
8988			const layer = layers[order[this._currentPlacementIndex]];
8989			const placementZoom = this.placement.collisionIndex.transform.zoom;
8990			if (isSymbolStyleLayer(layer) && layer.layout && (!layer.minzoom || layer.minzoom <= placementZoom) && (!layer.maxzoom || layer.maxzoom > placementZoom)) {
8991				this._inProgressLayer ||= new LayerPlacement(layer);
8992				if (this._inProgressLayer.continuePlacement(layerTiles[layer.source], this.placement, this._showCollisionBoxes, layer, shouldPausePlacement)) return;
8993				delete this._inProgressLayer;
8994			}
8995			this._currentPlacementIndex--;
8996		}
8997		this._done = true;
8998	}
8999	commit(now) {
9000		this.placement.commit(now);
9001		return this.placement;
9002	}
9003};
9004//#endregion
9005//#region node_modules/kdbush/index.js
9006const ARRAY_TYPES = [
9007	Int8Array,
9008	Uint8Array,
9009	Uint8ClampedArray,
9010	Int16Array,
9011	Uint16Array,
9012	Int32Array,
9013	Uint32Array,
9014	Float32Array,
9015	Float64Array
9016];
9017/** @typedef {Int8ArrayConstructor | Uint8ArrayConstructor | Uint8ClampedArrayConstructor | Int16ArrayConstructor | Uint16ArrayConstructor | Int32ArrayConstructor | Uint32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor} TypedArrayConstructor */
9018/** @typedef {Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array} TypedArray */
9019const VERSION = 1;
9020const HEADER_SIZE = 8;
9021const STACK = /* @__PURE__ */ new Uint32Array(96);
9022var KDBush = class KDBush {
9023	/**
9024	* Creates an index from raw `ArrayBuffer` data.
9025	* @param {ArrayBufferLike} data
9026	*/
9027	static from(data) {
9028		if (!data || data.byteLength === void 0 || data.buffer) throw new Error("Data must be an instance of ArrayBuffer or SharedArrayBuffer.");
9029		const [magic, versionAndType] = new Uint8Array(data, 0, 2);
9030		if (magic !== 219) throw new Error("Data does not appear to be in a KDBush format.");
9031		const version = versionAndType >> 4;
9032		if (version !== VERSION) throw new Error(`Got v${version} data when expected v${VERSION}.`);
9033		const ArrayType = ARRAY_TYPES[versionAndType & 15];
9034		if (!ArrayType) throw new Error("Unrecognized array type.");
9035		const [nodeSize] = new Uint16Array(data, 2, 1);
9036		const [numItems] = new Uint32Array(data, 4, 1);
9037		return new KDBush(numItems, nodeSize, ArrayType, void 0, data);
9038	}
9039	/**
9040	* Creates an index that will hold a given number of items.
9041	* @param {number} numItems
9042	* @param {number} [nodeSize=64] Size of the KD-tree node (64 by default).
9043	* @param {TypedArrayConstructor} [ArrayType=Float64Array] The array type used for coordinates storage (`Float64Array` by default).
9044	* @param {ArrayBufferConstructor | SharedArrayBufferConstructor} [ArrayBufferType=ArrayBuffer] The array buffer type used for storage (`ArrayBuffer` by default).
9045	* @param {ArrayBufferLike} [data] (For internal use only)
9046	*/
9047	constructor(numItems, nodeSize = 64, ArrayType = Float64Array, ArrayBufferType = ArrayBuffer, data) {
9048		if (isNaN(numItems) || numItems < 0) throw new Error(`Unexpected numItems value: ${numItems}.`);
9049		this.numItems = +numItems;
9050		this.nodeSize = Math.min(Math.max(+nodeSize, 2), 65535);
9051		this.ArrayType = ArrayType;
9052		this.IndexArrayType = numItems < 65536 ? Uint16Array : Uint32Array;
9053		const arrayTypeIndex = ARRAY_TYPES.indexOf(this.ArrayType);
9054		const coordsByteSize = numItems * 2 * this.ArrayType.BYTES_PER_ELEMENT;
9055		const idsByteSize = numItems * this.IndexArrayType.BYTES_PER_ELEMENT;
9056		const padCoords = (8 - idsByteSize % 8) % 8;
9057		if (arrayTypeIndex < 0) throw new Error(`Unexpected typed array class: ${ArrayType}.`);
9058		if (data) {
9059			this.data = data;
9060			this.ids = new this.IndexArrayType(data, HEADER_SIZE, numItems);
9061			this.coords = new ArrayType(data, HEADER_SIZE + idsByteSize + padCoords, numItems * 2);
9062			this._pos = numItems * 2;
9063			this._finished = true;
9064		} else {
9065			const data = this.data = new ArrayBufferType(HEADER_SIZE + coordsByteSize + idsByteSize + padCoords);
9066			this.ids = new this.IndexArrayType(data, HEADER_SIZE, numItems);
9067			this.coords = new ArrayType(data, HEADER_SIZE + idsByteSize + padCoords, numItems * 2);
9068			this._pos = 0;
9069			this._finished = false;
9070			new Uint8Array(data, 0, 2).set([219, 16 + arrayTypeIndex]);
9071			new Uint16Array(data, 2, 1)[0] = nodeSize;
9072			new Uint32Array(data, 4, 1)[0] = numItems;
9073		}
9074	}
9075	/**
9076	* Add a point to the index.
9077	* @param {number} x
9078	* @param {number} y
9079	* @returns {number} An incremental index associated with the added item (starting from `0`).
9080	*/
9081	add(x, y) {
9082		const index = this._pos >> 1;
9083		this.ids[index] = index;
9084		this.coords[this._pos++] = x;
9085		this.coords[this._pos++] = y;
9086		return index;
9087	}
9088	/**
9089	* Perform indexing of the added points.
9090	*/
9091	finish() {
9092		const numAdded = this._pos >> 1;
9093		if (numAdded !== this.numItems) throw new Error(`Added ${numAdded} items when expected ${this.numItems}.`);
9094		sort(this.ids, this.coords, this.nodeSize, 0, this.numItems - 1, 0);
9095		this._finished = true;
9096		return this;
9097	}
9098	/**
9099	* Search the index for items within a given bounding box.
9100	* @param {number} minX
9101	* @param {number} minY
9102	* @param {number} maxX
9103	* @param {number} maxY
9104	* @returns {number[]} An array of indices correponding to the found items.
9105	*/
9106	range(minX, minY, maxX, maxY) {
9107		if (!this._finished) throw new Error("Data not yet indexed - call index.finish().");
9108		const { ids, coords, nodeSize } = this;
9109		STACK[0] = 0;
9110		STACK[1] = ids.length - 1;
9111		STACK[2] = 0;
9112		let sp = 3;
9113		const result = [];
9114		while (sp > 0) {
9115			const axis = STACK[--sp];
9116			const right = STACK[--sp];
9117			const left = STACK[--sp];
9118			if (right - left <= nodeSize) {
9119				for (let i = left; i <= right; i++) {
9120					const x = coords[2 * i];
9121					const y = coords[2 * i + 1];
9122					if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[i]);
9123				}
9124				continue;
9125			}
9126			const m = left + right >> 1;
9127			const x = coords[2 * m];
9128			const y = coords[2 * m + 1];
9129			if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[m]);
9130			if (axis === 0 ? minX <= x : minY <= y) {
9131				STACK[sp++] = left;
9132				STACK[sp++] = m - 1;
9133				STACK[sp++] = 1 - axis;
9134			}
vendor: 1,866 bytes, lines 9135-9191
9135			if (axis === 0 ? maxX >= x : maxY >= y) {
9136				STACK[sp++] = m + 1;
9137				STACK[sp++] = right;
9138				STACK[sp++] = 1 - axis;
9139			}
9140		}
9141		return result;
9142	}
9143	/**
9144	* Search the index for items within a given radius.
9145	* @param {number} qx
9146	* @param {number} qy
9147	* @param {number} r Query radius.
9148	* @returns {number[]} An array of indices correponding to the found items.
9149	*/
9150	within(qx, qy, r) {
9151		const result = [];
9152		this.withinInto(qx, qy, r, result);
9153		return result;
9154	}
9155	/**
9156	* Search the index for items within a given radius, writing matching ids into `out`
9157	* via indexed assignment (`out[i] = id`). Accepts any indexed-writable container —
9158	* a typed array sized to the expected upper bound (allocation-free, fast) or a plain
9159	* `Array` (which will grow as needed). Returns the number of matches written.
9160	* @param {number} qx
9161	* @param {number} qy
9162	* @param {number} r Query radius.
9163	* @param {number[] | TypedArray} out Container to write matching ids into.
9164	* @returns {number} The number of matches written to `out`.
9165	*/
9166	withinInto(qx, qy, r, out) {
9167		if (!this._finished) throw new Error("Data not yet indexed - call index.finish().");
9168		const { ids, coords, nodeSize } = this;
9169		STACK[0] = 0;
9170		STACK[1] = ids.length - 1;
9171		STACK[2] = 0;
9172		let sp = 3;
9173		let count = 0;
9174		const r2 = r * r;
9175		while (sp > 0) {
9176			const axis = STACK[--sp];
9177			const right = STACK[--sp];
9178			const left = STACK[--sp];
9179			if (right - left <= nodeSize) {
9180				for (let i = left; i <= right; i++) if (sqDist(coords[2 * i], coords[2 * i + 1], qx, qy) <= r2) out[count++] = ids[i];
9181				continue;
9182			}
9183			const m = left + right >> 1;
9184			const x = coords[2 * m];
9185			const y = coords[2 * m + 1];
9186			if (sqDist(x, y, qx, qy) <= r2) out[count++] = ids[m];
9187			if (axis === 0 ? qx - r <= x : qy - r <= y) {
9188				STACK[sp++] = left;
9189				STACK[sp++] = m - 1;
9190				STACK[sp++] = 1 - axis;
9191			}
vendor: 50,119 bytes, lines 9192-9559
9192			if (axis === 0 ? qx + r >= x : qy + r >= y) {
9193				STACK[sp++] = m + 1;
9194				STACK[sp++] = right;
9195				STACK[sp++] = 1 - axis;
9196			}
9197		}
9198		return count;
9199	}
9200};
9201/**
9202* @param {Uint16Array | Uint32Array} ids
9203* @param {TypedArray} coords
9204* @param {number} nodeSize
9205* @param {number} left
9206* @param {number} right
9207* @param {number} axis
9208*/
9209function sort(ids, coords, nodeSize, left, right, axis) {
9210	if (right - left <= nodeSize) return;
9211	const m = left + right >> 1;
9212	select(ids, coords, m, left, right, axis);
9213	sort(ids, coords, nodeSize, left, m - 1, 1 - axis);
9214	sort(ids, coords, nodeSize, m + 1, right, 1 - axis);
9215}
9216/**
9217* Custom Floyd-Rivest selection algorithm: sort ids and coords so that
9218* [left..k-1] items are smaller than k-th item (on either x or y axis)
9219* @param {Uint16Array | Uint32Array} ids
9220* @param {TypedArray} coords
9221* @param {number} k
9222* @param {number} left
9223* @param {number} right
9224* @param {number} axis
9225*/
9226function select(ids, coords, k, left, right, axis) {
9227	while (right > left) {
9228		if (right - left > 600) {
9229			const n = right - left + 1;
9230			const m = k - left + 1;
9231			const z = Math.log(n);
9232			const s = .5 * Math.exp(2 * z / 3);
9233			const sd = .5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
9234			select(ids, coords, k, Math.max(left, Math.floor(k - m * s / n + sd)), Math.min(right, Math.floor(k + (n - m) * s / n + sd)), axis);
9235		}
9236		const t = coords[2 * k + axis];
9237		let i = left;
9238		let j = right;
9239		swapItem(ids, coords, left, k);
9240		if (coords[2 * right + axis] > t) swapItem(ids, coords, left, right);
9241		while (i < j) {
9242			swapItem(ids, coords, i, j);
9243			i++;
9244			j--;
9245			while (coords[2 * i + axis] < t) i++;
9246			while (coords[2 * j + axis] > t) j--;
9247		}
9248		if (coords[2 * left + axis] === t) swapItem(ids, coords, left, j);
9249		else {
9250			j++;
9251			swapItem(ids, coords, j, right);
9252		}
9253		if (j <= k) left = j + 1;
9254		if (k <= j) right = j - 1;
9255	}
9256}
9257/**
9258* @param {Uint16Array | Uint32Array} ids
9259* @param {TypedArray} coords
9260* @param {number} i
9261* @param {number} j
9262*/
9263function swapItem(ids, coords, i, j) {
9264	swap(ids, i, j);
9265	swap(coords, 2 * i, 2 * j);
9266	swap(coords, 2 * i + 1, 2 * j + 1);
9267}
9268/**
9269* @param {TypedArray} arr
9270* @param {number} i
9271* @param {number} j
9272*/
9273function swap(arr, i, j) {
9274	const tmp = arr[i];
9275	arr[i] = arr[j];
9276	arr[j] = tmp;
9277}
9278/**
9279* @param {number} ax
9280* @param {number} ay
9281* @param {number} bx
9282* @param {number} by
9283*/
9284function sqDist(ax, ay, bx, by) {
9285	const dx = ax - bx;
9286	const dy = ay - by;
9287	return dx * dx + dy * dy;
9288}
9289//#endregion
9290//#region src/symbol/cross_tile_symbol_index.ts
9291const roundingFactor = 512 / EXTENT / 2;
9292var TileLayerIndex = class {
9293	constructor(tileID, symbolInstances, bucketInstanceId) {
9294		this.tileID = tileID;
9295		this.bucketInstanceId = bucketInstanceId;
9296		this._symbolsByKey = {};
9297		const symbolInstancesByKey = /* @__PURE__ */ new Map();
9298		for (let i = 0; i < symbolInstances.length; i++) {
9299			const symbolInstance = symbolInstances.get(i);
9300			const key = symbolInstance.key;
9301			const instances = symbolInstancesByKey.get(key);
9302			if (instances) instances.push(symbolInstance);
9303			else symbolInstancesByKey.set(key, [symbolInstance]);
9304		}
9305		for (const [key, symbols] of symbolInstancesByKey) {
9306			const entry = {
9307				positions: symbols.map((symbolInstance) => ({
9308					x: Math.floor(symbolInstance.anchorX * roundingFactor),
9309					y: Math.floor(symbolInstance.anchorY * roundingFactor)
9310				})),
9311				crossTileIDs: symbols.map((v) => v.crossTileID)
9312			};
9313			if (entry.positions.length > 128) {
9314				const index = new KDBush(entry.positions.length, 16, Uint16Array);
9315				for (const { x, y } of entry.positions) index.add(x, y);
9316				index.finish();
9317				delete entry.positions;
9318				entry.index = index;
9319			}
9320			this._symbolsByKey[key] = entry;
9321		}
9322	}
9323	getScaledCoordinates(symbolInstance, childTileID) {
9324		const { x: localX, y: localY, z: localZ } = this.tileID.canonical;
9325		const { x, y, z } = childTileID.canonical;
9326		const zDifference = z - localZ;
9327		const scale = roundingFactor / Math.pow(2, zDifference);
9328		const xWorld = (x * EXTENT + symbolInstance.anchorX) * scale;
9329		const yWorld = (y * EXTENT + symbolInstance.anchorY) * scale;
9330		const xOffset = localX * EXTENT * roundingFactor;
9331		const yOffset = localY * EXTENT * roundingFactor;
9332		return {
9333			x: Math.floor(xWorld - xOffset),
9334			y: Math.floor(yWorld - yOffset)
9335		};
9336	}
9337	findMatches(symbolInstances, newTileID, zoomCrossTileIDs) {
9338		const tolerance = this.tileID.canonical.z < newTileID.canonical.z ? 1 : Math.pow(2, this.tileID.canonical.z - newTileID.canonical.z);
9339		for (let i = 0; i < symbolInstances.length; i++) {
9340			const symbolInstance = symbolInstances.get(i);
9341			if (symbolInstance.crossTileID) continue;
9342			const entry = this._symbolsByKey[symbolInstance.key];
9343			if (!entry) continue;
9344			const scaledSymbolCoord = this.getScaledCoordinates(symbolInstance, newTileID);
9345			if (entry.index) this.findMatchesForIndexedEntry(entry, symbolInstance, scaledSymbolCoord, tolerance, zoomCrossTileIDs);
9346			else if (entry.positions) this.findMatchesForNonIndexedEntry(entry, symbolInstance, scaledSymbolCoord, tolerance, zoomCrossTileIDs);
9347		}
9348	}
9349	/**
9350	* Matches a symbol instance against an entry backed by a KDBush spatial index
9351	* (used for keys with more than {@link KDBUSH_THRESHHOLD} symbols).
9352	*
9353	* Matches any symbol with the same key whose coordinates are within one grid
9354	* unit (with a 4px grid, this covers a 12px by 12px area). The lowest-index
9355	* unclaimed candidate is claimed in a single pass rather than sorting the
9356	* whole result set on every query: `range()` can return many candidates where
9357	* symbols are coincident (e.g. stacked point markers), so a per-query sort
9358	* would dominate placement cost on dense layers.
9359	*
9360	* Once a symbol is marked duplicate against a parent symbol, no other symbol
9361	* at the same zoom level may duplicate against the same parent (see issue #5993).
9362	*/
9363	findMatchesForIndexedEntry(entry, symbolInstance, scaledSymbolCoord, tolerance, zoomCrossTileIDs) {
9364		const indexes = entry.index.range(scaledSymbolCoord.x - tolerance, scaledSymbolCoord.y - tolerance, scaledSymbolCoord.x + tolerance, scaledSymbolCoord.y + tolerance);
9365		let bestIndex = Infinity;
9366		for (const candidate of indexes) if (candidate < bestIndex && !zoomCrossTileIDs[entry.crossTileIDs[candidate]]) bestIndex = candidate;
9367		if (bestIndex !== Infinity) {
9368			const crossTileID = entry.crossTileIDs[bestIndex];
9369			zoomCrossTileIDs[crossTileID] = true;
9370			symbolInstance.crossTileID = crossTileID;
9371		}
9372	}
9373	/**
9374	* Matches a symbol instance against an entry that stores its positions as a
9375	* plain array (used for keys with at most {@link KDBUSH_THRESHHOLD} symbols).
9376	*
9377	* Matches any symbol with the same key whose coordinates are within one grid
9378	* unit (with a 4px grid, this covers a 12px by 12px area). Positions are
9379	* stored in symbol-instance order, so the first unclaimed match is also the
9380	* lowest-index one.
9381	*
9382	* Once a symbol is marked duplicate against a parent symbol, no other symbol
9383	* at the same zoom level may duplicate against the same parent (see issue #5993).
9384	*/
9385	findMatchesForNonIndexedEntry(entry, symbolInstance, scaledSymbolCoord, tolerance, zoomCrossTileIDs) {
9386		for (let i = 0; i < entry.positions.length; i++) {
9387			const thisTileSymbol = entry.positions[i];
9388			const crossTileID = entry.crossTileIDs[i];
9389			if (Math.abs(thisTileSymbol.x - scaledSymbolCoord.x) <= tolerance && Math.abs(thisTileSymbol.y - scaledSymbolCoord.y) <= tolerance && !zoomCrossTileIDs[crossTileID]) {
9390				zoomCrossTileIDs[crossTileID] = true;
9391				symbolInstance.crossTileID = crossTileID;
9392				break;
9393			}
9394		}
9395	}
9396	getCrossTileIDsLists() {
9397		return Object.values(this._symbolsByKey).map(({ crossTileIDs }) => crossTileIDs);
9398	}
9399};
9400var CrossTileIDs = class {
9401	constructor() {
9402		this.maxCrossTileID = 0;
9403	}
9404	generate() {
9405		return ++this.maxCrossTileID;
9406	}
9407};
9408var CrossTileSymbolLayerIndex = class {
9409	constructor() {
9410		this.indexes = {};
9411		this.usedCrossTileIDs = {};
9412		this.lng = 0;
9413	}
9414	handleWrapJump(lng) {
9415		const wrapDelta = Math.round((lng - this.lng) / 360);
9416		if (wrapDelta !== 0) for (const zoom in this.indexes) {
9417			const zoomIndexes = this.indexes[zoom];
9418			const newZoomIndex = {};
9419			for (const key in zoomIndexes) {
9420				const index = zoomIndexes[key];
9421				index.tileID = index.tileID.unwrapTo(index.tileID.wrap + wrapDelta);
9422				newZoomIndex[index.tileID.key] = index;
9423			}
9424			this.indexes[zoom] = newZoomIndex;
9425		}
9426		this.lng = lng;
9427	}
9428	addBucket(tileID, bucket, crossTileIDs) {
9429		if (this.indexes[tileID.overscaledZ]?.[tileID.key]) {
9430			if (this.indexes[tileID.overscaledZ][tileID.key].bucketInstanceId === bucket.bucketInstanceId) return false;
9431			else this.removeBucketCrossTileIDs(tileID.overscaledZ, this.indexes[tileID.overscaledZ][tileID.key]);
9432		}
9433		for (let i = 0; i < bucket.symbolInstances.length; i++) {
9434			const symbolInstance = bucket.symbolInstances.get(i);
9435			symbolInstance.crossTileID = 0;
9436		}
9437		this.usedCrossTileIDs[tileID.overscaledZ] ||= {};
9438		const zoomCrossTileIDs = this.usedCrossTileIDs[tileID.overscaledZ];
9439		for (const zoom in this.indexes) {
9440			const zoomIndexes = this.indexes[zoom];
9441			if (Number(zoom) > tileID.overscaledZ) for (const id in zoomIndexes) {
9442				const childIndex = zoomIndexes[id];
9443				if (childIndex.tileID.isChildOf(tileID)) childIndex.findMatches(bucket.symbolInstances, tileID, zoomCrossTileIDs);
9444			}
9445			else {
9446				const parentIndex = zoomIndexes[tileID.scaledTo(Number(zoom)).key];
9447				if (parentIndex) parentIndex.findMatches(bucket.symbolInstances, tileID, zoomCrossTileIDs);
9448			}
9449		}
9450		for (let i = 0; i < bucket.symbolInstances.length; i++) {
9451			const symbolInstance = bucket.symbolInstances.get(i);
9452			if (!symbolInstance.crossTileID) {
9453				symbolInstance.crossTileID = crossTileIDs.generate();
9454				zoomCrossTileIDs[symbolInstance.crossTileID] = true;
9455			}
9456		}
9457		if (this.indexes[tileID.overscaledZ] === void 0) this.indexes[tileID.overscaledZ] = {};
9458		this.indexes[tileID.overscaledZ][tileID.key] = new TileLayerIndex(tileID, bucket.symbolInstances, bucket.bucketInstanceId);
9459		return true;
9460	}
9461	removeBucketCrossTileIDs(zoom, removedBucket) {
9462		for (const crossTileIDs of removedBucket.getCrossTileIDsLists()) for (const crossTileID of crossTileIDs) delete this.usedCrossTileIDs[zoom][crossTileID];
9463	}
9464	removeStaleBuckets(currentIDs) {
9465		let tilesChanged = false;
9466		for (const z in this.indexes) {
9467			const zoomIndexes = this.indexes[z];
9468			for (const tileKey in zoomIndexes) if (!currentIDs[zoomIndexes[tileKey].bucketInstanceId]) {
9469				this.removeBucketCrossTileIDs(z, zoomIndexes[tileKey]);
9470				delete zoomIndexes[tileKey];
9471				tilesChanged = true;
9472			}
9473		}
9474		return tilesChanged;
9475	}
9476};
9477var CrossTileSymbolIndex = class {
9478	constructor() {
9479		this.layerIndexes = {};
9480		this.crossTileIDs = new CrossTileIDs();
9481		this.maxBucketInstanceId = 0;
9482		this.bucketsInCurrentPlacement = {};
9483	}
9484	addLayer(styleLayer, tiles, lng) {
9485		let layerIndex = this.layerIndexes[styleLayer.id];
9486		if (layerIndex === void 0) layerIndex = this.layerIndexes[styleLayer.id] = new CrossTileSymbolLayerIndex();
9487		let symbolBucketsChanged = false;
9488		const currentBucketIDs = {};
9489		layerIndex.handleWrapJump(lng);
9490		for (const tile of tiles) {
9491			const symbolBucket = tile.getBucket(styleLayer);
9492			if (styleLayer.id !== symbolBucket?.layerIds[0]) continue;
9493			if (!symbolBucket.bucketInstanceId) {
9494				this.maxBucketInstanceId += 1;
9495				symbolBucket.bucketInstanceId = this.maxBucketInstanceId;
9496			}
9497			if (layerIndex.addBucket(tile.tileID, symbolBucket, this.crossTileIDs)) symbolBucketsChanged = true;
9498			currentBucketIDs[symbolBucket.bucketInstanceId] = true;
9499		}
9500		if (layerIndex.removeStaleBuckets(currentBucketIDs)) symbolBucketsChanged = true;
9501		return symbolBucketsChanged;
9502	}
9503	pruneUnusedLayers(usedLayers) {
9504		const usedLayerMap = {};
9505		for (const usedLayer of usedLayers) usedLayerMap[usedLayer] = true;
9506		for (const layerId in this.layerIndexes) if (!usedLayerMap[layerId]) delete this.layerIndexes[layerId];
9507	}
9508};
9509//#endregion
9510//#region src/shaders/glsl/_prelude.fragment.glsl.g.ts
9511var _prelude_fragment_glsl_g_default = "#ifdef GL_ES\nprecision mediump float;\n#else\n#if !defined(lowp)\n#define lowp\n#endif\n#if !defined(mediump)\n#define mediump\n#endif\n#if !defined(highp)\n#define highp\n#endif\n#endif\nout highp vec4 fragColor;layout(std140) uniform ProjectionUBO {highp mat4 u_projection_matrix;highp mat4 u_projection_fallback_matrix;highp vec4 u_projection_tile_mercator_coords;highp vec4 u_projection_clipping_plane;highp float u_projection_transition;highp int u_projection_clip_antimeridian;};layout(std140) uniform FrameUBO {highp vec2 u_units_to_pixels;highp vec2 u_world_size;highp float u_camera_to_center_distance;highp float u_symbol_fade_change;highp float u_aspect_ratio;highp float u_device_pixel_ratio;highp vec2 u_viewport_size;highp vec2 u_pixel_extrude_scale;highp float u_pitch;};";
9512//#endregion
9513//#region src/shaders/glsl/_prelude.vertex.glsl.g.ts
9514var _prelude_vertex_glsl_g_default = "#ifdef GL_ES\nprecision highp float;\n#else\n#if !defined(lowp)\n#define lowp\n#endif\n#if !defined(mediump)\n#define mediump\n#endif\n#if !defined(highp)\n#define highp\n#endif\n#endif\nvec2 unpack_float(const float packedValue) {int packedIntValue=int(packedValue);int v0=packedIntValue/256;return vec2(v0,packedIntValue-v0*256);}vec2 unpack_opacity(const uint packedOpacity) {return vec2(float(packedOpacity >> 1u)/127.0,float(packedOpacity & 1u));}vec4 decode_color(const vec2 encodedColor) {return vec4(unpack_float(encodedColor[0])/255.0,unpack_float(encodedColor[1])/255.0\n);}float unpack_mix_vec2(const vec2 packedValue,const float t) {return mix(packedValue[0],packedValue[1],t);}vec4 unpack_mix_color(const vec4 packedColors,const float t) {vec4 minColor=decode_color(vec2(packedColors[0],packedColors[1]));vec4 maxColor=decode_color(vec2(packedColors[2],packedColors[3]));return mix(minColor,maxColor,t);}vec2 get_pattern_pos(const vec2 pixel_coord_upper,const vec2 pixel_coord_lower,const vec2 pattern_size,const float tile_units_to_pixels,const vec2 pos) {vec2 offset=mod(mod(mod(pixel_coord_upper,pattern_size)*256.0,pattern_size)*256.0+pixel_coord_lower,pattern_size);return (tile_units_to_pixels*pos+offset)/pattern_size;}mat3 rotationMatrixFromAxisAngle(vec3 u,float angle) {float c=cos(angle);float s=sin(angle);float c2=1.0-c;return mat3(u.x*u.x*c2+      c,u.x*u.y*c2-u.z*s,u.x*u.z*c2+u.y*s,u.y*u.x*c2+u.z*s,u.y*u.y*c2+    c,u.y*u.z*c2-u.x*s,u.z*u.x*c2-u.y*s,u.z*u.y*c2+u.x*s,u.z*u.z*c2+    c\n);}\n#ifdef TERRAIN3D\nuniform sampler2D u_terrain;uniform highp sampler2D u_depth;layout(std140) uniform TerrainUBO {highp mat4 u_terrain_matrix;highp vec4 u_terrain_unpack;highp float u_terrain_dim;highp float u_terrain_exaggeration;};\n#endif\nconst highp vec4 bitSh=vec4(256.*256.*256.,256.*256.,256.,1.);const highp vec4 bitShifts=vec4(1.)/bitSh;highp float unpack(highp vec4 color) {return dot(color,bitShifts);}highp float depthOpacity(vec3 frag) {\n#ifdef TERRAIN3D\nhighp float d=unpack(texture(u_depth,frag.xy*0.5+0.5))+0.0001-frag.z;return 1.0-max(0.0,min(1.0,-d*500.0));\n#else\nreturn 1.0;\n#endif\n}float calculate_visibility(vec4 pos) {\n#ifdef TERRAIN3D\nvec3 frag=pos.xyz/pos.w;highp float d=depthOpacity(frag);if (d > 0.95) return 1.0;return (d+depthOpacity(frag+vec3(0.0,0.01,0.0)))/2.0;\n#else\nreturn 1.0;\n#endif\n}float ele(ivec2 pos) {\n#ifdef TERRAIN3D\nvec4 rgb=(texelFetch(u_terrain,pos,0)*255.0)*u_terrain_unpack;return rgb.r+rgb.g+rgb.b-u_terrain_unpack.a;\n#else\nreturn 0.0;\n#endif\n}float get_elevation(vec2 pos) {\n#ifdef TERRAIN3D\n#ifdef GLOBE\nif ((pos.y <-32767.5) || (pos.y > 32766.5)) {return 0.0;}\n#endif\nvec2 coord=(u_terrain_matrix*vec4(pos,0.0,1.0)).xy*u_terrain_dim+1.5;vec2 f=fract(coord);ivec2 c=ivec2(floor(coord));ivec2 hi=textureSize(u_terrain,0)-1;float tl=ele(clamp(c,ivec2(0),hi));float tr=ele(clamp(c+ivec2(1,0),ivec2(0),hi));float bl=ele(clamp(c+ivec2(0,1),ivec2(0),hi));float br=ele(clamp(c+ivec2(1,1),ivec2(0),hi));float elevation=mix(mix(tl,tr,f.x),mix(bl,br,f.x),f.y);return elevation*u_terrain_exaggeration;\n#else\nreturn 0.0;\n#endif\n}const float PI=3.141592653589793;\n#define PROJECTION_UBO\nlayout(std140) uniform ProjectionUBO {highp mat4 u_projection_matrix;highp mat4 u_projection_fallback_matrix;highp vec4 u_projection_tile_mercator_coords;highp vec4 u_projection_clipping_plane;highp float u_projection_transition;highp int u_projection_clip_antimeridian;};layout(std140) uniform FrameUBO {highp vec2 u_units_to_pixels;highp vec2 u_world_size;highp float u_camera_to_center_distance;highp float u_symbol_fade_change;highp float u_aspect_ratio;highp float u_device_pixel_ratio;highp vec2 u_viewport_size;highp vec2 u_pixel_extrude_scale;highp float u_pitch;};";
9515//#endregion
9516//#region src/shaders/glsl/background.fragment.glsl.g.ts
9517var background_fragment_glsl_g_default = "uniform vec4 u_color;uniform float u_opacity;void main() {fragColor=u_color*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}";
9518//#endregion
9519//#region src/shaders/glsl/background.vertex.glsl.g.ts
9520var background_vertex_glsl_g_default = "layout(location=0) in vec2 a_pos;void main() {gl_Position=projectTile(a_pos);}";
9521//#endregion
9522//#region src/shaders/glsl/background_pattern.fragment.glsl.g.ts
9523var background_pattern_fragment_glsl_g_default = "uniform vec2 u_pattern_tl_a;uniform vec2 u_pattern_br_a;uniform vec2 u_pattern_tl_b;uniform vec2 u_pattern_br_b;uniform vec2 u_texsize;uniform float u_mix;uniform float u_opacity;uniform sampler2D u_image;in vec2 v_pos_a;in vec2 v_pos_b;void main() {vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(u_pattern_tl_a/u_texsize,u_pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(u_pattern_tl_b/u_texsize,u_pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);fragColor=mix(color1,color2,u_mix)*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}";
9524//#endregion
9525//#region src/shaders/glsl/background_pattern.vertex.glsl.g.ts
9526var background_pattern_vertex_glsl_g_default = "uniform vec2 u_pattern_size_a;uniform vec2 u_pattern_size_b;uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform float u_scale_a;uniform float u_scale_b;uniform float u_tile_units_to_pixels;layout(location=0) in vec2 a_pos;out vec2 v_pos_a;out vec2 v_pos_b;void main() {gl_Position=projectTile(a_pos);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,u_scale_a*u_pattern_size_a,u_tile_units_to_pixels,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,u_scale_b*u_pattern_size_b,u_tile_units_to_pixels,a_pos);}";
9527//#endregion
9528//#region src/shaders/glsl/circle.fragment.glsl.g.ts
9529var circle_fragment_glsl_g_default = "in vec3 v_data;flat in float v_visibility;\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define mediump float radius\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define highp vec4 stroke_color\n#pragma maplibre: define mediump float stroke_width\n#pragma maplibre: define lowp float stroke_opacity\nvoid main() {\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize mediump float radius\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize highp vec4 stroke_color\n#pragma maplibre: initialize mediump float stroke_width\n#pragma maplibre: initialize lowp float stroke_opacity\nvec2 extrude=v_data.xy;float extrude_length=length(extrude);float antialiased_blur=v_data.z;float opacity_t=smoothstep(0.0,antialiased_blur,extrude_length-1.0);float color_t=stroke_width < 0.01 ? 0.0 : smoothstep(antialiased_blur,0.0,extrude_length-radius/(radius+stroke_width));fragColor=v_visibility*opacity_t*mix(color*opacity,stroke_color*stroke_opacity,color_t);const float epsilon=0.5/255.0;if (fragColor.r < epsilon && fragColor.g < epsilon && fragColor.b < epsilon && fragColor.a < epsilon) {discard;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}";
9530//#endregion
9531//#region src/shaders/glsl/circle.vertex.glsl.g.ts
9532var circle_vertex_glsl_g_default = "uniform bool u_scale_with_map;uniform bool u_pitch_with_map;uniform vec2 u_extrude_scale;uniform highp float u_globe_extrude_scale;uniform vec2 u_translate;layout(location=0) in ivec2 a_pos;out vec3 v_data;flat out float v_visibility;\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define mediump float radius\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define highp vec4 stroke_color\n#pragma maplibre: define mediump float stroke_width\n#pragma maplibre: define lowp float stroke_opacity\nvoid main(void) {\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize mediump float radius\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize highp vec4 stroke_color\n#pragma maplibre: initialize mediump float stroke_width\n#pragma maplibre: initialize lowp float stroke_opacity\nivec2 pos_raw=a_pos+32768;vec2 extrude=vec2(pos_raw & 7)/7.0*2.0-1.0;vec2 circle_center=vec2(pos_raw >> 3)+u_translate;float ele=get_elevation(circle_center);v_visibility=calculate_visibility(projectTileWithElevation(circle_center,ele));if (u_pitch_with_map) {\n#ifdef GLOBE\nvec3 center_vector=projectToSphere(circle_center);\n#endif\nfloat angle_scale=u_globe_extrude_scale;vec2 corner_position=circle_center;if (u_scale_with_map) {angle_scale*=(radius+stroke_width);corner_position+=extrude*u_extrude_scale*(radius+stroke_width);} else {\n#ifdef GLOBE\nvec4 projected_center=interpolateProjection(circle_center,center_vector,ele);\n#else\nvec4 projected_center=projectTileWithElevation(circle_center,ele);\n#endif\ncorner_position+=extrude*u_extrude_scale*(radius+stroke_width)*(projected_center.w/u_camera_to_center_distance);angle_scale*=(radius+stroke_width)*(projected_center.w/u_camera_to_center_distance);}\n#ifdef GLOBE\nvec2 angles=extrude*angle_scale;vec3 corner_vector=globeRotateVector(center_vector,angles);gl_Position=interpolateProjection(corner_position,corner_vector,ele);\n#else\ngl_Position=projectTileWithElevation(corner_position,ele);\n#endif\n} else {gl_Position=projectTileWithElevation(circle_center,ele);if (gl_Position.z/gl_Position.w > 1.0) {gl_Position.xy=vec2(10000.0);}if (u_scale_with_map) {gl_Position.xy+=extrude*(radius+stroke_width)*u_extrude_scale*u_camera_to_center_distance;} else {gl_Position.xy+=extrude*(radius+stroke_width)*u_extrude_scale*gl_Position.w;}}float antialiasblur=-max(1.0/u_device_pixel_ratio/(radius+stroke_width),blur);v_data=vec3(extrude.x,extrude.y,antialiasblur);}";
9533//#endregion
9534//#region src/shaders/glsl/clipping_mask.fragment.glsl.g.ts
9535var clipping_mask_fragment_glsl_g_default = "void main() {fragColor=vec4(1.0);}";
9536//#endregion
9537//#region src/shaders/shaders.ts
9538const shaders = {
9539	prelude: prepare(_prelude_fragment_glsl_g_default, _prelude_vertex_glsl_g_default),
9540	projectionMercator: prepare("\nvoid clipAntimeridian() {}", "float projectLineThickness(float tileY) {return 1.0;}float projectCircleRadius(float tileY) {return 1.0;}vec4 projectTile(vec2 p) {vec4 result=u_projection_matrix*vec4(p,0.0,1.0);return result;}vec4 projectTile(vec2 p,vec2 rawPos) {vec4 result=u_projection_matrix*vec4(p,0.0,1.0);if (rawPos.y <-32767.5 || rawPos.y > 32766.5) {result.z=-10000000.0;}return result;}vec4 projectTileWithElevation(vec2 posInTile,float elevation) {return u_projection_matrix*vec4(posInTile,elevation,1.0);}vec4 projectTileFor3D(vec2 posInTile,float elevation) {return projectTileWithElevation(posInTile,elevation);}"),
9541	projectionGlobe: prepare("in highp float v_projection_tile_x;void clipAntimeridian() {if (u_projection_clip_antimeridian !=0 && (v_projection_tile_x < 0.0 || v_projection_tile_x >=8192.0)) {discard;}}", "#define GLOBE_RADIUS 6371008.8\n#ifndef PROJECTION_UBO\nuniform highp vec4 u_projection_tile_mercator_coords;uniform highp vec4 u_projection_clipping_plane;uniform highp float u_projection_transition;uniform mat4 u_projection_fallback_matrix;\n#endif\nout highp float v_projection_tile_x;vec3 globeRotateVector(vec3 vec,vec2 angles) {vec3 axisRight=vec3(vec.z,0.0,-vec.x);vec3 axisUp=cross(axisRight,vec);axisRight=normalize(axisRight);axisUp=normalize(axisUp);vec2 t=tan(angles);return normalize(vec+axisRight*t.x+axisUp*t.y);}mat3 globeGetRotationMatrix(vec3 spherePos) {vec3 axisRight=vec3(spherePos.z,0.0,-spherePos.x);vec3 axisDown=cross(axisRight,spherePos);axisRight=normalize(axisRight);axisDown=normalize(axisDown);return mat3(axisRight,axisDown,spherePos\n);}float circumferenceRatioAtTileY(float tileY) {float mercator_pos_y=u_projection_tile_mercator_coords.y+u_projection_tile_mercator_coords.w*tileY;float t=exp(PI-(mercator_pos_y*PI*2.0));return (2.0*t)/(t*t+1.0);}float projectLineThickness(float tileY) {float thickness=1.0/circumferenceRatioAtTileY(tileY);if (u_projection_transition < 0.999) {return mix(1.0,thickness,u_projection_transition);} else {return thickness;}}vec3 projectToSphere(vec2 translatedPos,vec2 rawPos) {vec2 mercator_pos=u_projection_tile_mercator_coords.xy+u_projection_tile_mercator_coords.zw*translatedPos;float spherical_x=mercator_pos.x*PI*2.0+PI;float t=exp(PI-(mercator_pos.y*PI*2.0));float t2=t*t;float denom=t2+1.0;float sin_sy=(t2-1.0)/denom;float cos_sy=(2.0*t)/denom;vec3 pos=vec3(sin(spherical_x)*cos_sy,sin_sy,cos(spherical_x)*cos_sy\n);if (rawPos.y <-32767.5) {pos=vec3(0.0,1.0,0.0);}if (rawPos.y > 32766.5) {pos=vec3(0.0,-1.0,0.0);}return pos;}vec3 projectToSphere(vec2 posInTile) {return projectToSphere(posInTile,vec2(0.0,0.0));}float globeComputeClippingZ(vec3 spherePos) {return (1.0-(dot(spherePos,u_projection_clipping_plane.xyz)+u_projection_clipping_plane.w));}vec4 interpolateProjection(vec2 posInTile,vec3 spherePos,float elevation) {v_projection_tile_x=posInTile.x;vec3 elevatedPos=spherePos*(1.0+elevation/GLOBE_RADIUS);vec4 globePosition=u_projection_matrix*vec4(elevatedPos,1.0);globePosition.z=globeComputeClippingZ(elevatedPos)*globePosition.w;if (u_projection_transition > 0.999) {return globePosition;}vec4 flatPosition=u_projection_fallback_matrix*vec4(posInTile,elevation,1.0);const float z_globeness_threshold=0.2;vec4 result=globePosition;result.z=mix(0.0,globePosition.z,clamp((u_projection_transition-z_globeness_threshold)/(1.0-z_globeness_threshold),0.0,1.0));result.xyw=mix(flatPosition.xyw,globePosition.xyw,u_projection_transition);if ((posInTile.y <-32767.5) || (posInTile.y > 32766.5)) {result=globePosition;const float poles_hidden_anim_percentage=0.02;result.z=mix(globePosition.z,100.0,pow(max((1.0-u_projection_transition)/poles_hidden_anim_percentage,0.0),8.0));}return result;}vec4 interpolateProjectionFor3D(vec2 posInTile,vec3 spherePos,float elevation) {v_projection_tile_x=posInTile.x;vec3 elevatedPos=spherePos*(1.0+elevation/GLOBE_RADIUS);vec4 globePosition=u_projection_matrix*vec4(elevatedPos,1.0);if (u_projection_transition > 0.999) {return globePosition;}vec4 fallbackPosition=u_projection_fallback_matrix*vec4(posInTile,elevation,1.0);return mix(fallbackPosition,globePosition,u_projection_transition);}vec4 projectTile(vec2 posInTile) {return interpolateProjection(posInTile,projectToSphere(posInTile),0.0);}vec4 projectTile(vec2 posInTile,vec2 rawPos) {return interpolateProjection(posInTile,projectToSphere(posInTile,rawPos),0.0);}vec4 projectTileWithElevation(vec2 posInTile,float elevation) {return interpolateProjection(posInTile,projectToSphere(posInTile),elevation);}vec4 projectTileFor3D(vec2 posInTile,float elevation) {vec3 spherePos=projectToSphere(posInTile,posInTile);return interpolateProjectionFor3D(posInTile,spherePos,elevation);}"),
9542	background: prepare(background_fragment_glsl_g_default, background_vertex_glsl_g_default),
9543	backgroundPattern: prepare(background_pattern_fragment_glsl_g_default, background_pattern_vertex_glsl_g_default),
9544	circle: prepare(circle_fragment_glsl_g_default, circle_vertex_glsl_g_default),
9545	clippingMask: prepare(clipping_mask_fragment_glsl_g_default, "layout(location=0) in vec2 a_pos;void main() {gl_Position=projectTile(a_pos);}"),
9546	heatmap: prepare("uniform highp float u_intensity;in vec2 v_extrude;\n#pragma maplibre: define highp float weight\n#define GAUSS_COEF 0.3989422804014327\nvoid main() {\n#pragma maplibre: initialize highp float weight\nfloat d=-0.5*3.0*3.0*dot(v_extrude,v_extrude);float val=weight*u_intensity*GAUSS_COEF*exp(d);fragColor=vec4(val,1.0,1.0,1.0);\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform float u_extrude_scale;uniform float u_opacity;uniform float u_intensity;uniform highp float u_globe_extrude_scale;layout(location=0) in ivec2 a_pos;out vec2 v_extrude;\n#pragma maplibre: define highp float weight\n#pragma maplibre: define mediump float radius\nconst highp float ZERO=1.0/255.0/16.0;\n#define GAUSS_COEF 0.3989422804014327\nvoid main(void) {\n#pragma maplibre: initialize highp float weight\n#pragma maplibre: initialize mediump float radius\nivec2 pos_raw=a_pos+32768;vec2 unscaled_extrude=vec2(pos_raw & 7)/7.0*2.0-1.0;float S=sqrt(-2.0*log(ZERO/weight/u_intensity/GAUSS_COEF))/3.0;v_extrude=S*unscaled_extrude;vec2 extrude=v_extrude*radius*u_extrude_scale;vec2 circle_center=vec2(pos_raw >> 3);\n#ifdef GLOBE\nvec2 angles=v_extrude*radius*u_globe_extrude_scale;vec3 center_vector=projectToSphere(circle_center);vec3 corner_vector=globeRotateVector(center_vector,angles);gl_Position=interpolateProjection(circle_center+extrude,corner_vector,0.0);\n#else\ngl_Position=projectTileFor3D(circle_center+extrude,get_elevation(circle_center));\n#endif\n}"),
9547	heatmapTexture: prepare("uniform sampler2D u_image;uniform sampler2D u_color_ramp;uniform float u_opacity;in vec2 v_pos;void main() {float t=texture(u_image,v_pos).r;vec4 color=texture(u_color_ramp,vec2(t,0.5));fragColor=color*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(0.0);\n#endif\n}", "uniform mat4 u_matrix;layout(location=0) in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos*u_world_size,0,1);v_pos.x=a_pos.x;v_pos.y=1.0-a_pos.y;}"),
9548	collisionBox: prepare("flat in float v_placed;flat in float v_notUsed;void main() {float alpha=0.5;fragColor=vec4(1.0,0.0,0.0,1.0)*alpha;if (v_placed > 0.5) {fragColor=vec4(0.0,0.0,1.0,0.5)*alpha;}if (v_notUsed > 0.5) {fragColor*=.1;}}", "layout(location=0) in vec2 a_anchor_pos;layout(location=1) in vec2 a_placed;layout(location=2) in vec2 a_box_real;flat out float v_placed;flat out float v_notUsed;void main() {gl_Position=projectTileWithElevation(a_anchor_pos,get_elevation(a_anchor_pos));gl_Position.xy=((a_box_real+0.5)*u_pixel_extrude_scale*2.0-1.0)*vec2(1.0,-1.0)*gl_Position.w;if (gl_Position.z/gl_Position.w < 1.1) {gl_Position.z=0.5;}v_placed=a_placed.x;v_notUsed=a_placed.y;}"),
9549	collisionCircle: prepare("flat in float v_radius;in vec2 v_extrude;flat in float v_collision;void main() {float alpha=0.5;float stroke_radius=0.9;float distance_to_center=length(v_extrude);float distance_to_edge=abs(distance_to_center-v_radius);float opacity_t=smoothstep(-stroke_radius,0.0,-distance_to_edge);vec4 color=mix(vec4(0.0,0.0,1.0,0.5),vec4(1.0,0.0,0.0,1.0),v_collision);fragColor=color*alpha*opacity_t;}", "layout(location=0) in vec2 a_pos;layout(location=1) in float a_radius;layout(location=2) in vec2 a_flags;flat out float v_radius;out vec2 v_extrude;flat out float v_collision;void main() {float radius=a_radius;float collision=a_flags.x;float vertexIdx=a_flags.y;vec2 quadVertexOffset=vec2(mix(-1.0,1.0,float(vertexIdx >=2.0)),mix(-1.0,1.0,float(vertexIdx >=1.0 && vertexIdx <=2.0)));vec2 quadVertexExtent=quadVertexOffset*radius;float padding_factor=1.2;v_radius=radius;v_extrude=quadVertexExtent*padding_factor;v_collision=collision;gl_Position=vec4((a_pos/u_viewport_size*2.0-1.0)*vec2(1.0,-1.0),0.0,1.0)+vec4(quadVertexExtent*padding_factor/u_viewport_size*2.0,0.0,0.0);}"),
9550	colorRelief: prepare("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform sampler2D u_image;uniform vec4 u_unpack;uniform sampler2D u_elevation_stops;uniform sampler2D u_color_stops;uniform int u_color_ramp_size;uniform float u_opacity;in vec2 v_pos;float getElevation(vec2 coord) {vec4 data=texture(u_image,coord)*255.0;data.a=-1.0;return dot(data,u_unpack);}float getElevationStop(int stop) {vec4 data=texelFetch(u_elevation_stops,ivec2(stop,0),0)*255.0;data.a=-1.0;return dot(data,u_unpack);}void main() {float el=getElevation(v_pos);int r=(u_color_ramp_size-1);int l=0;float el_l=getElevationStop(l);float el_r=getElevationStop(r);while(r-l > 1){int m=(r+l)/2;float el_m=getElevationStop(m);if(el < el_m){r=m;el_r=el_m;}else\n{l=m;el_l=el_m;}}float x=(float(l)+(el-el_l)/(el_r-el_l)+0.5)/float(u_color_ramp_size);fragColor=u_opacity*texture(u_color_stops,vec2(x,0));\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec2 u_dimension;layout(location=0) in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=projectTile(a_pos,a_pos);highp vec2 epsilon=2.0/u_dimension;float scale=(u_dimension.x-4.0)/u_dimension.x;v_pos=(a_pos/8192.0)*scale+epsilon;if (a_pos.y <-32767.5) {v_pos.y=0.0;}if (a_pos.y > 32766.5) {v_pos.y=1.0;}}"),
9551	debug: prepare("uniform highp vec4 u_color;uniform sampler2D u_overlay;in vec2 v_uv;void main() {vec4 overlay_color=texture(u_overlay,v_uv);fragColor=mix(u_color,overlay_color,overlay_color.a);}", "layout(location=0) in vec2 a_pos;out vec2 v_uv;uniform float u_overlay_scale;void main() {v_uv=a_pos/8192.0;gl_Position=projectTileWithElevation(a_pos*u_overlay_scale,get_elevation(a_pos));}"),
9552	depth: prepare(clipping_mask_fragment_glsl_g_default, "layout(location=0) in vec2 a_pos;void main() {\n#ifdef GLOBE\ngl_Position=projectTileFor3D(a_pos,0.0);\n#else\ngl_Position=u_projection_matrix*vec4(a_pos,0.0,1.0);\n#endif\n}"),
9553	fill: prepare("#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize lowp float opacity\nfragColor=color*opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec2 u_fill_translate;layout(location=0) in vec2 a_pos;\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize lowp float opacity\nif (opacity < 0.01) {gl_Position=vec4(-2.0,-2.0,-2.0,1.0);return;}gl_Position=projectTile(a_pos+u_fill_translate,a_pos);}"),
9554	fillOutline: prepare("in vec2 v_pos;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma maplibre: define highp vec4 outline_color\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize highp vec4 outline_color\n#pragma maplibre: initialize lowp float opacity\nfloat dist=length(v_pos-gl_FragCoord.xy);float alpha=1.0-smoothstep(0.0,1.0,dist);fragColor=outline_color*(alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec2 u_fill_translate;layout(location=0) in vec2 a_pos;out vec2 v_pos;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma maplibre: define highp vec4 outline_color\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize highp vec4 outline_color\n#pragma maplibre: initialize lowp float opacity\nif (opacity < 0.01) {gl_Position=vec4(-2.0,-2.0,-2.0,1.0);return;}gl_Position=projectTile(a_pos+u_fill_translate,a_pos);v_pos=(gl_Position.xy/gl_Position.w+1.0)/2.0*u_world_size;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n}"),
9555	fillOutlinePattern: prepare("uniform vec2 u_texsize;uniform sampler2D u_image;uniform float u_fade;uniform bool u_sdf_pattern;in vec2 v_pos_a;in vec2 v_pos_b;in vec2 v_pos;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\nvoid main() {\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);float dist=length(v_pos-gl_FragCoord.xy);float alpha=1.0-smoothstep(0.0,1.0,dist);if (u_sdf_pattern) {highp float sdf_edge=(256.0-64.0)/256.0;highp float sdf_gamma_a=max(fwidth(color1.a)*0.5,1.0/255.0/16.0);highp float sdf_gamma_b=max(fwidth(color2.a)*0.5,1.0/255.0/16.0);float sdf_alpha_a=smoothstep(sdf_edge-sdf_gamma_a,sdf_edge+sdf_gamma_a,color1.a);float sdf_alpha_b=smoothstep(sdf_edge-sdf_gamma_b,sdf_edge+sdf_gamma_b,color2.a);fragColor=mix(color*sdf_alpha_a,color*sdf_alpha_b,u_fade)*alpha*opacity;} else {fragColor=mix(color1,color2,u_fade)*alpha*opacity;}\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform vec3 u_scale;uniform vec2 u_fill_translate;layout(location=0) in vec2 a_pos;out vec2 v_pos_a;out vec2 v_pos_b;out vec2 v_pos;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\n#pragma maplibre: define lowp float pixel_ratio_from\n#pragma maplibre: define lowp float pixel_ratio_to\nvoid main() {\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\n#pragma maplibre: initialize lowp float pixel_ratio_from\n#pragma maplibre: initialize lowp float pixel_ratio_to\nif (opacity < 0.01) {gl_Position=vec4(-2.0,-2.0,-2.0,1.0);return;}vec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;gl_Position=projectTile(a_pos+u_fill_translate,a_pos);vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileRatio,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileRatio,a_pos);v_pos=(gl_Position.xy/gl_Position.w+1.0)/2.0*u_world_size;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n}"),
9556	fillPattern: prepare("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform vec2 u_texsize;uniform float u_fade;uniform bool u_sdf_pattern;uniform sampler2D u_image;in vec2 v_pos_a;in vec2 v_pos_b;\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\nvoid main() {\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);if (u_sdf_pattern) {highp float sdf_edge=(256.0-64.0)/256.0;highp float sdf_gamma_a=max(fwidth(color1.a)*0.5,1.0/255.0/16.0);highp float sdf_gamma_b=max(fwidth(color2.a)*0.5,1.0/255.0/16.0);float sdf_alpha_a=smoothstep(sdf_edge-sdf_gamma_a,sdf_edge+sdf_gamma_a,color1.a);float sdf_alpha_b=smoothstep(sdf_edge-sdf_gamma_b,sdf_edge+sdf_gamma_b,color2.a);fragColor=mix(color*sdf_alpha_a,color*sdf_alpha_b,u_fade)*opacity;} else {fragColor=mix(color1,color2,u_fade)*opacity;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform vec3 u_scale;uniform vec2 u_fill_translate;layout(location=0) in vec2 a_pos;out vec2 v_pos_a;out vec2 v_pos_b;\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\n#pragma maplibre: define lowp float pixel_ratio_from\n#pragma maplibre: define lowp float pixel_ratio_to\nvoid main() {\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\n#pragma maplibre: initialize lowp float pixel_ratio_from\n#pragma maplibre: initialize lowp float pixel_ratio_to\nif (opacity < 0.01) {gl_Position=vec4(-2.0,-2.0,-2.0,1.0);return;}vec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;gl_Position=projectTile(a_pos+u_fill_translate,a_pos);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileZoomRatio,a_pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileZoomRatio,a_pos);}"),
9557	fillExtrusion: prepare("in vec4 v_color;void main() {fragColor=v_color;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec3 u_lightcolor;uniform lowp vec3 u_lightpos;uniform lowp vec3 u_lightpos_globe;uniform lowp float u_lightintensity;uniform float u_vertical_gradient;uniform lowp float u_opacity;uniform vec2 u_fill_translate;layout(location=0) in vec2 a_pos;layout(location=1) in ivec4 a_normal_ed;\n#ifdef TERRAIN3D\nlayout(location=2) in vec2 a_centroid;\n#endif\nout vec4 v_color;\n#pragma maplibre: define highp float base\n#pragma maplibre: define highp float height\n#pragma maplibre: define highp vec4 color\nvoid main() {\n#pragma maplibre: initialize highp float base\n#pragma maplibre: initialize highp float height\n#pragma maplibre: initialize highp vec4 color\nvec3 normal=vec3(a_normal_ed.xyz);float gradient_base=max(0.0,base);float gradient_height=max(0.0,height);\n#ifdef TERRAIN3D\nfloat height_terrain3d_offset=get_elevation(a_centroid);float base_terrain3d_offset=height_terrain3d_offset-(base > 0.0 ? 0.0 : 10.0);\n#else\nfloat height_terrain3d_offset=0.0;float base_terrain3d_offset=0.0;\n#endif\nbase=max(0.0,base)+base_terrain3d_offset;height=max(0.0,height)+height_terrain3d_offset;float t=float(a_normal_ed.x & 1);float elevation=t > 0.0 ? height : base;vec2 posInTile=a_pos+u_fill_translate;\n#ifdef GLOBE\nvec3 spherePos=projectToSphere(posInTile,a_pos);gl_Position=interpolateProjectionFor3D(posInTile,spherePos,elevation);\n#else\ngl_Position=u_projection_matrix*vec4(posInTile,elevation,1.0);\n#endif\nfloat colorvalue=color.r*0.2126+color.g*0.7152+color.b*0.0722;v_color=vec4(0.0,0.0,0.0,1.0);vec4 ambientlight=vec4(0.03,0.03,0.03,1.0);color+=ambientlight;vec3 normalForLighting=normal/16384.0;float directional=clamp(dot(normalForLighting,u_lightpos),0.0,1.0);\n#ifdef GLOBE\nmat3 rotMatrix=globeGetRotationMatrix(spherePos);normalForLighting=rotMatrix*normalForLighting;directional=mix(directional,clamp(dot(normalForLighting,u_lightpos_globe),0.0,1.0),u_projection_transition);\n#endif\ndirectional=mix((1.0-u_lightintensity),max((1.0-colorvalue+u_lightintensity),1.0),directional);if (normal.y !=0.0) {directional*=((1.0-u_vertical_gradient)+(u_vertical_gradient*clamp((t+gradient_base)*pow(gradient_height/150.0,0.5),mix(0.7,0.98,1.0-u_lightintensity),1.0)));}v_color.r+=clamp(color.r*directional*u_lightcolor.r,mix(0.0,0.3,1.0-u_lightcolor.r),1.0);v_color.g+=clamp(color.g*directional*u_lightcolor.g,mix(0.0,0.3,1.0-u_lightcolor.g),1.0);v_color.b+=clamp(color.b*directional*u_lightcolor.b,mix(0.0,0.3,1.0-u_lightcolor.b),1.0);v_color*=u_opacity;}"),
9558	fillExtrusionPattern: prepare("uniform vec2 u_texsize;uniform float u_fade;uniform sampler2D u_image;in vec2 v_pos_a;in vec2 v_pos_b;in vec4 v_lighting;\n#pragma maplibre: define lowp float base\n#pragma maplibre: define lowp float height\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\n#pragma maplibre: define lowp float pixel_ratio_from\n#pragma maplibre: define lowp float pixel_ratio_to\nvoid main() {\n#pragma maplibre: initialize lowp float base\n#pragma maplibre: initialize lowp float height\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\n#pragma maplibre: initialize lowp float pixel_ratio_from\n#pragma maplibre: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;vec2 imagecoord=mod(v_pos_a,1.0);vec2 pos=mix(pattern_tl_a/u_texsize,pattern_br_a/u_texsize,imagecoord);vec4 color1=texture(u_image,pos);vec2 imagecoord_b=mod(v_pos_b,1.0);vec2 pos2=mix(pattern_tl_b/u_texsize,pattern_br_b/u_texsize,imagecoord_b);vec4 color2=texture(u_image,pos2);vec4 mixedColor=mix(color1,color2,u_fade);fragColor=mixedColor*v_lighting;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec2 u_pixel_coord_upper;uniform vec2 u_pixel_coord_lower;uniform float u_height_factor;uniform vec3 u_scale;uniform float u_vertical_gradient;uniform lowp float u_opacity;uniform vec2 u_fill_translate;uniform vec3 u_lightcolor;uniform lowp vec3 u_lightpos;uniform lowp vec3 u_lightpos_globe;uniform lowp float u_lightintensity;layout(location=0) in vec2 a_pos;layout(location=1) in ivec4 a_normal_ed;\n#ifdef TERRAIN3D\nlayout(location=2) in vec2 a_centroid;\n#endif\n#ifdef GLOBE\nout vec3 v_sphere_pos;\n#endif\nout vec2 v_pos_a;out vec2 v_pos_b;out vec4 v_lighting;\n#pragma maplibre: define lowp float base\n#pragma maplibre: define lowp float height\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\n#pragma maplibre: define lowp float pixel_ratio_from\n#pragma maplibre: define lowp float pixel_ratio_to\nvoid main() {\n#pragma maplibre: initialize lowp float base\n#pragma maplibre: initialize lowp float height\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\n#pragma maplibre: initialize lowp float pixel_ratio_from\n#pragma maplibre: initialize lowp float pixel_ratio_to\nvec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec3 normal=vec3(a_normal_ed.xyz);float edgedistance=float(a_normal_ed.w);vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;float gradient_base=max(0.0,base);float gradient_height=max(0.0,height);\n#ifdef TERRAIN3D\nfloat height_terrain3d_offset=get_elevation(a_centroid);float base_terrain3d_offset=height_terrain3d_offset-(base > 0.0 ? 0.0 : 10.0);\n#else\nfloat height_terrain3d_offset=0.0;float base_terrain3d_offset=0.0;\n#endif\nbase=max(0.0,base)+base_terrain3d_offset;height=max(0.0,height)+height_terrain3d_offset;float t=float(a_normal_ed.x & 1);float elevation=t > 0.0 ? height : base;vec2 posInTile=a_pos+u_fill_translate;\n#ifdef GLOBE\nvec3 spherePos=projectToSphere(posInTile,a_pos);vec3 elevatedPos=spherePos*(1.0+elevation/GLOBE_RADIUS);v_sphere_pos=elevatedPos;gl_Position=interpolateProjectionFor3D(posInTile,spherePos,elevation);\n#else\ngl_Position=u_projection_matrix*vec4(posInTile,elevation,1.0);\n#endif\nvec2 pos=a_normal_ed.x==1 && a_normal_ed.y==0 && a_normal_ed.z==16384\n? a_pos\n: vec2(edgedistance,elevation*u_height_factor);v_pos_a=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,fromScale*display_size_a,tileRatio,pos);v_pos_b=get_pattern_pos(u_pixel_coord_upper,u_pixel_coord_lower,toScale*display_size_b,tileRatio,pos);v_lighting=vec4(0.0,0.0,0.0,1.0);float directional=clamp(dot(normal/16383.0,u_lightpos),0.0,1.0);directional=mix((1.0-u_lightintensity),max((0.5+u_lightintensity),1.0),directional);if (normal.y !=0.0) {directional*=((1.0-u_vertical_gradient)+(u_vertical_gradient*clamp((t+gradient_base)*pow(gradient_height/150.0,0.5),mix(0.7,0.98,1.0-u_lightintensity),1.0)));}v_lighting.rgb+=clamp(directional*u_lightcolor,mix(vec3(0.0),vec3(0.3),1.0-u_lightcolor),vec3(1.0));v_lighting*=u_opacity;}"),
9559	hillshadePrepare: prepare("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform sampler2D u_image;in vec2 v_pos;uniform vec2 u_dimension;uniform float u_zoom;uniform vec4 u_unpack;float getElevation(ivec2 texel) {vec4 data=texelFetch(u_image,texel,0)*255.0;data.a=-1.0;return dot(data,u_unpack);}void main() {ivec2 pos=ivec2(gl_FragCoord.xy)+ivec2(1);float tileSize=u_dimension.x-4.0;float a=getElevation(pos+ivec2(-1,-1));float b=getElevation(pos+ivec2(0,-1));float c=getElevation(pos+ivec2(1,-1));float d=getElevation(pos+ivec2(-1,0));float e=getElevation(pos);float f=getElevation(pos+ivec2(1,0));float g=getElevation(pos+ivec2(-1,1));float h=getElevation(pos+ivec2(0,1));float i=getElevation(pos+ivec2(1,1));float exaggerationFactor=u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3;float exaggeration=u_zoom < 15.0 ? (u_zoom-15.0)*exaggerationFactor : 0.0;vec2 deriv=vec2((c+f+f+i)-(a+d+d+g),(g+h+h+i)-(a+b+b+c))*tileSize/p
vendor: 6,984 bytes, lines 9559-9561
9559ow(2.0,exaggeration+(28.2562-u_zoom));fragColor=clamp(vec4(deriv.x/8.0+0.5,deriv.y/8.0+0.5,1.0,1.0),0.0,1.0);\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform mat4 u_matrix;uniform vec2 u_dimension;layout(location=0) in vec2 a_pos;layout(location=1) in vec2 a_texture_pos;out vec2 v_pos;void main() {gl_Position=u_matrix*vec4(a_pos,0,1);highp vec2 epsilon=1.0/u_dimension;float scale=(u_dimension.x-2.0)/u_dimension.x;v_pos=(a_texture_pos/8192.0)*scale+epsilon;}"),
9560	hillshade: prepare("uniform sampler2D u_image;in highp vec2 v_pos;uniform vec2 u_latrange;uniform float u_exaggeration;uniform vec4 u_accent;uniform int u_method;uniform float u_altitudes[NUM_ILLUMINATION_SOURCES];uniform float u_azimuths[NUM_ILLUMINATION_SOURCES];uniform vec4 u_shadows[NUM_ILLUMINATION_SOURCES];uniform vec4 u_highlights[NUM_ILLUMINATION_SOURCES];\n#define PI 3.141592653589793\n#define STANDARD 0\n#define COMBINED 1\n#define IGOR 2\n#define MULTIDIRECTIONAL 3\n#define BASIC 4\nfloat get_aspect(vec2 deriv){return deriv.x !=0.0 ? atan(deriv.y,-deriv.x) : PI/2.0*(deriv.y > 0.0 ? 1.0 :-1.0);}void igor_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float aspect=get_aspect(deriv);float azimuth=u_azimuths[0]+PI;float slope_stength=atan(length(deriv))*2.0/PI;float aspect_strength=1.0-abs(mod((aspect+azimuth)/PI+0.5,2.0)-1.0);float shadow_strength=slope_stength*aspect_strength;float highlight_strength=slope_stength*(1.0-aspect_strength);fragColor=u_shadows[0]*shadow_strength+u_highlights[0]*highlight_strength;}void standard_hillshade(vec2 deriv){float azimuth=u_azimuths[0]+PI;float slope=atan(0.625*length(deriv));float aspect=get_aspect(deriv);float intensity=u_exaggeration;float base=1.875-intensity*1.75;float maxValue=0.5*PI;float scaledSlope=intensity !=0.5 ? ((pow(base,slope)-1.0)/(pow(base,maxValue)-1.0))*maxValue : slope;float accent=cos(scaledSlope);vec4 accent_color=(1.0-accent)*u_accent*clamp(intensity*2.0,0.0,1.0);float shade=abs(mod((aspect+azimuth)/PI+0.5,2.0)-1.0);vec4 shade_color=mix(u_shadows[0],u_highlights[0],shade)*sin(scaledSlope)*clamp(intensity*2.0,0.0,1.0);fragColor=accent_color*(1.0-shade_color.a)+shade_color;}void basic_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float azimuth=u_azimuths[0]+PI;float cos_az=cos(azimuth);float sin_az=sin(azimuth);float cos_alt=cos(u_altitudes[0]);float sin_alt=sin(u_altitudes[0]);float cang=(sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv));float shade=clamp(cang,0.0,1.0);if(shade > 0.5){fragColor=u_highlights[0]*(2.0*shade-1.0);}else\n{fragColor=u_shadows[0]*(1.0-2.0*shade);}}void multidirectional_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;fragColor=vec4(0,0,0,0);for(int i=0; i < NUM_ILLUMINATION_SOURCES; i++){float cos_alt=cos(u_altitudes[i]);float sin_alt=sin(u_altitudes[i]);float cos_az=-cos(u_azimuths[i]);float sin_az=-sin(u_azimuths[i]);float cang=(sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv));float shade=clamp(cang,0.0,1.0);if(shade > 0.5){fragColor+=u_highlights[i]*(2.0*shade-1.0)/float(NUM_ILLUMINATION_SOURCES);}else\n{fragColor+=u_shadows[i]*(1.0-2.0*shade)/float(NUM_ILLUMINATION_SOURCES);}}}void combined_hillshade(vec2 deriv){deriv=deriv*u_exaggeration*2.0;float azimuth=u_azimuths[0]+PI;float cos_az=cos(azimuth);float sin_az=sin(azimuth);float cos_alt=cos(u_altitudes[0]);float sin_alt=sin(u_altitudes[0]);float cang=acos((sin_alt-(deriv.y*cos_az*cos_alt-deriv.x*sin_az*cos_alt))/sqrt(1.0+dot(deriv,deriv)));cang=clamp(cang,0.0,PI/2.0);float shade=cang*atan(length(deriv))*4.0/PI/PI;float highlight=(PI/2.0-cang)*atan(length(deriv))*4.0/PI/PI;fragColor=u_shadows[0]*shade+u_highlights[0]*highlight;}void main() {highp vec2 size=vec2(textureSize(u_image,0));highp vec2 texturePos=(v_pos*(size-2.0)+1.0)/size;vec4 pixel=texture(u_image,texturePos);float scaleFactor=cos(radians((u_latrange[0]-u_latrange[1])*(1.0-v_pos.y)+u_latrange[1]));vec2 deriv=((pixel.rg*8.0)-4.0)/scaleFactor;if (u_method==BASIC) {basic_hillshade(deriv);} else if (u_method==COMBINED) {combined_hillshade(deriv);} else if (u_method==IGOR) {igor_hillshade(deriv);} else if (u_method==MULTIDIRECTIONAL) {multidirectional_hillshade(deriv);} else if (u_method==STANDARD) {standard_hillshade(deriv);} else {standard_hillshade(deriv);}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform mat4 u_matrix;layout(location=0) in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=projectTile(a_pos,a_pos);v_pos=a_pos/8192.0;if (a_pos.y <-32767.5) {v_pos.y=0.0;}if (a_pos.y > 32766.5) {v_pos.y=1.0;}}"),
9561	line: prepare("flat in vec2 v_width2;in vec2 v_normal;in float v_gamma_scale;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\nclipAntimeridian();float dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);fragColor=color*(alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "\n#define scale 0.015873016\nlayout(location=0) in ivec2 a_pos_normal;layout(location=1) in uvec4 a_data;uniform vec2 u_translation;uniform mediump float u_ratio;out vec2 v_normal;flat out vec2 v_width2;out float v_gamma_scale;out highp float v_linesofar;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define mediump float gapwidth\n#pragma maplibre: define lowp float offset\n#pragma maplibre: define mediump float width\nvoid main() {\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize mediump float gapwidth\n#pragma maplibre: initialize lowp float offset\n#pragma maplibre: initialize mediump float width\nif (opacity < 0.01) {gl_Position=vec4(-2.0,-2.0,-2.0,1.0);return;}float ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=vec2(ivec2(a_data.xy)-128);float a_direction=float(int(a_data.z & 3u)-1);v_linesofar=float((a_data.z >> 2u)+a_data.w*64u)*2.0;vec2 pos=vec2(a_pos_normal >> 1);mediump vec2 normal=vec2(a_pos_normal & 1);normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);
vendor: 63,850 bytes, lines 9561-10178
9561float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nv_width2=vec2(outset,inset);}"),
9562	lineGradient: prepare("uniform sampler2D u_image;flat in vec2 v_width2;in vec2 v_normal;in float v_gamma_scale;in highp vec2 v_uv;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\nclipAntimeridian();float dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);vec4 color=texture(u_image,v_uv);fragColor=color*(alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "\n#define scale 0.015873016\nlayout(location=0) in ivec2 a_pos_normal;layout(location=1) in uvec4 a_data;layout(location=2) in float a_uv_x;layout(location=3) in float a_split_index;uniform vec2 u_translation;uniform mediump float u_ratio;uniform float u_image_height;out vec2 v_normal;flat out vec2 v_width2;out float v_gamma_scale;out highp vec2 v_uv;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define mediump float gapwidth\n#pragma maplibre: define lowp float offset\n#pragma maplibre: define mediump float width\nvoid main() {\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize mediump float gapwidth\n#pragma maplibre: initialize lowp float offset\n#pragma maplibre: initialize mediump float width\nif (opacity < 0.01) {gl_Position=vec4(-2.0,-2.0,-2.0,1.0);return;}float ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=vec2(ivec2(a_data.xy)-128);float a_direction=float(int(a_data.z & 3u)-1);highp float texel_height=1.0/u_image_height;highp float half_texel_height=0.5*texel_height;v_uv=vec2(a_uv_x,a_split_index*texel_height-half_texel_height);vec2 pos=vec2(a_pos_normal >> 1);mediump vec2 normal=vec2(a_pos_normal & 1);normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nv_width2=vec2(outset,inset);}"),
9563	linePattern: prepare("#ifdef GL_ES\nprecision highp float;\n#endif\nuniform vec2 u_texsize;uniform float u_fade;uniform mediump vec3 u_scale;uniform sampler2D u_image;in vec2 v_normal;flat in vec2 v_width2;in float v_linesofar;in float v_gamma_scale;flat in float v_width;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\n#pragma maplibre: define lowp float pixel_ratio_from\n#pragma maplibre: define lowp float pixel_ratio_to\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\n#pragma maplibre: initialize lowp float pixel_ratio_from\n#pragma maplibre: initialize lowp float pixel_ratio_to\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\nclipAntimeridian();vec2 pattern_tl_a=pattern_from.xy;vec2 pattern_br_a=pattern_from.zw;vec2 pattern_tl_b=pattern_to.xy;vec2 pattern_br_b=pattern_to.zw;float tileZoomRatio=u_scale.x;float fromScale=u_scale.y;float toScale=u_scale.z;vec2 display_size_a=(pattern_br_a-pattern_tl_a)/pixel_ratio_from;vec2 display_size_b=(pattern_br_b-pattern_tl_b)/pixel_ratio_to;vec2 pattern_size_a=vec2(display_size_a.x*fromScale/tileZoomRatio,display_size_a.y);vec2 pattern_size_b=vec2(display_size_b.x*toScale/tileZoomRatio,display_size_b.y);float aspect_a=display_size_a.y/v_width;float aspect_b=display_size_b.y/v_width;float dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);float x_a=mod(v_linesofar/pattern_size_a.x*aspect_a,1.0);float x_b=mod(v_linesofar/pattern_size_b.x*aspect_b,1.0);float y=0.5*v_normal.y+0.5;vec2 texel_size=1.0/u_texsize;vec2 pos_a=mix(pattern_tl_a*texel_size-texel_size,pattern_br_a*texel_size+texel_size,vec2(x_a,y));vec2 pos_b=mix(pattern_tl_b*texel_size-texel_size,pattern_br_b*texel_size+texel_size,vec2(x_b,y));vec4 color=mix(texture(u_image,pos_a),texture(u_image,pos_b),u_fade);fragColor=color*alpha*opacity;\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "\n#define scale 0.015873016\n#define LINE_DISTANCE_SCALE 2.0\nlayout(location=0) in ivec2 a_pos_normal;layout(location=1) in uvec4 a_data;uniform vec2 u_translation;uniform mediump float u_ratio;out vec2 v_normal;flat out vec2 v_width2;out float v_linesofar;out float v_gamma_scale;flat out float v_width;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define lowp float offset\n#pragma maplibre: define mediump float gapwidth\n#pragma maplibre: define mediump float width\n#pragma maplibre: define lowp float floorwidth\n#pragma maplibre: define lowp vec4 pattern_from\n#pragma maplibre: define lowp vec4 pattern_to\n#pragma maplibre: define lowp float pixel_ratio_from\n#pragma maplibre: define lowp float pixel_ratio_to\nvoid main() {\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize lowp float offset\n#pragma maplibre: initialize mediump float gapwidth\n#pragma maplibre: initialize mediump float width\n#pragma maplibre: initialize lowp float floorwidth\n#pragma maplibre: initialize mediump vec4 pattern_from\n#pragma maplibre: initialize mediump vec4 pattern_to\n#pragma maplibre: initialize lowp float pixel_ratio_from\n#pragma maplibre: initialize lowp float pixel_ratio_to\nif (opacity < 0.01) {gl_Position=vec4(-2.0,-2.0,-2.0,1.0);return;}float ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=vec2(ivec2(a_data.xy)-128);float a_direction=float(int(a_data.z & 3u)-1);float a_linesofar=float((a_data.z >> 2u)+a_data.w*64u)*LINE_DISTANCE_SCALE;vec2 pos=vec2(a_pos_normal >> 1);mediump vec2 normal=vec2(a_pos_normal & 1);normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nv_linesofar=a_linesofar;v_width2=vec2(outset,inset);v_width=floorwidth;}"),
9564	lineSDF: prepare("uniform lowp float u_lineatlas_width;uniform sampler2D u_image;uniform float u_mix;in vec2 v_normal;flat in vec2 v_width2;in vec2 v_tex_a;in vec2 v_tex_b;in float v_gamma_scale;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define mediump float width\n#pragma maplibre: define lowp float floorwidth\n#pragma maplibre: define mediump vec4 dasharray_from\n#pragma maplibre: define mediump vec4 dasharray_to\nvoid main() {\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize mediump float width\n#pragma maplibre: initialize lowp float floorwidth\n#pragma maplibre: initialize mediump vec4 dasharray_from\n#pragma maplibre: initialize mediump vec4 dasharray_to\nclipAntimeridian();float dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);float sdfdist_a=texture(u_image,v_tex_a).a;float sdfdist_b=texture(u_image,v_tex_b).a;float sdfdist=mix(sdfdist_a,sdfdist_b,u_mix);float sdfgamma=(u_lineatlas_width/256.0/u_device_pixel_ratio)/min(dasharray_from.w,dasharray_to.w);alpha*=smoothstep(0.5-sdfgamma/floorwidth,0.5+sdfgamma/floorwidth,sdfdist);fragColor=color*(alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "\n#define scale 0.015873016\n#define LINE_DISTANCE_SCALE 2.0\nlayout(location=0) in ivec2 a_pos_normal;layout(location=1) in uvec4 a_data;uniform vec2 u_translation;uniform mediump float u_ratio;uniform float u_tileratio;uniform float u_crossfade_from;uniform float u_crossfade_to;uniform float u_lineatlas_height;out vec2 v_normal;flat out vec2 v_width2;out vec2 v_tex_a;out vec2 v_tex_b;out float v_gamma_scale;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma maplibre: define highp vec4 color\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define mediump float gapwidth\n#pragma maplibre: define lowp float offset\n#pragma maplibre: define mediump float width\n#pragma maplibre: define lowp float floorwidth\n#pragma maplibre: define mediump vec4 dasharray_from\n#pragma maplibre: define mediump vec4 dasharray_to\nvoid main() {\n#pragma maplibre: initialize highp vec4 color\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize mediump float gapwidth\n#pragma maplibre: initialize lowp float offset\n#pragma maplibre: initialize mediump float width\n#pragma maplibre: initialize lowp float floorwidth\n#pragma maplibre: initialize mediump vec4 dasharray_from\n#pragma maplibre: initialize mediump vec4 dasharray_to\nif (opacity < 0.01) {gl_Position=vec4(-2.0,-2.0,-2.0,1.0);return;}float ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=vec2(ivec2(a_data.xy)-128);float a_direction=float(int(a_data.z & 3u)-1);float a_linesofar=float((a_data.z >> 2u)+a_data.w*64u)*LINE_DISTANCE_SCALE;vec2 pos=vec2(a_pos_normal >> 1);mediump vec2 normal=vec2(a_pos_normal & 1);normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nfloat u_patternscale_a_x=u_tileratio/dasharray_from.w/u_crossfade_from;float u_patternscale_a_y=-dasharray_from.z/2.0/u_lineatlas_height;float u_patternscale_b_x=u_tileratio/dasharray_to.w/u_crossfade_to;float u_patternscale_b_y=-dasharray_to.z/2.0/u_lineatlas_height;v_tex_a=vec2(a_linesofar*u_patternscale_a_x/floorwidth,normal.y*u_patternscale_a_y+(float(dasharray_from.y)+0.5)/u_lineatlas_height);v_tex_b=vec2(a_linesofar*u_patternscale_b_x/floorwidth,normal.y*u_patternscale_b_y+(float(dasharray_to.y)+0.5)/u_lineatlas_height);v_width2=vec2(outset,inset);}"),
9565	lineGradientSDF: prepare("uniform sampler2D u_image;uniform sampler2D u_image_dash;uniform float u_mix;uniform lowp float u_lineatlas_width;in vec2 v_normal;flat in vec2 v_width2;in vec2 v_tex_a;in vec2 v_tex_b;in float v_gamma_scale;in highp vec2 v_uv;\n#ifdef GLOBE\nin float v_depth;\n#endif\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define mediump float width\n#pragma maplibre: define lowp float floorwidth\n#pragma maplibre: define mediump vec4 dasharray_from\n#pragma maplibre: define mediump vec4 dasharray_to\nvoid main() {\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize mediump float width\n#pragma maplibre: initialize lowp float floorwidth\n#pragma maplibre: initialize mediump vec4 dasharray_from\n#pragma maplibre: initialize mediump vec4 dasharray_to\nclipAntimeridian();float dist=length(v_normal)*v_width2.s;float blur2=(blur+1.0/u_device_pixel_ratio)*v_gamma_scale;float alpha=clamp(min(dist-(v_width2.t-blur2),v_width2.s-dist)/blur2,0.0,1.0);vec4 color=texture(u_image,v_uv);float sdfdist_a=texture(u_image_dash,v_tex_a).a;float sdfdist_b=texture(u_image_dash,v_tex_b).a;float sdfdist=mix(sdfdist_a,sdfdist_b,u_mix);float sdfgamma=(u_lineatlas_width/256.0)/min(dasharray_from.w,dasharray_to.w);float dash_alpha=smoothstep(0.5-sdfgamma/floorwidth,0.5+sdfgamma/floorwidth,sdfdist);fragColor=color*(alpha*dash_alpha*opacity);\n#ifdef GLOBE\nif (v_depth > 1.0) {discard;}\n#endif\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "\n#define scale 0.015873016\n#define LINE_DISTANCE_SCALE 2.0\nlayout(location=0) in ivec2 a_pos_normal;layout(location=1) in uvec4 a_data;layout(location=2) in float a_uv_x;layout(location=3) in float a_split_index;uniform vec2 u_translation;uniform mediump float u_ratio;uniform float u_image_height;uniform float u_tileratio;uniform float u_crossfade_from;uniform float u_crossfade_to;uniform float u_lineatlas_height;out vec2 v_normal;flat out vec2 v_width2;out float v_gamma_scale;out highp vec2 v_uv;out vec2 v_tex_a;out vec2 v_tex_b;\n#ifdef GLOBE\nout float v_depth;\n#endif\n#pragma maplibre: define lowp float blur\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define mediump float gapwidth\n#pragma maplibre: define lowp float offset\n#pragma maplibre: define mediump float width\n#pragma maplibre: define lowp float floorwidth\n#pragma maplibre: define mediump vec4 dasharray_from\n#pragma maplibre: define mediump vec4 dasharray_to\nvoid main() {\n#pragma maplibre: initialize lowp float blur\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize mediump float gapwidth\n#pragma maplibre: initialize lowp float offset\n#pragma maplibre: initialize mediump float width\n#pragma maplibre: initialize lowp float floorwidth\n#pragma maplibre: initialize mediump vec4 dasharray_from\n#pragma maplibre: initialize mediump vec4 dasharray_to\nif (opacity < 0.01) {gl_Position=vec4(-2.0,-2.0,-2.0,1.0);return;}float ANTIALIASING=1.0/u_device_pixel_ratio/2.0;vec2 a_extrude=vec2(ivec2(a_data.xy)-128);float a_direction=float(int(a_data.z & 3u)-1);float a_linesofar=float((a_data.z >> 2u)+a_data.w*64u)*LINE_DISTANCE_SCALE;float texel_height=1.0/u_image_height;float half_texel_height=0.5*texel_height;v_uv=vec2(a_uv_x,a_split_index*texel_height-half_texel_height);vec2 pos=vec2(a_pos_normal >> 1);mediump vec2 normal=vec2(a_pos_normal & 1);normal.y=normal.y*2.0-1.0;v_normal=normal;gapwidth=gapwidth/2.0;float halfwidth=width/2.0;offset=-1.0*offset;float inset=gapwidth+(gapwidth > 0.0 ? ANTIALIASING : 0.0);float outset=gapwidth+halfwidth*(gapwidth > 0.0 ? 2.0 : 1.0)+(halfwidth==0.0 ? 0.0 : ANTIALIASING);mediump vec2 dist=outset*a_extrude*scale;mediump float u=0.5*a_direction;mediump float t=1.0-abs(u);mediump vec2 offset2=offset*a_extrude*scale*normal.y*mat2(t,-u,u,t);float adjustedThickness=projectLineThickness(pos.y);vec4 projected_no_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation);vec4 projected_with_extrude=projectTile(pos+offset2/u_ratio*adjustedThickness+u_translation+dist/u_ratio*adjustedThickness);gl_Position=projected_with_extrude;\n#ifdef GLOBE\nv_depth=gl_Position.z/gl_Position.w;\n#endif\n#ifdef TERRAIN3D\nv_gamma_scale=1.0;\n#else\nfloat extrude_length_without_perspective=length(dist);float extrude_length_with_perspective=length((projected_with_extrude.xy-projected_no_extrude.xy)/projected_with_extrude.w*u_units_to_pixels);v_gamma_scale=extrude_length_without_perspective/extrude_length_with_perspective;\n#endif\nfloat u_patternscale_a_x=u_tileratio/dasharray_from.w/u_crossfade_from;float u_patternscale_a_y=-dasharray_from.z/2.0/u_lineatlas_height;float u_patternscale_b_x=u_tileratio/dasharray_to.w/u_crossfade_to;float u_patternscale_b_y=-dasharray_to.z/2.0/u_lineatlas_height;v_tex_a=vec2(a_linesofar*u_patternscale_a_x/floorwidth,normal.y*u_patternscale_a_y+(float(dasharray_from.y)+0.5)/u_lineatlas_height);v_tex_b=vec2(a_linesofar*u_patternscale_b_x/floorwidth,normal.y*u_patternscale_b_y+(float(dasharray_to.y)+0.5)/u_lineatlas_height);v_width2=vec2(outset,inset);}"),
9566	layerOpacity: prepare("uniform sampler2D u_image;uniform float u_opacity;in vec2 v_pos;void main() {fragColor=texture(u_image,v_pos)*u_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(0.0);\n#endif\n}", "layout(location=0) in vec2 a_pos;out vec2 v_pos;void main() {gl_Position=vec4(a_pos.x*2.0-1.0,1.0-a_pos.y*2.0,0.0,1.0);v_pos.x=a_pos.x;v_pos.y=1.0-a_pos.y;}"),
9567	raster: prepare("uniform float u_fade_t;uniform float u_opacity;uniform sampler2D u_image0;uniform sampler2D u_image1;in vec3 v_pos0;in vec3 v_pos1;uniform float u_brightness_low;uniform float u_brightness_high;uniform float u_saturation_factor;uniform float u_contrast_factor;uniform vec3 u_spin_weights;void main() {vec4 color0=texture(u_image0,v_pos0.xy/v_pos0.z);vec4 color1=texture(u_image1,v_pos1.xy/v_pos1.z);if (color0.a > 0.0) {color0.rgb=color0.rgb/color0.a;}if (color1.a > 0.0) {color1.rgb=color1.rgb/color1.a;}vec4 color=mix(color0,color1,u_fade_t);color.a*=u_opacity;vec3 rgb=color.rgb;rgb=vec3(dot(rgb,u_spin_weights.xyz),dot(rgb,u_spin_weights.zxy),dot(rgb,u_spin_weights.yzx));float average=(color.r+color.g+color.b)/3.0;rgb+=(average-rgb)*u_saturation_factor;rgb=(rgb-0.5)*u_contrast_factor+0.5;vec3 u_high_vec=vec3(u_brightness_low,u_brightness_low,u_brightness_low);vec3 u_low_vec=vec3(u_brightness_high,u_brightness_high,u_brightness_high);fragColor=vec4(mix(u_high_vec,u_low_vec,rgb)*color.a,color.a);\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "uniform vec2 u_tl_parent;uniform float u_scale_parent;uniform float u_buffer_scale;uniform vec3 u_image_warp;uniform vec4 u_coords_top;uniform vec4 u_coords_bottom;layout(location=0) in vec2 a_pos;out vec3 v_pos0;out vec3 v_pos1;void main() {vec2 fractionalPos=a_pos/8192.0;vec2 topLeft=u_coords_top.xy;vec2 topRight=u_coords_top.zw;vec2 bottomLeft=u_coords_bottom.xy;vec2 bottomRight=u_coords_bottom.zw;vec2 bilinearPos=mix(mix(topLeft,topRight,fractionalPos.x),mix(bottomLeft,bottomRight,fractionalPos.x),fractionalPos.y);float denominator=dot(u_image_warp.xy,fractionalPos)+1.0;vec2 acrossTop=topRight-topLeft+u_image_warp.x*topRight;vec2 downLeft=bottomLeft-topLeft+u_image_warp.y*bottomLeft;vec2 projectivePos=(acrossTop*fractionalPos.x+downLeft*fractionalPos.y+topLeft)/denominator;vec2 position=mix(projectivePos,bilinearPos,u_image_warp.z);gl_Position=projectTile(position,position);vec2 texturePos=((fractionalPos-0.5)/u_buffer_scale)+0.5;\n#ifdef GLOBE\nif (a_pos.y <-32767.5) {texturePos.y=0.0;}if (a_pos.y > 32766.5) {texturePos.y=1.0;}\n#endif\nfloat perspectiveRatio=mix(1.0/denominator,1.0,u_image_warp.z);v_pos0=vec3(texturePos*perspectiveRatio,perspectiveRatio);vec2 parentPos=(texturePos*u_scale_parent)+u_tl_parent;v_pos1=vec3(parentPos*perspectiveRatio,perspectiveRatio);}"),
9568	symbolIcon: prepare("uniform sampler2D u_texture;in vec2 v_tex;flat in float v_total_opacity;void main() {fragColor=texture(u_texture,v_tex)*v_total_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec4 a_pixeloffset;layout(location=3) in vec3 a_projected_pos;layout(location=4) in uint a_fade_opacity;layout(location=5) in float a_height_offset;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform bool u_rotate_symbol;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform vec2 u_texsize;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;uniform bool u_height_anchor_ground;out vec2 v_tex;flat out float v_total_opacity;\n#pragma maplibre: define lowp float opacity\nvoid main() {\n#pragma maplibre: initialize lowp float opacity\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);vec2 a_pxoffset=a_pixeloffset.xy;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=a_height_offset+(u_height_anchor_ground ? get_elevation(a_pos) : 0.0);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float fade_change=fade_opacity[1] > 0.5 ? u_symbol_fade_change :-u_symbol_fade_change;float visibility=calculate_visibility(projectedPoint);v_total_opacity=opacity*max(0.0,min(visibility,fade_opacity[0]+fade_change));if (v_total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset/16.0)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}gl_Position=finalPos;v_tex=a_tex/u_texsize;}"),
9569	symbolSDF: prepare("#define SDF_PX 8.0\nuniform bool u_is_halo;uniform bool u_is_plain;uniform sampler2D u_texture;uniform highp float u_gamma_scale;uniform bool u_is_text;in vec2 v_data0;in vec3 v_data1;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nfloat EDGE_GAMMA=0.105/u_device_pixel_ratio;vec2 tex=v_data0.xy;float gamma_scale=v_data1.x;float size=v_data1.y;float total_opacity=v_data1[2];float fontScale=u_is_text ? size/24.0 : size;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float inner_edge=(256.0-64.0)/256.0;lowp float dist=texture(u_texture,tex).a;lowp vec4 color_alpha_out_text,color_alpha_out_halo;if (u_is_plain){highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(inner_edge-gamma_scaled,inner_edge+gamma_scaled,dist);color_alpha_out_text=total_opacity*alpha*fill_color;}if (u_is_halo) {float gamma_halo=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);float inner_edge_halo=inner_edge+gamma_halo*gamma_scale;highp float gamma_scaled_halo=gamma_halo*gamma_scale;highp float alpha_halo=smoothstep(inner_edge_halo-gamma_scaled_halo,inner_edge_halo+gamma_scaled_halo,dist);highp float halo_edge=(6.0-halo_width/fontScale)/SDF_PX;alpha_halo= min(smoothstep(halo_edge-gamma_scaled_halo,halo_edge+gamma_scaled_halo,dist),1.0-alpha_halo);color_alpha_out_halo=total_opacity*alpha_halo*halo_color;}if (u_is_plain && u_is_halo) {fragColor=color_alpha_out_text+(1.-color_alpha_out_text.a)*color_alpha_out_halo;} else if (u_is_halo){fragColor=color_alpha_out_halo;} else {fragColor=color_alpha_out_text;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec4 a_pixeloffset;layout(location=3) in vec3 a_projected_pos;layout(location=4) in uint a_fade_opacity;layout(location=5) in float a_height_offset;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform bool u_rotate_symbol;uniform vec2 u_texsize;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;uniform bool u_height_anchor_ground;out vec2 v_data0;out vec3 v_data1;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);vec2 a_pxoffset=a_pixeloffset.xy/16.0;vec2 a_minFontScale=a_pixeloffset.zw/256.0;float ele=a_height_offset+(u_height_anchor_ground ? get_elevation(a_pos) : 0.0);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_symbol_fade_change :-u_symbol_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));float total_opacity=opacity*interpolated_fade_opacity;if (total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=u_is_text ? size/24.0 : size;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*max(a_minFontScale,fontScale)+a_pxoffset)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;v_data0=a_tex/u_texsize;v_data1=vec3(gamma_scale,size,total_opacity);}"),
9570	symbolTextAndIcon: prepare("#define SDF_PX 8.0\n#define SDF 1.0\n#define ICON 0.0\nuniform bool u_is_halo;uniform bool u_is_text;uniform sampler2D u_texture;uniform sampler2D u_texture_icon;uniform highp float u_gamma_scale;in vec4 v_data0;in vec3 v_data1;flat in float v_is_sdf;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nfloat total_opacity=v_data1[2];if (v_is_sdf==ICON) {vec2 tex_icon=v_data0.zw;fragColor=texture(u_texture_icon,tex_icon)*total_opacity;\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\nreturn;}vec2 tex=v_data0.xy;float EDGE_GAMMA=0.105/u_device_pixel_ratio;float gamma_scale=v_data1.x;float size=v_data1.y;float fontScale=size/24.0;highp float gamma=EDGE_GAMMA/(fontScale*u_gamma_scale);lowp float buff=(256.0-64.0)/256.0;lowp float dist=texture(u_texture,tex).a;lowp vec4 color_alpha_out,color_alpha_out_halo;if (u_is_text) {highp float gamma_scaled=gamma*gamma_scale;highp float alpha=smoothstep(buff-gamma_scaled,buff+gamma_scaled,dist);color_alpha_out=fill_color*(alpha*total_opacity);}if (u_is_halo) {highp float gamma_halo=(halo_blur*1.19/SDF_PX+EDGE_GAMMA)/(fontScale*u_gamma_scale);lowp float buff_halo=(6.0-halo_width/fontScale)/SDF_PX;highp float gamma_scaled_halo=gamma_halo*gamma_scale;highp float alpha_halo=smoothstep(buff_halo-gamma_scaled_halo,buff_halo+gamma_scaled_halo,dist);color_alpha_out_halo=halo_color*(alpha_halo*total_opacity);}if (u_is_text && u_is_halo) {fragColor=color_alpha_out+(1.-color_alpha_out.a)*color_alpha_out_halo;} else if (u_is_halo) {fragColor=color_alpha_out_halo;} else {fragColor=color_alpha_out;}\n#ifdef OVERDRAW_INSPECTOR\nfragColor=vec4(1.0);\n#endif\n}", "layout(location=0) in vec4 a_pos_offset;layout(location=1) in uvec4 a_data;layout(location=2) in vec3 a_projected_pos;layout(location=3) in uint a_fade_opacity;layout(location=4) in float a_height_offset;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform bool u_rotate_symbol;uniform vec2 u_texsize;uniform vec2 u_texsize_icon;uniform bool u_is_along_line;uniform bool u_is_variable_anchor;uniform vec2 u_translation;uniform float u_pitched_scale;uniform bool u_is_offset;uniform bool u_height_anchor_ground;out vec4 v_data0;out vec3 v_data1;flat out float v_is_sdf;\n#pragma maplibre: define highp vec4 fill_color\n#pragma maplibre: define highp vec4 halo_color\n#pragma maplibre: define lowp float opacity\n#pragma maplibre: define lowp float halo_width\n#pragma maplibre: define lowp float halo_blur\nvoid main() {\n#pragma maplibre: initialize highp vec4 fill_color\n#pragma maplibre: initialize highp vec4 halo_color\n#pragma maplibre: initialize lowp float opacity\n#pragma maplibre: initialize lowp float halo_width\n#pragma maplibre: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=vec2(a_data.xy);vec2 a_size=vec2(a_data.zw);float a_size_min=float(a_data.z >> 1u);float is_sdf=float(a_data.z & 1u);float ele=a_height_offset+(u_height_anchor_ground ? get_elevation(a_pos) : 0.0);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec2 translated_a_pos=a_pos+u_translation;vec4 projectedPoint=projectTileWithElevation(translated_a_pos,ele);vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_symbol_fade_change :-u_symbol_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));float total_opacity=opacity*interpolated_fade_opacity;if (total_opacity < 0.1){gl_Position=vec4(-2.,-2.,-2.,1.);return;}highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);if (!u_is_offset) {size*=perspective_ratio;}float fontScale=size/24.0;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=projectTileWithElevation(translated_a_pos+vec2(1,0),ele);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos;if (u_is_along_line || u_is_variable_anchor) {projected_pos=vec4(a_projected_pos.xy,ele,1.0);} else if (u_pitch_with_map) {projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy+u_translation,ele,1.0);} else {projected_pos=u_label_plane_matrix*projectTileWithElevation(a_projected_pos.xy+u_translation,ele);}float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;float projectionScaling=1.0;\n#ifdef GLOBE\nif(u_pitch_with_map && !u_is_along_line) {float anchor_pos_tile_y=(u_coord_matrix*vec4(projected_pos.xy/projected_pos.w,z,1.0)).y;projectionScaling=mix(projectionScaling,1.0/circumferenceRatioAtTileY(anchor_pos_tile_y)*u_pitched_scale,u_projection_transition);}\n#endif\nvec4 finalPos=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*fontScale)*projectionScaling,z,1.0);if(u_pitch_with_map) {finalPos=projectTileWithElevation(finalPos.xy,finalPos.z);}float gamma_scale=finalPos.w;gl_Position=finalPos;v_data0.xy=a_tex/u_texsize;v_data0.zw=a_tex/u_texsize_icon;v_data1=vec3(gamma_scale,size,total_opacity);v_is_sdf=is_sdf;}"),
9571	terrain: prepare("uniform sampler2D u_texture;uniform vec4 u_fog_color;uniform vec4 u_horizon_color;uniform float u_fog_ground_blend;uniform float u_fog_ground_blend_opacity;uniform float u_horizon_fog_blend;uniform bool u_is_globe_mode;in vec2 v_texture_pos;in float v_fog_depth;const float gamma=2.2;vec4 gammaToLinear(vec4 color) {return pow(color,vec4(gamma));}vec4 linearToGamma(vec4 color) {return pow(color,vec4(1.0/gamma));}void main() {vec4 surface_color=texture(u_texture,vec2(v_texture_pos.x,1.0-v_texture_pos.y));if (!u_is_globe_mode && u_fog_ground_blend_opacity > 0.0 && v_fog_depth > u_fog_ground_blend) {vec4 surface_color_linear=gammaToLinear(surface_color);float blend_color=smoothstep(0.0,1.0,max((v_fog_depth-u_horizon_fog_blend)/(1.0-u_horizon_fog_blend),0.0));vec4 fog_horizon_color_linear=mix(gammaToLinear(u_fog_color),gammaToLinear(u_horizon_color),blend_color);float factor_fog=max(v_fog_depth-u_fog_ground_blend,0.0)/(1.0-u_fog_ground_blend);fragColor=linearToGamma(mix(surface_color_linear,fog_horizon_color_linear,pow(factor_fog,2.0)*u_fog_ground_blend_opacity));} else {fragColor=surface_color;}}", "layout(location=0) in vec3 a_pos3d;uniform mat4 u_fog_matrix;uniform float u_ele_delta;out vec2 v_texture_pos;out float v_fog_depth;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;v_texture_pos=a_pos3d.xy/8192.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);vec4 pos=u_fog_matrix*vec4(a_pos3d.xy,ele,1.0);v_fog_depth=pos.z/pos.w*0.5+0.5;}"),
9572	terrainDepth: prepare("in float v_depth;const highp vec4 bitSh=vec4(256.*256.*256.,256.*256.,256.,1.);const highp vec4 bitMsk=vec4(0.,vec3(1./256.0));highp vec4 pack(highp float value) {highp vec4 comp=fract(value*bitSh);comp-=comp.xxyz*bitMsk;return comp;}void main() {fragColor=pack(v_depth);}", "layout(location=0) in vec3 a_pos3d;uniform float u_ele_delta;out float v_depth;void main() {float ele=get_elevation(a_pos3d.xy);float ele_delta=a_pos3d.z==1.0 ? u_ele_delta : 0.0;gl_Position=projectTileFor3D(a_pos3d.xy,ele-ele_delta);v_depth=gl_Position.z/gl_Position.w;}"),
9573	atmosphere: prepare("#ifdef GL_ES\nprecision highp float;\n#endif\nin vec3 view_direction;uniform vec3 u_sun_pos;uniform vec3 u_globe_position;uniform float u_globe_radius;uniform float u_atmosphere_blend;/**Shader use from https:*Made some change to adapt to MapLibre Globe geometry*/const float PI=3.141592653589793;const int iSteps=5;const int jSteps=3;/*radius of the planet*/const float EARTH_RADIUS=6371e3;/*radius of the atmosphere*/const float ATMOS_RADIUS=6471e3;vec2 rsi(vec3 r0,vec3 rd,float sr) {float a=dot(rd,rd);float b=2.0*dot(rd,r0);float c=dot(r0,r0)-(sr*sr);float d=(b*b)-4.0*a*c;if (d < 0.0) return vec2(1e5,-1e5);return vec2((-b-sqrt(d))/(2.0*a),(-b+sqrt(d))/(2.0*a));}vec4 atmosphere(vec3 r,vec3 r0,vec3 pSun,float iSun,float rPlanet,float rAtmos,vec3 kRlh,float kMie,float shRlh,float shMie,float g) {pSun=normalize(pSun);r=normalize(r);vec2 p=rsi(r0,r,rAtmos);if (p.x > p.y) {return vec4(0.0,0.0,0.0,1.0);}if (p.x < 0.0) {p.x=0.0;}vec3 pos=r0+r*p.x;vec2 p2=rsi(r0,r,rPlanet);if (p2.x <=p2.y && p2.x > 0.0) {p.y=min(p.y,p2.x);}float iStepSize=(p.y-p.x)/float(iSteps);float iTime=p.x+iStepSize*0.5;vec3 totalRlh=vec3(0,0,0);vec3 totalMie=vec3(0,0,0);float iOdRlh=0.0;float iOdMie=0.0;float mu=dot(r,pSun);float mumu=mu*mu;float gg=g*g;float pRlh=3.0/(16.0*PI)*(1.0+mumu);float pMie=3.0/(8.0*PI)*((1.0-gg)*(mumu+1.0))/(pow(1.0+gg-2.0*mu*g,1.5)*(2.0+gg));for (int i=0; i < iSteps; i++) {vec3 iPos=r0+r*iTime;float iHeight=length(iPos)-rPlanet;float odStepRlh=exp(-iHeight/shRlh)*iStepSize;float odStepMie=exp(-iHeight/shMie)*iStepSize;iOdRlh+=odStepRlh;iOdMie+=odStepMie;float jStepSize=rsi(iPos,pSun,rAtmos).y/float(jSteps);float jTime=jStepSize*0.5;float jOdRlh=0.0;float jOdMie=0.0;for (int j=0; j < jSteps; j++) {vec3 jPos=iPos+pSun*jTime;float jHeight=length(jPos)-rPlanet;jOdRlh+=exp(-jHeight/shRlh)*jStepSize;jOdMie+=exp(-jHeight/shMie)*jStepSize;jTime+=jStepSize;}vec3 attn=exp(-(kMie*(iOdMie+jOdMie)+kRlh*(iOdRlh+jOdRlh)));totalRlh+=odStepRlh*attn;totalMie+=odStepMie*attn;iTime+=iStepSize;}float opacity=exp(-(length(kRlh)*length(totalRlh)+kMie*length(totalMie)));vec3 color=iSun*(pRlh*kRlh*totalRlh+pMie*kMie*totalMie);return vec4(color,opacity);}void main() {vec3 scale_camera_pos=-u_globe_position*EARTH_RADIUS/u_globe_radius;vec4 color=atmosphere(normalize(view_direction),scale_camera_pos,u_sun_pos,22.0,EARTH_RADIUS,ATMOS_RADIUS,vec3(5.5e-6,13.0e-6,22.4e-6),21e-6,8e3,1.2e3,0.758\n);color.rgb=1.0-exp(-1.0*color.rgb);color=pow(color,vec4(1.0/2.2));fragColor=vec4(color.rgb,1.0-color.a)*u_atmosphere_blend;}", "layout(location=0) in vec2 a_pos;uniform mat4 u_inv_proj_matrix;out vec3 view_direction;void main() {view_direction=(u_inv_proj_matrix*vec4(a_pos,0.0,1.0)).xyz;gl_Position=vec4(a_pos,0.0,1.0);}"),
9574	sky: prepare("uniform vec4 u_sky_color;uniform vec4 u_horizon_color;uniform vec2 u_horizon;uniform vec2 u_horizon_normal;uniform float u_sky_horizon_blend;uniform float u_sky_blend;uniform vec3 u_globe_position;uniform float u_globe_radius;uniform float u_atmosphere_blend;in vec3 v_view_direction;const float ATMOSPHERE_HEIGHT_TO_GLOBE_RADIUS=100000.0/6371000.0;void main() {float x=gl_FragCoord.x;float y=gl_FragCoord.y;float blend=(y-u_horizon.y)*u_horizon_normal.y+(x-u_horizon.x)*u_horizon_normal.x;float opacity=1.0;if (u_sky_blend > 0.0) {vec3 ray=normalize(v_view_direction);float globe_distance=length(u_globe_position);float angle_to_globe_center=acos(clamp(dot(ray,u_globe_position)/globe_distance,-1.0,1.0));float horizon_angle=asin(min(u_globe_radius/globe_distance,1.0));blend=mix(blend,((angle_to_globe_center-horizon_angle)*u_camera_to_center_distance+1.0)*u_device_pixel_ratio,u_sky_blend);float closest_approach=max(dot(ray,u_globe_position),0.0);float lowest_altitude=sqrt(max(globe_distance*globe_distance-closest_approach*closest_approach,0.0))-u_globe_radius;float atmosphere_height=u_globe_radius*ATMOSPHERE_HEIGHT_TO_GLOBE_RADIUS;float inside_atmosphere=1.0-smoothstep(atmosphere_height,10.0*atmosphere_height,globe_distance-u_globe_radius);float through_atmosphere=1.0-smoothstep(0.0,atmosphere_height,lowest_altitude);opacity=inside_atmosphere*through_atmosphere*(1.0-u_atmosphere_blend);}if (blend > 0.0) {if (blend < u_sky_horizon_blend) {fragColor=mix(u_sky_color,u_horizon_color,pow(1.0-blend/u_sky_horizon_blend,2.0));} else {fragColor=u_sky_color;}}fragColor*=mix(1.0,opacity,u_sky_blend);}", "layout(location=0) in vec2 a_pos;uniform mat4 u_inv_proj_matrix;out vec3 v_view_direction;void main() {v_view_direction=(u_inv_proj_matrix*vec4(a_pos,0.0,1.0)).xyz;gl_Position=vec4(a_pos,1.0,1.0);}")
9575};
9576/** Expand #pragmas to #ifdefs, extract attributes and uniforms */
9577function prepare(fragmentSource, vertexSource) {
9578	const re = /#pragma maplibre: ([\w]+) ([\w]+) ([\w]+) ([\w]+)/g;
9579	const vertexAttributes = vertexSource.match(/in ([\w]+) ([\w]+)/g);
9580	const fragmentUniforms = fragmentSource.match(/uniform ([\w]+) ([\w]+)([\s]*)([\w]*)/g);
9581	const vertexUniforms = vertexSource.match(/uniform ([\w]+) ([\w]+)([\s]*)([\w]*)/g);
9582	const shaderUniforms = vertexUniforms ? vertexUniforms.concat(fragmentUniforms) : fragmentUniforms;
9583	let locationCounter = vertexAttributes ? vertexAttributes.length : 0;
9584	const fragmentPragmas = {};
9585	fragmentSource = fragmentSource.replace(re, (match, operation, precision, type, name) => {
9586		fragmentPragmas[name] = true;
9587		if (operation === "define") return `
9588#ifndef HAS_UNIFORM_u_${name}
9589in ${precision} ${type} ${name};
9590#else
9591uniform ${precision} ${type} u_${name};
9592#endif
9593`;
9594		else return `
9595#ifdef HAS_UNIFORM_u_${name}
9596    ${precision} ${type} ${name} = u_${name};
9597#endif
9598`;
9599	});
9600	vertexSource = vertexSource.replace(re, (match, operation, precision, type, name) => {
9601		const attrType = type === "float" ? "vec2" : "vec4";
9602		const unpackType = name.match(/color/) ? "color" : attrType;
9603		if (fragmentPragmas[name]) {
9604			if (operation === "define") return `
9605#ifndef HAS_UNIFORM_u_${name}
9606uniform lowp float u_${name}_t;
9607layout(location = ${locationCounter++}) in ${precision} ${attrType} a_${name};
9608out ${precision} ${type} ${name};
9609#else
9610uniform ${precision} ${type} u_${name};
9611#endif
9612`;
9613			else if (unpackType === "vec4") return `
9614#ifndef HAS_UNIFORM_u_${name}
9615    ${name} = a_${name};
9616#else
9617    ${precision} ${type} ${name} = u_${name};
9618#endif
9619`;
9620			else return `
9621#ifndef HAS_UNIFORM_u_${name}
9622    ${name} = unpack_mix_${unpackType}(a_${name}, u_${name}_t);
9623#else
9624    ${precision} ${type} ${name} = u_${name};
9625#endif
9626`;
9627		} else if (operation === "define") return `
9628#ifndef HAS_UNIFORM_u_${name}
9629uniform lowp float u_${name}_t;
9630layout(location = ${locationCounter++}) in ${precision} ${attrType} a_${name};
9631#else
9632uniform ${precision} ${type} u_${name};
9633#endif
9634`;
9635		else if (unpackType === "vec4") return `
9636#ifndef HAS_UNIFORM_u_${name}
9637    ${precision} ${type} ${name} = a_${name};
9638#else
9639    ${precision} ${type} ${name} = u_${name};
9640#endif
9641`;
9642		else return `
9643#ifndef HAS_UNIFORM_u_${name}
9644    ${precision} ${type} ${name} = unpack_mix_${unpackType}(a_${name}, u_${name}_t);
9645#else
9646    ${precision} ${type} ${name} = u_${name};
9647#endif
9648`;
9649	});
9650	return {
9651		fragmentSource,
9652		vertexSource,
9653		staticAttributes: vertexAttributes,
9654		staticUniforms: shaderUniforms
9655	};
9656}
9657//#endregion
9658//#region src/geo/projection/mercator_projection.ts
9659const MercatorShaderDefine = "#define PROJECTION_MERCATOR";
9660const MercatorShaderVariantKey = "mercator";
9661var MercatorProjection = class {
9662	constructor() {
9663		this._cachedMesh = null;
9664	}
9665	get name() {
9666		return "mercator";
9667	}
9668	get useSubdivision() {
9669		return false;
9670	}
9671	get shaderVariantName() {
9672		return MercatorShaderVariantKey;
9673	}
9674	get shaderDefine() {
9675		return MercatorShaderDefine;
9676	}
9677	get shaderPreludeCode() {
9678		return shaders.projectionMercator;
9679	}
9680	get vertexShaderPreludeCode() {
9681		return shaders.projectionMercator.vertexSource;
9682	}
9683	get subdivisionGranularity() {
9684		return SubdivisionGranularitySetting.noSubdivision;
9685	}
9686	get useGlobeControls() {
9687		return false;
9688	}
9689	get transitionState() {
9690		return 0;
9691	}
9692	destroy() {}
9693	getMeshFromTileID(context, _tileID, _hasBorder, _allowPoles, _usage) {
9694		if (this._cachedMesh) return this._cachedMesh;
9695		const tileExtentArray = new PosArray();
9696		tileExtentArray.emplaceBack(0, 0);
9697		tileExtentArray.emplaceBack(EXTENT, 0);
9698		tileExtentArray.emplaceBack(0, EXTENT);
9699		tileExtentArray.emplaceBack(EXTENT, EXTENT);
9700		const tileExtentBuffer = context.createVertexBuffer(tileExtentArray, posAttributes.members);
9701		const tileExtentSegments = SegmentVector.simpleSegment(0, 0, 4, 2);
9702		const quadTriangleIndices = new TriangleIndexArray();
9703		quadTriangleIndices.emplaceBack(1, 0, 2);
9704		quadTriangleIndices.emplaceBack(1, 2, 3);
9705		const quadTriangleIndexBuffer = context.createIndexBuffer(quadTriangleIndices);
9706		this._cachedMesh = new Mesh(tileExtentBuffer, quadTriangleIndexBuffer, tileExtentSegments);
9707		return this._cachedMesh;
9708	}
9709	recalculate() {}
9710	hasTransition() {
9711		return false;
9712	}
9713};
9714//#endregion
9715//#region src/geo/edge_insets.ts
9716/**
9717* An `EdgeInset` object represents screen space padding applied to the edges of the viewport.
9718* This shifts the apparent center or the vanishing point of the map. This is useful for adding floating UI elements
9719* on top of the map and having the vanishing point shift as UI elements resize.
9720*
9721* @group Geography and Geometry
9722*/
9723var EdgeInsets = class EdgeInsets {
9724	constructor(top = 0, bottom = 0, left = 0, right = 0) {
9725		if (isNaN(top) || top < 0 || isNaN(bottom) || bottom < 0 || isNaN(left) || left < 0 || isNaN(right) || right < 0) throw new Error("Invalid value for edge-insets, top, bottom, left and right must all be numbers");
9726		this.top = top;
9727		this.bottom = bottom;
9728		this.left = left;
9729		this.right = right;
9730	}
9731	/**
9732	* Interpolates the inset in-place.
9733	* This maintains the current inset value for any inset not present in `target`.
9734	* @param start - interpolation start
9735	* @param target - interpolation target
9736	* @param t - interpolation step/weight
9737	* @returns the insets
9738	*/
9739	interpolate(start, target, t) {
9740		if (target.top != null && start.top != null) this.top = interpolateFactory.number(start.top, target.top, t);
9741		if (target.bottom != null && start.bottom != null) this.bottom = interpolateFactory.number(start.bottom, target.bottom, t);
9742		if (target.left != null && start.left != null) this.left = interpolateFactory.number(start.left, target.left, t);
9743		if (target.right != null && start.right != null) this.right = interpolateFactory.number(start.right, target.right, t);
9744		return this;
9745	}
9746	/**
9747	* Utility method that computes the new apparent center or vanishing point after applying insets.
9748	* This is in pixels and with the top left being (0.0) and +y being downwards.
9749	*
9750	* @param width - the width
9751	* @param height - the height
9752	* @returns the point
9753	*/
9754	getCenter(width, height) {
9755		const x = clamp((this.left + width - this.right) / 2, 0, width);
9756		const y = clamp((this.top + height - this.bottom) / 2, 0, height);
9757		return new Point(x, y);
9758	}
9759	equals(other) {
9760		return this.top === other.top && this.bottom === other.bottom && this.left === other.left && this.right === other.right;
9761	}
9762	clone() {
9763		return new EdgeInsets(this.top, this.bottom, this.left, this.right);
9764	}
9765	/**
9766	* Returns the current state as json, useful when you want to have a
9767	* read-only representation of the inset.
9768	*
9769	* @returns state as json
9770	*/
9771	toJSON() {
9772		return {
9773			top: this.top,
9774			bottom: this.bottom,
9775			left: this.left,
9776			right: this.right
9777		};
9778	}
9779};
9780//#endregion
9781//#region src/geo/transform_helper.ts
9782/**
9783* If a path crossing the antimeridian would be shorter, extend the final coordinate so that
9784* interpolating between the two endpoints will cross it.
9785* @param center - The LngLat object of the desired center. This object will be mutated.
9786*/
9787function normalizeCenter(tr, center) {
9788	if (!tr.renderWorldCopies || tr.lngRange) return;
9789	const delta = center.lng - tr.center.lng;
9790	center.lng += delta > 180 ? -360 : delta < -180 ? 360 : 0;
9791}
9792function getTileZoom(zoom) {
9793	return Math.max(0, Math.floor(zoom));
9794}
9795/**
9796* @internal
9797* This class stores all values that define a transform's state,
9798* such as center, zoom, minZoom, etc.
9799* This can be used as a helper for implementing the ITransform interface.
9800*/
9801var TransformHelper = class {
9802	constructor(callbacks, options) {
9803		this.applyConstrain = (lngLat, zoom) => {
9804			if (this._constrainOverride !== null) return this._constrainOverride(lngLat, zoom);
9805			else return this._callbacks.defaultConstrain(lngLat, zoom);
9806		};
9807		this._callbacks = callbacks;
9808		this._tileSize = 512;
9809		this._renderWorldCopies = options?.renderWorldCopies === void 0 ? true : !!options?.renderWorldCopies;
9810		this._minZoom = options?.minZoom || 0;
9811		this._maxZoom = options?.maxZoom || 22;
9812		this._minPitch = options?.minPitch === void 0 || options?.minPitch === null ? 0 : options?.minPitch;
9813		this._maxPitch = options?.maxPitch === void 0 || options?.maxPitch === null ? 60 : options?.maxPitch;
9814		this._constrainOverride = options?.constrainOverride ?? null;
9815		this.setMaxBounds();
9816		this._width = 0;
9817		this._height = 0;
9818		this._center = new LngLat(0, 0);
9819		this._elevation = 0;
9820		this._zoom = 0;
9821		this._tileZoom = getTileZoom(this._zoom);
9822		this._scale = zoomScale(this._zoom);
9823		this._bearingInRadians = 0;
9824		this._fovInRadians = .6435011087932844;
9825		this._pitchInRadians = 0;
9826		this._rollInRadians = 0;
9827		this._unmodified = true;
9828		this._edgeInsets = new EdgeInsets();
9829		this._minElevationForCurrentTile = 0;
9830		this._autoCalculateNearFarZ = true;
9831	}
9832	apply(thatI, constrain) {
9833		this._constrainOverride = thatI.constrainOverride;
9834		this._latRange = thatI.latRange;
9835		this._lngRange = thatI.lngRange;
9836		this._width = thatI.width;
9837		this._height = thatI.height;
9838		this._center = thatI.center;
9839		this._elevation = thatI.elevation;
9840		this._minElevationForCurrentTile = thatI.minElevationForCurrentTile;
9841		this._zoom = thatI.zoom;
9842		this._tileZoom = getTileZoom(this._zoom);
9843		this._scale = zoomScale(this._zoom);
9844		this._bearingInRadians = thatI.bearingInRadians;
9845		this._fovInRadians = thatI.fovInRadians;
9846		this._pitchInRadians = thatI.pitchInRadians;
9847		this._rollInRadians = thatI.rollInRadians;
9848		this._unmodified = thatI.unmodified;
9849		this._edgeInsets = new EdgeInsets(thatI.padding.top, thatI.padding.bottom, thatI.padding.left, thatI.padding.right);
9850		this._minZoom = thatI.minZoom;
9851		this._maxZoom = thatI.maxZoom;
9852		this._minPitch = thatI.minPitch;
9853		this._maxPitch = thatI.maxPitch;
9854		this._renderWorldCopies = thatI.renderWorldCopies;
9855		this._cameraToCenterDistance = thatI.cameraToCenterDistance;
9856		this._nearZ = thatI.nearZ;
9857		this._farZ = thatI.farZ;
9858		this._autoCalculateNearFarZ = thatI.autoCalculateNearFarZ;
9859		if (constrain) this.constrainInternal();
9860		this._calcMatrices();
9861	}
9862	get pixelsToClipSpaceMatrix() {
9863		return this._pixelsToClipSpaceMatrix;
9864	}
9865	get clipSpaceToPixelsMatrix() {
9866		return this._clipSpaceToPixelsMatrix;
9867	}
9868	get minElevationForCurrentTile() {
9869		return this._minElevationForCurrentTile;
9870	}
9871	setMinElevationForCurrentTile(ele) {
9872		this._minElevationForCurrentTile = ele;
9873	}
9874	get tileSize() {
9875		return this._tileSize;
9876	}
9877	get tileZoom() {
9878		return this._tileZoom;
9879	}
9880	get scale() {
9881		return this._scale;
9882	}
9883	/**
9884	* Gets the transform's width in pixels. Use {@link resize} to set the transform's size.
9885	*/
9886	get width() {
9887		return this._width;
9888	}
9889	/**
9890	* Gets the transform's height in pixels. Use {@link resize} to set the transform's size.
9891	*/
9892	get height() {
9893		return this._height;
9894	}
9895	/**
9896	* Gets the transform's bearing in radians.
9897	*/
9898	get bearingInRadians() {
9899		return this._bearingInRadians;
9900	}
9901	get lngRange() {
9902		return this._lngRange;
9903	}
9904	get latRange() {
9905		return this._latRange;
9906	}
9907	get pixelsToGLUnits() {
9908		return this._pixelsToGLUnits;
9909	}
9910	get minZoom() {
9911		return this._minZoom;
9912	}
9913	setMinZoom(zoom) {
9914		if (this._minZoom === zoom) return;
9915		this._minZoom = zoom;
9916		const unmodified = this._unmodified;
9917		this.setZoom(this.applyConstrain(this._center, this.zoom).zoom);
9918		this._unmodified = unmodified;
9919	}
9920	get maxZoom() {
9921		return this._maxZoom;
9922	}
9923	setMaxZoom(zoom) {
9924		if (this._maxZoom === zoom) return;
9925		this._maxZoom = zoom;
9926		const unmodified = this._unmodified;
9927		this.setZoom(this.applyConstrain(this._center, this.zoom).zoom);
9928		this._unmodified = unmodified;
9929	}
9930	get minPitch() {
9931		return this._minPitch;
9932	}
9933	setMinPitch(pitch) {
9934		if (this._minPitch === pitch) return;
9935		this._minPitch = pitch;
9936		const unmodified = this._unmodified;
9937		this.setPitch(Math.max(this.pitch, pitch));
9938		this._unmodified = unmodified;
9939	}
9940	get maxPitch() {
9941		return this._maxPitch;
9942	}
9943	setMaxPitch(pitch) {
9944		if (this._maxPitch === pitch) return;
9945		this._maxPitch = pitch;
9946		const unmodified = this._unmodified;
9947		this.setPitch(Math.min(this.pitch, pitch));
9948		this._unmodified = unmodified;
9949	}
9950	get renderWorldCopies() {
9951		return this._renderWorldCopies;
9952	}
9953	setRenderWorldCopies(renderWorldCopies) {
9954		if (renderWorldCopies === void 0) renderWorldCopies = true;
9955		else if (renderWorldCopies === null) renderWorldCopies = false;
9956		this._renderWorldCopies = renderWorldCopies;
9957	}
9958	get constrainOverride() {
9959		return this._constrainOverride;
9960	}
9961	setConstrainOverride(constrain) {
9962		if (constrain === void 0) constrain = null;
9963		if (this._constrainOverride === constrain) return;
9964		this._constrainOverride = constrain;
9965		this.constrainInternal();
9966		this._calcMatrices();
9967	}
9968	get worldSize() {
9969		return this._tileSize * this._scale;
9970	}
9971	get centerOffset() {
9972		return this.centerPoint._sub(this.size._div(2));
9973	}
9974	/**
9975	* Gets the transform's dimensions packed into a Point object.
9976	*/
9977	get size() {
9978		return new Point(this._width, this._height);
9979	}
9980	get bearing() {
9981		return this._bearingInRadians / Math.PI * 180;
9982	}
9983	setBearing(bearing) {
9984		const b = wrap(bearing, -180, 180) * Math.PI / 180;
9985		if (this._bearingInRadians === b) return;
9986		this._unmodified = false;
9987		this._bearingInRadians = b;
9988		this._calcMatrices();
9989		this._rotationMatrix = create$2();
9990		rotate(this._rotationMatrix, this._rotationMatrix, -this._bearingInRadians);
9991	}
9992	get rotationMatrix() {
9993		return this._rotationMatrix;
9994	}
9995	get pitchInRadians() {
9996		return this._pitchInRadians;
9997	}
9998	get pitch() {
9999		return this._pitchInRadians / Math.PI * 180;
10000	}
10001	setPitch(pitch) {
10002		const p = clamp(pitch, this.minPitch, this.maxPitch) / 180 * Math.PI;
10003		if (this._pitchInRadians === p) return;
10004		this._unmodified = false;
10005		this._pitchInRadians = p;
10006		this._calcMatrices();
10007	}
10008	get rollInRadians() {
10009		return this._rollInRadians;
10010	}
10011	get roll() {
10012		return this._rollInRadians / Math.PI * 180;
10013	}
10014	setRoll(roll) {
10015		const r = roll / 180 * Math.PI;
10016		if (this._rollInRadians === r) return;
10017		this._unmodified = false;
10018		this._rollInRadians = r;
10019		this._calcMatrices();
10020	}
10021	get fovInRadians() {
10022		return this._fovInRadians;
10023	}
10024	get fov() {
10025		return radiansToDegrees(this._fovInRadians);
10026	}
10027	setFov(fov) {
10028		fov = clamp(fov, .1, 150);
10029		if (this.fov === fov) return;
10030		this._unmodified = false;
10031		this._fovInRadians = degreesToRadians(fov);
10032		this._calcMatrices();
10033	}
10034	get zoom() {
10035		return this._zoom;
10036	}
10037	setZoom(zoom) {
10038		const constrainedZoom = this.applyConstrain(this._center, zoom).zoom;
10039		if (this._zoom === constrainedZoom) return;
10040		this._unmodified = false;
10041		this._zoom = constrainedZoom;
10042		this._tileZoom = Math.max(0, Math.floor(constrainedZoom));
10043		this._scale = zoomScale(constrainedZoom);
10044		this.constrainInternal();
10045		this._calcMatrices();
10046	}
10047	get center() {
10048		return this._center;
10049	}
10050	setCenter(center) {
10051		if (center.lat === this._center.lat && center.lng === this._center.lng) return;
10052		this._unmodified = false;
10053		this._center = center;
10054		this.constrainInternal();
10055		this._calcMatrices();
10056	}
10057	/**
10058	* Elevation at current center point, meters above sea level
10059	*/
10060	get elevation() {
10061		return this._elevation;
10062	}
10063	setElevation(elevation) {
10064		if (elevation === this._elevation) return;
10065		this._elevation = elevation;
10066		this.constrainInternal();
10067		this._calcMatrices();
10068	}
10069	get padding() {
10070		return this._edgeInsets.toJSON();
10071	}
10072	setPadding(padding) {
10073		if (this._edgeInsets.equals(padding)) return;
10074		this._unmodified = false;
10075		this._edgeInsets.interpolate(this._edgeInsets, padding, 1);
10076		this._calcMatrices();
10077	}
10078	/**
10079	* The center of the screen in pixels with the top-left corner being (0,0)
10080	* and +y axis pointing downwards. This accounts for padding.
10081	*/
10082	get centerPoint() {
10083		return this._edgeInsets.getCenter(this._width, this._height);
10084	}
10085	/**
10086	* @internal
10087	*/
10088	get pixelsPerMeter() {
10089		return this._pixelPerMeter;
10090	}
10091	get unmodified() {
10092		return this._unmodified;
10093	}
10094	get cameraToCenterDistance() {
10095		return this._cameraToCenterDistance;
10096	}
10097	get nearZ() {
10098		return this._nearZ;
10099	}
10100	get farZ() {
10101		return this._farZ;
10102	}
10103	get autoCalculateNearFarZ() {
10104		return this._autoCalculateNearFarZ;
10105	}
10106	overrideNearFarZ(nearZ, farZ) {
10107		this._autoCalculateNearFarZ = false;
10108		this._nearZ = nearZ;
10109		this._farZ = farZ;
10110		this._calcMatrices();
10111	}
10112	clearNearFarZOverride() {
10113		this._autoCalculateNearFarZ = true;
10114		this._calcMatrices();
10115	}
10116	/**
10117	* Returns if the padding params match
10118	*
10119	* @param padding - the padding to check against
10120	* @returns true if they are equal, false otherwise
10121	*/
10122	isPaddingEqual(padding) {
10123		return this._edgeInsets.equals(padding);
10124	}
10125	/**
10126	* Helper method to update edge-insets in place
10127	*
10128	* @param start - the starting padding
10129	* @param target - the target padding
10130	* @param t - the step/weight
10131	*/
10132	interpolatePadding(start, target, t) {
10133		this._unmodified = false;
10134		this._edgeInsets.interpolate(start, target, t);
10135		this.constrainInternal();
10136		this._calcMatrices();
10137	}
10138	resize(width, height, constrain = true) {
10139		this._width = width;
10140		this._height = height;
10141		if (constrain) this.constrainInternal();
10142		this._calcMatrices();
10143	}
10144	/**
10145	* Returns the maximum geographical bounds the map is constrained to, or `null` if none set.
10146	* @returns max bounds
10147	*/
10148	getMaxBounds() {
10149		if (this._latRange?.length !== 2 || this._lngRange?.length !== 2) return null;
10150		return new LngLatBounds([this._lngRange[0], this._latRange[0]], [this._lngRange[1], this._latRange[1]]);
10151	}
10152	/**
10153	* Sets or clears the map's geographical constraints.
10154	* @param bounds - A {@link LngLatBounds} object describing the new geographic boundaries of the map.
10155	*/
10156	setMaxBounds(bounds) {
10157		if (bounds) {
10158			this._lngRange = [bounds.getWest(), bounds.getEast()];
10159			this._latRange = [bounds.getSouth(), bounds.getNorth()];
10160			this.constrainInternal();
10161		} else {
10162			this._lngRange = null;
10163			this._latRange = [-MAX_VALID_LATITUDE, MAX_VALID_LATITUDE];
10164		}
10165	}
10166	/**
10167	* When the map is pitched, some of the 3D features that intersect a query will not intersect
10168	* the query at the surface of the earth. Instead the feature may be closer and only intersect
10169	* the query because it extrudes into the air.
10170	* @param queryGeometry - For point queries, the line from the query point to the "camera point",
10171	* for other geometries, the envelope of the query geometry and the "camera point"
10172	* @returns a geometry that includes all of the original query as well as all possible ares of the
10173	* screen where the *base* of a visible extrusion could be.
10174	*
10175	*/
10176	getCameraQueryGeometry(cameraPoint, queryGeometry) {
10177		if (queryGeometry.length === 1) return [queryGeometry[0], cameraPoint];
10178		else {
vendor: 15,237 bytes, lines 10179-10602
10179			const { minX, minY, maxX, maxY } = Bounds.fromPoints(queryGeometry).extend(cameraPoint);
10180			return [
10181				new Point(minX, minY),
10182				new Point(maxX, minY),
10183				new Point(maxX, maxY),
10184				new Point(minX, maxY),
10185				new Point(minX, minY)
10186			];
10187		}
10188	}
10189	/**
10190	* @internal
10191	* Snaps the transform's center, zoom, etc. into the valid range.
10192	*/
10193	constrainInternal() {
10194		if (!this.center || !this._width || !this._height || this._constraining) return;
10195		this._constraining = true;
10196		const unmodified = this._unmodified;
10197		const { center, zoom } = this.applyConstrain(this.center, this.zoom);
10198		this.setCenter(center);
10199		this.setZoom(zoom);
10200		this._unmodified = unmodified;
10201		this._constraining = false;
10202	}
10203	/**
10204	* This function is called every time one of the transform's defining properties (center, pitch, etc.) changes.
10205	* This function should update the transform's internal data, such as matrices.
10206	* Any derived `_calcMatrices` function should also call the base function first. The base function only depends on the `_width` and `_height` fields.
10207	* While either dimension is zero there is no view to build, so the derived function is not called at all.
10208	*/
10209	_calcMatrices() {
10210		this._pixelPerMeter = mercatorZfromAltitude(1, this.center.lat) * this.worldSize;
10211		if (!this._width || !this._height) return;
10212		this._pixelsToGLUnits = [2 / this._width, -2 / this._height];
10213		let m = identity(/* @__PURE__ */ new Float64Array(16));
10214		scale(m, m, [
10215			this._width / 2,
10216			-this._height / 2,
10217			1
10218		]);
10219		translate(m, m, [
10220			1,
10221			-1,
10222			0
10223		]);
10224		this._clipSpaceToPixelsMatrix = m;
10225		m = identity(/* @__PURE__ */ new Float64Array(16));
10226		scale(m, m, [
10227			1,
10228			-1,
10229			1
10230		]);
10231		translate(m, m, [
10232			-1,
10233			-1,
10234			0
10235		]);
10236		scale(m, m, [
10237			2 / this._width,
10238			2 / this._height,
10239			1
10240		]);
10241		this._pixelsToClipSpaceMatrix = m;
10242		const halfFov = this.fovInRadians / 2;
10243		this._cameraToCenterDistance = .5 / Math.tan(halfFov) * this._height;
10244		this._callbacks.calcMatrices();
10245	}
10246	calculateCenterFromCameraLngLatAlt(lnglat, alt, bearing, pitch) {
10247		const cameraBearing = bearing !== void 0 ? bearing : this.bearing;
10248		const cameraPitch = pitch = pitch !== void 0 ? pitch : this.pitch;
10249		const { distanceToCenter, clampedElevation } = this._distanceToCenterFromAltElevationPitch(alt, this.elevation, cameraPitch);
10250		const { x, y } = cameraDirectionFromPitchBearing(cameraPitch, cameraBearing);
10251		const camMercator = MercatorCoordinate.fromLngLat(lnglat, alt);
10252		let metersPerMercUnit = altitudeFromMercatorZ(1, camMercator.y);
10253		let centerMercator;
10254		let dMercator;
10255		let iter = 0;
10256		const maxIter = 10;
10257		do {
10258			iter += 1;
10259			if (iter > maxIter) break;
10260			dMercator = distanceToCenter / metersPerMercUnit;
10261			const dx = x * dMercator;
10262			const dy = y * dMercator;
10263			centerMercator = new MercatorCoordinate(camMercator.x + dx, camMercator.y + dy);
10264			metersPerMercUnit = 1 / centerMercator.meterInMercatorCoordinateUnits();
10265		} while (Math.abs(distanceToCenter - dMercator * metersPerMercUnit) > 1e-12);
10266		return {
10267			center: centerMercator.toLngLat(),
10268			elevation: clampedElevation,
10269			zoom: scaleZoom(this.height / 2 / Math.tan(this.fovInRadians / 2) / dMercator / this.tileSize)
10270		};
10271	}
10272	recalculateZoomAndCenter(elevation) {
10273		if (this.elevation - elevation === 0) return;
10274		const mercUnitsPerPixel = 1 / this.worldSize;
10275		const originalPixelsPerMeter = mercatorZfromAltitude(1, this.center.lat) * this.worldSize;
10276		const originalCenterMercator = MercatorCoordinate.fromLngLat(this.center, this.elevation);
10277		const originalCenterPixelX = originalCenterMercator.x / mercUnitsPerPixel;
10278		const originalCenterPixelY = originalCenterMercator.y / mercUnitsPerPixel;
10279		const originalCenterPixelZ = originalCenterMercator.z / mercUnitsPerPixel;
10280		const cameraPitch = this.pitch;
10281		const cameraBearing = this.bearing;
10282		const { x, y, z } = cameraDirectionFromPitchBearing(cameraPitch, cameraBearing);
10283		const dCamPixel = this.cameraToCenterDistance;
10284		const camPixelX = originalCenterPixelX + dCamPixel * -x;
10285		const camPixelY = originalCenterPixelY + dCamPixel * -y;
10286		const camPixelZ = originalCenterPixelZ + dCamPixel * z;
10287		const { distanceToCenter, clampedElevation } = this._distanceToCenterFromAltElevationPitch(camPixelZ / originalPixelsPerMeter, elevation, cameraPitch);
10288		const distanceToCenterPixels = distanceToCenter * originalPixelsPerMeter;
10289		const centerPixelX = camPixelX + x * distanceToCenterPixels;
10290		const centerPixelY = camPixelY + y * distanceToCenterPixels;
10291		const center = new MercatorCoordinate(centerPixelX * mercUnitsPerPixel, centerPixelY * mercUnitsPerPixel, 0).toLngLat();
10292		const mercUnitsPerMeter = mercatorZfromAltitude(1, center.lat);
10293		const zoom = scaleZoom(this.height / 2 / Math.tan(this.fovInRadians / 2) / distanceToCenter / mercUnitsPerMeter / this.tileSize);
10294		this._elevation = clampedElevation;
10295		this._center = center;
10296		this.setZoom(zoom);
10297	}
10298	_distanceToCenterFromAltElevationPitch(alt, elevation, pitch) {
10299		const dzNormalized = -Math.cos(degreesToRadians(pitch));
10300		const altitudeAGL = alt - elevation;
10301		let distanceToCenter;
10302		let clampedElevation = elevation;
10303		if (dzNormalized * altitudeAGL >= 0 || Math.abs(dzNormalized) < .1) {
10304			distanceToCenter = 1e4;
10305			clampedElevation = alt + distanceToCenter * dzNormalized;
10306		} else distanceToCenter = -altitudeAGL / dzNormalized;
10307		return {
10308			distanceToCenter,
10309			clampedElevation
10310		};
10311	}
10312	getCameraPoint() {
10313		const pitch = this.pitchInRadians;
10314		const offset = Math.tan(pitch) * (this.cameraToCenterDistance || 1);
10315		return this.centerPoint.add(new Point(offset * Math.sin(this.rollInRadians), offset * Math.cos(this.rollInRadians)));
10316	}
10317	getMercatorTileCoordinates(overscaledTileID) {
10318		if (!overscaledTileID) return [
10319			0,
10320			0,
10321			1,
10322			1
10323		];
10324		const scale = overscaledTileID.canonical.z >= 0 ? 1 << overscaledTileID.canonical.z : Math.pow(2, overscaledTileID.canonical.z);
10325		return [
10326			overscaledTileID.canonical.x / scale,
10327			overscaledTileID.canonical.y / scale,
10328			1 / scale / EXTENT,
10329			1 / scale / EXTENT
10330		];
10331	}
10332};
10333//#endregion
10334//#region src/geo/projection/mercator_covering_tiles_details_provider.ts
10335var MercatorCoveringTilesDetailsProvider = class {
10336	distanceToTile2d(pointX, pointY, _tileID, boundingVolume) {
10337		const aabb = boundingVolume;
10338		const distanceX = aabb.distanceX([pointX, pointY]);
10339		const distanceY = aabb.distanceY([pointX, pointY]);
10340		return Math.hypot(distanceX, distanceY);
10341	}
10342	/**
10343	* Returns the wrap value for a given tile, computed so that tiles will remain loaded when crossing the antimeridian.
10344	*/
10345	getWrap(centerCoord, tileID, parentWrap) {
10346		return parentWrap;
10347	}
10348	/**
10349	* Returns the AABB of the specified tile.
10350	* @param tileID - Tile x, y and z for zoom.
10351	*/
10352	getTileBoundingVolume(tileID, wrap, elevation, options) {
10353		let minElevation = Math.min(0, elevation);
10354		let maxElevation = Math.max(0, elevation);
10355		if (options?.terrain) {
10356			const overscaledTileID = new OverscaledTileID(tileID.z, wrap, tileID.z, tileID.x, tileID.y);
10357			const minMax = options.terrain.getMinMaxElevation(overscaledTileID);
10358			minElevation = minMax.minElevation ?? minElevation;
10359			maxElevation = minMax.maxElevation ?? maxElevation;
10360		}
10361		const numTiles = 1 << tileID.z;
10362		return new Aabb([
10363			wrap + tileID.x / numTiles,
10364			tileID.y / numTiles,
10365			minElevation
10366		], [
10367			wrap + (tileID.x + 1) / numTiles,
10368			(tileID.y + 1) / numTiles,
10369			maxElevation
10370		]);
10371	}
10372	allowVariableZoom(transform, options) {
10373		const zfov = transform.fov * (Math.abs(Math.cos(transform.rollInRadians)) * transform.height + Math.abs(Math.sin(transform.rollInRadians)) * transform.width) / transform.height;
10374		const maxConstantZoomPitch = clamp(78.5 - zfov / 2, 0, 60);
10375		return !!options.terrain || transform.pitch > maxConstantZoomPitch;
10376	}
10377	allowWorldCopies() {
10378		return true;
10379	}
10380	prepareNextFrame() {}
10381};
10382//#endregion
10383//#region src/data/pos3d_attributes.ts
10384const pos3dAttributes = createLayout([{
10385	name: "a_pos3d",
10386	type: "Int16",
10387	components: 3
10388}]);
10389//#endregion
10390//#region src/tile/terrain_tile_manager.ts
10391/**
10392* @internal
10393* This class is a helper for the Terrain-class, it:
10394*
10395* - loads raster-dem tiles
10396* - manages all renderToTexture tiles.
10397* - caches previous rendered tiles.
10398* - finds all necessary renderToTexture tiles for a OverscaledTileID area
10399* - finds the corresponding raster-dem tile for OverscaledTileID
10400*/
10401var TerrainTileManager = class extends Evented {
10402	constructor(tileManager) {
10403		super();
10404		this._lastTilesetChange = now();
10405		this.tileManager = tileManager;
10406		this._tiles = {};
10407		this._renderableTilesKeys = [];
10408		this._sourceTileCache = {};
10409		this.minzoom = 0;
10410		this.maxzoom = 22;
10411		this.deltaZoom = 1;
10412		this.tileSize = tileManager._source.tileSize * 2 ** this.deltaZoom;
10413		tileManager.usedForTerrain = true;
10414		tileManager.tileSize = this.tileSize;
10415	}
10416	destruct() {
10417		this.tileManager.usedForTerrain = false;
10418		this.tileManager.tileSize = null;
10419		this.releaseAllRTT();
10420	}
10421	getSource() {
10422		return this.tileManager._source;
10423	}
10424	/**
10425	* Load Terrain Tiles, create internal render-to-texture tiles, free GPU memory.
10426	* @param transform - the operation to do
10427	* @param terrain - the terrain
10428	* @returns true when the set of renderable tiles changed
10429	*/
10430	update(transform, terrain) {
10431		this.tileManager.update(transform, terrain);
10432		this._renderableTilesKeys = [];
10433		const keys = {};
10434		let changed = false;
10435		for (const tileID of coveringTiles(transform, {
10436			tileSize: this.tileSize,
10437			minzoom: this.minzoom,
10438			maxzoom: this.maxzoom,
10439			reparseOverscaled: false,
10440			terrain
10441		})) {
10442			keys[tileID.key] = true;
10443			this._renderableTilesKeys.push(tileID.key);
10444			if (!this._tiles[tileID.key]) {
10445				tileID.terrainRttPosMatrix32f = /* @__PURE__ */ new Float32Array(16);
10446				ortho(tileID.terrainRttPosMatrix32f, 0, EXTENT, EXTENT, 0, 0, 1);
10447				this._tiles[tileID.key] = new Tile(tileID, this.tileSize);
10448				this._lastTilesetChange = now();
10449				changed = true;
10450			}
10451		}
10452		for (const key in this._tiles) if (!keys[key]) {
10453			this._tiles[key].releaseRTT(this.tileManager.map.painter);
10454			delete this._tiles[key];
10455			changed = true;
10456		}
10457		return changed;
10458	}
10459	/**
10460	* Release the RTT objects for `tileID` (and its ancestors/descendants),
10461	*/
10462	releaseRTT(tileID) {
10463		for (const key in this._tiles) {
10464			const tile = this._tiles[key];
10465			if (tile.tileID.equals(tileID) || tile.tileID.isChildOf(tileID) || tileID.isChildOf(tile.tileID)) tile.releaseRTT(this.tileManager.map.painter);
10466		}
10467	}
10468	/**
10469	* Release the A RTT objects for all tiles.
10470	*/
10471	releaseAllRTT() {
10472		for (const key in this._tiles) this._tiles[key].releaseRTT(this.tileManager.map.painter);
10473	}
10474	/**
10475	* get a list of tiles, which are loaded and should be rendered in the current scene
10476	* @returns the renderable tiles
10477	*/
10478	getRenderableTiles() {
10479		return this._renderableTilesKeys.map((key) => this.getTileByID(key));
10480	}
10481	/**
10482	* get terrain tile by the TileID key
10483	* @param id - the tile id
10484	* @returns the tile
10485	*/
10486	getTileByID(id) {
10487		return this._tiles[id];
10488	}
10489	/**
10490	* Searches for the corresponding current renderable terrain-tiles
10491	* @param tileID - the tile to look for
10492	* @returns the tiles that were found
10493	*/
10494	getTerrainCoords(tileID, terrainTileRanges) {
10495		if (terrainTileRanges) return this._getTerrainCoordsForTileRanges(tileID, terrainTileRanges);
10496		else return this._getTerrainCoordsForRegularTile(tileID);
10497	}
10498	/**
10499	* Searches for the corresponding current renderable terrain-tiles.
10500	* Includes terrain tiles that are either:
10501	* - the same as the tileID
10502	* - a parent of the tileID
10503	* - a child of the tileID
10504	* @param tileID - the tile to look for
10505	* @returns the tiles that were found
10506	*/
10507	_getTerrainCoordsForRegularTile(tileID) {
10508		const coords = {};
10509		for (const key of this._renderableTilesKeys) {
10510			const terrainTileID = this._tiles[key].tileID;
10511			const coord = tileID.clone();
10512			const mat = createMat4f64();
10513			if (terrainTileID.canonical.equals(tileID.canonical)) ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
10514			else if (terrainTileID.canonical.isChildOf(tileID.canonical)) {
10515				const dz = terrainTileID.canonical.z - tileID.canonical.z;
10516				const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz);
10517				const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz);
10518				const size = EXTENT >> dz;
10519				ortho(mat, 0, size, size, 0, 0, 1);
10520				translate(mat, mat, [
10521					-dx * size,
10522					-dy * size,
10523					0
10524				]);
10525			} else if (tileID.canonical.isChildOf(terrainTileID.canonical)) {
10526				const dz = tileID.canonical.z - terrainTileID.canonical.z;
10527				const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
10528				const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
10529				const size = EXTENT >> dz;
10530				ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
10531				translate(mat, mat, [
10532					dx * size,
10533					dy * size,
10534					0
10535				]);
10536				scale(mat, mat, [
10537					1 / 2 ** dz,
10538					1 / 2 ** dz,
10539					0
10540				]);
10541			} else continue;
10542			coord.terrainRttPosMatrix32f = new Float32Array(mat);
10543			coords[key] = coord;
10544		}
10545		return coords;
10546	}
10547	/**
10548	* Searches for the corresponding current renderable terrain-tiles.
10549	* Includes terrain tiles that are within terrain tile ranges.
10550	* @param tileID - the tile to look for
10551	* @returns the tiles that were found
10552	*/
10553	_getTerrainCoordsForTileRanges(tileID, terrainTileRanges) {
10554		const coords = {};
10555		for (const key of this._renderableTilesKeys) {
10556			const terrainTileID = this._tiles[key].tileID;
10557			if (!this._isWithinTileRanges(terrainTileID, terrainTileRanges)) continue;
10558			const coord = tileID.clone();
10559			const mat = createMat4f64();
10560			if (terrainTileID.canonical.z === tileID.canonical.z) {
10561				const dx = tileID.canonical.x - terrainTileID.canonical.x + tileID.wrap * (1 << tileID.canonical.z);
10562				const dy = tileID.canonical.y - terrainTileID.canonical.y;
10563				ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
10564				translate(mat, mat, [
10565					dx * EXTENT,
10566					dy * EXTENT,
10567					0
10568				]);
10569			} else if (terrainTileID.canonical.z > tileID.canonical.z) {
10570				const dz = terrainTileID.canonical.z - tileID.canonical.z;
10571				const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz) + tileID.wrap * (1 << terrainTileID.canonical.z);
10572				const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz);
10573				const dx2 = tileID.canonical.x - (terrainTileID.canonical.x >> dz);
10574				const dy2 = tileID.canonical.y - (terrainTileID.canonical.y >> dz);
10575				const size = EXTENT >> dz;
10576				ortho(mat, 0, size, size, 0, 0, 1);
10577				translate(mat, mat, [
10578					-dx * size + dx2 * EXTENT,
10579					-dy * size + dy2 * EXTENT,
10580					0
10581				]);
10582			} else {
10583				const dz = tileID.canonical.z - terrainTileID.canonical.z;
10584				const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz) + tileID.wrap * (1 << tileID.canonical.z);
10585				const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
10586				const dx2 = (tileID.canonical.x >> dz) - terrainTileID.canonical.x;
10587				const dy2 = (tileID.canonical.y >> dz) - terrainTileID.canonical.y;
10588				const size = EXTENT << dz;
10589				ortho(mat, 0, size, size, 0, 0, 1);
10590				translate(mat, mat, [
10591					dx * EXTENT + dx2 * size,
10592					dy * EXTENT + dy2 * size,
10593					0
10594				]);
10595			}
10596			coord.terrainRttPosMatrix32f = new Float32Array(mat);
10597			coords[key] = coord;
10598		}
10599		return coords;
10600	}
10601	/**
10602	* find the covering raster-dem tile
vendor: 37,543 bytes, lines 10603-11610
10603	* @param tileID - the tile to look for
10604	* @param searchForDEM - Optional parameter to search for (parent) source tiles with loaded dem.
10605	* @returns the tile
10606	*/
10607	getSourceTile(tileID, searchForDEM) {
10608		const source = this.tileManager._source;
10609		let z = tileID.overscaledZ - this.deltaZoom;
10610		if (z > source.maxzoom) z = source.maxzoom;
10611		if (z < source.minzoom) return void 0;
10612		this._sourceTileCache[tileID.key] ||= tileID.scaledTo(z).key;
10613		let tile = this.findTileInCaches(this._sourceTileCache[tileID.key]);
10614		if (!tile?.dem && searchForDEM) while (z >= source.minzoom && !tile?.dem) tile = this.findTileInCaches(tileID.scaledTo(z--).key);
10615		return tile;
10616	}
10617	findTileInCaches(key) {
10618		let tile = this.tileManager.getTileByID(key);
10619		if (tile) return tile;
10620		tile = this.tileManager._outOfViewCache.getByKey(key);
10621		return tile;
10622	}
10623	/**
10624	* gets whether any tiles were loaded after a specific time. This is used to update the depth framebuffer.
10625	* @param time - the time
10626	* @returns true if any tiles came into view at or after the specified time
10627	*/
10628	anyTilesAfterTime(time = now()) {
10629		return this._lastTilesetChange >= time;
10630	}
10631	/**
10632	* Checks whether a tile is within the canonical tile ranges.
10633	* @param tileID - Tile to check
10634	* @param canonicalTileRanges - Canonical tile ranges
10635	* @returns
10636	*/
10637	_isWithinTileRanges(tileID, canonicalTileRanges) {
10638		const range = canonicalTileRanges[tileID.canonical.z];
10639		return !!range && (tileID.wrap > range.minWrap || tileID.wrap < range.maxWrap || tileID.canonical.x >= range.minTileXWrapped && tileID.canonical.x <= range.maxTileXWrapped && tileID.canonical.y >= range.minTileY && tileID.canonical.y <= range.maxTileY);
10640	}
10641};
10642//#endregion
10643//#region src/render/terrain.ts
10644const MAX_BISECTIONS = 40;
10645const HIT_EPSILON_M = 1e-6;
10646/** Keeps the elevation bracket non-degenerate when the terrain is entirely flat, such as unloaded DEMs. */
10647const BRACKET_PADDING_M = 10;
10648/** Tile coordinates run from 0 up to but not including `EXTENT`; a point on the far edge is clamped back into the tile. */
10649const MAX_TILE_COORD = EXTENT * (1 - 1e-12);
10650/**
10651* Offset, in DEM pixels, from a tile coordinate scaled by `dim` to the pixel index `DEMData.sampleBilinear` expects.
10652* DEM pixel `i` describes the cell centred at tile coordinate `(i + 0.5) / dim`, the same placement hillshade and
10653* color-relief use, so a sample between two cell centres interpolates the pixels on either side of it.
10654*/
10655const DEM_CELL_CENTER_OFFSET = -.5;
10656/**
10657* @internal
10658* This is the main class which handles most of the 3D Terrain logic. It has the following topics:
10659*
10660* 1. loads raster-dem tiles via the internal tileManager this.tileManager
10661* 2. creates a depth-framebuffer, which is used to calculate the visibility of coordinates
10662* 3. stores all render-to-texture tiles in the this.tileManager._tiles
10663* 4. calculates the elevation for a specific tile-coordinate
10664* 5. creates a terrain-mesh
10665*
10666* A note about the GPU resource-usage:
10667*
10668* Framebuffers:
10669*
10670* - one for the depth framebuffer with the size of the map-div.
10671* - one for rendering a tile to texture with the size of tileSize (= 512x512).
10672*
10673* Textures:
10674*
10675* - one texture for an empty raster-dem tile with size 1x1
10676* - one texture for an empty depth-buffer, when terrain is disabled with size 1x1
10677* - one texture for an each loaded raster-dem with size of the source.tileSize
10678* - one texture for the depth-framebuffer with the size of the map-div.
10679* - finally for each render-to-texture tile (= this._tiles) a set of textures
10680* for each render stack (The stack-concept is documented in painter.ts).
10681*
10682* Normally there exists 1-3 Textures per tile, depending on the stylesheet.
10683* Each Textures has the size 2*tileSize (= 1024x1024). Also there exists a
10684* cache of the last 150 newest rendered tiles.
10685*
10686*/
10687var Terrain = class {
10688	constructor(painter, tileManager, options, terrainSkirtLength = "auto") {
10689		this._meshCache = {};
10690		this.painter = painter;
10691		this.tileManager = new TerrainTileManager(tileManager);
10692		this.options = options;
10693		this.exaggeration = typeof options.exaggeration === "number" ? options.exaggeration : 1;
10694		this._terrainSkirtLength = terrainSkirtLength;
10695		this.qualityFactor = 2;
10696		this.meshSize = 128;
10697		this._demMatrixCache = /* @__PURE__ */ new Map();
10698		this._elevationSamplerCache = /* @__PURE__ */ new Map();
10699	}
10700	destroy() {
10701		if (this._fbo) {
10702			this._fbo.destroy();
10703			this._fbo = null;
10704		}
10705		if (this._fboDepthTexture) {
10706			this._fboDepthTexture.destroy();
10707			this._fboDepthTexture = null;
10708		}
10709		if (this._emptyDemTexture) {
10710			this._emptyDemTexture.destroy();
10711			this._emptyDemTexture = null;
10712		}
10713		if (this._emptyDepthTexture) {
10714			this._emptyDepthTexture.destroy();
10715			this._emptyDepthTexture = null;
10716		}
10717		for (const key in this._meshCache) this._meshCache[key].destroy();
10718		this._meshCache = {};
10719		this.tileManager.destruct();
10720	}
10721	/**
10722	* Get the elevation-value from original dem-data for a given tile-coordinate.
10723	* Coordinates that fall outside `[0, extent)` are normalized to the
10724	* appropriate neighbor tile before lookup.
10725	* @param tileID - the tile to get the elevation for
10726	* @param x - x coordinate relative to the tile, may be outside `[0, extent)`
10727	* @param y - y coordinate relative to the tile, may be outside `[0, extent)`
10728	* @param extent - optional, default 8192
10729	* @returns the elevation
10730	*/
10731	getDEMElevation(tileID, x, y, extent = EXTENT) {
10732		const normalized = tileID.normalizeCoordinates(x, y, extent);
10733		if (!normalized) return 0;
10734		const sampler = this.getElevationSampler(normalized.tileID);
10735		return sampler ? sampler(normalized.x, normalized.y, extent) : 0;
10736	}
10737	/**
10738	* Get the elevation for given {@link LngLat} in respect of exaggeration.
10739	* @param lnglat - the location
10740	* @param zoom - the zoom, use {@link getElevationForLngLat} if you don't want a specific zoom level, but more accurate results.
10741	* @returns the elevation
10742	*/
10743	getElevationForLngLatZoom(lnglat, zoom) {
10744		if (!isInBoundsForZoomLngLat(zoom, lnglat.wrap())) return 0;
10745		const { tileID, mercatorX, mercatorY } = this._getOverscaledTileIDFromLngLatZoom(lnglat, zoom);
10746		return this.getElevation(tileID, mercatorX % EXTENT, mercatorY % EXTENT, EXTENT);
10747	}
10748	/**
10749	* Get the elevation for given {@link LngLat} in respect of exaggeration.
10750	* Where the location is covered by a rendered tile with loaded DEM data this samples the
10751	* rendered surface, so the result agrees with what is drawn; elsewhere it traverses up the
10752	* zoom levels to find the first tile with data to return.
10753	* @param lnglat - the location
10754	* @returns the elevation
10755	*/
10756	getElevationForLngLat(lnglat, transform) {
10757		const index = this.getCoverageIndex();
10758		if (index) {
10759			const mercator = MercatorCoordinate.fromLngLat(lnglat);
10760			const sample = sampleAt(index, this.exaggeration, mercator.x, mercator.y);
10761			if (sample.demLoaded) return sample.elevation;
10762		}
10763		const terrainCoveringTiles = coveringTiles(transform, {
10764			maxzoom: this.tileManager.maxzoom,
10765			minzoom: this.tileManager.minzoom,
10766			tileSize: 512,
10767			terrain: this
10768		});
10769		let zoom = 0;
10770		for (const tile of terrainCoveringTiles) if (tile.canonical.z > zoom) zoom = Math.min(tile.canonical.z, this.tileManager.maxzoom);
10771		return this.getElevationForLngLatZoom(lnglat, zoom);
10772	}
10773	/**
10774	* Get the elevation for given coordinate in respect of exaggeration.
10775	* @param tileID - the tile id
10776	* @param x - x coordinate relative to the tile, may be outside `[0, extent)`
10777	* @param y - y coordinate relative to the tile, may be outside `[0, extent)`
10778	* @param extent - optional, default 8192
10779	* @returns the elevation
10780	*/
10781	getElevation(tileID, x, y, extent = EXTENT) {
10782		return this.getDEMElevation(tileID, x, y, extent) * this.exaggeration;
10783	}
10784	/**
10785	* Clear CPU elevation samplers that may retain a previously selected DEM tile.
10786	* @internal
10787	*/
10788	resetElevationCache() {
10789		this._elevationSamplerCache.clear();
10790		this._coverageIndex = void 0;
10791	}
10792	/**
10793	* Index of the tiles the terrain currently renders, for sampling the terrain surface on the CPU.
10794	* Built on first use and kept until {@link resetElevationCache}.
10795	* @returns the index, or null when no terrain tile is renderable
10796	*/
10797	getCoverageIndex() {
10798		if (this._coverageIndex === void 0) this._coverageIndex = this._buildCoverageIndex();
10799		return this._coverageIndex;
10800	}
10801	_buildCoverageIndex() {
10802		const zooms = [];
10803		const samplerPerTile = /* @__PURE__ */ new Map();
10804		let minElevation = 0;
10805		let maxElevation = 0;
10806		for (const tile of this.tileManager.getRenderableTiles()) {
10807			if (!tile) continue;
10808			const { canonical, wrap } = tile.tileID;
10809			if (!zooms.includes(canonical.z)) zooms.push(canonical.z);
10810			const sampler = this.getElevationSampler(tile.tileID);
10811			samplerPerTile.set(`${wrap}/${canonical.z}/${canonical.x}/${canonical.y}`, sampler);
10812			const { minElevation: tileMin, maxElevation: tileMax } = this.getMinMaxElevation(tile.tileID);
10813			minElevation = Math.min(minElevation, tileMin ?? 0);
10814			maxElevation = Math.max(maxElevation, tileMax ?? 0);
10815		}
10816		if (samplerPerTile.size === 0) return null;
10817		zooms.sort((a, b) => b - a);
10818		return {
10819			zooms,
10820			samplerPerTile,
10821			minElevation: minElevation - BRACKET_PADDING_M,
10822			maxElevation: maxElevation + BRACKET_PADDING_M
10823		};
10824	}
10825	/**
10826	* Get a function that samples the raw DEM elevation of a tile, without exaggeration.
10827	* The sampler places DEM pixels at cell centres, matching `get_elevation` in the vertex shader prelude.
10828	* @param tileID - the tile id
10829	* @returns the sampler, or null when the tile's DEM data is not loaded
10830	*/
10831	getElevationSampler(tileID) {
10832		const key = tileID.key;
10833		const cachedSampler = this._elevationSamplerCache.get(key);
10834		if (cachedSampler) return cachedSampler;
10835		const sourceTile = this.tileManager.getSourceTile(tileID, true);
10836		const dem = sourceTile?.dem;
10837		if (!sourceTile || !dem) return null;
10838		const matrix = this._getDEMTileMatrix(tileID, sourceTile);
10839		const demPixelScaleX = matrix[0] * dem.dim;
10840		const demPixelScaleY = matrix[5] * dem.dim;
10841		const demPixelOffsetX = matrix[12] * dem.dim + DEM_CELL_CENTER_OFFSET;
10842		const demPixelOffsetY = matrix[13] * dem.dim + DEM_CELL_CENTER_OFFSET;
10843		const sampler = (x, y, extent) => {
10844			const extentScale = extent === 8192 ? 1 : EXTENT / extent;
10845			return dem.sampleBilinear(x * extentScale * demPixelScaleX + demPixelOffsetX, y * extentScale * demPixelScaleY + demPixelOffsetY);
10846		};
10847		this._elevationSamplerCache.set(key, sampler);
10848		return sampler;
10849	}
10850	/**
10851	* Get the matrix that maps a tile's coordinates into the DEM tile it is rendered with.
10852	* The transform is derived from the loaded DEM tile's own zoom level, not from the source's
10853	* declared maxzoom: getSourceTile falls back to a loaded parent tile while the deepest DEM
10854	* tile is still loading, and the scale and offset must match the tile that is actually used.
10855	* @param tileID - the tile id
10856	* @param sourceTile - the DEM tile that is used for this tile, either its own tile or a loaded parent
10857	* @returns the matrix that maps the tile's coordinates onto the DEM tile
10858	*/
10859	_getDEMTileMatrix(tileID, sourceTile) {
10860		const matrixKey = `${sourceTile.tileID.key}/${tileID.key}`;
10861		const cachedMatrix = this._demMatrixCache.get(matrixKey);
10862		if (cachedMatrix) return cachedMatrix;
10863		const dz = tileID.canonical.z - sourceTile.tileID.canonical.z;
10864		const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
10865		const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
10866		const demMatrix = fromScaling(/* @__PURE__ */ new Float64Array(16), [
10867			1 / (EXTENT << dz),
10868			1 / (EXTENT << dz),
10869			0
10870		]);
10871		translate(demMatrix, demMatrix, [
10872			dx * EXTENT,
10873			dy * EXTENT,
10874			0
10875		]);
10876		this._demMatrixCache.set(matrixKey, demMatrix);
10877		return demMatrix;
10878	}
10879	/**
10880	* returns a Terrain Object for a tile. Unless the tile corresponds to data (e.g. tile is loading), return a flat dem object
10881	* @param tileID - the tile to get the terrain for
10882	* @returns the terrain data to use in the program
10883	*/
10884	getTerrainData(tileID) {
10885		if (!this._emptyDemTexture) {
10886			const context = this.painter.context;
10887			const image = new RGBAImage({
10888				width: 1,
10889				height: 1
10890			}, /* @__PURE__ */ new Uint8Array(4));
10891			this._emptyDepthTexture = new Texture(context, image, context.gl.RGBA, { premultiply: false });
10892			this._emptyDemUnpack = [
10893				0,
10894				0,
10895				0,
10896				0
10897			];
10898			this._emptyDemTexture = new Texture(context, new RGBAImage({
10899				width: 1,
10900				height: 1
10901			}), context.gl.RGBA, { premultiply: false });
10902			this._emptyDemTexture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
10903			this._emptyDemMatrix = identity([]);
10904		}
10905		const sourceTile = this.tileManager.getSourceTile(tileID, true);
10906		if (sourceTile?.dem && (!sourceTile.demTexture || sourceTile.needsTerrainPrepare)) {
10907			const context = this.painter.context;
10908			sourceTile.demTexture ||= this.painter.getTileTexture(sourceTile.dem.stride);
10909			if (sourceTile.demTexture) sourceTile.demTexture.update(sourceTile.dem.getPixels(), { premultiply: false });
10910			else sourceTile.demTexture = new Texture(context, sourceTile.dem.getPixels(), context.gl.RGBA, { premultiply: false });
10911			sourceTile.demTexture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
10912			sourceTile.needsTerrainPrepare = false;
10913		}
10914		const terrainMatrix = sourceTile ? this._getDEMTileMatrix(tileID, sourceTile) : this._emptyDemMatrix;
10915		return {
10916			"u_depth": 2,
10917			"u_terrain": 3,
10918			"u_terrain_dim": sourceTile?.dem?.dim || 1,
10919			"u_terrain_matrix": terrainMatrix,
10920			"u_terrain_unpack": sourceTile?.dem?.getUnpackVector() || this._emptyDemUnpack,
10921			"u_terrain_exaggeration": this.exaggeration,
10922			texture: (sourceTile?.demTexture || this._emptyDemTexture).texture,
10923			depthTexture: (this._fboDepthTexture || this._emptyDepthTexture).texture,
10924			tile: sourceTile
10925		};
10926	}
10927	/**
10928	* get a framebuffer as big as the map-div, which will be used to render depth into a texture
10929	* @returns the frame buffer
10930	*/
10931	getFramebuffer() {
10932		const painter = this.painter;
10933		const width = painter.width / devicePixelRatio;
10934		const height = painter.height / devicePixelRatio;
10935		if (this._fbo && (this._fbo.width !== width || this._fbo.height !== height)) {
10936			this._fbo.destroy();
10937			this._fboDepthTexture.destroy();
10938			delete this._fbo;
10939			delete this._fboDepthTexture;
10940		}
10941		if (!this._fboDepthTexture) {
10942			this._fboDepthTexture = new Texture(painter.context, {
10943				width,
10944				height,
10945				data: null
10946			}, painter.context.gl.RGBA, { premultiply: false });
10947			this._fboDepthTexture.bind(painter.context.gl.NEAREST, painter.context.gl.CLAMP_TO_EDGE);
10948		}
10949		if (!this._fbo) {
10950			this._fbo = painter.context.createFramebuffer(width, height, true, false);
10951			this._fbo.depthAttachment.set(painter.context.createRenderbuffer(painter.context.gl.DEPTH_COMPONENT16, width, height));
10952		}
10953		this._fbo.colorAttachment.set(this._fboDepthTexture.texture);
10954		return this._fbo;
10955	}
10956	/**
10957	* create a regular mesh which will be used by all terrain-tiles
10958	* @returns the created regular mesh
10959	*/
10960	getTerrainMesh(tileId) {
10961		const globeEnabled = this.painter.style.projection?.transitionState > 0;
10962		const northPole = globeEnabled && tileId.canonical.y === 0;
10963		const southPole = globeEnabled && tileId.canonical.y === (1 << tileId.canonical.z) - 1;
10964		const key = `m_${northPole ? "n" : ""}_${southPole ? "s" : ""}`;
10965		if (this._meshCache[key]) return this._meshCache[key];
10966		const context = this.painter.context;
10967		const vertexArray = new Pos3dArray();
10968		const indexArray = new TriangleIndexArray();
10969		const meshSize = this.meshSize;
10970		const delta = EXTENT / meshSize;
10971		const meshSize2 = meshSize * meshSize;
10972		for (let y = 0; y <= meshSize; y++) for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, y * delta, 0);
10973		for (let y = 0; y < meshSize2; y += meshSize + 1) for (let x = 0; x < meshSize; x++) {
10974			indexArray.emplaceBack(x + y, meshSize + x + y + 1, meshSize + x + y + 2);
10975			indexArray.emplaceBack(x + y, meshSize + x + y + 2, x + y + 1);
10976		}
10977		if (this._terrainSkirtLength !== "none") this._buildSkirts(vertexArray, indexArray, meshSize, delta, northPole, southPole);
10978		const mesh = new Mesh(context.createVertexBuffer(vertexArray, pos3dAttributes.members), context.createIndexBuffer(indexArray), SegmentVector.simpleSegment(0, 0, vertexArray.length, indexArray.length));
10979		this._meshCache[key] = mesh;
10980		return mesh;
10981	}
10982	/**
10983	* Calculates the height of the tile skirts for the "auto" strategy.
10984	* @see {@link MapOptions.terrainSkirtLength}
10985	* @param zoom - current zoomlevel
10986	* @returns the elevation delta in meters
10987	*/
10988	getSkirtLength(zoom) {
10989		return 2 * Math.PI * earthRadius / Math.pow(2, Math.max(zoom, 0)) / 5;
10990	}
10991	getMinTileElevationForLngLatZoom(lnglat, zoom) {
10992		if (!isInBoundsForZoomLngLat(zoom, lnglat.wrap())) return 0;
10993		const { tileID } = this._getOverscaledTileIDFromLngLatZoom(lnglat, zoom);
10994		return this.getMinMaxElevation(tileID).minElevation ?? 0;
10995	}
10996	/**
10997	* Get the minimum and maximum elevation contained in a tile. This includes any
10998	* exaggeration included in the terrain.
10999	*
11000	* @param tileID - ID of the tile to be used as a source for the min/max elevation
11001	* @returns the minimum and maximum elevation found in the tile, including the terrain's
11002	* exaggeration
11003	*/
11004	getMinMaxElevation(tileID) {
11005		const tile = this.tileManager.getSourceTile(tileID, true);
11006		const minMax = {
11007			minElevation: null,
11008			maxElevation: null
11009		};
11010		if (tile?.dem) {
11011			minMax.minElevation = tile.dem.min * this.exaggeration;
11012			minMax.maxElevation = tile.dem.max * this.exaggeration;
11013		}
11014		return minMax;
11015	}
11016	_getOverscaledTileIDFromLngLatZoom(lnglat, zoom) {
11017		const mercatorCoordinate = MercatorCoordinate.fromLngLat(lnglat.wrap());
11018		const worldSize = (1 << zoom) * EXTENT;
11019		const mercatorX = mercatorCoordinate.x * worldSize;
11020		const mercatorY = mercatorCoordinate.y * worldSize;
11021		const tileX = Math.floor(mercatorX / EXTENT), tileY = Math.floor(mercatorY / EXTENT);
11022		return {
11023			tileID: new OverscaledTileID(zoom, 0, zoom, tileX, tileY),
11024			mercatorX,
11025			mercatorY
11026		};
11027	}
11028	/** Add an extra frame around the mesh to avoid hairline gaps (stitching) on tile boundaries with different zoomlevels.
11029	* @see {@link MapOptions.terrainSkirtLength}
11030	*/
11031	_buildSkirts(vertexArray, indexArray, meshSize, delta, northPole, southPole) {
11032		const offsetTop = vertexArray.length;
11033		const offsetTopEdge = 0;
11034		const offsetBottom = offsetTop + (meshSize + 1);
11035		const offsetBottomEdge = (meshSize + 1) * meshSize;
11036		const northY = northPole ? NORTH_POLE_Y : 0;
11037		const northZ = northPole ? 0 : 1;
11038		const southY = southPole ? SOUTH_POLE_Y : EXTENT;
11039		const southZ = southPole ? 0 : 1;
11040		for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, northY, northZ);
11041		for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, southY, southZ);
11042		for (let x = 0; x < meshSize; x++) {
11043			indexArray.emplaceBack(offsetBottomEdge + x, offsetBottom + x, offsetBottom + x + 1);
11044			indexArray.emplaceBack(offsetBottomEdge + x, offsetBottom + x + 1, offsetBottomEdge + x + 1);
11045			indexArray.emplaceBack(offsetTopEdge + x, offsetTop + x + 1, offsetTop + x);
11046			indexArray.emplaceBack(offsetTopEdge + x, offsetTopEdge + x + 1, offsetTop + x + 1);
11047		}
11048		const offsetLeft = vertexArray.length;
11049		const offsetRight = offsetLeft + (meshSize + 1) * 2;
11050		for (const x of [0, 1]) for (let y = 0; y <= meshSize; y++) for (const z of [0, 1]) vertexArray.emplaceBack(x * EXTENT, y * delta, z);
11051		for (let y = 0; y < meshSize * 2; y += 2) {
11052			indexArray.emplaceBack(offsetLeft + y, offsetLeft + y + 1, offsetLeft + y + 3);
11053			indexArray.emplaceBack(offsetLeft + y, offsetLeft + y + 3, offsetLeft + y + 2);
11054			indexArray.emplaceBack(offsetRight + y, offsetRight + y + 3, offsetRight + y + 1);
11055			indexArray.emplaceBack(offsetRight + y, offsetRight + y + 2, offsetRight + y + 3);
11056		}
11057	}
11058};
11059const NOT_COVERED = {
11060	covered: false,
11061	demLoaded: false,
11062	elevation: 0
11063};
11064/**
11065* Elevation of the rendered terrain surface at a mercator position, and whether it is covered at all.
11066* A covered tile whose DEM has not loaded yet is flat at zero, which is what the terrain mesh renders.
11067*/
11068function sampleAt(index, exaggeration, mercatorX, mercatorY) {
11069	if (mercatorY < 0 || mercatorY >= 1) return NOT_COVERED;
11070	const wrap = Math.floor(mercatorX);
11071	const wrappedX = mercatorX - wrap;
11072	for (const z of index.zooms) {
11073		const scale = 1 << z;
11074		const scaledX = wrappedX * scale;
11075		const scaledY = mercatorY * scale;
11076		const tileX = Math.floor(scaledX);
11077		const tileY = Math.floor(scaledY);
11078		const key = `${wrap}/${z}/${tileX}/${tileY}`;
11079		if (!index.samplerPerTile.has(key)) continue;
11080		const sampler = index.samplerPerTile.get(key);
11081		if (!sampler) return {
11082			covered: true,
11083			demLoaded: false,
11084			elevation: 0
11085		};
11086		return {
11087			covered: true,
11088			demLoaded: true,
11089			elevation: sampler(Math.min((scaledX - tileX) * EXTENT, MAX_TILE_COORD), Math.min((scaledY - tileY) * EXTENT, MAX_TILE_COORD), EXTENT) * exaggeration
11090		};
11091	}
11092	return NOT_COVERED;
11093}
11094/**
11095* Whether a height in meters is at or below the sampled terrain surface.
11096* The epsilon absorbs rounding when a bracket endpoint lands exactly on the surface.
11097*/
11098function isBelowTerrainSample(sample, height) {
11099	return sample.covered && height <= sample.elevation + HIT_EPSILON_M;
11100}
11101/**
11102* Narrows the bracket `[lo, hi]` around the surface crossing until it is shorter than `tolerance` in ray parameter units.
11103*/
11104function bisect(ray, isBelowTerrain, lo, hi, tolerance) {
11105	for (let j = 0; j < MAX_BISECTIONS && hi - lo > tolerance; j++) {
11106		const mid = (lo + hi) / 2;
11107		if (isBelowTerrain(ray, mid)) hi = mid;
11108		else lo = mid;
11109	}
11110	return {
11111		lo,
11112		hi
11113	};
11114}
11115//#endregion
11116//#region src/geo/projection/mercator_transform.ts
11117const TARGET_WORLD_STEP_PX = 4;
11118const MAX_SAMPLES = 512;
11119const MERCATOR_BISECT_EPSILON_WORLD_PX = .001;
11120var MercatorTransform = class MercatorTransform {
11121	get pixelsToClipSpaceMatrix() {
11122		return this._helper.pixelsToClipSpaceMatrix;
11123	}
11124	get clipSpaceToPixelsMatrix() {
11125		return this._helper.clipSpaceToPixelsMatrix;
11126	}
11127	get pixelsToGLUnits() {
11128		return this._helper.pixelsToGLUnits;
11129	}
11130	get centerOffset() {
11131		return this._helper.centerOffset;
11132	}
11133	get size() {
11134		return this._helper.size;
11135	}
11136	get rotationMatrix() {
11137		return this._helper.rotationMatrix;
11138	}
11139	get centerPoint() {
11140		return this._helper.centerPoint;
11141	}
11142	get pixelsPerMeter() {
11143		return this._helper.pixelsPerMeter;
11144	}
11145	setMinZoom(zoom) {
11146		this._helper.setMinZoom(zoom);
11147	}
11148	setMaxZoom(zoom) {
11149		this._helper.setMaxZoom(zoom);
11150	}
11151	setMinPitch(pitch) {
11152		this._helper.setMinPitch(pitch);
11153	}
11154	setMaxPitch(pitch) {
11155		this._helper.setMaxPitch(pitch);
11156	}
11157	setRenderWorldCopies(renderWorldCopies) {
11158		this._helper.setRenderWorldCopies(renderWorldCopies);
11159	}
11160	setBearing(bearing) {
11161		this._helper.setBearing(bearing);
11162	}
11163	setPitch(pitch) {
11164		this._helper.setPitch(pitch);
11165	}
11166	setRoll(roll) {
11167		this._helper.setRoll(roll);
11168	}
11169	setFov(fov) {
11170		this._helper.setFov(fov);
11171	}
11172	setZoom(zoom) {
11173		this._helper.setZoom(zoom);
11174	}
11175	setCenter(center) {
11176		this._helper.setCenter(center);
11177	}
11178	setElevation(elevation) {
11179		this._helper.setElevation(elevation);
11180	}
11181	setMinElevationForCurrentTile(elevation) {
11182		this._helper.setMinElevationForCurrentTile(elevation);
11183	}
11184	setPadding(padding) {
11185		this._helper.setPadding(padding);
11186	}
11187	interpolatePadding(start, target, t) {
11188		this._helper.interpolatePadding(start, target, t);
11189	}
11190	isPaddingEqual(padding) {
11191		return this._helper.isPaddingEqual(padding);
11192	}
11193	resize(width, height, constrain = true) {
11194		this._helper.resize(width, height, constrain);
11195	}
11196	getMaxBounds() {
11197		return this._helper.getMaxBounds();
11198	}
11199	setMaxBounds(bounds) {
11200		this._helper.setMaxBounds(bounds);
11201	}
11202	setConstrainOverride(constrain) {
11203		this._helper.setConstrainOverride(constrain);
11204	}
11205	overrideNearFarZ(nearZ, farZ) {
11206		this._helper.overrideNearFarZ(nearZ, farZ);
11207	}
11208	clearNearFarZOverride() {
11209		this._helper.clearNearFarZOverride();
11210	}
11211	getCameraQueryGeometry(queryGeometry) {
11212		return this._helper.getCameraQueryGeometry(this.getCameraPoint(), queryGeometry);
11213	}
11214	get tileSize() {
11215		return this._helper.tileSize;
11216	}
11217	get tileZoom() {
11218		return this._helper.tileZoom;
11219	}
11220	get scale() {
11221		return this._helper.scale;
11222	}
11223	get worldSize() {
11224		return this._helper.worldSize;
11225	}
11226	get width() {
11227		return this._helper.width;
11228	}
11229	get height() {
11230		return this._helper.height;
11231	}
11232	get lngRange() {
11233		return this._helper.lngRange;
11234	}
11235	get latRange() {
11236		return this._helper.latRange;
11237	}
11238	get minZoom() {
11239		return this._helper.minZoom;
11240	}
11241	get maxZoom() {
11242		return this._helper.maxZoom;
11243	}
11244	get zoom() {
11245		return this._helper.zoom;
11246	}
11247	get center() {
11248		return this._helper.center;
11249	}
11250	get minPitch() {
11251		return this._helper.minPitch;
11252	}
11253	get maxPitch() {
11254		return this._helper.maxPitch;
11255	}
11256	get pitch() {
11257		return this._helper.pitch;
11258	}
11259	get pitchInRadians() {
11260		return this._helper.pitchInRadians;
11261	}
11262	get roll() {
11263		return this._helper.roll;
11264	}
11265	get rollInRadians() {
11266		return this._helper.rollInRadians;
11267	}
11268	get bearing() {
11269		return this._helper.bearing;
11270	}
11271	get bearingInRadians() {
11272		return this._helper.bearingInRadians;
11273	}
11274	get fov() {
11275		return this._helper.fov;
11276	}
11277	get fovInRadians() {
11278		return this._helper.fovInRadians;
11279	}
11280	get elevation() {
11281		return this._helper.elevation;
11282	}
11283	get minElevationForCurrentTile() {
11284		return this._helper.minElevationForCurrentTile;
11285	}
11286	get padding() {
11287		return this._helper.padding;
11288	}
11289	get unmodified() {
11290		return this._helper.unmodified;
11291	}
11292	get renderWorldCopies() {
11293		return this._helper.renderWorldCopies;
11294	}
11295	get cameraToCenterDistance() {
11296		return this._helper.cameraToCenterDistance;
11297	}
11298	get constrainOverride() {
11299		return this._helper.constrainOverride;
11300	}
11301	get nearZ() {
11302		return this._helper.nearZ;
11303	}
11304	get farZ() {
11305		return this._helper.farZ;
11306	}
11307	get autoCalculateNearFarZ() {
11308		return this._helper.autoCalculateNearFarZ;
11309	}
11310	setTransitionState(_value) {}
11311	/**
11312	* @param options - Initial state. Ignored when `sharedHelper` is given, which already carries it.
11313	* @param sharedHelper - Camera to use instead of owning one, so that a composing transform such as
11314	* {@link GlobeTransform} keeps a single copy of the state rather than one per child. Its owner then drives
11315	* {@link _calcMatrices}, because a helper has only one `calcMatrices` callback.
11316	*/
11317	constructor(options, sharedHelper) {
11318		this._posMatrixCache = /* @__PURE__ */ new Map();
11319		this._alignedPosMatrixCache = /* @__PURE__ */ new Map();
11320		this._fogMatrixCacheF32 = /* @__PURE__ */ new Map();
11321		this.defaultConstrain = (lngLat, zoom) => {
11322			zoom = clamp(+zoom, this.minZoom, this.maxZoom);
11323			const result = {
11324				center: new LngLat(lngLat.lng, lngLat.lat),
11325				zoom
11326			};
11327			let lngRange = this._helper._lngRange;
11328			if (!this._helper._renderWorldCopies && lngRange === null) lngRange = [-179.9999999999, 180 - 1e-10];
11329			const worldSize = this.tileSize * zoomScale(result.zoom);
11330			let minY = 0;
11331			let maxY = worldSize;
11332			let minX = 0;
11333			let maxX = worldSize;
11334			let scaleY = 0;
11335			let scaleX = 0;
11336			const { x: screenWidth, y: screenHeight } = this.size;
11337			if (this._helper._latRange) {
11338				const latRange = this._helper._latRange;
11339				minY = mercatorYfromLat(latRange[1]) * worldSize;
11340				maxY = mercatorYfromLat(latRange[0]) * worldSize;
11341				if (maxY - minY < screenHeight) scaleY = screenHeight / (maxY - minY);
11342			}
11343			if (lngRange) {
11344				minX = wrap(mercatorXfromLng(lngRange[0]) * worldSize, 0, worldSize);
11345				maxX = wrap(mercatorXfromLng(lngRange[1]) * worldSize, 0, worldSize);
11346				if (maxX < minX) maxX += worldSize;
11347				if (maxX - minX < screenWidth) scaleX = screenWidth / (maxX - minX);
11348			}
11349			const { x: originalX, y: originalY } = projectToWorldCoordinates(worldSize, lngLat);
11350			let modifiedX, modifiedY;
11351			const scale = Math.max(scaleX || 0, scaleY || 0);
11352			if (scale) {
11353				const newPoint = new Point(scaleX ? (maxX + minX) / 2 : originalX, scaleY ? (maxY + minY) / 2 : originalY);
11354				result.center = unprojectFromWorldCoordinates(worldSize, newPoint).wrap();
11355				result.zoom += scaleZoom(scale);
11356				return result;
11357			}
11358			if (this._helper._latRange) {
11359				const h2 = screenHeight / 2;
11360				if (originalY - h2 < minY) modifiedY = minY + h2;
11361				if (originalY + h2 > maxY) modifiedY = maxY - h2;
11362			}
11363			if (lngRange) {
11364				const centerX = (minX + maxX) / 2;
11365				let wrappedX = originalX;
11366				if (this._helper._renderWorldCopies) wrappedX = wrap(originalX, centerX - worldSize / 2, centerX + worldSize / 2);
11367				const w2 = screenWidth / 2;
11368				if (wrappedX - w2 < minX) modifiedX = minX + w2;
11369				if (wrappedX + w2 > maxX) modifiedX = maxX - w2;
11370			}
11371			if (modifiedX !== void 0 || modifiedY !== void 0) {
11372				const newPoint = new Point(modifiedX ?? originalX, modifiedY ?? originalY);
11373				result.center = unprojectFromWorldCoordinates(worldSize, newPoint).wrap();
11374			}
11375			return result;
11376		};
11377		this.applyConstrain = (lngLat, zoom) => {
11378			return this._helper.applyConstrain(lngLat, zoom);
11379		};
11380		this._helper = sharedHelper ?? new TransformHelper({
11381			calcMatrices: () => this._calcMatrices(),
11382			defaultConstrain: (center, zoom) => {
11383				return this.defaultConstrain(center, zoom);
11384			}
11385		}, options);
11386		this._coveringTilesDetailsProvider = new MercatorCoveringTilesDetailsProvider();
11387	}
11388	clone() {
11389		const clone = new MercatorTransform();
11390		clone.apply(this, false);
11391		return clone;
11392	}
11393	apply(that, constrain) {
11394		this._helper.apply(that, constrain);
11395	}
11396	get cameraPosition() {
11397		return this._cameraPosition;
11398	}
11399	get projectionMatrix() {
11400		return this._projectionMatrix;
11401	}
11402	get modelViewProjectionMatrix() {
11403		return this._viewProjMatrix;
11404	}
11405	get inverseProjectionMatrix() {
11406		return this._invProjMatrix;
11407	}
11408	get mercatorMatrix() {
11409		return this._mercatorMatrix;
11410	}
11411	getVisibleUnwrappedCoordinates(tileID) {
11412		const result = [new UnwrappedTileID(0, tileID)];
11413		if (this._helper._renderWorldCopies) {
11414			const utl = this.screenPointToMercatorCoordinate(new Point(0, 0));
11415			const utr = this.screenPointToMercatorCoordinate(new Point(this._helper._width, 0));
11416			const ubl = this.screenPointToMercatorCoordinate(new Point(this._helper._width, this._helper._height));
11417			const ubr = this.screenPointToMercatorCoordinate(new Point(0, this._helper._height));
11418			const w0 = Math.floor(Math.min(utl.x, utr.x, ubl.x, ubr.x));
11419			const w1 = Math.floor(Math.max(utl.x, utr.x, ubl.x, ubr.x));
11420			const extraWorldCopy = 1;
11421			for (let w = w0 - extraWorldCopy; w <= w1 + extraWorldCopy; w++) {
11422				if (w === 0) continue;
11423				result.push(new UnwrappedTileID(w, tileID));
11424			}
11425		}
11426		return result;
11427	}
11428	getCameraFrustum() {
11429		return Frustum.fromInvProjectionMatrix(this._invViewProjMatrix, this.worldSize);
11430	}
11431	getClippingPlane() {
11432		return null;
11433	}
11434	getCoveringTilesDetailsProvider() {
11435		return this._coveringTilesDetailsProvider;
11436	}
11437	recalculateZoomAndCenter(terrain) {
11438		const center = this.screenPointToLocation(this.centerPoint, terrain);
11439		const elevation = terrain ? terrain.getElevationForLngLat(center, this) : 0;
11440		this._helper.recalculateZoomAndCenter(elevation);
11441	}
11442	/**
11443	* Moves the center so that `lnglat`, on the ground at `elevation` meters, renders
11444	* at the screen `point`. Both rays are cast through the same inverse pixel matrix
11445	* so its inversion error cancels out of their difference; the current-center ray
11446	* must be intersected at the center's own elevation (z=0) — intersecting it with
11447	* the elevated plane would land `(elevation - centerElevation)·tan(pitch)` away
11448	* from the center and make repeated calls drift.
11449	*/
11450	setLocationAtPoint(lnglat, point, elevation = this.elevation) {
11451		const z = elevation - this.elevation;
11452		const a = this.screenPointToMercatorCoordinateAtZ(point, z);
11453		const b = this.screenPointToMercatorCoordinateAtZ(this.centerPoint, 0);
11454		const loc = MercatorCoordinate.fromLngLat(lnglat);
11455		const newCenter = new MercatorCoordinate(loc.x - (a.x - b.x), loc.y - (a.y - b.y));
11456		this.setCenter(newCenter?.toLngLat());
11457		if (this._helper._renderWorldCopies) this.setCenter(this.center.wrap());
11458	}
11459	locationToScreenPoint(lnglat, terrain) {
11460		return terrain ? this.coordinatePoint(MercatorCoordinate.fromLngLat(lnglat), terrain.getElevationForLngLat(lnglat, this), this._pixelMatrix3D) : this.coordinatePoint(MercatorCoordinate.fromLngLat(lnglat));
11461	}
11462	screenPointToLocation(p, terrain) {
11463		return this.screenPointToMercatorCoordinate(p, terrain)?.toLngLat();
11464	}
11465	screenPointToLocationAtElevation(p, elevation) {
11466		return this.screenPointToMercatorCoordinateAtZ(p, elevation - this.elevation)?.toLngLat();
11467	}
11468	screenPointToMercatorCoordinate(p, terrain) {
11469		if (terrain) {
11470			const coordinate = this.screenTerrainPointToMercatorCoordinate(p, terrain);
11471			if (coordinate != null) return coordinate;
11472		}
11473		return this.screenPointToMercatorCoordinateAtZ(p);
11474	}
11475	/** {@inheritDoc ITransform.screenTerrainPointToMercatorCoordinate} */
11476	screenTerrainPointToMercatorCoordinate(p, terrain) {
11477		const index = terrain.getCoverageIndex();
11478		if (!index) return null;
11479		const { near, far } = this.getRaySegmentFromPixel(p);
11480		const worldSize = this.worldSize;
11481		const dx = far[0] - near[0];
11482		const dy = far[1] - near[1];
11483		const dz = far[2] - near[2];
11484		const ray = {
11485			index,
11486			exaggeration: terrain.exaggeration,
11487			near,
11488			dx,
11489			dy,
11490			dz,
11491			worldSize
11492		};
11493		let tStart = 0;
11494		let tEnd = 1;
11495		if (dz === 0) {
11496			if (near[2] > index.maxElevation || near[2] < index.minElevation) return null;
11497		} else {
11498			const tHigh = (index.maxElevation - near[2]) / dz;
11499			const tLow = (index.minElevation - near[2]) / dz;
11500			tStart = Math.max(tStart, Math.min(tHigh, tLow));
11501			tEnd = Math.min(tEnd, Math.max(tHigh, tLow));
11502			if (tStart > tEnd) return null;
11503		}
11504		const horizontalLength = Math.hypot(dx, dy);
11505		const samples = clamp(Math.ceil(horizontalLength * (tEnd - tStart) / TARGET_WORLD_STEP_PX), 1, MAX_SAMPLES);
11506		let previousT = 0;
11507		let aboveTerrain = !mercatorIsBelowTerrain(ray, 0);
11508		for (let i = 0; i <= samples; i++) {
11509			const t = tStart + (tEnd - tStart) * i / samples;
11510			if (!aboveTerrain) aboveTerrain = !mercatorIsBelowTerrain(ray, t);
11511			else if (mercatorIsBelowTerrain(ray, t)) {
11512				const { lo, hi } = bisect(ray, mercatorIsBelowTerrain, previousT, t, MERCATOR_BISECT_EPSILON_WORLD_PX / horizontalLength);
11513				const sampleLo = mercatorSampleAt(ray, lo);
11514				const sampleHi = mercatorSampleAt(ray, hi);
11515				const fLo = near[2] + lo * dz - sampleLo.elevation;
11516				const fHi = near[2] + hi * dz - sampleHi.elevation;
11517				const hit = sampleLo.covered && fLo > fHi ? clamp(lo + fLo * (hi - lo) / (fLo - fHi), lo, hi) : hi;
11518				return new MercatorCoordinate((near[0] + hit * dx) / worldSize, (near[1] + hit * dy) / worldSize, mercatorSampleAt(ray, hit).elevation);
11519			}
11520			previousT = t;
11521		}
11522		return null;
11523	}
11524	/**
11525	* Returns the segment of the ray through the given screen pixel that lies inside the view frustum.
11526	*/
11527	getRaySegmentFromPixel(p) {
11528		const coord0 = [
11529			p.x,
11530			p.y,
11531			0,
11532			1
11533		];
11534		const coord1 = [
11535			p.x,
11536			p.y,
11537			1,
11538			1
11539		];
11540		transformMat4(coord0, coord0, this._pixelMatrixInverse);
11541		transformMat4(coord1, coord1, this._pixelMatrixInverse);
11542		const w0 = coord0[3];
11543		const w1 = coord1[3];
11544		const elevation = this.elevation;
11545		return {
11546			near: [
11547				coord0[0] / w0,
11548				coord0[1] / w0,
11549				coord0[2] / w0 + elevation
11550			],
11551			far: [
11552				coord1[0] / w1,
11553				coord1[1] / w1,
11554				coord1[2] / w1 + elevation
11555			]
11556		};
11557	}
11558	/**
11559	* Intersects the ray through a screen point with the horizontal plane at `z`,
11560	* given in meters relative to the plane at the center's elevation (not mercator units).
11561	*/
11562	screenPointToMercatorCoordinateAtZ(p, z) {
11563		const targetZ = z ? z : 0;
11564		const { near, far } = this.getRaySegmentFromPixel(p);
11565		const t = near[2] === far[2] ? 0 : (targetZ + this.elevation - near[2]) / (far[2] - near[2]);
11566		return new MercatorCoordinate(interpolateFactory.number(near[0], far[0], t) / this.worldSize, interpolateFactory.number(near[1], far[1], t) / this.worldSize, targetZ);
11567	}
11568	/**
11569	* Given a coordinate, return the screen point that corresponds to it
11570	* @param coord - the coordinates
11571	* @param elevation - the elevation
11572	* @param pixelMatrix - the pixel matrix
11573	* @returns screen point. Point will be outside the viewport if the coordinate is behind the camera.
11574	*/
11575	coordinatePoint(coord, elevation = 0, pixelMatrix = this._pixelMatrix) {
11576		const p = this._coordinateClipPoint(coord, elevation, pixelMatrix);
11577		const w = p[3];
11578		if (w > 0) return new Point(p[0] / w, p[1] / w);
11579		return this._offScreenPointBehindCamera(p[0], p[1], w);
11580	}
11581	/**
11582	* The coordinate in the clip space of `pixelMatrix`: pixel x and y and NDC depth, each times `w`, which is positive
11583	* in front of the camera.
11584	*/
11585	_coordinateClipPoint(coord, elevation, pixelMatrix) {
11586		const p = [
11587			coord.x * this.worldSize,
11588			coord.y * this.worldSize,
11589			elevation,
11590			1
11591		];
11592		return transformMat4(p, p, pixelMatrix);
11593	}
11594	/**
11595	* Returns a screen point outside the viewport for a coordinate that is behind the camera.
11596	* The point lies on the boundary of the viewport enlarged by one viewport size on each side,
11597	* on whichever edge the ray from the screen centre in the coordinate's direction reaches first.
11598	* A coordinate exactly behind the camera has no direction to leave through, so it is placed straight down.
11599	* @param x - the x component of the clip space position, before the division by w
11600	* @param y - the y component of the clip space position, before the division by w
11601	* @param w - the w component of the clip space position, zero or negative
11602	* @returns screen point outside the viewport
11603	*/
11604	_offScreenPointBehindCamera(x, y, w) {
11605		const cx = this.width / 2;
11606		const cy = this.height / 2;
11607		const dx = x - cx * w;
11608		let dy = y - cy * w;
11609		if (dx === 0 && dy === 0) dy = 1;
11610		const halfExtentX = cx + this.width;
vendor: 4,533 bytes, lines 11611-11684
11611		const halfExtentY = cy + this.height;
11612		const scale = Math.min(dx !== 0 ? halfExtentX / Math.abs(dx) : Infinity, dy !== 0 ? halfExtentY / Math.abs(dy) : Infinity);
11613		return new Point(cx + dx * scale, cy + dy * scale);
11614	}
11615	getBounds() {
11616		const top = Math.max(0, this._helper._height / 2 - getMercatorHorizon(this));
11617		return new LngLatBounds().extend(this.screenPointToLocation(new Point(0, top))).extend(this.screenPointToLocation(new Point(this._helper._width, top))).extend(this.screenPointToLocation(new Point(this._helper._width, this._helper._height))).extend(this.screenPointToLocation(new Point(0, this._helper._height)));
11618	}
11619	isPointOnMapSurface(p, terrain) {
11620		if (terrain) return this.screenTerrainPointToMercatorCoordinate(p, terrain) != null;
11621		return p.y > this.height / 2 - getMercatorHorizon(this);
11622	}
11623	calculatePosMatrix(tileID, aligned = false, useFloat32 = false) {
11624		const posMatrixKey = tileID.key ?? calculateTileKey(tileID.wrap, tileID.canonical.z, tileID.canonical.z, tileID.canonical.x, tileID.canonical.y);
11625		const cache = aligned ? this._alignedPosMatrixCache : this._posMatrixCache;
11626		if (cache.has(posMatrixKey)) {
11627			const matrices = cache.get(posMatrixKey);
11628			return useFloat32 ? matrices.f32 : matrices.f64;
11629		}
11630		const tileMatrix = calculateTileMatrix(tileID, this.worldSize);
11631		multiply(tileMatrix, aligned ? this._alignedProjMatrix : this._viewProjMatrix, tileMatrix);
11632		const matrices = {
11633			f64: tileMatrix,
11634			f32: new Float32Array(tileMatrix)
11635		};
11636		cache.set(posMatrixKey, matrices);
11637		return useFloat32 ? matrices.f32 : matrices.f64;
11638	}
11639	calculateFogMatrix(unwrappedTileID) {
11640		const posMatrixKey = unwrappedTileID.key;
11641		const cache = this._fogMatrixCacheF32;
11642		if (cache.has(posMatrixKey)) return cache.get(posMatrixKey);
11643		const fogMatrix = calculateTileMatrix(unwrappedTileID, this.worldSize);
11644		multiply(fogMatrix, this._fogMatrix, fogMatrix);
11645		cache.set(posMatrixKey, new Float32Array(fogMatrix));
11646		return cache.get(posMatrixKey);
11647	}
11648	calculateCenterFromCameraLngLatAlt(lnglat, alt, bearing, pitch) {
11649		return this._helper.calculateCenterFromCameraLngLatAlt(lnglat, alt, bearing, pitch);
11650	}
11651	calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo) {
11652		const fromMercator = MercatorCoordinate.fromLngLat(from, altitudeFrom);
11653		const toMercator = MercatorCoordinate.fromLngLat(to, altitudeTo);
11654		const dx = toMercator.x - fromMercator.x;
11655		const dy = toMercator.y - fromMercator.y;
11656		const dz = toMercator.z - fromMercator.z;
11657		const distance3D = Math.hypot(dx, dy, dz);
11658		if (distance3D === 0) throw new Error("Can't calculate camera options with same From and To");
11659		const groundDistance = Math.hypot(dx, dy);
11660		const zoom = scaleZoom(this.cameraToCenterDistance / distance3D / this.tileSize);
11661		const bearing = radiansToDegrees(Math.atan2(dx, -dy));
11662		let pitch = radiansToDegrees(Math.acos(groundDistance / distance3D));
11663		pitch = dz < 0 ? 90 - pitch : 90 + pitch;
11664		return {
11665			center: toMercator.toLngLat(),
11666			elevation: altitudeTo,
11667			zoom,
11668			pitch,
11669			bearing
11670		};
11671	}
11672	_calculateNearFarZ(cameraToSeaLevelDistance, limitedPitchRadians, offset) {
11673		const minElevation = Math.min(this.elevation, this.minElevationForCurrentTile, this.getCameraAltitude() - 100);
11674		const cameraToLowestPointDistance = cameraToSeaLevelDistance - minElevation * this._helper._pixelPerMeter / Math.cos(limitedPitchRadians);
11675		const lowestPlane = minElevation < 0 ? cameraToLowestPointDistance : cameraToSeaLevelDistance;
11676		const groundAngle = Math.PI / 2 + this.pitchInRadians;
11677		const fovAboveCenter = degreesToRadians(this.fov) * (Math.abs(Math.cos(degreesToRadians(this.roll))) * this.height + Math.abs(Math.sin(degreesToRadians(this.roll))) * this.width) / this.height * (.5 + offset.y / this.height);
11678		const topHalfSurfaceDistance = Math.sin(fovAboveCenter) * lowestPlane / Math.sin(clamp(Math.PI - groundAngle - fovAboveCenter, .01, Math.PI - .01));
11679		const horizon = getMercatorHorizon(this);
11680		const horizonAngle = Math.atan(horizon / this._helper.cameraToCenterDistance);
11681		const minFovCenterToHorizonRadians = degreesToRadians(90 - maxMercatorHorizonAngle);
11682		const fovCenterToHorizon = horizonAngle > minFovCenterToHorizonRadians ? 2 * horizonAngle * (.5 + offset.y / (horizon * 2)) : minFovCenterToHorizonRadians;
11683		const topHalfSurfaceDistanceHorizon = Math.sin(fovCenterToHorizon) * lowestPlane / Math.sin(clamp(Math.PI - groundAngle - fovCenterToHorizon, .01, Math.PI - .01));
11684		const topHalfMinDistance = Math.min(topHalfSurfaceDistance, topHalfSurfaceDistanceHorizon);
vendor: 15,158 bytes, lines 11685-12084
11685		this._helper._farZ = (Math.cos(Math.PI / 2 - limitedPitchRadians) * topHalfMinDistance + lowestPlane) * 1.01;
11686		this._helper._nearZ = this._helper._height / 50;
11687	}
11688	/**
11689	* @param calculateNearFarZ - Whether to compute the near/far Z range, or leave the range the helper already
11690	* holds. Defaults to {@link autoCalculateNearFarZ}; a composing transform such as {@link GlobeTransform}
11691	* overrides it so that its two children share a single depth range.
11692	*/
11693	_calcMatrices(calculateNearFarZ = this._helper.autoCalculateNearFarZ) {
11694		const offset = this.centerOffset;
11695		const point = projectToWorldCoordinates(this.worldSize, this.center);
11696		const x = point.x, y = point.y;
11697		const limitedPitchRadians = degreesToRadians(Math.min(this.pitch, maxMercatorHorizonAngle));
11698		const cameraToSeaLevelDistance = Math.max(this._helper.cameraToCenterDistance / 2, this._helper.cameraToCenterDistance + this._helper._elevation * this._helper._pixelPerMeter / Math.cos(limitedPitchRadians));
11699		if (calculateNearFarZ) this._calculateNearFarZ(cameraToSeaLevelDistance, limitedPitchRadians, offset);
11700		let m;
11701		m = /* @__PURE__ */ new Float64Array(16);
11702		perspective(m, this.fovInRadians, this._helper._width / this._helper._height, this._helper._nearZ, this._helper._farZ);
11703		this._invProjMatrix = /* @__PURE__ */ new Float64Array(16);
11704		fastInvertProjMat4(this._invProjMatrix, m);
11705		m[8] = -offset.x * 2 / this._helper._width;
11706		m[9] = offset.y * 2 / this._helper._height;
11707		this._projectionMatrix = clone$1(m);
11708		scale(m, m, [
11709			1,
11710			-1,
11711			1
11712		]);
11713		translate(m, m, [
11714			0,
11715			0,
11716			-this._helper.cameraToCenterDistance
11717		]);
11718		rotateZ(m, m, -this.rollInRadians);
11719		rotateX(m, m, this.pitchInRadians);
11720		rotateZ(m, m, -this.bearingInRadians);
11721		translate(m, m, [
11722			-x,
11723			-y,
11724			0
11725		]);
11726		this._mercatorMatrix = scale([], m, [
11727			this.worldSize,
11728			this.worldSize,
11729			this.worldSize
11730		]);
11731		scale(m, m, [
11732			1,
11733			1,
11734			this._helper._pixelPerMeter
11735		]);
11736		this._pixelMatrix = multiply(/* @__PURE__ */ new Float64Array(16), this.clipSpaceToPixelsMatrix, m);
11737		translate(m, m, [
11738			0,
11739			0,
11740			-this.elevation
11741		]);
11742		this._viewProjMatrix = m;
11743		this._invViewProjMatrix = invert$1([], m);
11744		const cameraPos = [
11745			0,
11746			0,
11747			-1,
11748			1
11749		];
11750		transformMat4(cameraPos, cameraPos, this._invViewProjMatrix);
11751		this._cameraPosition = [
11752			cameraPos[0] / cameraPos[3],
11753			cameraPos[1] / cameraPos[3],
11754			cameraPos[2] / cameraPos[3]
11755		];
11756		this._fogMatrix = /* @__PURE__ */ new Float64Array(16);
11757		perspective(this._fogMatrix, this.fovInRadians, this.width / this.height, cameraToSeaLevelDistance, this._helper._farZ);
11758		this._fogMatrix[8] = -offset.x * 2 / this.width;
11759		this._fogMatrix[9] = offset.y * 2 / this.height;
11760		scale(this._fogMatrix, this._fogMatrix, [
11761			1,
11762			-1,
11763			1
11764		]);
11765		translate(this._fogMatrix, this._fogMatrix, [
11766			0,
11767			0,
11768			-this.cameraToCenterDistance
11769		]);
11770		rotateZ(this._fogMatrix, this._fogMatrix, -this.rollInRadians);
11771		rotateX(this._fogMatrix, this._fogMatrix, this.pitchInRadians);
11772		rotateZ(this._fogMatrix, this._fogMatrix, -this.bearingInRadians);
11773		translate(this._fogMatrix, this._fogMatrix, [
11774			-x,
11775			-y,
11776			0
11777		]);
11778		scale(this._fogMatrix, this._fogMatrix, [
11779			1,
11780			1,
11781			this._helper._pixelPerMeter
11782		]);
11783		translate(this._fogMatrix, this._fogMatrix, [
11784			0,
11785			0,
11786			-this.elevation
11787		]);
11788		this._pixelMatrix3D = multiply(/* @__PURE__ */ new Float64Array(16), this.clipSpaceToPixelsMatrix, m);
11789		const xShift = this._helper._width % 2 / 2, yShift = this._helper._height % 2 / 2, angleCos = Math.cos(this.bearingInRadians), angleSin = Math.sin(-this.bearingInRadians), dx = x - Math.round(x) + angleCos * xShift + angleSin * yShift, dy = y - Math.round(y) + angleCos * yShift + angleSin * xShift;
11790		const alignedM = new Float64Array(m);
11791		translate(alignedM, alignedM, [
11792			dx > .5 ? dx - 1 : dx,
11793			dy > .5 ? dy - 1 : dy,
11794			0
11795		]);
11796		this._alignedProjMatrix = alignedM;
11797		m = invert$1(/* @__PURE__ */ new Float64Array(16), this._pixelMatrix);
11798		if (!m) throw new Error("failed to invert matrix");
11799		this._pixelMatrixInverse = m;
11800		this._clearMatrixCaches();
11801	}
11802	_clearMatrixCaches() {
11803		this._posMatrixCache.clear();
11804		this._alignedPosMatrixCache.clear();
11805		this._fogMatrixCacheF32.clear();
11806	}
11807	maxPitchScaleFactor() {
11808		if (!this._pixelMatrixInverse) return 1;
11809		const coord = this.screenPointToMercatorCoordinate(new Point(0, 0));
11810		const p = [
11811			coord.x * this.worldSize,
11812			coord.y * this.worldSize,
11813			0,
11814			1
11815		];
11816		return transformMat4(p, p, this._pixelMatrix)[3] / this._helper.cameraToCenterDistance;
11817	}
11818	getCameraPoint() {
11819		return this._helper.getCameraPoint();
11820	}
11821	getCameraAltitude() {
11822		return Math.cos(this.pitchInRadians) * this.cameraToCenterDistance / this.pixelsPerMeter + this.elevation;
11823	}
11824	getCameraLngLat() {
11825		const pixelPerMeter = mercatorZfromAltitude(1, this.center.lat) * this.worldSize;
11826		const cameraToCenterDistanceMeters = this._helper.cameraToCenterDistance / pixelPerMeter;
11827		return cameraMercatorCoordinateFromCenterAndRotation(this.center, this.elevation, this.pitch, this.bearing, cameraToCenterDistanceMeters).toLngLat();
11828	}
11829	getProjectionData(params) {
11830		const { overscaledTileID, aligned, applyTerrainMatrix } = params;
11831		const mercatorTileCoordinates = this._helper.getMercatorTileCoordinates(overscaledTileID);
11832		const tilePosMatrix = overscaledTileID ? this.calculatePosMatrix(overscaledTileID, aligned, true) : null;
11833		let mainMatrix;
11834		if (overscaledTileID?.terrainRttPosMatrix32f && applyTerrainMatrix) mainMatrix = overscaledTileID.terrainRttPosMatrix32f;
11835		else if (tilePosMatrix) mainMatrix = tilePosMatrix;
11836		else mainMatrix = createIdentityMat4f32();
11837		return {
11838			mainMatrix,
11839			tileMercatorCoords: mercatorTileCoordinates,
11840			clippingPlane: [
11841				0,
11842				0,
11843				0,
11844				0
11845			],
11846			projectionTransition: 0,
11847			fallbackMatrix: mainMatrix,
11848			clipAntimeridian: false
11849		};
11850	}
11851	/** {@inheritDoc ITransform.isLocationOccluded} */
11852	isLocationOccluded(lngLat, terrain, elevation) {
11853		if (!terrain?.getCoverageIndex()) return false;
11854		const location = MercatorCoordinate.fromLngLat(lngLat);
11855		elevation ??= terrain.getElevationForLngLat(lngLat, this);
11856		const clip = this._coordinateClipPoint(location, elevation, this._pixelMatrix3D);
11857		const w = clip[3];
11858		if (w <= 0 || clip[2] > w) return true;
11859		const p = new Point(clip[0] / w, clip[1] / w);
11860		const hit = this.screenTerrainPointToMercatorCoordinate(p, terrain);
11861		if (hit == null) return false;
11862		const segment = this.getRaySegmentFromPixel(p);
11863		const tLocation = raySegmentParameter(segment, location.x * this.worldSize, location.y * this.worldSize, elevation);
11864		return raySegmentParameter(segment, hit.x * this.worldSize, hit.y * this.worldSize, hit.z) < tLocation * .99;
11865	}
11866	getPixelScale() {
11867		return 1;
11868	}
11869	getCircleRadiusCorrection() {
11870		return 1;
11871	}
11872	getPitchedTextCorrection(_textAnchorX, _textAnchorY, _tileID) {
11873		return 1;
11874	}
11875	transformLightDirection(dir) {
11876		return clone(dir);
11877	}
11878	getRayDirectionFromPixel(_p) {
11879		throw new Error("Not implemented.");
11880	}
11881	projectTileCoordinates(x, y, unwrappedTileID, elevation) {
11882		const matrix = this.calculatePosMatrix(unwrappedTileID);
11883		let pos;
11884		if (elevation != null) {
11885			pos = [
11886				x,
11887				y,
11888				elevation,
11889				1
11890			];
11891			transformMat4(pos, pos, matrix);
11892		} else {
11893			pos = [
11894				x,
11895				y,
11896				0,
11897				1
11898			];
11899			xyTransformMat4(pos, pos, matrix);
11900		}
11901		const w = pos[3];
11902		return {
11903			point: new Point(pos[0] / w, pos[1] / w),
11904			signedDistanceFromCamera: w,
11905			isOccluded: false
11906		};
11907	}
11908	populateCache(coords) {
11909		for (const coord of coords) this.calculatePosMatrix(coord);
11910	}
11911	getProjectionDataForCustomLayer(applyGlobeMatrix = true) {
11912		const tileID = new OverscaledTileID(0, 0, 0, 0, 0);
11913		const rendererProjectionData = this.getProjectionData({
11914			overscaledTileID: tileID,
11915			applyGlobeMatrix
11916		});
11917		const tileMatrix = calculateTileMatrix(tileID, this.worldSize);
11918		multiply(tileMatrix, this._viewProjMatrix, tileMatrix);
11919		const scale$4 = [
11920			EXTENT,
11921			EXTENT,
11922			this.worldSize / this._helper.pixelsPerMeter
11923		];
11924		const projectionMatrixScaled = createMat4f64();
11925		scale(projectionMatrixScaled, tileMatrix, scale$4);
11926		return {
11927			...rendererProjectionData,
11928			tileMercatorCoords: [
11929				0,
11930				0,
11931				1,
11932				1
11933			],
11934			fallbackMatrix: projectionMatrixScaled,
11935			mainMatrix: projectionMatrixScaled
11936		};
11937	}
11938	getFastPathSimpleProjectionMatrix(tileID) {
11939		return this.calculatePosMatrix(tileID);
11940	}
11941};
11942function mercatorSampleAt(ray, t) {
11943	return sampleAt(ray.index, ray.exaggeration, (ray.near[0] + t * ray.dx) / ray.worldSize, (ray.near[1] + t * ray.dy) / ray.worldSize);
11944}
11945function mercatorIsBelowTerrain(ray, t) {
11946	return isBelowTerrainSample(mercatorSampleAt(ray, t), ray.near[2] + t * ray.dz);
11947}
11948/**
11949* Where the point of `segment` closest to the given world pixel position and elevation lies along it,
11950* as the fraction from `near` (0) to `far` (1).
11951*/
11952function raySegmentParameter({ near, far }, worldX, worldY, elevation) {
11953	const dx = far[0] - near[0];
11954	const dy = far[1] - near[1];
11955	const dz = far[2] - near[2];
11956	return ((worldX - near[0]) * dx + (worldY - near[1]) * dy + (elevation - near[2]) * dz) / (dx * dx + dy * dy + dz * dz);
11957}
11958//#endregion
11959//#region src/geo/projection/camera_helper.ts
11960/**
11961* @internal
11962*/
11963function cameraBoundsWarning() {
11964	warnOnce("Map cannot fit within canvas with the given bounds, padding, and/or offset.");
11965}
11966/**
11967* @internal
11968* Set a transform's rotation to a value interpolated between startEulerAngles and endEulerAngles
11969*/
11970function updateRotation(args) {
11971	if (args.useSlerp) {
11972		if (args.k < 1) {
11973			const startRotation = rollPitchBearingToQuat(args.startEulerAngles.roll, args.startEulerAngles.pitch, args.startEulerAngles.bearing);
11974			const endRotation = rollPitchBearingToQuat(args.endEulerAngles.roll, args.endEulerAngles.pitch, args.endEulerAngles.bearing);
11975			const rotation = /* @__PURE__ */ new Float64Array(4);
11976			slerp(rotation, startRotation, endRotation, args.k);
11977			const eulerAngles = getRollPitchBearing(rotation);
11978			args.tr.setRoll(eulerAngles.roll);
11979			args.tr.setPitch(eulerAngles.pitch);
11980			args.tr.setBearing(eulerAngles.bearing);
11981		} else {
11982			args.tr.setRoll(args.endEulerAngles.roll);
11983			args.tr.setPitch(args.endEulerAngles.pitch);
11984			args.tr.setBearing(args.endEulerAngles.bearing);
11985		}
11986	} else {
11987		args.tr.setRoll(interpolateFactory.number(args.startEulerAngles.roll, args.endEulerAngles.roll, args.k));
11988		args.tr.setPitch(interpolateFactory.number(args.startEulerAngles.pitch, args.endEulerAngles.pitch, args.k));
11989		args.tr.setBearing(interpolateFactory.number(args.startEulerAngles.bearing, args.endEulerAngles.bearing, args.k));
11990	}
11991}
11992function cameraForBoxAndBearing(options, padding, bounds, bearing, tr) {
11993	const edgePadding = tr.padding;
11994	const nwWorld = projectToWorldCoordinates(tr.worldSize, bounds.getNorthWest());
11995	const neWorld = projectToWorldCoordinates(tr.worldSize, bounds.getNorthEast());
11996	const seWorld = projectToWorldCoordinates(tr.worldSize, bounds.getSouthEast());
11997	const swWorld = projectToWorldCoordinates(tr.worldSize, bounds.getSouthWest());
11998	const bearingRadians = degreesToRadians(-bearing);
11999	const nwRotatedWorld = nwWorld.rotate(bearingRadians);
12000	const neRotatedWorld = neWorld.rotate(bearingRadians);
12001	const seRotatedWorld = seWorld.rotate(bearingRadians);
12002	const swRotatedWorld = swWorld.rotate(bearingRadians);
12003	const upperRight = new Point(Math.max(nwRotatedWorld.x, neRotatedWorld.x, swRotatedWorld.x, seRotatedWorld.x), Math.max(nwRotatedWorld.y, neRotatedWorld.y, swRotatedWorld.y, seRotatedWorld.y));
12004	const lowerLeft = new Point(Math.min(nwRotatedWorld.x, neRotatedWorld.x, swRotatedWorld.x, seRotatedWorld.x), Math.min(nwRotatedWorld.y, neRotatedWorld.y, swRotatedWorld.y, seRotatedWorld.y));
12005	const size = upperRight.sub(lowerLeft);
12006	const availableWidth = tr.width - (edgePadding.left + edgePadding.right + padding.left + padding.right);
12007	const availableHeight = tr.height - (edgePadding.top + edgePadding.bottom + padding.top + padding.bottom);
12008	const scaleX = availableWidth / size.x;
12009	const scaleY = availableHeight / size.y;
12010	if (scaleY < 0 || scaleX < 0) {
12011		cameraBoundsWarning();
12012		return;
12013	}
12014	const zoom = Math.min(scaleZoom(tr.scale * Math.min(scaleX, scaleY)), options.maxZoom);
12015	const offset = Point.convert(options.offset);
12016	const paddingOffsetX = (padding.left - padding.right) / 2;
12017	const paddingOffsetY = (padding.top - padding.bottom) / 2;
12018	const rotatedPaddingOffset = new Point(paddingOffsetX, paddingOffsetY).rotate(degreesToRadians(bearing));
12019	const offsetAtFinalZoom = offset.add(rotatedPaddingOffset).mult(tr.scale / zoomScale(zoom));
12020	return {
12021		center: unprojectFromWorldCoordinates(tr.worldSize, nwWorld.add(seWorld).div(2).sub(offsetAtFinalZoom)),
12022		zoom,
12023		bearing
12024	};
12025}
12026//#endregion
12027//#region src/geo/projection/mercator_camera_helper.ts
12028/**
12029* @internal
12030*/
12031var MercatorCameraHelper = class {
12032	get useGlobeControls() {
12033		return false;
12034	}
12035	handlePanInertia(pan, transform) {
12036		const offsetLength = pan.mag();
12037		const pixelsToHorizon = Math.abs(getMercatorHorizon(transform));
12038		return {
12039			easingOffset: pan.mult(Math.min(pixelsToHorizon * .75 / offsetLength, 1)),
12040			easingCenter: transform.center
12041		};
12042	}
12043	handleMapControlsRollPitchBearingZoom(deltas, tr) {
12044		if (deltas.bearingDelta) tr.setBearing(tr.bearing + deltas.bearingDelta);
12045		if (deltas.pitchDelta) tr.setPitch(tr.pitch + deltas.pitchDelta);
12046		if (deltas.rollDelta) tr.setRoll(tr.roll + deltas.rollDelta);
12047		if (deltas.zoomDelta) tr.setZoom(tr.zoom + deltas.zoomDelta);
12048	}
12049	handleMapControlsPan(deltas, tr, preZoomAroundLoc) {
12050		if (deltas.around.distSqr(tr.centerPoint) < .01) return;
12051		tr.setLocationAtPoint(preZoomAroundLoc, deltas.around, deltas.aroundElevation);
12052	}
12053	cameraForBoxAndBearing(options, padding, bounds, bearing, tr) {
12054		return cameraForBoxAndBearing(options, padding, bounds, bearing, tr);
12055	}
12056	handleJumpToCenterZoom(tr, options) {
12057		const zoom = typeof options.zoom !== "undefined" ? +options.zoom : tr.zoom;
12058		if (tr.zoom !== zoom) tr.setZoom(+options.zoom);
12059		if (options.center !== void 0) tr.setCenter(LngLat.convert(options.center));
12060	}
12061	handleEaseTo(tr, options) {
12062		const startZoom = tr.zoom;
12063		const startPadding = tr.padding;
12064		const startEulerAngles = {
12065			roll: tr.roll,
12066			pitch: tr.pitch,
12067			bearing: tr.bearing
12068		};
12069		const endEulerAngles = {
12070			roll: options.roll === void 0 ? tr.roll : options.roll,
12071			pitch: options.pitch === void 0 ? tr.pitch : options.pitch,
12072			bearing: options.bearing === void 0 ? tr.bearing : options.bearing
12073		};
12074		const optionsZoom = typeof options.zoom !== "undefined";
12075		const doPadding = !tr.isPaddingEqual(options.padding);
12076		let isZooming = false;
12077		const zoom = optionsZoom ? +options.zoom : tr.zoom;
12078		let pointAtOffset = tr.centerPoint.add(options.offsetAsPoint);
12079		const locationAtOffset = tr.screenPointToLocation(pointAtOffset);
12080		const { center, zoom: endZoom } = tr.applyConstrain(LngLat.convert(options.center || locationAtOffset), zoom ?? startZoom);
12081		normalizeCenter(tr, center);
12082		const from = projectToWorldCoordinates(tr.worldSize, locationAtOffset);
12083		const delta = projectToWorldCoordinates(tr.worldSize, center).sub(from);
12084		const finalScale = zoomScale(endZoom - startZoom);
vendor: 11,027 bytes, lines 12085-12359
12085		isZooming = endZoom !== startZoom;
12086		const easeFunc = (k) => {
12087			if (isZooming) tr.setZoom(interpolateFactory.number(startZoom, endZoom, k));
12088			if (!rollPitchBearingEqual(startEulerAngles, endEulerAngles)) updateRotation({
12089				startEulerAngles,
12090				endEulerAngles,
12091				tr,
12092				k,
12093				useSlerp: startEulerAngles.roll != endEulerAngles.roll
12094			});
12095			if (doPadding) {
12096				tr.interpolatePadding(startPadding, options.padding, k);
12097				pointAtOffset = tr.centerPoint.add(options.offsetAsPoint);
12098			}
12099			if (options.around) tr.setLocationAtPoint(options.around, options.aroundPoint);
12100			else {
12101				const scale = zoomScale(tr.zoom - startZoom);
12102				const speedup = Math.pow(endZoom > startZoom ? Math.min(2, finalScale) : Math.max(.5, finalScale), 1 - k);
12103				const newCenter = unprojectFromWorldCoordinates(tr.worldSize, from.add(delta.mult(k * speedup)).mult(scale));
12104				tr.setLocationAtPoint(tr.renderWorldCopies ? newCenter.wrap() : newCenter, pointAtOffset);
12105			}
12106		};
12107		return {
12108			easeFunc,
12109			isZooming,
12110			elevationCenter: center
12111		};
12112	}
12113	handleFlyTo(tr, options) {
12114		const optionsZoom = typeof options.zoom !== "undefined";
12115		const startZoom = tr.zoom;
12116		const constrained = tr.applyConstrain(LngLat.convert(options.center || options.locationAtOffset), optionsZoom ? +options.zoom : startZoom);
12117		const targetCenter = constrained.center;
12118		const targetZoom = constrained.zoom;
12119		normalizeCenter(tr, targetCenter);
12120		const startWorldSize = tr.worldSize;
12121		const from = projectToWorldCoordinates(startWorldSize, options.locationAtOffset);
12122		const delta = projectToWorldCoordinates(startWorldSize, targetCenter).sub(from);
12123		const pixelPathLength = delta.mag();
12124		const scaleOfZoom = zoomScale(targetZoom - startZoom);
12125		const requestedMinZoom = typeof options.minZoom !== "undefined" ? +options.minZoom : tr.minZoom;
12126		const effectiveMinZoom = Math.max(requestedMinZoom, tr.minZoom);
12127		const minZoomPreConstrain = Math.min(effectiveMinZoom, startZoom, targetZoom);
12128		const minZoom = tr.applyConstrain(targetCenter, minZoomPreConstrain).zoom;
12129		const scaleOfMinZoom = zoomScale(minZoom - startZoom);
12130		const easeFunc = (k, scale, centerFactor, pointAtOffset) => {
12131			tr.setZoom(k === 1 ? targetZoom : startZoom + scaleZoom(scale));
12132			const newCenter = k === 1 ? targetCenter : unprojectFromWorldCoordinates(startWorldSize, from.add(delta.mult(centerFactor)));
12133			tr.setLocationAtPoint(tr.renderWorldCopies ? newCenter.wrap() : newCenter, pointAtOffset);
12134		};
12135		return {
12136			easeFunc,
12137			scaleOfZoom,
12138			targetCenter,
12139			scaleOfMinZoom,
12140			pixelPathLength
12141		};
12142	}
12143};
12144//#endregion
12145//#region src/style/projection_properties.g.ts
12146let properties;
12147const getProperties = () => properties = properties || new Properties({ "type": new DataConstantProperty(latest["projection"]["type"], "type") });
12148//#endregion
12149//#region src/geo/projection/vertical_perspective_projection.ts
12150const VerticalPerspectiveShaderDefine = "#define GLOBE";
12151const VerticalPerspectiveShaderVariantKey = "globe";
12152const granularitySettingsGlobe = new SubdivisionGranularitySetting({
12153	fill: new SubdivisionGranularityExpression(128, 2),
12154	line: new SubdivisionGranularityExpression(512, 0),
12155	tile: new SubdivisionGranularityExpression(128, 32),
12156	stencil: new SubdivisionGranularityExpression(128, 1),
12157	circle: 3
12158});
12159var VerticalPerspectiveProjection = class {
12160	constructor() {
12161		this._tileMeshCache = {};
12162	}
12163	get name() {
12164		return "vertical-perspective";
12165	}
12166	get transitionState() {
12167		return 1;
12168	}
12169	get useSubdivision() {
12170		return true;
12171	}
12172	get shaderVariantName() {
12173		return VerticalPerspectiveShaderVariantKey;
12174	}
12175	get shaderDefine() {
12176		return VerticalPerspectiveShaderDefine;
12177	}
12178	get shaderPreludeCode() {
12179		return shaders.projectionGlobe;
12180	}
12181	get vertexShaderPreludeCode() {
12182		return shaders.projectionMercator.vertexSource;
12183	}
12184	get subdivisionGranularity() {
12185		return granularitySettingsGlobe;
12186	}
12187	get useGlobeControls() {
12188		return true;
12189	}
12190	destroy() {}
12191	_getMeshKey(options) {
12192		return `${options.granularity.toString(36)}_${options.generateBorders ? "b" : ""}${options.extendToNorthPole ? "n" : ""}${options.extendToSouthPole ? "s" : ""}`;
12193	}
12194	getMeshFromTileID(context, canonical, hasBorder, allowPoles, usage) {
12195		const granularity = (usage === "stencil" ? granularitySettingsGlobe.stencil : granularitySettingsGlobe.tile).getGranularityForZoomLevel(canonical.z);
12196		const north = canonical.y === 0 && allowPoles;
12197		const south = canonical.y === (1 << canonical.z) - 1 && allowPoles;
12198		return this._getMesh(context, {
12199			granularity,
12200			generateBorders: hasBorder,
12201			extendToNorthPole: north,
12202			extendToSouthPole: south
12203		});
12204	}
12205	_getMesh(context, options) {
12206		const key = this._getMeshKey(options);
12207		if (key in this._tileMeshCache) return this._tileMeshCache[key];
12208		const mesh = createTileMeshWithBuffers(context, options);
12209		this._tileMeshCache[key] = mesh;
12210		return mesh;
12211	}
12212	recalculate(_params) {}
12213	hasTransition() {
12214		return false;
12215	}
12216};
12217//#endregion
12218//#region src/geo/projection/globe_projection.ts
12219var GlobeProjection = class extends Evented {
12220	constructor(projection, globalState) {
12221		super();
12222		this._transitionable = new Transitionable(getProperties(), "projection", globalState);
12223		this.setProjection(projection);
12224		this._transitioning = this._transitionable.untransitioned();
12225		this.recalculate(new EvaluationParameters(0));
12226		this._mercatorProjection = new MercatorProjection();
12227		this._verticalPerspectiveProjection = new VerticalPerspectiveProjection();
12228	}
12229	get transitionState() {
12230		const currentProjectionSpecValue = this.properties.get("type");
12231		if (typeof currentProjectionSpecValue === "string" && currentProjectionSpecValue === "mercator") return 0;
12232		if (typeof currentProjectionSpecValue === "string" && currentProjectionSpecValue === "vertical-perspective") return 1;
12233		if (currentProjectionSpecValue instanceof ProjectionDefinition) {
12234			if (currentProjectionSpecValue.from === currentProjectionSpecValue.to) return currentProjectionSpecValue.from === "mercator" ? 0 : 1;
12235			if (currentProjectionSpecValue.from === "vertical-perspective" && currentProjectionSpecValue.to === "mercator") return 1 - currentProjectionSpecValue.transition;
12236			if (currentProjectionSpecValue.from === "mercator" && currentProjectionSpecValue.to === "vertical-perspective") return currentProjectionSpecValue.transition;
12237		}
12238		return 1;
12239	}
12240	get useGlobeRendering() {
12241		return this.transitionState > 0;
12242	}
12243	get currentProjection() {
12244		return this.useGlobeRendering ? this._verticalPerspectiveProjection : this._mercatorProjection;
12245	}
12246	get name() {
12247		return "globe";
12248	}
12249	get useSubdivision() {
12250		return this.currentProjection.useSubdivision;
12251	}
12252	get shaderVariantName() {
12253		return this.currentProjection.shaderVariantName;
12254	}
12255	get shaderDefine() {
12256		return this.currentProjection.shaderDefine;
12257	}
12258	get shaderPreludeCode() {
12259		return this.currentProjection.shaderPreludeCode;
12260	}
12261	get vertexShaderPreludeCode() {
12262		return this.currentProjection.vertexShaderPreludeCode;
12263	}
12264	get subdivisionGranularity() {
12265		return this.currentProjection.subdivisionGranularity;
12266	}
12267	get useGlobeControls() {
12268		return this.transitionState > 0;
12269	}
12270	destroy() {
12271		this._mercatorProjection.destroy();
12272		this._verticalPerspectiveProjection.destroy();
12273	}
12274	getMeshFromTileID(context, _tileID, _hasBorder, _allowPoles, _usage) {
12275		return this.currentProjection.getMeshFromTileID(context, _tileID, _hasBorder, _allowPoles, _usage);
12276	}
12277	setProjection(projection) {
12278		this._transitionable.setValue("type", projection?.type || "mercator");
12279	}
12280	updateTransitions(parameters) {
12281		this._transitioning = this._transitionable.transitioned(parameters, this._transitioning);
12282	}
12283	hasTransition() {
12284		return this._transitioning.hasTransition() || this.currentProjection.hasTransition();
12285	}
12286	recalculate(parameters) {
12287		this.properties = this._transitioning.possiblyEvaluate(parameters);
12288	}
12289};
12290//#endregion
12291//#region src/geo/projection/globe_utils.ts
12292function getGlobeCircumferencePixels(transform) {
12293	const radius = getGlobeRadiusPixels(transform.worldSize, transform.center.lat);
12294	return 2 * Math.PI * radius;
12295}
12296function globeDistanceOfLocationsPixels(transform, a, b) {
12297	const vecA = angularCoordinatesToSurfaceVector(a);
12298	const vecB = angularCoordinatesToSurfaceVector(b);
12299	const dot$2 = dot(vecA, vecB);
12300	const radians = Math.acos(dot$2);
12301	const circumference = getGlobeCircumferencePixels(transform);
12302	return radians / (2 * Math.PI) * circumference;
12303}
12304/**
12305* For given mercator coordinates in range 0..1, returns the angular coordinates on the sphere's surface, in radians.
12306*/
12307function mercatorCoordinatesToAngularCoordinatesRadians(mercatorX, mercatorY) {
12308	return [mod(mercatorX * Math.PI * 2 + Math.PI, Math.PI * 2), 2 * Math.atan(Math.exp(Math.PI - mercatorY * Math.PI * 2)) - Math.PI * .5];
12309}
12310/**
12311* For a given longitude and latitude (note: in radians) returns the normalized vector from the planet center to the specified place on the surface.
12312* @param lngRadians - Longitude in radians.
12313* @param latRadians - Latitude in radians.
12314*/
12315function angularCoordinatesRadiansToVector(lngRadians, latRadians) {
12316	const len = Math.cos(latRadians);
12317	const vec = /* @__PURE__ */ new Float64Array(3);
12318	vec[0] = Math.sin(lngRadians) * len;
12319	vec[1] = Math.sin(latRadians);
12320	vec[2] = Math.cos(lngRadians) * len;
12321	return vec;
12322}
12323/**
12324* Projects a point within a tile to the surface of the unit sphere globe.
12325* @param inTileX - X coordinate inside the tile in range [0 .. 8192].
12326* @param inTileY - Y coordinate inside the tile in range [0 .. 8192].
12327* @param tileIdX - Tile's X coordinate in range [0 .. 2^zoom - 1].
12328* @param tileIdY - Tile's Y coordinate in range [0 .. 2^zoom - 1].
12329* @param tileIdZ - Tile's zoom.
12330* @returns A 3D vector - coordinates of the projected point on a unit sphere.
12331*/
12332function projectTileCoordinatesToSphere(inTileX, inTileY, tileIdX, tileIdY, tileIdZ) {
12333	const scale = 1 / (1 << tileIdZ);
12334	const mercatorX = inTileX / EXTENT * scale + tileIdX * scale;
12335	const mercatorY = inTileY / EXTENT * scale + tileIdY * scale;
12336	const sphericalX = mod(mercatorX * Math.PI * 2 + Math.PI, Math.PI * 2);
12337	const sphericalY = 2 * Math.atan(Math.exp(Math.PI - mercatorY * Math.PI * 2)) - Math.PI * .5;
12338	const len = Math.cos(sphericalY);
12339	const vec = /* @__PURE__ */ new Float64Array(3);
12340	vec[0] = Math.sin(sphericalX) * len;
12341	vec[1] = Math.sin(sphericalY);
12342	vec[2] = Math.cos(sphericalX) * len;
12343	return vec;
12344}
12345/**
12346* For a given longitude and latitude (note: in degrees) returns the normalized vector from the planet center to the specified place on the surface.
12347*/
12348function angularCoordinatesToSurfaceVector(lngLat) {
12349	return angularCoordinatesRadiansToVector(lngLat.lng * Math.PI / 180, lngLat.lat * Math.PI / 180);
12350}
12351function getGlobeRadiusPixels(worldSize, latitudeDegrees) {
12352	return worldSize / (2 * Math.PI) / Math.cos(latitudeDegrees * Math.PI / 180);
12353}
12354/**
12355* Returns the globe center in view space, in pixels: the camera is at the origin and looks down the negative Z axis.
12356*/
12357function getGlobeCenterInViewSpace(transform) {
12358	const position = transformMat4(createVec4f64(), [
12359		0,
vendor: 12,885 bytes, lines 12360-12615
12360		0,
12361		0,
12362		1
12363	], transform.modelViewProjectionMatrix);
12364	transformMat4(position, position, transform.inverseProjectionMatrix);
12365	return [
12366		position[0] / position[3],
12367		position[1] / position[3],
12368		position[2] / position[3]
12369	];
12370}
12371/**
12372* Given a 3D point on the surface of a unit sphere, returns its angular coordinates in degrees.
12373* The input vector must be normalized.
12374*/
12375function sphereSurfacePointToCoordinates(surface) {
12376	const latDegrees = Math.asin(surface[1]) / Math.PI * 180;
12377	const lengthXZ = Math.sqrt(surface[0] * surface[0] + surface[2] * surface[2]);
12378	if (lengthXZ > 1e-6) {
12379		const projX = surface[0] / lengthXZ;
12380		const projZ = surface[2] / lengthXZ;
12381		const acosZ = Math.acos(projZ);
12382		const lngDegrees = (projX > 0 ? acosZ : -acosZ) / Math.PI * 180;
12383		return new LngLat(wrap(lngDegrees, -180, 180), latDegrees);
12384	} else return new LngLat(0, latDegrees);
12385}
12386/**
12387* Returns the globe orientation quaternion for the given map center and bearing.
12388* The inverse of {@link lngLatBearingFromOrientation}.
12389*/
12390function orientationFromLngLatBearing(lngLat, bearing) {
12391	return fromEuler(createVec4f64(), -lngLat.lng, -lngLat.lat, bearing);
12392}
12393/**
12394* Given a globe orientation quaternion, returns the corresponding map center and bearing.
12395* The inverse of {@link orientationFromLngLatBearing}.
12396*/
12397function lngLatBearingFromOrientation(q) {
12398	const x = q[0], y = q[1], z = q[2], w = q[3];
12399	return {
12400		lng: -Math.atan2(2 * (w * x + y * z), 1 - 2 * (x * x + y * y)) * 180 / Math.PI,
12401		lat: -Math.asin(clamp(2 * (w * y - z * x), -1, 1)) * 180 / Math.PI,
12402		bearing: Math.atan2(2 * (w * z + x * y), 1 - 2 * (y * y + z * z)) * 180 / Math.PI
12403	};
12404}
12405/** Kept just below a half turn so the center cannot land exactly on the far side of the globe. */
12406const PAN_MAX_ANGLE = Math.PI * .98;
12407/**
12408* Returns the point on the globe that a drag towards `point` should aim at.
12409*
12410* Exact tracking breaks down at the silhouette: the ray meets the globe at `asin(D sin a) - a`,
12411* whose slope runs away to infinity as the ray goes tangent, and past it there is no intersection
12412* at all. So the exact curve is left {@link PAN_FALLOFF_BAND} early and continued with a hyperbola
12413* matching it in value and slope, easing off and saturating short of the far side. Bell's virtual
12414* trackball takes the same idea of easing a sphere into a hyperbola before the rim, though it fits
12415* the curve differently: SGI's `trackball.c`, described in Henriksen, Sporring and Hornbæk,
12416* "Virtual Trackballs Revisited", IEEE TVCG 10(2):206-216, 2004.
12417*
12418* The angle comes from atan2, so a ray pointing away from the globe is a wide angle the falloff
12419* saturates rather than a case to reject.
12420* @param tr - The transform being dragged.
12421* @param point - The cursor position.
12422*/
12423function panSurfaceLocation(tr, point) {
12424	const origin = tr.cameraPosition;
12425	const distance = length(origin);
12426	if (distance <= 1) return tr.screenPointToLocation(point);
12427	const u = createVec3f64();
12428	normalize(u, origin);
12429	const direction = tr.getRayDirectionFromPixel(point);
12430	const c = -dot(direction, u);
12431	const lateral = createVec3f64();
12432	scaleAndAdd(lateral, direction, u, c);
12433	const s = length(lateral);
12434	if (s < 1e-9) return tr.screenPointToLocation(point);
12435	const angle = Math.atan2(s, c);
12436	const handoverAngle = Math.asin(1 / distance) * .9;
12437	if (angle < handoverAngle) return tr.screenPointToLocation(point);
12438	const sinHandover = distance * Math.sin(handoverAngle);
12439	const targetAtHandover = Math.asin(clamp(sinHandover, -1, 1)) - handoverAngle;
12440	const slopeAtHandover = distance * Math.cos(handoverAngle) / Math.sqrt(Math.max(1 - sinHandover * sinHandover, 1e-12)) - 1;
12441	const room = PAN_MAX_ANGLE - targetAtHandover;
12442	const excess = angle - handoverAngle;
12443	const target = targetAtHandover + room * (slopeAtHandover * excess) / (room + slopeAtHandover * excess);
12444	const e = createVec3f64();
12445	scale$1(e, lateral, 1 / s);
12446	const surface = createVec3f64();
12447	scale$1(surface, u, Math.cos(clamp(target, 0, PAN_MAX_ANGLE)));
12448	scaleAndAdd(surface, surface, e, Math.sin(clamp(target, 0, PAN_MAX_ANGLE)));
12449	normalize(surface, surface);
12450	return sphereSurfacePointToCoordinates(surface);
12451}
12452/**
12453* Rotates the globe so that the given location appears at the given screen point, by composing
12454* versors, the unit quaternions that represent rotations. Unlike the bearing-preserving
12455* {@link ITransform.setLocationAtPoint}, this stays smooth near and across the poles, and keeps
12456* panning once the cursor leaves the globe.
12457*
12458* Note: the delta rotation's axis is in the surface-vector frame of
12459* {@link angularCoordinatesToSurfaceVector}, while the orientation quaternion uses the Euler frame
12460* of {@link orientationFromLngLatBearing}, hence the component swizzle where they combine. Zoom is
12461* adjusted to keep the planet the same size, as `setLocationAtPoint` does.
12462* @param tr - The transform to rotate.
12463* @param lnglat - The location to bring under `point`.
12464* @param point - The screen point that `lnglat` should appear at.
12465* @param panDelta - The drag's pixel delta. Used to re-derive the previous cursor location through
12466* {@link panSurfaceLocation}, since both ends of the rotation must come from the same mapping.
12467* @param fixedBearing - Applies the swing only, keeping the bearing fixed, which is what dragging
12468* the globe does. Pass `false` to apply the twist about the view axis as well, so that the grabbed
12469* location tracks the cursor exactly and the bearing drifts with it.
12470*/
12471function versorSetLocationAtPoint(tr, lnglat, point, panDelta, fixedBearing = true) {
12472	const pointLngLat = panSurfaceLocation(tr, point);
12473	let sourceLngLat = lnglat;
12474	if (panDelta) sourceLngLat = panSurfaceLocation(tr, point.sub(panDelta));
12475	else if (!tr.isPointOnMapSurface(point)) return;
12476	const vecToPixelCurrent = angularCoordinatesToSurfaceVector(pointLngLat);
12477	const vecToTarget = angularCoordinatesToSurfaceVector(sourceLngLat);
12478	const centerQuat = orientationFromLngLatBearing(tr.center, tr.bearing);
12479	const w = cross(createVec3f64(), vecToTarget, vecToPixelCurrent);
12480	const l = Math.sqrt(dot(w, w));
12481	const t = Math.acos(clamp(dot(vecToTarget, vecToPixelCurrent), -1, 1)) / 2;
12482	const s = Math.sin(t);
12483	const delta = l ? fromValues(w[1] / l * s, -w[0] / l * s, w[2] / l * s, Math.cos(t)) : fromValues(0, 0, 0, 1);
12484	const { lng: newCenterLng, lat: newCenterLat, bearing: newBearing } = lngLatBearingFromOrientation(multiply$1(createVec4f64(), centerQuat, delta));
12485	const oldLat = tr.center.lat;
12486	const oldZoom = tr.zoom;
12487	const finalLat = clamp(newCenterLat, -90, 90);
12488	const finalLng = fixedBearing ? fixedBearingLongitude(tr, point, panDelta, newCenterLng) : newCenterLng;
12489	tr.setCenter(new LngLat(wrap(finalLng, -180, 180), finalLat));
12490	if (!fixedBearing) tr.setBearing(newBearing);
12491	tr.setZoom(oldZoom + getZoomAdjustment(oldLat, tr.center.lat));
12492}
12493/**
12494* Returns the center longitude for a bearing-preserving drag.
12495*
12496* The swing longitude becomes ill-conditioned near the pole and the grabbed location slips away
12497* from the cursor, so within the last ~12 degrees of latitude the cursor is treated as turning a
12498* dial around the pole, blended in with a smoothstep anchored on {@link MAX_VALID_LATITUDE}, the
12499* highest latitude the center can reach. The sweep comes from the raw pixel delta rather than a
12500* round-tripped previous cursor position, which would lose its sign to cancellation at the pole,
12501* and is damped within {@link DIAL_MIN_RADIUS_PIXELS} so it eases to nothing there instead of
12502* being dropped, which would leave a spot where the drag could not move at all.
12503* @param tr - The transform being dragged.
12504* @param point - The cursor position.
12505* @param panDelta - The drag's pixel delta, or undefined to use the swing longitude only.
12506* @param newCenterLng - The swing target longitude.
12507* @returns the center longitude to apply.
12508*/
12509function fixedBearingLongitude(tr, point, panDelta, newCenterLng) {
12510	const oldLng = tr.center.lng;
12511	const oldLat = tr.center.lat;
12512	const poleLat = oldLat >= 0 ? 90 : -90;
12513	const polePoint = tr.locationToScreenPoint(new LngLat(0, poleLat));
12514	const rx = point.x - polePoint.x, ry = point.y - polePoint.y;
12515	const r2 = rx * rx + ry * ry;
12516	const tRamp = clamp(1 - (MAX_VALID_LATITUDE - Math.abs(oldLat)) / 12, 0, 1);
12517	const dial = tRamp * tRamp * (3 - 2 * tRamp);
12518	const dLngSwing = mod(newCenterLng - oldLng + 180, 360) - 180;
12519	let dLngDial = 0;
12520	if (dial > 0 && panDelta) {
12521		const dTheta = (rx * panDelta.y - ry * panDelta.x) / Math.max(r2, 400);
12522		dLngDial = (poleLat > 0 ? 1 : -1) * dTheta * 180 / Math.PI;
12523	}
12524	return oldLng + (1 - dial) * dLngSwing + dial * dLngDial;
12525}
12526/**
12527* Given a normalized horizon plane in Ax+By+Cz+D=0 format, compute the center and radius of
12528* the circle in that plain that contains the entire visible portion of the unit sphere from horizon
12529* to horizon.
12530* @param horizonPlane - The plane that passes through visible horizon in Ax + By + Cz + D = 0 format where mag(A,B,C)=1
12531* @returns the center point and radius of the disc that passes through the entire visible horizon
12532*/
12533function horizonPlaneToCenterAndRadius(horizonPlane) {
12534	const center = createVec3f64();
12535	center[0] = horizonPlane[0] * -horizonPlane[3];
12536	center[1] = horizonPlane[1] * -horizonPlane[3];
12537	center[2] = horizonPlane[2] * -horizonPlane[3];
12538	return {
12539		center,
12540		radius: Math.sqrt(1 - horizonPlane[3] * horizonPlane[3])
12541	};
12542}
12543/**
12544* Computes the closest point on a sphere to `point`.
12545* @param center - Center of the sphere
12546* @param radius - Radius of the sphere
12547* @param point - Point inside or outside the sphere
12548* @returns A 3d vector of the point on the sphere closest to `point`
12549*/
12550function clampToSphere(center, radius, point) {
12551	const relativeToCenter = createVec3f64();
12552	sub(relativeToCenter, point, center);
12553	const clamped = createVec3f64();
12554	scaleAndAdd(clamped, center, relativeToCenter, radius / len(relativeToCenter));
12555	return clamped;
12556}
12557function planetScaleAtLatitude(latitudeDegrees) {
12558	return Math.cos(latitudeDegrees * Math.PI / 180);
12559}
12560/**
12561* Computes how much to modify zoom to keep the globe size constant when changing latitude.
12562* @param transform - An instance of any transform. Does not have any relation on the computed values.
12563* @param oldLat - Latitude before change, in degrees.
12564* @param newLat - Latitude after change, in degrees.
12565* @returns A value to add to zoom level used for old latitude to keep same planet radius at new latitude.
12566*/
12567function getZoomAdjustment(oldLat, newLat) {
12568	const oldCircumference = planetScaleAtLatitude(oldLat);
12569	const newCircumference = planetScaleAtLatitude(newLat);
12570	return scaleZoom(newCircumference / oldCircumference);
12571}
12572function getDegreesPerPixel(worldSize, lat) {
12573	return 360 / getGlobeCircumferencePixels({
12574		worldSize,
12575		center: { lat }
12576	});
12577}
12578/**
12579* Returns transform's new center rotation after applying panning.
12580* @param panDelta - Panning delta, in same units as what is supplied to {@link HandlerManager}.
12581* @param tr - Current transform. This object is not modified by the function.
12582* @returns New center location to set to the map's transform to apply the specified panning.
12583*/
12584function computeGlobePanCenter(panDelta, tr) {
12585	const rotatedPanDelta = panDelta.rotate(tr.bearingInRadians);
12586	const normalizedGlobeZoom = tr.zoom + getZoomAdjustment(tr.center.lat, 0);
12587	const lngSpeed = lerp(1 / planetScaleAtLatitude(tr.center.lat), 1 / planetScaleAtLatitude(Math.min(Math.abs(tr.center.lat), 60)), remapSaturate(normalizedGlobeZoom, 7, 3, 0, 1));
12588	const panningDegreesPerPixel = getDegreesPerPixel(tr.worldSize, tr.center.lat);
12589	return new LngLat(tr.center.lng - rotatedPanDelta.x * panningDegreesPerPixel * lngSpeed, clamp(tr.center.lat + rotatedPanDelta.y * panningDegreesPerPixel, -MAX_VALID_LATITUDE, MAX_VALID_LATITUDE));
12590}
12591/**
12592* Integration of `1 / cos(x)`.
12593*/
12594function integrateSecX(x) {
12595	const xHalf = .5 * x;
12596	const sin = Math.sin(xHalf);
12597	const cos = Math.cos(xHalf);
12598	return Math.log(sin + cos) - Math.log(cos - sin);
12599}
12600/**
12601* Interpolates globe center between two locations while preserving apparent rotation speed during interpolation.
12602* @param start - The starting location of the interpolation.
12603* @param deltaLng - Longitude delta to the end of the interpolation.
12604* @param deltaLat - Latitude delta to the end of the interpolation.
12605* @param t - The interpolation point in [0..1], where 0 is starting location, 1 is end location and other values are in between.
12606* @returns The interpolated location.
12607*/
12608function interpolateLngLatForGlobe(start, deltaLng, deltaLat, t) {
12609	const interpolatedLat = start.lat + deltaLat * t;
12610	if (Math.abs(deltaLat) > 1) {
12611		const endLat = start.lat + deltaLat;
12612		const samplePointStart = (Math.sign(endLat) !== Math.sign(start.lat) ? -Math.abs(start.lat) : Math.abs(start.lat)) * Math.PI / 180;
12613		const samplePointEnd = Math.abs(start.lat + deltaLat) * Math.PI / 180;
12614		const valueT = integrateSecX(samplePointStart + t * (samplePointEnd - samplePointStart));
12615		const valueStart = integrateSecX(samplePointStart);
vendor: 5,022 bytes, lines 12616-12740
12616		const valueEnd = integrateSecX(samplePointEnd);
12617		const newT = (valueT - valueStart) / (valueEnd - valueStart);
12618		const interpolatedLng = start.lng + deltaLng * newT;
12619		return new LngLat(interpolatedLng, interpolatedLat);
12620	} else {
12621		const interpolatedLng = start.lng + deltaLng * t;
12622		return new LngLat(interpolatedLng, interpolatedLat);
12623	}
12624}
12625/**
12626* Returns the two intersection points of the ray and a sphere centered at the origin,
12627* or null if no intersection occurs.
12628* The intersections are encoded as the parameter for parametric ray equation,
12629* with `tMin` being the first intersection and `tMax` being the second.
12630* The quadratic is solved as suggested in Ray Tracing Gems, chapter 7, since the
12631* schoolbook approach leads to floating point precision issues:
12632* https://www.realtimerendering.com/raytracinggems/rtg/index.html
12633* @param origin - The ray origin.
12634* @param direction - The normalized ray direction.
12635* @param radius - The sphere radius, defaults to the unit sphere of the planet.
12636*/
12637function raySphereIntersection(origin, direction, radius = 1) {
12638	const originDotDirection = dot(origin, direction);
12639	const radiusSquared = radius * radius;
12640	const inner = createVec3f64();
12641	const scaledDir = createVec3f64();
12642	scale$1(scaledDir, direction, originDotDirection);
12643	sub(inner, origin, scaledDir);
12644	const discriminant = radiusSquared - dot(inner, inner);
12645	if (discriminant < 0) return null;
12646	const c = dot(origin, origin) - radiusSquared;
12647	const q = -originDotDirection + (originDotDirection < 0 ? 1 : -1) * Math.sqrt(discriminant);
12648	const t0 = c / q;
12649	const t1 = q;
12650	return {
12651		tMin: Math.min(t0, t1),
12652		tMax: Math.max(t0, t1)
12653	};
12654}
12655/** Height of the atmosphere relative to the globe radius, 100 km for Earth. */
12656const ATMOSPHERE_HEIGHT_TO_GLOBE_RADIUS = 1e5 / 6371e3;
12657/**
12658* Returns how much of the atmosphere to draw for a camera at the given altitude above the globe.
12659* It is 0 inside the atmosphere and reaches 1 at ten times the atmosphere height. The sky shader
12660* fades out along the same curve.
12661*/
12662function getAtmosphereAltitudeBlend(altitude, globeRadius) {
12663	const atmosphereHeight = globeRadius * ATMOSPHERE_HEIGHT_TO_GLOBE_RADIUS;
12664	const x = clamp((altitude - atmosphereHeight) / (9 * atmosphereHeight), 0, 1);
12665	return x * x * (3 - 2 * x);
12666}
12667//#endregion
12668//#region src/util/primitives/bounding_volume_cache.ts
12669var BoundingVolumeCache = class {
12670	constructor(boundingVolumeFactory) {
12671		this._cachePrevious = /* @__PURE__ */ new Map();
12672		this._cache = /* @__PURE__ */ new Map();
12673		this._hadAnyChanges = false;
12674		this._boundingVolumeFactory = boundingVolumeFactory;
12675	}
12676	/**
12677	* Prepares bounding volume cache for next frame. Call at the beginning of a frame.
12678	* Bounding volume of any tile accesses in the last frame is kept in the cache, other (unaccessed) bounding volumes are deleted.
12679	*/
12680	swapBuffers() {
12681		if (!this._hadAnyChanges) return;
12682		const oldCache = this._cachePrevious;
12683		this._cachePrevious = this._cache;
12684		this._cache = oldCache;
12685		this._cache.clear();
12686		this._hadAnyChanges = false;
12687	}
12688	/**
12689	* Returns the bounding volume of the specified tile, fetching it from cache or creating it using the factory function if needed.
12690	* @param tileID - Tile x, y and z for zoom.
12691	*/
12692	getTileBoundingVolume(tileID, wrap, elevation, options) {
12693		const key = `${tileID.z}_${tileID.x}_${tileID.y}_${options?.terrain ? "t" : ""}_${Math.round(elevation)}`;
12694		const cached = this._cache.get(key);
12695		if (cached) return cached;
12696		const cachedPrevious = this._cachePrevious.get(key);
12697		if (cachedPrevious) {
12698			this._cache.set(key, cachedPrevious);
12699			return cachedPrevious;
12700		}
12701		const boundingVolume = this._boundingVolumeFactory(tileID, wrap, elevation, options);
12702		this._cache.set(key, boundingVolume);
12703		this._hadAnyChanges = true;
12704		return boundingVolume;
12705	}
12706};
12707//#endregion
12708//#region src/util/primitives/convex_volume.ts
12709/**
12710* A general convex bounding volume, defined by a set of points.
12711*/
12712var ConvexVolume = class ConvexVolume {
12713	/**
12714	* Creates an instance of a general convex bounding volume.
12715	* Note that the provided points array is used *as is*, its contents are not copied!
12716	*
12717	* Additionally, an AABB must be provided for rejecting frustum intersections.
12718	* This AABB does not need to bound this convex volume (it may be smaller),
12719	* but it *must* accurately bound the actual shape this volume is approximating.
12720	* @param points - Points forming the convex shape. Note that this array reference is used *as is*, its contents are not copied!
12721	* @param min - The bounding AABB's min point.
12722	* @param max - The bounding AABB's min point.
12723	*/
12724	constructor(points, planes, min, max) {
12725		this.min = min;
12726		this.max = max;
12727		this.points = points;
12728		this.planes = planes;
12729	}
12730	/**
12731	* Creates a convex BV equivalent to the specified AABB.
12732	* @param min - The AABB's min point.
12733	* @param max - The AABB's max point.
12734	*/
12735	static fromAabb(min, max) {
12736		const points = [];
12737		for (let i = 0; i < 8; i++) points.push([
12738			(i >> 0 & 1) === 1 ? max[0] : min[0],
12739			(i >> 1 & 1) === 1 ? max[1] : min[1],
12740			(i >>
vendor: 58,668 bytes, lines 12740-14435
12740 2 & 1) === 1 ? max[2] : min[2]
12741		]);
12742		return new ConvexVolume(points, [
12743			[
12744				-1,
12745				0,
12746				0,
12747				max[0]
12748			],
12749			[
12750				1,
12751				0,
12752				0,
12753				-min[0]
12754			],
12755			[
12756				0,
12757				-1,
12758				0,
12759				max[1]
12760			],
12761			[
12762				0,
12763				1,
12764				0,
12765				-min[1]
12766			],
12767			[
12768				0,
12769				0,
12770				-1,
12771				max[2]
12772			],
12773			[
12774				0,
12775				0,
12776				1,
12777				-min[2]
12778			]
12779		], min, max);
12780	}
12781	/**
12782	* Creates a convex bounding volume that is actually an oriented bounding box created from the specified center, half-size and rotation angles.
12783	* @param center - Center of the OBB.
12784	* @param halfSize - The half-size of the OBB in each axis. The box will extend by this value in each direction for the given axis.
12785	* @param angles - The rotation of the box. Euler angles, in degrees.
12786	*/
12787	static fromCenterSizeAngles(center, halfSize, angles) {
12788		const q = fromEuler([], angles[0], angles[1], angles[2]);
12789		const axisX = transformQuat([], [
12790			halfSize[0],
12791			0,
12792			0
12793		], q);
12794		const axisY = transformQuat([], [
12795			0,
12796			halfSize[1],
12797			0
12798		], q);
12799		const axisZ = transformQuat([], [
12800			0,
12801			0,
12802			halfSize[2]
12803		], q);
12804		const min = [...center];
12805		const max = [...center];
12806		for (let i = 0; i < 8; i++) for (let axis = 0; axis < 3; axis++) {
12807			const point = center[axis] + axisX[axis] * ((i >> 0 & 1) === 1 ? 1 : -1) + axisY[axis] * ((i >> 1 & 1) === 1 ? 1 : -1) + axisZ[axis] * ((i >> 2 & 1) === 1 ? 1 : -1);
12808			min[axis] = Math.min(min[axis], point);
12809			max[axis] = Math.max(max[axis], point);
12810		}
12811		const points = [];
12812		for (let i = 0; i < 8; i++) {
12813			const p = [...center];
12814			add(p, p, scale$1([], axisX, (i >> 0 & 1) === 1 ? 1 : -1));
12815			add(p, p, scale$1([], axisY, (i >> 1 & 1) === 1 ? 1 : -1));
12816			add(p, p, scale$1([], axisZ, (i >> 2 & 1) === 1 ? 1 : -1));
12817			points.push(p);
12818		}
12819		return new ConvexVolume(points, [
12820			[...axisX, -dot(axisX, points[0])],
12821			[...axisY, -dot(axisY, points[0])],
12822			[...axisZ, -dot(axisZ, points[0])],
12823			[
12824				-axisX[0],
12825				-axisX[1],
12826				-axisX[2],
12827				-dot(axisX, points[7])
12828			],
12829			[
12830				-axisY[0],
12831				-axisY[1],
12832				-axisY[2],
12833				-dot(axisY, points[7])
12834			],
12835			[
12836				-axisZ[0],
12837				-axisZ[1],
12838				-axisZ[2],
12839				-dot(axisZ, points[7])
12840			]
12841		], min, max);
12842	}
12843	/**
12844	* Performs an approximate frustum-obb intersection test.
12845	*/
12846	intersectsFrustum(frustum) {
12847		let fullyInside = true;
12848		const boxPointCount = this.points.length;
12849		const boxPlaneCount = this.planes.length;
12850		const frustumPlaneCount = frustum.planes.length;
12851		const frustumPointCount = frustum.points.length;
12852		for (let i = 0; i < frustumPlaneCount; i++) {
12853			const plane = frustum.planes[i];
12854			let boxPointsPassed = 0;
12855			for (let j = 0; j < boxPointCount; j++) {
12856				const point = this.points[j];
12857				if (plane[0] * point[0] + plane[1] * point[1] + plane[2] * point[2] + plane[3] >= 0) boxPointsPassed++;
12858			}
12859			if (boxPointsPassed === 0) return 0;
12860			if (boxPointsPassed < boxPointCount) fullyInside = false;
12861		}
12862		if (fullyInside) return 2;
12863		for (let i = 0; i < boxPlaneCount; i++) {
12864			const plane = this.planes[i];
12865			let frustumPointsPassed = 0;
12866			for (let j = 0; j < frustumPointCount; j++) {
12867				const point = frustum.points[j];
12868				if (plane[0] * point[0] + plane[1] * point[1] + plane[2] * point[2] + plane[3] >= 0) frustumPointsPassed++;
12869			}
12870			if (frustumPointsPassed === 0) return 0;
12871		}
12872		return 1;
12873	}
12874	/**
12875	* Performs an intersection test with a halfspace.
12876	*/
12877	intersectsPlane(plane) {
12878		const pointCount = this.points.length;
12879		let positivePoints = 0;
12880		for (let i = 0; i < pointCount; i++) {
12881			const point = this.points[i];
12882			if (plane[0] * point[0] + plane[1] * point[1] + plane[2] * point[2] + plane[3] >= 0) positivePoints++;
12883		}
12884		if (positivePoints === pointCount) return 2;
12885		if (positivePoints === 0) return 0;
12886		return 1;
12887	}
12888};
12889//#endregion
12890//#region src/geo/projection/globe_covering_tiles_details_provider.ts
12891/**
12892* Computes distance of a point to a tile in an arbitrary axis.
12893* World is assumed to have size 1, distance returned is to the nearer tile edge.
12894* @param point - Point position.
12895* @param tile - Tile position.
12896* @param tileSize - Tile size.
12897*/
12898function distanceToTileSimple(point, tile, tileSize) {
12899	const delta = point - tile;
12900	return delta < 0 ? -delta : Math.max(0, delta - tileSize);
12901}
12902function distanceToTileWrapX(pointX, pointY, tileCornerX, tileCornerY, tileSize) {
12903	const tileCornerToPointX = pointX - tileCornerX;
12904	let distanceX;
12905	if (tileCornerToPointX < 0) distanceX = Math.min(-tileCornerToPointX, 1 + tileCornerToPointX - tileSize);
12906	else if (tileCornerToPointX > tileSize) distanceX = Math.min(Math.max(tileCornerToPointX - tileSize, 0), 1 - tileCornerToPointX);
12907	else distanceX = 0;
12908	return Math.max(distanceX, distanceToTileSimple(pointY, tileCornerY, tileSize));
12909}
12910var GlobeCoveringTilesDetailsProvider = class {
12911	constructor() {
12912		this._boundingVolumeCache = new BoundingVolumeCache(this._computeTileBoundingVolume);
12913	}
12914	/**
12915	* Prepares the internal bounding volume cache for the next frame.
12916	*/
12917	prepareNextFrame() {
12918		this._boundingVolumeCache.swapBuffers();
12919	}
12920	/**
12921	* Returns the distance of a point to a square tile. If the point is inside the tile, returns 0.
12922	* Assumes the world to be of size 1.
12923	* Handles distances on a sphere correctly: X is wrapped when crossing the antimeridian,
12924	* when crossing the poles Y is mirrored and X is shifted by half world size.
12925	*/
12926	distanceToTile2d(pointX, pointY, tileID, _bv) {
12927		const scale = 1 << tileID.z;
12928		const tileMercatorSize = 1 / scale;
12929		const tileCornerX = tileID.x / scale;
12930		const tileCornerY = tileID.y / scale;
12931		const halfWorld = .5;
12932		let smallestDistance = 2;
12933		smallestDistance = Math.min(smallestDistance, distanceToTileWrapX(pointX, pointY, tileCornerX, tileCornerY, tileMercatorSize));
12934		smallestDistance = Math.min(smallestDistance, distanceToTileWrapX(pointX, pointY, tileCornerX + halfWorld, -tileCornerY - tileMercatorSize, tileMercatorSize));
12935		smallestDistance = Math.min(smallestDistance, distanceToTileWrapX(pointX, pointY, tileCornerX + halfWorld, 2 - tileCornerY - tileMercatorSize, tileMercatorSize));
12936		return smallestDistance;
12937	}
12938	/**
12939	* Returns the wrap value for a given tile, computed so that tiles will remain loaded when crossing the antimeridian.
12940	*/
12941	getWrap(centerCoord, tileID, _parentWrap) {
12942		const scale = 1 << tileID.z;
12943		const tileMercatorSize = 1 / scale;
12944		const tileX = tileID.x / scale;
12945		const distanceCurrent = distanceToTileSimple(centerCoord.x, tileX, tileMercatorSize);
12946		const distanceLeft = distanceToTileSimple(centerCoord.x, tileX - 1, tileMercatorSize);
12947		const distanceRight = distanceToTileSimple(centerCoord.x, tileX + 1, tileMercatorSize);
12948		const distanceSmallest = Math.min(distanceCurrent, distanceLeft, distanceRight);
12949		if (distanceSmallest === distanceRight) return 1;
12950		if (distanceSmallest === distanceLeft) return -1;
12951		return 0;
12952	}
12953	allowVariableZoom(transform, options) {
12954		return coveringZoomLevel(transform, options) > 4;
12955	}
12956	allowWorldCopies() {
12957		return false;
12958	}
12959	getTileBoundingVolume(tileID, wrap, elevation, options) {
12960		return this._boundingVolumeCache.getTileBoundingVolume(tileID, wrap, elevation, options);
12961	}
12962	/**
12963	* Computes the bounding volume of a tile for culling. The volume reaches up to `elevation`,
12964	* which carries the content elevation allowance (e.g. elevated symbols); terrain elevations
12965	* widen the volume but never shrink that allowance.
12966	*/
12967	_computeTileBoundingVolume(tileID, wrap, elevation, options) {
12968		let minElevation = Math.min(0, elevation);
12969		let maxElevation = Math.max(0, elevation);
12970		if (options?.terrain) {
12971			const overscaledTileID = new OverscaledTileID(tileID.z, wrap, tileID.z, tileID.x, tileID.y);
12972			const minMax = options.terrain.getMinMaxElevation(overscaledTileID);
12973			minElevation = minMax.minElevation ?? minElevation;
12974			maxElevation = Math.max(minMax.maxElevation ?? maxElevation, maxElevation);
12975		}
12976		minElevation /= earthRadius;
12977		maxElevation /= earthRadius;
12978		minElevation += 1;
12979		maxElevation += 1;
12980		if (tileID.z <= 0) return ConvexVolume.fromAabb([
12981			-maxElevation,
12982			-maxElevation,
12983			-maxElevation
12984		], [
12985			maxElevation,
12986			maxElevation,
12987			maxElevation
12988		]);
12989		else if (tileID.z === 1) return ConvexVolume.fromAabb([
12990			tileID.x === 0 ? -maxElevation : 0,
12991			tileID.y === 0 ? 0 : -maxElevation,
12992			-maxElevation
12993		], [
12994			tileID.x === 0 ? 0 : maxElevation,
12995			tileID.y === 0 ? maxElevation : 0,
12996			maxElevation
12997		]);
12998		else {
12999			const corners = [
13000				projectTileCoordinatesToSphere(0, 0, tileID.x, tileID.y, tileID.z),
13001				projectTileCoordinatesToSphere(EXTENT, 0, tileID.x, tileID.y, tileID.z),
13002				projectTileCoordinatesToSphere(EXTENT, EXTENT, tileID.x, tileID.y, tileID.z),
13003				projectTileCoordinatesToSphere(0, EXTENT, tileID.x, tileID.y, tileID.z)
13004			];
13005			const extremesPoints = [];
13006			for (const c of corners) extremesPoints.push(scale$1([], c, maxElevation));
13007			if (maxElevation !== minElevation) for (const c of corners) extremesPoints.push(scale$1([], c, minElevation));
13008			if (tileID.y === 0) extremesPoints.push([
13009				0,
13010				1,
13011				0
13012			]);
13013			if (tileID.y === (1 << tileID.z) - 1) extremesPoints.push([
13014				0,
13015				-1,
13016				0
13017			]);
13018			const aabbMin = [
13019				1,
13020				1,
13021				1
13022			];
13023			const aabbMax = [
13024				-1,
13025				-1,
13026				-1
13027			];
13028			for (const c of extremesPoints) for (let i = 0; i < 3; i++) {
13029				aabbMin[i] = Math.min(aabbMin[i], c[i]);
13030				aabbMax[i] = Math.max(aabbMax[i], c[i]);
13031			}
13032			const center = projectTileCoordinatesToSphere(EXTENT / 2, EXTENT / 2, tileID.x, tileID.y, tileID.z);
13033			const centerEast = cross([], [
13034				0,
13035				1,
13036				0
13037			], center);
13038			normalize(centerEast, centerEast);
13039			const north = cross([], center, centerEast);
13040			normalize(north, north);
13041			const axisEast = cross([], corners[2], corners[1]);
13042			normalize(axisEast, axisEast);
13043			const axisWest = cross([], corners[0], corners[3]);
13044			normalize(axisWest, axisWest);
13045			extremesPoints.push(scale$1([], center, maxElevation));
13046			if (tileID.y >= (1 << tileID.z) / 2) extremesPoints.push(scale$1([], projectTileCoordinatesToSphere(EXTENT / 2, 0, tileID.x, tileID.y, tileID.z), maxElevation));
13047			if (tileID.y < (1 << tileID.z) / 2) extremesPoints.push(scale$1([], projectTileCoordinatesToSphere(EXTENT / 2, EXTENT, tileID.x, tileID.y, tileID.z), maxElevation));
13048			const upDownMinMax = findAxisMinMax(center, extremesPoints);
13049			const northSouthMinMax = findAxisMinMax(north, extremesPoints);
13050			const planeUp = [
13051				-center[0],
13052				-center[1],
13053				-center[2],
13054				upDownMinMax.max
13055			];
13056			const planeDown = [
13057				center[0],
13058				center[1],
13059				center[2],
13060				-upDownMinMax.min
13061			];
13062			const planeNorth = [
13063				-north[0],
13064				-north[1],
13065				-north[2],
13066				northSouthMinMax.max
13067			];
13068			const planeSouth = [
13069				north[0],
13070				north[1],
13071				north[2],
13072				-northSouthMinMax.min
13073			];
13074			const planeEast = [...axisEast, 0];
13075			const planeWest = [...axisWest, 0];
13076			const points = [];
13077			if (tileID.y === 0) points.push(threePlaneIntersection(planeWest, planeEast, planeUp), threePlaneIntersection(planeWest, planeEast, planeDown));
13078			else points.push(threePlaneIntersection(planeNorth, planeEast, planeUp), threePlaneIntersection(planeNorth, planeEast, planeDown), threePlaneIntersection(planeNorth, planeWest, planeUp), threePlaneIntersection(planeNorth, planeWest, planeDown));
13079			if (tileID.y === (1 << tileID.z) - 1) points.push(threePlaneIntersection(planeWest, planeEast, planeUp), threePlaneIntersection(planeWest, planeEast, planeDown));
13080			else points.push(threePlaneIntersection(planeSouth, planeEast, planeUp), threePlaneIntersection(planeSouth, planeEast, planeDown), threePlaneIntersection(planeSouth, planeWest, planeUp), threePlaneIntersection(planeSouth, planeWest, planeDown));
13081			return new ConvexVolume(points, [
13082				planeUp,
13083				planeDown,
13084				planeNorth,
13085				planeSouth,
13086				planeEast,
13087				planeWest
13088			], aabbMin, aabbMax);
13089		}
13090	}
13091};
13092function findAxisMinMax(axis, points) {
13093	let min = Infinity;
13094	let max = -Infinity;
13095	for (const c of points) {
13096		const dot$1 = dot(axis, c);
13097		min = Math.min(min, dot$1);
13098		max = Math.max(max, dot$1);
13099	}
13100	return {
13101		min,
13102		max
13103	};
13104}
13105//#endregion
13106//#region src/geo/projection/vertical_perspective_transform.ts
13107const GLOBE_SAMPLES = 256;
13108const GLOBE_BISECT_EPSILON_T = 1e-12;
13109/** Latitudes outside the mercator range project past the world edge; the globe mesh still covers them. */
13110const MAX_MERCATOR_Y = 1 - 1e-9;
13111/** Two points on the unit globe closer than this, a micrometre, are the same point. */
13112const SAME_POINT_DISTANCE = 1e-12;
13113/** A camera whose horizontal offset is this small relative to its distance is straight above the center. */
13114const STRAIGHT_ABOVE_RATIO = 1e-9;
13115var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
13116	get pixelsToClipSpaceMatrix() {
13117		return this._helper.pixelsToClipSpaceMatrix;
13118	}
13119	get clipSpaceToPixelsMatrix() {
13120		return this._helper.clipSpaceToPixelsMatrix;
13121	}
13122	get pixelsToGLUnits() {
13123		return this._helper.pixelsToGLUnits;
13124	}
13125	get centerOffset() {
13126		return this._helper.centerOffset;
13127	}
13128	get size() {
13129		return this._helper.size;
13130	}
13131	get rotationMatrix() {
13132		return this._helper.rotationMatrix;
13133	}
13134	get centerPoint() {
13135		return this._helper.centerPoint;
13136	}
13137	get pixelsPerMeter() {
13138		return this._helper.pixelsPerMeter;
13139	}
13140	setMinZoom(zoom) {
13141		this._helper.setMinZoom(zoom);
13142	}
13143	setMaxZoom(zoom) {
13144		this._helper.setMaxZoom(zoom);
13145	}
13146	setMinPitch(pitch) {
13147		this._helper.setMinPitch(pitch);
13148	}
13149	setMaxPitch(pitch) {
13150		this._helper.setMaxPitch(pitch);
13151	}
13152	setRenderWorldCopies(renderWorldCopies) {
13153		this._helper.setRenderWorldCopies(renderWorldCopies);
13154	}
13155	setBearing(bearing) {
13156		this._helper.setBearing(bearing);
13157	}
13158	setPitch(pitch) {
13159		this._helper.setPitch(pitch);
13160	}
13161	setRoll(roll) {
13162		this._helper.setRoll(roll);
13163	}
13164	setFov(fov) {
13165		this._helper.setFov(fov);
13166	}
13167	setZoom(zoom) {
13168		this._helper.setZoom(zoom);
13169	}
13170	setCenter(center) {
13171		this._helper.setCenter(center);
13172	}
13173	setElevation(elevation) {
13174		this._helper.setElevation(elevation);
13175	}
13176	setMinElevationForCurrentTile(elevation) {
13177		this._helper.setMinElevationForCurrentTile(elevation);
13178	}
13179	setPadding(padding) {
13180		this._helper.setPadding(padding);
13181	}
13182	interpolatePadding(start, target, t) {
13183		this._helper.interpolatePadding(start, target, t);
13184	}
13185	isPaddingEqual(padding) {
13186		return this._helper.isPaddingEqual(padding);
13187	}
13188	resize(width, height) {
13189		this._helper.resize(width, height);
13190	}
13191	getMaxBounds() {
13192		return this._helper.getMaxBounds();
13193	}
13194	setMaxBounds(bounds) {
13195		this._helper.setMaxBounds(bounds);
13196	}
13197	setConstrainOverride(constrain) {
13198		this._helper.setConstrainOverride(constrain);
13199	}
13200	overrideNearFarZ(nearZ, farZ) {
13201		this._helper.overrideNearFarZ(nearZ, farZ);
13202	}
13203	clearNearFarZOverride() {
13204		this._helper.clearNearFarZOverride();
13205	}
13206	getCameraQueryGeometry(queryGeometry) {
13207		return this._helper.getCameraQueryGeometry(this.getCameraPoint(), queryGeometry);
13208	}
13209	get tileSize() {
13210		return this._helper.tileSize;
13211	}
13212	get tileZoom() {
13213		return this._helper.tileZoom;
13214	}
13215	get scale() {
13216		return this._helper.scale;
13217	}
13218	get worldSize() {
13219		return this._helper.worldSize;
13220	}
13221	get width() {
13222		return this._helper.width;
13223	}
13224	get height() {
13225		return this._helper.height;
13226	}
13227	get lngRange() {
13228		return this._helper.lngRange;
13229	}
13230	get latRange() {
13231		return this._helper.latRange;
13232	}
13233	get minZoom() {
13234		return this._helper.minZoom;
13235	}
13236	get maxZoom() {
13237		return this._helper.maxZoom;
13238	}
13239	get zoom() {
13240		return this._helper.zoom;
13241	}
13242	get center() {
13243		return this._helper.center;
13244	}
13245	get minPitch() {
13246		return this._helper.minPitch;
13247	}
13248	get maxPitch() {
13249		return this._helper.maxPitch;
13250	}
13251	get pitch() {
13252		return this._helper.pitch;
13253	}
13254	get pitchInRadians() {
13255		return this._helper.pitchInRadians;
13256	}
13257	get roll() {
13258		return this._helper.roll;
13259	}
13260	get rollInRadians() {
13261		return this._helper.rollInRadians;
13262	}
13263	get bearing() {
13264		return this._helper.bearing;
13265	}
13266	get bearingInRadians() {
13267		return this._helper.bearingInRadians;
13268	}
13269	get fov() {
13270		return this._helper.fov;
13271	}
13272	get fovInRadians() {
13273		return this._helper.fovInRadians;
13274	}
13275	get elevation() {
13276		return this._helper.elevation;
13277	}
13278	get minElevationForCurrentTile() {
13279		return this._helper.minElevationForCurrentTile;
13280	}
13281	get padding() {
13282		return this._helper.padding;
13283	}
13284	get unmodified() {
13285		return this._helper.unmodified;
13286	}
13287	get renderWorldCopies() {
13288		return this._helper.renderWorldCopies;
13289	}
13290	get constrainOverride() {
13291		return this._helper.constrainOverride;
13292	}
13293	get nearZ() {
13294		return this._helper.nearZ;
13295	}
13296	get farZ() {
13297		return this._helper.farZ;
13298	}
13299	get autoCalculateNearFarZ() {
13300		return this._helper.autoCalculateNearFarZ;
13301	}
13302	setTransitionState(_value) {}
13303	/**
13304	* @param options - Initial state. Ignored when `sharedHelper` is given, which already carries it.
13305	* @param sharedHelper - Camera to use instead of owning one, so that a composing transform such as
13306	* {@link GlobeTransform} keeps a single copy of the state rather than one per child. Its owner then drives
13307	* {@link _calcMatrices}, because a helper has only one `calcMatrices` callback.
13308	*/
13309	constructor(options, sharedHelper) {
13310		this._cachedClippingPlane = createVec4f64();
13311		this._projectionMatrix = createIdentityMat4f64();
13312		this._globeViewProjMatrix32f = createIdentityMat4f32();
13313		this._globeViewProjMatrixF64 = createIdentityMat4f64();
13314		this._globeViewProjMatrixF64Inverted = createIdentityMat4f64();
13315		this._globeProjMatrixInverted = createIdentityMat4f64();
13316		this._cameraPosition = createVec3f64();
13317		this.defaultConstrain = (lngLat, zoom) => {
13318			const constrainedLat = clamp(lngLat.lat, -MAX_VALID_LATITUDE, MAX_VALID_LATITUDE);
13319			const constrainedZoom = clamp(+zoom, this.minZoom + getZoomAdjustment(0, constrainedLat), this.maxZoom);
13320			return {
13321				center: new LngLat(lngLat.lng, constrainedLat),
13322				zoom: constrainedZoom
13323			};
13324		};
13325		this.applyConstrain = (lngLat, zoom) => {
13326			return this._helper.applyConstrain(lngLat, zoom);
13327		};
13328		this._helper = sharedHelper ?? new TransformHelper({
13329			calcMatrices: () => this._calcMatrices(),
13330			defaultConstrain: (center, zoom) => {
13331				return this.defaultConstrain(center, zoom);
13332			}
13333		}, options);
13334		this._coveringTilesDetailsProvider = new GlobeCoveringTilesDetailsProvider();
13335	}
13336	clone() {
13337		const clone = new VerticalPerspectiveTransform();
13338		clone.apply(this, false);
13339		return clone;
13340	}
13341	apply(that, constrain) {
13342		this._helper.apply(that, constrain);
13343	}
13344	get projectionMatrix() {
13345		return this._projectionMatrix;
13346	}
13347	get modelViewProjectionMatrix() {
13348		return this._globeViewProjMatrixF64;
13349	}
13350	get inverseProjectionMatrix() {
13351		return this._globeProjMatrixInverted;
13352	}
13353	get cameraPosition() {
13354		const copy = createVec3f64();
13355		copy[0] = this._cameraPosition[0];
13356		copy[1] = this._cameraPosition[1];
13357		copy[2] = this._cameraPosition[2];
13358		return copy;
13359	}
13360	get cameraToCenterDistance() {
13361		return this._helper.cameraToCenterDistance;
13362	}
13363	getProjectionData(params) {
13364		const { overscaledTileID, applyGlobeMatrix } = params;
13365		const mercatorTileCoordinates = this._helper.getMercatorTileCoordinates(overscaledTileID);
13366		return {
13367			mainMatrix: this._globeViewProjMatrix32f,
13368			tileMercatorCoords: mercatorTileCoordinates,
13369			clippingPlane: this._cachedClippingPlane,
13370			projectionTransition: applyGlobeMatrix ? 1 : 0,
13371			fallbackMatrix: this._globeViewProjMatrix32f,
13372			clipAntimeridian: overscaledTileID?.canonical.z === 0
13373		};
13374	}
13375	_computeClippingPlane(globeRadiusPixels) {
13376		const pitch = this.pitchInRadians;
13377		const distanceCameraToB = this.cameraToCenterDistance / globeRadiusPixels;
13378		const radius = 1;
13379		const distanceCameraToA = Math.sin(pitch) * distanceCameraToB;
13380		const distanceAtoC = Math.cos(pitch) * distanceCameraToB + radius;
13381		const tangentPlaneDistanceToC = radius / Math.sqrt(distanceCameraToA * distanceCameraToA + distanceAtoC * distanceAtoC) * radius;
13382		let vectorCtoCamX = -distanceCameraToA;
13383		let vectorCtoCamY = distanceAtoC;
13384		const vectorCtoCamLength = Math.sqrt(vectorCtoCamX * vectorCtoCamX + vectorCtoCamY * vectorCtoCamY);
13385		vectorCtoCamX /= vectorCtoCamLength;
13386		vectorCtoCamY /= vectorCtoCamLength;
13387		const planeVector = [
13388			0,
13389			vectorCtoCamX,
13390			vectorCtoCamY
13391		];
13392		rotateZ$1(planeVector, planeVector, [
13393			0,
13394			0,
13395			0
13396		], -this.bearingInRadians);
13397		rotateX$1(planeVector, planeVector, [
13398			0,
13399			0,
13400			0
13401		], -1 * this.center.lat * Math.PI / 180);
13402		rotateY(planeVector, planeVector, [
13403			0,
13404			0,
13405			0
13406		], this.center.lng * Math.PI / 180);
13407		const scale = 1 / length(planeVector);
13408		scale$1(planeVector, planeVector, scale);
13409		return [...planeVector, -tangentPlaneDistanceToC * scale];
13410	}
13411	/** {@inheritDoc ITransform.isLocationOccluded} */
13412	isLocationOccluded(lngLat, terrain, elevation) {
13413		const coverage = terrain?.getCoverageIndex();
13414		elevation ??= coverage ? terrain.getElevationForLngLat(lngLat, this) : 0;
13415		const location = raisedSurfaceVector(lngLat, elevation);
13416		if (!this.isSurfacePointVisible(location)) return true;
13417		if (!coverage) return false;
13418		const p = this._projectSurfacePointToScreen(location);
13419		const origin = this.cameraPosition;
13420		const direction = this.getRayDirectionFromPixel(p);
13421		const tLocation = rayParameter(origin, direction, location);
13422		if (tLocation <= 0) return true;
13423		const hit = this.screenTerrainPointToMercatorCoordinate(p, terrain);
13424		return hit != null && rayParameter(origin, direction, raisedSurfaceVector(hit.toLngLat(), hit.z)) < tLocation * .99;
13425	}
13426	transformLightDirection(dir) {
13427		const sphereX = this._helper._center.lng * Math.PI / 180;
13428		const sphereY = this._helper._center.lat * Math.PI / 180;
13429		const len = Math.cos(sphereY);
13430		const spherePos = [
13431			Math.sin(sphereX) * len,
13432			Math.sin(sphereY),
13433			Math.cos(sphereX) * len
13434		];
13435		const axisRight = [
13436			spherePos[2],
13437			0,
13438			-spherePos[0]
13439		];
13440		const axisDown = [
13441			0,
13442			0,
13443			0
13444		];
13445		cross(axisDown, axisRight, spherePos);
13446		normalize(axisRight, axisRight);
13447		normalize(axisDown, axisDown);
13448		const transformed = [
13449			axisRight[0] * dir[0] + axisDown[0] * dir[1] + spherePos[0] * dir[2],
13450			axisRight[1] * dir[0] + axisDown[1] * dir[1] + spherePos[1] * dir[2],
13451			axisRight[2] * dir[0] + axisDown[2] * dir[1] + spherePos[2] * dir[2]
13452		];
13453		const normalized = [
13454			0,
13455			0,
13456			0
13457		];
13458		normalize(normalized, transformed);
13459		return normalized;
13460	}
13461	getPixelScale() {
13462		return 1 / Math.cos(this._helper._center.lat * Math.PI / 180);
13463	}
13464	getCircleRadiusCorrection() {
13465		return Math.cos(this._helper._center.lat * Math.PI / 180);
13466	}
13467	getPitchedTextCorrection(textAnchorX, textAnchorY, tileID) {
13468		const mercator = tileCoordinatesToMercatorCoordinates(textAnchorX, textAnchorY, tileID.canonical);
13469		const angular = mercatorCoordinatesToAngularCoordinatesRadians(mercator.x, mercator.y);
13470		return this.getCircleRadiusCorrection() / Math.cos(angular[1]);
13471	}
13472	projectTileCoordinates(x, y, unwrappedTileID, elevation) {
13473		const canonical = unwrappedTileID.canonical;
13474		const spherePos = projectTileCoordinatesToSphere(x, y, canonical.x, canonical.y, canonical.z);
13475		const vectorMultiplier = 1 + (elevation ?? 0) / earthRadius;
13476		const elevatedX = spherePos[0] * vectorMultiplier;
13477		const elevatedY = spherePos[1] * vectorMultiplier;
13478		const elevatedZ = spherePos[2] * vectorMultiplier;
13479		const pos = [
13480			elevatedX,
13481			elevatedY,
13482			elevatedZ,
13483			1
13484		];
13485		transformMat4(pos, pos, this._globeViewProjMatrixF64);
13486		let isOccluded;
13487		if (vectorMultiplier <= 1) {
13488			const plane = this._cachedClippingPlane;
13489			isOccluded = plane[0] * spherePos[0] + plane[1] * spherePos[1] + plane[2] * spherePos[2] + plane[3] < 0;
13490		} else isOccluded = this._isLineOfSightBlocked(elevatedX, elevatedY, elevatedZ);
13491		return {
13492			point: new Point(pos[0] / pos[3], pos[1] / pos[3]),
13493			signedDistanceFromCamera: pos[3],
13494			isOccluded
13495		};
13496	}
13497	/**
13498	* True when the segment from the camera to the given point, both in unit-globe coordinates, passes through the planet.
13499	*/
13500	_isLineOfSightBlocked(x, y, z) {
13501		const cam = this._cameraPosition;
13502		const dx = x - cam[0];
13503		const dy = y - cam[1];
13504		const dz = z - cam[2];
13505		const lengthSq = dx * dx + dy * dy + dz * dz;
13506		if (lengthSq === 0) return false;
13507		const t = clamp(-(cam[0] * dx + cam[1] * dy + cam[2] * dz) / lengthSq, 0, 1);
13508		const cx = cam[0] + t * dx;
13509		const cy = cam[1] + t * dy;
13510		const cz = cam[2] + t * dz;
13511		return cx * cx + cy * cy + cz * cz < 1;
13512	}
13513	/**
13514	* @param calculateNearFarZ - Whether to compute the near/far Z range, or leave the range the helper already
13515	* holds. Defaults to {@link autoCalculateNearFarZ}; a composing transform such as {@link GlobeTransform}
13516	* overrides it so that its two children share a single depth range.
13517	*/
13518	_calcMatrices(calculateNearFarZ = this._helper.autoCalculateNearFarZ) {
13519		const globeRadiusPixels = getGlobeRadiusPixels(this.worldSize, this.center.lat);
13520		const globeMatrix = createMat4f64();
13521		if (calculateNearFarZ) {
13522			this._helper._nearZ = .5;
13523			this._helper._farZ = this.cameraToCenterDistance + globeRadiusPixels * 2;
13524		}
13525		perspective(globeMatrix, this.fovInRadians, this.width / this.height, this._helper._nearZ, this._helper._farZ);
13526		const offset = this.centerOffset;
13527		globeMatrix[8] = -offset.x * 2 / this._helper._width;
13528		globeMatrix[9] = offset.y * 2 / this._helper._height;
13529		this._projectionMatrix = clone$1(globeMatrix);
13530		this._globeProjMatrixInverted = createMat4f64();
13531		invert$1(this._globeProjMatrixInverted, globeMatrix);
13532		translate(globeMatrix, globeMatrix, [
13533			0,
13534			0,
13535			-this.cameraToCenterDistance
13536		]);
13537		rotateZ(globeMatrix, globeMatrix, this.rollInRadians);
13538		rotateX(globeMatrix, globeMatrix, -this.pitchInRadians);
13539		rotateZ(globeMatrix, globeMatrix, this.bearingInRadians);
13540		translate(globeMatrix, globeMatrix, [
13541			0,
13542			0,
13543			-globeRadiusPixels
13544		]);
13545		const scaleVec = createVec3f64();
13546		scaleVec[0] = globeRadiusPixels;
13547		scaleVec[1] = globeRadiusPixels;
13548		scaleVec[2] = globeRadiusPixels;
13549		rotateX(globeMatrix, globeMatrix, this.center.lat * Math.PI / 180);
13550		rotateY$1(globeMatrix, globeMatrix, -this.center.lng * Math.PI / 180);
13551		scale(globeMatrix, globeMatrix, scaleVec);
13552		this._globeViewProjMatrixF64 = globeMatrix;
13553		this._globeViewProjMatrix32f = new Float32Array(globeMatrix);
13554		this._globeViewProjMatrixF64Inverted = createMat4f64();
13555		invert$1(this._globeViewProjMatrixF64Inverted, globeMatrix);
13556		const zero = createVec3f64();
13557		this._cameraPosition = createVec3f64();
13558		this._cameraPosition[2] = this.cameraToCenterDistance / globeRadiusPixels;
13559		rotateZ$1(this._cameraPosition, this._cameraPosition, zero, -this.rollInRadians);
13560		rotateX$1(this._cameraPosition, this._cameraPosition, zero, this.pitchInRadians);
13561		rotateZ$1(this._cameraPosition, this._cameraPosition, zero, -this.bearingInRadians);
13562		add(this._cameraPosition, this._cameraPosition, [
13563			0,
13564			0,
13565			1
13566		]);
13567		rotateX$1(this._cameraPosition, this._cameraPosition, zero, -this.center.lat * Math.PI / 180);
13568		rotateY(this._cameraPosition, this._cameraPosition, zero, this.center.lng * Math.PI / 180);
13569		this._cachedClippingPlane = this._computeClippingPlane(globeRadiusPixels);
13570		const matrix = clone$1(this._globeViewProjMatrixF64Inverted);
13571		scale(matrix, matrix, [
13572			1,
13573			1,
13574			-1
13575		]);
13576		this._cachedFrustum = Frustum.fromInvProjectionMatrix(matrix, 1, 0, this._cachedClippingPlane, true);
13577	}
13578	calculateFogMatrix(_unwrappedTileID) {
13579		warnOnce("calculateFogMatrix is not supported on globe projection.");
13580		const m = createMat4f64();
13581		identity(m);
13582		return m;
13583	}
13584	getVisibleUnwrappedCoordinates(tileID) {
13585		return [new UnwrappedTileID(0, tileID)];
13586	}
13587	getCameraFrustum() {
13588		return this._cachedFrustum;
13589	}
13590	getClippingPlane() {
13591		return this._cachedClippingPlane;
13592	}
13593	getCoveringTilesDetailsProvider() {
13594		return this._coveringTilesDetailsProvider;
13595	}
13596	recalculateZoomAndCenter(terrain) {
13597		if (terrain) {
13598			warnOnce("terrain is not fully supported on vertical perspective projection.");
13599			return;
13600		}
13601		this._helper.recalculateZoomAndCenter(0);
13602	}
13603	maxPitchScaleFactor() {
13604		return 1;
13605	}
13606	getCameraPoint() {
13607		return this._helper.getCameraPoint();
13608	}
13609	/**
13610	* The altitude of the rendered camera above sea level. The sphere keeps the center point at sea level whatever its
13611	* elevation (`_calcMatrices` does not apply it), so unlike on mercator the center elevation does not lift the camera.
13612	* {@link calculateCameraOptionsFromTo} is the inverse.
13613	*/
13614	getCameraAltitude() {
13615		return (length(this._cameraPosition) - 1) * earthRadius;
13616	}
13617	getCameraLngLat() {
13618		const surface = createVec3f64();
13619		normalize(surface, this._cameraPosition);
13620		return sphereSurfacePointToCoordinates(surface);
13621	}
13622	populateCache(_coords) {}
13623	getBounds() {
13624		const xMid = this.width * .5;
13625		const yMid = this.height * .5;
13626		const testPoints = [
13627			new Point(0, 0),
13628			new Point(xMid, 0),
13629			new Point(this.width, 0),
13630			new Point(this.width, yMid),
13631			new Point(this.width, this.height),
13632			new Point(xMid, this.height),
13633			new Point(0, this.height),
13634			new Point(0, yMid)
13635		];
13636		const projectedPoints = [];
13637		for (const p of testPoints) projectedPoints.push(this.unprojectScreenPoint(p));
13638		let mostEast = 0, mostWest = 0, mostNorth = 0, mostSouth = 0;
13639		const center = this.center;
13640		for (const p of projectedPoints) {
13641			const dLng = differenceOfAnglesDegrees(center.lng, p.lng);
13642			const dLat = differenceOfAnglesDegrees(center.lat, p.lat);
13643			if (dLng < mostWest) mostWest = dLng;
13644			if (dLng > mostEast) mostEast = dLng;
13645			if (dLat < mostSouth) mostSouth = dLat;
13646			if (dLat > mostNorth) mostNorth = dLat;
13647		}
13648		const boundsArray = [
13649			center.lng + mostWest,
13650			center.lat + mostSouth,
13651			center.lng + mostEast,
13652			center.lat + mostNorth
13653		];
13654		if (this.isSurfacePointOnScreen([
13655			0,
13656			1,
13657			0
13658		])) {
13659			boundsArray[3] = 90;
13660			boundsArray[0] = -180;
13661			boundsArray[2] = 180;
13662		}
13663		if (this.isSurfacePointOnScreen([
13664			0,
13665			-1,
13666			0
13667		])) {
13668			boundsArray[1] = -90;
13669			boundsArray[0] = -180;
13670			boundsArray[2] = 180;
13671		}
13672		return new LngLatBounds(boundsArray);
13673	}
13674	calculateCenterFromCameraLngLatAlt(lngLat, alt, bearing, pitch) {
13675		return this._helper.calculateCenterFromCameraLngLatAlt(lngLat, alt, bearing, pitch);
13676	}
13677	/**
13678	* Inverts the camera placement of `_calcMatrices` in unit-globe coordinates: the camera sits at radius
13679	* `1 + altitudeFrom / earthRadius` and looks at the center on the sea-level sphere, the target altitude only becoming
13680	* the center elevation (the inverse of {@link getCameraAltitude}). Pitch and bearing are read in the center's local
13681	* frame, +z up, +y north, +x east. A camera straight above the center keeps the transform's bearing.
13682	*/
13683	calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo) {
13684		const center = LngLat.convert(to);
13685		const camera = angularCoordinatesToSurfaceVector(LngLat.convert(from));
13686		scale$1(camera, camera, 1 + altitudeFrom / earthRadius);
13687		const target = angularCoordinatesToSurfaceVector(center);
13688		const targetAtAltitude = scale$1(createVec3f64(), target, 1 + altitudeTo / earthRadius);
13689		const toCamera = subtract(createVec3f64(), camera, target);
13690		const distance$1 = length(toCamera);
13691		if (distance$1 < SAME_POINT_DISTANCE || distance(camera, targetAtAltitude) < SAME_POINT_DISTANCE) throw new Error("Can't calculate camera options with same From and To");
13692		const zero = createVec3f64();
13693		rotateY(toCamera, toCamera, zero, -degreesToRadians(center.lng));
13694		rotateX$1(toCamera, toCamera, zero, degreesToRadians(center.lat));
13695		const pitch = radiansToDegrees(Math.acos(clamp(toCamera[2] / distance$1, -1, 1)));
13696		const bearing = Math.hypot(toCamera[0], toCamera[1]) < distance$1 * STRAIGHT_ABOVE_RATIO ? this.bearing : radiansToDegrees(Math.atan2(-toCamera[0], -toCamera[1]));
13697		return {
13698			center,
13699			elevation: altitudeTo,
13700			zoom: scaleZoom(this.cameraToCenterDistance / distance$1 / getGlobeRadiusPixels(this.tileSize, center.lat)),
13701			pitch,
13702			bearing
13703		};
13704	}
13705	/**
13706	* Note: automatically adjusts zoom to keep planet size consistent
13707	* (same size before and after a {@link setLocationAtPoint} call).
13708	*/
13709	setLocationAtPoint(lnglat, point, _elevation) {
13710		const vecToPixelCurrent = angularCoordinatesToSurfaceVector(this.unprojectScreenPoint(point));
13711		const vecToTarget = angularCoordinatesToSurfaceVector(lnglat);
13712		const zero = createVec3f64();
13713		zero$1(zero);
13714		const rotatedPixelVector = createVec3f64();
13715		rotateY(rotatedPixelVector, vecToPixelCurrent, zero, -this.center.lng * Math.PI / 180);
13716		rotateX$1(rotatedPixelVector, rotatedPixelVector, zero, this.center.lat * Math.PI / 180);
13717		const vecToTargetXZLengthSquared = vecToTarget[0] * vecToTarget[0] + vecToTarget[2] * vecToTarget[2];
13718		const targetXSquared = rotatedPixelVector[0] * rotatedPixelVector[0];
13719		if (vecToTargetXZLengthSquared < targetXSquared) return;
13720		const intersectionA = Math.sqrt(vecToTargetXZLengthSquared - targetXSquared);
13721		const intersectionB = -intersectionA;
13722		const lngA = angleToRotateBetweenVectors2D(vecToTarget[0], vecToTarget[2], rotatedPixelVector[0], intersectionA);
13723		const lngB = angleToRotateBetweenVectors2D(vecToTarget[0], vecToTarget[2], rotatedPixelVector[0], intersectionB);
13724		const vecToTargetLngA = createVec3f64();
13725		rotateY(vecToTargetLngA, vecToTarget, zero, -lngA);
13726		const latA = angleToRotateBetweenVectors2D(vecToTargetLngA[1], vecToTargetLngA[2], rotatedPixelVector[1], rotatedPixelVector[2]);
13727		const vecToTargetLngB = createVec3f64();
13728		rotateY(vecToTargetLngB, vecToTarget, zero, -lngB);
13729		const latB = angleToRotateBetweenVectors2D(vecToTargetLngB[1], vecToTargetLngB[2], rotatedPixelVector[1], rotatedPixelVector[2]);
13730		const limit = Math.PI * .5;
13731		const isValidA = latA >= -limit && latA <= limit;
13732		const isValidB = latB >= -limit && latB <= limit;
13733		let validLng;
13734		let validLat;
13735		if (isValidA && isValidB) {
13736			const centerLngRadians = this.center.lng * Math.PI / 180;
13737			const centerLatRadians = this.center.lat * Math.PI / 180;
13738			const lngDistA = distanceOfAnglesRadians(lngA, centerLngRadians);
13739			const latDistA = distanceOfAnglesRadians(latA, centerLatRadians);
13740			const lngDistB = distanceOfAnglesRadians(lngB, centerLngRadians);
13741			const latDistB = distanceOfAnglesRadians(latB, centerLatRadians);
13742			if (lngDistA + latDistA < lngDistB + latDistB) {
13743				validLng = lngA;
13744				validLat = latA;
13745			} else {
13746				validLng = lngB;
13747				validLat = latB;
13748			}
13749		} else if (isValidA) {
13750			validLng = lngA;
13751			validLat = latA;
13752		} else if (isValidB) {
13753			validLng = lngB;
13754			validLat = latB;
13755		} else return;
13756		const newLng = validLng / Math.PI * 180;
13757		const newLat = validLat / Math.PI * 180;
13758		const oldLat = this.center.lat;
13759		this.setCenter(new LngLat(newLng, clamp(newLat, -90, 90)));
13760		this.setZoom(this.zoom + getZoomAdjustment(oldLat, this.center.lat));
13761	}
13762	locationToScreenPoint(lnglat, terrain) {
13763		const pos = angularCoordinatesToSurfaceVector(lnglat);
13764		if (terrain) {
13765			const elevation = terrain.getElevationForLngLatZoom(lnglat, this._helper._tileZoom);
13766			scale$1(pos, pos, 1 + elevation / earthRadius);
13767		}
13768		return this._projectSurfacePointToScreen(pos);
13769	}
13770	/**
13771	* Projects a given vector on the surface of a unit sphere (or possible above the surface)
13772	* and returns its coordinates on screen in pixels.
13773	*/
13774	_projectSurfacePointToScreen(pos) {
13775		const projected = createVec4f64();
13776		transformMat4(projected, [...pos, 1], this._globeViewProjMatrixF64);
13777		projected[0] /= projected[3];
13778		projected[1] /= projected[3];
13779		return new Point((projected[0] * .5 + .5) * this.width, (-projected[1] * .5 + .5) * this.height);
13780	}
13781	screenPointToMercatorCoordinate(p, terrain) {
13782		if (terrain) {
13783			const coordinate = this.screenTerrainPointToMercatorCoordinate(p, terrain);
13784			if (coordinate) return coordinate;
13785		}
13786		return MercatorCoordinate.fromLngLat(this.unprojectScreenPoint(p));
13787	}
13788	/** {@inheritDoc ITransform.screenTerrainPointToMercatorCoordinate} */
13789	screenTerrainPointToMercatorCoordinate(p, terrain) {
13790		const index = terrain.getCoverageIndex();
13791		if (!index) return null;
13792		const origin = this.cameraPosition;
13793		const direction = this.getRayDirectionFromPixel(p);
13794		const outer = raySphereIntersection(origin, direction, 1 + index.maxElevation / earthRadius);
13795		if (!outer) return null;
13796		const inner = raySphereIntersection(origin, direction, 1 + index.minElevation / earthRadius);
13797		const tStart = Math.max(outer.tMin, 0);
13798		const tEnd = inner ? inner.tMin : outer.tMax;
13799		if (tEnd <= tStart) return null;
13800		const ray = {
13801			index,
13802			exaggeration: terrain.exaggeration,
13803			origin,
13804			direction
13805		};
13806		let previousT = 0;
13807		for (let i = 0; i <= GLOBE_SAMPLES; i++) {
13808			const t = tStart + (tEnd - tStart) * i / GLOBE_SAMPLES;
13809			if (globeIsBelowTerrain(ray, t)) {
13810				const { hi } = bisect(ray, globeIsBelowTerrain, previousT, t, GLOBE_BISECT_EPSILON_T);
13811				const { sample, mercator } = globeSampleAt(ray, hi);
13812				return new MercatorCoordinate(mercator.x, mercator.y, sample.elevation);
13813			}
13814			previousT = t;
13815		}
13816		return null;
13817	}
13818	screenPointToLocation(p, terrain) {
13819		return this.screenPointToMercatorCoordinate(p, terrain)?.toLngLat();
13820	}
13821	screenPointToLocationAtElevation(p, _elevation) {
13822		return this.screenPointToLocation(p);
13823	}
13824	isPointOnMapSurface(p, _terrain) {
13825		const rayOrigin = this._cameraPosition;
13826		return !!raySphereIntersection(rayOrigin, this.getRayDirectionFromPixel(p));
13827	}
13828	/**
13829	* Computes normalized direction of a ray from the camera to the given screen pixel.
13830	*/
13831	getRayDirectionFromPixel(p) {
13832		const pos = createVec4f64();
13833		pos[0] = p.x / this.width * 2 - 1;
13834		pos[1] = (p.y / this.height * 2 - 1) * -1;
13835		pos[2] = 1;
13836		pos[3] = 1;
13837		transformMat4(pos, pos, this._globeViewProjMatrixF64Inverted);
13838		pos[0] /= pos[3];
13839		pos[1] /= pos[3];
13840		pos[2] /= pos[3];
13841		const ray = createVec3f64();
13842		ray[0] = pos[0] - this._cameraPosition[0];
13843		ray[1] = pos[1] - this._cameraPosition[1];
13844		ray[2] = pos[2] - this._cameraPosition[2];
13845		const rayNormalized = createVec3f64();
13846		normalize(rayNormalized, ray);
13847		return rayNormalized;
13848	}
13849	/**
13850	* For a given point on the unit sphere of the planet, or raised above it, returns whether it lies on the camera's
13851	* side of the horizon plane, the plane the globe shaders clip with (not taking into account camera rotation at all).
13852	*/
13853	isSurfacePointVisible(p) {
13854		const plane = this._cachedClippingPlane;
13855		return plane[0] * p[0] + plane[1] * p[1] + plane[2] * p[2] + plane[3] >= 0;
13856	}
13857	/**
13858	* Returns whether surface point is visible on screen.
13859	* It must both project to a pixel in screen bounds and not be occluded by the planet.
13860	*/
13861	isSurfacePointOnScreen(vec) {
13862		if (!this.isSurfacePointVisible(vec)) return false;
13863		const projected = createVec4f64();
13864		transformMat4(projected, [...vec, 1], this._globeViewProjMatrixF64);
13865		projected[0] /= projected[3];
13866		projected[1] /= projected[3];
13867		projected[2] /= projected[3];
13868		return projected[0] > -1 && projected[0] < 1 && projected[1] > -1 && projected[1] < 1 && projected[2] > -1 && projected[2] < 1;
13869	}
13870	/**
13871	* @internal
13872	* Returns a {@link LngLat} representing geographical coordinates that correspond to the specified pixel coordinates.
13873	* Note: if the point does not lie on the globe, returns a location on the visible globe horizon (edge) that is
13874	* as close to the point as possible.
13875	* @param p - Screen point in pixels to unproject.
13876	* @param terrain - Optional terrain.
13877	*/
13878	unprojectScreenPoint(p) {
13879		const rayOrigin = this._cameraPosition;
13880		const rayDirection = this.getRayDirectionFromPixel(p);
13881		const intersection = raySphereIntersection(rayOrigin, rayDirection);
13882		if (intersection) {
13883			const intersectionPoint = createVec3f64();
13884			add(intersectionPoint, rayOrigin, [
13885				rayDirection[0] * intersection.tMin,
13886				rayDirection[1] * intersection.tMin,
13887				rayDirection[2] * intersection.tMin
13888			]);
13889			const sphereSurface = createVec3f64();
13890			normalize(sphereSurface, intersectionPoint);
13891			return sphereSurfacePointToCoordinates(sphereSurface);
13892		}
13893		const horizonPlane = this._cachedClippingPlane;
13894		const directionDotPlaneXyz = horizonPlane[0] * rayDirection[0] + horizonPlane[1] * rayDirection[1] + horizonPlane[2] * rayDirection[2];
13895		const distanceToIntersection = -pointPlaneSignedDistance(horizonPlane, rayOrigin) / directionDotPlaneXyz;
13896		const maxRayLength = 2;
13897		const planeIntersection = createVec3f64();
13898		if (distanceToIntersection > 0) add(planeIntersection, rayOrigin, [
13899			rayDirection[0] * distanceToIntersection,
13900			rayDirection[1] * distanceToIntersection,
13901			rayDirection[2] * distanceToIntersection
13902		]);
13903		else {
13904			const distantPoint = createVec3f64();
13905			add(distantPoint, rayOrigin, [
13906				rayDirection[0] * maxRayLength,
13907				rayDirection[1] * maxRayLength,
13908				rayDirection[2] * maxRayLength
13909			]);
13910			const distanceFromPlane = pointPlaneSignedDistance(this._cachedClippingPlane, distantPoint);
13911			sub(planeIntersection, distantPoint, [
13912				this._cachedClippingPlane[0] * distanceFromPlane,
13913				this._cachedClippingPlane[1] * distanceFromPlane,
13914				this._cachedClippingPlane[2] * distanceFromPlane
13915			]);
13916		}
13917		const horizonDisk = horizonPlaneToCenterAndRadius(horizonPlane);
13918		return sphereSurfacePointToCoordinates(clampToSphere(horizonDisk.center, horizonDisk.radius, planeIntersection));
13919	}
13920	getProjectionDataForCustomLayer(applyGlobeMatrix = true) {
13921		const globeData = this.getProjectionData({
13922			overscaledTileID: new OverscaledTileID(0, 0, 0, 0, 0),
13923			applyGlobeMatrix
13924		});
13925		globeData.tileMercatorCoords = [
13926			0,
13927			0,
13928			1,
13929			1
13930		];
13931		return globeData;
13932	}
13933	getFastPathSimpleProjectionMatrix(_tileID) {}
13934};
13935function globeSampleAt(ray, t) {
13936	const position = createVec3f64();
13937	scaleAndAdd(position, ray.origin, ray.direction, t);
13938	const radius = length(position);
13939	const surface = createVec3f64();
13940	scale$1(surface, position, 1 / radius);
13941	const lngLat = sphereSurfacePointToCoordinates(surface);
13942	const projected = MercatorCoordinate.fromLngLat(lngLat);
13943	const mercator = new MercatorCoordinate(projected.x, clamp(projected.y, 0, MAX_MERCATOR_Y));
13944	const sample = sampleAt(ray.index, ray.exaggeration, mercator.x, mercator.y);
13945	const elevation = Math.abs(lngLat.lat) > 85.051129 ? 0 : sample.elevation;
13946	return {
13947		sample: {
13948			...sample,
13949			elevation
13950		},
13951		radius,
13952		mercator
13953	};
13954}
13955function globeIsBelowTerrain(ray, t) {
13956	const { sample, radius } = globeSampleAt(ray, t);
13957	return isBelowTerrainSample(sample, (radius - 1) * earthRadius);
13958}
13959/**
13960* The location as a vector from the globe's center in globe radii, raised `elevation` meters above the surface.
13961*/
13962function raisedSurfaceVector(lngLat, elevation) {
13963	const vector = angularCoordinatesToSurfaceVector(lngLat);
13964	return scale$1(vector, vector, 1 + elevation / earthRadius);
13965}
13966/**
13967* Where the point of the ray closest to `point` lies along it, in units of `direction` from `origin`.
13968*/
13969function rayParameter(origin, direction, point) {
13970	const offset = subtract(createVec3f64(), point, origin);
13971	return dot(offset, direction);
13972}
13973//#endregion
13974//#region src/geo/projection/globe_transform.ts
13975/**
13976* Globe transform is a transform that moves between vertical perspective and mercator projections.
13977*
13978* The two child transforms share this transform's {@link TransformHelper}, so the camera exists exactly once:
13979* a child that moves itself - as {@link setLocationAtPoint} has it do, since only the child knows its own
13980* projection's geometry - has moved this transform too, with no copying back. The children own only what they
13981* derive from that shared camera, such as their matrices, and this transform drives their `_calcMatrices`
13982* because a helper has a single `calcMatrices` callback.
13983*/
13984var GlobeTransform = class GlobeTransform {
13985	get pixelsToClipSpaceMatrix() {
13986		return this._helper.pixelsToClipSpaceMatrix;
13987	}
13988	get clipSpaceToPixelsMatrix() {
13989		return this._helper.clipSpaceToPixelsMatrix;
13990	}
13991	get pixelsToGLUnits() {
13992		return this._helper.pixelsToGLUnits;
13993	}
13994	get centerOffset() {
13995		return this._helper.centerOffset;
13996	}
13997	get size() {
13998		return this._helper.size;
13999	}
14000	get rotationMatrix() {
14001		return this._helper.rotationMatrix;
14002	}
14003	get centerPoint() {
14004		return this._helper.centerPoint;
14005	}
14006	get pixelsPerMeter() {
14007		return this._helper.pixelsPerMeter;
14008	}
14009	setMinZoom(zoom) {
14010		this._helper.setMinZoom(zoom);
14011	}
14012	setMaxZoom(zoom) {
14013		this._helper.setMaxZoom(zoom);
14014	}
14015	setMinPitch(pitch) {
14016		this._helper.setMinPitch(pitch);
14017	}
14018	setMaxPitch(pitch) {
14019		this._helper.setMaxPitch(pitch);
14020	}
14021	setRenderWorldCopies(renderWorldCopies) {
14022		this._helper.setRenderWorldCopies(renderWorldCopies);
14023	}
14024	setBearing(bearing) {
14025		this._helper.setBearing(bearing);
14026	}
14027	setPitch(pitch) {
14028		this._helper.setPitch(pitch);
14029	}
14030	setRoll(roll) {
14031		this._helper.setRoll(roll);
14032	}
14033	setFov(fov) {
14034		this._helper.setFov(fov);
14035	}
14036	setZoom(zoom) {
14037		this._helper.setZoom(zoom);
14038	}
14039	setCenter(center) {
14040		this._helper.setCenter(center);
14041	}
14042	setElevation(elevation) {
14043		this._helper.setElevation(elevation);
14044	}
14045	setMinElevationForCurrentTile(elevation) {
14046		this._helper.setMinElevationForCurrentTile(elevation);
14047	}
14048	setPadding(padding) {
14049		this._helper.setPadding(padding);
14050	}
14051	interpolatePadding(start, target, t) {
14052		this._helper.interpolatePadding(start, target, t);
14053	}
14054	isPaddingEqual(padding) {
14055		return this._helper.isPaddingEqual(padding);
14056	}
14057	resize(width, height, constrainTransform = true) {
14058		this._helper.resize(width, height, constrainTransform);
14059	}
14060	getMaxBounds() {
14061		return this._helper.getMaxBounds();
14062	}
14063	setMaxBounds(bounds) {
14064		this._helper.setMaxBounds(bounds);
14065	}
14066	setConstrainOverride(constrain) {
14067		this._helper.setConstrainOverride(constrain);
14068	}
14069	overrideNearFarZ(nearZ, farZ) {
14070		this._helper.overrideNearFarZ(nearZ, farZ);
14071	}
14072	clearNearFarZOverride() {
14073		this._helper.clearNearFarZOverride();
14074	}
14075	getCameraQueryGeometry(queryGeometry) {
14076		return this._helper.getCameraQueryGeometry(this.getCameraPoint(), queryGeometry);
14077	}
14078	get tileSize() {
14079		return this._helper.tileSize;
14080	}
14081	get tileZoom() {
14082		return this._helper.tileZoom;
14083	}
14084	get scale() {
14085		return this._helper.scale;
14086	}
14087	get worldSize() {
14088		return this._helper.worldSize;
14089	}
14090	get width() {
14091		return this._helper.width;
14092	}
14093	get height() {
14094		return this._helper.height;
14095	}
14096	get lngRange() {
14097		return this._helper.lngRange;
14098	}
14099	get latRange() {
14100		return this._helper.latRange;
14101	}
14102	get minZoom() {
14103		return this._helper.minZoom;
14104	}
14105	get maxZoom() {
14106		return this._helper.maxZoom;
14107	}
14108	get zoom() {
14109		return this._helper.zoom;
14110	}
14111	get center() {
14112		return this._helper.center;
14113	}
14114	get minPitch() {
14115		return this._helper.minPitch;
14116	}
14117	get maxPitch() {
14118		return this._helper.maxPitch;
14119	}
14120	get pitch() {
14121		return this._helper.pitch;
14122	}
14123	get pitchInRadians() {
14124		return this._helper.pitchInRadians;
14125	}
14126	get roll() {
14127		return this._helper.roll;
14128	}
14129	get rollInRadians() {
14130		return this._helper.rollInRadians;
14131	}
14132	get bearing() {
14133		return this._helper.bearing;
14134	}
14135	get bearingInRadians() {
14136		return this._helper.bearingInRadians;
14137	}
14138	get fov() {
14139		return this._helper.fov;
14140	}
14141	get fovInRadians() {
14142		return this._helper.fovInRadians;
14143	}
14144	get elevation() {
14145		return this._helper.elevation;
14146	}
14147	get minElevationForCurrentTile() {
14148		return this._helper.minElevationForCurrentTile;
14149	}
14150	get padding() {
14151		return this._helper.padding;
14152	}
14153	get unmodified() {
14154		return this._helper.unmodified;
14155	}
14156	get renderWorldCopies() {
14157		return this._helper.renderWorldCopies;
14158	}
14159	get cameraToCenterDistance() {
14160		return this._helper.cameraToCenterDistance;
14161	}
14162	get constrainOverride() {
14163		return this._helper.constrainOverride;
14164	}
14165	get nearZ() {
14166		return this._helper.nearZ;
14167	}
14168	get farZ() {
14169		return this._helper.farZ;
14170	}
14171	get autoCalculateNearFarZ() {
14172		return this._helper.autoCalculateNearFarZ;
14173	}
14174	/**
14175	* True when globe render path should be used instead of the old but simpler mercator rendering.
14176	* Globe automatically transitions to mercator at high zoom levels, which causes a switch from
14177	* globe to mercator render path.
14178	*/
14179	get isGlobeRendering() {
14180		return this._globeness > 0;
14181	}
14182	setTransitionState(globeness) {
14183		this._globeness = globeness;
14184		this._calcMatrices();
14185		this._verticalPerspectiveTransform.getCoveringTilesDetailsProvider().prepareNextFrame();
14186		this._mercatorTransform.getCoveringTilesDetailsProvider().prepareNextFrame();
14187	}
14188	get currentTransform() {
14189		return this.isGlobeRendering ? this._verticalPerspectiveTransform : this._mercatorTransform;
14190	}
14191	constructor(options) {
14192		this._globeness = 1;
14193		this.defaultConstrain = (lngLat, zoom) => {
14194			return this.currentTransform.defaultConstrain(lngLat, zoom);
14195		};
14196		this.applyConstrain = (lngLat, zoom) => {
14197			return this._helper.applyConstrain(lngLat, zoom);
14198		};
14199		this._helper = new TransformHelper({
14200			calcMatrices: () => this._calcMatrices(),
14201			defaultConstrain: (center, zoom) => {
14202				return this.defaultConstrain(center, zoom);
14203			}
14204		}, options);
14205		this._globeness = 1;
14206		this._mercatorTransform = new MercatorTransform(void 0, this._helper);
14207		this._verticalPerspectiveTransform = new VerticalPerspectiveTransform(void 0, this._helper);
14208	}
14209	clone() {
14210		const clone = new GlobeTransform();
14211		clone._globeness = this._globeness;
14212		clone.apply(this, false);
14213		return clone;
14214	}
14215	apply(that, constrain) {
14216		this._helper.apply(that, constrain);
14217	}
14218	get projectionMatrix() {
14219		return this.currentTransform.projectionMatrix;
14220	}
14221	get modelViewProjectionMatrix() {
14222		return this.currentTransform.modelViewProjectionMatrix;
14223	}
14224	get inverseProjectionMatrix() {
14225		return this.currentTransform.inverseProjectionMatrix;
14226	}
14227	get cameraPosition() {
14228		return this.currentTransform.cameraPosition;
14229	}
14230	getProjectionData(params) {
14231		const mercatorProjectionData = this._mercatorTransform.getProjectionData(params);
14232		const verticalPerspectiveProjectionData = this._verticalPerspectiveTransform.getProjectionData(params);
14233		return {
14234			mainMatrix: this.isGlobeRendering ? verticalPerspectiveProjectionData.mainMatrix : mercatorProjectionData.mainMatrix,
14235			clippingPlane: verticalPerspectiveProjectionData.clippingPlane,
14236			tileMercatorCoords: verticalPerspectiveProjectionData.tileMercatorCoords,
14237			projectionTransition: params.applyGlobeMatrix ? this._globeness : 0,
14238			fallbackMatrix: mercatorProjectionData.fallbackMatrix,
14239			clipAntimeridian: verticalPerspectiveProjectionData.clipAntimeridian
14240		};
14241	}
14242	/** {@inheritDoc ITransform.isLocationOccluded} */
14243	isLocationOccluded(lngLat, terrain, elevation) {
14244		return this.currentTransform.isLocationOccluded(lngLat, terrain, elevation);
14245	}
14246	transformLightDirection(dir) {
14247		return this.currentTransform.transformLightDirection(dir);
14248	}
14249	getPixelScale() {
14250		return lerp(this._mercatorTransform.getPixelScale(), this._verticalPerspectiveTransform.getPixelScale(), this._globeness);
14251	}
14252	getCircleRadiusCorrection() {
14253		return lerp(this._mercatorTransform.getCircleRadiusCorrection(), this._verticalPerspectiveTransform.getCircleRadiusCorrection(), this._globeness);
14254	}
14255	getPitchedTextCorrection(textAnchorX, textAnchorY, tileID) {
14256		const mercatorCorrection = this._mercatorTransform.getPitchedTextCorrection(textAnchorX, textAnchorY, tileID);
14257		const verticalCorrection = this._verticalPerspectiveTransform.getPitchedTextCorrection(textAnchorX, textAnchorY, tileID);
14258		return lerp(mercatorCorrection, verticalCorrection, this._globeness);
14259	}
14260	projectTileCoordinates(x, y, unwrappedTileID, elevation) {
14261		return this.currentTransform.projectTileCoordinates(x, y, unwrappedTileID, elevation);
14262	}
14263	/**
14264	* Both children write their near/far Z into the shared helper, so the order here is what keeps the two render
14265	* paths at the same depth across the globe-to-mercator transition: vertical perspective computes the globe's Z
14266	* first, and while the globe is rendering mercator is made to reuse that result instead of computing its own.
14267	*/
14268	_calcMatrices() {
14269		if (!this._helper._width || !this._helper._height) return;
14270		this._verticalPerspectiveTransform._calcMatrices();
14271		this._mercatorTransform._calcMatrices(this.autoCalculateNearFarZ && !this.isGlobeRendering);
14272	}
14273	calculateFogMatrix(unwrappedTileID) {
14274		return this.currentTransform.calculateFogMatrix(unwrappedTileID);
14275	}
14276	getVisibleUnwrappedCoordinates(tileID) {
14277		return this.currentTransform.getVisibleUnwrappedCoordinates(tileID);
14278	}
14279	getCameraFrustum() {
14280		return this.currentTransform.getCameraFrustum();
14281	}
14282	getClippingPlane() {
14283		return this.currentTransform.getClippingPlane();
14284	}
14285	getCoveringTilesDetailsProvider() {
14286		return this.currentTransform.getCoveringTilesDetailsProvider();
14287	}
14288	recalculateZoomAndCenter(terrain) {
14289		this.currentTransform.recalculateZoomAndCenter(terrain);
14290	}
14291	maxPitchScaleFactor() {
14292		return this._mercatorTransform.maxPitchScaleFactor();
14293	}
14294	getCameraPoint() {
14295		return this._helper.getCameraPoint();
14296	}
14297	/** The camera of the child that renders the current frame. */
14298	getCameraAltitude() {
14299		return this.currentTransform.getCameraAltitude();
14300	}
14301	/** See {@link getCameraAltitude}. */
14302	getCameraLngLat() {
14303		return this.currentTransform.getCameraLngLat();
14304	}
14305	populateCache(coords) {
14306		this._mercatorTransform.populateCache(coords);
14307		this._verticalPerspectiveTransform.populateCache(coords);
14308	}
14309	getBounds() {
14310		return this.currentTransform.getBounds();
14311	}
14312	calculateCenterFromCameraLngLatAlt(lngLat, alt, bearing, pitch) {
14313		return this._helper.calculateCenterFromCameraLngLatAlt(lngLat, alt, bearing, pitch);
14314	}
14315	/** See {@link getCameraAltitude}. */
14316	calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo) {
14317		return this.currentTransform.calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo);
14318	}
14319	/**
14320	* Note: automatically adjusts zoom to keep planet size consistent
14321	* (same size before and after a {@link setLocationAtPoint} call).
14322	*/
14323	setLocationAtPoint(lnglat, point, elevation) {
14324		this.currentTransform.setLocationAtPoint(lnglat, point, elevation);
14325	}
14326	locationToScreenPoint(lnglat, terrain) {
14327		return this.currentTransform.locationToScreenPoint(lnglat, terrain);
14328	}
14329	screenPointToMercatorCoordinate(p, terrain) {
14330		return this.currentTransform.screenPointToMercatorCoordinate(p, terrain);
14331	}
14332	/** {@inheritDoc ITransform.screenTerrainPointToMercatorCoordinate} */
14333	screenTerrainPointToMercatorCoordinate(p, terrain) {
14334		return this.currentTransform.screenTerrainPointToMercatorCoordinate(p, terrain);
14335	}
14336	screenPointToLocation(p, terrain) {
14337		return this.currentTransform.screenPointToLocation(p, terrain);
14338	}
14339	screenPointToLocationAtElevation(p, elevation) {
14340		return this.currentTransform.screenPointToLocationAtElevation(p, elevation);
14341	}
14342	isPointOnMapSurface(p, terrain) {
14343		return this.currentTransform.isPointOnMapSurface(p, terrain);
14344	}
14345	/**
14346	* Computes normalized direction of a ray from the camera to the given screen pixel.
14347	*
14348	* Always answered by the vertical perspective child: the ray is in unit-sphere space, so it is only meaningful
14349	* to the globe camera controls that ask for it, and the mercator transform does not implement it at all.
14350	*/
14351	getRayDirectionFromPixel(p) {
14352		return this._verticalPerspectiveTransform.getRayDirectionFromPixel(p);
14353	}
14354	getProjectionDataForCustomLayer(applyGlobeMatrix = true) {
14355		const mercatorData = this._mercatorTransform.getProjectionDataForCustomLayer(applyGlobeMatrix);
14356		if (!this.isGlobeRendering) return mercatorData;
14357		const globeData = this._verticalPerspectiveTransform.getProjectionDataForCustomLayer(applyGlobeMatrix);
14358		globeData.fallbackMatrix = mercatorData.mainMatrix;
14359		globeData.projectionTransition = this._globeness;
14360		return globeData;
14361	}
14362	getFastPathSimpleProjectionMatrix(tileID) {
14363		return this.currentTransform.getFastPathSimpleProjectionMatrix(tileID);
14364	}
14365};
14366//#endregion
14367//#region src/geo/projection/vertical_perspective_camera_helper.ts
14368/**
14369* Zoom movement slowing starts when the mouse ray passes further than this above the planet surface,
14370* so a cursor off the globe does not move the map unnaturally. Globe radius is 1, so 0.3 is ~2000 km.
14371*/
14372const RAY_SURFACE_DISTANCE_FOR_SLOWING_START = .3;
14373/** How sharply zoom movement slows as the mouse ray rises above the planet surface. Lower is more gradual. */
14374const SLOWING_MULTIPLIER = .5;
14375/** Longitude difference between zoom location and map center at which the blend from exact to heuristic zooming starts, in degrees. */
14376const INTERPOLATE_TO_HEURISTIC_START_LNG = 45;
14377/** Longitude difference at which the blend to heuristic zooming is complete, in degrees. */
14378const INTERPOLATE_TO_HEURISTIC_END_LNG = 85;
14379/** Exponent applied to the blend factor: below 1, so the blend leans towards heuristic zooming and flattens as it completes. */
14380const INTERPOLATE_TO_HEURISTIC_EXPONENT = .25;
14381/**
14382* Distance of the mouse ray from the globe center at which the blend from exact to heuristic zooming
14383* starts. Globe radius is 1, so 1 is a ray grazing the horizon.
14384*/
14385const INTERPOLATE_TO_HEURISTIC_START_HORIZON = .95;
14386/** Ray distance at which the blend to heuristic zooming is complete. */
14387const INTERPOLATE_TO_HEURISTIC_END_HORIZON = .999;
14388/**
14389* Globe radius relative to the smaller viewport dimension below which zoom movement near the horizon
14390* starts being inhibited, avoiding unnatural movements when the map is zoomed out a lot.
14391*/
14392const SLOWING_RADIUS_START = .9;
14393/** Globe radius relative to the smaller viewport dimension at which that inhibition is at full strength. */
14394const SLOWING_RADIUS_STOP = .5;
14395/** Fraction of the zoom movement that remains once the globe has shrunk to `SLOWING_RADIUS_STOP`. */
14396const SLOWING_RADIUS_SLOW_FACTOR = .25;
14397/**
14398* @internal
14399*/
14400var VerticalPerspectiveCameraHelper = class VerticalPerspectiveCameraHelper {
14401	get useGlobeControls() {
14402		return true;
14403	}
14404	handlePanInertia(pan, transform) {
14405		const panCenter = computeGlobePanCenter(pan, transform);
14406		if (Math.abs(panCenter.lng - transform.center.lng) > 180) panCenter.lng = transform.center.lng + 179.5 * Math.sign(panCenter.lng - transform.center.lng);
14407		return {
14408			easingCenter: panCenter,
14409			easingOffset: new Point(0, 0)
14410		};
14411	}
14412	/**
14413	* Zooms around the pointer.
14414	*
14415	* `setLocationAtPoint` is exact but degenerates when called repeatedly for a
14416	* location whose longitude is far from the center's, or one near the horizon,
14417	* so those cases blend towards a heuristic.
14418	*/
14419	handleMapControlsRollPitchBearingZoom(deltas, tr) {
14420		const zoomPixel = deltas.around;
14421		const zoomLoc = tr.screenPointToLocation(zoomPixel);
14422		if (deltas.bearingDelta) tr.setBearing(tr.bearing + deltas.bearingDelta);
14423		if (deltas.pitchDelta) tr.setPitch(tr.pitch + deltas.pitchDelta);
14424		if (deltas.rollDelta) tr.setRoll(tr.roll + deltas.rollDelta);
14425		const oldZoomPreZoomDelta = tr.zoom;
14426		if (deltas.zoomDelta) tr.setZoom(tr.zoom + deltas.zoomDelta);
14427		const actualZoomDelta = tr.zoom - oldZoomPreZoomDelta;
14428		if (actualZoomDelta === 0) return;
14429		const dLngRaw = differenceOfAnglesDegrees(tr.center.lng, zoomLoc.lng);
14430		const dLng = dLngRaw / (Math.abs(dLngRaw / 180) + 1);
14431		const dLat = differenceOfAnglesDegrees(tr.center.lat, zoomLoc.lat);
14432		const rayDirection = tr.getRayDirectionFromPixel(zoomPixel);
14433		const rayOrigin = tr.cameraPosition;
14434		const distanceToClosestPoint = dot(rayOrigin, rayDirection) * -1;
14435		const closestPoint = createVec3f64();
vendor: 3,919 bytes, lines 14436-14495
14436		add(closestPoint, rayOrigin, [
14437			rayDirection[0] * distanceToClosestPoint,
14438			rayDirection[1] * distanceToClosestPoint,
14439			rayDirection[2] * distanceToClosestPoint
14440		]);
14441		const rayDistanceFromGlobeCenter = length(closestPoint);
14442		const distanceFromSurface = rayDistanceFromGlobeCenter - 1;
14443		const distanceFactor = Math.exp(-Math.max(distanceFromSurface - RAY_SURFACE_DISTANCE_FOR_SLOWING_START, 0) * SLOWING_MULTIPLIER);
14444		const interpolationFactorHorizon = remapSaturate(rayDistanceFromGlobeCenter, INTERPOLATE_TO_HEURISTIC_START_HORIZON, INTERPOLATE_TO_HEURISTIC_END_HORIZON, 0, 1);
14445		const radius = getGlobeRadiusPixels(tr.worldSize, tr.center.lat) / Math.min(tr.width, tr.height);
14446		const radiusFactor = remapSaturate(radius, SLOWING_RADIUS_START, SLOWING_RADIUS_STOP, 1, SLOWING_RADIUS_SLOW_FACTOR);
14447		const slowingFactor = Math.min(distanceFactor, lerp(1, radiusFactor, interpolationFactorHorizon));
14448		const factor = (1 - zoomScale(-actualZoomDelta)) * slowingFactor;
14449		const oldCenterLat = tr.center.lat;
14450		const oldZoom = tr.zoom;
14451		const heuristicCenter = new LngLat(tr.center.lng + dLng * factor, clamp(tr.center.lat + dLat * factor, -MAX_VALID_LATITUDE, MAX_VALID_LATITUDE));
14452		tr.setLocationAtPoint(zoomLoc, zoomPixel);
14453		const exactCenter = tr.center;
14454		const interpolationFactorLongitude = remapSaturate(Math.abs(dLngRaw), INTERPOLATE_TO_HEURISTIC_START_LNG, INTERPOLATE_TO_HEURISTIC_END_LNG, 0, 1);
14455		const heuristicFactor = Math.pow(Math.max(interpolationFactorLongitude, interpolationFactorHorizon), INTERPOLATE_TO_HEURISTIC_EXPONENT);
14456		const lngExactToHeuristic = differenceOfAnglesDegrees(exactCenter.lng, heuristicCenter.lng);
14457		const latExactToHeuristic = differenceOfAnglesDegrees(exactCenter.lat, heuristicCenter.lat);
14458		tr.setCenter(new LngLat(exactCenter.lng + lngExactToHeuristic * heuristicFactor, exactCenter.lat + latExactToHeuristic * heuristicFactor).wrap());
14459		tr.setZoom(oldZoom + getZoomAdjustment(oldCenterLat, tr.center.lat));
14460	}
14461	/**
14462	* Pans the globe by rotating it with a single quaternion that brings the grabbed location back
14463	* under the cursor. This stays consistent near and across the poles, where the previous
14464	* bearing-preserving mapping inverted and stalled (#5296).
14465	* @param deltas - The deltas accumulated for this frame.
14466	* @param tr - The transform to pan.
14467	* @param preZoomAroundLoc - The location that was under the cursor before this frame.
14468	*/
14469	handleMapControlsPan(deltas, tr, preZoomAroundLoc) {
14470		if (!deltas.panDelta) return;
14471		versorSetLocationAtPoint(tr, preZoomAroundLoc, tr.isPointOnMapSurface(deltas.around) ? deltas.around : tr.centerPoint, deltas.panDelta);
14472	}
14473	cameraForBoxAndBearing(options, padding, bounds, bearing, tr) {
14474		const result = cameraForBoxAndBearing(options, padding, bounds, bearing, tr);
14475		const xLeft = padding.left / tr.width * 2 - 1;
14476		const xRight = (tr.width - padding.right) / tr.width * 2 - 1;
14477		const yTop = padding.top / tr.height * -2 + 1;
14478		const yBottom = (tr.height - padding.bottom) / tr.height * -2 + 1;
14479		const flipEastWest = differenceOfAnglesDegrees(bounds.getWest(), bounds.getEast()) < 0;
14480		const lngWest = flipEastWest ? bounds.getEast() : bounds.getWest();
14481		const lngEast = flipEastWest ? bounds.getWest() : bounds.getEast();
14482		const latNorth = Math.max(bounds.getNorth(), bounds.getSouth());
14483		const latSouth = Math.min(bounds.getNorth(), bounds.getSouth());
14484		const lngMid = lngWest + differenceOfAnglesDegrees(lngWest, lngEast) * .5;
14485		const latMid = latNorth + differenceOfAnglesDegrees(latNorth, latSouth) * .5;
14486		const clonedTr = tr.clone();
14487		clonedTr.setCenter(result.center);
14488		clonedTr.setBearing(result.bearing);
14489		clonedTr.setPitch(0);
14490		clonedTr.setRoll(0);
14491		clonedTr.setZoom(result.zoom);
14492		const matrix = clonedTr.modelViewProjectionMatrix;
14493		const testVectors = [
14494			angularCoordinatesToSurfaceVector(bounds.getNorthWest()),
14495			angularCoordinatesToSurfaceVector(bounds.getNorthEast()),
vendor: 70,575 bytes, lines 14496-16261
14496			angularCoordinatesToSurfaceVector(bounds.getSouthWest()),
14497			angularCoordinatesToSurfaceVector(bounds.getSouthEast()),
14498			angularCoordinatesToSurfaceVector(new LngLat(lngEast, latMid)),
14499			angularCoordinatesToSurfaceVector(new LngLat(lngWest, latMid)),
14500			angularCoordinatesToSurfaceVector(new LngLat(lngMid, latNorth)),
14501			angularCoordinatesToSurfaceVector(new LngLat(lngMid, latSouth))
14502		];
14503		const vecToCenter = angularCoordinatesToSurfaceVector(result.center);
14504		let smallestNeededScale = Number.POSITIVE_INFINITY;
14505		for (const vec of testVectors) {
14506			if (xLeft < 0) smallestNeededScale = VerticalPerspectiveCameraHelper.getLesserNonNegativeNonNull(smallestNeededScale, VerticalPerspectiveCameraHelper.solveVectorScale(vec, vecToCenter, matrix, "x", xLeft));
14507			if (xRight > 0) smallestNeededScale = VerticalPerspectiveCameraHelper.getLesserNonNegativeNonNull(smallestNeededScale, VerticalPerspectiveCameraHelper.solveVectorScale(vec, vecToCenter, matrix, "x", xRight));
14508			if (yTop > 0) smallestNeededScale = VerticalPerspectiveCameraHelper.getLesserNonNegativeNonNull(smallestNeededScale, VerticalPerspectiveCameraHelper.solveVectorScale(vec, vecToCenter, matrix, "y", yTop));
14509			if (yBottom < 0) smallestNeededScale = VerticalPerspectiveCameraHelper.getLesserNonNegativeNonNull(smallestNeededScale, VerticalPerspectiveCameraHelper.solveVectorScale(vec, vecToCenter, matrix, "y", yBottom));
14510		}
14511		if (!Number.isFinite(smallestNeededScale) || smallestNeededScale === 0) {
14512			cameraBoundsWarning();
14513			return;
14514		}
14515		result.zoom = Math.min(clonedTr.zoom + scaleZoom(smallestNeededScale), options.maxZoom);
14516		return result;
14517	}
14518	/**
14519	* Handles the zoom and center change during camera jumpTo.
14520	*/
14521	handleJumpToCenterZoom(tr, options) {
14522		const startingLat = tr.center.lat;
14523		const constrainedCenter = tr.applyConstrain(options.center ? LngLat.convert(options.center) : tr.center, tr.zoom).center;
14524		tr.setCenter(constrainedCenter.wrap());
14525		const targetZoom = typeof options.zoom !== "undefined" ? +options.zoom : tr.zoom + getZoomAdjustment(startingLat, constrainedCenter.lat);
14526		if (tr.zoom !== targetZoom) tr.setZoom(targetZoom);
14527	}
14528	handleEaseTo(tr, options) {
14529		const startZoom = tr.zoom;
14530		const startCenter = tr.center;
14531		const startPadding = tr.padding;
14532		const startEulerAngles = {
14533			roll: tr.roll,
14534			pitch: tr.pitch,
14535			bearing: tr.bearing
14536		};
14537		const endEulerAngles = {
14538			roll: options.roll === void 0 ? tr.roll : options.roll,
14539			pitch: options.pitch === void 0 ? tr.pitch : options.pitch,
14540			bearing: options.bearing === void 0 ? tr.bearing : options.bearing
14541		};
14542		const optionsZoom = typeof options.zoom !== "undefined";
14543		const doPadding = !tr.isPaddingEqual(options.padding);
14544		let isZooming = false;
14545		const preConstrainCenter = options.center ? LngLat.convert(options.center) : startCenter;
14546		const constrainedCenter = tr.applyConstrain(preConstrainCenter, startZoom).center;
14547		normalizeCenter(tr, constrainedCenter);
14548		const clonedTr = tr.clone();
14549		clonedTr.setCenter(constrainedCenter);
14550		clonedTr.setZoom(optionsZoom ? +options.zoom : startZoom + getZoomAdjustment(startCenter.lat, preConstrainCenter.lat));
14551		clonedTr.setBearing(options.bearing);
14552		const clampedPoint = new Point(clamp(tr.centerPoint.x + options.offsetAsPoint.x, 0, tr.width), clamp(tr.centerPoint.y + options.offsetAsPoint.y, 0, tr.height));
14553		clonedTr.setLocationAtPoint(constrainedCenter, clampedPoint);
14554		const endCenterWithShift = (options.offset && options.offsetAsPoint.mag()) > 0 ? clonedTr.center : constrainedCenter;
14555		const endZoomWithShift = optionsZoom ? +options.zoom : startZoom + getZoomAdjustment(startCenter.lat, endCenterWithShift.lat);
14556		const normalizedStartZoom = startZoom + getZoomAdjustment(startCenter.lat, 0);
14557		const normalizedEndZoom = endZoomWithShift + getZoomAdjustment(endCenterWithShift.lat, 0);
14558		const deltaLng = differenceOfAnglesDegrees(startCenter.lng, endCenterWithShift.lng);
14559		const deltaLat = differenceOfAnglesDegrees(startCenter.lat, endCenterWithShift.lat);
14560		const finalScale = zoomScale(normalizedEndZoom - normalizedStartZoom);
14561		isZooming = endZoomWithShift !== startZoom;
14562		const easeFunc = (k) => {
14563			if (!rollPitchBearingEqual(startEulerAngles, endEulerAngles)) updateRotation({
14564				startEulerAngles,
14565				endEulerAngles,
14566				tr,
14567				k,
14568				useSlerp: startEulerAngles.roll != endEulerAngles.roll
14569			});
14570			if (doPadding) tr.interpolatePadding(startPadding, options.padding, k);
14571			if (options.around) {
14572				warnOnce("Easing around a point is not supported under globe projection.");
14573				tr.setLocationAtPoint(options.around, options.aroundPoint);
14574			} else {
14575				const factor = k * Math.pow(normalizedEndZoom > normalizedStartZoom ? Math.min(2, finalScale) : Math.max(.5, finalScale), 1 - k);
14576				const newCenter = interpolateLngLatForGlobe(startCenter, deltaLng, deltaLat, factor);
14577				tr.setCenter(newCenter.wrap());
14578			}
14579			if (isZooming) {
14580				const interpolatedZoom = interpolateFactory.number(normalizedStartZoom, normalizedEndZoom, k) + getZoomAdjustment(0, tr.center.lat);
14581				tr.setZoom(interpolatedZoom);
14582			}
14583		};
14584		return {
14585			easeFunc,
14586			isZooming,
14587			elevationCenter: endCenterWithShift
14588		};
14589	}
14590	handleFlyTo(tr, options) {
14591		const optionsZoom = typeof options.zoom !== "undefined";
14592		const startCenter = tr.center;
14593		const startZoom = tr.zoom;
14594		const startPadding = tr.padding;
14595		const doPadding = !tr.isPaddingEqual(options.padding);
14596		const constrainedCenter = tr.applyConstrain(LngLat.convert(options.center || options.locationAtOffset), startZoom).center;
14597		const targetZoom = optionsZoom ? +options.zoom : tr.zoom + getZoomAdjustment(tr.center.lat, constrainedCenter.lat);
14598		const clonedTr = tr.clone();
14599		clonedTr.setCenter(constrainedCenter);
14600		clonedTr.setZoom(targetZoom);
14601		clonedTr.setBearing(options.bearing);
14602		const clampedPoint = new Point(clamp(tr.centerPoint.x + options.offsetAsPoint.x, 0, tr.width), clamp(tr.centerPoint.y + options.offsetAsPoint.y, 0, tr.height));
14603		clonedTr.setLocationAtPoint(constrainedCenter, clampedPoint);
14604		const targetCenter = clonedTr.center;
14605		normalizeCenter(tr, targetCenter);
14606		const pixelPathLength = globeDistanceOfLocationsPixels(tr, startCenter, targetCenter);
14607		const normalizedStartZoom = startZoom + getZoomAdjustment(startCenter.lat, 0);
14608		const normalizedTargetZoom = targetZoom + getZoomAdjustment(targetCenter.lat, 0);
14609		const scaleOfZoom = zoomScale(normalizedTargetZoom - normalizedStartZoom);
14610		const requestedMinZoom = typeof options.minZoom === "number" ? +options.minZoom : tr.minZoom;
14611		const normalizedEffectiveMinZoom = Math.max(requestedMinZoom, tr.minZoom) + getZoomAdjustment(targetCenter.lat, 0);
14612		const minZoomPreConstrain = Math.min(normalizedEffectiveMinZoom, normalizedStartZoom, normalizedTargetZoom) + getZoomAdjustment(0, targetCenter.lat);
14613		const normalizedMinZoom = tr.applyConstrain(targetCenter, minZoomPreConstrain).zoom + getZoomAdjustment(targetCenter.lat, 0);
14614		const scaleOfMinZoom = zoomScale(normalizedMinZoom - normalizedStartZoom);
14615		const deltaLng = differenceOfAnglesDegrees(startCenter.lng, targetCenter.lng);
14616		const deltaLat = differenceOfAnglesDegrees(startCenter.lat, targetCenter.lat);
14617		const easeFunc = (k, scale, centerFactor, _pointAtOffset) => {
14618			const interpolatedCenter = interpolateLngLatForGlobe(startCenter, deltaLng, deltaLat, centerFactor);
14619			if (doPadding) tr.interpolatePadding(startPadding, options.padding, k);
14620			const newCenter = k === 1 ? targetCenter : interpolatedCenter;
14621			tr.setCenter(newCenter.wrap());
14622			const interpolatedZoom = normalizedStartZoom + scaleZoom(scale);
14623			tr.setZoom(k === 1 ? targetZoom : interpolatedZoom + getZoomAdjustment(0, newCenter.lat));
14624		};
14625		return {
14626			easeFunc,
14627			scaleOfZoom,
14628			targetCenter,
14629			scaleOfMinZoom,
14630			pixelPathLength
14631		};
14632	}
14633	/**
14634	* Computes how much to scale the globe in order for a given point on its surface (a location) to project to a given clip space coordinate in either the X or the Y axis.
14635	* @param vector - Position of the queried location on the surface of the unit sphere globe.
14636	* @param toCenter - Position of current transform center on the surface of the unit sphere globe.
14637	* This is needed because zooming the globe not only changes its scale,
14638	* but also moves the camera closer or further away along this vector (pitch is disregarded).
14639	* @param projection - The globe projection matrix.
14640	* @param targetDimension - The dimension in which the scaled vector must match the target value in clip space.
14641	* @param targetValue - The target clip space value in the specified dimension to which the queried vector must project.
14642	* @returns How much to scale the globe.
14643	*/
14644	static solveVectorScale(vector, toCenter, projection, targetDimension, targetValue) {
14645		const k = targetValue;
14646		const columnXorY = targetDimension === "x" ? [
14647			projection[0],
14648			projection[4],
14649			projection[8],
14650			projection[12]
14651		] : [
14652			projection[1],
14653			projection[5],
14654			projection[9],
14655			projection[13]
14656		];
14657		const columnZ = [
14658			projection[3],
14659			projection[7],
14660			projection[11],
14661			projection[15]
14662		];
14663		const vecDotXY = vector[0] * columnXorY[0] + vector[1] * columnXorY[1] + vector[2] * columnXorY[2];
14664		const vecDotZ = vector[0] * columnZ[0] + vector[1] * columnZ[1] + vector[2] * columnZ[2];
14665		const toCenterDotXY = toCenter[0] * columnXorY[0] + toCenter[1] * columnXorY[1] + toCenter[2] * columnXorY[2];
14666		const toCenterDotZ = toCenter[0] * columnZ[0] + toCenter[1] * columnZ[1] + toCenter[2] * columnZ[2];
14667		const t = (toCenterDotXY + columnXorY[3] - k * toCenterDotZ - k * columnZ[3]) / (toCenterDotXY - vecDotXY - k * toCenterDotZ + k * vecDotZ);
14668		if (toCenterDotXY + k * vecDotZ === vecDotXY + k * toCenterDotZ || columnZ[3] * (vecDotXY - toCenterDotXY) + columnXorY[3] * (toCenterDotZ - vecDotZ) + vecDotXY * toCenterDotZ === toCenterDotXY * vecDotZ) return null;
14669		return t;
14670	}
14671	/**
14672	* Returns `newValue` if it is:
14673	*
14674	* - not null AND
14675	* - not negative AND
14676	* - smaller than `newValue`,
14677	*
14678	* ...otherwise returns `oldValue`.
14679	*/
14680	static getLesserNonNegativeNonNull(oldValue, newValue) {
14681		if (newValue !== null && newValue >= 0 && newValue < oldValue) return newValue;
14682		else return oldValue;
14683	}
14684};
14685//#endregion
14686//#region src/geo/projection/globe_camera_helper.ts
14687/**
14688* @internal
14689*/
14690var GlobeCameraHelper = class {
14691	constructor(globe) {
14692		this._globe = globe;
14693		this._mercatorCameraHelper = new MercatorCameraHelper();
14694		this._verticalPerspectiveCameraHelper = new VerticalPerspectiveCameraHelper();
14695	}
14696	get useGlobeControls() {
14697		return this._globe.useGlobeRendering;
14698	}
14699	get currentHelper() {
14700		return this.useGlobeControls ? this._verticalPerspectiveCameraHelper : this._mercatorCameraHelper;
14701	}
14702	handlePanInertia(pan, transform) {
14703		return this.currentHelper.handlePanInertia(pan, transform);
14704	}
14705	handleMapControlsRollPitchBearingZoom(deltas, tr) {
14706		this.currentHelper.handleMapControlsRollPitchBearingZoom(deltas, tr);
14707	}
14708	handleMapControlsPan(deltas, tr, preZoomAroundLoc) {
14709		this.currentHelper.handleMapControlsPan(deltas, tr, preZoomAroundLoc);
14710	}
14711	cameraForBoxAndBearing(options, padding, bounds, bearing, tr) {
14712		return this.currentHelper.cameraForBoxAndBearing(options, padding, bounds, bearing, tr);
14713	}
14714	/**
14715	* Handles the zoom and center change during camera jumpTo.
14716	*/
14717	handleJumpToCenterZoom(tr, options) {
14718		this.currentHelper.handleJumpToCenterZoom(tr, options);
14719	}
14720	handleEaseTo(tr, options) {
14721		return this.currentHelper.handleEaseTo(tr, options);
14722	}
14723	handleFlyTo(tr, options) {
14724		return this.currentHelper.handleFlyTo(tr, options);
14725	}
14726};
14727//#endregion
14728//#region src/geo/projection/projection_factory.ts
14729function createProjectionFromName(name, transformConstrain, globalState) {
14730	const transformOptions = { constrainOverride: transformConstrain };
14731	if (Array.isArray(name)) {
14732		const globeProjection = new GlobeProjection({ type: name }, globalState);
14733		return {
14734			projection: globeProjection,
14735			transform: new GlobeTransform(transformOptions),
14736			cameraHelper: new GlobeCameraHelper(globeProjection)
14737		};
14738	}
14739	switch (name) {
14740		case "mercator": return {
14741			projection: new MercatorProjection(),
14742			transform: new MercatorTransform(transformOptions),
14743			cameraHelper: new MercatorCameraHelper()
14744		};
14745		case "globe": {
14746			const globeProjection = new GlobeProjection({ type: [
14747				"interpolate",
14748				["linear"],
14749				["zoom"],
14750				11,
14751				"vertical-perspective",
14752				12,
14753				"mercator"
14754			] }, {});
14755			return {
14756				projection: globeProjection,
14757				transform: new GlobeTransform(transformOptions),
14758				cameraHelper: new GlobeCameraHelper(globeProjection)
14759			};
14760		}
14761		case "vertical-perspective": return {
14762			projection: new VerticalPerspectiveProjection(),
14763			transform: new VerticalPerspectiveTransform(transformOptions),
14764			cameraHelper: new VerticalPerspectiveCameraHelper()
14765		};
14766		default:
14767			warnOnce(`Unknown projection name: ${name}. Falling back to mercator projection.`);
14768			return {
14769				projection: new MercatorProjection(),
14770				transform: new MercatorTransform(transformOptions),
14771				cameraHelper: new MercatorCameraHelper()
14772			};
14773	}
14774}
14775//#endregion
14776//#region src/style/style.ts
14777const empty = emptyStyle();
14778/**
14779* The Style base class
14780*/
14781var Style = class extends Evented {
14782	constructor(map, options = {}) {
14783		super();
14784		this._rtlPluginLoaded = () => {
14785			for (const id in this.tileManagers) {
14786				const sourceType = this.tileManagers[id].getSource().type;
14787				if (sourceType === "vector" || sourceType === "geojson") this.tileManagers[id].reload();
14788			}
14789		};
14790		this.map = map;
14791		this.dispatcher = new Dispatcher(getGlobalWorkerPool(), map._getMapId()).setEventedParent(this);
14792		this.dispatcher.registerMessageHandler("GG", (mapId, params) => {
14793			return this.getGlyphs(mapId, params);
14794		});
14795		this.dispatcher.registerMessageHandler("GI", (mapId, params) => {
14796			return this.getImages(mapId, params);
14797		});
14798		this.dispatcher.registerMessageHandler("GDA", (mapId, params) => {
14799			return this.getDashes(mapId, params);
14800		});
14801		this.imageManager = new ImageManager();
14802		this.imageManager.setEventedParent(this);
14803		this.imageManager.setMissingImageResolver(map._missingStyleImageResolver);
14804		this.patternAtlas = new PatternAtlas(this.imageManager);
14805		const glyphLang = map._container?.lang || typeof document !== "undefined" && document.documentElement?.lang || void 0;
14806		this.glyphManager = new GlyphManager(map._requestManager, options.localIdeographFontFamily, glyphLang);
14807		this.lineAtlas = new LineAtlas(256, 512);
14808		this.crossTileSymbolIndex = new CrossTileSymbolIndex();
14809		this._setInitialValues();
14810		this._resetUpdates();
14811		this.dispatcher.broadcast("SR", getReferrer());
14812		rtlMainThreadPluginFactory().on(RTLPluginLoadedEventName, this._rtlPluginLoaded);
14813		this.on("data", (event) => {
14814			if (event.dataType !== "source" || event.sourceDataType !== "metadata") return;
14815			const tileManager = this.tileManagers[event.sourceId];
14816			if (!tileManager) return;
14817			const source = tileManager.getSource();
14818			if (!source?.vectorLayerIds) return;
14819			for (const layerId in this._layers) {
14820				const layer = this._layers[layerId];
14821				if (layer.source === source.id) this._validateLayer(layer);
14822			}
14823		});
14824	}
14825	_setInitialValues() {
14826		this._layers = {};
14827		this._order = [];
14828		this.tileManagers = {};
14829		this.zoomHistory = new ZoomHistory();
14830		this._imagesListDirty = false;
14831		this._globalState = {};
14832		this._serializedLayers = {};
14833		this.stylesheet = null;
14834		this.light = null;
14835		this.sky = null;
14836		if (this.projection) {
14837			this.projection.destroy();
14838			delete this.projection;
14839		}
14840		this._loaded = false;
14841		this._changed = false;
14842		this._updatedLayers = {};
14843		this._updatedSources = {};
14844		this._changedImages = {};
14845		this._glyphsDidChange = false;
14846		this._updatedPaintProps = {};
14847		this._layerOrderChanged = false;
14848		this._symbolPlacementTriggered = false;
14849		this._placedProjectionTransition = void 0;
14850		this.crossTileSymbolIndex = new ((this.crossTileSymbolIndex?.constructor) || Object)();
14851		this.pauseablePlacement = void 0;
14852		this.placement = void 0;
14853		this.z = 0;
14854	}
14855	setGlobalStateProperty(name, value) {
14856		this._checkLoaded();
14857		const newValue = value === null ? this.stylesheet.state?.[name]?.default ?? null : value;
14858		this._setGlobalStateValues({ [name]: newValue });
14859		return this;
14860	}
14861	getGlobalState() {
14862		return this._globalState;
14863	}
14864	setGlobalState(newStylesheetState) {
14865		this._checkLoaded();
14866		const values = {};
14867		for (const propertyName in newStylesheetState) values[propertyName] = newStylesheetState[propertyName].default;
14868		this._setGlobalStateValues(values);
14869	}
14870	/** Sets the keys in `values` that differ, keeping a copy of the state from before so readers transition from it. */
14871	_setGlobalStateValues(values) {
14872		const changedGlobalStateRefs = [];
14873		for (const ref in values) if (!deepEqual(values[ref], this._globalState[ref])) changedGlobalStateRefs.push(ref);
14874		if (changedGlobalStateRefs.length === 0) return;
14875		const priorGlobalState = { ...this._globalState };
14876		for (const ref of changedGlobalStateRefs) this._globalState[ref] = values[ref];
14877		this._applyGlobalStateChanges(changedGlobalStateRefs, priorGlobalState);
14878	}
14879	/**
14880	* @internal
14881	* Find all sources that are affected by the global state changes and reload them.
14882	* Find all paint, light and sky properties that are affected and transition them from `priorGlobalState`, the state before the change.
14883	* For example, if a layer filter uses global-state expression, this function will find the source id of that layer.
14884	*/
14885	_applyGlobalStateChanges(globalStateRefs, priorGlobalState) {
14886		const sourceIdsToReload = /* @__PURE__ */ new Set();
14887		const globalStateChange = {};
14888		for (const ref of globalStateRefs) globalStateChange[ref] = this._globalState[ref];
14889		for (const layerId in this._layers) {
14890			const layer = this._layers[layerId];
14891			layer.retainPriorGlobalState(globalStateRefs, priorGlobalState);
14892			const layoutAffectingGlobalStateRefs = layer.getLayoutAffectingGlobalStateRefs();
14893			const paintAffectingGlobalStateRefs = layer.getPaintAffectingGlobalStateRefs();
14894			const visibilityAffectingGlobalStateRefs = layer.getVisibilityAffectingGlobalStateRefs();
14895			for (const ref of globalStateRefs) {
14896				if (layoutAffectingGlobalStateRefs.has(ref)) sourceIdsToReload.add(layer.source);
14897				if (paintAffectingGlobalStateRefs.has(ref)) for (const { name, value } of paintAffectingGlobalStateRefs.get(ref)) this._updatePaintProperty(layer, name, value, { validate: false });
14898				if (visibilityAffectingGlobalStateRefs?.has(ref)) {
14899					layer.recalculateVisibility();
14900					this._updateLayer(layer);
14901				}
14902			}
14903		}
14904		const parameters = {
14905			now: now(),
14906			transition: this.getTransition()
14907		};
14908		this.light?.applyGlobalStateChange(globalStateRefs, priorGlobalState, parameters);
14909		this.sky?.applyGlobalStateChange(globalStateRefs, priorGlobalState, parameters);
14910		this.dispatcher.broadcast("UGS", globalStateChange);
14911		for (const id in this.tileManagers) if (sourceIdsToReload.has(id)) {
14912			this._reloadSource(id);
14913			this._changed = true;
14914		}
14915	}
14916	async loadURL(url, options = {}, previousStyle) {
14917		this.fire(new MapStyleDataEvent("dataloading"));
14918		options.validate = typeof options.validate === "boolean" ? options.validate : true;
14919		this._loadStyleRequest = new AbortController();
14920		const abortController = this._loadStyleRequest;
14921		try {
14922			const request = await this.map._requestManager.transformRequest(url, "Style");
14923			throwIfAborted(abortController.signal);
14924			const response = await getJSON(request, abortController);
14925			if (this._loadStyleRequest === abortController) this._loadStyleRequest = null;
14926			this._load(response.data, options, previousStyle);
14927		} catch (error) {
14928			if (this._loadStyleRequest === abortController) this._loadStyleRequest = null;
14929			if (error && !abortController.signal.aborted) this.fire(new ErrorEvent(ensureError(error)));
14930		}
14931	}
14932	loadJSON(json, options = {}, previousStyle) {
14933		this.fire(new MapStyleDataEvent("dataloading"));
14934		this._frameRequest = new AbortController();
14935		browser.frameAsync(this._frameRequest, this.map._ownerWindow).then(() => {
14936			this._frameRequest = null;
14937			options.validate = options.validate !== false;
14938			this._load(json, options, previousStyle);
14939		}).catch(() => {});
14940	}
14941	loadEmpty() {
14942		this.fire(new MapStyleDataEvent("dataloading"));
14943		this._load(empty, { validate: false });
14944	}
14945	_load(json, options, previousStyle) {
14946		let nextState = options.transformStyle ? options.transformStyle(previousStyle, json) : json;
14947		if (options.validate && validateStyleAndEmit(this, nextState)) return;
14948		nextState = { ...nextState };
14949		this._loaded = true;
14950		this.stylesheet = nextState;
14951		for (const id in nextState.sources) this.addSource(id, nextState.sources[id], { validate: false });
14952		if (nextState.sprite) this._loadSprite(nextState.sprite);
14953		else this.imageManager.setLoaded(true);
14954		this.glyphManager.setURL(nextState.glyphs);
14955		this.glyphManager.setFontFaces(nextState["font-faces"]);
14956		this._createLayers();
14957		this.light = new Light(this.stylesheet.light ?? {}, this._globalState);
14958		this._setProjectionInternal(this.stylesheet.projection?.type || "mercator");
14959		this.sky = new Sky(this.stylesheet.sky, this._globalState);
14960		this.sky.setEventedParent(this);
14961		this.map.setTerrain(this.stylesheet.terrain ?? null, { validate: false });
14962		this.fire(new MapStyleDataEvent("data"));
14963		this.fire(new MapStyleLoadEvent());
14964	}
14965	_createLayers() {
14966		const dereferencedLayers = derefLayers(this.stylesheet.layers);
14967		this.setGlobalState(this.stylesheet.state ?? null);
14968		this.dispatcher.broadcast("SL", dereferencedLayers);
14969		this._order = dereferencedLayers.map((layer) => layer.id);
14970		this._layers = {};
14971		this._serializedLayers = null;
14972		for (const layer of dereferencedLayers) {
14973			const styledLayer = createStyleLayer(layer, this._globalState);
14974			styledLayer.setEventedParent(this, { layer: { id: layer.id } });
14975			this._layers[layer.id] = styledLayer;
14976			if (isRasterStyleLayer(styledLayer) && this.tileManagers[styledLayer.source]) {
14977				const rasterFadeDuration = layer.paint?.["raster-fade-duration"] ?? styledLayer.paint.get("raster-fade-duration");
14978				this.tileManagers[styledLayer.source].setRasterFadeDuration(rasterFadeDuration);
14979			}
14980		}
14981	}
14982	async _loadSprite(sprite, isUpdate = false, completion = void 0) {
14983		this.imageManager.setLoaded(false);
14984		const abortController = new AbortController();
14985		this._spriteRequest = abortController;
14986		let err;
14987		try {
14988			const images = await loadSprite(sprite, this.map._requestManager, this.map.getPixelRatio(), abortController);
14989			if (!images) return;
14990			for (const spriteId in images) {
14991				const { loaded, removed } = this.imageManager.setSpriteImages(spriteId, images[spriteId]);
14992				this._markImagesChanged(removed);
14993				if (isUpdate) this._markImagesChanged(loaded);
14994			}
14995		} catch (error) {
14996			err = error;
14997			if (!abortController.signal.aborted) this.fire(new ErrorEvent(err));
14998		} finally {
14999			this._spriteRequest = null;
15000			this.imageManager.setLoaded(true);
15001			if (isUpdate) this._changed = true;
15002			this.dispatcher.broadcast("SI", this.imageManager.listImages());
15003			this.fire(new MapStyleDataEvent("data"));
15004			completion?.(err);
15005		}
15006	}
15007	_unloadSprite() {
15008		this._markImagesChanged(this.imageManager.removeAllSpriteImages());
15009		this._imagesListDirty = true;
15010		this._changed = true;
15011		this.fire(new MapStyleDataEvent("data"));
15012	}
15013	_validateLayer(layer) {
15014		const tileManager = this.tileManagers[layer.source];
15015		if (!tileManager) return;
15016		const sourceLayer = layer.sourceLayer;
15017		if (!sourceLayer) return;
15018		const source = tileManager.getSource();
15019		if (source.type === "geojson" || source.vectorLayerIds && !source.vectorLayerIds.includes(sourceLayer)) this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Source layer "${sourceLayer}" does not exist on source "${source.id}" as specified by style layer "${layer.id}".`)));
15020	}
15021	loaded() {
15022		if (!this._loaded) return false;
15023		if (Object.keys(this._updatedSources).length) return false;
15024		for (const id in this.tileManagers) if (!this.tileManagers[id].loaded()) return false;
15025		return this.imageManager.isLoaded();
15026	}
15027	/**
15028	* @hidden
15029	* take an array of string IDs, and based on this._layers, generate an array of LayerSpecification
15030	* @param ids - an array of string IDs, for which serialized layers will be generated. If omitted, all serialized layers will be returned
15031	* @param returnClone - if true, return a clone of the layer object
15032	* @returns generated result
15033	*/
15034	_serializeByIds(ids, returnClone = false) {
15035		const serializedLayersDictionary = this._serializedAllLayers();
15036		if (!ids || ids.length === 0) return returnClone ? Object.values(clone$2(serializedLayersDictionary)) : Object.values(serializedLayersDictionary);
15037		const serializedLayers = [];
15038		for (const id of ids) if (serializedLayersDictionary[id]) {
15039			const toPush = returnClone ? clone$2(serializedLayersDictionary[id]) : serializedLayersDictionary[id];
15040			serializedLayers.push(toPush);
15041		}
15042		return serializedLayers;
15043	}
15044	/**
15045	* @hidden
15046	* Lazy initialization of this._serializedLayers dictionary and return it
15047	* @returns this._serializedLayers dictionary
15048	*/
15049	_serializedAllLayers() {
15050		let serializedLayers = this._serializedLayers;
15051		if (serializedLayers) return serializedLayers;
15052		serializedLayers = this._serializedLayers = {};
15053		const allLayerIds = Object.keys(this._layers);
15054		for (const layerId of allLayerIds) {
15055			const layer = this._layers[layerId];
15056			if (layer.type !== "custom") serializedLayers[layerId] = layer.serialize();
15057		}
15058		return serializedLayers;
15059	}
15060	hasTransitions() {
15061		if (this.light?.hasTransition()) return true;
15062		if (this.sky?.hasTransition()) return true;
15063		if (this.projection?.hasTransition()) return true;
15064		for (const id in this.tileManagers) if (this.tileManagers[id].hasTransition()) return true;
15065		for (const id in this._layers) if (this._layers[id].hasTransition()) return true;
15066		return false;
15067	}
15068	_checkLoaded() {
15069		if (!this._loaded) throw new Error("Style is not done loading.");
15070	}
15071	/**
15072	* @internal
15073	* Apply queued style updates in a batch and recalculate zoom-dependent paint properties.
15074	*/
15075	update(parameters) {
15076		if (!this._loaded) return;
15077		const changed = this._changed;
15078		if (changed) {
15079			if (this._imagesListDirty) {
15080				this.dispatcher.broadcast("SI", this.imageManager.listImages());
15081				this._imagesListDirty = false;
15082			}
15083			const updatedIds = Object.keys(this._updatedLayers);
15084			const removedIds = Object.keys(this._removedLayers);
15085			if (updatedIds.length || removedIds.length) this._updateWorkerLayers(updatedIds, removedIds);
15086			for (const id in this._updatedSources) {
15087				const action = this._updatedSources[id];
15088				if (action === "reload") this._reloadSource(id);
15089				else if (action === "clear") this._clearSource(id);
15090				else throw new Error(`Invalid action ${action}`);
15091			}
15092			this._updateTilesForChangedImages();
15093			this._updateTilesForChangedGlyphs();
15094			for (const id in this._updatedPaintProps) this._layers[id].updateTransitions(parameters);
15095			this._resetUpdates();
15096		}
15097		const managersUsedBefore = {};
15098		for (const id in this.tileManagers) {
15099			const tileManager = this.tileManagers[id];
15100			managersUsedBefore[id] = tileManager.used;
15101			tileManager.used = false;
15102		}
15103		const availableImages = this.imageManager.listImages();
15104		for (const layerId of this._order) {
15105			const layer = this._layers[layerId];
15106			layer.recalculate(parameters, availableImages);
15107			if (!layer.isHidden(parameters.zoom) && layer.source) this.tileManagers[layer.source].used = true;
15108		}
15109		for (const id in managersUsedBefore) {
15110			const tileManager = this.tileManagers[id];
15111			if (!!managersUsedBefore[id] !== !!tileManager.used) tileManager.fire(new MapSourceDataEvent("data", {
15112				sourceDataType: "visibility",
15113				sourceId: id
15114			}));
15115		}
15116		this.light.recalculate(parameters);
15117		this.sky.recalculate(parameters);
15118		this.projection.recalculate(parameters);
15119		this.z = parameters.zoom;
15120		if (changed) this.fire(new MapStyleDataEvent("data"));
15121	}
15122	_updateTilesForChangedImages() {
15123		const changedImages = Object.keys(this._changedImages);
15124		if (changedImages.length) {
15125			for (const name in this.tileManagers) this.tileManagers[name].reloadTilesForDependencies(["icons", "patterns"], changedImages);
15126			this._changedImages = {};
15127		}
15128	}
15129	_updateTilesForChangedGlyphs() {
15130		if (this._glyphsDidChange) {
15131			for (const name in this.tileManagers) this.tileManagers[name].reloadTilesForDependencies(["glyphs"], [""]);
15132			this._glyphsDidChange = false;
15133		}
15134	}
15135	_updateWorkerLayers(updatedIds, removedIds) {
15136		this.dispatcher.broadcast("UL", {
15137			layers: this._serializeByIds(updatedIds, false),
15138			removedIds
15139		});
15140	}
15141	_resetUpdates() {
15142		this._changed = false;
15143		this._updatedLayers = {};
15144		this._removedLayers = {};
15145		this._updatedSources = {};
15146		this._updatedPaintProps = {};
15147		this._changedImages = {};
15148		this._glyphsDidChange = false;
15149	}
15150	/**
15151	* Update this style's state to match the given style JSON, performing only
15152	* the necessary mutations.
15153	*
15154	* May throw an Error ('Unimplemented: METHOD') if the maplibre-gl-style-spec
15155	* diff algorithm produces an operation that is not supported.
15156	*
15157	* @returns true if any changes were made; false otherwise
15158	*/
15159	setState(nextState, options = {}) {
15160		this._checkLoaded();
15161		const serializedStyle = this.serialize();
15162		nextState = options.transformStyle ? options.transformStyle(serializedStyle, nextState) : nextState;
15163		if ((options.validate ?? true) && validateStyleAndEmit(this, nextState)) return false;
15164		nextState = clone$2(nextState);
15165		nextState.layers = derefLayers(nextState.layers);
15166		const changes = diff(serializedStyle, nextState);
15167		const operations = this._getOperationsToPerform(changes);
15168		if (operations.unimplemented.length > 0) throw new Error(`Unimplemented: ${operations.unimplemented.join(", ")}.`);
15169		if (operations.operations.length === 0) return false;
15170		for (const styleChangeOperation of operations.operations) styleChangeOperation();
15171		this.stylesheet = nextState;
15172		this._serializedLayers = null;
15173		this.fire(new MapStyleLoadEvent({ style: this }));
15174		return true;
15175	}
15176	/**
15177	* Translates a style diff into the calls that apply it to this style.
15178	* @param diff - the operations produced by the style-spec diff algorithm
15179	* @returns the operations to run, and the names of the commands that are not supported
15180	*/
15181	_getOperationsToPerform(diff) {
15182		const operations = [];
15183		const unimplemented = [];
15184		for (const op of diff) switch (op.command) {
15185			case "setCenter":
15186			case "setZoom":
15187			case "setBearing":
15188			case "setPitch":
15189			case "setRoll": continue;
15190			case "addLayer": {
15191				const [layer, before] = op.args;
15192				operations.push(() => this.addLayer(layer, before));
15193				break;
15194			}
15195			case "removeLayer": {
15196				const [layerId] = op.args;
15197				operations.push(() => this.removeLayer(layerId));
15198				break;
15199			}
15200			case "setPaintProperty": {
15201				const [layerId, name, value] = op.args;
15202				operations.push(() => this.setPaintProperty(layerId, name, value));
15203				break;
15204			}
15205			case "setLayoutProperty": {
15206				const [layerId, name, value] = op.args;
15207				operations.push(() => this.setLayoutProperty(layerId, name, value));
15208				break;
15209			}
15210			case "setFilter": {
15211				const [layerId, filter] = op.args;
15212				operations.push(() => this.setFilter(layerId, filter));
15213				break;
15214			}
15215			case "addSource": {
15216				const [id, source] = op.args;
15217				operations.push(() => this.addSource(id, source));
15218				break;
15219			}
15220			case "removeSource": {
15221				const [id] = op.args;
15222				operations.push(() => this.removeSource(id));
15223				break;
15224			}
15225			case "setLayerZoomRange": {
15226				const [layerId, minzoom, maxzoom] = op.args;
15227				operations.push(() => this.setLayerZoomRange(layerId, minzoom, maxzoom));
15228				break;
15229			}
15230			case "setLight": {
15231				const [light] = op.args;
15232				operations.push(() => this.setLight(light));
15233				break;
15234			}
15235			case "setGeoJSONSourceData": {
15236				const [id, data] = op.args;
15237				operations.push(() => this.setGeoJSONSourceData(id, data));
15238				break;
15239			}
15240			case "setGlyphs": {
15241				const [glyphsUrl] = op.args;
15242				operations.push(() => this.setGlyphs(glyphsUrl));
15243				break;
15244			}
15245			case "setFontFaces": {
15246				const [fontFaces] = op.args;
15247				operations.push(() => this.setFontFaces(fontFaces));
15248				break;
15249			}
15250			case "setSprite": {
15251				const [sprite] = op.args;
15252				operations.push(() => this.setSprite(sprite));
15253				break;
15254			}
15255			case "setTerrain": {
15256				const [terrain] = op.args;
15257				operations.push(() => this.map.setTerrain(terrain));
15258				break;
15259			}
15260			case "setSky": {
15261				const [sky] = op.args;
15262				operations.push(() => this.setSky(sky));
15263				break;
15264			}
15265			case "setProjection": {
15266				const [projection] = op.args;
15267				operations.push(() => this.setProjection(projection));
15268				break;
15269			}
15270			case "setGlobalState": {
15271				const [state] = op.args;
15272				operations.push(() => this.setGlobalState(state));
15273				break;
15274			}
15275			case "setTransition":
15276				operations.push(() => {});
15277				break;
15278			default: unimplemented.push(op.command);
15279		}
15280		return {
15281			operations,
15282			unimplemented
15283		};
15284	}
15285	addImage(id, image) {
15286		if (this.getImage(id)) {
15287			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`An image named "${id}" already exists.`)));
15288			return;
15289		}
15290		this.imageManager.addImage(id, image);
15291		this._afterImageUpdated(id);
15292	}
15293	updateImage(id, image) {
15294		this.imageManager.updateImage(id, image);
15295	}
15296	getImage(id) {
15297		return this.imageManager.getImage(id);
15298	}
15299	setMissingImageResolver(resolver) {
15300		this.imageManager.setMissingImageResolver(resolver);
15301	}
15302	removeImage(id) {
15303		if (!this.getImage(id)) {
15304			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`An image named "${id}" does not exist.`)));
15305			return;
15306		}
15307		this.imageManager.removeImage(id);
15308		this._afterImageUpdated(id);
15309	}
15310	/**
15311	* Queues the tiles that depend on these images to be reloaded on the next update, which is
15312	* needed whenever an image appears, disappears or changes size.
15313	*/
15314	_markImagesChanged(ids) {
15315		for (const id of ids) this._changedImages[id] = true;
15316	}
15317	_afterImageUpdated(id) {
15318		this._changedImages[id] = true;
15319		this._imagesListDirty = true;
15320		this._changed = true;
15321		this.fire(new MapStyleDataEvent("data"));
15322	}
15323	listImages() {
15324		this._checkLoaded();
15325		return this.imageManager.listImages();
15326	}
15327	addSource(id, source, options = {}) {
15328		this._checkLoaded();
15329		if (this.tileManagers[id] !== void 0) throw new Error(`Source "${id}" already exists.`);
15330		if (!source.type) throw new Error(`The type property must be defined, but only the following properties were given: ${Object.keys(source).join(", ")}.`);
15331		if (SPEC_SOURCE_TYPES.has(source.type) && this._validate(validateStyle.source, `sources.${id}`, source, null, options)) return;
15332		if (this.map?._collectResourceTiming) source.collectResourceTiming = true;
15333		const tileManager = this.tileManagers[id] = new TileManager(id, source, this.dispatcher);
15334		tileManager.style = this;
15335		tileManager.setEventedParent(this, () => ({
15336			isSourceLoaded: tileManager.loaded(),
15337			source: tileManager.serialize(),
15338			sourceId: id
15339		}));
15340		tileManager.onAdd(this.map);
15341		this._changed = true;
15342	}
15343	/**
15344	* Remove a source from this stylesheet, given its id.
15345	* @param id - id of the source to remove
15346	* @throws if no source is found with the given ID
15347	*/
15348	removeSource(id) {
15349		this._checkLoaded();
15350		if (this.tileManagers[id] === void 0) throw new Error(`There is no source with this ID=${id}`);
15351		for (const layerId in this._layers) if (this._layers[layerId].source === id) return this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Source "${id}" cannot be removed while layer "${layerId}" is using it.`)));
15352		const tileManager = this.tileManagers[id];
15353		delete this.tileManagers[id];
15354		delete this._updatedSources[id];
15355		tileManager.fire(new MapSourceDataEvent("data", {
15356			sourceDataType: "metadata",
15357			sourceId: id
15358		}));
15359		tileManager.setEventedParent(null);
15360		tileManager.onRemove(this.map);
15361		this._changed = true;
15362	}
15363	/**
15364	* Set the data of a GeoJSON source, given its id.
15365	* @param id - id of the source
15366	* @param data - GeoJSON source
15367	*/
15368	setGeoJSONSourceData(id, data) {
15369		this._checkLoaded();
15370		if (this.tileManagers[id] === void 0) throw new Error(`There is no source with this ID=${id}`);
15371		const geojsonSource = this.tileManagers[id].getSource();
15372		if (geojsonSource.type !== "geojson") throw new Error(`geojsonSource.type is ${geojsonSource.type}, which is !== 'geojson`);
15373		geojsonSource.setData(data);
15374		this._changed = true;
15375	}
15376	/**
15377	* Get a source by ID.
15378	* @param id - ID of the desired source
15379	* @returns source
15380	*/
15381	getSource(id) {
15382		return this.tileManagers[id]?.getSource();
15383	}
15384	/**
15385	* Add a layer to the map style. The layer will be inserted before the layer with
15386	* ID `before`, or appended if `before` is omitted.
15387	* @param layerObject - The style layer to add.
15388	* @param before - ID of an existing layer to insert before
15389	* @param options - Style setter options.
15390	*/
15391	addLayer(layerObject, before, options = {}) {
15392		this._checkLoaded();
15393		const id = layerObject.id;
15394		if (this.getLayer(id)) {
15395			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Layer "${id}" already exists on this map.`)));
15396			return;
15397		}
15398		let layer;
15399		if (layerObject.type === "custom") {
15400			if (emitValidationErrors(this, validateCustomStyleLayer(layerObject))) return;
15401			layer = createStyleLayer(layerObject, this._globalState);
15402		} else {
15403			if ("source" in layerObject && typeof layerObject.source === "object") {
15404				this.addSource(id, layerObject.source);
15405				layerObject = clone$2(layerObject);
15406				layerObject = extend(layerObject, { source: id });
15407			}
15408			if (this._validate(validateStyle.layer, `layers.${id}`, layerObject, { arrayIndex: -1 }, options)) return;
15409			layer = createStyleLayer(layerObject, this._globalState);
15410			this._validateLayer(layer);
15411			layer.setEventedParent(this, { layer: { id } });
15412		}
15413		const index = before ? this._order.indexOf(before) : this._order.length;
15414		if (before && index === -1) {
15415			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot add layer "${id}" before non-existing layer "${before}".`)));
15416			return;
15417		}
15418		this._order.splice(index, 0, id);
15419		this._layerOrderChanged = true;
15420		this._layers[id] = layer;
15421		if (this._removedLayers[id] && layer.source && layer.type !== "custom") {
15422			const removed = this._removedLayers[id];
15423			delete this._removedLayers[id];
15424			if (removed.type !== layer.type) this._updatedSources[layer.source] = "clear";
15425			else {
15426				this._updatedSources[layer.source] = "reload";
15427				this.tileManagers[layer.source].pause();
15428			}
15429		}
15430		this._updateLayer(layer);
15431		if (layer.onAdd) layer.onAdd(this.map);
15432	}
15433	/**
15434	* Moves a layer to a different z-position. The layer will be inserted before the layer with
15435	* ID `before`, or appended if `before` is omitted.
15436	* @param id - ID of the layer to move
15437	* @param before - ID of an existing layer to insert before
15438	*/
15439	moveLayer(id, before) {
15440		this._checkLoaded();
15441		this._changed = true;
15442		if (!this._layers[id]) {
15443			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`The layer '${id}' does not exist in the map's style and cannot be moved.`)));
15444			return;
15445		}
15446		if (id === before) return;
15447		if (before && !this._order.includes(before)) {
15448			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot move layer "${id}" before non-existing layer "${before}".`)));
15449			return;
15450		}
15451		const index = this._order.indexOf(id);
15452		this._order.splice(index, 1);
15453		const newIndex = before ? this._order.indexOf(before) : this._order.length;
15454		this._order.splice(newIndex, 0, id);
15455		this._layerOrderChanged = true;
15456	}
15457	/**
15458	* Remove the layer with the given id from the style.
15459	* A {@link ErrorEvent} event will be fired if no such layer exists.
15460	*
15461	* @param id - id of the layer to remove
15462	*/
15463	removeLayer(id) {
15464		this._checkLoaded();
15465		const layer = this._layers[id];
15466		if (!layer) {
15467			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot remove non-existing layer "${id}".`)));
15468			return;
15469		}
15470		layer.setEventedParent(null);
15471		const index = this._order.indexOf(id);
15472		this._order.splice(index, 1);
15473		this._layerOrderChanged = true;
15474		this._changed = true;
15475		this._removedLayers[id] = layer;
15476		delete this._layers[id];
15477		if (this._serializedLayers) delete this._serializedLayers[id];
15478		delete this._updatedLayers[id];
15479		delete this._updatedPaintProps[id];
15480		if (layer.type === "symbol" && layer.source) this.tileManagers[layer.source]?.resetMaxContentElevation();
15481		if (layer.onRemove) layer.onRemove(this.map);
15482	}
15483	/**
15484	* Return the style layer object with the given `id`.
15485	*
15486	* @param id - id of the desired layer
15487	* @returns a layer, if one with the given `id` exists
15488	*/
15489	getLayer(id) {
15490		return this._layers[id];
15491	}
15492	/**
15493	* Return the ids of all layers currently in the style, including custom layers, in order.
15494	*
15495	* @returns ids of layers, in order
15496	*/
15497	getLayersOrder() {
15498		return [...this._order];
15499	}
15500	/**
15501	* Checks if a specific layer is present within the style.
15502	*
15503	* @param id - the id of the desired layer
15504	* @returns a boolean specifying if the given layer is present
15505	*/
15506	hasLayer(id) {
15507		return id in this._layers;
15508	}
15509	setLayerZoomRange(layerId, minzoom, maxzoom) {
15510		this._checkLoaded();
15511		const layer = this.getLayer(layerId);
15512		if (!layer) {
15513			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot set the zoom range of non-existing layer "${layerId}".`)));
15514			return;
15515		}
15516		if (layer.minzoom === minzoom && layer.maxzoom === maxzoom) return;
15517		if (minzoom != null) layer.minzoom = minzoom;
15518		if (maxzoom != null) layer.maxzoom = maxzoom;
15519		this._updateLayer(layer);
15520	}
15521	setFilter(layerId, filter, options = {}) {
15522		this._checkLoaded();
15523		const layer = this.getLayer(layerId);
15524		if (!layer) {
15525			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot filter non-existing layer "${layerId}".`)));
15526			return;
15527		}
15528		if (deepEqual(layer.filter, filter)) return;
15529		if (filter === null || filter === void 0) {
15530			layer.setFilter(void 0);
15531			this._updateLayer(layer);
15532			return;
15533		}
15534		if (this._validate(validateStyle.filter, `layers.${layer.id}.filter`, filter, null, options)) return;
15535		layer.setFilter(clone$2(filter));
15536		this._updateLayer(layer);
15537	}
15538	/**
15539	* Get a layer's filter object
15540	* @param layer - the layer to inspect
15541	* @returns the layer's filter, if any
15542	*/
15543	getFilter(layer) {
15544		return clone$2(this.getLayer(layer).filter);
15545	}
15546	setLayoutProperty(layerId, name, value, options = {}) {
15547		this._checkLoaded();
15548		const layer = this.getLayer(layerId);
15549		if (!layer) {
15550			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot style non-existing layer "${layerId}".`)));
15551			return;
15552		}
15553		if (deepEqual(layer.getLayoutProperty(name), value)) return;
15554		layer.setLayoutProperty(name, value, options);
15555		this._updateLayer(layer);
15556	}
15557	/**
15558	* Get a layout property's value from a given layer
15559	* @param layerId - the layer to inspect
15560	* @param name - the name of the layout property
15561	* @returns the property value
15562	*/
15563	getLayoutProperty(layerId, name) {
15564		const layer = this.getLayer(layerId);
15565		if (!layer) {
15566			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot get style of non-existing layer "${layerId}".`)));
15567			return;
15568		}
15569		return layer.getLayoutProperty(name);
15570	}
15571	setPaintProperty(layerId, name, value, options = {}) {
15572		this._checkLoaded();
15573		const layer = this.getLayer(layerId);
15574		if (!layer) {
15575			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot style non-existing layer "${layerId}".`)));
15576			return;
15577		}
15578		if (deepEqual(layer.getPaintProperty(name), value)) return;
15579		this._updatePaintProperty(layer, name, value, options);
15580	}
15581	_updatePaintProperty(layer, name, value, options = {}) {
15582		if (layer.setPaintProperty(name, value, options)) this._updateLayer(layer);
15583		if (isRasterStyleLayer(layer) && name === "raster-fade-duration") this.tileManagers[layer.source].setRasterFadeDuration(value);
15584		this._changed = true;
15585		this._updatedPaintProps[layer.id] = true;
15586		if (layer.type === "symbol") this.triggerSymbolPlacement();
15587		this._serializedLayers = null;
15588	}
15589	getPaintProperty(layer, name) {
15590		return this.getLayer(layer).getPaintProperty(name);
15591	}
15592	setFeatureState(target, state) {
15593		this._checkLoaded();
15594		const sourceId = target.source;
15595		const sourceLayer = target.sourceLayer;
15596		const tileManager = this.tileManagers[sourceId];
15597		if (tileManager === void 0) {
15598			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`The source '${sourceId}' does not exist in the map's style.`)));
15599			return;
15600		}
15601		const sourceType = tileManager.getSource().type;
15602		if (sourceType === "geojson" && sourceLayer) {
15603			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("GeoJSON sources cannot have a sourceLayer parameter.")));
15604			return;
15605		}
15606		if (sourceType === "vector" && !sourceLayer) {
15607			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("The sourceLayer parameter must be provided for vector source types.")));
15608			return;
15609		}
15610		if (target.id === void 0) {
15611			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("The feature id parameter must be provided.")));
15612			return;
15613		}
15614		const forbiddenStateKeys = [
15615			"__proto__",
15616			"constructor",
15617			"prototype"
15618		];
15619		if (state && Object.keys(state).some((stateKey) => forbiddenStateKeys.includes(stateKey))) {
15620			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`The feature state should not include one of the following keys: ${forbiddenStateKeys}`)));
15621			return;
15622		}
15623		tileManager.setFeatureState(sourceLayer, target.id, state);
15624	}
15625	removeFeatureState(target, key) {
15626		this._checkLoaded();
15627		const sourceId = target.source;
15628		const tileManager = this.tileManagers[sourceId];
15629		if (tileManager === void 0) {
15630			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`The source '${sourceId}' does not exist in the map's style.`)));
15631			return;
15632		}
15633		const sourceType = tileManager.getSource().type;
15634		const sourceLayer = sourceType === "vector" ? target.sourceLayer : void 0;
15635		if (sourceType === "vector" && !sourceLayer) {
15636			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("The sourceLayer parameter must be provided for vector source types.")));
15637			return;
15638		}
15639		if (key && typeof target.id !== "string" && typeof target.id !== "number") {
15640			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("A feature id is required to remove its specific state property.")));
15641			return;
15642		}
15643		tileManager.removeFeatureState(sourceLayer, target.id, key);
15644	}
15645	getFeatureState(target) {
15646		this._checkLoaded();
15647		const sourceId = target.source;
15648		const sourceLayer = target.sourceLayer;
15649		const tileManager = this.tileManagers[sourceId];
15650		if (tileManager === void 0) {
15651			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`The source '${sourceId}' does not exist in the map's style.`)));
15652			return;
15653		}
15654		if (tileManager.getSource().type === "vector" && !sourceLayer) {
15655			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("The sourceLayer parameter must be provided for vector source types.")));
15656			return;
15657		}
15658		if (target.id === void 0) this.fire(new ErrorEvent(/* @__PURE__ */ new Error("The feature id parameter must be provided.")));
15659		return tileManager.getFeatureState(sourceLayer, target.id);
15660	}
15661	getTransition() {
15662		return extend({
15663			duration: 300,
15664			delay: 0
15665		}, this.stylesheet?.transition);
15666	}
15667	serialize() {
15668		if (!this._loaded) return;
15669		const sources = mapObject(this.tileManagers, (source) => source.serialize());
15670		const layers = this._serializeByIds(this._order, true);
15671		const terrain = this.map.getTerrain() || void 0;
15672		const myStyleSheet = this.stylesheet;
15673		return filterObject({
15674			version: myStyleSheet.version,
15675			name: myStyleSheet.name,
15676			metadata: myStyleSheet.metadata,
15677			light: myStyleSheet.light,
15678			sky: myStyleSheet.sky,
15679			center: myStyleSheet.center,
15680			zoom: myStyleSheet.zoom,
15681			bearing: myStyleSheet.bearing,
15682			pitch: myStyleSheet.pitch,
15683			sprite: myStyleSheet.sprite,
15684			glyphs: myStyleSheet.glyphs,
15685			"font-faces": myStyleSheet["font-faces"],
15686			transition: myStyleSheet.transition,
15687			projection: myStyleSheet.projection,
15688			state: myStyleSheet.state,
15689			sources,
15690			layers,
15691			terrain
15692		}, (value) => value !== void 0);
15693	}
15694	_updateLayer(layer) {
15695		this._updatedLayers[layer.id] = true;
15696		if (layer.source && !this._updatedSources[layer.source] && this.tileManagers[layer.source].getSource().type !== "raster") {
15697			this._updatedSources[layer.source] = "reload";
15698			this.tileManagers[layer.source].pause();
15699		}
15700		this._serializedLayers = null;
15701		this._changed = true;
15702	}
15703	_flattenAndSortRenderedFeatures(sourceResults) {
15704		const isLayer3D = (layerId) => this._layers[layerId].type === "fill-extrusion";
15705		const layerIndex = {};
15706		const features3D = [];
15707		for (let l = this._order.length - 1; l >= 0; l--) {
15708			const layerId = this._order[l];
15709			if (isLayer3D(layerId)) {
15710				layerIndex[layerId] = l;
15711				for (const sourceResult of sourceResults) {
15712					const layerFeatures = sourceResult[layerId];
15713					if (layerFeatures) for (const featureWrapper of layerFeatures) features3D.push(featureWrapper);
15714				}
15715			}
15716		}
15717		features3D.sort((a, b) => {
15718			return b.intersectionZ - a.intersectionZ;
15719		});
15720		const features = [];
15721		for (let l = this._order.length - 1; l >= 0; l--) {
15722			const layerId = this._order[l];
15723			if (isLayer3D(layerId)) for (let i = features3D.length - 1; i >= 0; i--) {
15724				const topmost3D = features3D[i].feature;
15725				if (layerIndex[topmost3D.layer.id] < l) break;
15726				features.push(topmost3D);
15727				features3D.pop();
15728			}
15729			else for (const sourceResult of sourceResults) {
15730				const layerFeatures = sourceResult[layerId];
15731				if (layerFeatures) for (const featureWrapper of layerFeatures) features.push(featureWrapper.feature);
15732			}
15733		}
15734		return features;
15735	}
15736	queryRenderedFeatures(queryGeometry, params, transform) {
15737		if (params?.filter) this._validate(validateStyle.filter, "queryRenderedFeatures.filter", params.filter, null, params);
15738		const includedSources = {};
15739		if (params?.layers) {
15740			if (!(Array.isArray(params.layers) || params.layers instanceof Set)) {
15741				this.fire(new ErrorEvent(/* @__PURE__ */ new Error("parameters.layers must be an Array or a Set of strings")));
15742				return [];
15743			}
15744			for (const layerId of params.layers) {
15745				const layer = this._layers[layerId];
15746				if (!layer) {
15747					this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`The layer '${layerId}' does not exist in the map's style and cannot be queried for features.`)));
15748					return [];
15749				}
15750				includedSources[layer.source] = true;
15751			}
15752		}
15753		const sourceResults = [];
15754		params.availableImages = this.imageManager.listImages();
15755		const serializedLayers = this._serializedAllLayers();
15756		const layersAsSet = params.layers instanceof Set ? params.layers : Array.isArray(params.layers) ? new Set(params.layers) : null;
15757		const paramsStrict = {
15758			...params,
15759			layers: layersAsSet,
15760			globalState: this._globalState
15761		};
15762		for (const id in this.tileManagers) {
15763			if (params.layers && !includedSources[id]) continue;
15764			sourceResults.push(queryRenderedFeatures(this.tileManagers[id], this._layers, serializedLayers, queryGeometry, paramsStrict, transform, this.map.terrain ? (id, x, y) => this.map.terrain.getElevation(id, x, y) : void 0));
15765		}
15766		if (this.placement) sourceResults.push(queryRenderedSymbols(this._layers, serializedLayers, this.tileManagers, queryGeometry, paramsStrict, this.placement.collisionIndex, this.placement.retainedQueryData));
15767		return this._flattenAndSortRenderedFeatures(sourceResults);
15768	}
15769	querySourceFeatures(sourceID, params) {
15770		if (params?.filter) this._validate(validateStyle.filter, "querySourceFeatures.filter", params.filter, null, params);
15771		const tileManager = this.tileManagers[sourceID];
15772		return tileManager ? querySourceFeatures(tileManager, params ? {
15773			...params,
15774			globalState: this._globalState
15775		} : { globalState: this._globalState }) : [];
15776	}
15777	getLight() {
15778		return this.light.getLight();
15779	}
15780	setLight(lightOptions, options = {}) {
15781		this._checkLoaded();
15782		const light = this.light.getLight();
15783		let _update = false;
15784		for (const key in lightOptions) if (!deepEqual(lightOptions[key], light[key])) {
15785			_update = true;
15786			break;
15787		}
15788		if (!_update) return;
15789		const parameters = {
15790			now: now(),
15791			transition: extend({
15792				duration: 300,
15793				delay: 0
15794			}, this.stylesheet.transition)
15795		};
15796		this.light.setLight(lightOptions, options);
15797		this.light.updateTransitions(parameters);
15798	}
15799	getProjection() {
15800		return this.stylesheet?.projection;
15801	}
15802	setProjection(projection) {
15803		this._checkLoaded();
15804		const resolvedProjection = projection ?? { type: "mercator" };
15805		this.stylesheet.projection = projection;
15806		if (this.projection) {
15807			if (this.projection.name === resolvedProjection.type) return;
15808			this.projection.destroy();
15809			delete this.projection;
15810		}
15811		this._setProjectionInternal(resolvedProjection.type);
15812	}
15813	getSky() {
15814		return this.stylesheet?.sky;
15815	}
15816	setSky(skyOptions, options = {}) {
15817		this._checkLoaded();
15818		const sky = this.getSky();
15819		let update = false;
15820		if (!skyOptions && !sky) return;
15821		if (skyOptions && !sky) update = true;
15822		else if (!skyOptions && sky) update = true;
15823		else for (const key in skyOptions) if (!deepEqual(skyOptions[key], sky[key])) {
15824			update = true;
15825			break;
15826		}
15827		if (!update) return;
15828		const parameters = {
15829			now: now(),
15830			transition: extend({
15831				duration: 300,
15832				delay: 0
15833			}, this.stylesheet.transition)
15834		};
15835		if (!this.sky.setSky(skyOptions, options)) return;
15836		this.stylesheet.sky = skyOptions;
15837		this.sky.updateTransitions(parameters);
15838	}
15839	_setProjectionInternal(name) {
15840		const projectionObjects = createProjectionFromName(name, this.map._camera?.transform.constrainOverride, this._globalState);
15841		this.projection = projectionObjects.projection;
15842		this.map.migrateProjection(projectionObjects.transform, projectionObjects.cameraHelper);
15843		for (const key in this.tileManagers) this.tileManagers[key].reload();
15844	}
15845	_validate(validate, key, value, props, options = {}) {
15846		if (options.validate === false) return false;
15847		return validateAndEmit(this, validate, {
15848			key,
15849			style: this.serialize(),
15850			value,
15851			...props
15852		}, options);
15853	}
15854	_remove(mapRemoved = true) {
15855		if (this._frameRequest) {
15856			this._frameRequest.abort();
15857			this._frameRequest = null;
15858		}
15859		if (this._loadStyleRequest) {
15860			this._loadStyleRequest.abort();
15861			this._loadStyleRequest = null;
15862		}
15863		if (this._spriteRequest) {
15864			this._spriteRequest.abort();
15865			this._spriteRequest = null;
15866		}
15867		rtlMainThreadPluginFactory().off(RTLPluginLoadedEventName, this._rtlPluginLoaded);
15868		for (const layerId in this._layers) this._layers[layerId].setEventedParent(null);
15869		for (const id in this.tileManagers) {
15870			const tileManager = this.tileManagers[id];
15871			tileManager.setEventedParent(null);
15872			tileManager.onRemove(this.map);
15873		}
15874		this.imageManager.setEventedParent(null);
15875		this.setEventedParent(null);
15876		if (mapRemoved) this.dispatcher.broadcast("RM", void 0);
15877		this.dispatcher.remove(mapRemoved);
15878	}
15879	_clearSource(id) {
15880		this.tileManagers[id].clearTiles();
15881	}
15882	_reloadSource(id) {
15883		this.tileManagers[id].resume();
15884		this.tileManagers[id].reload();
15885	}
15886	_updateSources(transform) {
15887		for (const id in this.tileManagers) this.tileManagers[id].update(transform, this.map.terrain);
15888	}
15889	_generateCollisionBoxes() {
15890		for (const id in this.tileManagers) this._reloadSource(id);
15891	}
15892	/**
15893	* Re-places symbols on the next frame.
15894	* Placement is skipped while its inputs look unchanged, so call this when something
15895	* the map cannot see for itself has moved symbols.
15896	*/
15897	triggerSymbolPlacement() {
15898		this._symbolPlacementTriggered = true;
15899	}
15900	/**
15901	* Whether re-running symbol placement could produce a different result than the last run.
15902	* Every camera and viewport parameter placement can observe perturbs the model-view-projection
15903	* matrix, so comparing the matrix covers them all. Inputs the matrix cannot see arrive as
15904	* {@link triggerSymbolPlacement} calls.
15905	*
15906	* @internal
15907	*/
15908	_placementInputsChanged(transform, showCollisionBoxes, crossSourceCollisions) {
15909		const lastPlacement = this.pauseablePlacement;
15910		return !lastPlacement || this._symbolPlacementTriggered || this._placedProjectionTransition !== this.projection?.transitionState || lastPlacement._showCollisionBoxes !== showCollisionBoxes || lastPlacement.placement.collisionGroups.crossSourceCollisions !== crossSourceCollisions || lastPlacement.placement.transform.renderWorldCopies !== transform.renderWorldCopies || !exactEquals(lastPlacement.placement.transform.modelViewProjectionMatrix, transform.modelViewProjectionMatrix);
15911	}
15912	_updatePlacement(transform, showCollisionBoxes, fadeDuration, crossSourceCollisions, forceFullPlacement = false) {
15913		let symbolBucketsChanged = false;
15914		let placementCommitted = false;
15915		const layerTiles = {};
15916		for (const layerID of this._order) {
15917			const styleLayer = this._layers[layerID];
15918			if (styleLayer.type !== "symbol") continue;
15919			if (!layerTiles[styleLayer.source]) {
15920				const tileManager = this.tileManagers[styleLayer.source];
15921				layerTiles[styleLayer.source] = tileManager.getRenderableIds(true).map((id) => tileManager.getTileByID(id)).sort((a, b) => b.tileID.overscaledZ - a.tileID.overscaledZ || (a.tileID.isLessThan(b.tileID) ? -1 : 1));
15922			}
15923			const layerBucketsChanged = this.crossTileSymbolIndex.addLayer(styleLayer, layerTiles[styleLayer.source], transform.center.lng);
15924			symbolBucketsChanged ||= layerBucketsChanged;
15925		}
15926		this.crossTileSymbolIndex.pruneUnusedLayers(this._order);
15927		forceFullPlacement ||= this._layerOrderChanged || fadeDuration === 0;
15928		const placementInputsChanged = symbolBucketsChanged || this._placementInputsChanged(transform, showCollisionBoxes, crossSourceCollisions);
15929		const placementSettled = this.pauseablePlacement?.isDone() && !this.placement.stillRecent(now(), transform.zoom);
15930		if (forceFullPlacement || !this.pauseablePlacement || placementSettled && (placementInputsChanged || this.placement.stale)) {
15931			this._symbolPlacementTriggered = false;
15932			this._placedProjectionTransition = this.projection?.transitionState;
15933			this.pauseablePlacement = new PauseablePlacement(transform, this.map.terrain, this._order, forceFullPlacement, showCollisionBoxes, fadeDuration, crossSourceCollisions, this.placement);
15934			this._layerOrderChanged = false;
15935		}
15936		if (this.pauseablePlacement.isDone()) {
15937			if (placementInputsChanged) this.placement.setStale();
15938		} else {
15939			this.pauseablePlacement.continuePlacement(this._order, this._layers, layerTiles);
15940			if (this.pauseablePlacement.isDone()) {
15941				this.placement = this.pauseablePlacement.commit(now());
15942				placementCommitted = true;
15943			}
15944			if (symbolBucketsChanged) this.pauseablePlacement.placement.setStale();
15945		}
15946		if (placementCommitted || symbolBucketsChanged) for (const layerID of this._order) {
15947			const styleLayer = this._layers[layerID];
15948			if (styleLayer.type !== "symbol") continue;
15949			this.placement.updateLayerOpacities(styleLayer, layerTiles[styleLayer.source]);
15950		}
15951		return !this.pauseablePlacement.isDone() || this.placement.hasTransitions(now());
15952	}
15953	_releaseSymbolFadeTiles() {
15954		for (const id in this.tileManagers) this.tileManagers[id].releaseSymbolFadeTiles();
15955	}
15956	async getImages(mapId, params) {
15957		const images = await this.imageManager.getImages(params.icons);
15958		this._updateTilesForChangedImages();
15959		const tileManager = this.tileManagers[params.source];
15960		if (tileManager) tileManager.setDependencies(params.tileID.key, params.type, params.icons);
15961		return images;
15962	}
15963	async getGlyphs(mapId, params) {
15964		const glyphs = await this.glyphManager.getGlyphs(params.stacks);
15965		const tileManager = this.tileManagers[params.source];
15966		if (tileManager) tileManager.setDependencies(params.tileID.key, params.type, [""]);
15967		return glyphs;
15968	}
15969	getGlyphsUrl() {
15970		return this.stylesheet.glyphs || null;
15971	}
15972	setGlyphs(glyphsUrl, options = {}) {
15973		this._checkLoaded();
15974		if (glyphsUrl && this._validate(validateStyle.glyphs, "glyphs", glyphsUrl, null, options)) return;
15975		this._changed = true;
15976		this._glyphsDidChange = true;
15977		this.stylesheet.glyphs = glyphsUrl;
15978		this.glyphManager.entries = {};
15979		this.glyphManager.setURL(glyphsUrl);
15980	}
15981	getFontFaces() {
15982		return this.stylesheet["font-faces"] || null;
15983	}
15984	setFontFaces(fontFaces) {
15985		this._checkLoaded();
15986		this._changed = true;
15987		this._glyphsDidChange = true;
15988		this.stylesheet["font-faces"] = fontFaces;
15989		this.glyphManager.setFontFaces(fontFaces);
15990	}
15991	async getDashes(mapId, params) {
15992		const result = {};
15993		for (const [key, dash] of Object.entries(params.dashes)) result[key] = this.lineAtlas.getDash(dash.dasharray, dash.round);
15994		return result;
15995	}
15996	/**
15997	* Add a sprite.
15998	*
15999	* @param id - The id of the desired sprite
16000	* @param url - The url to load the desired sprite from
16001	* @param options - The style setter options
16002	* @param completion - The completion handler
16003	*/
16004	addSprite(id, url, options = {}, completion) {
16005		this._checkLoaded();
16006		const spriteToAdd = [{
16007			id,
16008			url
16009		}];
16010		const updatedSprite = [...coerceSpriteToArray(this.stylesheet.sprite), ...spriteToAdd];
16011		if (this._validate(validateStyle.sprite, "sprite", updatedSprite, null, options)) return;
16012		this.stylesheet.sprite = updatedSprite;
16013		this._loadSprite(spriteToAdd, true, completion);
16014	}
16015	/**
16016	* Remove a sprite by its id. When the last sprite is removed, the whole `this.stylesheet.sprite` object becomes
16017	* `undefined`. This falsy `undefined` value later prevents attempts to load the sprite when it's absent.
16018	*
16019	* @param id - the id of the sprite to remove
16020	*/
16021	removeSprite(id) {
16022		this._checkLoaded();
16023		const internalSpriteRepresentation = coerceSpriteToArray(this.stylesheet.sprite);
16024		if (!internalSpriteRepresentation.find((sprite) => sprite.id === id)) {
16025			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Sprite "${id}" doesn't exists on this map.`)));
16026			return;
16027		}
16028		const changedImages = this.imageManager.removeSpriteImages(id);
16029		this._markImagesChanged(changedImages);
16030		internalSpriteRepresentation.splice(internalSpriteRepresentation.findIndex((sprite) => sprite.id === id), 1);
16031		this.stylesheet.sprite = internalSpriteRepresentation.length > 0 ? internalSpriteRepresentation : void 0;
16032		this._imagesListDirty = true;
16033		this._changed = true;
16034		this.fire(new MapStyleDataEvent("data"));
16035	}
16036	/**
16037	* Get the current sprite value.
16038	*
16039	* @returns empty array when no sprite is set; id-url pairs otherwise
16040	*/
16041	getSprite() {
16042		return coerceSpriteToArray(this.stylesheet.sprite);
16043	}
16044	/**
16045	* Set a new value for the style's sprite.
16046	*
16047	* @param sprite - new sprite value
16048	* @param options - style setter options
16049	* @param completion - the completion handler
16050	*/
16051	setSprite(sprite, options = {}, completion) {
16052		this._checkLoaded();
16053		if (sprite && this._validate(validateStyle.sprite, "sprite", sprite, null, options)) return;
16054		this.stylesheet.sprite = sprite;
16055		if (sprite) this._loadSprite(sprite, true, completion);
16056		else {
16057			this._unloadSprite();
16058			if (completion) completion(null);
16059		}
16060	}
16061	/**
16062	* Destroys all internal resources of the style (sources, images, layers, etc.)
16063	*/
16064	destroy() {
16065		if (this._frameRequest) {
16066			this._frameRequest.abort();
16067			this._frameRequest = null;
16068		}
16069		if (this._loadStyleRequest) {
16070			this._loadStyleRequest.abort();
16071			this._loadStyleRequest = null;
16072		}
16073		if (this._spriteRequest) {
16074			this._spriteRequest.abort();
16075			this._spriteRequest = null;
16076		}
16077		for (const id in this.tileManagers) {
16078			const tileManager = this.tileManagers[id];
16079			tileManager.setEventedParent(null);
16080			tileManager.onRemove(this.map);
16081		}
16082		this.tileManagers = {};
16083		if (this.imageManager) {
16084			this.imageManager.setEventedParent(null);
16085			this.imageManager.destroy();
16086			this.patternAtlas.destroy();
16087		}
16088		if (this.glyphManager) this.glyphManager.destroy();
16089		for (const layerId in this._layers) {
16090			const layer = this._layers[layerId];
16091			layer.setEventedParent(null);
16092			if (layer.onRemove) layer.onRemove(this.map);
16093		}
16094		this._setInitialValues();
16095		this.setEventedParent(null);
16096		this.dispatcher.unregisterMessageHandler("GG");
16097		this.dispatcher.unregisterMessageHandler("GI");
16098		this.dispatcher.unregisterMessageHandler("GDA");
16099		this.dispatcher.remove(true);
16100		this._listeners = {};
16101		this._oneTimeListeners = {};
16102	}
16103};
16104//#endregion
16105//#region src/data/raster_bounds_attributes.ts
16106const rasterBoundsAttributes = createLayout([{
16107	name: "a_pos",
16108	type: "Int16",
16109	components: 2
16110}, {
16111	name: "a_texture_pos",
16112	type: "Int16",
16113	components: 2
16114}]);
16115//#endregion
16116//#region src/webgl/vertex_array_object.ts
16117/**
16118* @internal
16119* A vertex array object used to pass data to the webgl code
16120*/
16121var VertexArrayObject = class {
16122	constructor() {
16123		this.boundProgram = null;
16124		this.boundLayoutVertexBuffer = null;
16125		this.boundPaintVertexBuffers = [];
16126		this.boundIndexBuffer = null;
16127		this.boundVertexOffset = null;
16128		this.boundDynamicVertexBuffer = null;
16129		this.vao = null;
16130	}
16131	bind(context, program, layoutVertexBuffer, paintVertexBuffers, indexBuffer, vertexOffset, dynamicVertexBuffer, dynamicVertexBuffer2, dynamicVertexBuffer3) {
16132		this.context = context;
16133		let paintBuffersDiffer = this.boundPaintVertexBuffers.length !== paintVertexBuffers.length;
16134		for (let i = 0; !paintBuffersDiffer && i < paintVertexBuffers.length; i++) if (this.boundPaintVertexBuffers[i] !== paintVertexBuffers[i]) paintBuffersDiffer = true;
16135		if (!this.vao || this.boundProgram !== program || this.boundLayoutVertexBuffer !== layoutVertexBuffer || paintBuffersDiffer || this.boundIndexBuffer !== indexBuffer || this.boundVertexOffset !== vertexOffset || this.boundDynamicVertexBuffer !== dynamicVertexBuffer || this.boundDynamicVertexBuffer2 !== dynamicVertexBuffer2 || this.boundDynamicVertexBuffer3 !== dynamicVertexBuffer3) this.freshBind(program, layoutVertexBuffer, paintVertexBuffers, indexBuffer, vertexOffset, dynamicVertexBuffer, dynamicVertexBuffer2, dynamicVertexBuffer3);
16136		else context.bindVertexArray.set(this.vao);
16137	}
16138	freshBind(program, layoutVertexBuffer, paintVertexBuffers, indexBuffer, vertexOffset, dynamicVertexBuffer, dynamicVertexBuffer2, dynamicVertexBuffer3) {
16139		const numNextAttributes = program.numAttributes;
16140		const context = this.context;
16141		const gl = context.gl;
16142		if (this.vao) this.destroy();
16143		this.vao = context.createVertexArray();
16144		context.bindVertexArray.set(this.vao);
16145		this.boundProgram = program;
16146		this.boundLayoutVertexBuffer = layoutVertexBuffer;
16147		this.boundPaintVertexBuffers = paintVertexBuffers;
16148		this.boundIndexBuffer = indexBuffer;
16149		this.boundVertexOffset = vertexOffset;
16150		this.boundDynamicVertexBuffer = dynamicVertexBuffer;
16151		this.boundDynamicVertexBuffer2 = dynamicVertexBuffer2;
16152		this.boundDynamicVertexBuffer3 = dynamicVertexBuffer3;
16153		layoutVertexBuffer.enableAttributes(gl, program);
16154		for (const vertexBuffer of paintVertexBuffers) vertexBuffer.enableAttributes(gl, program);
16155		if (dynamicVertexBuffer) dynamicVertexBuffer.enableAttributes(gl, program);
16156		if (dynamicVertexBuffer2) dynamicVertexBuffer2.enableAttributes(gl, program);
16157		if (dynamicVertexBuffer3) dynamicVertexBuffer3.enableAttributes(gl, program);
16158		layoutVertexBuffer.bind();
16159		layoutVertexBuffer.setVertexAttribPointers(gl, program, vertexOffset);
16160		for (const vertexBuffer of paintVertexBuffers) {
16161			vertexBuffer.bind();
16162			vertexBuffer.setVertexAttribPointers(gl, program, vertexOffset);
16163		}
16164		if (dynamicVertexBuffer) {
16165			dynamicVertexBuffer.bind();
16166			dynamicVertexBuffer.setVertexAttribPointers(gl, program, vertexOffset);
16167		}
16168		if (indexBuffer) indexBuffer.bind();
16169		if (dynamicVertexBuffer2) {
16170			dynamicVertexBuffer2.bind();
16171			dynamicVertexBuffer2.setVertexAttribPointers(gl, program, vertexOffset);
16172		}
16173		if (dynamicVertexBuffer3) {
16174			dynamicVertexBuffer3.bind();
16175			dynamicVertexBuffer3.setVertexAttribPointers(gl, program, vertexOffset);
16176		}
16177		context.currentNumAttributes = numNextAttributes;
16178	}
16179	destroy() {
16180		if (this.vao) {
16181			this.context.deleteVertexArray(this.vao);
16182			this.vao = null;
16183		}
16184	}
16185};
16186//#endregion
16187//#region src/webgl/program/terrain_program.ts
16188const terrainPreludeUniforms = (context, locations) => ({
16189	"u_depth": new Uniform1i(context, locations.u_depth),
16190	"u_terrain": new Uniform1i(context, locations.u_terrain)
16191});
16192const terrainUniforms = (context, locations) => ({
16193	"u_texture": new Uniform1i(context, locations.u_texture),
16194	"u_ele_delta": new Uniform1f(context, locations.u_ele_delta),
16195	"u_fog_matrix": new UniformMatrix4f(context, locations.u_fog_matrix),
16196	"u_fog_color": new UniformColor(context, locations.u_fog_color),
16197	"u_fog_ground_blend": new Uniform1f(context, locations.u_fog_ground_blend),
16198	"u_fog_ground_blend_opacity": new Uniform1f(context, locations.u_fog_ground_blend_opacity),
16199	"u_horizon_color": new UniformColor(context, locations.u_horizon_color),
16200	"u_horizon_fog_blend": new Uniform1f(context, locations.u_horizon_fog_blend),
16201	"u_is_globe_mode": new Uniform1f(context, locations.u_is_globe_mode)
16202});
16203const terrainDepthUniforms = (context, locations) => ({ "u_ele_delta": new Uniform1f(context, locations.u_ele_delta) });
16204const terrainUniformValues = (eleDelta, fogMatrix, sky, pitch, isGlobeMode) => ({
16205	"u_texture": 0,
16206	"u_ele_delta": eleDelta,
16207	"u_fog_matrix": fogMatrix,
16208	"u_fog_color": sky ? sky.properties.get("fog-color") : Color.white,
16209	"u_fog_ground_blend": sky ? sky.properties.get("fog-ground-blend") : 1,
16210	"u_fog_ground_blend_opacity": isGlobeMode ? 0 : sky ? sky.calculateFogBlendOpacity(pitch) : 0,
16211	"u_horizon_color": sky ? sky.properties.get("horizon-color") : Color.white,
16212	"u_horizon_fog_blend": sky ? sky.properties.get("horizon-fog-blend") : 1,
16213	"u_is_globe_mode": isGlobeMode ? 1 : 0
16214});
16215const terrainDepthUniformValues = (eleDelta) => ({ "u_ele_delta": eleDelta });
16216//#endregion
16217//#region src/webgl/uniform_buffer.ts
16218const UBO_BINDINGS = {
16219	ProjectionUBO: 0,
16220	TerrainUBO: 1,
16221	FrameUBO: 2
16222};
16223function applyUBOBindings(gl, program) {
16224	for (const [name, binding] of Object.entries(UBO_BINDINGS)) {
16225		const index = gl.getUniformBlockIndex(program, name);
16226		if (index !== gl.INVALID_INDEX) gl.uniformBlockBinding(program, index, binding);
16227	}
16228}
16229const STD140_SIZE_AND_ALIGNMENT_WORDS = {
16230	float: [1, 1],
16231	int: [1, 1],
16232	vec2: [2, 2],
16233	vec4: [4, 4],
16234	mat4: [16, 4]
16235};
16236function std140Layout(members) {
16237	const offsets = {};
16238	let words = 0;
16239	for (const { name, type } of members) {
16240		const [size, alignment] = STD140_SIZE_AND_ALIGNMENT_WORDS[type];
16241		words = Math.ceil(words / alignment) * alignment;
16242		offsets[name] = words;
16243		words += size;
16244	}
16245	return {
16246		offsets,
16247		contentWords: words,
16248		sizeWords: Math.ceil(words / 4) * 4
16249	};
16250}
16251/**
16252* @internal
16253* The buffer behind one std140 uniform block, bound to a fixed binding point. Callers write members into
16254* `pending` at the layout's offsets and call `upload`, which skips the GPU write when nothing changed. Uploads
16255* use `bufferData` because Apple's OpenGL driver stalls a `bufferSubData` into a buffer a pending draw still reads (#8468).
16256*/
16257var UniformBuffer = class {
16258	constructor(context, binding, layout) {
16259		this.context = context;
16260		this.binding = binding;
16261		this.contentWords = layout.contentWords;
vendor: 10,176 bytes, lines 16262-16528
16262		const gl = context.gl;
16263		this.buffer = gl.createBuffer();
16264		gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
16265		gl.bufferData(gl.UNIFORM_BUFFER, layout.sizeWords * 4, gl.DYNAMIC_DRAW);
16266		gl.bindBufferBase(gl.UNIFORM_BUFFER, binding, this.buffer);
16267		this.uploaded = new Float32Array(layout.sizeWords);
16268		this.pending = new Float32Array(layout.sizeWords);
16269		this.uploadedWords = new Uint32Array(this.uploaded.buffer);
16270		this.pendingWords = new Uint32Array(this.pending.buffer);
16271		this.hasData = false;
16272		this.bindingDirty = false;
16273	}
16274	upload() {
16275		const gl = this.context.gl;
16276		let changed = !this.hasData;
16277		if (!changed) {
16278			const words = this.pendingWords;
16279			const uploadedWords = this.uploadedWords;
16280			for (let i = 0; i < this.contentWords; i++) if (words[i] !== uploadedWords[i]) {
16281				changed = true;
16282				break;
16283			}
16284		}
16285		this.bind();
16286		if (!changed) return;
16287		gl.bindBufferBase(gl.UNIFORM_BUFFER, this.binding, this.buffer);
16288		gl.bufferData(gl.UNIFORM_BUFFER, this.pending, gl.DYNAMIC_DRAW);
16289		this.uploaded.set(this.pending);
16290		this.hasData = true;
16291	}
16292	/** Restores the indexed binding after external rendering without uploading unchanged data. */
16293	bind() {
16294		if (!this.bindingDirty) return;
16295		const gl = this.context.gl;
16296		gl.bindBufferBase(gl.UNIFORM_BUFFER, this.binding, this.buffer);
16297		this.bindingDirty = false;
16298	}
16299	destroy() {
16300		const gl = this.context.gl;
16301		if (this.buffer) {
16302			gl.deleteBuffer(this.buffer);
16303			delete this.buffer;
16304		}
16305	}
16306};
16307//#endregion
16308//#region src/webgl/projection_uniform_buffer.ts
16309const layout$2 = std140Layout([
16310	{
16311		name: "u_projection_matrix",
16312		type: "mat4"
16313	},
16314	{
16315		name: "u_projection_fallback_matrix",
16316		type: "mat4"
16317	},
16318	{
16319		name: "u_projection_tile_mercator_coords",
16320		type: "vec4"
16321	},
16322	{
16323		name: "u_projection_clipping_plane",
16324		type: "vec4"
16325	},
16326	{
16327		name: "u_projection_transition",
16328		type: "float"
16329	},
16330	{
16331		name: "u_projection_clip_antimeridian",
16332		type: "int"
16333	}
16334]);
16335const offsets$2 = layout$2.offsets;
16336/**
16337* @internal
16338* The buffer behind the `ProjectionUBO` block in the shader preludes, written by `Program.draw` before each draw.
16339*/
16340function createProjectionUniformBuffer(context) {
16341	return new UniformBuffer(context, UBO_BINDINGS.ProjectionUBO, layout$2);
16342}
16343function updateProjectionUniformBuffer(buffer, projectionData) {
16344	const f32 = buffer.pending;
16345	f32.set(projectionData.mainMatrix, offsets$2.u_projection_matrix);
16346	f32.set(projectionData.fallbackMatrix, offsets$2.u_projection_fallback_matrix);
16347	f32.set(projectionData.tileMercatorCoords, offsets$2.u_projection_tile_mercator_coords);
16348	f32.set(projectionData.clippingPlane, offsets$2.u_projection_clipping_plane);
16349	f32[offsets$2.u_projection_transition] = projectionData.projectionTransition;
16350	buffer.pendingWords[offsets$2.u_projection_clip_antimeridian] = projectionData.clipAntimeridian ? 1 : 0;
16351	buffer.upload();
16352}
16353//#endregion
16354//#region src/webgl/terrain_uniform_buffer.ts
16355const layout$1 = std140Layout([
16356	{
16357		name: "u_terrain_matrix",
16358		type: "mat4"
16359	},
16360	{
16361		name: "u_terrain_unpack",
16362		type: "vec4"
16363	},
16364	{
16365		name: "u_terrain_dim",
16366		type: "float"
16367	},
16368	{
16369		name: "u_terrain_exaggeration",
16370		type: "float"
16371	}
16372]);
16373const offsets$1 = layout$1.offsets;
16374/**
16375* @internal
16376* The buffer behind the `TerrainUBO` block in the vertex prelude, written by `Program.draw` for every draw with terrain.
16377*/
16378function createTerrainUniformBuffer(context) {
16379	return new UniformBuffer(context, UBO_BINDINGS.TerrainUBO, layout$1);
16380}
16381function updateTerrainUniformBuffer(buffer, terrain) {
16382	const f32 = buffer.pending;
16383	f32.set(terrain.u_terrain_matrix, offsets$1.u_terrain_matrix);
16384	f32.set(terrain.u_terrain_unpack, offsets$1.u_terrain_unpack);
16385	f32[offsets$1.u_terrain_dim] = terrain.u_terrain_dim;
16386	f32[offsets$1.u_terrain_exaggeration] = terrain.u_terrain_exaggeration;
16387	buffer.upload();
16388}
16389//#endregion
16390//#region src/webgl/program.ts
16391function getTokenizedAttributesAndUniforms(array) {
16392	const result = [];
16393	for (const entry of array) {
16394		if (entry === null) continue;
16395		const token = entry.split(" ");
16396		result.push(token.pop());
16397	}
16398	return result;
16399}
16400function getIntegerAttributeNames(gl, program) {
16401	const integerTypes = /* @__PURE__ */ new Set([
16402		gl.INT,
16403		gl.INT_VEC2,
16404		gl.INT_VEC3,
16405		gl.INT_VEC4,
16406		gl.UNSIGNED_INT,
16407		gl.UNSIGNED_INT_VEC2,
16408		gl.UNSIGNED_INT_VEC3,
16409		gl.UNSIGNED_INT_VEC4
16410	]);
16411	const names = /* @__PURE__ */ new Set();
16412	const numActiveAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
16413	for (let i = 0; i < numActiveAttributes; i++) {
16414		const attribute = gl.getActiveAttrib(program, i);
16415		if (attribute && integerTypes.has(attribute.type)) names.add(attribute.name);
16416	}
16417	return names;
16418}
16419/**
16420* @internal
16421* A webgl program to execute in the GPU space
16422*/
16423var Program = class {
16424	constructor(context, source, configuration, fixedUniforms, showOverdrawInspector, useTerrain, projectionPrelude, projectionDefine, extraDefines = []) {
16425		const gl = context.gl;
16426		this.program = gl.createProgram();
16427		const staticAttrInfo = getTokenizedAttributesAndUniforms(source.staticAttributes);
16428		const dynamicAttrInfo = configuration ? configuration.getBinderAttributes() : [];
16429		const allAttrInfo = staticAttrInfo.concat(dynamicAttrInfo);
16430		const preludeUniformsInfo = shaders.prelude.staticUniforms ? getTokenizedAttributesAndUniforms(shaders.prelude.staticUniforms) : [];
16431		const staticUniformsInfo = source.staticUniforms ? getTokenizedAttributesAndUniforms(source.staticUniforms) : [];
16432		const dynamicUniformsInfo = configuration ? configuration.getBinderUniforms() : [];
16433		const uniformList = preludeUniformsInfo.concat(staticUniformsInfo).concat(dynamicUniformsInfo);
16434		const allUniformsInfo = [];
16435		for (const uniform of uniformList) if (!allUniformsInfo.includes(uniform)) allUniformsInfo.push(uniform);
16436		const defines = configuration ? configuration.defines() : [];
16437		defines.unshift("#version 300 es");
16438		if (showOverdrawInspector) defines.push("#define OVERDRAW_INSPECTOR;");
16439		if (useTerrain) defines.push("#define TERRAIN3D;");
16440		if (projectionDefine) defines.push(projectionDefine);
16441		if (extraDefines) defines.push(...extraDefines);
16442		const fragmentSource = defines.concat(shaders.prelude.fragmentSource, projectionPrelude.fragmentSource, source.fragmentSource).join("\n");
16443		const vertexSource = defines.concat(shaders.prelude.vertexSource, projectionPrelude.vertexSource, source.vertexSource).join("\n");
16444		const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
16445		if (gl.isContextLost()) {
16446			this.failedToCreate = true;
16447			return;
16448		}
16449		gl.shaderSource(fragmentShader, fragmentSource);
16450		gl.compileShader(fragmentShader);
16451		gl.attachShader(this.program, fragmentShader);
16452		const vertexShader = gl.createShader(gl.VERTEX_SHADER);
16453		if (gl.isContextLost()) {
16454			this.failedToCreate = true;
16455			return;
16456		}
16457		gl.shaderSource(vertexShader, vertexSource);
16458		gl.compileShader(vertexShader);
16459		gl.attachShader(this.program, vertexShader);
16460		this.attributes = {};
16461		const uniformLocations = {};
16462		this.numAttributes = allAttrInfo.length;
16463		gl.linkProgram(this.program);
16464		if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
16465			if (gl.isContextLost()) {
16466				this.failedToCreate = true;
16467				return;
16468			}
16469			if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) throw new Error(`Could not compile fragment shader: ${gl.getShaderInfoLog(fragmentShader)}`);
16470			if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) throw new Error(`Could not compile vertex shader: ${gl.getShaderInfoLog(vertexShader)}`);
16471			throw new Error(`Program failed to link: ${gl.getProgramInfoLog(this.program)}`);
16472		}
16473		applyUBOBindings(gl, this.program);
16474		const integerAttributeNames = getIntegerAttributeNames(gl, this.program);
16475		for (const name of allAttrInfo) {
16476			if (!name) continue;
16477			const location = gl.getAttribLocation(this.program, name);
16478			if (location >= 0) this.attributes[name] = {
16479				location,
16480				isInteger: integerAttributeNames.has(name)
16481			};
16482		}
16483		gl.deleteShader(vertexShader);
16484		gl.deleteShader(fragmentShader);
16485		for (const uniform of allUniformsInfo) if (uniform && !uniformLocations[uniform]) {
16486			const uniformLocation = gl.getUniformLocation(this.program, uniform);
16487			if (uniformLocation) uniformLocations[uniform] = uniformLocation;
16488		}
16489		this.fixedUniforms = fixedUniforms(context, uniformLocations);
16490		this.terrainUniforms = terrainPreludeUniforms(context, uniformLocations);
16491		this.binderUniforms = configuration ? configuration.getUniforms(context, uniformLocations) : [];
16492	}
16493	draw(context, drawMode, depthMode, stencilMode, colorMode, cullFaceMode, uniformValues, terrain, projectionData, layerID, layoutVertexBuffer, indexBuffer, segments, currentProperties, zoom, configuration, dynamicLayoutBuffer, dynamicLayoutBuffer2, dynamicLayoutBuffer3) {
16494		const gl = context.gl;
16495		if (this.failedToCreate) return;
16496		context.program.set(this.program);
16497		context.projectionUniformBuffer.bind();
16498		context.terrainUniformBuffer.bind();
16499		context.frameUniformBuffer.bind();
16500		context.setDepthMode(depthMode);
16501		context.setStencilMode(stencilMode);
16502		context.setColorMode(colorMode);
16503		context.setCullFace(cullFaceMode);
16504		if (terrain) {
16505			context.activeTexture.set(gl.TEXTURE2);
16506			gl.bindTexture(gl.TEXTURE_2D, terrain.depthTexture);
16507			context.activeTexture.set(gl.TEXTURE3);
16508			gl.bindTexture(gl.TEXTURE_2D, terrain.texture);
16509			for (const name in this.terrainUniforms) this.terrainUniforms[name].set(terrain[name]);
16510			updateTerrainUniformBuffer(context.terrainUniformBuffer, terrain);
16511		}
16512		if (projectionData) updateProjectionUniformBuffer(context.projectionUniformBuffer, projectionData);
16513		if (uniformValues) for (const name in this.fixedUniforms) this.fixedUniforms[name].set(uniformValues[name]);
16514		if (configuration) configuration.setUniforms(context, this.binderUniforms, currentProperties, { zoom });
16515		let primitiveSize = 0;
16516		switch (drawMode) {
16517			case gl.LINES:
16518				primitiveSize = 2;
16519				break;
16520			case gl.TRIANGLES:
16521				primitiveSize = 3;
16522				break;
16523			case gl.LINE_STRIP: primitiveSize = 1;
16524		}
16525		for (const segment of segments.get()) {
16526			segment.vaos ||= {};
16527			segment.vaos[layerID] ||= new VertexArrayObject();
16528			segment.vaos[layerID].bind(context, this, layoutVertexBuffer, configuration ? c
vendor: 46,275 bytes, lines 16528-17813
16528onfiguration.getPaintVertexBuffers() : [], indexBuffer, segment.vertexOffset, dynamicLayoutBuffer, dynamicLayoutBuffer2, dynamicLayoutBuffer3);
16529			gl.drawElements(drawMode, segment.primitiveLength * primitiveSize, gl.UNSIGNED_SHORT, segment.primitiveOffset * primitiveSize * 2);
16530		}
16531	}
16532};
16533//#endregion
16534//#region src/webgl/program/pattern.ts
16535function patternUniformValues(crossfade, painter, tile) {
16536	const tileRatio = 1 / pixelsToTileUnits(tile, 1, painter.transform.tileZoom);
16537	const numTiles = Math.pow(2, tile.tileID.overscaledZ);
16538	const tileSizeAtNearestZoom = tile.tileSize * Math.pow(2, painter.transform.tileZoom) / numTiles;
16539	const pixelX = tileSizeAtNearestZoom * (tile.tileID.canonical.x + tile.tileID.wrap * numTiles);
16540	const pixelY = tileSizeAtNearestZoom * tile.tileID.canonical.y;
16541	return {
16542		"u_image": 0,
16543		"u_texsize": tile.imageAtlasTexture.size,
16544		"u_scale": [
16545			tileRatio,
16546			crossfade.fromScale,
16547			crossfade.toScale
16548		],
16549		"u_fade": crossfade.t,
16550		"u_pixel_coord_upper": [pixelX >> 16, pixelY >> 16],
16551		"u_pixel_coord_lower": [pixelX & 65535, pixelY & 65535]
16552	};
16553}
16554function bgPatternUniformValues(image, crossfade, painter, tile) {
16555	const imagePosA = painter.patternAtlas.getPattern(image.from.toString());
16556	const imagePosB = painter.patternAtlas.getPattern(image.to.toString());
16557	const { width, height } = painter.patternAtlas.getPixelSize();
16558	const numTiles = Math.pow(2, tile.tileID.overscaledZ);
16559	const tileSizeAtNearestZoom = tile.tileSize * Math.pow(2, painter.transform.tileZoom) / numTiles;
16560	const pixelX = tileSizeAtNearestZoom * (tile.tileID.canonical.x + tile.tileID.wrap * numTiles);
16561	const pixelY = tileSizeAtNearestZoom * tile.tileID.canonical.y;
16562	return {
16563		"u_image": 0,
16564		"u_pattern_tl_a": imagePosA.tl,
16565		"u_pattern_br_a": imagePosA.br,
16566		"u_pattern_tl_b": imagePosB.tl,
16567		"u_pattern_br_b": imagePosB.br,
16568		"u_texsize": [width, height],
16569		"u_mix": crossfade.t,
16570		"u_pattern_size_a": imagePosA.displaySize,
16571		"u_pattern_size_b": imagePosB.displaySize,
16572		"u_scale_a": crossfade.fromScale,
16573		"u_scale_b": crossfade.toScale,
16574		"u_tile_units_to_pixels": 1 / pixelsToTileUnits(tile, 1, painter.transform.tileZoom),
16575		"u_pixel_coord_upper": [pixelX >> 16, pixelY >> 16],
16576		"u_pixel_coord_lower": [pixelX & 65535, pixelY & 65535]
16577	};
16578}
16579//#endregion
16580//#region src/webgl/program/fill_extrusion_program.ts
16581const fillExtrusionUniforms = (context, locations) => ({
16582	"u_lightpos": new Uniform3f(context, locations.u_lightpos),
16583	"u_lightpos_globe": new Uniform3f(context, locations.u_lightpos_globe),
16584	"u_lightintensity": new Uniform1f(context, locations.u_lightintensity),
16585	"u_lightcolor": new Uniform3f(context, locations.u_lightcolor),
16586	"u_vertical_gradient": new Uniform1f(context, locations.u_vertical_gradient),
16587	"u_opacity": new Uniform1f(context, locations.u_opacity),
16588	"u_fill_translate": new Uniform2f(context, locations.u_fill_translate)
16589});
16590const fillExtrusionPatternUniforms = (context, locations) => ({
16591	"u_lightpos": new Uniform3f(context, locations.u_lightpos),
16592	"u_lightpos_globe": new Uniform3f(context, locations.u_lightpos_globe),
16593	"u_lightintensity": new Uniform1f(context, locations.u_lightintensity),
16594	"u_lightcolor": new Uniform3f(context, locations.u_lightcolor),
16595	"u_vertical_gradient": new Uniform1f(context, locations.u_vertical_gradient),
16596	"u_height_factor": new Uniform1f(context, locations.u_height_factor),
16597	"u_opacity": new Uniform1f(context, locations.u_opacity),
16598	"u_fill_translate": new Uniform2f(context, locations.u_fill_translate),
16599	"u_image": new Uniform1i(context, locations.u_image),
16600	"u_texsize": new Uniform2f(context, locations.u_texsize),
16601	"u_pixel_coord_upper": new Uniform2f(context, locations.u_pixel_coord_upper),
16602	"u_pixel_coord_lower": new Uniform2f(context, locations.u_pixel_coord_lower),
16603	"u_scale": new Uniform3f(context, locations.u_scale),
16604	"u_fade": new Uniform1f(context, locations.u_fade)
16605});
16606const fillExtrusionUniformValues = (painter, shouldUseVerticalGradient, opacity, translate) => {
16607	const light = painter.style.light;
16608	const lightPos = light.getCartesianPosition();
16609	const lightMat = create$4();
16610	if (light.properties.get("anchor") === "viewport") fromRotation(lightMat, painter.transform.bearingInRadians);
16611	transformMat3(lightPos, lightPos, lightMat);
16612	const transformedLightPos = painter.transform.transformLightDirection(lightPos);
16613	const lightColor = light.properties.get("color");
16614	return {
16615		"u_lightpos": lightPos,
16616		"u_lightpos_globe": transformedLightPos,
16617		"u_lightintensity": light.properties.get("intensity"),
16618		"u_lightcolor": [
16619			lightColor.r,
16620			lightColor.g,
16621			lightColor.b
16622		],
16623		"u_vertical_gradient": +shouldUseVerticalGradient,
16624		"u_opacity": opacity,
16625		"u_fill_translate": translate
16626	};
16627};
16628const fillExtrusionPatternUniformValues = (painter, shouldUseVerticalGradient, opacity, translate, coord, crossfade, tile) => {
16629	return extend(fillExtrusionUniformValues(painter, shouldUseVerticalGradient, opacity, translate), patternUniformValues(crossfade, painter, tile), { "u_height_factor": -Math.pow(2, coord.overscaledZ) / tile.tileSize / 8 });
16630};
16631//#endregion
16632//#region src/webgl/program/fill_program.ts
16633const fillUniforms = (context, locations) => ({ "u_fill_translate": new Uniform2f(context, locations.u_fill_translate) });
16634const fillPatternUniforms = (context, locations) => ({
16635	"u_image": new Uniform1i(context, locations.u_image),
16636	"u_texsize": new Uniform2f(context, locations.u_texsize),
16637	"u_pixel_coord_upper": new Uniform2f(context, locations.u_pixel_coord_upper),
16638	"u_pixel_coord_lower": new Uniform2f(context, locations.u_pixel_coord_lower),
16639	"u_scale": new Uniform3f(context, locations.u_scale),
16640	"u_fade": new Uniform1f(context, locations.u_fade),
16641	"u_sdf_pattern": new Uniform1i(context, locations.u_sdf_pattern),
16642	"u_fill_translate": new Uniform2f(context, locations.u_fill_translate)
16643});
16644const fillOutlineUniforms = (context, locations) => ({ "u_fill_translate": new Uniform2f(context, locations.u_fill_translate) });
16645const fillOutlinePatternUniforms = (context, locations) => ({
16646	"u_image": new Uniform1i(context, locations.u_image),
16647	"u_texsize": new Uniform2f(context, locations.u_texsize),
16648	"u_pixel_coord_upper": new Uniform2f(context, locations.u_pixel_coord_upper),
16649	"u_pixel_coord_lower": new Uniform2f(context, locations.u_pixel_coord_lower),
16650	"u_scale": new Uniform3f(context, locations.u_scale),
16651	"u_fade": new Uniform1f(context, locations.u_fade),
16652	"u_sdf_pattern": new Uniform1i(context, locations.u_sdf_pattern),
16653	"u_fill_translate": new Uniform2f(context, locations.u_fill_translate)
16654});
16655const fillPatternUniformValues = (painter, crossfade, tile, translate, isSdfPattern) => extend(patternUniformValues(crossfade, painter, tile), {
16656	"u_fill_translate": translate,
16657	"u_sdf_pattern": isSdfPattern ? 1 : 0
16658});
16659const fillUniformValues = (translate) => ({ "u_fill_translate": translate });
16660const fillOutlineUniformValues = (translate) => ({ "u_fill_translate": translate });
16661const fillOutlinePatternUniformValues = (painter, crossfade, tile, translate, isSdfPattern) => fillPatternUniformValues(painter, crossfade, tile, translate, isSdfPattern);
16662//#endregion
16663//#region src/webgl/program/circle_program.ts
16664const circleUniforms = (context, locations) => ({
16665	"u_scale_with_map": new Uniform1i(context, locations.u_scale_with_map),
16666	"u_pitch_with_map": new Uniform1i(context, locations.u_pitch_with_map),
16667	"u_extrude_scale": new Uniform2f(context, locations.u_extrude_scale),
16668	"u_globe_extrude_scale": new Uniform1f(context, locations.u_globe_extrude_scale),
16669	"u_translate": new Uniform2f(context, locations.u_translate)
16670});
16671const circleUniformValues = (painter, tile, layer, translate, radiusCorrectionFactor) => {
16672	const transform = painter.transform;
16673	let pitchWithMap, extrudeScale;
16674	let globeExtrudeScale = 0;
16675	if (layer.paint.get("circle-pitch-alignment") === "map") {
16676		const pixelRatio = pixelsToTileUnits(tile, 1, transform.zoom);
16677		pitchWithMap = true;
16678		extrudeScale = [pixelRatio, pixelRatio];
16679		globeExtrudeScale = pixelRatio / (EXTENT * Math.pow(2, tile.tileID.overscaledZ)) * 2 * Math.PI * radiusCorrectionFactor;
16680	} else {
16681		pitchWithMap = false;
16682		extrudeScale = transform.pixelsToGLUnits;
16683	}
16684	return {
16685		"u_scale_with_map": +(layer.paint.get("circle-pitch-scale") === "map"),
16686		"u_pitch_with_map": +pitchWithMap,
16687		"u_extrude_scale": extrudeScale,
16688		"u_globe_extrude_scale": globeExtrudeScale,
16689		"u_translate": translate
16690	};
16691};
16692//#endregion
16693//#region src/webgl/program/debug_program.ts
16694const debugUniforms = (context, locations) => ({
16695	"u_color": new UniformColor(context, locations.u_color),
16696	"u_overlay": new Uniform1i(context, locations.u_overlay),
16697	"u_overlay_scale": new Uniform1f(context, locations.u_overlay_scale)
16698});
16699const debugUniformValues = (color, scaleRatio = 1) => ({
16700	"u_color": color,
16701	"u_overlay": 0,
16702	"u_overlay_scale": scaleRatio
16703});
16704//#endregion
16705//#region src/webgl/program/heatmap_program.ts
16706const heatmapUniforms = (context, locations) => ({
16707	"u_extrude_scale": new Uniform1f(context, locations.u_extrude_scale),
16708	"u_intensity": new Uniform1f(context, locations.u_intensity),
16709	"u_globe_extrude_scale": new Uniform1f(context, locations.u_globe_extrude_scale)
16710});
16711const heatmapTextureUniforms = (context, locations) => ({
16712	"u_matrix": new UniformMatrix4f(context, locations.u_matrix),
16713	"u_image": new Uniform1i(context, locations.u_image),
16714	"u_color_ramp": new Uniform1i(context, locations.u_color_ramp),
16715	"u_opacity": new Uniform1f(context, locations.u_opacity)
16716});
16717const heatmapUniformValues = (tile, zoom, intensity, radiusCorrectionFactor) => {
16718	const globeExtrudeScale = pixelsToTileUnits(tile, 1, zoom) / (EXTENT * Math.pow(2, tile.tileID.overscaledZ)) * 2 * Math.PI * radiusCorrectionFactor;
16719	return {
16720		"u_extrude_scale": pixelsToTileUnits(tile, 1, zoom),
16721		"u_intensity": intensity,
16722		"u_globe_extrude_scale": globeExtrudeScale
16723	};
16724};
16725const heatmapTextureUniformValues = (painter, layer, textureUnit, colorRampUnit) => {
16726	const matrix = create$1();
16727	ortho(matrix, 0, painter.width, painter.height, 0, 0, 1);
16728	return {
16729		"u_matrix": matrix,
16730		"u_image": textureUnit,
16731		"u_color_ramp": colorRampUnit,
16732		"u_opacity": layer.paint.get("heatmap-opacity")
16733	};
16734};
16735//#endregion
16736//#region src/webgl/program/hillshade_program.ts
16737const hillshadeUniforms = (context, locations) => ({
16738	"u_image": new Uniform1i(context, locations.u_image),
16739	"u_latrange": new Uniform2f(context, locations.u_latrange),
16740	"u_exaggeration": new Uniform1f(context, locations.u_exaggeration),
16741	"u_altitudes": new UniformFloatArray(context, locations.u_altitudes),
16742	"u_azimuths": new UniformFloatArray(context, locations.u_azimuths),
16743	"u_accent": new UniformColor(context, locations.u_accent),
16744	"u_method": new Uniform1i(context, locations.u_method),
16745	"u_shadows": new UniformColorArray(context, locations.u_shadows),
16746	"u_highlights": new UniformColorArray(context, locations.u_highlights)
16747});
16748const hillshadePrepareUniforms = (context, locations) => ({
16749	"u_matrix": new UniformMatrix4f(context, locations.u_matrix),
16750	"u_image": new Uniform1i(context, locations.u_image),
16751	"u_dimension": new Uniform2f(context, locations.u_dimension),
16752	"u_zoom": new Uniform1f(context, locations.u_zoom),
16753	"u_unpack": new Uniform4f(context, locations.u_unpack)
16754});
16755const hillshadeUniformValues = (painter, tile, layer) => {
16756	const accent = layer.paint.get("hillshade-accent-color");
16757	let method;
16758	switch (layer.paint.get("hillshade-method")) {
16759		case "basic":
16760			method = 4;
16761			break;
16762		case "combined":
16763			method = 1;
16764			break;
16765		case "igor":
16766			method = 2;
16767			break;
16768		case "multidirectional":
16769			method = 3;
16770			break;
16771		default: method = 0;
16772	}
16773	const illumination = layer.getIlluminationProperties();
16774	for (let i = 0; i < illumination.directionRadians.length; i++) if (layer.paint.get("hillshade-illumination-anchor") === "viewport") illumination.directionRadians[i] += painter.transform.bearingInRadians;
16775	return {
16776		"u_image": 0,
16777		"u_latrange": getTileLatRange(painter, tile.tileID),
16778		"u_exaggeration": layer.paint.get("hillshade-exaggeration"),
16779		"u_altitudes": illumination.altitudeRadians,
16780		"u_azimuths": illumination.directionRadians,
16781		"u_accent": accent,
16782		"u_method": method,
16783		"u_highlights": illumination.highlightColor,
16784		"u_shadows": illumination.shadowColor
16785	};
16786};
16787const hillshadeUniformPrepareValues = (tileID, dem) => {
16788	const stride = dem.stride;
16789	const matrix = create$1();
16790	ortho(matrix, 0, EXTENT, -EXTENT, 0, 0, 1);
16791	translate(matrix, matrix, [
16792		0,
16793		-EXTENT,
16794		0
16795	]);
16796	return {
16797		"u_matrix": matrix,
16798		"u_image": 1,
16799		"u_dimension": [stride, stride],
16800		"u_zoom": tileID.overscaledZ,
16801		"u_unpack": dem.getUnpackVector()
16802	};
16803};
16804function getTileLatRange(painter, tileID) {
16805	const tilesAtZoom = Math.pow(2, tileID.canonical.z);
16806	const y = tileID.canonical.y;
16807	return [new MercatorCoordinate(0, y / tilesAtZoom).toLngLat().lat, new MercatorCoordinate(0, (y + 1) / tilesAtZoom).toLngLat().lat];
16808}
16809//#endregion
16810//#region src/webgl/program/color_relief_program.ts
16811const colorReliefUniforms = (context, locations) => ({
16812	"u_image": new Uniform1i(context, locations.u_image),
16813	"u_unpack": new Uniform4f(context, locations.u_unpack),
16814	"u_dimension": new Uniform2f(context, locations.u_dimension),
16815	"u_elevation_stops": new Uniform1i(context, locations.u_elevation_stops),
16816	"u_color_stops": new Uniform1i(context, locations.u_color_stops),
16817	"u_color_ramp_size": new Uniform1i(context, locations.u_color_ramp_size),
16818	"u_opacity": new Uniform1f(context, locations.u_opacity)
16819});
16820const colorReliefUniformValues = (layer, dem, colorRampSize = 0) => {
16821	return {
16822		"u_image": 0,
16823		"u_unpack": dem.getUnpackVector(),
16824		"u_dimension": [dem.stride, dem.stride],
16825		"u_elevation_stops": 1,
16826		"u_color_stops": 4,
16827		"u_color_ramp_size": colorRampSize,
16828		"u_opacity": layer.paint.get("color-relief-opacity")
16829	};
16830};
16831//#endregion
16832//#region src/webgl/program/line_program.ts
16833const lineUniforms = (context, locations) => ({
16834	"u_translation": new Uniform2f(context, locations.u_translation),
16835	"u_ratio": new Uniform1f(context, locations.u_ratio)
16836});
16837const lineGradientUniforms = (context, locations) => ({
16838	"u_translation": new Uniform2f(context, locations.u_translation),
16839	"u_ratio": new Uniform1f(context, locations.u_ratio),
16840	"u_image": new Uniform1i(context, locations.u_image),
16841	"u_image_height": new Uniform1f(context, locations.u_image_height)
16842});
16843const linePatternUniforms = (context, locations) => ({
16844	"u_translation": new Uniform2f(context, locations.u_translation),
16845	"u_texsize": new Uniform2f(context, locations.u_texsize),
16846	"u_ratio": new Uniform1f(context, locations.u_ratio),
16847	"u_image": new Uniform1i(context, locations.u_image),
16848	"u_scale": new Uniform3f(context, locations.u_scale),
16849	"u_fade": new Uniform1f(context, locations.u_fade)
16850});
16851const lineSDFUniforms = (context, locations) => ({
16852	"u_translation": new Uniform2f(context, locations.u_translation),
16853	"u_ratio": new Uniform1f(context, locations.u_ratio),
16854	"u_image": new Uniform1i(context, locations.u_image),
16855	"u_mix": new Uniform1f(context, locations.u_mix),
16856	"u_tileratio": new Uniform1f(context, locations.u_tileratio),
16857	"u_crossfade_from": new Uniform1f(context, locations.u_crossfade_from),
16858	"u_crossfade_to": new Uniform1f(context, locations.u_crossfade_to),
16859	"u_lineatlas_width": new Uniform1f(context, locations.u_lineatlas_width),
16860	"u_lineatlas_height": new Uniform1f(context, locations.u_lineatlas_height)
16861});
16862const lineGradientSDFUniforms = (context, locations) => ({
16863	"u_translation": new Uniform2f(context, locations.u_translation),
16864	"u_ratio": new Uniform1f(context, locations.u_ratio),
16865	"u_image": new Uniform1i(context, locations.u_image),
16866	"u_image_height": new Uniform1f(context, locations.u_image_height),
16867	"u_tileratio": new Uniform1f(context, locations.u_tileratio),
16868	"u_crossfade_from": new Uniform1f(context, locations.u_crossfade_from),
16869	"u_crossfade_to": new Uniform1f(context, locations.u_crossfade_to),
16870	"u_image_dash": new Uniform1i(context, locations.u_image_dash),
16871	"u_mix": new Uniform1f(context, locations.u_mix),
16872	"u_lineatlas_width": new Uniform1f(context, locations.u_lineatlas_width),
16873	"u_lineatlas_height": new Uniform1f(context, locations.u_lineatlas_height)
16874});
16875const lineUniformValues = (painter, tile, layer, ratioScale) => {
16876	const transform = painter.transform;
16877	return {
16878		"u_translation": calculateTranslation(painter, tile, layer),
16879		"u_ratio": ratioScale / pixelsToTileUnits(tile, 1, transform.zoom)
16880	};
16881};
16882const lineGradientUniformValues = (painter, tile, layer, ratioScale, imageHeight) => {
16883	return extend(lineUniformValues(painter, tile, layer, ratioScale), {
16884		"u_image": 0,
16885		"u_image_height": imageHeight
16886	});
16887};
16888const linePatternUniformValues = (painter, tile, layer, ratioScale, crossfade) => {
16889	const transform = painter.transform;
16890	const tileZoomRatio = calculateTileRatio(tile, transform);
16891	return {
16892		"u_translation": calculateTranslation(painter, tile, layer),
16893		"u_texsize": tile.imageAtlasTexture.size,
16894		"u_ratio": ratioScale / pixelsToTileUnits(tile, 1, transform.zoom),
16895		"u_image": 0,
16896		"u_scale": [
16897			tileZoomRatio,
16898			crossfade.fromScale,
16899			crossfade.toScale
16900		],
16901		"u_fade": crossfade.t
16902	};
16903};
16904const lineSDFUniformValues = (painter, tile, layer, ratioScale, crossfade) => {
16905	const transform = painter.transform;
16906	const tileRatio = calculateTileRatio(tile, transform);
16907	return extend(lineUniformValues(painter, tile, layer, ratioScale), {
16908		"u_tileratio": tileRatio,
16909		"u_crossfade_from": crossfade.fromScale,
16910		"u_crossfade_to": crossfade.toScale,
16911		"u_image": 0,
16912		"u_mix": crossfade.t,
16913		"u_lineatlas_width": painter.lineAtlas.width,
16914		"u_lineatlas_height": painter.lineAtlas.height
16915	});
16916};
16917const lineGradientSDFUniformValues = (painter, tile, layer, ratioScale, crossfade, imageHeight) => {
16918	const transform = painter.transform;
16919	const tileRatio = calculateTileRatio(tile, transform);
16920	return extend(lineUniformValues(painter, tile, layer, ratioScale), {
16921		"u_image": 0,
16922		"u_image_height": imageHeight,
16923		"u_tileratio": tileRatio,
16924		"u_crossfade_from": crossfade.fromScale,
16925		"u_crossfade_to": crossfade.toScale,
16926		"u_image_dash": 1,
16927		"u_mix": crossfade.t,
16928		"u_lineatlas_width": painter.lineAtlas.width,
16929		"u_lineatlas_height": painter.lineAtlas.height
16930	});
16931};
16932function calculateTileRatio(tile, transform) {
16933	return 1 / pixelsToTileUnits(tile, 1, transform.tileZoom);
16934}
16935function calculateTranslation(painter, tile, layer) {
16936	return translatePosition(painter.transform, tile, layer.paint.get("line-translate"), layer.paint.get("line-translate-anchor"));
16937}
16938//#endregion
16939//#region src/webgl/program/layer_opacity_program.ts
16940const layerOpacityUniforms = (context, locations) => ({
16941	"u_image": new Uniform1i(context, locations.u_image),
16942	"u_opacity": new Uniform1f(context, locations.u_opacity)
16943});
16944const layerOpacityUniformValues = (opacity, textureUnit) => ({
16945	"u_image": textureUnit,
16946	"u_opacity": opacity
16947});
16948//#endregion
16949//#region src/webgl/program/symbol_program.ts
16950const symbolIconUniforms = (context, locations) => ({
16951	"u_is_size_zoom_constant": new Uniform1i(context, locations.u_is_size_zoom_constant),
16952	"u_is_size_feature_constant": new Uniform1i(context, locations.u_is_size_feature_constant),
16953	"u_size_t": new Uniform1f(context, locations.u_size_t),
16954	"u_size": new Uniform1f(context, locations.u_size),
16955	"u_rotate_symbol": new Uniform1i(context, locations.u_rotate_symbol),
16956	"u_label_plane_matrix": new UniformMatrix4f(context, locations.u_label_plane_matrix),
16957	"u_coord_matrix": new UniformMatrix4f(context, locations.u_coord_matrix),
16958	"u_is_text": new Uniform1i(context, locations.u_is_text),
16959	"u_pitch_with_map": new Uniform1i(context, locations.u_pitch_with_map),
16960	"u_is_along_line": new Uniform1i(context, locations.u_is_along_line),
16961	"u_is_variable_anchor": new Uniform1i(context, locations.u_is_variable_anchor),
16962	"u_texsize": new Uniform2f(context, locations.u_texsize),
16963	"u_texture": new Uniform1i(context, locations.u_texture),
16964	"u_translation": new Uniform2f(context, locations.u_translation),
16965	"u_pitched_scale": new Uniform1f(context, locations.u_pitched_scale),
16966	"u_is_offset": new Uniform1i(context, locations.u_is_offset),
16967	"u_height_anchor_ground": new Uniform1i(context, locations.u_height_anchor_ground)
16968});
16969const symbolSDFUniforms = (context, locations) => ({
16970	"u_is_size_zoom_constant": new Uniform1i(context, locations.u_is_size_zoom_constant),
16971	"u_is_size_feature_constant": new Uniform1i(context, locations.u_is_size_feature_constant),
16972	"u_size_t": new Uniform1f(context, locations.u_size_t),
16973	"u_size": new Uniform1f(context, locations.u_size),
16974	"u_rotate_symbol": new Uniform1i(context, locations.u_rotate_symbol),
16975	"u_label_plane_matrix": new UniformMatrix4f(context, locations.u_label_plane_matrix),
16976	"u_coord_matrix": new UniformMatrix4f(context, locations.u_coord_matrix),
16977	"u_is_text": new Uniform1i(context, locations.u_is_text),
16978	"u_pitch_with_map": new Uniform1i(context, locations.u_pitch_with_map),
16979	"u_is_along_line": new Uniform1i(context, locations.u_is_along_line),
16980	"u_is_variable_anchor": new Uniform1i(context, locations.u_is_variable_anchor),
16981	"u_texsize": new Uniform2f(context, locations.u_texsize),
16982	"u_texture": new Uniform1i(context, locations.u_texture),
16983	"u_gamma_scale": new Uniform1f(context, locations.u_gamma_scale),
16984	"u_is_halo": new Uniform1i(context, locations.u_is_halo),
16985	"u_is_plain": new Uniform1i(context, locations.u_is_plain),
16986	"u_translation": new Uniform2f(context, locations.u_translation),
16987	"u_pitched_scale": new Uniform1f(context, locations.u_pitched_scale),
16988	"u_is_offset": new Uniform1i(context, locations.u_is_offset),
16989	"u_height_anchor_ground": new Uniform1i(context, locations.u_height_anchor_ground)
16990});
16991const symbolTextAndIconUniforms = (context, locations) => ({
16992	"u_is_size_zoom_constant": new Uniform1i(context, locations.u_is_size_zoom_constant),
16993	"u_is_size_feature_constant": new Uniform1i(context, locations.u_is_size_feature_constant),
16994	"u_size_t": new Uniform1f(context, locations.u_size_t),
16995	"u_size": new Uniform1f(context, locations.u_size),
16996	"u_rotate_symbol": new Uniform1i(context, locations.u_rotate_symbol),
16997	"u_label_plane_matrix": new UniformMatrix4f(context, locations.u_label_plane_matrix),
16998	"u_coord_matrix": new UniformMatrix4f(context, locations.u_coord_matrix),
16999	"u_is_text": new Uniform1i(context, locations.u_is_text),
17000	"u_pitch_with_map": new Uniform1i(context, locations.u_pitch_with_map),
17001	"u_is_along_line": new Uniform1i(context, locations.u_is_along_line),
17002	"u_is_variable_anchor": new Uniform1i(context, locations.u_is_variable_anchor),
17003	"u_texsize": new Uniform2f(context, locations.u_texsize),
17004	"u_texsize_icon": new Uniform2f(context, locations.u_texsize_icon),
17005	"u_texture": new Uniform1i(context, locations.u_texture),
17006	"u_texture_icon": new Uniform1i(context, locations.u_texture_icon),
17007	"u_gamma_scale": new Uniform1f(context, locations.u_gamma_scale),
17008	"u_is_halo": new Uniform1i(context, locations.u_is_halo),
17009	"u_translation": new Uniform2f(context, locations.u_translation),
17010	"u_pitched_scale": new Uniform1f(context, locations.u_pitched_scale),
17011	"u_is_offset": new Uniform1i(context, locations.u_is_offset),
17012	"u_height_anchor_ground": new Uniform1i(context, locations.u_height_anchor_ground)
17013});
17014const symbolIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, pitchedScale, isOffset, heightAnchorGround) => {
17015	return {
17016		"u_is_size_zoom_constant": +(functionType === "constant" || functionType === "source"),
17017		"u_is_size_feature_constant": +(functionType === "constant" || functionType === "camera"),
17018		"u_size_t": size ? size.uSizeT : 0,
17019		"u_size": size ? size.uSize : 0,
17020		"u_rotate_symbol": +rotateInShader,
17021		"u_label_plane_matrix": labelPlaneMatrix,
17022		"u_coord_matrix": glCoordMatrix,
17023		"u_is_text": +isText,
17024		"u_pitch_with_map": +pitchWithMap,
17025		"u_is_along_line": isAlongLine,
17026		"u_is_variable_anchor": isVariableAnchor,
17027		"u_texsize": texSize,
17028		"u_texture": 0,
17029		"u_translation": translation,
17030		"u_pitched_scale": pitchedScale,
17031		"u_is_offset": isOffset,
17032		"u_height_anchor_ground": +heightAnchorGround
17033	};
17034};
17035const symbolSDFUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, isHalo, pitchedScale, isOffset, heightAnchorGround) => {
17036	const transform = painter.transform;
17037	return extend(symbolIconUniformValues(functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, pitchedScale, isOffset, heightAnchorGround), {
17038		"u_gamma_scale": pitchWithMap ? Math.cos(transform.pitch * Math.PI / 180) * transform.cameraToCenterDistance : 1,
17039		"u_is_halo": isHalo ? 1 : 0,
17040		"u_is_plain": 1
17041	});
17042};
17043const symbolTextAndIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, texSizeSDF, texSizeIcon, pitchedScale, isOffset, heightAnchorGround) => {
17044	return extend(symbolSDFUniformValues(functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, true, texSizeSDF, true, pitchedScale, isOffset, heightAnchorGround), {
17045		"u_texsize_icon": texSizeIcon,
17046		"u_texture_icon": 1
17047	});
17048};
17049//#endregion
17050//#region src/webgl/program/background_program.ts
17051const backgroundUniforms = (context, locations) => ({
17052	"u_opacity": new Uniform1f(context, locations.u_opacity),
17053	"u_color": new UniformColor(context, locations.u_color)
17054});
17055const backgroundPatternUniforms = (context, locations) => ({
17056	"u_opacity": new Uniform1f(context, locations.u_opacity),
17057	"u_image": new Uniform1i(context, locations.u_image),
17058	"u_pattern_tl_a": new Uniform2f(context, locations.u_pattern_tl_a),
17059	"u_pattern_br_a": new Uniform2f(context, locations.u_pattern_br_a),
17060	"u_pattern_tl_b": new Uniform2f(context, locations.u_pattern_tl_b),
17061	"u_pattern_br_b": new Uniform2f(context, locations.u_pattern_br_b),
17062	"u_texsize": new Uniform2f(context, locations.u_texsize),
17063	"u_mix": new Uniform1f(context, locations.u_mix),
17064	"u_pattern_size_a": new Uniform2f(context, locations.u_pattern_size_a),
17065	"u_pattern_size_b": new Uniform2f(context, locations.u_pattern_size_b),
17066	"u_scale_a": new Uniform1f(context, locations.u_scale_a),
17067	"u_scale_b": new Uniform1f(context, locations.u_scale_b),
17068	"u_pixel_coord_upper": new Uniform2f(context, locations.u_pixel_coord_upper),
17069	"u_pixel_coord_lower": new Uniform2f(context, locations.u_pixel_coord_lower),
17070	"u_tile_units_to_pixels": new Uniform1f(context, locations.u_tile_units_to_pixels)
17071});
17072const backgroundUniformValues = (opacity, color) => ({
17073	"u_opacity": opacity,
17074	"u_color": color
17075});
17076const backgroundPatternUniformValues = (opacity, painter, image, tile, crossfade) => extend(bgPatternUniformValues(image, crossfade, painter, tile), { "u_opacity": opacity });
17077//#endregion
17078//#region src/webgl/program/atmosphere_program.ts
17079const atmosphereUniforms = (context, locations) => ({
17080	"u_sun_pos": new Uniform3f(context, locations.u_sun_pos),
17081	"u_atmosphere_blend": new Uniform1f(context, locations.u_atmosphere_blend),
17082	"u_globe_position": new Uniform3f(context, locations.u_globe_position),
17083	"u_globe_radius": new Uniform1f(context, locations.u_globe_radius),
17084	"u_inv_proj_matrix": new UniformMatrix4f(context, locations.u_inv_proj_matrix)
17085});
17086const atmosphereUniformValues = (sunPos, atmosphereBlend, globePosition, globeRadius, invProjMatrix) => ({
17087	"u_sun_pos": sunPos,
17088	"u_atmosphere_blend": atmosphereBlend,
17089	"u_globe_position": globePosition,
17090	"u_globe_radius": globeRadius,
17091	"u_inv_proj_matrix": invProjMatrix
17092});
17093//#endregion
17094//#region src/webgl/program/sky_program.ts
17095const skyUniforms = (context, locations) => ({
17096	"u_sky_color": new UniformColor(context, locations.u_sky_color),
17097	"u_horizon_color": new UniformColor(context, locations.u_horizon_color),
17098	"u_horizon": new Uniform2f(context, locations.u_horizon),
17099	"u_horizon_normal": new Uniform2f(context, locations.u_horizon_normal),
17100	"u_sky_horizon_blend": new Uniform1f(context, locations.u_sky_horizon_blend),
17101	"u_sky_blend": new Uniform1f(context, locations.u_sky_blend),
17102	"u_inv_proj_matrix": new UniformMatrix4f(context, locations.u_inv_proj_matrix),
17103	"u_globe_position": new Uniform3f(context, locations.u_globe_position),
17104	"u_globe_radius": new Uniform1f(context, locations.u_globe_radius),
17105	"u_atmosphere_blend": new Uniform1f(context, locations.u_atmosphere_blend)
17106});
17107const skyUniformValues = (sky, transform, pixelRatio) => {
17108	const cosRoll = Math.cos(transform.rollInRadians);
17109	const sinRoll = Math.sin(transform.rollInRadians);
17110	const mercatorHorizon = getMercatorHorizon(transform);
17111	const skyBlend = transform.getProjectionData({
17112		overscaledTileID: null,
17113		applyGlobeMatrix: true,
17114		applyTerrainMatrix: true
17115	}).projectionTransition;
17116	const globePosition = getGlobeCenterInViewSpace(transform);
17117	const globeRadius = getGlobeRadiusPixels(transform.worldSize, transform.center.lat);
17118	return {
17119		"u_sky_color": sky.properties.get("sky-color"),
17120		"u_horizon_color": sky.properties.get("horizon-color"),
17121		"u_horizon": [(transform.width / 2 - mercatorHorizon * sinRoll) * pixelRatio, (transform.height / 2 + mercatorHorizon * cosRoll) * pixelRatio],
17122		"u_horizon_normal": [-sinRoll, cosRoll],
17123		"u_sky_horizon_blend": sky.properties.get("sky-horizon-blend") * transform.height / 2 * pixelRatio,
17124		"u_sky_blend": skyBlend,
17125		"u_inv_proj_matrix": transform.inverseProjectionMatrix,
17126		"u_globe_position": globePosition,
17127		"u_globe_radius": globeRadius,
17128		"u_atmosphere_blend": sky.properties.get("atmosphere-blend") * getAtmosphereAltitudeBlend(length(globePosition) - globeRadius, globeRadius)
17129	};
17130};
17131//#endregion
17132//#region src/webgl/program/program_uniforms.ts
17133const emptyUniforms = (_, __) => ({});
17134const programUniforms = {
17135	fillExtrusion: fillExtrusionUniforms,
17136	fillExtrusionPattern: fillExtrusionPatternUniforms,
17137	fill: fillUniforms,
17138	fillPattern: fillPatternUniforms,
17139	fillOutline: fillOutlineUniforms,
17140	fillOutlinePattern: fillOutlinePatternUniforms,
17141	circle: circleUniforms,
17142	collisionBox: emptyUniforms,
17143	collisionCircle: emptyUniforms,
17144	debug: debugUniforms,
17145	depth: emptyUniforms,
17146	clippingMask: emptyUniforms,
17147	heatmap: heatmapUniforms,
17148	heatmapTexture: heatmapTextureUniforms,
17149	hillshade: hillshadeUniforms,
17150	hillshadePrepare: hillshadePrepareUniforms,
17151	colorRelief: colorReliefUniforms,
17152	line: lineUniforms,
17153	lineGradient: lineGradientUniforms,
17154	linePattern: linePatternUniforms,
17155	lineSDF: lineSDFUniforms,
17156	lineGradientSDF: lineGradientSDFUniforms,
17157	layerOpacity: layerOpacityUniforms,
17158	raster: rasterUniforms,
17159	symbolIcon: symbolIconUniforms,
17160	symbolSDF: symbolSDFUniforms,
17161	symbolTextAndIcon: symbolTextAndIconUniforms,
17162	background: backgroundUniforms,
17163	backgroundPattern: backgroundPatternUniforms,
17164	terrain: terrainUniforms,
17165	terrainDepth: terrainDepthUniforms,
17166	atmosphere: atmosphereUniforms,
17167	sky: skyUniforms
17168};
17169//#endregion
17170//#region src/webgl/index_buffer.ts
17171/**
17172* @internal
17173* an index buffer class
17174*/
17175var IndexBuffer = class {
17176	constructor(context, array, dynamicDraw) {
17177		this.context = context;
17178		const gl = context.gl;
17179		this.buffer = gl.createBuffer();
17180		this.dynamicDraw = Boolean(dynamicDraw);
17181		this.context.unbindVAO();
17182		context.bindElementBuffer.set(this.buffer);
17183		gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, array.arrayBuffer, this.dynamicDraw ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW);
17184		if (!this.dynamicDraw) array.freeBufferAfterUpload();
17185	}
17186	bind() {
17187		this.context.bindElementBuffer.set(this.buffer);
17188	}
17189	updateData(array) {
17190		const gl = this.context.gl;
17191		if (!this.dynamicDraw) throw new Error("Attempted to update data while not in dynamic mode.");
17192		this.context.unbindVAO();
17193		this.bind();
17194		gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, array.arrayBuffer);
17195	}
17196	destroy() {
17197		const gl = this.context.gl;
17198		if (this.buffer) {
17199			gl.deleteBuffer(this.buffer);
17200			delete this.buffer;
17201		}
17202	}
17203};
17204//#endregion
17205//#region src/webgl/vertex_buffer.ts
17206/**
17207* An Enum for AttributeType
17208*/
17209const AttributeType = {
17210	Int8: "BYTE",
17211	Uint8: "UNSIGNED_BYTE",
17212	Int16: "SHORT",
17213	Uint16: "UNSIGNED_SHORT",
17214	Int32: "INT",
17215	Uint32: "UNSIGNED_INT",
17216	Float32: "FLOAT"
17217};
17218/**
17219* @internal
17220* The `VertexBuffer` class turns a `StructArray` into a WebGL buffer. Each member of the StructArray's
17221* Struct type is converted to a WebGL attribute.
17222*/
17223var VertexBuffer = class {
17224	/**
17225	* @param dynamicDraw - Whether this buffer will be repeatedly updated.
17226	*/
17227	constructor(context, array, attributes, dynamicDraw) {
17228		this.length = array.length;
17229		this.attributes = attributes;
17230		this.itemSize = array.bytesPerElement;
17231		this.dynamicDraw = dynamicDraw;
17232		this.context = context;
17233		const gl = context.gl;
17234		this.buffer = gl.createBuffer();
17235		context.bindVertexBuffer.set(this.buffer);
17236		gl.bufferData(gl.ARRAY_BUFFER, array.arrayBuffer, this.dynamicDraw ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW);
17237		if (!this.dynamicDraw) array.freeBufferAfterUpload();
17238	}
17239	bind() {
17240		this.context.bindVertexBuffer.set(this.buffer);
17241	}
17242	updateData(array) {
17243		if (array.length !== this.length) throw new Error(`Length of new data is ${array.length}, which doesn't match current length of ${this.length}`);
17244		const gl = this.context.gl;
17245		this.bind();
17246		gl.bufferSubData(gl.ARRAY_BUFFER, 0, array.arrayBuffer);
17247	}
17248	enableAttributes(gl, program) {
17249		for (const member of this.attributes) {
17250			const attribute = program.attributes[member.name];
17251			if (attribute !== void 0) gl.enableVertexAttribArray(attribute.location);
17252		}
17253	}
17254	/**
17255	* Set the attribute pointers in a WebGL context
17256	* @param gl - The WebGL context
17257	* @param program - The active WebGL program
17258	* @param vertexOffset - Index of the starting vertex of the segment
17259	*/
17260	setVertexAttribPointers(gl, program, vertexOffset) {
17261		for (const member of this.attributes) {
17262			const attribute = program.attributes[member.name];
17263			if (attribute !== void 0) {
17264				const offset = member.offset + this.itemSize * (vertexOffset || 0);
17265				if (attribute.isInteger) gl.vertexAttribIPointer(attribute.location, member.components, gl[AttributeType[member.type]], this.itemSize, offset);
17266				else gl.vertexAttribPointer(attribute.location, member.components, gl[AttributeType[member.type]], false, this.itemSize, offset);
17267			}
17268		}
17269	}
17270	/**
17271	* Destroy the GL buffer bound to the given WebGL context
17272	*/
17273	destroy() {
17274		const gl = this.context.gl;
17275		if (this.buffer) {
17276			gl.deleteBuffer(this.buffer);
17277			delete this.buffer;
17278		}
17279	}
17280};
17281//#endregion
17282//#region src/webgl/value.ts
17283var BaseValue = class {
17284	constructor(context) {
17285		this.gl = context.gl;
17286		this.default = this.getDefault();
17287		this.current = this.default;
17288		this.dirty = false;
17289	}
17290	get() {
17291		return this.current;
17292	}
17293	set(value) {}
17294	getDefault() {
17295		return this.default;
17296	}
17297	setDefault() {
17298		this.set(this.default);
17299	}
17300};
17301var ClearColor = class extends BaseValue {
17302	getDefault() {
17303		return Color.transparent;
17304	}
17305	set(v) {
17306		const c = this.current;
17307		if (v.r === c.r && v.g === c.g && v.b === c.b && v.a === c.a && !this.dirty) return;
17308		this.gl.clearColor(v.r, v.g, v.b, v.a);
17309		this.current = v;
17310		this.dirty = false;
17311	}
17312};
17313var ClearDepth = class extends BaseValue {
17314	getDefault() {
17315		return 1;
17316	}
17317	set(v) {
17318		if (v === this.current && !this.dirty) return;
17319		this.gl.clearDepth(v);
17320		this.current = v;
17321		this.dirty = false;
17322	}
17323};
17324var ClearStencil = class extends BaseValue {
17325	getDefault() {
17326		return 0;
17327	}
17328	set(v) {
17329		if (v === this.current && !this.dirty) return;
17330		this.gl.clearStencil(v);
17331		this.current = v;
17332		this.dirty = false;
17333	}
17334};
17335var ColorMask = class extends BaseValue {
17336	getDefault() {
17337		return [
17338			true,
17339			true,
17340			true,
17341			true
17342		];
17343	}
17344	set(v) {
17345		const c = this.current;
17346		if (v[0] === c[0] && v[1] === c[1] && v[2] === c[2] && v[3] === c[3] && !this.dirty) return;
17347		this.gl.colorMask(v[0], v[1], v[2], v[3]);
17348		this.current = v;
17349		this.dirty = false;
17350	}
17351};
17352var DepthMask = class extends BaseValue {
17353	getDefault() {
17354		return true;
17355	}
17356	set(v) {
17357		if (v === this.current && !this.dirty) return;
17358		this.gl.depthMask(v);
17359		this.current = v;
17360		this.dirty = false;
17361	}
17362};
17363var StencilMask = class extends BaseValue {
17364	getDefault() {
17365		return 255;
17366	}
17367	set(v) {
17368		if (v === this.current && !this.dirty) return;
17369		this.gl.stencilMask(v);
17370		this.current = v;
17371		this.dirty = false;
17372	}
17373};
17374var StencilFunc = class extends BaseValue {
17375	getDefault() {
17376		return {
17377			func: this.gl.ALWAYS,
17378			ref: 0,
17379			mask: 255
17380		};
17381	}
17382	set(v) {
17383		const c = this.current;
17384		if (v.func === c.func && v.ref === c.ref && v.mask === c.mask && !this.dirty) return;
17385		this.gl.stencilFunc(v.func, v.ref, v.mask);
17386		this.current = v;
17387		this.dirty = false;
17388	}
17389};
17390var StencilOp = class extends BaseValue {
17391	getDefault() {
17392		const gl = this.gl;
17393		return [
17394			gl.KEEP,
17395			gl.KEEP,
17396			gl.KEEP
17397		];
17398	}
17399	set(v) {
17400		const c = this.current;
17401		if (v[0] === c[0] && v[1] === c[1] && v[2] === c[2] && !this.dirty) return;
17402		this.gl.stencilOp(v[0], v[1], v[2]);
17403		this.current = v;
17404		this.dirty = false;
17405	}
17406};
17407var StencilTest = class extends BaseValue {
17408	getDefault() {
17409		return false;
17410	}
17411	set(v) {
17412		if (v === this.current && !this.dirty) return;
17413		const gl = this.gl;
17414		if (v) gl.enable(gl.STENCIL_TEST);
17415		else gl.disable(gl.STENCIL_TEST);
17416		this.current = v;
17417		this.dirty = false;
17418	}
17419};
17420var DepthRange = class extends BaseValue {
17421	getDefault() {
17422		return [0, 1];
17423	}
17424	set(v) {
17425		const c = this.current;
17426		if (v[0] === c[0] && v[1] === c[1] && !this.dirty) return;
17427		this.gl.depthRange(v[0], v[1]);
17428		this.current = v;
17429		this.dirty = false;
17430	}
17431};
17432var DepthTest = class extends BaseValue {
17433	getDefault() {
17434		return false;
17435	}
17436	set(v) {
17437		if (v === this.current && !this.dirty) return;
17438		const gl = this.gl;
17439		if (v) gl.enable(gl.DEPTH_TEST);
17440		else gl.disable(gl.DEPTH_TEST);
17441		this.current = v;
17442		this.dirty = false;
17443	}
17444};
17445var DepthFunc = class extends BaseValue {
17446	getDefault() {
17447		return this.gl.LESS;
17448	}
17449	set(v) {
17450		if (v === this.current && !this.dirty) return;
17451		this.gl.depthFunc(v);
17452		this.current = v;
17453		this.dirty = false;
17454	}
17455};
17456var Blend = class extends BaseValue {
17457	getDefault() {
17458		return false;
17459	}
17460	set(v) {
17461		if (v === this.current && !this.dirty) return;
17462		const gl = this.gl;
17463		if (v) gl.enable(gl.BLEND);
17464		else gl.disable(gl.BLEND);
17465		this.current = v;
17466		this.dirty = false;
17467	}
17468};
17469var BlendFunc = class extends BaseValue {
17470	getDefault() {
17471		const gl = this.gl;
17472		return [gl.ONE, gl.ZERO];
17473	}
17474	set(v) {
17475		const c = this.current;
17476		if (v[0] === c[0] && v[1] === c[1] && !this.dirty) return;
17477		this.gl.blendFunc(v[0], v[1]);
17478		this.current = v;
17479		this.dirty = false;
17480	}
17481};
17482var BlendColor = class extends BaseValue {
17483	getDefault() {
17484		return Color.transparent;
17485	}
17486	set(v) {
17487		const c = this.current;
17488		if (v.r === c.r && v.g === c.g && v.b === c.b && v.a === c.a && !this.dirty) return;
17489		this.gl.blendColor(v.r, v.g, v.b, v.a);
17490		this.current = v;
17491		this.dirty = false;
17492	}
17493};
17494var BlendEquation = class extends BaseValue {
17495	getDefault() {
17496		return this.gl.FUNC_ADD;
17497	}
17498	set(v) {
17499		if (v === this.current && !this.dirty) return;
17500		this.gl.blendEquation(v);
17501		this.current = v;
17502		this.dirty = false;
17503	}
17504};
17505var CullFace = class extends BaseValue {
17506	getDefault() {
17507		return false;
17508	}
17509	set(v) {
17510		if (v === this.current && !this.dirty) return;
17511		const gl = this.gl;
17512		if (v) gl.enable(gl.CULL_FACE);
17513		else gl.disable(gl.CULL_FACE);
17514		this.current = v;
17515		this.dirty = false;
17516	}
17517};
17518var CullFaceSide = class extends BaseValue {
17519	getDefault() {
17520		return this.gl.BACK;
17521	}
17522	set(v) {
17523		if (v === this.current && !this.dirty) return;
17524		this.gl.cullFace(v);
17525		this.current = v;
17526		this.dirty = false;
17527	}
17528};
17529var FrontFace = class extends BaseValue {
17530	getDefault() {
17531		return this.gl.CCW;
17532	}
17533	set(v) {
17534		if (v === this.current && !this.dirty) return;
17535		this.gl.frontFace(v);
17536		this.current = v;
17537		this.dirty = false;
17538	}
17539};
17540var ProgramValue = class extends BaseValue {
17541	getDefault() {
17542		return null;
17543	}
17544	set(v) {
17545		if (v === this.current && !this.dirty) return;
17546		this.gl.useProgram(v);
17547		this.current = v;
17548		this.dirty = false;
17549	}
17550};
17551var ActiveTextureUnit = class extends BaseValue {
17552	getDefault() {
17553		return this.gl.TEXTURE0;
17554	}
17555	set(v) {
17556		if (v === this.current && !this.dirty) return;
17557		this.gl.activeTexture(v);
17558		this.current = v;
17559		this.dirty = false;
17560	}
17561};
17562var Viewport = class extends BaseValue {
17563	getDefault() {
17564		const gl = this.gl;
17565		return [
17566			0,
17567			0,
17568			gl.drawingBufferWidth,
17569			gl.drawingBufferHeight
17570		];
17571	}
17572	set(v) {
17573		const c = this.current;
17574		if (v[0] === c[0] && v[1] === c[1] && v[2] === c[2] && v[3] === c[3] && !this.dirty) return;
17575		this.gl.viewport(v[0], v[1], v[2], v[3]);
17576		this.current = v;
17577		this.dirty = false;
17578	}
17579};
17580var BindFramebuffer = class extends BaseValue {
17581	getDefault() {
17582		return null;
17583	}
17584	set(v) {
17585		if (v === this.current && !this.dirty) return;
17586		const gl = this.gl;
17587		gl.bindFramebuffer(gl.FRAMEBUFFER, v);
17588		this.current = v;
17589		this.dirty = false;
17590	}
17591};
17592var BindRenderbuffer = class extends BaseValue {
17593	getDefault() {
17594		return null;
17595	}
17596	set(v) {
17597		if (v === this.current && !this.dirty) return;
17598		const gl = this.gl;
17599		gl.bindRenderbuffer(gl.RENDERBUFFER, v);
17600		this.current = v;
17601		this.dirty = false;
17602	}
17603};
17604var BindTexture = class extends BaseValue {
17605	getDefault() {
17606		return null;
17607	}
17608	set(v) {
17609		if (v === this.current && !this.dirty) return;
17610		const gl = this.gl;
17611		gl.bindTexture(gl.TEXTURE_2D, v);
17612		this.current = v;
17613		this.dirty = false;
17614	}
17615};
17616var BindVertexBuffer = class extends BaseValue {
17617	getDefault() {
17618		return null;
17619	}
17620	set(v) {
17621		if (v === this.current && !this.dirty) return;
17622		const gl = this.gl;
17623		gl.bindBuffer(gl.ARRAY_BUFFER, v);
17624		this.current = v;
17625		this.dirty = false;
17626	}
17627};
17628var BindElementBuffer = class extends BaseValue {
17629	getDefault() {
17630		return null;
17631	}
17632	set(v) {
17633		const gl = this.gl;
17634		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, v);
17635		this.current = v;
17636		this.dirty = false;
17637	}
17638};
17639var BindVertexArray = class extends BaseValue {
17640	getDefault() {
17641		return null;
17642	}
17643	set(v) {
17644		if (v === this.current && !this.dirty) return;
17645		this.gl.bindVertexArray(v);
17646		this.current = v;
17647		this.dirty = false;
17648	}
17649};
17650var PixelStoreUnpack = class extends BaseValue {
17651	getDefault() {
17652		return 4;
17653	}
17654	set(v) {
17655		if (v === this.current && !this.dirty) return;
17656		const gl = this.gl;
17657		gl.pixelStorei(gl.UNPACK_ALIGNMENT, v);
17658		this.current = v;
17659		this.dirty = false;
17660	}
17661};
17662var PixelStoreUnpackPremultiplyAlpha = class extends BaseValue {
17663	getDefault() {
17664		return false;
17665	}
17666	set(v) {
17667		if (v === this.current && !this.dirty) return;
17668		const gl = this.gl;
17669		gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, v);
17670		this.current = v;
17671		this.dirty = false;
17672	}
17673};
17674var PixelStoreUnpackFlipY = class extends BaseValue {
17675	getDefault() {
17676		return false;
17677	}
17678	set(v) {
17679		if (v === this.current && !this.dirty) return;
17680		const gl = this.gl;
17681		gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, v);
17682		this.current = v;
17683		this.dirty = false;
17684	}
17685};
17686var FramebufferAttachment = class extends BaseValue {
17687	constructor(context, parent) {
17688		super(context);
17689		this.context = context;
17690		this.parent = parent;
17691	}
17692	getDefault() {
17693		return null;
17694	}
17695};
17696var ColorAttachment = class extends FramebufferAttachment {
17697	setDirty() {
17698		this.dirty = true;
17699	}
17700	set(v) {
17701		if (v === this.current && !this.dirty) return;
17702		this.context.bindFramebuffer.set(this.parent);
17703		const gl = this.gl;
17704		gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, v, 0);
17705		this.current = v;
17706		this.dirty = false;
17707	}
17708};
17709var DepthAttachment = class extends FramebufferAttachment {
17710	set(v) {
17711		if (v === this.current && !this.dirty) return;
17712		this.context.bindFramebuffer.set(this.parent);
17713		const gl = this.gl;
17714		gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, v);
17715		this.current = v;
17716		this.dirty = false;
17717	}
17718};
17719var DepthStencilAttachment = class extends FramebufferAttachment {
17720	set(v) {
17721		if (v === this.current && !this.dirty) return;
17722		this.context.bindFramebuffer.set(this.parent);
17723		const gl = this.gl;
17724		gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, v);
17725		this.current = v;
17726		this.dirty = false;
17727	}
17728};
17729//#endregion
17730//#region src/webgl/framebuffer.ts
17731/**
17732* @internal
17733* A framebuffer holder object
17734*/
17735var Framebuffer = class {
17736	constructor(context, width, height, hasDepth, hasStencil) {
17737		this.context = context;
17738		this.width = width;
17739		this.height = height;
17740		const gl = context.gl;
17741		const fbo = this.framebuffer = gl.createFramebuffer();
17742		this.colorAttachment = new ColorAttachment(context, fbo);
17743		if (hasDepth) this.depthAttachment = hasStencil ? new DepthStencilAttachment(context, fbo) : new DepthAttachment(context, fbo);
17744		else if (hasStencil) throw new Error("Stencil cannot be set without depth");
17745	}
17746	destroy() {
17747		const gl = this.context.gl;
17748		const texture = this.colorAttachment.get();
17749		if (texture) gl.deleteTexture(texture);
17750		if (this.depthAttachment) {
17751			const renderbuffer = this.depthAttachment.get();
17752			if (renderbuffer) gl.deleteRenderbuffer(renderbuffer);
17753		}
17754		gl.deleteFramebuffer(this.framebuffer);
17755	}
17756};
17757//#endregion
17758//#region src/webgl/frame_uniform_buffer.ts
17759const layout = std140Layout([
17760	{
17761		name: "u_units_to_pixels",
17762		type: "vec2"
17763	},
17764	{
17765		name: "u_world_size",
17766		type: "vec2"
17767	},
17768	{
17769		name: "u_camera_to_center_distance",
17770		type: "float"
17771	},
17772	{
17773		name: "u_symbol_fade_change",
17774		type: "float"
17775	},
17776	{
17777		name: "u_aspect_ratio",
17778		type: "float"
17779	},
17780	{
17781		name: "u_device_pixel_ratio",
17782		type: "float"
17783	},
17784	{
17785		name: "u_viewport_size",
17786		type: "vec2"
17787	},
17788	{
17789		name: "u_pixel_extrude_scale",
17790		type: "vec2"
17791	},
17792	{
17793		name: "u_pitch",
17794		type: "float"
17795	}
17796]);
17797const offsets = layout.offsets;
17798/**
17799* @internal
17800* The buffer behind the `FrameUBO` block in the shader preludes, written once per frame by `Painter.render`.
17801*/
17802function createFrameUniformBuffer(context) {
17803	return new UniformBuffer(context, UBO_BINDINGS.FrameUBO, layout);
17804}
17805function updateFrameUniformBuffer(buffer, painter) {
17806	const f32 = buffer.pending;
17807	const { transform } = painter;
17808	const gl = painter.context.gl;
17809	f32[offsets.u_units_to_pixels] = 1 / transform.pixelsToGLUnits[0];
17810	f32[offsets.u_units_to_pixels + 1] = 1 / transform.pixelsToGLUnits[1];
17811	f32[offsets.u_world_size] = gl.drawingBufferWidth;
17812	f32[offsets.u_world_size + 1] = gl.drawingBufferHeight;
17813	f32[offsets.u_camera_to_center_distance] = transform.cameraToCenterDistance;
vendor: 17,207 bytes, lines 17814-18259
17814	f32[offsets.u_symbol_fade_change] = painter.options.fadeDuration ? painter.symbolFadeChange : 1;
17815	f32[offsets.u_aspect_ratio] = transform.width / transform.height;
17816	f32[offsets.u_device_pixel_ratio] = painter.pixelRatio;
17817	f32[offsets.u_viewport_size] = transform.width;
17818	f32[offsets.u_viewport_size + 1] = transform.height;
17819	f32[offsets.u_pixel_extrude_scale] = 1 / transform.width;
17820	f32[offsets.u_pixel_extrude_scale + 1] = 1 / transform.height;
17821	f32[offsets.u_pitch] = transform.pitch / 360 * 2 * Math.PI;
17822	buffer.upload();
17823}
17824//#endregion
17825//#region src/webgl/color_mode.ts
17826const ZERO = 0;
17827const ONE = 1;
17828const ONE_MINUS_SRC_ALPHA = 771;
17829var ColorMode = class {
17830	constructor(blendFunction, blendColor, mask) {
17831		this.blendFunction = blendFunction;
17832		this.blendColor = blendColor;
17833		this.mask = mask;
17834	}
17835};
17836ColorMode.Replace = [ONE, ZERO];
17837ColorMode.disabled = new ColorMode(ColorMode.Replace, Color.transparent, [
17838	false,
17839	false,
17840	false,
17841	false
17842]);
17843ColorMode.unblended = new ColorMode(ColorMode.Replace, Color.transparent, [
17844	true,
17845	true,
17846	true,
17847	true
17848]);
17849ColorMode.alphaBlended = new ColorMode([ONE, ONE_MINUS_SRC_ALPHA], Color.transparent, [
17850	true,
17851	true,
17852	true,
17853	true
17854]);
17855//#endregion
17856//#region src/webgl/context.ts
17857/**
17858* @internal
17859* A webgl wrapper class to allow injection, mocking and abstraction
17860*/
17861var Context = class {
17862	constructor(gl) {
17863		this.gl = gl;
17864		this.clearColor = new ClearColor(this);
17865		this.clearDepth = new ClearDepth(this);
17866		this.clearStencil = new ClearStencil(this);
17867		this.colorMask = new ColorMask(this);
17868		this.depthMask = new DepthMask(this);
17869		this.stencilMask = new StencilMask(this);
17870		this.stencilFunc = new StencilFunc(this);
17871		this.stencilOp = new StencilOp(this);
17872		this.stencilTest = new StencilTest(this);
17873		this.depthRange = new DepthRange(this);
17874		this.depthTest = new DepthTest(this);
17875		this.depthFunc = new DepthFunc(this);
17876		this.blend = new Blend(this);
17877		this.blendFunc = new BlendFunc(this);
17878		this.blendColor = new BlendColor(this);
17879		this.blendEquation = new BlendEquation(this);
17880		this.cullFace = new CullFace(this);
17881		this.cullFaceSide = new CullFaceSide(this);
17882		this.frontFace = new FrontFace(this);
17883		this.program = new ProgramValue(this);
17884		this.activeTexture = new ActiveTextureUnit(this);
17885		this.viewport = new Viewport(this);
17886		this.bindFramebuffer = new BindFramebuffer(this);
17887		this.bindRenderbuffer = new BindRenderbuffer(this);
17888		this.bindTexture = new BindTexture(this);
17889		this.bindVertexBuffer = new BindVertexBuffer(this);
17890		this.bindElementBuffer = new BindElementBuffer(this);
17891		this.bindVertexArray = new BindVertexArray(this);
17892		this.pixelStoreUnpack = new PixelStoreUnpack(this);
17893		this.pixelStoreUnpackPremultiplyAlpha = new PixelStoreUnpackPremultiplyAlpha(this);
17894		this.pixelStoreUnpackFlipY = new PixelStoreUnpackFlipY(this);
17895		this.extTextureFilterAnisotropic = gl.getExtension("EXT_texture_filter_anisotropic");
17896		if (this.extTextureFilterAnisotropic) this.extTextureFilterAnisotropicMax = gl.getParameter(this.extTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
17897		this.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
17898		gl.getExtension("EXT_color_buffer_half_float");
17899		gl.getExtension("EXT_color_buffer_float");
17900		this.projectionUniformBuffer = createProjectionUniformBuffer(this);
17901		this.terrainUniformBuffer = createTerrainUniformBuffer(this);
17902		this.frameUniformBuffer = createFrameUniformBuffer(this);
17903	}
17904	setDefault() {
17905		this.unbindVAO();
17906		this.clearColor.setDefault();
17907		this.clearDepth.setDefault();
17908		this.clearStencil.setDefault();
17909		this.colorMask.setDefault();
17910		this.depthMask.setDefault();
17911		this.stencilMask.setDefault();
17912		this.stencilFunc.setDefault();
17913		this.stencilOp.setDefault();
17914		this.stencilTest.setDefault();
17915		this.depthRange.setDefault();
17916		this.depthTest.setDefault();
17917		this.depthFunc.setDefault();
17918		this.blend.setDefault();
17919		this.blendFunc.setDefault();
17920		this.blendColor.setDefault();
17921		this.blendEquation.setDefault();
17922		this.cullFace.setDefault();
17923		this.cullFaceSide.setDefault();
17924		this.frontFace.setDefault();
17925		this.program.setDefault();
17926		this.activeTexture.setDefault();
17927		this.bindFramebuffer.setDefault();
17928		this.pixelStoreUnpack.setDefault();
17929		this.pixelStoreUnpackPremultiplyAlpha.setDefault();
17930		this.pixelStoreUnpackFlipY.setDefault();
17931	}
17932	setDirty() {
17933		this.clearColor.dirty = true;
17934		this.clearDepth.dirty = true;
17935		this.clearStencil.dirty = true;
17936		this.colorMask.dirty = true;
17937		this.depthMask.dirty = true;
17938		this.stencilMask.dirty = true;
17939		this.stencilFunc.dirty = true;
17940		this.stencilOp.dirty = true;
17941		this.stencilTest.dirty = true;
17942		this.depthRange.dirty = true;
17943		this.depthTest.dirty = true;
17944		this.depthFunc.dirty = true;
17945		this.blend.dirty = true;
17946		this.blendFunc.dirty = true;
17947		this.blendColor.dirty = true;
17948		this.blendEquation.dirty = true;
17949		this.cullFace.dirty = true;
17950		this.cullFaceSide.dirty = true;
17951		this.frontFace.dirty = true;
17952		this.program.dirty = true;
17953		this.activeTexture.dirty = true;
17954		this.viewport.dirty = true;
17955		this.bindFramebuffer.dirty = true;
17956		this.bindRenderbuffer.dirty = true;
17957		this.bindTexture.dirty = true;
17958		this.bindVertexBuffer.dirty = true;
17959		this.bindElementBuffer.dirty = true;
17960		this.bindVertexArray.dirty = true;
17961		this.pixelStoreUnpack.dirty = true;
17962		this.pixelStoreUnpackPremultiplyAlpha.dirty = true;
17963		this.pixelStoreUnpackFlipY.dirty = true;
17964		this.projectionUniformBuffer.bindingDirty = true;
17965		this.terrainUniformBuffer.bindingDirty = true;
17966		this.frameUniformBuffer.bindingDirty = true;
17967	}
17968	/**
17969	* Reset some GL state to default values before handing users the raw context, as we do for
17970	* custom layers and WebGL style images, to avoid hard-to-debug bugs in their code.
17971	*
17972	* MapLibre restores all of its own state afterwards, so the only state worth resetting first
17973	* is state users would be surprised to find dirty: `CULL_FACE`, `TEXTURE0` and the three
17974	* `UNPACK_` settings, whose defaults are meaningful enough that most code assumes them.
17975	* The vertex array is unbound rather than reset, so that MapLibre never has to track it and
17976	* a user's `vertexAttribPointer` calls cannot land on one of ours.
17977	*/
17978	setCustomLayerDefaults() {
17979		this.unbindVAO();
17980		this.cullFace.setDefault();
17981		this.activeTexture.setDefault();
17982		this.pixelStoreUnpack.setDefault();
17983		this.pixelStoreUnpackPremultiplyAlpha.setDefault();
17984		this.pixelStoreUnpackFlipY.setDefault();
17985	}
17986	createIndexBuffer(array, dynamicDraw) {
17987		return new IndexBuffer(this, array, dynamicDraw);
17988	}
17989	createVertexBuffer(array, attributes, dynamicDraw) {
17990		return new VertexBuffer(this, array, attributes, dynamicDraw);
17991	}
17992	createRenderbuffer(storageFormat, width, height) {
17993		const gl = this.gl;
17994		const rbo = gl.createRenderbuffer();
17995		this.bindRenderbuffer.set(rbo);
17996		gl.renderbufferStorage(gl.RENDERBUFFER, storageFormat, width, height);
17997		this.bindRenderbuffer.set(null);
17998		return rbo;
17999	}
18000	createFramebuffer(width, height, hasDepth, hasStencil) {
18001		return new Framebuffer(this, width, height, hasDepth, hasStencil);
18002	}
18003	clear({ color, depth, stencil }) {
18004		const gl = this.gl;
18005		let mask = 0;
18006		if (color) {
18007			mask |= gl.COLOR_BUFFER_BIT;
18008			this.clearColor.set(color);
18009			this.colorMask.set([
18010				true,
18011				true,
18012				true,
18013				true
18014			]);
18015		}
18016		if (typeof depth !== "undefined") {
18017			mask |= gl.DEPTH_BUFFER_BIT;
18018			this.depthRange.set([0, 1]);
18019			this.clearDepth.set(depth);
18020			this.depthMask.set(true);
18021		}
18022		if (typeof stencil !== "undefined") {
18023			mask |= gl.STENCIL_BUFFER_BIT;
18024			this.clearStencil.set(stencil);
18025			this.stencilMask.set(255);
18026		}
18027		gl.clear(mask);
18028	}
18029	setCullFace(cullFaceMode) {
18030		if (cullFaceMode.enable === false) this.cullFace.set(false);
18031		else {
18032			this.cullFace.set(true);
18033			this.cullFaceSide.set(cullFaceMode.mode);
18034			this.frontFace.set(cullFaceMode.frontFace);
18035		}
18036	}
18037	setDepthMode(depthMode) {
18038		if (depthMode.func === this.gl.ALWAYS && !depthMode.mask) this.depthTest.set(false);
18039		else {
18040			this.depthTest.set(true);
18041			this.depthFunc.set(depthMode.func);
18042			this.depthMask.set(depthMode.mask);
18043			this.depthRange.set(depthMode.range);
18044		}
18045	}
18046	setStencilMode(stencilMode) {
18047		if (stencilMode.test.func === this.gl.ALWAYS && !stencilMode.mask) this.stencilTest.set(false);
18048		else {
18049			this.stencilTest.set(true);
18050			this.stencilMask.set(stencilMode.mask);
18051			this.stencilOp.set([
18052				stencilMode.fail,
18053				stencilMode.depthFail,
18054				stencilMode.pass
18055			]);
18056			this.stencilFunc.set({
18057				func: stencilMode.test.func,
18058				ref: stencilMode.ref,
18059				mask: stencilMode.test.mask
18060			});
18061		}
18062	}
18063	setColorMode(colorMode) {
18064		if (deepEqual(colorMode.blendFunction, ColorMode.Replace)) this.blend.set(false);
18065		else {
18066			this.blend.set(true);
18067			this.blendFunc.set(colorMode.blendFunction);
18068			this.blendColor.set(colorMode.blendColor);
18069		}
18070		this.colorMask.set(colorMode.mask);
18071	}
18072	createVertexArray() {
18073		return this.gl.createVertexArray();
18074	}
18075	deleteVertexArray(x) {
18076		this.gl.deleteVertexArray(x);
18077	}
18078	unbindVAO() {
18079		this.bindVertexArray.set(null);
18080	}
18081};
18082//#endregion
18083//#region src/webgl/depth_mode.ts
18084const ALWAYS$1 = 519;
18085var DepthMode = class {
18086	constructor(depthFunc, depthMask, depthRange) {
18087		this.func = depthFunc;
18088		this.mask = depthMask;
18089		this.range = depthRange;
18090	}
18091};
18092DepthMode.ReadOnly = false;
18093DepthMode.ReadWrite = true;
18094DepthMode.disabled = new DepthMode(ALWAYS$1, DepthMode.ReadOnly, [0, 1]);
18095//#endregion
18096//#region src/webgl/stencil_mode.ts
18097const ALWAYS = 519;
18098const KEEP = 7680;
18099var StencilMode = class {
18100	constructor(test, ref, mask, fail, depthFail, pass) {
18101		this.test = test;
18102		this.ref = ref;
18103		this.mask = mask;
18104		this.fail = fail;
18105		this.depthFail = depthFail;
18106		this.pass = pass;
18107	}
18108};
18109StencilMode.disabled = new StencilMode({
18110	func: ALWAYS,
18111	mask: 0
18112}, 0, 0, KEEP, KEEP, KEEP);
18113//#endregion
18114//#region src/webgl/cull_face_mode.ts
18115const FRONT = 1028;
18116const BACK = 1029;
18117const CCW = 2305;
18118var CullFaceMode = class {
18119	constructor(enable, mode, frontFace) {
18120		this.enable = enable;
18121		this.mode = mode;
18122		this.frontFace = frontFace;
18123	}
18124};
18125CullFaceMode.disabled = new CullFaceMode(false, BACK, CCW);
18126CullFaceMode.backCCW = new CullFaceMode(true, BACK, CCW);
18127CullFaceMode.frontCCW = new CullFaceMode(true, FRONT, CCW);
18128//#endregion
18129//#region src/render/render_context.ts
18130function createRenderContext(transform, projection, terrain) {
18131	const projectionTransition = projection?.transitionState ?? 0;
18132	return {
18133		currentPass: "offscreen",
18134		currentLayer: 0,
18135		opaquePassCutoff: Infinity,
18136		depthRangeFor3D: [0, 1],
18137		isRenderingToTexture: false,
18138		transform,
18139		terrain,
18140		projectionTransition,
18141		isRenderingGlobe: projectionTransition > 0
18142	};
18143}
18144function getProjectionDataForTile(renderContext, tileID, options = {}) {
18145	return renderContext.transform.getProjectionData({
18146		overscaledTileID: tileID,
18147		aligned: options.aligned,
18148		applyGlobeMatrix: !renderContext.isRenderingToTexture,
18149		applyTerrainMatrix: options.applyTerrainMatrix ?? true
18150	});
18151}
18152/**
18153* Returns terrain data for a tile.
18154* Returns null if terrain is not configured or tiles are being rendered to a texture.
18155*/
18156function getTerrainDataForTile(renderContext, tileID) {
18157	if (renderContext.isRenderingToTexture) return null;
18158	return renderContext.terrain?.getTerrainData(tileID) ?? null;
18159}
18160//#endregion
18161//#region src/webgl/draw/draw_collision_debug.ts
18162let quadTriangles;
18163function drawCollisionDebug(painter, tileManager, layer, coords, isText, renderContext) {
18164	const context = painter.context;
18165	const gl = context.gl;
18166	const program = painter.useProgram("collisionBox");
18167	const tileBatches = [];
18168	let circleCount = 0;
18169	let circleOffset = 0;
18170	for (const coord of coords) {
18171		const bucket = tileManager.getTile(coord).getBucket(layer);
18172		if (!bucket) continue;
18173		const buffers = isText ? bucket.textCollisionBox : bucket.iconCollisionBox;
18174		const circleArray = bucket.collisionCircleArray;
18175		if (circleArray.length > 0) {
18176			tileBatches.push({
18177				circleArray,
18178				circleOffset,
18179				coord
18180			});
18181			circleCount += circleArray.length / 4;
18182			circleOffset = circleCount;
18183		}
18184		if (!buffers) continue;
18185		program.draw(context, gl.LINES, DepthMode.disabled, StencilMode.disabled, painter.colorModeForRenderPass(), CullFaceMode.disabled, null, getTerrainDataForTile(renderContext, coord), getProjectionDataForTile(renderContext, coord), layer.id, buffers.layoutVertexBuffer, buffers.indexBuffer, buffers.segments, null, painter.transform.zoom, null, null, buffers.collisionVertexBuffer);
18186	}
18187	if (!isText || !tileBatches.length) return;
18188	const circleProgram = painter.useProgram("collisionCircle");
18189	const vertexData = new CollisionCircleLayoutArray();
18190	vertexData.resize(circleCount * 4);
18191	vertexData._trim();
18192	let vertexOffset = 0;
18193	for (const batch of tileBatches) for (let i = 0; i < batch.circleArray.length / 4; i++) {
18194		const circleIdx = i * 4;
18195		const x = batch.circleArray[circleIdx + 0];
18196		const y = batch.circleArray[circleIdx + 1];
18197		const radius = batch.circleArray[circleIdx + 2];
18198		const collision = batch.circleArray[circleIdx + 3];
18199		vertexData.emplace(vertexOffset++, x, y, radius, collision, 0);
18200		vertexData.emplace(vertexOffset++, x, y, radius, collision, 1);
18201		vertexData.emplace(vertexOffset++, x, y, radius, collision, 2);
18202		vertexData.emplace(vertexOffset++, x, y, radius, collision, 3);
18203	}
18204	if (!quadTriangles || quadTriangles.length < circleCount * 2) quadTriangles = createQuadTriangles(circleCount);
18205	const indexBuffer = context.createIndexBuffer(quadTriangles, true);
18206	const vertexBuffer = context.createVertexBuffer(vertexData, collisionCircleLayout.members, true);
18207	for (const batch of tileBatches) circleProgram.draw(context, gl.TRIANGLES, DepthMode.disabled, StencilMode.disabled, painter.colorModeForRenderPass(), CullFaceMode.disabled, null, getTerrainDataForTile(renderContext, batch.coord), null, layer.id, vertexBuffer, indexBuffer, SegmentVector.simpleSegment(0, batch.circleOffset * 2, batch.circleArray.length, batch.circleArray.length / 2), null, painter.transform.zoom, null, null, null);
18208	vertexBuffer.destroy();
18209	indexBuffer.destroy();
18210}
18211function createQuadTriangles(quadCount) {
18212	const triCount = quadCount * 2;
18213	const array = new QuadTriangleArray();
18214	array.resize(triCount);
18215	array._trim();
18216	for (let i = 0; i < triCount; i++) {
18217		const idx = i * 6;
18218		array.uint16[idx + 0] = i * 4 + 0;
18219		array.uint16[idx + 1] = i * 4 + 1;
18220		array.uint16[idx + 2] = i * 4 + 2;
18221		array.uint16[idx + 3] = i * 4 + 2;
18222		array.uint16[idx + 4] = i * 4 + 3;
18223		array.uint16[idx + 5] = i * 4 + 0;
18224	}
18225	return array;
18226}
18227//#endregion
18228//#region src/webgl/draw/draw_symbol.ts
18229const identityMat4 = identity(/* @__PURE__ */ new Float32Array(16));
18230function drawSymbols(painter, tileManager, layer, coords, variableOffsets, renderContext) {
18231	if (renderContext.currentPass !== "translucent") return;
18232	const stencilMode = StencilMode.disabled;
18233	const colorMode = painter.colorModeForRenderPass();
18234	if (layer._unevaluatedLayout.hasValue("text-variable-anchor") || layer._unevaluatedLayout.hasValue("text-variable-anchor-offset")) updateVariableAnchors(coords, painter, layer, tileManager, layer.layout.get("text-rotation-alignment"), layer.layout.get("text-pitch-alignment"), layer.paint.get("text-translate"), layer.paint.get("text-translate-anchor"), variableOffsets);
18235	if (layer.paint.get("icon-opacity").constantOr(1) !== 0) drawLayerSymbols(painter, tileManager, layer, coords, false, layer.paint.get("icon-translate"), layer.paint.get("icon-translate-anchor"), layer.layout.get("icon-rotation-alignment").constantOr("viewport"), layer.layout.get("icon-pitch-alignment"), layer.layout.get("icon-keep-upright"), stencilMode, colorMode, renderContext);
18236	if (layer.paint.get("text-opacity").constantOr(1) !== 0) drawLayerSymbols(painter, tileManager, layer, coords, true, layer.paint.get("text-translate"), layer.paint.get("text-translate-anchor"), layer.layout.get("text-rotation-alignment"), layer.layout.get("text-pitch-alignment"), layer.layout.get("text-keep-upright"), stencilMode, colorMode, renderContext);
18237	if (tileManager.map.showCollisionBoxes) {
18238		drawCollisionDebug(painter, tileManager, layer, coords, true, renderContext);
18239		drawCollisionDebug(painter, tileManager, layer, coords, false, renderContext);
18240	}
18241}
18242function calculateVariableRenderShift(anchor, width, height, textOffset, textBoxScale, renderTextSize) {
18243	const { horizontalAlign, verticalAlign } = getAnchorAlignment(anchor);
18244	const shiftX = -(horizontalAlign - .5) * width;
18245	const shiftY = -(verticalAlign - .5) * height;
18246	return new Point((shiftX / textBoxScale + textOffset[0]) * renderTextSize, (shiftY / textBoxScale + textOffset[1]) * renderTextSize);
18247}
18248function updateVariableAnchors(coords, painter, layer, tileManager, rotationAlignment, pitchAlignment, translate, translateAnchor, variableOffsets) {
18249	const transform = painter.transform;
18250	const terrain = painter.style.map.terrain;
18251	const rotateWithMap = rotationAlignment === "map";
18252	const pitchWithMap = pitchAlignment === "map";
18253	for (const coord of coords) {
18254		const tile = tileManager.getTile(coord);
18255		const bucket = tile.getBucket(layer);
18256		if (!bucket?.text?.segments.get().length) continue;
18257		const sizeData = bucket.textSizeData;
18258		const size = evaluateSizeForZoom(sizeData, transform.zoom);
18259		const pixelToTileScale = pixelsToTileUnits(tile, 1, painter.transform.zoom);
vendor: 72,018 bytes, lines 18260-19659
18260		const pitchedLabelPlaneMatrix = getPitchedLabelPlaneMatrix(rotateWithMap, painter.transform, pixelToTileScale);
18261		const updateTextFitIcon = layer.layout.get("icon-text-fit") !== "none" && bucket.hasIconData();
18262		if (size) {
18263			const tileScale = Math.pow(2, transform.zoom - tile.tileID.overscaledZ);
18264			const getElevation = terrain ? (x, y) => terrain.getElevation(coord, x, y) : void 0;
18265			updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, transform, pitchedLabelPlaneMatrix, tileScale, size, updateTextFitIcon, translatePosition(transform, tile, translate, translateAnchor), coord.toUnwrapped(), getElevation, layer.layout.get("symbol-height-anchor") === "ground");
18266		}
18267	}
18268}
18269function getShiftedAnchor(projectedAnchorPoint, projectionContext, rotateWithMap, shift, transformAngle, pitchedTextShiftCorrection) {
18270	const translatedAnchor = projectionContext.tileAnchorPoint.add(new Point(projectionContext.translation[0], projectionContext.translation[1]));
18271	if (projectionContext.pitchWithMap) {
18272		let adjustedShift = shift.mult(pitchedTextShiftCorrection);
18273		if (!rotateWithMap) adjustedShift = adjustedShift.rotate(-transformAngle);
18274		const tileAnchorShifted = translatedAnchor.add(adjustedShift);
18275		return projectWithMatrix(tileAnchorShifted.x, tileAnchorShifted.y, projectionContext.pitchedLabelPlaneMatrix, elevationAt(projectionContext, tileAnchorShifted.x, tileAnchorShifted.y)).point;
18276	} else if (rotateWithMap) {
18277		const east = projectTileCoordinatesToLabelPlane(projectionContext.tileAnchorPoint.x + 1, projectionContext.tileAnchorPoint.y, projectionContext).point.sub(projectedAnchorPoint);
18278		const angle = Math.atan(east.y / east.x) + (east.x < 0 ? Math.PI : 0);
18279		return projectedAnchorPoint.add(shift.rotate(angle));
18280	} else return projectedAnchorPoint.add(shift);
18281}
18282function updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, transform, pitchedLabelPlaneMatrix, tileScale, size, updateTextFitIcon, translation, unwrappedTileID, getElevation, heightAnchorGround) {
18283	const placedSymbols = bucket.text.placedSymbolArray;
18284	const dynamicTextLayoutVertexArray = bucket.text.dynamicLayoutVertexArray;
18285	const dynamicIconLayoutVertexArray = bucket.icon.dynamicLayoutVertexArray;
18286	const placedTextShifts = {};
18287	dynamicTextLayoutVertexArray.clear();
18288	for (let s = 0; s < placedSymbols.length; s++) {
18289		const symbol = placedSymbols.get(s);
18290		const skipOrientation = bucket.allowVerticalPlacement && !symbol.placedOrientation;
18291		const variableOffset = !symbol.hidden && symbol.crossTileID && !skipOrientation ? variableOffsets[symbol.crossTileID] : null;
18292		if (!variableOffset) hideGlyphs(symbol.numGlyphs, dynamicTextLayoutVertexArray);
18293		else {
18294			const tileAnchor = new Point(symbol.anchorX, symbol.anchorY);
18295			const projectionContext = {
18296				getElevation,
18297				heightOffset: symbol.heightOffset,
18298				heightAnchorGround,
18299				width: transform.width,
18300				height: transform.height,
18301				pitchedLabelPlaneMatrix,
18302				lineVertexArray: null,
18303				pitchWithMap,
18304				transform,
18305				projectionCache: null,
18306				tileAnchorPoint: tileAnchor,
18307				translation,
18308				unwrappedTileID
18309			};
18310			const projectedAnchor = pitchWithMap ? projectTileCoordinatesToClipSpace(tileAnchor.x, tileAnchor.y, projectionContext) : projectTileCoordinatesToLabelPlane(tileAnchor.x, tileAnchor.y, projectionContext);
18311			const perspectiveRatio = getPerspectiveRatio(transform.cameraToCenterDistance, projectedAnchor.signedDistanceFromCamera);
18312			let renderTextSize = evaluateSizeForFeature(bucket.textSizeData, size, symbol) * perspectiveRatio / 24;
18313			if (pitchWithMap) renderTextSize *= bucket.tilePixelRatio / tileScale;
18314			const { width, height, anchor, textOffset, textBoxScale } = variableOffset;
18315			const shift = calculateVariableRenderShift(anchor, width, height, textOffset, textBoxScale, renderTextSize);
18316			const pitchedTextCorrection = transform.getPitchedTextCorrection(tileAnchor.x + translation[0], tileAnchor.y + translation[1], unwrappedTileID);
18317			const shiftedAnchor = getShiftedAnchor(projectedAnchor.point, projectionContext, rotateWithMap, shift, -transform.bearingInRadians, pitchedTextCorrection);
18318			const angle = bucket.allowVerticalPlacement && symbol.placedOrientation === 2 ? Math.PI / 2 : 0;
18319			for (let g = 0; g < symbol.numGlyphs; g++) addDynamicAttributes(dynamicTextLayoutVertexArray, shiftedAnchor, angle);
18320			if (updateTextFitIcon && symbol.associatedIconIndex >= 0) placedTextShifts[symbol.associatedIconIndex] = {
18321				shiftedAnchor,
18322				angle
18323			};
18324		}
18325	}
18326	if (updateTextFitIcon) {
18327		dynamicIconLayoutVertexArray.clear();
18328		const placedIcons = bucket.icon.placedSymbolArray;
18329		for (let i = 0; i < placedIcons.length; i++) {
18330			const placedIcon = placedIcons.get(i);
18331			if (placedIcon.hidden) hideGlyphs(placedIcon.numGlyphs, dynamicIconLayoutVertexArray);
18332			else {
18333				const shift = placedTextShifts[i];
18334				if (!shift) hideGlyphs(placedIcon.numGlyphs, dynamicIconLayoutVertexArray);
18335				else for (let g = 0; g < placedIcon.numGlyphs; g++) addDynamicAttributes(dynamicIconLayoutVertexArray, shift.shiftedAnchor, shift.angle);
18336			}
18337		}
18338		bucket.icon.dynamicLayoutVertexBuffer.updateData(dynamicIconLayoutVertexArray);
18339	}
18340	bucket.text.dynamicLayoutVertexBuffer.updateData(dynamicTextLayoutVertexArray);
18341}
18342function getSymbolProgramName(isSDF, isText, bucket) {
18343	if (bucket.iconsInText && isText) return "symbolTextAndIcon";
18344	else if (isSDF) return "symbolSDF";
18345	else return "symbolIcon";
18346}
18347function drawLayerSymbols(painter, tileManager, layer, coords, isText, translate, translateAnchor, rotationAlignment, pitchAlignment, keepUpright, stencilMode, colorMode, renderContext) {
18348	const context = painter.context;
18349	const gl = context.gl;
18350	const transform = painter.transform;
18351	const rotateWithMap = rotationAlignment === "map";
18352	const pitchWithMap = pitchAlignment === "map";
18353	const alongLine = rotationAlignment !== "viewport" && layer.layout.get("symbol-placement") !== "point";
18354	const rotateInShader = rotateWithMap && !pitchWithMap && !alongLine;
18355	const hasSortKey = !layer.layout.get("symbol-sort-key").isConstant();
18356	let sortFeaturesByKey = false;
18357	const depthMode = painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
18358	const hasVariablePlacement = layer._unevaluatedLayout.hasValue("text-variable-anchor") || layer._unevaluatedLayout.hasValue("text-variable-anchor-offset");
18359	const tileRenderState = [];
18360	const pitchedTextRescaling = transform.getCircleRadiusCorrection();
18361	for (const coord of coords) {
18362		const tile = tileManager.getTile(coord);
18363		const bucket = tile.getBucket(layer);
18364		if (!bucket) continue;
18365		const buffers = isText ? bucket.text : bucket.icon;
18366		if (!buffers?.segments.get().length || !buffers.hasVisibleVertices) continue;
18367		const programConfiguration = buffers.programConfigurations.get(layer.id);
18368		const isSDF = isText || bucket.sdfIcons;
18369		const sizeData = isText ? bucket.textSizeData : bucket.iconSizeData;
18370		const transformed = pitchWithMap || transform.pitch !== 0;
18371		const program = painter.useProgram(getSymbolProgramName(isSDF, isText, bucket), programConfiguration);
18372		const size = evaluateSizeForZoom(sizeData, transform.zoom);
18373		const terrainData = getTerrainDataForTile(renderContext, coord);
18374		let texSize;
18375		let texSizeIcon = [0, 0];
18376		let atlasTexture;
18377		let atlasInterpolation;
18378		let atlasTextureIcon = null;
18379		let atlasInterpolationIcon;
18380		if (isText) {
18381			atlasTexture = tile.glyphAtlasTexture;
18382			atlasInterpolation = gl.LINEAR;
18383			texSize = tile.glyphAtlasTexture.size;
18384			if (bucket.iconsInText) {
18385				texSizeIcon = tile.imageAtlasTexture.size;
18386				atlasTextureIcon = tile.imageAtlasTexture;
18387				const zoomDependentSize = sizeData.kind === "composite" || sizeData.kind === "camera";
18388				atlasInterpolationIcon = transformed || painter.options.rotating || painter.options.zooming || zoomDependentSize ? gl.LINEAR : gl.NEAREST;
18389			}
18390		} else {
18391			const iconScaled = layer.layout.get("icon-size").constantOr(0) !== 1 || bucket.iconsNeedLinear;
18392			atlasTexture = tile.imageAtlasTexture;
18393			atlasInterpolation = isSDF || painter.options.rotating || painter.options.zooming || iconScaled || transformed ? gl.LINEAR : gl.NEAREST;
18394			texSize = tile.imageAtlasTexture.size;
18395		}
18396		const s = pixelsToTileUnits(tile, 1, painter.transform.zoom);
18397		const pitchedLabelPlaneMatrix = getPitchedLabelPlaneMatrix(rotateWithMap, painter.transform, s);
18398		const glCoordMatrixForShader = getGlCoordMatrix(pitchWithMap, rotateWithMap, painter.transform, s);
18399		const translation = translatePosition(transform, tile, translate, translateAnchor);
18400		const projectionData = getProjectionDataForTile(renderContext, coord);
18401		const hasVariableAnchors = hasVariablePlacement && bucket.hasTextData();
18402		const updateTextFitIcon = layer.layout.get("icon-text-fit") !== "none" && hasVariableAnchors && bucket.hasIconData();
18403		const isOffset = layer._unevaluatedLayout.hasValue("icon-offset");
18404		const heightAnchorGround = layer.layout.get("symbol-height-anchor") === "ground";
18405		if (alongLine) {
18406			const pitchedLabelPlaneMatrixInverse = create$1();
18407			fastInvertTransformMat4(pitchedLabelPlaneMatrixInverse, pitchedLabelPlaneMatrix);
18408			const getElevation = painter.style.map.terrain ? (x, y) => painter.style.map.terrain.getElevation(coord, x, y) : void 0;
18409			updateLineLabels(bucket, painter, isText, pitchedLabelPlaneMatrix, pitchedLabelPlaneMatrixInverse, pitchWithMap, keepUpright, layer.layout.get("text-rotation-alignment") === "map", coord.toUnwrapped(), transform.width, transform.height, translation, getElevation);
18410		}
18411		const shaderVariableAnchor = isText && hasVariablePlacement || updateTextFitIcon;
18412		const combinedLabelPlaneMatrix = pitchWithMap ? pitchedLabelPlaneMatrix : painter.transform.clipSpaceToPixelsMatrix;
18413		const uLabelPlaneMatrix = alongLine || shaderVariableAnchor ? identityMat4 : combinedLabelPlaneMatrix;
18414		const hasHalo = isSDF && layer.paint.get(isText ? "text-halo-width" : "icon-halo-width").constantOr(1) !== 0;
18415		let uniformValues;
18416		if (isSDF) {
18417			if (!bucket.iconsInText) uniformValues = symbolSDFUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, isText, texSize, hasHalo, pitchedTextRescaling, isOffset, heightAnchorGround);
18418			else uniformValues = symbolTextAndIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, texSize, texSizeIcon, pitchedTextRescaling, isOffset, heightAnchorGround);
18419		} else uniformValues = symbolIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, isText, texSize, pitchedTextRescaling, isOffset, heightAnchorGround);
18420		const state = {
18421			program,
18422			buffers,
18423			uniformValues,
18424			projectionData,
18425			atlasTexture,
18426			atlasTextureIcon,
18427			atlasInterpolation,
18428			atlasInterpolationIcon,
18429			isSDF,
18430			hasHalo
18431		};
18432		if (hasSortKey && bucket.canOverlap) {
18433			sortFeaturesByKey = true;
18434			const oldSegments = buffers.segments.get();
18435			for (const segment of oldSegments) tileRenderState.push({
18436				segments: new SegmentVector([segment]),
18437				sortKey: segment.sortKey,
18438				state,
18439				terrainData
18440			});
18441		} else tileRenderState.push({
18442			segments: buffers.segments,
18443			sortKey: 0,
18444			state,
18445			terrainData
18446		});
18447	}
18448	if (sortFeaturesByKey) tileRenderState.sort((a, b) => a.sortKey - b.sortKey);
18449	const haloWidth = layer.paint.get(isText ? "text-halo-width" : "icon-halo-width").constantOr(null) ?? Infinity;
18450	const isGlyphOverlap = layer.layout.get("text-letter-spacing").constantOr(0) * 24 < 0 || haloWidth > 1;
18451	for (const segmentState of tileRenderState) {
18452		const state = segmentState.state;
18453		context.activeTexture.set(gl.TEXTURE0);
18454		state.atlasTexture.bind(state.atlasInterpolation, gl.CLAMP_TO_EDGE);
18455		if (state.atlasTextureIcon) {
18456			context.activeTexture.set(gl.TEXTURE1);
18457			if (state.atlasTextureIcon) state.atlasTextureIcon.bind(state.atlasInterpolationIcon, gl.CLAMP_TO_EDGE);
18458		}
18459		const isHalo = state.isSDF && state.hasHalo;
18460		if (isHalo) {
18461			const uniformValues = state.uniformValues;
18462			uniformValues["u_is_halo"] = 1;
18463			if (isGlyphOverlap) {
18464				uniformValues["u_is_plain"] = 0;
18465				drawSymbolElements(state.buffers, segmentState.segments, layer, painter, state.program, depthMode, stencilMode, colorMode, uniformValues, state.projectionData, segmentState.terrainData);
18466				uniformValues["u_is_halo"] = 0;
18467				uniformValues["u_is_plain"] = 1;
18468			}
18469		}
18470		drawSymbolElements(state.buffers, segmentState.segments, layer, painter, state.program, depthMode, stencilMode, colorMode, state.uniformValues, state.projectionData, segmentState.terrainData);
18471		if (isHalo && !isGlyphOverlap) state.uniformValues["u_is_halo"] = 0;
18472	}
18473}
18474function drawSymbolElements(buffers, segments, layer, painter, program, depthMode, stencilMode, colorMode, uniformValues, projectionData, terrainData) {
18475	const context = painter.context;
18476	const gl = context.gl;
18477	program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, layer.id, buffers.layoutVertexBuffer, buffers.indexBuffer, segments, layer.paint, painter.transform.zoom, buffers.programConfigurations.get(layer.id), buffers.dynamicLayoutVertexBuffer, buffers.opacityVertexBuffer);
18478}
18479//#endregion
18480//#region src/webgl/draw/draw_circle.ts
18481function drawCircles(painter, tileManager, layer, coords, renderContext) {
18482	if (renderContext.currentPass !== "translucent") return;
18483	const opacity = layer.paint.get("circle-opacity");
18484	const strokeWidth = layer.paint.get("circle-stroke-width");
18485	const strokeOpacity = layer.paint.get("circle-stroke-opacity");
18486	const sortFeaturesByKey = !layer.layout.get("circle-sort-key").isConstant();
18487	if (opacity.constantOr(1) === 0 && (strokeWidth.constantOr(1) === 0 || strokeOpacity.constantOr(1) === 0)) return;
18488	const context = painter.context;
18489	const gl = context.gl;
18490	const transform = painter.transform;
18491	const depthMode = painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
18492	const stencilMode = StencilMode.disabled;
18493	const colorMode = painter.colorModeForRenderPass();
18494	const segmentsRenderStates = [];
18495	const radiusCorrectionFactor = transform.getCircleRadiusCorrection();
18496	for (const coord of coords) {
18497		const tile = tileManager.getTile(coord);
18498		const bucket = tile.getBucket(layer);
18499		if (!bucket) continue;
18500		const styleTranslate = layer.paint.get("circle-translate");
18501		const styleTranslateAnchor = layer.paint.get("circle-translate-anchor");
18502		const translateForUniforms = translatePosition(transform, tile, styleTranslate, styleTranslateAnchor);
18503		const programConfiguration = bucket.programConfigurations.get(layer.id);
18504		const program = painter.useProgram("circle", programConfiguration);
18505		const layoutVertexBuffer = bucket.layoutVertexBuffer;
18506		const indexBuffer = bucket.indexBuffer;
18507		const terrainData = getTerrainDataForTile(renderContext, coord);
18508		const state = {
18509			programConfiguration,
18510			program,
18511			layoutVertexBuffer,
18512			indexBuffer,
18513			uniformValues: circleUniformValues(painter, tile, layer, translateForUniforms, radiusCorrectionFactor),
18514			terrainData,
18515			projectionData: getProjectionDataForTile(renderContext, coord)
18516		};
18517		if (sortFeaturesByKey) {
18518			const oldSegments = bucket.segments.get();
18519			for (const segment of oldSegments) segmentsRenderStates.push({
18520				segments: new SegmentVector([segment]),
18521				sortKey: segment.sortKey,
18522				state
18523			});
18524		} else segmentsRenderStates.push({
18525			segments: bucket.segments,
18526			sortKey: 0,
18527			state
18528		});
18529	}
18530	if (sortFeaturesByKey) segmentsRenderStates.sort((a, b) => a.sortKey - b.sortKey);
18531	for (const segmentsState of segmentsRenderStates) {
18532		const { programConfiguration, program, layoutVertexBuffer, indexBuffer, uniformValues, terrainData, projectionData } = segmentsState.state;
18533		const segments = segmentsState.segments;
18534		program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, layer.id, layoutVertexBuffer, indexBuffer, segments, layer.paint, painter.transform.zoom, programConfiguration);
18535	}
18536}
18537//#endregion
18538//#region src/webgl/draw/draw_heatmap.ts
18539function drawHeatmap(painter, tileManager, layer, tileIDs, renderContext) {
18540	if (layer.paint.get("heatmap-opacity") === 0) return;
18541	const context = painter.context;
18542	if (painter.style.map.terrain) {
18543		for (const coord of tileIDs) {
18544			const tile = tileManager.getTile(coord);
18545			if (tileManager.hasRenderableParent(coord)) continue;
18546			if (renderContext.currentPass === "offscreen") prepareHeatmapTerrain(painter, tile, layer, coord, renderContext);
18547			else if (renderContext.currentPass === "translucent") renderHeatmapTerrain(painter, layer, coord, renderContext);
18548		}
18549		context.viewport.set([
18550			0,
18551			0,
18552			painter.width,
18553			painter.height
18554		]);
18555	} else if (renderContext.currentPass === "offscreen") prepareHeatmapFlat(painter, tileManager, layer, tileIDs, renderContext);
18556	else if (renderContext.currentPass === "translucent") renderHeatmapFlat(painter, layer);
18557}
18558function prepareHeatmapFlat(painter, tileManager, layer, coords, renderContext) {
18559	const context = painter.context;
18560	const gl = context.gl;
18561	const transform = painter.transform;
18562	const stencilMode = StencilMode.disabled;
18563	const colorMode = new ColorMode([gl.ONE, gl.ONE], Color.transparent, [
18564		true,
18565		true,
18566		true,
18567		true
18568	]);
18569	bindFramebuffer(context, painter, layer);
18570	context.clear({ color: Color.transparent });
18571	for (const coord of coords) {
18572		if (tileManager.hasRenderableParent(coord)) continue;
18573		const tile = tileManager.getTile(coord);
18574		const bucket = tile.getBucket(layer);
18575		if (!bucket) continue;
18576		const programConfiguration = bucket.programConfigurations.get(layer.id);
18577		const program = painter.useProgram("heatmap", programConfiguration);
18578		const projectionData = getProjectionDataForTile(renderContext, coord, { applyTerrainMatrix: false });
18579		const radiusCorrectionFactor = transform.getCircleRadiusCorrection();
18580		program.draw(context, gl.TRIANGLES, DepthMode.disabled, stencilMode, colorMode, CullFaceMode.backCCW, heatmapUniformValues(tile, transform.zoom, layer.paint.get("heatmap-intensity"), radiusCorrectionFactor), null, projectionData, layer.id, bucket.layoutVertexBuffer, bucket.indexBuffer, bucket.segments, layer.paint, transform.zoom, programConfiguration);
18581	}
18582	context.viewport.set([
18583		0,
18584		0,
18585		painter.width,
18586		painter.height
18587	]);
18588}
18589function renderHeatmapFlat(painter, layer) {
18590	const context = painter.context;
18591	const gl = context.gl;
18592	context.setColorMode(painter.colorModeForRenderPass());
18593	const fbo = layer.heatmapFbos.get(HEATMAP_FULL_RENDER_FBO_KEY);
18594	if (!fbo) return;
18595	context.activeTexture.set(gl.TEXTURE0);
18596	gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
18597	context.activeTexture.set(gl.TEXTURE1);
18598	getColorRampTexture(context, layer).bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
18599	painter.useProgram("heatmapTexture").draw(context, gl.TRIANGLES, DepthMode.disabled, StencilMode.disabled, painter.colorModeForRenderPass(), CullFaceMode.disabled, heatmapTextureUniformValues(painter, layer, 0, 1), null, null, layer.id, painter.viewportBuffer, painter.quadTriangleIndexBuffer, painter.viewportSegments, layer.paint, painter.transform.zoom);
18600}
18601function prepareHeatmapTerrain(painter, tile, layer, coord, renderContext) {
18602	const context = painter.context;
18603	const gl = context.gl;
18604	const stencilMode = StencilMode.disabled;
18605	const colorMode = new ColorMode([gl.ONE, gl.ONE], Color.transparent, [
18606		true,
18607		true,
18608		true,
18609		true
18610	]);
18611	const bucket = tile.getBucket(layer);
18612	if (!bucket) return;
18613	const tileKey = coord.key;
18614	let fbo = layer.heatmapFbos.get(tileKey);
18615	if (!fbo) {
18616		fbo = createHeatmapFbo(context, tile.tileSize, tile.tileSize);
18617		layer.heatmapFbos.set(tileKey, fbo);
18618	}
18619	context.bindFramebuffer.set(fbo.framebuffer);
18620	context.viewport.set([
18621		0,
18622		0,
18623		tile.tileSize,
18624		tile.tileSize
18625	]);
18626	context.clear({ color: Color.transparent });
18627	const programConfiguration = bucket.programConfigurations.get(layer.id);
18628	const program = painter.useProgram("heatmap", programConfiguration, !renderContext.isRenderingGlobe);
18629	const projectionData = getProjectionDataForTile(renderContext, tile.tileID);
18630	const terrainData = getTerrainDataForTile(renderContext, coord);
18631	program.draw(context, gl.TRIANGLES, DepthMode.disabled, stencilMode, colorMode, CullFaceMode.disabled, heatmapUniformValues(tile, painter.transform.zoom, layer.paint.get("heatmap-intensity"), 1), terrainData, projectionData, layer.id, bucket.layoutVertexBuffer, bucket.indexBuffer, bucket.segments, layer.paint, painter.transform.zoom, programConfiguration);
18632}
18633function renderHeatmapTerrain(painter, layer, coord, renderContext) {
18634	const context = painter.context;
18635	const gl = context.gl;
18636	const transform = painter.transform;
18637	context.setColorMode(painter.colorModeForRenderPass());
18638	const colorRampTexture = getColorRampTexture(context, layer);
18639	const tileKey = coord.key;
18640	const fbo = layer.heatmapFbos.get(tileKey);
18641	if (!fbo) return;
18642	context.activeTexture.set(gl.TEXTURE0);
18643	gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
18644	context.activeTexture.set(gl.TEXTURE1);
18645	colorRampTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
18646	const projectionData = getProjectionDataForTile(renderContext, coord, { applyTerrainMatrix: renderContext.isRenderingGlobe });
18647	painter.useProgram("heatmapTexture").draw(context, gl.TRIANGLES, DepthMode.disabled, StencilMode.disabled, painter.colorModeForRenderPass(), CullFaceMode.disabled, heatmapTextureUniformValues(painter, layer, 0, 1), null, projectionData, layer.id, painter.rasterBoundsBuffer, painter.quadTriangleIndexBuffer, painter.rasterBoundsSegments, layer.paint, transform.zoom);
18648	fbo.destroy();
18649	layer.heatmapFbos.delete(tileKey);
18650}
18651function bindFramebuffer(context, painter, layer) {
18652	const gl = context.gl;
18653	context.activeTexture.set(gl.TEXTURE1);
18654	context.viewport.set([
18655		0,
18656		0,
18657		painter.width / 4,
18658		painter.height / 4
18659	]);
18660	let fbo = layer.heatmapFbos.get(HEATMAP_FULL_RENDER_FBO_KEY);
18661	if (!fbo) {
18662		fbo = createHeatmapFbo(context, painter.width / 4, painter.height / 4);
18663		layer.heatmapFbos.set(HEATMAP_FULL_RENDER_FBO_KEY, fbo);
18664	} else {
18665		gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
18666		context.bindFramebuffer.set(fbo.framebuffer);
18667	}
18668}
18669function createHeatmapFbo(context, width, height) {
18670	const gl = context.gl;
18671	const texture = gl.createTexture();
18672	gl.bindTexture(gl.TEXTURE_2D, texture);
18673	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
18674	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
18675	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
18676	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
18677	gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA16F, width, height);
18678	const fbo = context.createFramebuffer(width, height, false, false);
18679	fbo.colorAttachment.set(texture);
18680	return fbo;
18681}
18682function getColorRampTexture(context, layer) {
18683	layer.colorRampTexture ||= new Texture(context, layer.colorRamp, context.gl.RGBA);
18684	return layer.colorRampTexture;
18685}
18686//#endregion
18687//#region src/webgl/draw/draw_layer_opacity.ts
18688/**
18689* Partial line-layer-opacity
18690* render the whole layer to a scratch FBO, then composite with `layerOpacity`.
18691* Applies opacity uniformly to the layer instead of accumulating alpha across overlapping segments.
18692*/
18693function prepareDrawLayerOpacity(painter, layer, coords) {
18694	const context = painter.context;
18695	const compositeTarget = context.bindFramebuffer.get();
18696	const compositeViewport = context.viewport.get();
18697	const [, , width, height] = compositeViewport;
18698	bindLayerOpacity(painter, width, height);
18699	context.viewport.set([
18700		0,
18701		0,
18702		width,
18703		height
18704	]);
18705	context.clear({
18706		color: Color.transparent,
18707		depth: 1,
18708		stencil: 0
18709	});
18710	painter.currentStencilSource = void 0;
18711	painter.renderTileClippingMasks(layer, coords);
18712	return {
18713		compositeTarget,
18714		compositeViewport
18715	};
18716}
18717function bindLayerOpacity(painter, width, height) {
18718	const gl = painter.context.gl;
18719	if (!painter.layerOpacityFbo) {
18720		const fbo = painter.context.createFramebuffer(width, height, true, true);
18721		const texture = gl.createTexture();
18722		gl.bindTexture(gl.TEXTURE_2D, texture);
18723		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
18724		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
18725		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
18726		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
18727		gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
18728		fbo.colorAttachment.set(texture);
18729		fbo.depthAttachment.set(painter.context.createRenderbuffer(gl.DEPTH_STENCIL, width, height));
18730		painter.layerOpacityFbo = fbo;
18731		painter.context.bindFramebuffer.set(painter.layerOpacityFbo.framebuffer);
18732		return;
18733	}
18734	if (painter.layerOpacityFbo.width === width && painter.layerOpacityFbo.height === height) {
18735		painter.context.bindFramebuffer.set(painter.layerOpacityFbo.framebuffer);
18736		return;
18737	}
18738	const fbo = painter.layerOpacityFbo;
18739	gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
18740	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
18741	painter.context.bindRenderbuffer.set(fbo.depthAttachment.get());
18742	gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
18743	painter.context.bindRenderbuffer.set(null);
18744	fbo.width = width;
18745	fbo.height = height;
18746	painter.context.bindFramebuffer.set(fbo.framebuffer);
18747}
18748function drawLayerOpacity(painter, opacity, prepareDrawLayerOpacityResult, layer) {
18749	const context = painter.context;
18750	const gl = context.gl;
18751	context.bindFramebuffer.set(prepareDrawLayerOpacityResult.compositeTarget);
18752	context.viewport.set(prepareDrawLayerOpacityResult.compositeViewport);
18753	context.activeTexture.set(gl.TEXTURE0);
18754	gl.bindTexture(gl.TEXTURE_2D, painter.layerOpacityFbo.colorAttachment.get());
18755	painter.useProgram("layerOpacity").draw(context, gl.TRIANGLES, DepthMode.disabled, StencilMode.disabled, painter.colorModeForRenderPass(), CullFaceMode.disabled, layerOpacityUniformValues(opacity, 0), null, null, layer.id, painter.viewportBuffer, painter.quadTriangleIndexBuffer, painter.viewportSegments, layer.paint, painter.transform.zoom);
18756	painter.currentStencilSource = void 0;
18757}
18758//#endregion
18759//#region src/webgl/draw/draw_line.ts
18760function updateGradientTexture(painter, tileManager, context, gl, layer, bucket, coord, layerGradient) {
18761	let textureResolution = 256;
18762	if (layer.stepInterpolant) {
18763		const sourceMaxZoom = tileManager.getSource().maxzoom;
18764		const potentialOverzoom = coord.canonical.z === sourceMaxZoom ? Math.ceil(1 << painter.transform.maxZoom - coord.canonical.z) : 1;
18765		const maxTextureCoverage = bucket.maxLineLength / EXTENT * 1024 * potentialOverzoom;
18766		textureResolution = clamp(nextPowerOfTwo(maxTextureCoverage), 256, context.maxTextureSize);
18767	}
18768	layerGradient.gradient = renderColorRamp({
18769		expression: layer.gradientExpression(),
18770		evaluationKey: "lineProgress",
18771		resolution: textureResolution,
18772		image: layerGradient.gradient || void 0,
18773		clips: bucket.lineClipsArray
18774	});
18775	if (layerGradient.texture) layerGradient.texture.update(layerGradient.gradient);
18776	else layerGradient.texture = new Texture(context, layerGradient.gradient, gl.RGBA);
18777	layerGradient.version = layer.gradientVersion;
18778	return layerGradient.texture;
18779}
18780function bindImagePatternTextures(context, gl, tile, programConfiguration, crossfade) {
18781	context.activeTexture.set(gl.TEXTURE0);
18782	tile.imageAtlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
18783	programConfiguration.updatePaintBuffers(crossfade);
18784}
18785function bindDasharrayTextures(painter, context, gl, programConfiguration, programChanged, crossfade) {
18786	if (programChanged || painter.lineAtlas.dirty) {
18787		context.activeTexture.set(gl.TEXTURE0);
18788		painter.lineAtlas.bind(context);
18789	}
18790	programConfiguration.updatePaintBuffers(crossfade);
18791}
18792function bindGradientTextures(painter, tileManager, context, gl, layer, bucket, coord) {
18793	const layerGradient = bucket.gradients[layer.id];
18794	let gradientTexture = layerGradient.texture;
18795	if (layer.gradientVersion !== layerGradient.version) gradientTexture = updateGradientTexture(painter, tileManager, context, gl, layer, bucket, coord, layerGradient);
18796	context.activeTexture.set(gl.TEXTURE0);
18797	gradientTexture.bind(layer.stepInterpolant ? gl.NEAREST : gl.LINEAR, gl.CLAMP_TO_EDGE);
18798}
18799function bindGradientAndDashTextures(painter, tileManager, context, gl, layer, bucket, coord, programConfiguration, crossfade) {
18800	const layerGradient = bucket.gradients[layer.id];
18801	let gradientTexture = layerGradient.texture;
18802	if (layer.gradientVersion !== layerGradient.version) gradientTexture = updateGradientTexture(painter, tileManager, context, gl, layer, bucket, coord, layerGradient);
18803	context.activeTexture.set(gl.TEXTURE0);
18804	gradientTexture.bind(layer.stepInterpolant ? gl.NEAREST : gl.LINEAR, gl.CLAMP_TO_EDGE);
18805	context.activeTexture.set(gl.TEXTURE1);
18806	painter.lineAtlas.bind(context);
18807	programConfiguration.updatePaintBuffers(crossfade);
18808}
18809function drawLine(painter, tileManager, layer, coords, renderContext) {
18810	if (renderContext.currentPass !== "translucent") return;
18811	const opacity = layer.paint.get("line-opacity");
18812	const width = layer.paint.get("line-width");
18813	const layerOpacity = layer.paint.get("line-layer-opacity");
18814	if (opacity.constantOr(1) === 0 || width.constantOr(1) === 0 || layerOpacity === 0) return;
18815	if (layerOpacity < 1) {
18816		const results = prepareDrawLayerOpacity(painter, layer, coords);
18817		drawLineTiles(painter, tileManager, layer, coords, renderContext);
18818		drawLayerOpacity(painter, layerOpacity, results, layer);
18819		return;
18820	}
18821	drawLineTiles(painter, tileManager, layer, coords, renderContext);
18822}
18823function drawLineTiles(painter, tileManager, layer, coords, renderContext) {
18824	const depthMode = painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
18825	const colorMode = painter.colorModeForRenderPass();
18826	const dasharrayProperty = layer.paint.get("line-dasharray");
18827	const dasharray = dasharrayProperty.constantOr(1);
18828	const patternProperty = layer.paint.get("line-pattern");
18829	const image = patternProperty.constantOr(1);
18830	const gradient = layer.paint.get("line-gradient");
18831	const crossfade = layer.getCrossfadeParameters();
18832	let programId;
18833	if (image) programId = "linePattern";
18834	else if (dasharray && gradient) programId = "lineGradientSDF";
18835	else if (dasharray) programId = "lineSDF";
18836	else if (gradient) programId = "lineGradient";
18837	else programId = "line";
18838	const context = painter.context;
18839	const gl = context.gl;
18840	const transform = painter.transform;
18841	let firstTile = true;
18842	for (const coord of coords) {
18843		const tile = tileManager.getTile(coord);
18844		if (image && !tile.patternsLoaded()) continue;
18845		const bucket = tile.getBucket(layer);
18846		if (!bucket) continue;
18847		const programConfiguration = bucket.programConfigurations.get(layer.id);
18848		const prevProgram = painter.context.program.get();
18849		const program = painter.useProgram(programId, programConfiguration);
18850		const programChanged = firstTile || program.program !== prevProgram;
18851		const terrainData = getTerrainDataForTile(renderContext, coord);
18852		const constantPattern = patternProperty.constantOr(null);
18853		const constantDasharray = dasharrayProperty?.constantOr(null);
18854		if (constantPattern && tile.imageAtlas) {
18855			const atlas = tile.imageAtlas;
18856			const posTo = atlas.patternPositions[constantPattern.to.toString()];
18857			const posFrom = atlas.patternPositions[constantPattern.from.toString()];
18858			if (posTo && posFrom) programConfiguration.setConstantPatternPositions(posTo, posFrom);
18859		} else if (constantDasharray) {
18860			const round = layer.layout.get("line-cap").constantOr(null) === "round";
18861			const dashTo = painter.lineAtlas.getDash(constantDasharray.to, round);
18862			const dashFrom = painter.lineAtlas.getDash(constantDasharray.from, round);
18863			programConfiguration.setConstantDashPositions(dashTo, dashFrom);
18864		}
18865		const projectionData = getProjectionDataForTile(renderContext, coord);
18866		const pixelRatio = transform.getPixelScale();
18867		let uniformValues;
18868		if (image) {
18869			uniformValues = linePatternUniformValues(painter, tile, layer, pixelRatio, crossfade);
18870			bindImagePatternTextures(context, gl, tile, programConfiguration, crossfade);
18871		} else if (dasharray && gradient) {
18872			uniformValues = lineGradientSDFUniformValues(painter, tile, layer, pixelRatio, crossfade, bucket.lineClipsArray.length);
18873			bindGradientAndDashTextures(painter, tileManager, context, gl, layer, bucket, coord, programConfiguration, crossfade);
18874		} else if (dasharray) {
18875			uniformValues = lineSDFUniformValues(painter, tile, layer, pixelRatio, crossfade);
18876			bindDasharrayTextures(painter, context, gl, programConfiguration, programChanged, crossfade);
18877		} else if (gradient) {
18878			uniformValues = lineGradientUniformValues(painter, tile, layer, pixelRatio, bucket.lineClipsArray.length);
18879			bindGradientTextures(painter, tileManager, context, gl, layer, bucket, coord);
18880		} else uniformValues = lineUniformValues(painter, tile, layer, pixelRatio);
18881		const stencil = painter.stencilModeForClipping(coord);
18882		program.draw(context, gl.TRIANGLES, depthMode, stencil, colorMode, CullFaceMode.disabled, uniformValues, terrainData, projectionData, layer.id, bucket.layoutVertexBuffer, bucket.indexBuffer, bucket.segments, layer.paint, painter.transform.zoom, programConfiguration, bucket.layoutVertexBuffer2);
18883		firstTile = false;
18884	}
18885}
18886//#endregion
18887//#region src/render/update_pattern_positions_in_program.ts
18888/**
18889* A simple helper shared by draw_fill and draw_fill_extrusions to find the correct pattern positions AND update program.
18890* For transitionable properties, especially 'fill-pattern' and 'fill-extrusion-pattern', while rendering certain frames
18891* tile.imageAtlas has been updated by worker to hold the new pattern only, but rendering code is still looking for the previous image.
18892* The mismatch was causing setConstantPatternPositions method not being called and pixelRatio was always the
18893* default of 1, instead of actual values set by original map.addImage.
18894*
18895* @param programConfiguration - to be used to set pattern position and device pixel ratio.
18896* @param propertyName - 'fill-pattern' or 'fill-extrusion-pattern' property key
18897* @param constantPattern - either 'fill-pattern' or 'fill-extrusion-pattern' property value
18898* @param tile - current tile being drawn
18899* @param layer - current layer being rendered
18900*/
18901function updatePatternPositionsInProgram(programConfiguration, propertyName, constantPattern, tile, layer) {
18902	if (!constantPattern || !tile?.imageAtlas) return;
18903	const patternPositions = tile.imageAtlas.patternPositions;
18904	let posTo = patternPositions[constantPattern.to.toString()];
18905	let posFrom = patternPositions[constantPattern.from.toString()];
18906	if (!posTo && posFrom) posTo = posFrom;
18907	if (!posFrom && posTo) posFrom = posTo;
18908	if (!posTo || !posFrom) {
18909		const transitioned = layer.getPaintProperty(propertyName);
18910		posTo = patternPositions[transitioned];
18911		posFrom = patternPositions[transitioned];
18912	}
18913	if (posTo && posFrom) programConfiguration.setConstantPatternPositions(posTo, posFrom);
18914}
18915//#endregion
18916//#region src/webgl/draw/draw_fill.ts
18917function drawFill(painter, tileManager, layer, coords, renderContext) {
18918	const color = layer.paint.get("fill-color");
18919	const opacity = layer.paint.get("fill-opacity");
18920	const layerOpacity = layer.paint.get("fill-layer-opacity");
18921	if (opacity.constantOr(1) === 0 || layerOpacity === 0) return;
18922	if (layerOpacity < 1) {
18923		if (renderContext.currentPass !== "translucent") return;
18924		const results = prepareDrawLayerOpacity(painter, layer, coords);
18925		drawFillAndOutline(painter, tileManager, layer, coords, renderContext);
18926		drawLayerOpacity(painter, layerOpacity, results, layer);
18927		return;
18928	}
18929	const pattern = layer.paint.get("fill-pattern");
18930	const fillEligibleForOpaque = painter.opaquePassEnabledForLayer() && !pattern.constantOr(1) && color.constantOr(Color.transparent).a === 1 && opacity.constantOr(0) === 1;
18931	if (fillEligibleForOpaque && renderContext.currentPass === "opaque") {
18932		const colorMode = painter.colorModeForRenderPass();
18933		drawFillTiles(painter, tileManager, layer, coords, painter.getDepthModeForSublayer(1, DepthMode.ReadWrite), colorMode, false, renderContext);
18934		return;
18935	}
18936	if (fillEligibleForOpaque && renderContext.currentPass === "translucent") {
18937		drawOutline(painter, tileManager, layer, coords, renderContext);
18938		return;
18939	}
18940	if (renderContext.currentPass === "translucent") drawFillAndOutline(painter, tileManager, layer, coords, renderContext);
18941}
18942/**
18943* Draw fill + outline in a single translucent pass with ReadOnly depth.
18944* Shared by the layer-opacity subpass (always) and the normal translucent path
18945* (when the fill is not opaque-pass-eligible).
18946*/
18947function drawFillAndOutline(painter, tileManager, layer, coords, renderContext) {
18948	const colorMode = painter.colorModeForRenderPass();
18949	drawFillTiles(painter, tileManager, layer, coords, painter.getDepthModeForSublayer(1, DepthMode.ReadOnly), colorMode, false, renderContext);
18950	drawOutline(painter, tileManager, layer, coords, renderContext);
18951}
18952function drawOutline(painter, tileManager, layer, coords, renderContext) {
18953	if (!layer.paint.get("fill-antialias")) return;
18954	const colorMode = painter.colorModeForRenderPass();
18955	drawFillTiles(painter, tileManager, layer, coords, painter.getDepthModeForSublayer(layer.getPaintProperty("fill-outline-color") ? 2 : 0, DepthMode.ReadOnly), colorMode, true, renderContext);
18956}
18957function drawFillTiles(painter, tileManager, layer, coords, depthMode, colorMode, isOutline, renderContext) {
18958	const gl = painter.context.gl;
18959	const fillPropertyName = "fill-pattern";
18960	const patternProperty = layer.paint.get(fillPropertyName);
18961	const image = patternProperty?.constantOr(1);
18962	const crossfade = layer.getCrossfadeParameters();
18963	let drawMode, programName, uniformValues, indexBuffer, segments;
18964	const transform = painter.transform;
18965	const propertyFillTranslate = layer.paint.get("fill-translate");
18966	const propertyFillTranslateAnchor = layer.paint.get("fill-translate-anchor");
18967	if (!isOutline) {
18968		programName = image ? "fillPattern" : "fill";
18969		drawMode = gl.TRIANGLES;
18970	} else {
18971		programName = image && !layer.getPaintProperty("fill-outline-color") ? "fillOutlinePattern" : "fillOutline";
18972		drawMode = gl.LINES;
18973	}
18974	const constantPattern = patternProperty.constantOr(null);
18975	for (const coord of coords) {
18976		const tile = tileManager.getTile(coord);
18977		if (image && !tile.patternsLoaded()) continue;
18978		const bucket = tile.getBucket(layer);
18979		if (!bucket) continue;
18980		const isSdfPattern = bucket.sdfPatterns[layer.id] ?? false;
18981		const programConfiguration = bucket.programConfigurations.get(layer.id);
18982		const program = painter.useProgram(programName, programConfiguration);
18983		const terrainData = getTerrainDataForTile(renderContext, coord);
18984		if (image) {
18985			painter.context.activeTexture.set(gl.TEXTURE0);
18986			tile.imageAtlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
18987			programConfiguration.updatePaintBuffers(crossfade);
18988		}
18989		updatePatternPositionsInProgram(programConfiguration, fillPropertyName, constantPattern, tile, layer);
18990		const projectionData = getProjectionDataForTile(renderContext, coord);
18991		const translateForUniforms = translatePosition(transform, tile, propertyFillTranslate, propertyFillTranslateAnchor);
18992		if (!isOutline) {
18993			indexBuffer = bucket.indexBuffer;
18994			segments = bucket.segments;
18995			uniformValues = image ? fillPatternUniformValues(painter, crossfade, tile, translateForUniforms, isSdfPattern) : fillUniformValues(translateForUniforms);
18996		} else {
18997			indexBuffer = bucket.indexBuffer2;
18998			segments = bucket.segments2;
18999			uniformValues = programName === "fillOutlinePattern" && image ? fillOutlinePatternUniformValues(painter, crossfade, tile, translateForUniforms, isSdfPattern) : fillOutlineUniformValues(translateForUniforms);
19000		}
19001		const stencil = painter.stencilModeForClipping(coord);
19002		program.draw(painter.context, drawMode, depthMode, stencil, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, layer.id, bucket.layoutVertexBuffer, indexBuffer, segments, layer.paint, painter.transform.zoom, programConfiguration);
19003	}
19004}
19005//#endregion
19006//#region src/webgl/draw/draw_fill_extrusion.ts
19007function drawFillExtrusion(painter, tileManager, layer, coords, renderContext) {
19008	const opacity = layer.paint.get("fill-extrusion-opacity");
19009	if (opacity === 0) return;
19010	if (renderContext.currentPass === "translucent") {
19011		const depthMode = new DepthMode(painter.context.gl.LEQUAL, DepthMode.ReadWrite, renderContext.depthRangeFor3D);
19012		if (opacity === 1 && !layer.paint.get("fill-extrusion-pattern").constantOr(1)) {
19013			const colorMode = painter.colorModeForRenderPass();
19014			drawExtrusionTiles(painter, tileManager, layer, coords, depthMode, StencilMode.disabled, colorMode, renderContext);
19015		} else {
19016			drawExtrusionTiles(painter, tileManager, layer, coords, depthMode, StencilMode.disabled, ColorMode.disabled, renderContext);
19017			drawExtrusionTiles(painter, tileManager, layer, coords, depthMode, painter.stencilModeFor3D(), painter.colorModeForRenderPass(), renderContext);
19018		}
19019	}
19020}
19021function drawExtrusionTiles(painter, tileManager, layer, coords, depthMode, stencilMode, colorMode, renderContext) {
19022	const context = painter.context;
19023	const gl = context.gl;
19024	const fillPropertyName = "fill-extrusion-pattern";
19025	const patternProperty = layer.paint.get(fillPropertyName);
19026	const image = patternProperty.constantOr(1);
19027	const crossfade = layer.getCrossfadeParameters();
19028	const opacity = layer.paint.get("fill-extrusion-opacity");
19029	const constantPattern = patternProperty.constantOr(null);
19030	const transform = painter.transform;
19031	for (const coord of coords) {
19032		const tile = tileManager.getTile(coord);
19033		const bucket = tile.getBucket(layer);
19034		if (!bucket) continue;
19035		const terrainData = getTerrainDataForTile(renderContext, coord);
19036		const programConfiguration = bucket.programConfigurations.get(layer.id);
19037		const program = painter.useProgram(image ? "fillExtrusionPattern" : "fillExtrusion", programConfiguration);
19038		if (image) {
19039			painter.context.activeTexture.set(gl.TEXTURE0);
19040			tile.imageAtlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
19041			programConfiguration.updatePaintBuffers(crossfade);
19042		}
19043		const projectionData = getProjectionDataForTile(renderContext, coord);
19044		updatePatternPositionsInProgram(programConfiguration, fillPropertyName, constantPattern, tile, layer);
19045		const translate = translatePosition(transform, tile, layer.paint.get("fill-extrusion-translate"), layer.paint.get("fill-extrusion-translate-anchor"));
19046		const shouldUseVerticalGradient = layer.paint.get("fill-extrusion-vertical-gradient");
19047		const uniformValues = image ? fillExtrusionPatternUniformValues(painter, shouldUseVerticalGradient, opacity, translate, coord, crossfade, tile) : fillExtrusionUniformValues(painter, shouldUseVerticalGradient, opacity, translate);
19048		program.draw(context, context.gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, layer.id, bucket.layoutVertexBuffer, bucket.indexBuffer, bucket.segments, layer.paint, painter.transform.zoom, programConfiguration, painter.style.map.terrain && bucket.centroidVertexBuffer);
19049	}
19050}
19051//#endregion
19052//#region src/webgl/draw/draw_hillshade.ts
19053function drawHillshade(painter, tileManager, layer, tileIDs, renderContext) {
19054	if (renderContext.currentPass !== "offscreen" && renderContext.currentPass !== "translucent") return;
19055	const context = painter.context;
19056	const useSubdivision = painter.style.projection.useSubdivision;
19057	const depthMode = painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
19058	const colorMode = painter.colorModeForRenderPass();
19059	if (renderContext.currentPass === "offscreen") {
19060		prepareHillshade(painter, tileManager, tileIDs, layer, depthMode, StencilMode.disabled, colorMode);
19061		context.viewport.set([
19062			0,
19063			0,
19064			painter.width,
19065			painter.height
19066		]);
19067	} else if (renderContext.currentPass === "translucent") {
19068		if (useSubdivision) {
19069			const [stencilBorderless, stencilBorders, coords] = painter.stencilConfigForOverlapTwoPass(tileIDs);
19070			renderHillshade(painter, tileManager, layer, coords, stencilBorderless, depthMode, colorMode, false, renderContext);
19071			renderHillshade(painter, tileManager, layer, coords, stencilBorders, depthMode, colorMode, true, renderContext);
19072		} else {
19073			const [stencil, coords] = painter.getStencilConfigForOverlapAndUpdateStencilID(tileIDs);
19074			renderHillshade(painter, tileManager, layer, coords, stencil, depthMode, colorMode, false, renderContext);
19075		}
19076	}
19077}
19078function renderHillshade(painter, tileManager, layer, coords, stencilModes, depthMode, colorMode, useBorder, renderContext) {
19079	const projection = painter.style.projection;
19080	const context = painter.context;
19081	const gl = context.gl;
19082	const defines = [`#define NUM_ILLUMINATION_SOURCES ${layer.paint.get("hillshade-highlight-color").values.length}`];
19083	const program = painter.useProgram("hillshade", null, false, defines);
19084	const align = !painter.options.moving;
19085	for (const coord of coords) {
19086		const tile = tileManager.getTile(coord);
19087		const fbo = tile.fbo;
19088		if (!fbo) continue;
19089		const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder, true, "raster");
19090		const terrainData = getTerrainDataForTile(renderContext, coord);
19091		context.activeTexture.set(gl.TEXTURE0);
19092		gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
19093		const projectionData = getProjectionDataForTile(renderContext, coord, { aligned: align });
19094		program.draw(context, gl.TRIANGLES, depthMode, stencilModes[coord.overscaledZ], colorMode, CullFaceMode.backCCW, hillshadeUniformValues(painter, tile, layer), terrainData, projectionData, layer.id, mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19095	}
19096}
19097function prepareHillshade(painter, tileManager, tileIDs, layer, depthMode, stencilMode, colorMode) {
19098	const context = painter.context;
19099	const gl = context.gl;
19100	const textureFilter = layer.paint.get("resampling") === "nearest" ? gl.NEAREST : gl.LINEAR;
19101	for (const coord of tileIDs) {
19102		const tile = tileManager.getTile(coord);
19103		const dem = tile.dem;
19104		if (!dem?.data) continue;
19105		if (!tile.needsHillshadePrepare) continue;
19106		const hillshadeTextureSize = dem.dim + 2;
19107		const textureStride = dem.stride;
19108		const pixelData = dem.getPixels();
19109		context.activeTexture.set(gl.TEXTURE1);
19110		context.pixelStoreUnpackPremultiplyAlpha.set(false);
19111		tile.demTexture ||= painter.getTileTexture(textureStride);
19112		if (tile.demTexture) {
19113			const demTexture = tile.demTexture;
19114			demTexture.update(pixelData, { premultiply: false });
19115			demTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);
19116		} else {
19117			tile.demTexture = new Texture(context, pixelData, gl.RGBA, { premultiply: false });
19118			tile.demTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);
19119		}
19120		context.activeTexture.set(gl.TEXTURE0);
19121		let fbo = tile.fbo;
19122		if (!fbo) {
19123			const renderTexture = new Texture(context, {
19124				width: hillshadeTextureSize,
19125				height: hillshadeTextureSize,
19126				data: null
19127			}, gl.RGBA);
19128			renderTexture.bind(textureFilter, gl.CLAMP_TO_EDGE);
19129			fbo = tile.fbo = context.createFramebuffer(hillshadeTextureSize, hillshadeTextureSize, true, false);
19130			fbo.colorAttachment.set(renderTexture.texture);
19131		}
19132		context.bindFramebuffer.set(fbo.framebuffer);
19133		context.viewport.set([
19134			0,
19135			0,
19136			hillshadeTextureSize,
19137			hillshadeTextureSize
19138		]);
19139		painter.useProgram("hillshadePrepare").draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled, hillshadeUniformPrepareValues(tile.tileID, dem), null, null, layer.id, painter.rasterBoundsBuffer, painter.quadTriangleIndexBuffer, painter.rasterBoundsSegments);
19140		tile.needsHillshadePrepare = false;
19141	}
19142}
19143//#endregion
19144//#region src/webgl/draw/draw_color_relief.ts
19145function drawColorRelief(painter, tileManager, layer, tileIDs, renderContext) {
19146	if (renderContext.currentPass !== "translucent") return;
19147	if (!tileIDs.length) return;
19148	const useSubdivision = painter.style.projection.useSubdivision;
19149	const depthMode = painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
19150	const colorMode = painter.colorModeForRenderPass();
19151	if (useSubdivision) {
19152		const [stencilBorderless, stencilBorders, coords] = painter.stencilConfigForOverlapTwoPass(tileIDs);
19153		renderColorRelief(painter, tileManager, layer, coords, stencilBorderless, depthMode, colorMode, false, renderContext);
19154		renderColorRelief(painter, tileManager, layer, coords, stencilBorders, depthMode, colorMode, true, renderContext);
19155	} else {
19156		const [stencil, coords] = painter.getStencilConfigForOverlapAndUpdateStencilID(tileIDs);
19157		renderColorRelief(painter, tileManager, layer, coords, stencil, depthMode, colorMode, false, renderContext);
19158	}
19159}
19160let textureMaxSize = 0;
19161/**
19162* Draws the color-relief tiles of one pass.
19163*
19164* A loaded raster-DEM tile can carry no DEM at all (e.g. an empty 204 response);
19165* such tiles are skipped before the first-tile setup reads from them.
19166*/
19167function renderColorRelief(painter, tileManager, layer, coords, stencilModes, depthMode, colorMode, useBorder, renderContext) {
19168	const projection = painter.style.projection;
19169	const context = painter.context;
19170	const gl = context.gl;
19171	const program = painter.useProgram("colorRelief");
19172	const align = !painter.options.moving;
19173	const textureFilter = layer.paint.get("resampling") === "nearest" ? gl.NEAREST : gl.LINEAR;
19174	let firstTile = true;
19175	let colorRampSize = 0;
19176	for (const coord of coords) {
19177		const tile = tileManager.getTile(coord);
19178		const dem = tile.dem;
19179		if (!dem?.data) continue;
19180		if (firstTile) {
19181			textureMaxSize ||= gl.getParameter(gl.MAX_TEXTURE_SIZE);
19182			const maxLength = textureMaxSize;
19183			const { elevationTexture, colorTexture } = layer.getColorRampTextures(context, maxLength, dem.getUnpackVector());
19184			context.activeTexture.set(gl.TEXTURE1);
19185			elevationTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);
19186			context.activeTexture.set(gl.TEXTURE4);
19187			colorTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
19188			firstTile = false;
19189			colorRampSize = elevationTexture.size[0];
19190		}
19191		const textureStride = dem.stride;
19192		context.activeTexture.set(gl.TEXTURE0);
19193		if (!tile.demTexture || tile.needsColorReliefPrepare) {
19194			context.pixelStoreUnpackPremultiplyAlpha.set(false);
19195			tile.demTexture ||= painter.getTileTexture(textureStride) ?? new Texture(context, {
19196				width: textureStride,
19197				height: textureStride,
19198				data: null
19199			}, gl.RGBA);
19200			tile.demTexture.update(dem.getPixels(), { premultiply: false });
19201			tile.needsColorReliefPrepare = false;
19202		}
19203		tile.demTexture.bind(textureFilter, gl.CLAMP_TO_EDGE);
19204		const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder, true, "raster");
19205		const terrainData = getTerrainDataForTile(renderContext, coord);
19206		const projectionData = getProjectionDataForTile(renderContext, coord, { aligned: align });
19207		program.draw(context, gl.TRIANGLES, depthMode, stencilModes[coord.overscaledZ], colorMode, CullFaceMode.backCCW, colorReliefUniformValues(layer, tile.dem, colorRampSize), terrainData, projectionData, layer.id, mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19208	}
19209}
19210//#endregion
19211//#region src/webgl/draw/draw_raster.ts
19212const cornerCoords = [
19213	new Point(0, 0),
19214	new Point(EXTENT, 0),
19215	new Point(EXTENT, EXTENT),
19216	new Point(0, EXTENT)
19217];
19218function drawRaster(painter, tileManager, layer, tileIDs, renderContext) {
19219	if (renderContext.currentPass !== "translucent") return;
19220	if (layer.paint.get("raster-opacity") === 0) return;
19221	if (!tileIDs.length) return;
19222	const source = tileManager.getSource();
19223	const useSubdivision = painter.style.projection.useSubdivision;
19224	if (source instanceof ImageSource) drawTiles(painter, tileManager, layer, tileIDs, null, false, false, source.tileCoords, source.imageWarp, source.flippedWindingOrder, renderContext, source.getMesh(painter.context, useSubdivision));
19225	else if (useSubdivision) {
19226		const [stencilBorderless, stencilBorders, coords] = painter.stencilConfigForOverlapTwoPass(tileIDs);
19227		drawTiles(painter, tileManager, layer, coords, stencilBorderless, false, true, cornerCoords, bilinearImageWarp, false, renderContext);
19228		drawTiles(painter, tileManager, layer, coords, stencilBorders, true, true, cornerCoords, bilinearImageWarp, false, renderContext);
19229	} else {
19230		const [stencil, coords] = painter.getStencilConfigForOverlapAndUpdateStencilID(tileIDs);
19231		drawTiles(painter, tileManager, layer, coords, stencil, false, true, cornerCoords, bilinearImageWarp, false, renderContext);
19232	}
19233}
19234function drawTiles(painter, tileManager, layer, coords, stencilModes, useBorder, allowPoles, corners, imageWarp, flipCullfaceMode = false, renderContext, sourceMesh = null) {
19235	const minTileZ = coords[coords.length - 1].overscaledZ;
19236	const context = painter.context;
19237	const gl = context.gl;
19238	const program = painter.useProgram("raster");
19239	const projection = painter.style.projection;
19240	const colorMode = painter.colorModeForRenderPass();
19241	const align = !painter.options.moving;
19242	const rasterOpacity = layer.paint.get("raster-opacity");
19243	const textureFilter = layer.paint.get("resampling") === "nearest" || layer.paint.get("raster-resampling") === "nearest" ? gl.NEAREST : gl.LINEAR;
19244	const fadeDuration = layer.paint.get("raster-fade-duration");
19245	const isTerrain = !!painter.style.map.terrain;
19246	for (const coord of coords) {
19247		const depthMode = painter.getDepthModeForSublayer(coord.overscaledZ - minTileZ, rasterOpacity === 1 ? DepthMode.ReadWrite : DepthMode.ReadOnly, gl.LESS);
19248		const tile = tileManager.getTile(coord);
19249		context.activeTexture.set(gl.TEXTURE0);
19250		tile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
19251		context.activeTexture.set(gl.TEXTURE1);
19252		const { parentTile, parentScaleBy, parentTopLeft, fadeValues } = getFadeProperties(tile, tileManager, fadeDuration, isTerrain);
19253		tile.fadeOpacity = fadeValues.tileOpacity;
19254		if (parentTile) {
19255			parentTile.fadeOpacity = fadeValues.parentTileOpacity;
19256			parentTile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
19257		} else tile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
19258		if (tile.texture.useMipmap && context.extTextureFilterAnisotropic && painter.transform.pitch > painter.options.anisotropicFilterPitch) gl.texParameterf(gl.TEXTURE_2D, context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, context.extTextureFilterAnisotropicMax);
19259		const terrainData = getTerrainDataForTile(renderContext, coord);
19260		const projectionData = getProjectionDataForTile(renderContext, coord, { aligned: align });
19261		const uniformValues = rasterUniformValues(parentTopLeft, parentScaleBy, fadeValues.fadeMix, layer, corners, imageWarp);
19262		const mesh = sourceMesh ?? projection.getMeshFromTileID(context, coord.canonical, useBorder, allowPoles, "raster");
19263		const stencilMode = stencilModes ? stencilModes[coord.overscaledZ] : StencilMode.disabled;
19264		program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, flipCullfaceMode ? CullFaceMode.frontCCW : CullFaceMode.backCCW, uniformValues, terrainData, projectionData, layer.id, mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19265	}
19266}
19267/**
19268* Get fade properties for current tile - either cross-fading or self-fading properties.
19269*/
19270function getFadeProperties(tile, tileManager, fadeDuration, isTerrain) {
19271	const defaults = {
19272		parentTile: null,
19273		parentScaleBy: 1,
19274		parentTopLeft: [0, 0],
19275		fadeValues: {
19276			tileOpacity: 1,
19277			parentTileOpacity: 1,
19278			fadeMix: {
19279				opacity: 1,
19280				mix: 0
19281			}
19282		}
19283	};
19284	if (fadeDuration === 0 || isTerrain) return defaults;
19285	if (tile.fadingParentID) {
19286		const parentTile = tileManager.getLoadedTile(tile.fadingParentID);
19287		if (!parentTile) return defaults;
19288		const parentScaleBy = Math.pow(2, parentTile.tileID.overscaledZ - tile.tileID.overscaledZ);
19289		return {
19290			parentTile,
19291			parentScaleBy,
19292			parentTopLeft: [tile.tileID.canonical.x * parentScaleBy % 1, tile.tileID.canonical.y * parentScaleBy % 1],
19293			fadeValues: getCrossFadeValues(tile, parentTile, fadeDuration)
19294		};
19295	}
19296	if (tile.selfFading) return {
19297		parentTile: null,
19298		parentScaleBy: 1,
19299		parentTopLeft: [0, 0],
19300		fadeValues: getSelfFadeValues(tile, fadeDuration)
19301	};
19302	return defaults;
19303}
19304/**
19305* Cross-fade values for a base tile with a parent tile (for zooming in/out)
19306*/
19307function getCrossFadeValues(tile, parentTile, fadeDuration) {
19308	const currentTime = now();
19309	const timeSinceTile = (currentTime - tile.timeAdded) / fadeDuration;
19310	const timeSinceParent = (currentTime - parentTile.timeAdded) / fadeDuration;
19311	const doFadeIn = tile.fadingDirection === 1;
19312	const opacity1 = clamp(timeSinceTile, 0, 1);
19313	const opacity2 = clamp(1 - timeSinceParent, 0, 1);
19314	const tileOpacity = doFadeIn ? opacity1 : opacity2;
19315	return {
19316		tileOpacity,
19317		parentTileOpacity: doFadeIn ? opacity2 : opacity1,
19318		fadeMix: {
19319			opacity: 1,
19320			mix: 1 - tileOpacity
19321		}
19322	};
19323}
19324/**
19325* Simple fade-in values for tile without a parent (i.e. edge tiles)
19326*/
19327function getSelfFadeValues(tile, fadeDuration) {
19328	const timeSinceTile = (now() - tile.timeAdded) / fadeDuration;
19329	const tileOpacity = clamp(timeSinceTile, 0, 1);
19330	return {
19331		tileOpacity,
19332		fadeMix: {
19333			opacity: tileOpacity,
19334			mix: 0
19335		}
19336	};
19337}
19338//#endregion
19339//#region src/webgl/draw/draw_background.ts
19340function drawBackground(painter, tileManager, layer, coords, renderContext) {
19341	const color = layer.paint.get("background-color");
19342	const opacity = layer.paint.get("background-opacity");
19343	if (opacity === 0) return;
19344	const context = painter.context;
19345	const gl = context.gl;
19346	const projection = painter.style.projection;
19347	const transform = painter.transform;
19348	const tileSize = transform.tileSize;
19349	const image = layer.paint.get("background-pattern");
19350	if (painter.isPatternMissing(image)) return;
19351	const pass = !image && color.a === 1 && opacity === 1 && painter.opaquePassEnabledForLayer() ? "opaque" : "translucent";
19352	if (renderContext.currentPass !== pass) return;
19353	const stencilMode = StencilMode.disabled;
19354	const depthMode = painter.getDepthModeForSublayer(0, pass === "opaque" ? DepthMode.ReadWrite : DepthMode.ReadOnly);
19355	const colorMode = painter.colorModeForRenderPass();
19356	const program = painter.useProgram(image ? "backgroundPattern" : "background");
19357	const tileIDs = coords ? coords : coveringTiles(transform, {
19358		tileSize,
19359		terrain: painter.style.map.terrain
19360	});
19361	if (image) {
19362		context.activeTexture.set(gl.TEXTURE0);
19363		painter.patternAtlas.bind(painter.context);
19364	}
19365	const crossfade = layer.getCrossfadeParameters();
19366	for (const tileID of tileIDs) {
19367		const projectionData = getProjectionDataForTile(renderContext, tileID);
19368		const uniformValues = image ? backgroundPatternUniformValues(opacity, painter, image, {
19369			tileID,
19370			tileSize
19371		}, crossfade) : backgroundUniformValues(opacity, color);
19372		const terrainData = getTerrainDataForTile(renderContext, tileID);
19373		const mesh = projection.getMeshFromTileID(context, tileID.canonical, false, true, "raster");
19374		program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, layer.id, mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19375	}
19376}
19377//#endregion
19378//#region src/webgl/draw/draw_debug.ts
19379const topColor = new Color(1, 0, 0, 1);
19380const btmColor = new Color(0, 1, 0, 1);
19381const leftColor = new Color(0, 0, 1, 1);
19382const rightColor = new Color(1, 0, 1, 1);
19383const centerColor = new Color(0, 1, 1, 1);
19384function drawDebugPadding(painter) {
19385	const padding = painter.transform.padding;
19386	const lineWidth = 3;
19387	drawHorizontalLine(painter, painter.transform.height - (padding.top || 0), lineWidth, topColor);
19388	drawHorizontalLine(painter, padding.bottom || 0, lineWidth, btmColor);
19389	drawVerticalLine(painter, padding.left || 0, lineWidth, leftColor);
19390	drawVerticalLine(painter, painter.transform.width - (padding.right || 0), lineWidth, rightColor);
19391	const center = painter.transform.centerPoint;
19392	drawCrosshair(painter, center.x, painter.transform.height - center.y, centerColor);
19393}
19394function drawCrosshair(painter, x, y, color) {
19395	const size = 20;
19396	const lineWidth = 2;
19397	drawDebugSSRect(painter, x - lineWidth / 2, y - size / 2, lineWidth, size, color);
19398	drawDebugSSRect(painter, x - size / 2, y - lineWidth / 2, size, lineWidth, color);
19399}
19400function drawHorizontalLine(painter, y, lineWidth, color) {
19401	drawDebugSSRect(painter, 0, y + lineWidth / 2, painter.transform.width, lineWidth, color);
19402}
19403function drawVerticalLine(painter, x, lineWidth, color) {
19404	drawDebugSSRect(painter, x - lineWidth / 2, 0, lineWidth, painter.transform.height, color);
19405}
19406function drawDebugSSRect(painter, x, y, width, height, color) {
19407	const context = painter.context;
19408	const gl = context.gl;
19409	gl.enable(gl.SCISSOR_TEST);
19410	gl.scissor(x * painter.pixelRatio, y * painter.pixelRatio, width * painter.pixelRatio, height * painter.pixelRatio);
19411	context.clear({ color });
19412	gl.disable(gl.SCISSOR_TEST);
19413}
19414function drawDebug(painter, tileManager, coords, renderContext) {
19415	for (const coord of coords) drawDebugTile(painter, tileManager, coord, renderContext);
19416}
19417function drawDebugTile(painter, tileManager, coord, renderContext) {
19418	const context = painter.context;
19419	const gl = context.gl;
19420	const program = painter.useProgram("debug");
19421	const depthMode = DepthMode.disabled;
19422	const stencilMode = StencilMode.disabled;
19423	const colorMode = painter.colorModeForRenderPass();
19424	const id = "$debug";
19425	const terrainData = getTerrainDataForTile(renderContext, coord);
19426	context.activeTexture.set(gl.TEXTURE0);
19427	const tileByteLength = tileManager.getTileByID(coord.key).latestRawTileData?.byteLength || 0;
19428	const tileSizeKb = Math.floor(tileByteLength / 1024);
19429	const tileSize = tileManager.getTile(coord).tileSize;
19430	const scaleRatio = 512 / Math.min(tileSize, 512) * (coord.overscaledZ / painter.transform.zoom) * .5;
19431	let tileIdText = coord.canonical.toString();
19432	if (coord.overscaledZ !== coord.canonical.z) tileIdText += ` => ${coord.overscaledZ}`;
19433	drawTextToOverlay(painter, `${tileIdText} ${tileSizeKb}kB`);
19434	const projectionData = getProjectionDataForTile(renderContext, coord);
19435	program.draw(context, gl.TRIANGLES, depthMode, stencilMode, ColorMode.alphaBlended, CullFaceMode.disabled, debugUniformValues(Color.transparent, scaleRatio), null, projectionData, id, painter.debugBuffer, painter.quadTriangleIndexBuffer, painter.debugSegments);
19436	program.draw(context, gl.LINE_STRIP, depthMode, stencilMode, colorMode, CullFaceMode.disabled, debugUniformValues(Color.red), terrainData, projectionData, id, painter.debugBuffer, painter.tileBorderIndexBuffer, painter.debugSegments);
19437}
19438function drawTextToOverlay(painter, text) {
19439	painter.initDebugOverlayCanvas();
19440	const canvas = painter.debugOverlayCanvas;
19441	const gl = painter.context.gl;
19442	const ctx2d = painter.debugOverlayCanvas.getContext("2d");
19443	ctx2d.clearRect(0, 0, canvas.width, canvas.height);
19444	ctx2d.shadowColor = "white";
19445	ctx2d.shadowBlur = 2;
19446	ctx2d.lineWidth = 1.5;
19447	ctx2d.strokeStyle = "white";
19448	ctx2d.textBaseline = "top";
19449	ctx2d.font = `bold 36px Open Sans, sans-serif`;
19450	ctx2d.fillText(text, 5, 5);
19451	ctx2d.strokeText(text, 5, 5);
19452	painter.debugOverlayTexture.update(canvas);
19453	painter.debugOverlayTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
19454}
19455function selectDebugSource(style, zoom) {
19456	let selectedSource = null;
19457	const sources = Object.values(style._layers).flatMap((layer) => {
19458		if (layer.source && !layer.isHidden(zoom)) return [style.tileManagers[layer.source]];
19459		else return [];
19460	});
19461	const vectorSources = sources.filter((source) => source.getSource().type === "vector");
19462	const otherSources = sources.filter((source) => source.getSource().type !== "vector");
19463	const considerSource = (source) => {
19464		if (!selectedSource || selectedSource.getSource().maxzoom < source.getSource().maxzoom) selectedSource = source;
19465	};
19466	for (const source of vectorSources) considerSource(source);
19467	if (!selectedSource) for (const source of otherSources) considerSource(source);
19468	return selectedSource;
19469}
19470//#endregion
19471//#region src/webgl/draw/draw_custom.ts
19472function drawCustom(painter, tileManager, layer, renderContext) {
19473	const { isRenderingGlobe } = renderContext;
19474	const context = painter.context;
19475	const implementation = layer.implementation;
19476	const projection = painter.style.projection;
19477	const transform = painter.transform;
19478	const projectionData = transform.getProjectionDataForCustomLayer(isRenderingGlobe);
19479	const customLayerArgs = {
19480		farZ: transform.farZ,
19481		nearZ: transform.nearZ,
19482		fov: transform.fov * Math.PI / 180,
19483		modelViewProjectionMatrix: transform.modelViewProjectionMatrix,
19484		projectionMatrix: transform.projectionMatrix,
19485		shaderData: {
19486			variantName: projection.shaderVariantName,
19487			vertexShaderPrelude: `const float PI = 3.141592653589793;\nuniform mat4 u_projection_matrix;\n${projection.shaderPreludeCode.vertexSource}`,
19488			define: projection.shaderDefine
19489		},
19490		defaultProjectionData: projectionData,
19491		getProjectionData: (params) => {
19492			return transform.getProjectionData({
19493				overscaledTileID: new OverscaledTileID(params.tileID.canonical.z, params.tileID.wrap ?? 0, params.tileID.canonical.z, params.tileID.canonical.x, params.tileID.canonical.y),
19494				aligned: params.aligned,
19495				applyGlobeMatrix: params.applyGlobeMatrix,
19496				applyTerrainMatrix: params.applyTerrainMatrix
19497			});
19498		}
19499	};
19500	const renderingMode = implementation.renderingMode ? implementation.renderingMode : "2d";
19501	if (renderContext.currentPass === "offscreen") {
19502		const prerender = implementation.prerender;
19503		if (prerender) {
19504			painter.setCustomLayerDefaults();
19505			context.setColorMode(painter.colorModeForRenderPass());
19506			prerender.call(implementation, context.gl, customLayerArgs);
19507			context.setDirty();
19508			painter.setBaseState();
19509		}
19510	} else if (renderContext.currentPass === "translucent") {
19511		painter.setCustomLayerDefaults();
19512		context.setColorMode(painter.colorModeForRenderPass());
19513		context.setStencilMode(StencilMode.disabled);
19514		const depthMode = renderingMode === "3d" ? painter.getDepthModeFor3D() : painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
19515		context.setDepthMode(depthMode);
19516		implementation.render(context.gl, customLayerArgs);
19517		context.setDirty();
19518		painter.setBaseState();
19519		context.bindFramebuffer.set(null);
19520	}
19521}
19522//#endregion
19523//#region src/webgl/draw/draw_terrain.ts
19524/**
19525* Redraw the Depth Framebuffer
19526* @param painter - the painter
19527* @param terrain - the terrain
19528*/
19529function drawDepth(painter, terrain) {
19530	const context = painter.context;
19531	const gl = context.gl;
19532	const tr = painter.transform;
19533	const colorMode = ColorMode.unblended;
19534	const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, [0, 1]);
19535	const tiles = terrain.tileManager.getRenderableTiles();
19536	const program = painter.useProgram("terrainDepth");
19537	context.bindFramebuffer.set(terrain.getFramebuffer().framebuffer);
19538	context.viewport.set([
19539		0,
19540		0,
19541		painter.width / devicePixelRatio,
19542		painter.height / devicePixelRatio
19543	]);
19544	context.clear({
19545		color: Color.white,
19546		depth: 1
19547	});
19548	for (const tile of tiles) {
19549		const mesh = terrain.getTerrainMesh(tile.tileID);
19550		const terrainData = terrain.getTerrainData(tile.tileID);
19551		const projectionData = tr.getProjectionData({
19552			overscaledTileID: tile.tileID,
19553			applyTerrainMatrix: false,
19554			applyGlobeMatrix: true
19555		});
19556		const uniformValues = terrainDepthUniformValues(terrain.getSkirtLength(tr.zoom));
19557		program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, "terrain", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19558	}
19559	context.bindFramebuffer.set(null);
19560	context.viewport.set([
19561		0,
19562		0,
19563		painter.width,
19564		painter.height
19565	]);
19566}
19567function drawTerrain(painter, terrain, tiles, renderContext) {
19568	const { isRenderingGlobe } = renderContext;
19569	const context = painter.context;
19570	const gl = context.gl;
19571	const tr = painter.transform;
19572	const colorMode = painter.colorModeForRenderPass();
19573	const depthMode = painter.getDepthModeFor3D();
19574	const program = painter.useProgram("terrain");
19575	context.bindFramebuffer.set(null);
19576	context.viewport.set([
19577		0,
19578		0,
19579		painter.width,
19580		painter.height
19581	]);
19582	for (const tile of tiles) {
19583		const mesh = terrain.getTerrainMesh(tile.tileID);
19584		const texture = painter.renderToTexture.getTexture(tile);
19585		const terrainData = terrain.getTerrainData(tile.tileID);
19586		context.activeTexture.set(gl.TEXTURE0);
19587		texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_LINEAR);
19588		const eleDelta = terrain.getSkirtLength(tr.zoom);
19589		const fogMatrix = tr.calculateFogMatrix(tile.tileID.toUnwrapped());
19590		const uniformValues = terrainUniformValues(eleDelta, fogMatrix, painter.style.sky, tr.pitch, isRenderingGlobe);
19591		const projectionData = getProjectionDataForTile(renderContext, tile.tileID, { applyTerrainMatrix: false });
19592		program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, "terrain", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19593	}
19594}
19595//#endregion
19596//#region src/webgl/draw/draw_sky.ts
19597function getMesh(context, sky) {
19598	if (!sky.mesh) {
19599		const vertexArray = new PosArray();
19600		vertexArray.emplaceBack(-1, -1);
19601		vertexArray.emplaceBack(1, -1);
19602		vertexArray.emplaceBack(1, 1);
19603		vertexArray.emplaceBack(-1, 1);
19604		const indexArray = new TriangleIndexArray();
19605		indexArray.emplaceBack(0, 1, 2);
19606		indexArray.emplaceBack(0, 2, 3);
19607		sky.mesh = new Mesh(context.createVertexBuffer(vertexArray, posAttributes.members), context.createIndexBuffer(indexArray), SegmentVector.simpleSegment(0, 0, vertexArray.length, indexArray.length));
19608	}
19609	return sky.mesh;
19610}
19611function drawSky(painter, sky) {
19612	const context = painter.context;
19613	const gl = context.gl;
19614	const skyUniforms = skyUniformValues(sky, painter.transform, painter.pixelRatio);
19615	const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, [0, 1]);
19616	const stencilMode = StencilMode.disabled;
19617	const colorMode = painter.colorModeForRenderPass();
19618	const program = painter.useProgram("sky");
19619	const mesh = getMesh(context, sky);
19620	program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled, skyUniforms, null, void 0, "sky", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19621}
19622function getSunPos(light, transform) {
19623	const lightPos = light.getCartesianPosition();
19624	negate(lightPos, lightPos);
19625	const lightMat = identity(/* @__PURE__ */ new Float64Array(16));
19626	if (light.properties.get("anchor") === "map") {
19627		rotateZ(lightMat, lightMat, transform.rollInRadians);
19628		rotateX(lightMat, lightMat, -transform.pitchInRadians);
19629		rotateZ(lightMat, lightMat, transform.bearingInRadians);
19630		rotateX(lightMat, lightMat, transform.center.lat * Math.PI / 180);
19631		rotateY$1(lightMat, lightMat, -transform.center.lng * Math.PI / 180);
19632	}
19633	transformMat4$1(lightPos, lightPos, lightMat);
19634	return lightPos;
19635}
19636function drawAtmosphere(painter, sky, light) {
19637	const context = painter.context;
19638	const gl = context.gl;
19639	const program = painter.useProgram("atmosphere");
19640	const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadOnly, [0, 1]);
19641	const transform = painter.transform;
19642	const sunPos = getSunPos(light, painter.transform);
19643	const projectionData = transform.getProjectionData({
19644		overscaledTileID: null,
19645		applyGlobeMatrix: true,
19646		applyTerrainMatrix: true
19647	});
19648	const globeRadius = getGlobeRadiusPixels(transform.worldSize, transform.center.lat);
19649	const globePosition = getGlobeCenterInViewSpace(transform);
19650	const altitudeBlend = getAtmosphereAltitudeBlend(length(globePosition) - globeRadius, globeRadius);
19651	const atmosphereBlend = sky.properties.get("atmosphere-blend") * projectionData.projectionTransition * altitudeBlend;
19652	if (atmosphereBlend === 0) return;
19653	const uniformValues = atmosphereUniformValues(sunPos, atmosphereBlend, globePosition, globeRadius, transform.inverseProjectionMatrix);
19654	const mesh = getMesh(context, sky);
19655	program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, ColorMode.alphaBlended, CullFaceMode.disabled, uniformValues, null, null, "atmosphere", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19656}
19657//#endregion
19658//#region src/webgl/draw/index.ts
19659const webglDrawFunctions = {
vendor: 6,566 bytes, lines 19660-19828
19660	symbol: drawSymbols,
19661	circle: drawCircles,
19662	heatmap: drawHeatmap,
19663	line: drawLine,
19664	fill: drawFill,
19665	fillExtrusion: drawFillExtrusion,
19666	hillshade: drawHillshade,
19667	colorRelief: drawColorRelief,
19668	raster: drawRaster,
19669	background: drawBackground,
19670	sky: drawSky,
19671	atmosphere: drawAtmosphere,
19672	custom: drawCustom,
19673	debug: drawDebug,
19674	debugPadding: drawDebugPadding,
19675	terrainDepth: drawDepth
19676};
19677//#endregion
19678//#region src/render/painter.ts
19679/**
19680* @internal
19681* Initialize a new painter object.
19682*/
19683var Painter = class Painter {
19684	constructor(gl, transform) {
19685		this.drawFunctions = webglDrawFunctions;
19686		this.context = new Context(gl);
19687		this.transform = transform;
19688		this.layerOpacityFbo = null;
19689		this._tileTextures = {};
19690		this._rttObjectRecyclePool = [];
19691		this._rttSharedFbo = null;
19692		this.terrainFacilitator = {
19693			depthDirty: true,
19694			matrix: identity(/* @__PURE__ */ new Float64Array(16)),
19695			renderTime: 0
19696		};
19697		this.setup();
19698		this.numSublayers = TileManager.maxOverzooming + TileManager.maxUnderzooming + 1;
19699		this.depthEpsilon = 1 / Math.pow(2, 16);
19700		this.crossTileSymbolIndex = new CrossTileSymbolIndex();
19701	}
19702	resize(width, height, pixelRatio) {
19703		this.width = Math.floor(width * pixelRatio);
19704		this.height = Math.floor(height * pixelRatio);
19705		this.pixelRatio = pixelRatio;
19706		this.context.viewport.set([
19707			0,
19708			0,
19709			this.width,
19710			this.height
19711		]);
19712		if (this.style) for (const layerId of this.style._order) this.style._layers[layerId].resize();
19713	}
19714	setup() {
19715		const context = this.context;
19716		const tileExtentArray = new PosArray();
19717		tileExtentArray.emplaceBack(0, 0);
19718		tileExtentArray.emplaceBack(EXTENT, 0);
19719		tileExtentArray.emplaceBack(0, EXTENT);
19720		tileExtentArray.emplaceBack(EXTENT, EXTENT);
19721		this.tileExtentBuffer = context.createVertexBuffer(tileExtentArray, posAttributes.members);
19722		this.tileExtentSegments = SegmentVector.simpleSegment(0, 0, 4, 2);
19723		const debugArray = new PosArray();
19724		debugArray.emplaceBack(0, 0);
19725		debugArray.emplaceBack(EXTENT, 0);
19726		debugArray.emplaceBack(0, EXTENT);
19727		debugArray.emplaceBack(EXTENT, EXTENT);
19728		this.debugBuffer = context.createVertexBuffer(debugArray, posAttributes.members);
19729		this.debugSegments = SegmentVector.simpleSegment(0, 0, 4, 5);
19730		const rasterBoundsArray = new RasterBoundsArray();
19731		rasterBoundsArray.emplaceBack(0, 0, 0, 0);
19732		rasterBoundsArray.emplaceBack(EXTENT, 0, EXTENT, 0);
19733		rasterBoundsArray.emplaceBack(0, EXTENT, 0, EXTENT);
19734		rasterBoundsArray.emplaceBack(EXTENT, EXTENT, EXTENT, EXTENT);
19735		this.rasterBoundsBuffer = context.createVertexBuffer(rasterBoundsArray, rasterBoundsAttributes.members);
19736		this.rasterBoundsSegments = SegmentVector.simpleSegment(0, 0, 4, 2);
19737		const rasterBoundsArrayPosOnly = new PosArray();
19738		rasterBoundsArrayPosOnly.emplaceBack(0, 0);
19739		rasterBoundsArrayPosOnly.emplaceBack(EXTENT, 0);
19740		rasterBoundsArrayPosOnly.emplaceBack(0, EXTENT);
19741		rasterBoundsArrayPosOnly.emplaceBack(EXTENT, EXTENT);
19742		this.rasterBoundsBufferPosOnly = context.createVertexBuffer(rasterBoundsArrayPosOnly, posAttributes.members);
19743		this.rasterBoundsSegmentsPosOnly = SegmentVector.simpleSegment(0, 0, 4, 5);
19744		const viewportArray = new PosArray();
19745		viewportArray.emplaceBack(0, 0);
19746		viewportArray.emplaceBack(1, 0);
19747		viewportArray.emplaceBack(0, 1);
19748		viewportArray.emplaceBack(1, 1);
19749		this.viewportBuffer = context.createVertexBuffer(viewportArray, posAttributes.members);
19750		this.viewportSegments = SegmentVector.simpleSegment(0, 0, 4, 2);
19751		const tileLineStripIndices = new LineStripIndexArray();
19752		tileLineStripIndices.emplaceBack(0);
19753		tileLineStripIndices.emplaceBack(1);
19754		tileLineStripIndices.emplaceBack(3);
19755		tileLineStripIndices.emplaceBack(2);
19756		tileLineStripIndices.emplaceBack(0);
19757		this.tileBorderIndexBuffer = context.createIndexBuffer(tileLineStripIndices);
19758		const quadTriangleIndices = new TriangleIndexArray();
19759		quadTriangleIndices.emplaceBack(1, 0, 2);
19760		quadTriangleIndices.emplaceBack(1, 2, 3);
19761		this.quadTriangleIndexBuffer = context.createIndexBuffer(quadTriangleIndices);
19762		const gl = this.context.gl;
19763		this.stencilClearMode = new StencilMode({
19764			func: gl.ALWAYS,
19765			mask: 0
19766		}, 0, 255, gl.ZERO, gl.ZERO, gl.ZERO);
19767		this.tileExtentMesh = new Mesh(this.tileExtentBuffer, this.quadTriangleIndexBuffer, this.tileExtentSegments);
19768	}
19769	clearStencil() {
19770		const context = this.context;
19771		const gl = context.gl;
19772		this.nextStencilID = 1;
19773		this.currentStencilSource = void 0;
19774		const matrix = create$1();
19775		ortho(matrix, 0, this.width, this.height, 0, 0, 1);
19776		scale(matrix, matrix, [
19777			gl.drawingBufferWidth,
19778			gl.drawingBufferHeight,
19779			0
19780		]);
19781		const projectionData = {
19782			mainMatrix: matrix,
19783			tileMercatorCoords: [
19784				0,
19785				0,
19786				1,
19787				1
19788			],
19789			clippingPlane: [
19790				0,
19791				0,
19792				0,
19793				0
19794			],
19795			projectionTransition: 0,
19796			fallbackMatrix: matrix,
19797			clipAntimeridian: false
19798		};
19799		this.useProgram("clippingMask", null, true).draw(context, gl.TRIANGLES, DepthMode.disabled, this.stencilClearMode, ColorMode.disabled, CullFaceMode.disabled, null, null, projectionData, "$clipping", this.viewportBuffer, this.quadTriangleIndexBuffer, this.viewportSegments);
19800	}
19801	renderTileClippingMasks(layer, tileIDs) {
19802		if (this.currentStencilSource === layer.source || !layer.isTileClipped() || !tileIDs?.length) return;
19803		this.currentStencilSource = layer.source;
19804		if (this.nextStencilID + tileIDs.length > 256) this.clearStencil();
19805		const context = this.context;
19806		context.setColorMode(ColorMode.disabled);
19807		context.setDepthMode(DepthMode.disabled);
19808		const stencilRefs = {};
19809		for (const tileID of tileIDs) stencilRefs[tileID.key] = this.nextStencilID++;
19810		if (this.style.projection.useSubdivision) this._renderTileMasks(stencilRefs, tileIDs, true);
19811		this._renderTileMasks(stencilRefs, tileIDs, false);
19812		this._tileClippingMaskIDs = stencilRefs;
19813	}
19814	_renderTileMasks(tileStencilRefs, tileIDs, useBorders) {
19815		const context = this.context;
19816		const gl = context.gl;
19817		const projection = this.style.projection;
19818		const renderContext = this.renderContext;
19819		const program = this.useProgram("clippingMask");
19820		for (const tileID of tileIDs) {
19821			const stencilRef = tileStencilRefs[tileID.key];
19822			const terrainData = getTerrainDataForTile(renderContext, tileID);
19823			const mesh = projection.getMeshFromTileID(this.context, tileID.canonical, useBorders, true, "stencil");
19824			const projectionData = getProjectionDataForTile(renderContext, tileID);
19825			program.draw(context, gl.TRIANGLES, DepthMode.disabled, new StencilMode({
19826				func: gl.ALWAYS,
19827				mask: 0
19828			}, stencilRef, 255, gl.KEEP, gl.KEEP, gl.REPLACE), ColorMode.disabled, renderContext.
vendor: 1,869 bytes, lines 19828-19870
19828isRenderingToTexture ? CullFaceMode.disabled : CullFaceMode.backCCW, null, terrainData, projectionData, "$clipping", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19829		}
19830	}
19831	/**
19832	* Fills the depth buffer with the geometry of all supplied tiles.
19833	* Does not change the color buffer or the stencil buffer.
19834	*/
19835	_renderTilesDepthBuffer() {
19836		const context = this.context;
19837		const gl = context.gl;
19838		const projection = this.style.projection;
19839		const transform = this.transform;
19840		const program = this.useProgram("depth");
19841		const depthMode = this.getDepthModeFor3D();
19842		const tileIDs = coveringTiles(transform, { tileSize: transform.tileSize });
19843		for (const tileID of tileIDs) {
19844			const terrainData = getTerrainDataForTile(this.renderContext, tileID);
19845			const mesh = projection.getMeshFromTileID(this.context, tileID.canonical, true, true, "raster");
19846			const projectionData = getProjectionDataForTile(this.renderContext, tileID);
19847			program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, ColorMode.disabled, CullFaceMode.backCCW, null, terrainData, projectionData, "$clipping", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19848		}
19849	}
19850	stencilModeFor3D() {
19851		this.currentStencilSource = void 0;
19852		if (this.nextStencilID + 1 > 256) this.clearStencil();
19853		const id = this.nextStencilID++;
19854		const gl = this.context.gl;
19855		return new StencilMode({
19856			func: gl.NOTEQUAL,
19857			mask: 255
19858		}, id, 255, gl.KEEP, gl.KEEP, gl.REPLACE);
19859	}
19860	stencilModeForClipping(tileID) {
19861		const gl = this.context.gl;
19862		return new StencilMode({
19863			func: gl.EQUAL,
19864			mask: 255
19865		}, this._tileClippingMaskIDs[tileID.key], 0, gl.KEEP, gl.KEEP, gl.REPLACE);
19866	}
19867	getStencilConfigForOverlapAndUpdateStencilID(tileIDs) {
19868		const gl = this.context.gl;
19869		const coords = tileIDs.sort((a, b) => b.overscaledZ - a.overscaledZ);
19870		const minTileZ = coords[coords.length - 1].overscaledZ;
vendor: 5,660 bytes, lines 19871-20020
19871		const stencilValues = coords[0].overscaledZ - minTileZ + 1;
19872		if (stencilValues > 1) {
19873			this.currentStencilSource = void 0;
19874			if (this.nextStencilID + stencilValues > 256) this.clearStencil();
19875			const zToStencilMode = {};
19876			for (let i = 0; i < stencilValues; i++) zToStencilMode[i + minTileZ] = new StencilMode({
19877				func: gl.GEQUAL,
19878				mask: 255
19879			}, i + this.nextStencilID, 255, gl.KEEP, gl.KEEP, gl.REPLACE);
19880			this.nextStencilID += stencilValues;
19881			return [zToStencilMode, coords];
19882		}
19883		return [{ [minTileZ]: StencilMode.disabled }, coords];
19884	}
19885	stencilConfigForOverlapTwoPass(tileIDs) {
19886		const gl = this.context.gl;
19887		const coords = tileIDs.sort((a, b) => b.overscaledZ - a.overscaledZ);
19888		const minTileZ = coords[coords.length - 1].overscaledZ;
19889		const stencilValues = coords[0].overscaledZ - minTileZ + 1;
19890		this.clearStencil();
19891		if (stencilValues > 1) {
19892			const zToStencilModeHigh = {};
19893			const zToStencilModeLow = {};
19894			for (let i = 0; i < stencilValues; i++) {
19895				zToStencilModeHigh[i + minTileZ] = new StencilMode({
19896					func: gl.GREATER,
19897					mask: 255
19898				}, stencilValues + 1 + i, 255, gl.KEEP, gl.KEEP, gl.REPLACE);
19899				zToStencilModeLow[i + minTileZ] = new StencilMode({
19900					func: gl.GREATER,
19901					mask: 255
19902				}, 1 + i, 255, gl.KEEP, gl.KEEP, gl.REPLACE);
19903			}
19904			this.nextStencilID = stencilValues * 2 + 1;
19905			return [
19906				zToStencilModeHigh,
19907				zToStencilModeLow,
19908				coords
19909			];
19910		} else {
19911			this.nextStencilID = 3;
19912			return [
19913				{ [minTileZ]: new StencilMode({
19914					func: gl.GREATER,
19915					mask: 255
19916				}, 2, 255, gl.KEEP, gl.KEEP, gl.REPLACE) },
19917				{ [minTileZ]: new StencilMode({
19918					func: gl.GREATER,
19919					mask: 255
19920				}, 1, 255, gl.KEEP, gl.KEEP, gl.REPLACE) },
19921				coords
19922			];
19923		}
19924	}
19925	colorModeForRenderPass() {
19926		const gl = this.context.gl;
19927		if (this._showOverdrawInspector) {
19928			const a = 1 / 8;
19929			return new ColorMode([gl.CONSTANT_COLOR, gl.ONE], new Color(a, a, a, 0), [
19930				true,
19931				true,
19932				true,
19933				true
19934			]);
19935		} else if (this.renderContext.currentPass === "opaque") return ColorMode.unblended;
19936		else return ColorMode.alphaBlended;
19937	}
19938	getDepthModeForSublayer(n, mask, func) {
19939		if (!this.opaquePassEnabledForLayer()) return DepthMode.disabled;
19940		const depth = 1 - ((1 + this.renderContext.currentLayer) * this.numSublayers + n) * this.depthEpsilon;
19941		return new DepthMode(func || this.context.gl.LEQUAL, mask, [depth, depth]);
19942	}
19943	getDepthModeFor3D() {
19944		return new DepthMode(this.context.gl.LEQUAL, DepthMode.ReadWrite, this.renderContext.depthRangeFor3D);
19945	}
19946	opaquePassEnabledForLayer() {
19947		return this.renderContext.currentLayer < this.renderContext.opaquePassCutoff;
19948	}
19949	render(style, options) {
19950		this.style = style;
19951		this.options = options;
19952		const renderContext = this.renderContext = createRenderContext(this.transform, style.projection, style.map.terrain ?? null);
19953		this.lineAtlas = style.lineAtlas;
19954		this.imageManager = style.imageManager;
19955		this.patternAtlas = style.patternAtlas;
19956		this.glyphManager = style.glyphManager;
19957		this.symbolFadeChange = style.placement.symbolFadeChange(now());
19958		updateFrameUniformBuffer(this.context.frameUniformBuffer, this);
19959		this.imageManager.beginFrame();
19960		const layerIds = this.style._order;
19961		const tileManagers = this.style.tileManagers;
19962		const coordsAscending = {};
19963		const coordsDescending = {};
19964		const coordsDescendingSymbol = {};
19965		for (const id in tileManagers) {
19966			const tileManager = tileManagers[id];
19967			if (tileManager.used) tileManager.prepare(this.context);
19968			coordsAscending[id] = tileManager.getVisibleCoordinates(false);
19969			coordsDescending[id] = coordsAscending[id].slice().reverse();
19970			coordsDescendingSymbol[id] = tileManager.getVisibleCoordinates(true).reverse();
19971		}
19972		renderContext.opaquePassCutoff = Infinity;
19973		for (let i = 0; i < layerIds.length; i++) {
19974			const layerId = layerIds[i];
19975			if (this.style._layers[layerId].is3D()) {
19976				renderContext.opaquePassCutoff = i;
19977				break;
19978			}
19979		}
19980		this.maybeDrawDepth();
19981		if (this.renderToTexture) {
19982			this.renderToTexture.prepareForRender(this.style, this.transform.zoom);
19983			renderContext.opaquePassCutoff = 0;
19984		}
19985		renderContext.currentPass = "offscreen";
19986		for (const layerId of layerIds) {
19987			const layer = this.style._layers[layerId];
19988			if (!layer.hasOffscreenPass() || layer.isHidden(this.transform.zoom)) continue;
19989			const coords = coordsDescending[layer.source];
19990			if (layer.type !== "custom" && !coords.length) continue;
19991			this.renderLayer(this, tileManagers[layer.source], layer, coords, renderContext);
19992		}
19993		this.context.viewport.set([
19994			0,
19995			0,
19996			this.width,
19997			this.height
19998		]);
19999		this.context.bindFramebuffer.set(null);
20000		this.context.clear({
20001			color: options.showOverdrawInspector ? Color.black : Color.transparent,
20002			depth: 1
20003		});
20004		this.clearStencil();
20005		if (this.style.sky) this.drawFunctions.sky(this, this.style.sky);
20006		this._showOverdrawInspector = options.showOverdrawInspector;
20007		renderContext.depthRangeFor3D = [0, 1 - (style._order.length + 2) * this.numSublayers * this.depthEpsilon];
20008		if (!this.renderToTexture) {
20009			renderContext.currentPass = "opaque";
20010			for (renderContext.currentLayer = layerIds.length - 1; renderContext.currentLayer >= 0; renderContext.currentLayer--) {
20011				const layer = this.style._layers[layerIds[renderContext.currentLayer]];
20012				if (layer.isHidden(this.transform.zoom)) continue;
20013				const tileManager = tileManagers[layer.source];
20014				const coords = coordsAscending[layer.source];
20015				this.renderTileClippingMasks(layer, coords);
20016				this.renderLayer(this, tileManager, layer, coords, renderContext);
20017			}
20018		}
20019		renderContext.currentPass = "translucent";
20020		let globeDepthRendered = false;
vendor: 8,619 bytes, lines 20021-20207
20021		for (renderContext.currentLayer = 0; renderContext.currentLayer < layerIds.length; renderContext.currentLayer++) {
20022			const layer = this.style._layers[layerIds[renderContext.currentLayer]];
20023			if (layer.isHidden(this.transform.zoom)) continue;
20024			const tileManager = tileManagers[layer.source];
20025			if (this.renderToTexture?.renderLayer(layer, renderContext)) continue;
20026			if (!this.opaquePassEnabledForLayer() && !globeDepthRendered) {
20027				globeDepthRendered = true;
20028				if (renderContext.isRenderingGlobe && !this.style.map.terrain) this._renderTilesDepthBuffer();
20029			}
20030			const coords = (layer.type === "symbol" ? coordsDescendingSymbol : coordsDescending)[layer.source];
20031			this.renderTileClippingMasks(layer, coordsAscending[layer.source]);
20032			this.renderLayer(this, tileManager, layer, coords, renderContext);
20033		}
20034		if (renderContext.isRenderingGlobe) this.drawFunctions.atmosphere(this, this.style.sky, this.style.light);
20035		if (this.options.showTileBoundaries) {
20036			const selectedSource = selectDebugSource(this.style, this.transform.zoom);
20037			if (selectedSource) this.drawFunctions.debug(this, selectedSource, selectedSource.getVisibleCoordinates(), renderContext);
20038		}
20039		if (this.options.showPadding) this.drawFunctions.debugPadding(this);
20040		if (this.renderToTexture && !this.options.moving) this.clearRTTPool();
20041		this.context.setDefault();
20042	}
20043	/**
20044	* Invalidates cached terrain depth so the next eligible depth pass redraws it.
20045	* DEM data can change the rendered surface while the camera and terrain tile set stay unchanged.
20046	* Repeated calls coalesce without rendering or scheduling a frame, even if terrain is not yet present.
20047	*/
20048	markTerrainDepthDirty() {
20049		this.terrainFacilitator.depthDirty = true;
20050	}
20051	/**
20052	* Updates the depth framebuffer after explicit invalidation, camera movement, or tile reloading.
20053	*/
20054	maybeDrawDepth() {
20055		if (!this.style?.projection || !this.style.map?.terrain) return;
20056		const prevMatrix = this.terrainFacilitator.matrix;
20057		const currMatrix = this.transform.modelViewProjectionMatrix;
20058		let doUpdate = this.terrainFacilitator.depthDirty;
20059		doUpdate ||= !equals(prevMatrix, currMatrix);
20060		doUpdate ||= this.style.map.terrain.tileManager.anyTilesAfterTime(this.terrainFacilitator.renderTime);
20061		if (!doUpdate) return;
20062		copy(prevMatrix, currMatrix);
20063		this.terrainFacilitator.renderTime = now();
20064		this.terrainFacilitator.depthDirty = false;
20065		this.drawFunctions.terrainDepth(this, this.style.map.terrain);
20066	}
20067	renderLayer(painter, tileManager, layer, coords, renderContext) {
20068		if (layer.isHidden(this.transform.zoom)) return;
20069		if (layer.type !== "background" && layer.type !== "custom" && !(coords || []).length) return;
20070		this.id = layer.id;
20071		const draw = this.drawFunctions;
20072		if (isSymbolStyleLayer(layer)) draw.symbol(painter, tileManager, layer, coords, this.style.placement.variableOffsets, renderContext);
20073		else if (isCircleStyleLayer(layer)) draw.circle(painter, tileManager, layer, coords, renderContext);
20074		else if (isHeatmapStyleLayer(layer)) draw.heatmap(painter, tileManager, layer, coords, renderContext);
20075		else if (isLineStyleLayer(layer)) draw.line(painter, tileManager, layer, coords, renderContext);
20076		else if (isFillStyleLayer(layer)) draw.fill(painter, tileManager, layer, coords, renderContext);
20077		else if (isFillExtrusionStyleLayer(layer)) draw.fillExtrusion(painter, tileManager, layer, coords, renderContext);
20078		else if (isHillshadeStyleLayer(layer)) draw.hillshade(painter, tileManager, layer, coords, renderContext);
20079		else if (isColorReliefStyleLayer(layer)) draw.colorRelief(painter, tileManager, layer, coords, renderContext);
20080		else if (isRasterStyleLayer(layer)) draw.raster(painter, tileManager, layer, coords, renderContext);
20081		else if (isBackgroundStyleLayer(layer)) draw.background(painter, tileManager, layer, coords, renderContext);
20082		else if (isCustomStyleLayer(layer)) draw.custom(painter, tileManager, layer, renderContext);
20083	}
20084	static {
20085		this.MAX_TEXTURE_POOL_SIZE_PER_BUCKET = 50;
20086	}
20087	saveTileTexture(texture) {
20088		const textures = this._tileTextures[texture.size[0]];
20089		if (!textures) this._tileTextures[texture.size[0]] = [texture];
20090		else if (textures.length < Painter.MAX_TEXTURE_POOL_SIZE_PER_BUCKET) textures.push(texture);
20091		else texture.destroy();
20092	}
20093	getTileTexture(size) {
20094		const textures = this._tileTextures[size];
20095		return textures && textures.length > 0 ? textures.pop() : null;
20096	}
20097	acquireRTT(size) {
20098		const gl = this.context.gl;
20099		const obj = this._rttObjectRecyclePool.pop();
20100		if (obj) {
20101			if (obj.size !== size) {
20102				obj.texture.update({
20103					width: size,
20104					height: size,
20105					data: null
20106				}, {
20107					premultiply: false,
20108					useMipmap: true
20109				});
20110				obj.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_LINEAR);
20111				if (this.context.extTextureFilterAnisotropic) gl.texParameterf(gl.TEXTURE_2D, this.context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, this.context.extTextureFilterAnisotropicMax);
20112				obj.size = size;
20113			}
20114			return obj;
20115		}
20116		const texture = new Texture(this.context, {
20117			width: size,
20118			height: size,
20119			data: null
20120		}, gl.RGBA, {
20121			premultiply: false,
20122			useMipmap: true
20123		});
20124		texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_LINEAR);
20125		if (this.context.extTextureFilterAnisotropic) gl.texParameterf(gl.TEXTURE_2D, this.context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, this.context.extTextureFilterAnisotropicMax);
20126		return {
20127			texture,
20128			size
20129		};
20130	}
20131	/**
20132	* Binds the shared RTT FBO with the given RTTObject's texture attached
20133	* as color target. Creates / resizes the shared FBO and depth-stencil
20134	* renderbuffer lazily.
20135	*/
20136	bindRTT(obj) {
20137		const gl = this.context.gl;
20138		const size = obj.size;
20139		if (!this._rttSharedFbo) {
20140			const fbo = this.context.createFramebuffer(size, size, true, true);
20141			const depthRenderbuffer = this.context.createRenderbuffer(gl.DEPTH_STENCIL, size, size);
20142			fbo.depthAttachment.set(depthRenderbuffer);
20143			this._rttSharedFbo = {
20144				fbo,
20145				depthRenderbuffer,
20146				size
20147			};
20148		}
20149		if (this._rttSharedFbo.size !== size) {
20150			this.context.bindRenderbuffer.set(this._rttSharedFbo.depthRenderbuffer);
20151			gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, size, size);
20152			this.context.bindRenderbuffer.set(null);
20153			this._rttSharedFbo.fbo.width = size;
20154			this._rttSharedFbo.fbo.height = size;
20155			this._rttSharedFbo.size = size;
20156		}
20157		this._rttSharedFbo.fbo.colorAttachment.set(obj.texture.texture);
20158		this.context.bindFramebuffer.set(this._rttSharedFbo.fbo.framebuffer);
20159	}
20160	releaseRTT(obj) {
20161		this._rttObjectRecyclePool.push(obj);
20162	}
20163	/**
20164	* Destroys the pooled {@link RTTObject}s, the drapes no tile holds. Called at the end of a frame at rest,
20165	* when they would otherwise stay resident until the next gesture reuses them.
20166	*/
20167	clearRTTPool() {
20168		for (const obj of this._rttObjectRecyclePool) obj.texture.destroy();
20169		this._rttObjectRecyclePool.length = 0;
20170	}
20171	/**
20172	* Frees the pooled drapes and the shared render-to-texture FBO. Called when terrain is removed, after its tiles
20173	* released their drapes.
20174	*/
20175	destroyRTTResources() {
20176		this.clearRTTPool();
20177		if (!this._rttSharedFbo) return;
20178		this._rttSharedFbo.fbo.colorAttachment.set(null);
20179		this._rttSharedFbo.fbo.depthAttachment.set(null);
20180		const gl = this.context.gl;
20181		gl.deleteRenderbuffer(this._rttSharedFbo.depthRenderbuffer);
20182		gl.deleteFramebuffer(this._rttSharedFbo.fbo.framebuffer);
20183		this._rttSharedFbo = null;
20184	}
20185	/**
20186	* Checks whether a pattern image is needed, and if it is, whether it is not loaded.
20187	*
20188	* @returns true if a needed image is missing and rendering needs to be skipped.
20189	*/
20190	isPatternMissing(image) {
20191		if (!image) return false;
20192		if (!image.from || !image.to) return true;
20193		const imagePosA = this.patternAtlas.getPattern(image.from.toString());
20194		const imagePosB = this.patternAtlas.getPattern(image.to.toString());
20195		return !imagePosA || !imagePosB;
20196	}
20197	/**
20198	* Finds the required shader and its variant (base/terrain/globe, etc.) and binds it, compiling a new shader if required.
20199	* @param name - Name of the desired shader.
20200	* @param programConfiguration - Configuration of shader's inputs.
20201	* @param forceSimpleProjection - Whether to force the use of a shader variant with simple mercator projection vertex shader.
20202	* @param defines - Additional macros to be injected at the beginning of the shader. Expected format is `['#define XYZ']`, etc.
20203	* False by default. Use true when drawing with a simple projection matrix is desired, eg. when drawing a fullscreen quad.
20204	* @returns
20205	*/
20206	useProgram(name, programConfiguration, forceSimpleProjection = false, defines = []) {
20207		this.cache ||= {};
vendor: 4,457 bytes, lines 20208-20307
20208		const useTerrain = !!this.style.map.terrain;
20209		const projection = this.style.projection;
20210		const projectionPrelude = forceSimpleProjection ? shaders.projectionMercator : projection.shaderPreludeCode;
20211		const projectionDefine = forceSimpleProjection ? MercatorShaderDefine : projection.shaderDefine;
20212		const projectionKey = `/${forceSimpleProjection ? MercatorShaderVariantKey : projection.shaderVariantName}`;
20213		const configurationKey = programConfiguration ? programConfiguration.cacheKey : "";
20214		const overdrawKey = this._showOverdrawInspector ? "/overdraw" : "";
20215		const terrainKey = useTerrain ? "/terrain" : "";
20216		const definesKey = defines ? `/${defines.join("/")}` : "";
20217		const key = name + configurationKey + projectionKey + overdrawKey + terrainKey + definesKey;
20218		this.cache[key] ||= new Program(this.context, shaders[name], programConfiguration, programUniforms[name], this._showOverdrawInspector, useTerrain, projectionPrelude, projectionDefine, defines);
20219		return this.cache[key];
20220	}
20221	setCustomLayerDefaults() {
20222		this.context.setCustomLayerDefaults();
20223	}
20224	setBaseState() {
20225		const gl = this.context.gl;
20226		this.context.cullFace.set(false);
20227		this.context.viewport.set([
20228			0,
20229			0,
20230			this.width,
20231			this.height
20232		]);
20233		this.context.blendEquation.set(gl.FUNC_ADD);
20234	}
20235	initDebugOverlayCanvas() {
20236		if (this.debugOverlayCanvas == null) {
20237			this.debugOverlayCanvas = document.createElement("canvas");
20238			this.debugOverlayCanvas.width = 512;
20239			this.debugOverlayCanvas.height = 512;
20240			const gl = this.context.gl;
20241			this.debugOverlayTexture = new Texture(this.context, this.debugOverlayCanvas, gl.RGBA);
20242		}
20243	}
20244	destroy() {
20245		if (this._tileTextures) {
20246			for (const size in this._tileTextures) {
20247				const textures = this._tileTextures[size];
20248				if (textures) for (const texture of textures) texture.destroy();
20249			}
20250			this._tileTextures = {};
20251		}
20252		this.destroyRTTResources();
20253		this.layerOpacityFbo?.destroy();
20254		this.layerOpacityFbo = null;
20255		if (this.tileExtentBuffer) this.tileExtentBuffer.destroy();
20256		if (this.debugBuffer) this.debugBuffer.destroy();
20257		if (this.rasterBoundsBuffer) this.rasterBoundsBuffer.destroy();
20258		if (this.rasterBoundsBufferPosOnly) this.rasterBoundsBufferPosOnly.destroy();
20259		if (this.viewportBuffer) this.viewportBuffer.destroy();
20260		if (this.tileBorderIndexBuffer) this.tileBorderIndexBuffer.destroy();
20261		if (this.quadTriangleIndexBuffer) this.quadTriangleIndexBuffer.destroy();
20262		if (this.tileExtentMesh) this.tileExtentMesh.vertexBuffer?.destroy();
20263		if (this.tileExtentMesh) this.tileExtentMesh.indexBuffer?.destroy();
20264		if (this.debugOverlayTexture) this.debugOverlayTexture.destroy();
20265		this.context.projectionUniformBuffer.destroy();
20266		this.context.terrainUniformBuffer.destroy();
20267		this.context.frameUniformBuffer.destroy();
20268		if (this.cache) {
20269			for (const key in this.cache) {
20270				const program = this.cache[key];
20271				if (program?.program) this.context.gl.deleteProgram(program.program);
20272			}
20273			this.cache = {};
20274		}
20275		if (this.context) this.context.setDefault();
20276	}
20277	overLimit() {
20278		const { drawingBufferWidth, drawingBufferHeight } = this.context.gl;
20279		return this.width !== drawingBufferWidth || this.height !== drawingBufferHeight;
20280	}
20281};
20282//#endregion
20283//#region src/util/gpu_initialization_error.ts
20284/**
20285* Thrown by the `Map` constructor when a GPU rendering context cannot be created,
20286* or fired via the map's `error` event when recreating the context after a `webglcontextrestored` fails.
20287*
20288* Carries the canvas attributes that were requested and, when the browser provided one,
20289* the originating `webglcontextcreationerror` `statusMessage`.
20290* Consumers can branch on `instanceof GPUInitializationError` and inspect the cause programmatically.
20291* @see https://wiki.openstreetmap.org/wiki/This_map_requires_WebGL
20292*/
20293var GPUInitializationError = class extends Error {
20294	constructor(requestedAttributes, creationEvent) {
20295		super("WebGL2 is required to display this map. We are sorry, but it seems that your browser does not support WebGL2, a technology for rendering 3D graphics on the web. Read more on https://wiki.openstreetmap.org/wiki/This_map_requires_WebGL");
20296		this.name = "GPUInitializationError";
20297		this.requestedAttributes = requestedAttributes;
20298		this.statusMessage = creationEvent?.statusMessage ?? null;
20299	}
20300};
20301//#endregion
20302//#region src/util/throttle.ts
20303/**
20304* Throttle the given function to run at most every `period` milliseconds.
20305*/
20306function throttle(fn, time) {
20307	let pending = false;
vendor: 20,635 bytes, lines 20308-20962
20308	let timerId = null;
20309	let lastCallArgs;
20310	const later = () => {
20311		timerId = null;
20312		if (pending) {
20313			fn(...lastCallArgs);
20314			timerId = setTimeout(later, time);
20315			pending = false;
20316		}
20317	};
20318	return (...args) => {
20319		pending = true;
20320		lastCallArgs = args;
20321		if (!timerId) later();
20322		return timerId;
20323	};
20324}
20325//#endregion
20326//#region src/ui/hash.ts
20327/**
20328* Adds the map's position to its page's location hash.
20329* Passed as an option to the map object.
20330*
20331* @group Markers and Controls
20332*/
20333var Hash = class {
20334	constructor(hashName) {
20335		this._getHashParams = () => {
20336			return new URLSearchParams(window.location.hash.replace("#", ""));
20337		};
20338		this._getCurrentHash = () => {
20339			const params = this._getHashParams();
20340			if (this._hashName) return (params.get(this._hashName) || "").split("/");
20341			return ([...params.keys()][0] ?? "").split("/");
20342		};
20343		this._onHashChange = () => {
20344			const hash = this._getCurrentHash();
20345			if (!this._isValidHash(hash)) return false;
20346			const bearing = this._map.dragRotate.isEnabled() && this._map.touchZoomRotate.isEnabled() ? +(hash[3] || 0) : this._map.getBearing();
20347			this._map.jumpTo({
20348				center: [+hash[2], +hash[1]],
20349				zoom: +hash[0],
20350				bearing,
20351				pitch: +(hash[4] || 0)
20352			});
20353			return true;
20354		};
20355		this._updateHashUnthrottled = () => {
20356			const location = window.location.href.replace(/(#.*)?$/, this.getHashString());
20357			window.history.replaceState(window.history.state, null, location);
20358		};
20359		this._removeHash = () => {
20360			const params = this._getHashParams();
20361			if (this._hashName) params.delete(this._hashName);
20362			else {
20363				const keys = Array.from(params.keys());
20364				if (keys.length > 0) params.delete(keys[0]);
20365			}
20366			const newHash = decodeURIComponent(params.toString()).replace(/=&/g, "&").replace(/=$/g, "");
20367			const replaceString = newHash ? `#${newHash}` : "";
20368			let location = window.location.href.replace(/(#.+)?$/, replaceString);
20369			location = location.replace("&&", "&");
20370			window.history.replaceState(window.history.state, null, location);
20371		};
20372		this._updateHash = throttle(this._updateHashUnthrottled, 300);
20373		this._hashName = hashName && encodeURIComponent(hashName);
20374	}
20375	/**
20376	* Map element to listen for coordinate changes
20377	*
20378	* @param map - The map object
20379	*/
20380	addTo(map) {
20381		this._map = map;
20382		addEventListener("hashchange", this._onHashChange, false);
20383		this._map.on("moveend", this._updateHash);
20384		return this;
20385	}
20386	/**
20387	* Removes hash
20388	*/
20389	remove() {
20390		removeEventListener("hashchange", this._onHashChange, false);
20391		this._map.off("moveend", this._updateHash);
20392		clearTimeout(this._updateHash());
20393		this._removeHash();
20394		delete this._map;
20395		return this;
20396	}
20397	getHashString(mapFeedback) {
20398		const center = this._map.getCenter(), zoom = Math.round(this._map.getZoom() * 100) / 100, precision = Math.ceil((zoom * Math.LN2 + Math.log(512 / 360 / .5)) / Math.LN10), m = Math.pow(10, precision), lng = Math.round(center.lng * m) / m, lat = Math.round(center.lat * m) / m, bearing = this._map.getBearing(), pitch = this._map.getPitch();
20399		let hash = "";
20400		if (mapFeedback) hash += `/${lng}/${lat}/${zoom}`;
20401		else hash += `${zoom}/${lat}/${lng}`;
20402		if (bearing || pitch) hash += `/${Math.round(bearing * 10) / 10}`;
20403		if (pitch) hash += `/${Math.round(pitch)}`;
20404		if (this._hashName) {
20405			const params = this._getHashParams();
20406			params.set(this._hashName, hash);
20407			return `#${decodeURIComponent(params.toString()).replace(/=&/g, "&").replace(/=$/g, "")}`;
20408		}
20409		return `#${hash}`;
20410	}
20411	_isValidHash(hash) {
20412		if (hash.length < 3 || hash.some((h) => isNaN(+h))) return false;
20413		try {
20414			new LngLat(+hash[2], +hash[1]);
20415		} catch {
20416			return false;
20417		}
20418		const zoom = +hash[0];
20419		const bearing = +(hash[3] || 0);
20420		const pitch = +(hash[4] || 0);
20421		return zoom >= this._map.getMinZoom() && zoom <= this._map.getMaxZoom() && bearing >= -180 && bearing <= 180 && pitch >= this._map.getMinPitch() && pitch <= this._map.getMaxPitch();
20422	}
20423};
20424//#endregion
20425//#region src/ui/handler_inertia.ts
20426const defaultInertiaOptions = {
20427	linearity: .3,
20428	easing: bezier(0, 0, .3, 1)
20429};
20430const defaultPanInertiaOptions = extend({
20431	deceleration: 2500,
20432	maxSpeed: 1400
20433}, defaultInertiaOptions);
20434const defaultZoomInertiaOptions = extend({
20435	deceleration: 20,
20436	maxSpeed: 1400
20437}, defaultInertiaOptions);
20438const defaultBearingInertiaOptions = extend({
20439	deceleration: 1e3,
20440	maxSpeed: 360
20441}, defaultInertiaOptions);
20442const defaultPitchInertiaOptions = extend({
20443	deceleration: 1e3,
20444	maxSpeed: 90
20445}, defaultInertiaOptions);
20446const defaultRollInertiaOptions = extend({
20447	deceleration: 1e3,
20448	maxSpeed: 360
20449}, defaultInertiaOptions);
20450/**
20451* The maximum age of a recorded gesture update, in milliseconds.
20452*/
20453const BUFFER_CUTOFF = 160;
20454/**
20455* The time window the gesture velocity is measured over, in milliseconds.
20456*/
20457const VELOCITY_WINDOW = 60;
20458var HandlerInertia = class {
20459	constructor(map) {
20460		this._map = map;
20461		this.clear();
20462	}
20463	clear() {
20464		this._inertiaBuffer = [];
20465	}
20466	record(settings) {
20467		this._drainInertiaBuffer();
20468		this._inertiaBuffer.push({
20469			time: now(),
20470			settings
20471		});
20472	}
20473	_drainInertiaBuffer() {
20474		const inertia = this._inertiaBuffer, currentTime = now();
20475		while (inertia.length > 0 && currentTime - inertia[0].time > BUFFER_CUTOFF) inertia.shift();
20476	}
20477	/**
20478	* Returns the entries the velocity is measured from: everything recorded within
20479	* {@link VELOCITY_WINDOW} before now, but never fewer than the last two, so that
20480	* inertia also works below two recorded updates per window.
20481	*/
20482	_getVelocityEntries() {
20483		const inertia = this._inertiaBuffer;
20484		const windowStart = now() - VELOCITY_WINDOW;
20485		let firstIndex = Math.max(0, inertia.length - 2);
20486		while (firstIndex > 0 && inertia[firstIndex - 1].time >= windowStart) firstIndex--;
20487		return inertia.slice(firstIndex);
20488	}
20489	/**
20490	* Returns the ease which continues the gesture that has just ended.
20491	*
20492	* The velocity is measured over the time up to the release and not up to the last
20493	* recorded movement, so that a gesture held still before being released has a lower
20494	* velocity, down to no inertia at all. The delta of an entry happened before it was
20495	* recorded, so the first entry only marks the start of the measured interval; anchors
20496	* are the exception, as they describe the state at the moment of their entry.
20497	*
20498	* @param panInertiaOptions - overrides for the pan inertia defaults
20499	* @returns the ease options, or `undefined` when the measured interval holds no
20500	* movement, which terrain gestures produce by recording updates without any delta
20501	*/
20502	_onMoveEnd(panInertiaOptions) {
20503		this._drainInertiaBuffer();
20504		const entries = this._getVelocityEntries();
20505		if (entries.length < 2) {
20506			this.clear();
20507			return;
20508		}
20509		const deltas = {
20510			zoom: 0,
20511			bearing: 0,
20512			pitch: 0,
20513			roll: 0,
20514			pan: new Point(0, 0),
20515			pinchAround: void 0,
20516			around: void 0
20517		};
20518		for (const { settings } of entries) {
20519			if (settings.around) deltas.around = settings.around;
20520			if (settings.pinchAround) deltas.pinchAround = settings.pinchAround;
20521		}
20522		for (const { settings } of entries.slice(1)) {
20523			deltas.zoom += settings.zoomDelta || 0;
20524			deltas.bearing += settings.bearingDelta || 0;
20525			deltas.pitch += settings.pitchDelta || 0;
20526			deltas.roll += settings.rollDelta || 0;
20527			if (settings.panDelta) deltas.pan._add(settings.panDelta);
20528		}
20529		if (!deltas.pan.mag() && !deltas.zoom && !deltas.bearing && !deltas.pitch && !deltas.roll) {
20530			this.clear();
20531			return;
20532		}
20533		const duration = now() - entries[0].time;
20534		const easeOptions = {};
20535		if (deltas.pan.mag()) {
20536			const result = calculateEasing(deltas.pan.mag(), duration, extend({}, defaultPanInertiaOptions, panInertiaOptions || {}));
20537			const finalPan = deltas.pan.mult(result.amount / deltas.pan.mag());
20538			const computedEaseOptions = this._map._camera.cameraHelper.handlePanInertia(finalPan, this._map._camera.transform);
20539			easeOptions.center = computedEaseOptions.easingCenter;
20540			easeOptions.offset = computedEaseOptions.easingOffset;
20541			extendDuration(easeOptions, result);
20542		}
20543		if (deltas.zoom) {
20544			const result = calculateEasing(deltas.zoom, duration, defaultZoomInertiaOptions);
20545			easeOptions.zoom = evaluateZoomSnap(this._map.getZoom() + result.amount, this._map.getZoomSnap(), result.amount);
20546			extendDuration(easeOptions, result);
20547		}
20548		if (deltas.bearing) {
20549			const result = calculateEasing(deltas.bearing, duration, defaultBearingInertiaOptions);
20550			easeOptions.bearing = this._map.getBearing() + clamp(result.amount, -179, 179);
20551			extendDuration(easeOptions, result);
20552		}
20553		if (deltas.pitch) {
20554			const result = calculateEasing(deltas.pitch, duration, defaultPitchInertiaOptions);
20555			easeOptions.pitch = this._map.getPitch() + result.amount;
20556			extendDuration(easeOptions, result);
20557		}
20558		if (deltas.roll) {
20559			const result = calculateEasing(deltas.roll, duration, defaultRollInertiaOptions);
20560			easeOptions.roll = this._map.getRoll() + clamp(result.amount, -179, 179);
20561			extendDuration(easeOptions, result);
20562		}
20563		if (easeOptions.zoom || easeOptions.bearing) {
20564			const last = deltas.pinchAround === void 0 ? deltas.around : deltas.pinchAround;
20565			easeOptions.around = last ? this._map.unproject(last) : this._map.getCenter();
20566		}
20567		this.clear();
20568		return extend(easeOptions, { noMoveStart: true });
20569	}
20570};
20571function extendDuration(easeOptions, result) {
20572	if (!easeOptions.duration || easeOptions.duration < result.duration) {
20573		easeOptions.duration = result.duration;
20574		easeOptions.easing = result.easing;
20575	}
20576}
20577function calculateEasing(amount, inertiaDuration, inertiaOptions) {
20578	const { maxSpeed, linearity, deceleration } = inertiaOptions;
20579	const speed = clamp(amount * linearity / (inertiaDuration / 1e3), -maxSpeed, maxSpeed);
20580	const duration = Math.abs(speed) / (deceleration * linearity);
20581	return {
20582		easing: inertiaOptions.easing,
20583		duration: duration * 1e3,
20584		amount: speed * (duration / 2)
20585	};
20586}
20587//#endregion
20588//#region src/ui/handler/map_event.ts
20589var MapEventHandler = class {
20590	constructor(map, options) {
20591		this._map = map;
20592		this._clickTolerance = options.clickTolerance;
20593	}
20594	reset() {
20595		delete this._mousedownPos;
20596	}
20597	wheel(e) {
20598		return this._firePreventable(new MapWheelEvent(this._map, e));
20599	}
20600	mousedown(e, point) {
20601		this._mousedownPos = point;
20602		return this._firePreventable(new MapMouseEvent(e.type, this._map, e));
20603	}
20604	mouseup(e) {
20605		this._map.fire(new MapMouseEvent(e.type, this._map, e));
20606	}
20607	click(e, point) {
20608		if (this._mousedownPos && this._mousedownPos.dist(point) >= this._clickTolerance) return;
20609		this._map.fire(new MapMouseEvent(e.type, this._map, e));
20610	}
20611	dblclick(e) {
20612		return this._firePreventable(new MapMouseEvent(e.type, this._map, e));
20613	}
20614	mouseover(e) {
20615		this._map.fire(new MapMouseEvent(e.type, this._map, e));
20616	}
20617	mouseout(e) {
20618		this._map.fire(new MapMouseEvent(e.type, this._map, e));
20619	}
20620	touchstart(e) {
20621		return this._firePreventable(new MapTouchEvent(e.type, this._map, e));
20622	}
20623	touchmove(e) {
20624		this._map.fire(new MapTouchEvent(e.type, this._map, e));
20625	}
20626	touchend(e) {
20627		this._map.fire(new MapTouchEvent(e.type, this._map, e));
20628	}
20629	touchcancel(e) {
20630		this._map.fire(new MapTouchEvent(e.type, this._map, e));
20631	}
20632	_firePreventable(mapEvent) {
20633		this._map.fire(mapEvent);
20634		if (mapEvent.defaultPrevented) return {};
20635	}
20636	isEnabled() {
20637		return true;
20638	}
20639	isActive() {
20640		return false;
20641	}
20642	enable() {}
20643	disable() {}
20644};
20645/**
20646* A single finger held in place this long fires a long-press contextmenu.
20647*/
20648const LONG_PRESS_DELAY = 500;
20649var BlockableMapEventHandler = class {
20650	constructor(map, options) {
20651		this._map = map;
20652		this._clickTolerance = options.clickTolerance;
20653	}
20654	reset() {
20655		this._delayContextMenu = false;
20656		this._ignoreContextMenu = true;
20657		delete this._contextMenuEvent;
20658		this._clearLongPress();
20659		this._touchActive = false;
20660	}
20661	mousemove(e) {
20662		this._map.fire(new MapMouseEvent(e.type, this._map, e));
20663	}
20664	mousedown() {
20665		this._delayContextMenu = true;
20666		this._ignoreContextMenu = false;
20667	}
20668	mouseup() {
20669		this._delayContextMenu = false;
20670		if (this._contextMenuEvent) {
20671			this._map.fire(new MapMouseEvent("contextmenu", this._map, this._contextMenuEvent));
20672			delete this._contextMenuEvent;
20673		}
20674	}
20675	/**
20676	* Starts the touch long press. Touch devices have no right click, so a long press opens
20677	* the context menu. It is detected with a timer rather than by relying on a native
20678	* contextmenu event, because Android Chrome fires one on a long press but iOS
20679	* Safari does not. A single finger held past the delay without moving fires a
20680	* contextmenu at that point; a pan or a lift cancels it.
20681	* @param e - the touch event
20682	* @param points - the touch points, in map coordinates
20683	* @param mapTouches - the touches that are on the map
20684	*/
20685	touchstart(e, points, mapTouches) {
20686		this._clearLongPress();
20687		this._touchActive = mapTouches.length > 0;
20688		if (mapTouches.length !== 1) return;
20689		this._longPressStart = points[0];
20690		const touch = mapTouches[0];
20691		this._longPressTimer = setTimeout(() => {
20692			this._longPressTimer = void 0;
20693			const originalEvent = new MouseEvent("contextmenu", {
20694				clientX: touch.clientX,
20695				clientY: touch.clientY,
20696				button: 2,
20697				bubbles: true,
20698				cancelable: true
20699			});
20700			this._map.fire(new MapMouseEvent("contextmenu", this._map, originalEvent));
20701		}, LONG_PRESS_DELAY);
20702	}
20703	/**
20704	* Cancels a pending long press once the finger has moved further than the map's
20705	* click tolerance, or once a second finger lands.
20706	* @param e - the touch event
20707	* @param points - the touch points, in map coordinates
20708	* @param mapTouches - the touches that are on the map
20709	*/
20710	touchmove(e, points, mapTouches) {
20711		if (!this._longPressTimer) return;
20712		if (mapTouches.length !== 1 || !this._longPressStart || points[0].dist(this._longPressStart) > this._clickTolerance) this._clearLongPress();
20713	}
20714	touchend() {
20715		this._touchActive = false;
20716		this._clearLongPress();
20717	}
20718	touchcancel() {
20719		this._touchActive = false;
20720		this._clearLongPress();
20721	}
20722	_clearLongPress() {
20723		if (this._longPressTimer) {
20724			clearTimeout(this._longPressTimer);
20725			this._longPressTimer = void 0;
20726		}
20727		this._longPressStart = void 0;
20728	}
20729	/**
20730	* Handles a contextmenu event. A native one during a touch is suppressed, because the
20731	* long press timer already fires the event and the browser menu would double up.
20732	* @param e - the mouse event
20733	*/
20734	contextmenu(e) {
20735		if (this._touchActive) {
20736			e.preventDefault();
20737			return;
20738		}
20739		if (this._delayContextMenu) this._contextMenuEvent = e;
20740		else if (!this._ignoreContextMenu) this._map.fire(new MapMouseEvent(e.type, this._map, e));
20741		if (this._map.listens("contextmenu")) e.preventDefault();
20742	}
20743	isEnabled() {
20744		return true;
20745	}
20746	isActive() {
20747		return false;
20748	}
20749	enable() {}
20750	disable() {}
20751};
20752//#endregion
20753//#region src/ui/handler/box_zoom.ts
20754/**
20755* The `BoxZoomHandler` allows the user to zoom the map to fit within a bounding box.
20756* The bounding box is defined by clicking and holding `shift` while dragging the cursor.
20757*
20758* @group Handlers
20759*/
20760var BoxZoomHandler = class {
20761	/** @internal */
20762	constructor(map, options, transformProvider) {
20763		this._map = map;
20764		this._tr = transformProvider;
20765		this._el = map.getCanvasContainer();
20766		this._container = map.getContainer();
20767		this._clickTolerance = options.clickTolerance || 1;
20768		if (options.boxZoom && typeof options.boxZoom === "object") this._boxZoomEnd = options.boxZoom.boxZoomEnd;
20769	}
20770	/**
20771	* Returns a Boolean indicating whether the "box zoom" interaction is enabled.
20772	*
20773	* @returns `true` if the "box zoom" interaction is enabled.
20774	*/
20775	isEnabled() {
20776		return !!this._enabled;
20777	}
20778	/**
20779	* Returns a Boolean indicating whether the "box zoom" interaction is active, i.e. currently being used.
20780	*
20781	* @returns `true` if the "box zoom" interaction is active.
20782	*/
20783	isActive() {
20784		return !!this._active;
20785	}
20786	/**
20787	* Enables the "box zoom" interaction.
20788	*
20789	* @example
20790	* ```ts
20791	* map.boxZoom.enable();
20792	* ```
20793	*/
20794	enable() {
20795		if (this.isEnabled()) return;
20796		this._enabled = true;
20797	}
20798	/**
20799	* Disables the "box zoom" interaction.
20800	*
20801	* @example
20802	* ```ts
20803	* map.boxZoom.disable();
20804	* ```
20805	*/
20806	disable() {
20807		if (!this.isEnabled()) return;
20808		this._enabled = false;
20809	}
20810	mousedown(e, point) {
20811		if (!this.isEnabled()) return;
20812		if (!(e.shiftKey && e.button === 0)) return;
20813		DOM.disableDrag();
20814		this._startPos = this._lastPos = point;
20815		this._active = true;
20816	}
20817	mousemoveWindow(e, point) {
20818		if (!this._active) return;
20819		const pos = point;
20820		if (this._lastPos.equals(pos) || !this._box && pos.dist(this._startPos) < this._clickTolerance) return;
20821		const p0 = this._startPos;
20822		this._lastPos = pos;
20823		if (!this._box) {
20824			this._box = DOM.create("div", "maplibregl-boxzoom", this._container);
20825			this._container.classList.add("maplibregl-crosshair");
20826			this._fireEvent("boxzoomstart", e);
20827		}
20828		const minX = Math.min(p0.x, pos.x), maxX = Math.max(p0.x, pos.x), minY = Math.min(p0.y, pos.y), maxY = Math.max(p0.y, pos.y);
20829		this._box.style.transform = `translate(${minX}px,${minY}px)`;
20830		this._box.style.width = `${maxX - minX}px`;
20831		this._box.style.height = `${maxY - minY}px`;
20832	}
20833	mouseupWindow(e, point) {
20834		if (!this._active) return;
20835		if (e.button !== 0) return;
20836		const p0 = this._startPos, p1 = point;
20837		this.reset();
20838		DOM.suppressClick();
20839		if (p0.x === p1.x && p0.y === p1.y) this._fireEvent("boxzoomcancel", e);
20840		else {
20841			this._map.fire(new MapBoxZoomEvent("boxzoomend", { originalEvent: e }));
20842			if (this._boxZoomEnd) {
20843				this._boxZoomEnd(this._map, p0, p1, e);
20844				return;
20845			}
20846			return { cameraAnimation: (map) => map.fitScreenCoordinates(p0, p1, this._tr.bearing, { linear: true }) };
20847		}
20848	}
20849	keydown(e) {
20850		if (!this._active) return;
20851		if (e.keyCode === 27) {
20852			this.reset();
20853			this._fireEvent("boxzoomcancel", e);
20854		}
20855	}
20856	reset() {
20857		this._active = false;
20858		this._container.classList.remove("maplibregl-crosshair");
20859		if (this._box) {
20860			this._box.remove();
20861			this._box = null;
20862		}
20863		DOM.enableDrag();
20864		delete this._startPos;
20865		delete this._lastPos;
20866	}
20867	_fireEvent(type, e) {
20868		return this._map.fire(new MapBoxZoomEvent(type, { originalEvent: e }));
20869	}
20870};
20871//#endregion
20872//#region src/ui/handler/handler_util.ts
20873function indexTouches(touches, points) {
20874	if (touches.length !== points.length) throw new Error(`The number of touches and points are not equal - touches ${touches.length}, points ${points.length}`);
20875	const obj = {};
20876	for (let i = 0; i < touches.length; i++) obj[touches[i].identifier] = points[i];
20877	return obj;
20878}
20879//#endregion
20880//#region src/ui/handler/tap_recognizer.ts
20881function getCentroid(points) {
20882	const sum = new Point(0, 0);
20883	for (const point of points) sum._add(point);
20884	return sum.div(points.length);
20885}
20886const MAX_TOUCH_TIME = 500;
20887var SingleTapRecognizer = class {
20888	constructor(options) {
20889		this.reset();
20890		this.numTouches = options.numTouches;
20891	}
20892	reset() {
20893		delete this.centroid;
20894		delete this.startTime;
20895		delete this.touches;
20896		this.aborted = false;
20897	}
20898	touchstart(e, points, mapTouches) {
20899		if (this.centroid || mapTouches.length > this.numTouches) this.aborted = true;
20900		if (this.aborted) return;
20901		if (this.startTime === void 0) this.startTime = e.timeStamp;
20902		if (mapTouches.length === this.numTouches) {
20903			this.centroid = getCentroid(points);
20904			this.touches = indexTouches(mapTouches, points);
20905		}
20906	}
20907	touchmove(e, points, mapTouches) {
20908		if (this.aborted || !this.centroid) return;
20909		const newTouches = indexTouches(mapTouches, points);
20910		for (const id in this.touches) {
20911			const prevPos = this.touches[id];
20912			const pos = newTouches[id];
20913			if (!pos || pos.dist(prevPos) > 30) this.aborted = true;
20914		}
20915	}
20916	touchend(e, points, mapTouches) {
20917		if (!this.centroid || e.timeStamp - this.startTime > MAX_TOUCH_TIME) this.aborted = true;
20918		if (mapTouches.length === 0) {
20919			const centroid = !this.aborted && this.centroid;
20920			this.reset();
20921			if (centroid) return centroid;
20922		}
20923	}
20924};
20925var TapRecognizer = class {
20926	constructor(options) {
20927		this.singleTap = new SingleTapRecognizer(options);
20928		this.numTaps = options.numTaps;
20929		this.reset();
20930	}
20931	reset() {
20932		this.lastTime = Infinity;
20933		delete this.lastTap;
20934		this.count = 0;
20935		this.singleTap.reset();
20936	}
20937	touchstart(e, points, mapTouches) {
20938		this.singleTap.touchstart(e, points, mapTouches);
20939	}
20940	touchmove(e, points, mapTouches) {
20941		this.singleTap.touchmove(e, points, mapTouches);
20942	}
20943	touchend(e, points, mapTouches) {
20944		const tap = this.singleTap.touchend(e, points, mapTouches);
20945		if (tap) {
20946			const soonEnough = e.timeStamp - this.lastTime < 500;
20947			const closeEnough = !this.lastTap || this.lastTap.dist(tap) < 30;
20948			if (!soonEnough || !closeEnough) this.reset();
20949			this.count++;
20950			this.lastTime = e.timeStamp;
20951			this.lastTap = tap;
20952			if (this.count === this.numTaps) {
20953				this.reset();
20954				return tap;
20955			}
20956		}
20957	}
20958};
20959//#endregion
20960//#region src/ui/handler/tap_zoom.ts
20961/**
20962* A `TapZoomHandler` allows the user to zoom the map at a point b
vendor: 9,287 bytes, lines 20962-21270
20962y double tapping
20963*/
20964var TapZoomHandler = class {
20965	constructor(map, transformProvider) {
20966		this._tr = transformProvider;
20967		this._zoomIn = new TapRecognizer({
20968			numTouches: 1,
20969			numTaps: 2
20970		});
20971		this._zoomOut = new TapRecognizer({
20972			numTouches: 2,
20973			numTaps: 1
20974		});
20975		this.reset();
20976	}
20977	reset() {
20978		this._active = false;
20979		this._zoomIn.reset();
20980		this._zoomOut.reset();
20981	}
20982	touchstart(e, points, mapTouches) {
20983		this._zoomIn.touchstart(e, points, mapTouches);
20984		this._zoomOut.touchstart(e, points, mapTouches);
20985	}
20986	touchmove(e, points, mapTouches) {
20987		this._zoomIn.touchmove(e, points, mapTouches);
20988		this._zoomOut.touchmove(e, points, mapTouches);
20989	}
20990	touchend(e, points, mapTouches) {
20991		const zoomInPoint = this._zoomIn.touchend(e, points, mapTouches);
20992		const zoomOutPoint = this._zoomOut.touchend(e, points, mapTouches);
20993		const tr = this._tr;
20994		if (zoomInPoint) {
20995			this._active = true;
20996			e.preventDefault();
20997			setTimeout(() => this.reset(), 0);
20998			return { cameraAnimation: (map) => map.easeTo({
20999				duration: 300,
21000				zoom: evaluateZoomSnap(tr.zoom + 1, map.getZoomSnap()),
21001				around: tr.unproject(zoomInPoint)
21002			}, { originalEvent: e }) };
21003		} else if (zoomOutPoint) {
21004			this._active = true;
21005			e.preventDefault();
21006			setTimeout(() => this.reset(), 0);
21007			return { cameraAnimation: (map) => map.easeTo({
21008				duration: 300,
21009				zoom: evaluateZoomSnap(tr.zoom - 1, map.getZoomSnap()),
21010				around: tr.unproject(zoomOutPoint)
21011			}, { originalEvent: e }) };
21012		}
21013	}
21014	touchcancel() {
21015		this.reset();
21016	}
21017	enable() {
21018		this._enabled = true;
21019	}
21020	disable() {
21021		this._enabled = false;
21022		this.reset();
21023	}
21024	isEnabled() {
21025		return this._enabled;
21026	}
21027	isActive() {
21028		return this._active;
21029	}
21030};
21031//#endregion
21032//#region src/ui/handler/drag_handler.ts
21033/**
21034* A generic class to create handlers for drag events, from both mouse and touch events.
21035*/
21036var DragHandler = class {
21037	constructor(options) {
21038		this._enabled = !!options.enable;
21039		this._moveStateManager = options.moveStateManager;
21040		this._clickTolerance = options.clickTolerance || 1;
21041		this._moveFunction = options.move;
21042		this._activateOnStart = !!options.activateOnStart;
21043		options.assignEvents(this);
21044		this.reset();
21045	}
21046	reset(e) {
21047		this._active = false;
21048		this._moved = false;
21049		delete this._lastPoint;
21050		this._moveStateManager.endMove(e);
21051	}
21052	_move(...params) {
21053		const move = this._moveFunction(...params);
21054		if (move.bearingDelta || move.pitchDelta || move.rollDelta || move.around || move.panDelta) {
21055			this._active = true;
21056			return move;
21057		}
21058	}
21059	dragStart(e, point) {
21060		if (!this.isEnabled() || this._lastPoint) return;
21061		if (!this._moveStateManager.isValidStartEvent(e)) return;
21062		this._moveStateManager.startMove(e);
21063		this._lastPoint = Array.isArray(point) ? point[0] : point;
21064		if (this._activateOnStart && this._lastPoint) this._active = true;
21065	}
21066	dragMove(e, point) {
21067		if (!this.isEnabled()) return;
21068		const lastPoint = this._lastPoint;
21069		if (!lastPoint) return;
21070		e.preventDefault();
21071		if (!this._moveStateManager.isValidMoveEvent(e)) {
21072			this.reset(e);
21073			return;
21074		}
21075		const movePoint = Array.isArray(point) ? point[0] : point;
21076		if (!this._moved && movePoint.dist(lastPoint) < this._clickTolerance) return;
21077		this._moved = true;
21078		this._lastPoint = movePoint;
21079		return this._move(lastPoint, movePoint);
21080	}
21081	dragEnd(e) {
21082		if (!this.isEnabled() || !this._lastPoint) return;
21083		if (!this._moveStateManager.isValidEndEvent(e)) return;
21084		if (this._moved) DOM.suppressClick();
21085		this.reset(e);
21086	}
21087	enable() {
21088		this._enabled = true;
21089	}
21090	disable() {
21091		this._enabled = false;
21092		this.reset();
21093	}
21094	isEnabled() {
21095		return this._enabled;
21096	}
21097	isActive() {
21098		return this._active;
21099	}
21100	getClickTolerance() {
21101		return this._clickTolerance;
21102	}
21103};
21104//#endregion
21105//#region src/ui/handler/drag_move_state_manager.ts
21106const LEFT_BUTTON$1 = 0;
21107const RIGHT_BUTTON$1 = 2;
21108const BUTTONS_FLAGS = {
21109	[LEFT_BUTTON$1]: 1,
21110	[RIGHT_BUTTON$1]: 2
21111};
21112function buttonNoLongerPressed(e, button) {
21113	const flag = BUTTONS_FLAGS[button];
21114	return e.buttons === void 0 || (e.buttons & flag) !== flag;
21115}
21116var MouseMoveStateManager = class {
21117	constructor(options) {
21118		this._correctEvent = options.checkCorrectEvent;
21119	}
21120	startMove(e) {
21121		this._eventButton = e.button;
21122	}
21123	endMove(_e) {
21124		delete this._eventButton;
21125	}
21126	isValidStartEvent(e) {
21127		return this._correctEvent(e);
21128	}
21129	isValidMoveEvent(e) {
21130		return !buttonNoLongerPressed(e, this._eventButton);
21131	}
21132	isValidEndEvent(e) {
21133		return e.button === this._eventButton;
21134	}
21135};
21136var OneFingerTouchMoveStateManager = class {
21137	constructor() {
21138		this._firstTouch = void 0;
21139	}
21140	_isOneFingerTouch(e) {
21141		return e.targetTouches.length === 1;
21142	}
21143	_isSameTouchEvent(e) {
21144		return e.targetTouches[0].identifier === this._firstTouch;
21145	}
21146	startMove(e) {
21147		this._firstTouch = e.targetTouches[0].identifier;
21148	}
21149	endMove(_e) {
21150		delete this._firstTouch;
21151	}
21152	isValidStartEvent(e) {
21153		return this._isOneFingerTouch(e);
21154	}
21155	isValidMoveEvent(e) {
21156		return this._isOneFingerTouch(e) && this._isSameTouchEvent(e);
21157	}
21158	isValidEndEvent(e) {
21159		return this._isOneFingerTouch(e) && this._isSameTouchEvent(e);
21160	}
21161};
21162var MouseOrTouchMoveStateManager = class {
21163	constructor(mouseMoveStateManager = new MouseMoveStateManager({ checkCorrectEvent: () => true }), oneFingerTouchMoveStateManager = new OneFingerTouchMoveStateManager()) {
21164		this.mouseMoveStateManager = mouseMoveStateManager;
21165		this.oneFingerTouchMoveStateManager = oneFingerTouchMoveStateManager;
21166	}
21167	_executeRelevantHandler(e, onMouseEvent, onTouchEvent) {
21168		if (e instanceof MouseEvent) return onMouseEvent(e);
21169		if (typeof TouchEvent !== "undefined" && e instanceof TouchEvent) return onTouchEvent(e);
21170	}
21171	startMove(e) {
21172		this._executeRelevantHandler(e, (e) => {
21173			this.mouseMoveStateManager.startMove(e);
21174		}, (e) => {
21175			this.oneFingerTouchMoveStateManager.startMove(e);
21176		});
21177	}
21178	endMove(e) {
21179		this._executeRelevantHandler(e, (e) => {
21180			this.mouseMoveStateManager.endMove(e);
21181		}, (e) => {
21182			this.oneFingerTouchMoveStateManager.endMove(e);
21183		});
21184	}
21185	isValidStartEvent(e) {
21186		return !!this._executeRelevantHandler(e, (e) => this.mouseMoveStateManager.isValidStartEvent(e), (e) => this.oneFingerTouchMoveStateManager.isValidStartEvent(e));
21187	}
21188	isValidMoveEvent(e) {
21189		return !!this._executeRelevantHandler(e, (e) => this.mouseMoveStateManager.isValidMoveEvent(e), (e) => this.oneFingerTouchMoveStateManager.isValidMoveEvent(e));
21190	}
21191	isValidEndEvent(e) {
21192		return !!this._executeRelevantHandler(e, (e) => this.mouseMoveStateManager.isValidEndEvent(e), (e) => this.oneFingerTouchMoveStateManager.isValidEndEvent(e));
21193	}
21194};
21195//#endregion
21196//#region src/ui/handler/mouse.ts
21197const LEFT_BUTTON = 0;
21198const RIGHT_BUTTON = 2;
21199const assignEvents = (handler) => {
21200	handler.mousedown = handler.dragStart;
21201	handler.mousemoveWindow = handler.dragMove;
21202	handler.mouseup = handler.dragEnd;
21203	handler.contextmenu = (e) => {
21204		e.preventDefault();
21205	};
21206};
21207function generateMousePanHandler({ enable, clickTolerance }) {
21208	return new DragHandler({
21209		clickTolerance,
21210		move: (lastPoint, point) => ({
21211			around: point,
21212			panDelta: point.sub(lastPoint)
21213		}),
21214		activateOnStart: true,
21215		moveStateManager: new MouseMoveStateManager({ checkCorrectEvent: (e) => e.button === LEFT_BUTTON && !e.ctrlKey }),
21216		enable,
21217		assignEvents
21218	});
21219}
21220function generateMouseRotationHandler({ enable, clickTolerance, aroundCenter = true, minPixelCenterThreshold = 100, rotateSpeed = .8 }, getCenter) {
21221	return new DragHandler({
21222		clickTolerance,
21223		move: (lastPoint, currentPoint) => {
21224			const center = getCenter();
21225			if (aroundCenter && Math.abs(center.y - lastPoint.y) > minPixelCenterThreshold) return { bearingDelta: getAngleDelta(new Point(lastPoint.x, currentPoint.y), currentPoint, center) };
21226			let bearingDelta = (currentPoint.x - lastPoint.x) * rotateSpeed;
21227			if (aroundCenter && currentPoint.y < center.y) bearingDelta = -bearingDelta;
21228			return { bearingDelta };
21229		},
21230		moveStateManager: new MouseMoveStateManager({ checkCorrectEvent: (e) => e.button === LEFT_BUTTON && e.ctrlKey || e.button === RIGHT_BUTTON && !e.ctrlKey }),
21231		enable,
21232		assignEvents
21233	});
21234}
21235function generateMousePitchHandler({ enable, clickTolerance, pitchSpeed = -.5 }) {
21236	return new DragHandler({
21237		clickTolerance,
21238		move: (lastPoint, point) => ({ pitchDelta: (point.y - lastPoint.y) * pitchSpeed }),
21239		moveStateManager: new MouseMoveStateManager({ checkCorrectEvent: (e) => e.button === LEFT_BUTTON && e.ctrlKey || e.button === RIGHT_BUTTON }),
21240		enable,
21241		assignEvents
21242	});
21243}
21244function generateMouseRollHandler({ enable, clickTolerance, rollDegreesPerPixelMoved = .3 }, getCenter) {
21245	return new DragHandler({
21246		clickTolerance,
21247		move: (lastPoint, currentPoint) => {
21248			const center = getCenter();
21249			let rollDelta = (currentPoint.x - lastPoint.x) * rollDegreesPerPixelMoved;
21250			if (currentPoint.y < center.y) rollDelta = -rollDelta;
21251			return { rollDelta };
21252		},
21253		moveStateManager: new MouseMoveStateManager({ checkCorrectEvent: (e) => e.button === RIGHT_BUTTON && e.ctrlKey }),
21254		enable,
21255		assignEvents
21256	});
21257}
21258//#endregion
21259//#region src/ui/handler/touch_pan.ts
21260/**
21261* A `TouchPanHandler` allows the user to pan the map using touch gestures.
21262*/
21263var TouchPanHandler = class {
21264	constructor(options, map) {
21265		this._clickTolerance = options.clickTolerance || 1;
21266		this._map = map;
21267		this.reset();
21268	}
21269	reset() {
21270		this._active = false;
vendor: 21,256 bytes, lines 21271-22043
21271		this._touches = {};
21272		this._sum = new Point(0, 0);
21273	}
21274	_shouldBePrevented(touchesCount) {
21275		return touchesCount < (this._map.cooperativeGestures.isEnabled() ? 2 : 1);
21276	}
21277	touchstart(e, points, mapTouches) {
21278		return this._calculateTransform(e, points, mapTouches);
21279	}
21280	touchmove(e, points, mapTouches) {
21281		if (!this._active) return;
21282		if (this._shouldBePrevented(mapTouches.length)) {
21283			this._map.cooperativeGestures.notifyGestureBlocked("touch_pan", e);
21284			return;
21285		}
21286		e.preventDefault();
21287		return this._calculateTransform(e, points, mapTouches);
21288	}
21289	touchend(e, points, mapTouches) {
21290		this._calculateTransform(e, points, mapTouches);
21291		if (this._active && this._shouldBePrevented(mapTouches.length)) this.reset();
21292	}
21293	touchcancel() {
21294		this.reset();
21295	}
21296	_calculateTransform(e, points, mapTouches) {
21297		if (mapTouches.length > 0) this._active = true;
21298		const touches = indexTouches(mapTouches, points);
21299		const touchPointSum = new Point(0, 0);
21300		const touchDeltaSum = new Point(0, 0);
21301		let touchDeltaCount = 0;
21302		for (const identifier in touches) {
21303			const point = touches[identifier];
21304			const prevPoint = this._touches[identifier];
21305			if (prevPoint) {
21306				touchPointSum._add(point);
21307				touchDeltaSum._add(point.sub(prevPoint));
21308				touchDeltaCount++;
21309				touches[identifier] = point;
21310			}
21311		}
21312		this._touches = touches;
21313		if (this._shouldBePrevented(touchDeltaCount) || !touchDeltaSum.mag()) return;
21314		const panDelta = touchDeltaSum.div(touchDeltaCount);
21315		this._sum._add(panDelta);
21316		if (this._sum.mag() < this._clickTolerance) return;
21317		return {
21318			around: touchPointSum.div(touchDeltaCount),
21319			panDelta
21320		};
21321	}
21322	enable() {
21323		this._enabled = true;
21324	}
21325	disable() {
21326		this._enabled = false;
21327		this.reset();
21328	}
21329	isEnabled() {
21330		return this._enabled;
21331	}
21332	isActive() {
21333		return this._active;
21334	}
21335};
21336//#endregion
21337//#region src/ui/handler/two_fingers_touch.ts
21338/**
21339* The `TwoFingersTouchHandler`s allows the user to zoom, pitch and rotate the map using two fingers
21340*
21341*/
21342var TwoFingersTouchHandler = class {
21343	/** @internal */
21344	constructor() {
21345		this.reset();
21346	}
21347	reset() {
21348		this._active = false;
21349		delete this._firstTwoTouches;
21350	}
21351	touchstart(e, points, mapTouches) {
21352		if (this._firstTwoTouches || mapTouches.length < 2) return;
21353		this._firstTwoTouches = [mapTouches[0].identifier, mapTouches[1].identifier];
21354		this._start([points[0], points[1]]);
21355	}
21356	touchmove(e, points, mapTouches) {
21357		if (!this._firstTwoTouches) return;
21358		e.preventDefault();
21359		const [idA, idB] = this._firstTwoTouches;
21360		const a = getTouchById(mapTouches, points, idA);
21361		const b = getTouchById(mapTouches, points, idB);
21362		if (!a || !b) return;
21363		const pinchAround = this._aroundCenter ? null : a.add(b).div(2);
21364		return this._move([a, b], pinchAround, e);
21365	}
21366	touchend(e, points, mapTouches) {
21367		if (!this._firstTwoTouches) return;
21368		const [idA, idB] = this._firstTwoTouches;
21369		const a = getTouchById(mapTouches, points, idA);
21370		const b = getTouchById(mapTouches, points, idB);
21371		if (a && b) return;
21372		if (this._active) DOM.suppressClick();
21373		this.reset();
21374	}
21375	touchcancel() {
21376		this.reset();
21377	}
21378	/**
21379	* Enables the "drag to pitch" interaction.
21380	*
21381	* @example
21382	* ```ts
21383	* map.touchPitch.enable();
21384	* ```
21385	*/
21386	enable(options) {
21387		this._enabled = true;
21388		this._aroundCenter = !!options && options.around === "center";
21389	}
21390	/**
21391	* Disables the "drag to pitch" interaction.
21392	*
21393	* @example
21394	* ```ts
21395	* map.touchPitch.disable();
21396	* ```
21397	*/
21398	disable() {
21399		this._enabled = false;
21400		this.reset();
21401	}
21402	/**
21403	* Returns a Boolean indicating whether the "drag to pitch" interaction is enabled.
21404	*
21405	* @returns  `true` if the "drag to pitch" interaction is enabled.
21406	*/
21407	isEnabled() {
21408		return !!this._enabled;
21409	}
21410	/**
21411	* Returns a Boolean indicating whether the "drag to pitch" interaction is active, i.e. currently being used.
21412	*
21413	* @returns `true` if the "drag to pitch" interaction is active.
21414	*/
21415	isActive() {
21416		return !!this._active;
21417	}
21418};
21419function getTouchById(mapTouches, points, identifier) {
21420	for (let i = 0; i < mapTouches.length; i++) if (mapTouches[i].identifier === identifier) return points[i];
21421}
21422const defaultZoomRate$2 = 1;
21423const defaultZoomThreshold = .1;
21424function getZoomDelta(distance, lastDistance) {
21425	return Math.log(distance / lastDistance) / Math.LN2;
21426}
21427/**
21428* The `TwoFingersTouchHandler`s allows the user to zoom the map two fingers
21429*
21430* @group Handlers
21431*/
21432var TwoFingersTouchZoomHandler = class extends TwoFingersTouchHandler {
21433	constructor() {
21434		super();
21435		this._zoomRate = defaultZoomRate$2;
21436		this._zoomThreshold = defaultZoomThreshold;
21437	}
21438	/**
21439	* Sets the zoom rate of touch gestures.
21440	* @param zoomRate - 1 The rate used to scale touch movement to a zoom value. Set to `undefined` to restore the default.
21441	* @example
21442	* Slow down touch zoom
21443	* ```ts
21444	* map.touchZoomRotate.setZoomRate(0.5);
21445	* ```
21446	*/
21447	setZoomRate(zoomRate) {
21448		this._zoomRate = zoomRate ?? defaultZoomRate$2;
21449	}
21450	/**
21451	* Sets the threshold before a pinch gesture starts zooming.
21452	* @param zoomThreshold - 0.1 The minimum zoom delta before the pinch gesture becomes active. Set to `undefined` to restore the default.
21453	* @example
21454	* Make pinch zoom less sensitive
21455	* ```ts
21456	* map.touchZoomRotate.setZoomThreshold(0.3);
21457	* ```
21458	*/
21459	setZoomThreshold(zoomThreshold) {
21460		this._zoomThreshold = zoomThreshold ?? defaultZoomThreshold;
21461	}
21462	reset() {
21463		super.reset();
21464		delete this._distance;
21465		delete this._startDistance;
21466	}
21467	_start(points) {
21468		this._startDistance = this._distance = points[0].dist(points[1]);
21469	}
21470	_move(points, pinchAround) {
21471		const lastDistance = this._distance;
21472		this._distance = points[0].dist(points[1]);
21473		if (!this._active && Math.abs(getZoomDelta(this._distance, this._startDistance)) < this._zoomThreshold) return;
21474		this._active = true;
21475		return {
21476			zoomDelta: getZoomDelta(this._distance, lastDistance) * this._zoomRate,
21477			pinchAround
21478		};
21479	}
21480};
21481const ROTATION_THRESHOLD = 25;
21482function getBearingDelta(a, b) {
21483	return a.angleWith(b) * 180 / Math.PI;
21484}
21485/**
21486* The `TwoFingersTouchHandler`s allows the user to rotate the map two fingers
21487*
21488* @group Handlers
21489*/
21490var TwoFingersTouchRotateHandler = class extends TwoFingersTouchHandler {
21491	reset() {
21492		super.reset();
21493		delete this._minDiameter;
21494		delete this._startVector;
21495		delete this._vector;
21496	}
21497	_start(points) {
21498		this._startVector = this._vector = points[0].sub(points[1]);
21499		this._minDiameter = points[0].dist(points[1]);
21500	}
21501	_move(points, pinchAround, _e) {
21502		const lastVector = this._vector;
21503		this._vector = points[0].sub(points[1]);
21504		if (!this._active && this._isBelowThreshold(this._vector)) return;
21505		this._active = true;
21506		return {
21507			bearingDelta: getBearingDelta(this._vector, lastVector),
21508			pinchAround
21509		};
21510	}
21511	_isBelowThreshold(vector) {
21512		this._minDiameter = Math.min(this._minDiameter, vector.mag());
21513		const circumference = Math.PI * this._minDiameter;
21514		const threshold = ROTATION_THRESHOLD / circumference * 360;
21515		const bearingDeltaSinceStart = getBearingDelta(vector, this._startVector);
21516		return Math.abs(bearingDeltaSinceStart) < threshold;
21517	}
21518};
21519function isVertical(vector) {
21520	return Math.abs(vector.y) > Math.abs(vector.x);
21521}
21522const ALLOWED_SINGLE_TOUCH_TIME = 100;
21523/**
21524* The `TwoFingersTouchPitchHandler` allows the user to pitch the map by dragging up and down with two fingers.
21525*
21526* @group Handlers
21527*/
21528var TwoFingersTouchPitchHandler = class extends TwoFingersTouchHandler {
21529	constructor(map) {
21530		super();
21531		this._currentTouchCount = 0;
21532		this._map = map;
21533	}
21534	reset() {
21535		super.reset();
21536		this._valid = void 0;
21537		delete this._firstMove;
21538		delete this._lastPoints;
21539	}
21540	touchstart(e, points, mapTouches) {
21541		super.touchstart(e, points, mapTouches);
21542		this._currentTouchCount = mapTouches.length;
21543	}
21544	_start(points) {
21545		this._lastPoints = points;
21546		if (isVertical(points[0].sub(points[1]))) this._valid = false;
21547	}
21548	_move(points, center, e) {
21549		if (this._map.cooperativeGestures.isEnabled() && this._currentTouchCount < 3) return;
21550		const vectorA = points[0].sub(this._lastPoints[0]);
21551		const vectorB = points[1].sub(this._lastPoints[1]);
21552		this._valid = this.gestureBeginsVertically(vectorA, vectorB, e.timeStamp);
21553		if (!this._valid) return;
21554		this._lastPoints = points;
21555		this._active = true;
21556		return { pitchDelta: (vectorA.y + vectorB.y) / 2 * -.5 };
21557	}
21558	gestureBeginsVertically(vectorA, vectorB, timeStamp) {
21559		if (this._valid !== void 0) return this._valid;
21560		const threshold = 2;
21561		const movedA = vectorA.mag() >= threshold;
21562		const movedB = vectorB.mag() >= threshold;
21563		if (!movedA && !movedB) return;
21564		if (!movedA || !movedB) {
21565			if (this._firstMove === void 0) this._firstMove = timeStamp;
21566			if (timeStamp - this._firstMove < ALLOWED_SINGLE_TOUCH_TIME) return;
21567			else return false;
21568		}
21569		const isSameDirection = vectorA.y > 0 === vectorB.y > 0;
21570		return isVertical(vectorA) && isVertical(vectorB) && isSameDirection;
21571	}
21572};
21573//#endregion
21574//#region src/ui/handler/keyboard.ts
21575const defaultOptions$5 = {
21576	panStep: 100,
21577	bearingStep: 15,
21578	pitchStep: 10
21579};
21580/**
21581* The `KeyboardHandler` allows the user to zoom, rotate, and pan the map using
21582* the following keyboard shortcuts:
21583*
21584* - `=` / `+`: Increase the zoom level by 1.
21585* - `Shift-=` / `Shift-+`: Increase the zoom level by 2.
21586* - `-`: Decrease the zoom level by 1.
21587* - `Shift--`: Decrease the zoom level by 2.
21588* - Arrow keys: Pan by 100 pixels.
21589* - `Shift+⇢`: Increase the rotation by 15 degrees.
21590* - `Shift+⇠`: Decrease the rotation by 15 degrees.
21591* - `Shift+⇡`: Increase the pitch by 10 degrees.
21592* - `Shift+⇣`: Decrease the pitch by 10 degrees.
21593*
21594* @group Handlers
21595*/
21596var KeyboardHandler = class {
21597	/** @internal */
21598	constructor(map, transformProvider) {
21599		this._tr = transformProvider;
21600		const stepOptions = defaultOptions$5;
21601		this._panStep = stepOptions.panStep;
21602		this._bearingStep = stepOptions.bearingStep;
21603		this._pitchStep = stepOptions.pitchStep;
21604		this._rotationDisabled = false;
21605	}
21606	reset() {
21607		this._active = false;
21608	}
21609	keydown(e) {
21610		if (e.altKey || e.ctrlKey || e.metaKey) return;
21611		let zoomDir = 0;
21612		let bearingDir = 0;
21613		let pitchDir = 0;
21614		let xDir = 0;
21615		let yDir = 0;
21616		switch (e.keyCode) {
21617			case 61:
21618			case 107:
21619			case 171:
21620			case 187:
21621				zoomDir = 1;
21622				break;
21623			case 189:
21624			case 109:
21625			case 173:
21626				zoomDir = -1;
21627				break;
21628			case 37:
21629				if (e.shiftKey) bearingDir = -1;
21630				else {
21631					e.preventDefault();
21632					xDir = -1;
21633				}
21634				break;
21635			case 39:
21636				if (e.shiftKey) bearingDir = 1;
21637				else {
21638					e.preventDefault();
21639					xDir = 1;
21640				}
21641				break;
21642			case 38:
21643				if (e.shiftKey) pitchDir = 1;
21644				else {
21645					e.preventDefault();
21646					yDir = -1;
21647				}
21648				break;
21649			case 40:
21650				if (e.shiftKey) pitchDir = -1;
21651				else {
21652					e.preventDefault();
21653					yDir = 1;
21654				}
21655				break;
21656			default: return;
21657		}
21658		if (this._rotationDisabled) {
21659			bearingDir = 0;
21660			pitchDir = 0;
21661		}
21662		return { cameraAnimation: (map) => {
21663			const tr = this._tr;
21664			map.easeTo({
21665				duration: 300,
21666				easeId: "keyboardHandler",
21667				easing: easeOut,
21668				zoom: zoomDir ? evaluateZoomSnap(tr.zoom + zoomDir * (e.shiftKey ? 2 : 1), map.getZoomSnap()) : tr.zoom,
21669				bearing: tr.bearing + bearingDir * this._bearingStep,
21670				pitch: tr.pitch + pitchDir * this._pitchStep,
21671				offset: [-xDir * this._panStep, -yDir * this._panStep],
21672				center: tr.center
21673			}, { originalEvent: e });
21674		} };
21675	}
21676	/**
21677	* Enables the "keyboard rotate and zoom" interaction.
21678	*
21679	* @example
21680	* ```ts
21681	* map.keyboard.enable();
21682	* ```
21683	*/
21684	enable() {
21685		this._enabled = true;
21686	}
21687	/**
21688	* Disables the "keyboard rotate and zoom" interaction.
21689	*
21690	* @example
21691	* ```ts
21692	* map.keyboard.disable();
21693	* ```
21694	*/
21695	disable() {
21696		this._enabled = false;
21697		this.reset();
21698	}
21699	/**
21700	* Returns a Boolean indicating whether the "keyboard rotate and zoom"
21701	* interaction is enabled.
21702	*
21703	* @returns `true` if the "keyboard rotate and zoom"
21704	* interaction is enabled.
21705	*/
21706	isEnabled() {
21707		return this._enabled;
21708	}
21709	/**
21710	* Returns true if the handler is enabled and has detected the start of a
21711	* zoom/rotate gesture.
21712	*
21713	* @returns `true` if the handler is enabled and has detected the
21714	* start of a zoom/rotate gesture.
21715	*/
21716	isActive() {
21717		return this._active;
21718	}
21719	/**
21720	* Disables the "keyboard pan/rotate" interaction, leaving the
21721	* "keyboard zoom" interaction enabled.
21722	*
21723	* @example
21724	* ```ts
21725	* map.keyboard.disableRotation();
21726	* ```
21727	*/
21728	disableRotation() {
21729		this._rotationDisabled = true;
21730	}
21731	/**
21732	* Enables the "keyboard pan/rotate" interaction.
21733	*
21734	* @example
21735	* ```ts
21736	* map.keyboard.enable();
21737	* map.keyboard.enableRotation();
21738	* ```
21739	*/
21740	enableRotation() {
21741		this._rotationDisabled = false;
21742	}
21743};
21744function easeOut(t) {
21745	return t * (2 - t);
21746}
21747//#endregion
21748//#region src/ui/handler/scroll_zoom.ts
21749const wheelZoomDelta = 4.000244140625;
21750const defaultZoomRate$1 = 1 / 100;
21751const wheelZoomRate = 1 / 450;
21752const maxScalePerFrame = 2;
21753const wheelEventTimeDiffAdjustment = 5;
21754/**
21755* The `ScrollZoomHandler` allows the user to zoom the map by scrolling.
21756*
21757* @group Handlers
21758*/
21759var ScrollZoomHandler = class {
21760	/** @internal */
21761	constructor(map, triggerRenderFrame, transformProvider) {
21762		this._onTimeout = (initialEvent) => {
21763			this._type = "wheel";
21764			this._delta -= this._lastValue;
21765			if (!this._active) this._start(initialEvent);
21766		};
21767		this._map = map;
21768		this._tr = transformProvider;
21769		this._triggerRenderFrame = triggerRenderFrame;
21770		this._delta = 0;
21771		this._defaultZoomRate = defaultZoomRate$1;
21772		this._wheelZoomRate = wheelZoomRate;
21773	}
21774	/**
21775	* Set the zoom rate of a trackpad
21776	* @param zoomRate - 1/100 The rate used to scale trackpad movement to a zoom value.
21777	* @example
21778	* Speed up trackpad zoom
21779	* ```ts
21780	* map.scrollZoom.setZoomRate(1/25);
21781	* ```
21782	*/
21783	setZoomRate(zoomRate) {
21784		this._defaultZoomRate = zoomRate;
21785	}
21786	/**
21787	* Set the zoom rate of a mouse wheel
21788	* @param wheelZoomRate - 1/450 The rate used to scale mouse wheel movement to a zoom value.
21789	* @example
21790	* Slow down zoom of mouse wheel
21791	* ```ts
21792	* map.scrollZoom.setWheelZoomRate(1/600);
21793	* ```
21794	*/
21795	setWheelZoomRate(wheelZoomRate) {
21796		this._wheelZoomRate = wheelZoomRate;
21797	}
21798	/**
21799	* Returns a Boolean indicating whether the "scroll to zoom" interaction is enabled.
21800	* @returns `true` if the "scroll to zoom" interaction is enabled.
21801	*/
21802	isEnabled() {
21803		return !!this._enabled;
21804	}
21805	isActive() {
21806		return !!this._active || this._finishTimeout !== void 0;
21807	}
21808	isZooming() {
21809		return !!this._zooming;
21810	}
21811	/**
21812	* Enables the "scroll to zoom" interaction.
21813	*
21814	* @param options - Options object.
21815	* @example
21816	* ```ts
21817	* map.scrollZoom.enable();
21818	* map.scrollZoom.enable({ around: 'center' })
21819	* ```
21820	*/
21821	enable(options) {
21822		if (this.isEnabled()) return;
21823		this._enabled = true;
21824		this._aroundCenter = !!options && options.around === "center";
21825	}
21826	/**
21827	* Disables the "scroll to zoom" interaction.
21828	*
21829	* @example
21830	* ```ts
21831	* map.scrollZoom.disable();
21832	* ```
21833	*/
21834	disable() {
21835		if (!this.isEnabled()) return;
21836		this._enabled = false;
21837	}
21838	/**
21839	* Determines whether or not the gesture is blocked due to cooperativeGestures.
21840	*/
21841	_shouldBePrevented(e) {
21842		if (!this._map.cooperativeGestures.isEnabled()) return false;
21843		return !(e.ctrlKey || this._map.cooperativeGestures.isBypassed(e));
21844	}
21845	wheel(e) {
21846		if (!this.isEnabled()) return;
21847		if (this._shouldBePrevented(e)) {
21848			this._map.cooperativeGestures.notifyGestureBlocked("wheel_zoom", e);
21849			return;
21850		}
21851		let value = e.deltaMode === WheelEvent.DOM_DELTA_LINE ? e.deltaY * 40 : e.deltaY;
21852		const currentTime = now(), timeDelta = currentTime - (this._lastWheelEventTime || 0);
21853		this._lastWheelEventTime = currentTime;
21854		if (value !== 0 && value % wheelZoomDelta === 0) this._type = "wheel";
21855		else if (value !== 0 && Math.abs(value) < 4) this._type = "trackpad";
21856		else if (timeDelta > 400) {
21857			this._type = null;
21858			this._lastValue = value;
21859			this._timeout = setTimeout(this._onTimeout, 40, e);
21860		} else if (!this._type) {
21861			this._type = Math.abs(timeDelta * value) < 200 ? "trackpad" : "wheel";
21862			if (this._timeout) {
21863				clearTimeout(this._timeout);
21864				this._timeout = null;
21865				value += this._lastValue;
21866			}
21867		}
21868		if (e.shiftKey && value) value = value / 4;
21869		if (this._type) {
21870			this._lastWheelEvent = e;
21871			this._delta -= value;
21872			if (!this._active) this._start(e);
21873		}
21874		e.preventDefault();
21875	}
21876	_start(e) {
21877		if (!this._delta) return;
21878		this._needsRerender = false;
21879		this._active = true;
21880		if (!this.isZooming()) this._zooming = true;
21881		if (this._finishTimeout) {
21882			clearTimeout(this._finishTimeout);
21883			delete this._finishTimeout;
21884		}
21885		const pos = DOM.mousePos(this._map.getCanvas(), e);
21886		const tr = this._tr;
21887		if (this._aroundCenter) this._aroundPoint = tr.transform.locationToScreenPoint(LngLat.convert(tr.center));
21888		else this._aroundPoint = pos;
21889		if (!this._needsRerender) {
21890			this._needsRerender = true;
21891			this._triggerRenderFrame();
21892		}
21893	}
21894	renderFrame() {
21895		if (!this._needsRerender) return;
21896		this._needsRerender = false;
21897		if (!this.isActive()) return;
21898		const tr = this._tr.transform;
21899		if (typeof this._lastExpectedZoom === "number") {
21900			const externalZoomChange = tr.zoom - this._lastExpectedZoom;
21901			if (typeof this._startZoom === "number") this._startZoom += externalZoomChange;
21902			if (typeof this._targetZoom === "number") this._targetZoom += externalZoomChange;
21903		}
21904		if (this._delta !== 0) {
21905			const zoomRate = this._type === "wheel" && Math.abs(this._delta) > wheelZoomDelta ? this._wheelZoomRate : this._defaultZoomRate;
21906			let scale = maxScalePerFrame / (1 + Math.exp(-Math.abs(this._delta * zoomRate)));
21907			if (this._delta < 0 && scale !== 0) scale = 1 / scale;
21908			const fromScale = typeof this._targetZoom !== "number" ? tr.scale : zoomScale(this._targetZoom);
21909			const target = tr.applyConstrain(tr.getCameraLngLat(), scaleZoom(fromScale * scale)).zoom;
21910			const zoomSnap = this._map.getZoomSnap();
21911			if (this._type === "wheel" && zoomSnap > 0) {
21912				const currentSnapped = evaluateZoomSnap(tr.zoom, zoomSnap);
21913				this._targetZoom = evaluateZoomSnap(target, zoomSnap, target - currentSnapped);
21914			} else this._targetZoom = target;
21915			if (this._type === "wheel") {
21916				this._startZoom = tr.zoom;
21917				this._easing = this._smoothOutEasing(200);
21918			}
21919			this._delta = 0;
21920		}
21921		const targetZoom = typeof this._targetZoom !== "number" ? tr.zoom : this._targetZoom;
21922		const startZoom = this._startZoom;
21923		const easing = this._easing;
21924		let finished = false;
21925		let zoom;
21926		if (this._type === "wheel" && startZoom && easing) {
21927			const lastWheelEventTimeDiff = now() - this._lastWheelEventTime;
21928			const t = Math.min((lastWheelEventTimeDiff + wheelEventTimeDiffAdjustment) / 200, 1);
21929			const k = easing(t);
21930			zoom = interpolateFactory.number(startZoom, targetZoom, k);
21931			if (t < 1) this._needsRerender = true;
21932			else finished = true;
21933		} else {
21934			zoom = targetZoom;
21935			finished = true;
21936		}
21937		this._active = true;
21938		if (finished) {
21939			this._active = false;
21940			this._finishTimeout = setTimeout(() => {
21941				this._zooming = false;
21942				this._triggerRenderFrame();
21943				delete this._targetZoom;
21944				delete this._lastExpectedZoom;
21945				delete this._finishTimeout;
21946			}, 200);
21947		}
21948		this._lastExpectedZoom = zoom;
21949		return {
21950			noInertia: true,
21951			needsRenderFrame: !finished,
21952			zoomDelta: zoom - tr.zoom,
21953			around: this._aroundPoint,
21954			originalEvent: this._lastWheelEvent
21955		};
21956	}
21957	_smoothOutEasing(duration) {
21958		let easing = defaultEasing;
21959		if (this._prevEase) {
21960			const currentEase = this._prevEase;
21961			const t = (now() - currentEase.start) / currentEase.duration;
21962			const speed = currentEase.easing(t + .01) - currentEase.easing(t);
21963			const x = .27 / Math.sqrt(speed * speed + 1e-4) * .01;
21964			const y = Math.sqrt(.0729 - x * x);
21965			easing = bezier(x, y, .25, 1);
21966		}
21967		this._prevEase = {
21968			start: now(),
21969			duration,
21970			easing
21971		};
21972		return easing;
21973	}
21974	reset() {
21975		this._active = false;
21976		this._zooming = false;
21977		delete this._targetZoom;
21978		delete this._lastExpectedZoom;
21979		if (this._finishTimeout) {
21980			clearTimeout(this._finishTimeout);
21981			delete this._finishTimeout;
21982		}
21983	}
21984};
21985//#endregion
21986//#region src/ui/handler/shim/dblclick_zoom.ts
21987/**
21988* The `DoubleClickZoomHandler` allows the user to zoom the map at a point by
21989* double clicking or double tapping.
21990*
21991* @group Handlers
21992*/
21993var DoubleClickZoomHandler = class {
21994	/** @internal */
21995	constructor(clickZoom, TapZoom) {
21996		this._clickZoom = clickZoom;
21997		this._tapZoom = TapZoom;
21998	}
21999	/**
22000	* Enables the "double click to zoom" interaction.
22001	*
22002	* @example
22003	* ```ts
22004	* map.doubleClickZoom.enable();
22005	* ```
22006	*/
22007	enable() {
22008		this._clickZoom.enable();
22009		this._tapZoom.enable();
22010	}
22011	/**
22012	* Disables the "double click to zoom" interaction.
22013	*
22014	* @example
22015	* ```ts
22016	* map.doubleClickZoom.disable();
22017	* ```
22018	*/
22019	disable() {
22020		this._clickZoom.disable();
22021		this._tapZoom.disable();
22022	}
22023	/**
22024	* Returns a Boolean indicating whether the "double click to zoom" interaction is enabled.
22025	*
22026	* @returns `true` if the "double click to zoom" interaction is enabled.
22027	*/
22028	isEnabled() {
22029		return this._clickZoom.isEnabled() && this._tapZoom.isEnabled();
22030	}
22031	/**
22032	* Returns a Boolean indicating whether the "double click to zoom" interaction is active, i.e. currently being used.
22033	*
22034	* @returns `true` if the "double click to zoom" interaction is active.
22035	*/
22036	isActive() {
22037		return this._clickZoom.isActive() || this._tapZoom.isActive();
22038	}
22039};
22040//#endregion
22041//#region src/ui/handler/click_zoom.ts
22042/**
22043* The `ClickZoomHandler` allows the user to zoom the map at a point b
vendor: 4,838 bytes, lines 22043-22241
22043y double clicking
22044* It is used by other handlers
22045*/
22046var ClickZoomHandler = class {
22047	/** @internal */
22048	constructor(map, transformProvider) {
22049		this._tr = transformProvider;
22050		this.reset();
22051	}
22052	reset() {
22053		this._active = false;
22054	}
22055	dblclick(e, point) {
22056		e.preventDefault();
22057		return { cameraAnimation: (map) => {
22058			map.easeTo({
22059				duration: 300,
22060				zoom: evaluateZoomSnap(this._tr.zoom + (e.shiftKey ? -1 : 1), map.getZoomSnap()),
22061				around: this._tr.unproject(point)
22062			}, { originalEvent: e });
22063		} };
22064	}
22065	enable() {
22066		this._enabled = true;
22067	}
22068	disable() {
22069		this._enabled = false;
22070		this.reset();
22071	}
22072	isEnabled() {
22073		return this._enabled;
22074	}
22075	isActive() {
22076		return this._active;
22077	}
22078};
22079//#endregion
22080//#region src/ui/handler/tap_drag_zoom.ts
22081const defaultZoomRate = 1;
22082/**
22083* A `TapDragZoomHandler` allows the user to zoom the map at a point by double tapping. It also allows the user pan the map by dragging.
22084*/
22085var TapDragZoomHandler = class {
22086	constructor() {
22087		this._tap = new TapRecognizer({
22088			numTouches: 1,
22089			numTaps: 1
22090		});
22091		this._zoomRate = defaultZoomRate;
22092		this.reset();
22093	}
22094	setZoomRate(zoomRate) {
22095		this._zoomRate = zoomRate ?? defaultZoomRate;
22096	}
22097	reset() {
22098		this._active = false;
22099		delete this._swipePoint;
22100		delete this._swipeTouch;
22101		delete this._tapTime;
22102		delete this._tapPoint;
22103		this._tap.reset();
22104	}
22105	touchstart(e, points, mapTouches) {
22106		if (this._swipePoint) return;
22107		if (!this._tapTime) this._tap.touchstart(e, points, mapTouches);
22108		else {
22109			const swipePoint = points[0];
22110			const soonEnough = e.timeStamp - this._tapTime < 500;
22111			const closeEnough = this._tapPoint.dist(swipePoint) < 30;
22112			if (!soonEnough || !closeEnough) this.reset();
22113			else if (mapTouches.length > 0) {
22114				this._swipePoint = swipePoint;
22115				this._swipeTouch = mapTouches[0].identifier;
22116			}
22117		}
22118	}
22119	touchmove(e, points, mapTouches) {
22120		if (!this._tapTime) this._tap.touchmove(e, points, mapTouches);
22121		else if (this._swipePoint) {
22122			if (mapTouches[0].identifier !== this._swipeTouch) return;
22123			const newSwipePoint = points[0];
22124			const dist = newSwipePoint.y - this._swipePoint.y;
22125			this._swipePoint = newSwipePoint;
22126			e.preventDefault();
22127			this._active = true;
22128			return { zoomDelta: dist / 128 * this._zoomRate };
22129		}
22130	}
22131	touchend(e, points, mapTouches) {
22132		if (!this._tapTime) {
22133			const point = this._tap.touchend(e, points, mapTouches);
22134			if (point) {
22135				this._tapTime = e.timeStamp;
22136				this._tapPoint = point;
22137			}
22138		} else if (this._swipePoint) {
22139			if (mapTouches.length === 0) this.reset();
22140		}
22141	}
22142	touchcancel() {
22143		this.reset();
22144	}
22145	enable() {
22146		this._enabled = true;
22147	}
22148	disable() {
22149		this._enabled = false;
22150		this.reset();
22151	}
22152	isEnabled() {
22153		return this._enabled;
22154	}
22155	isActive() {
22156		return this._active;
22157	}
22158};
22159//#endregion
22160//#region src/ui/handler/shim/drag_pan.ts
22161/**
22162* The `DragPanHandler` allows the user to pan the map by clicking and dragging
22163* the cursor.
22164*
22165* @group Handlers
22166*/
22167var DragPanHandler = class {
22168	/** @internal */
22169	constructor(el, mousePan, touchPan) {
22170		this._el = el;
22171		this._mousePan = mousePan;
22172		this._touchPan = touchPan;
22173	}
22174	/**
22175	* Enables the "drag to pan" interaction.
22176	*
22177	* @param options - Options object
22178	* @example
22179	* ```ts
22180	*   map.dragPan.enable();
22181	*   map.dragPan.enable({
22182	*      linearity: 0.3,
22183	*      easing: bezier(0, 0, 0.3, 1),
22184	*      maxSpeed: 1400,
22185	*      deceleration: 2500,
22186	*   });
22187	* ```
22188	*/
22189	enable(options) {
22190		this._inertiaOptions = options || {};
22191		this._mousePan.enable();
22192		this._touchPan.enable();
22193		this._el.classList.add("maplibregl-touch-drag-pan");
22194	}
22195	/**
22196	* Disables the "drag to pan" interaction.
22197	*
22198	* @example
22199	* ```ts
22200	* map.dragPan.disable();
22201	* ```
22202	*/
22203	disable() {
22204		this._mousePan.disable();
22205		this._touchPan.disable();
22206		this._el.classList.remove("maplibregl-touch-drag-pan");
22207	}
22208	/**
22209	* Returns a Boolean indicating whether the "drag to pan" interaction is enabled.
22210	*
22211	* @returns `true` if the "drag to pan" interaction is enabled.
22212	*/
22213	isEnabled() {
22214		return this._mousePan.isEnabled() && this._touchPan.isEnabled();
22215	}
22216	/**
22217	* Returns a Boolean indicating whether the "drag to pan" interaction is active, i.e. currently being used.
22218	*
22219	* @returns `true` if the "drag to pan" interaction is active.
22220	*/
22221	isActive() {
22222		return this._mousePan.isActive() || this._touchPan.isActive();
22223	}
22224};
22225//#endregion
22226//#region src/ui/handler/shim/drag_rotate.ts
22227/**
22228* The `DragRotateHandler` allows the user to rotate the map by clicking and
22229* dragging the cursor while holding the right mouse button or `ctrl` key.
22230*
22231* @group Handlers
22232*/
22233var DragRotateHandler = class {
22234	/** @internal */
22235	constructor(options, mouseRotate, mousePitch, mouseRoll) {
22236		this._pitchWithRotate = options.pitchWithRotate;
22237		this._rollEnabled = options.rollEnabled;
22238		this._mouseRotate = mouseRotate;
22239		this._mousePitch = mousePitch;
22240		this._mouseRoll = mouseRoll;
22241	}
vendor: 8,930 bytes, lines 22242-22547
22242	/**
22243	* Enables the "drag to rotate" interaction.
22244	*
22245	* @example
22246	* ```ts
22247	* map.dragRotate.enable();
22248	* ```
22249	*/
22250	enable() {
22251		this._mouseRotate.enable();
22252		if (this._pitchWithRotate) this._mousePitch.enable();
22253		if (this._rollEnabled) this._mouseRoll.enable();
22254	}
22255	/**
22256	* Disables the "drag to rotate" interaction.
22257	*
22258	* @example
22259	* ```ts
22260	* map.dragRotate.disable();
22261	* ```
22262	*/
22263	disable() {
22264		this._mouseRotate.disable();
22265		this._mousePitch.disable();
22266		this._mouseRoll.disable();
22267	}
22268	/**
22269	* Returns a Boolean indicating whether the "drag to rotate" interaction is enabled.
22270	*
22271	* @returns `true` if the "drag to rotate" interaction is enabled.
22272	*/
22273	isEnabled() {
22274		return this._mouseRotate.isEnabled() && (!this._pitchWithRotate || this._mousePitch.isEnabled()) && (!this._rollEnabled || this._mouseRoll.isEnabled());
22275	}
22276	/**
22277	* Returns a Boolean indicating whether the "drag to rotate" interaction is active, i.e. currently being used.
22278	*
22279	* @returns `true` if the "drag to rotate" interaction is active.
22280	*/
22281	isActive() {
22282		return this._mouseRotate.isActive() || this._mousePitch.isActive() || this._mouseRoll.isActive();
22283	}
22284};
22285//#endregion
22286//#region src/ui/handler/shim/two_fingers_touch.ts
22287/**
22288* The `TwoFingersTouchZoomRotateHandler` allows the user to zoom and rotate the map by
22289* pinching on a touchscreen.
22290*
22291* They can zoom with one finger by double tapping and dragging. On the second tap,
22292* hold the finger down and drag up or down to zoom in or out.
22293*
22294* @group Handlers
22295*/
22296var TwoFingersTouchZoomRotateHandler = class {
22297	/** @internal */
22298	constructor(el, touchZoom, touchRotate, tapDragZoom) {
22299		this._el = el;
22300		this._touchZoom = touchZoom;
22301		this._touchRotate = touchRotate;
22302		this._tapDragZoom = tapDragZoom;
22303		this._rotationDisabled = false;
22304		this._enabled = true;
22305	}
22306	/**
22307	* Enables the "pinch to rotate and zoom" interaction.
22308	*
22309	* @param options - Options object.
22310	*
22311	* @example
22312	* ```ts
22313	* map.touchZoomRotate.enable();
22314	* map.touchZoomRotate.enable({ around: 'center' });
22315	* ```
22316	*/
22317	enable(options) {
22318		this._touchZoom.enable(options);
22319		if (!this._rotationDisabled) this._touchRotate.enable(options);
22320		this._tapDragZoom.enable();
22321		this._el.classList.add("maplibregl-touch-zoom-rotate");
22322	}
22323	/**
22324	* Disables the "pinch to rotate and zoom" interaction.
22325	*
22326	* @example
22327	* ```ts
22328	* map.touchZoomRotate.disable();
22329	* ```
22330	*/
22331	disable() {
22332		this._touchZoom.disable();
22333		this._touchRotate.disable();
22334		this._tapDragZoom.disable();
22335		this._el.classList.remove("maplibregl-touch-zoom-rotate");
22336	}
22337	/**
22338	* Returns a Boolean indicating whether the "pinch to rotate and zoom" interaction is enabled.
22339	*
22340	* @returns `true` if the "pinch to rotate and zoom" interaction is enabled.
22341	*/
22342	isEnabled() {
22343		return this._touchZoom.isEnabled() && (this._rotationDisabled || this._touchRotate.isEnabled()) && this._tapDragZoom.isEnabled();
22344	}
22345	/**
22346	* Returns true if the handler is enabled and has detected the start of a zoom/rotate gesture.
22347	*
22348	* @returns `true` if the handler is active, `false` otherwise
22349	*/
22350	isActive() {
22351		return this._touchZoom.isActive() || this._touchRotate.isActive() || this._tapDragZoom.isActive();
22352	}
22353	/**
22354	* Sets the zoom rate of touch gestures.
22355	* @param zoomRate - 1 The rate used to scale touch movement to a zoom value. Set to `undefined` to restore the default.
22356	* @example
22357	* Slow down touch zoom
22358	* ```ts
22359	* map.touchZoomRotate.setZoomRate(0.5);
22360	* ```
22361	*/
22362	setZoomRate(zoomRate) {
22363		this._touchZoom.setZoomRate(zoomRate);
22364		this._tapDragZoom.setZoomRate(zoomRate);
22365	}
22366	/**
22367	* Sets the threshold before a pinch gesture starts zooming.
22368	* @param zoomThreshold - 0.1 The minimum zoom delta before the pinch gesture becomes active. Set to `undefined` to restore the default.
22369	* @example
22370	* Make pinch zoom less sensitive
22371	* ```ts
22372	* map.touchZoomRotate.setZoomThreshold(0.3);
22373	* ```
22374	*/
22375	setZoomThreshold(zoomThreshold) {
22376		this._touchZoom.setZoomThreshold(zoomThreshold);
22377	}
22378	/**
22379	* Disables the "pinch to rotate" interaction, leaving the "pinch to zoom"
22380	* interaction enabled.
22381	*
22382	* @example
22383	* ```ts
22384	* map.touchZoomRotate.disableRotation();
22385	* ```
22386	*/
22387	disableRotation() {
22388		this._rotationDisabled = true;
22389		this._touchRotate.disable();
22390	}
22391	/**
22392	* Enables the "pinch to rotate" interaction.
22393	*
22394	* @example
22395	* ```ts
22396	* map.touchZoomRotate.enable();
22397	* map.touchZoomRotate.enableRotation();
22398	* ```
22399	*/
22400	enableRotation() {
22401		this._rotationDisabled = false;
22402		if (this._touchZoom.isEnabled()) this._touchRotate.enable();
22403	}
22404};
22405//#endregion
22406//#region src/ui/handler/cooperative_gestures.ts
22407/**
22408* A `CooperativeGestureHandler` is a control that adds cooperative gesture info when user tries to zoom in/out.
22409*
22410* When the CooperativeGestureHandler blocks a gesture, it will emit a `cooperativegestureprevented` event.
22411*
22412* @group Handlers
22413*
22414* @example
22415* ```ts
22416* const map = new Map({
22417*   cooperativeGestures: true
22418* });
22419* ```
22420* @see [Example: cooperative gestures](https://maplibre.org/maplibre-gl-js/docs/examples/cooperative-gestures/)
22421**/
22422var CooperativeGesturesHandler = class {
22423	constructor(map, options) {
22424		this._bypassKey = navigator.userAgent.includes("Mac") ? "metaKey" : "ctrlKey";
22425		this._map = map;
22426		this._options = options;
22427		this._enabled = false;
22428	}
22429	isActive() {
22430		return false;
22431	}
22432	reset() {}
22433	_setupUI() {
22434		if (this._container) return;
22435		const mapCanvasContainer = this._map.getCanvasContainer();
22436		mapCanvasContainer.classList.add("maplibregl-cooperative-gestures");
22437		this._container = DOM.create("div", "maplibregl-cooperative-gesture-screen", mapCanvasContainer);
22438		let desktopMessage = this._map._getUIString("CooperativeGesturesHandler.WindowsHelpText");
22439		if (this._bypassKey === "metaKey") desktopMessage = this._map._getUIString("CooperativeGesturesHandler.MacHelpText");
22440		const mobileMessage = this._map._getUIString("CooperativeGesturesHandler.MobileHelpText");
22441		const desktopDiv = document.createElement("div");
22442		desktopDiv.className = "maplibregl-desktop-message";
22443		desktopDiv.textContent = desktopMessage;
22444		this._container.appendChild(desktopDiv);
22445		const mobileDiv = document.createElement("div");
22446		mobileDiv.className = "maplibregl-mobile-message";
22447		mobileDiv.textContent = mobileMessage;
22448		this._container.appendChild(mobileDiv);
22449		this._container.setAttribute("aria-hidden", "true");
22450	}
22451	_destroyUI() {
22452		if (this._container) {
22453			this._container.remove();
22454			this._map.getCanvasContainer().classList.remove("maplibregl-cooperative-gestures");
22455		}
22456		delete this._container;
22457	}
22458	enable() {
22459		this._setupUI();
22460		this._enabled = true;
22461	}
22462	disable() {
22463		this._enabled = false;
22464		this._destroyUI();
22465	}
22466	isEnabled() {
22467		return this._enabled;
22468	}
22469	isBypassed(event) {
22470		return event[this._bypassKey];
22471	}
22472	notifyGestureBlocked(gestureType, originalEvent) {
22473		if (!this._enabled) return;
22474		this._map.fire(new MapLibreEvent("cooperativegestureprevented", {
22475			gestureType,
22476			originalEvent
22477		}));
22478		this._container.classList.add("maplibregl-show");
22479		setTimeout(() => {
22480			this._container.classList.remove("maplibregl-show");
22481		}, 100);
22482	}
22483};
22484//#endregion
22485//#region src/ui/handler/transform-provider.ts
22486/**
22487* @internal
22488* Shared utilities for the Handler classes to access the correct camera state.
22489* If Camera.transformCameraUpdate is specified or terrain is enabled, the
22490* "desired state" of camera may differ from the state used for rendering. The
22491* handlers need the "desired state" to track accumulated changes.
22492*/
22493var TransformProvider = class {
22494	constructor(camera) {
22495		this._camera = camera;
22496	}
22497	get transform() {
22498		return this._camera._requestedCameraState || this._camera.transform;
22499	}
22500	get center() {
22501		return {
22502			lng: this.transform.center.lng,
22503			lat: this.transform.center.lat
22504		};
22505	}
22506	get zoom() {
22507		return this.transform.zoom;
22508	}
22509	get pitch() {
22510		return this.transform.pitch;
22511	}
22512	get bearing() {
22513		return this.transform.bearing;
22514	}
22515	unproject(point) {
22516		return this.transform.screenPointToLocation(Point.convert(point), this._camera.terrain);
22517	}
22518};
22519//#endregion
22520//#region src/ui/handler_manager.ts
22521const isMoving = (p) => p.zoom || p.drag || p.roll || p.pitch || p.rotate;
22522/**
22523* A terrain gesture's anchor plane must stay below this fraction of the camera's
22524* altitude over the center-elevation plane, or the ray-plane solve degenerates.
22525*/
22526const TERRAIN_ANCHOR_MAX_CAMERA_ALTITUDE_FRACTION = .9;
22527var RenderFrameEvent = class extends Event {};
22528function hasChange(result) {
22529	return result.panDelta?.mag() || result.zoomDelta || result.bearingDelta || result.pitchDelta || result.rollDelta;
22530}
22531var HandlerManager = class {
22532	/**
22533	* @internal
22534	* The document that contains the map container element, for cross-window support.
22535	*/
22536	get _ownerDocument() {
22537		return this._el?.ownerDocument || document;
22538	}
22539	/**
22540	* @internal
22541	* The window that contains the map container element, for cross-window support.
22542	*/
22543	get _ownerWindow() {
22544		return this._el?.ownerDocument?.defaultView || window;
22545	}
22546	constructor(map, camera, options) {
22547		this._terrainGesture
vendor: 9,089 bytes, lines 22547-22818
22547AnchorElevation = null;
22548		this.handleWindowEvent = (e) => {
22549			this.handleEvent(e, `${e.type}Window`);
22550		};
22551		this.handleEvent = (e, eventName) => {
22552			if (e.type === "blur") {
22553				this.stop(true);
22554				return;
22555			}
22556			this._updatingCamera = true;
22557			const inputEvent = e.type === "renderFrame" ? void 0 : e;
22558			const mergedHandlerResult = { needsRenderFrame: false };
22559			const eventsInProgress = {};
22560			const activeHandlers = {};
22561			for (const { handlerName, handler, allowed } of this._handlers) {
22562				if (!handler.isEnabled()) continue;
22563				let data;
22564				if (this._blockedByActive(activeHandlers, allowed, handlerName)) handler.reset();
22565				else if (handler[eventName || e.type]) {
22566					if (isPointableEvent(e, eventName || e.type)) {
22567						const point = DOM.mousePos(this._map.getCanvas(), e);
22568						data = handler[eventName || e.type](e, point);
22569					} else if (isTouchableEvent(e, eventName || e.type)) {
22570						const eventTouches = e.touches;
22571						const mapTouches = this._getMapTouches(eventTouches);
22572						const points = DOM.touchPos(this._map.getCanvas(), mapTouches);
22573						data = handler[eventName || e.type](e, points, mapTouches);
22574					} else if (!isTouchableOrPointableType(eventName || e.type)) data = handler[eventName || e.type](e);
22575					this.mergeHandlerResult(mergedHandlerResult, eventsInProgress, data, handlerName, inputEvent);
22576					if (data?.needsRenderFrame) this._triggerRenderFrame();
22577				}
22578				if (data || handler.isActive()) activeHandlers[handlerName] = handler;
22579			}
22580			const deactivatedHandlers = {};
22581			for (const name in this._previousActiveHandlers) if (!activeHandlers[name]) deactivatedHandlers[name] = inputEvent;
22582			this._previousActiveHandlers = activeHandlers;
22583			if (Object.keys(deactivatedHandlers).length || hasChange(mergedHandlerResult)) {
22584				this._changes.push([
22585					mergedHandlerResult,
22586					eventsInProgress,
22587					deactivatedHandlers
22588				]);
22589				this._triggerRenderFrame();
22590			}
22591			if (Object.keys(activeHandlers).length || hasChange(mergedHandlerResult)) this._camera.stop(true);
22592			this._updatingCamera = false;
22593			const { cameraAnimation } = mergedHandlerResult;
22594			if (cameraAnimation) {
22595				this._inertia.clear();
22596				this._fireEvents({}, {}, true);
22597				this._changes = [];
22598				cameraAnimation(this._map);
22599			}
22600		};
22601		this._map = map;
22602		this._camera = camera;
22603		this._transformProvider = new TransformProvider(this._camera);
22604		this._el = this._map.getCanvasContainer();
22605		this._handlers = [];
22606		this._handlersById = {};
22607		this._changes = [];
22608		this._inertia = new HandlerInertia(map);
22609		this._bearingSnap = options.bearingSnap;
22610		this._previousActiveHandlers = {};
22611		this._eventsInProgress = {};
22612		this._addDefaultHandlers(options);
22613		const el = this._el;
22614		this._listeners = [
22615			[
22616				el,
22617				"touchstart",
22618				{ passive: true }
22619			],
22620			[
22621				el,
22622				"touchmove",
22623				{ passive: false }
22624			],
22625			[
22626				el,
22627				"touchend",
22628				void 0
22629			],
22630			[
22631				el,
22632				"touchcancel",
22633				void 0
22634			],
22635			[
22636				el,
22637				"mousedown",
22638				void 0
22639			],
22640			[
22641				el,
22642				"mousemove",
22643				void 0
22644			],
22645			[
22646				el,
22647				"mouseup",
22648				void 0
22649			],
22650			[
22651				this._ownerDocument,
22652				"mousemove",
22653				{ capture: true }
22654			],
22655			[
22656				this._ownerDocument,
22657				"mouseup",
22658				void 0
22659			],
22660			[
22661				el,
22662				"mouseover",
22663				void 0
22664			],
22665			[
22666				el,
22667				"mouseout",
22668				void 0
22669			],
22670			[
22671				el,
22672				"dblclick",
22673				void 0
22674			],
22675			[
22676				el,
22677				"click",
22678				void 0
22679			],
22680			[
22681				el,
22682				"keydown",
22683				{ capture: false }
22684			],
22685			[
22686				el,
22687				"keyup",
22688				void 0
22689			],
22690			[
22691				el,
22692				"wheel",
22693				{ passive: false }
22694			],
22695			[
22696				el,
22697				"contextmenu",
22698				void 0
22699			],
22700			[
22701				this._ownerWindow,
22702				"blur",
22703				void 0
22704			]
22705		];
22706		for (const [target, type, listenerOptions] of this._listeners) target.addEventListener(type, target === this._ownerDocument ? this.handleWindowEvent : this.handleEvent, listenerOptions);
22707	}
22708	destroy() {
22709		for (const [target, type, listenerOptions] of this._listeners) target.removeEventListener(type, target === this._ownerDocument ? this.handleWindowEvent : this.handleEvent, listenerOptions);
22710	}
22711	_addDefaultHandlers(options) {
22712		const map = this._map;
22713		const el = map.getCanvasContainer();
22714		this._add("mapEvent", new MapEventHandler(map, options));
22715		const boxZoom = map.boxZoom = new BoxZoomHandler(map, options, this._transformProvider);
22716		this._add("boxZoom", boxZoom);
22717		if (options.interactive && options.boxZoom) boxZoom.enable();
22718		const cooperativeGestures = map.cooperativeGestures = new CooperativeGesturesHandler(map, options.cooperativeGestures);
22719		this._add("cooperativeGestures", cooperativeGestures);
22720		if (options.cooperativeGestures) cooperativeGestures.enable();
22721		const tapZoom = new TapZoomHandler(map, this._transformProvider);
22722		const clickZoom = new ClickZoomHandler(map, this._transformProvider);
22723		map.doubleClickZoom = new DoubleClickZoomHandler(clickZoom, tapZoom);
22724		this._add("tapZoom", tapZoom);
22725		this._add("clickZoom", clickZoom);
22726		if (options.interactive && options.doubleClickZoom) map.doubleClickZoom.enable();
22727		const tapDragZoom = new TapDragZoomHandler();
22728		this._add("tapDragZoom", tapDragZoom);
22729		const touchPitch = map.touchPitch = new TwoFingersTouchPitchHandler(map);
22730		this._add("touchPitch", touchPitch);
22731		if (options.interactive && options.touchPitch) map.touchPitch.enable(options.touchPitch);
22732		const getCenter = () => map.project(map.getCenter());
22733		const mouseRotate = generateMouseRotationHandler(options, getCenter);
22734		const mousePitch = generateMousePitchHandler(options);
22735		const mouseRoll = generateMouseRollHandler(options, getCenter);
22736		map.dragRotate = new DragRotateHandler(options, mouseRotate, mousePitch, mouseRoll);
22737		this._add("mouseRotate", mouseRotate, ["mousePitch"]);
22738		this._add("mousePitch", mousePitch, ["mouseRotate", "mouseRoll"]);
22739		this._add("mouseRoll", mouseRoll, ["mousePitch"]);
22740		if (options.interactive && options.dragRotate) map.dragRotate.enable();
22741		const mousePan = generateMousePanHandler(options);
22742		const touchPan = new TouchPanHandler(options, map);
22743		map.dragPan = new DragPanHandler(el, mousePan, touchPan);
22744		this._add("mousePan", mousePan);
22745		this._add("touchPan", touchPan, ["touchZoom", "touchRotate"]);
22746		if (options.interactive && options.dragPan) map.dragPan.enable(options.dragPan);
22747		const touchRotate = new TwoFingersTouchRotateHandler();
22748		const touchZoom = new TwoFingersTouchZoomHandler();
22749		map.touchZoomRotate = new TwoFingersTouchZoomRotateHandler(el, touchZoom, touchRotate, tapDragZoom);
22750		this._add("touchRotate", touchRotate, ["touchPan", "touchZoom"]);
22751		this._add("touchZoom", touchZoom, ["touchPan", "touchRotate"]);
22752		if (options.interactive && options.touchZoomRotate) map.touchZoomRotate.enable(options.touchZoomRotate);
22753		this._add("blockableMapEvent", new BlockableMapEventHandler(map, options), ["touchPan"]);
22754		const scrollZoom = map.scrollZoom = new ScrollZoomHandler(map, () => this._triggerRenderFrame(), this._transformProvider);
22755		this._add("scrollZoom", scrollZoom, ["mousePan"]);
22756		if (options.interactive && options.scrollZoom) map.scrollZoom.enable(options.scrollZoom);
22757		const keyboard = map.keyboard = new KeyboardHandler(map, this._transformProvider);
22758		this._add("keyboard", keyboard);
22759		if (options.interactive && options.keyboard) map.keyboard.enable();
22760	}
22761	_add(handlerName, handler, allowed) {
22762		this._handlers.push({
22763			handlerName,
22764			handler,
22765			allowed
22766		});
22767		this._handlersById[handlerName] = handler;
22768	}
22769	stop(allowEndAnimation) {
22770		if (this._updatingCamera) return;
22771		for (const { handler } of this._handlers) handler.reset();
22772		this._inertia.clear();
22773		this._fireEvents({}, {}, allowEndAnimation);
22774		this._changes = [];
22775	}
22776	isActive() {
22777		for (const { handler } of this._handlers) if (handler.isActive()) return true;
22778		return false;
22779	}
22780	isZooming() {
22781		return !!this._eventsInProgress.zoom || this._map.scrollZoom.isZooming();
22782	}
22783	isRotating() {
22784		return !!this._eventsInProgress.rotate;
22785	}
22786	isMoving() {
22787		return Boolean(isMoving(this._eventsInProgress)) || this.isZooming();
22788	}
22789	_blockedByActive(activeHandlers, allowed, myName) {
22790		for (const name in activeHandlers) {
22791			if (name === myName) continue;
22792			if (!allowed?.includes(name)) return true;
22793		}
22794		return false;
22795	}
22796	_getMapTouches(touches) {
22797		const mapTouches = [];
22798		for (const t of touches) {
22799			const target = t.target;
22800			if (this._el.contains(target)) mapTouches.push(t);
22801		}
22802		return mapTouches;
22803	}
22804	mergeHandlerResult(mergedHandlerResult, eventsInProgress, handlerResult, name, e) {
22805		if (!handlerResult) return;
22806		extend(mergedHandlerResult, handlerResult);
22807		const eventData = {
22808			handlerName: name,
22809			originalEvent: handlerResult.originalEvent || e
22810		};
22811		if (handlerResult.zoomDelta !== void 0) eventsInProgress.zoom = eventData;
22812		if (handlerResult.panDelta !== void 0) eventsInProgress.drag = eventData;
22813		if (handlerResult.rollDelta !== void 0) eventsInProgress.roll = eventData;
22814		if (handlerResult.pitchDelta !== void 0) eventsInProgress.pitch = eventData;
22815		if (handlerResult.bearingDelta !== void 0) eventsInProgress.rotate = eventData;
22816	}
22817	_applyChanges() {
22818		const combined = {};
22819		const combinedEventsInProgress = {};
22820		const combinedDeactivatedHandlers = {};
22821		for (const [change, eventsInProgress, deactivatedHandlers] of this._changes) {
22822			if (change.panDelta) combined.panDelta = (combined.panDelta || new Point(0, 0))._add(change.panDelta);
22823			if (change.zoomDelta) combined.zoomDelta = (combined.zoomDelta || 0) + change.zoomDelta;
22824			if (change.bearingDelta) combined.bearingDelta = (combined.bearingDelta || 0) + change.bearingDelta;
22825			if (change.pitchDelta) combined.pitchDelta = (combined.pitchDelta || 0) + change.pitchDelta;
22826			if (change.rollDelta) combined.rollDelta = (combined.rollDelta || 0) + change.rollDelta;
22827			if (change.around !== void 0) combined.around = change.around;
22828			if (change.pinchAround !== void 0) combined.pinchAround = change.pinchAround;
22829			if (change.noInertia) combined.noInertia = change.noInertia;
22830			extend(combinedEventsInProgress, eventsInProgress);
22831			extend(combinedDeactivatedHandlers, deactivatedHandlers);
22832		}
22833		this._updateMapTransform(combined, combinedEventsInProgress, combinedDeactivatedHandlers);
22834		this._changes = [];
22835	}
22836	_updateMapTransform(combinedResult, combinedEventsInProgress, deactivatedHandlers) {
22837		const map = this._map;
22838		const tr = this._camera.getTransformForUpdate();
22839		const terrain = map.terrain;
22840		if (!hasChange(combinedResult) && !(terrain && this._terrainMovement)) {
22841			this._fireEvents(combinedEventsInProgress, deactivatedHandlers, true);
22842			return;
22843		}
22844		this._camera.stop(true);
22845		const { panDelta, zoomDelta, bearingDelta, pitchDelta, rollDelta } = combinedResult;
22846		let { around, aroundOnSurface } = this._resolveAround(combinedResult, terrain, tr);
22847		const aroundElevation = terrain ? this._terrainGestureElevation(terrain, around, aroundOnSurface, tr, combinedEventsInProgress) : void 0;
22848		const deltasForHelper = {
22849			panDelta,
22850			zoomDelta,
22851			rollDelta,
22852			pitchDelta,
22853			bearingDelta,
22854			around,
22855			aroundElevation
22856		};
22857		if (this._camera.cameraHelper.useGlobeControls && !tr.isPointOnMapSurface(around)) around = tr.centerPoint;
22858		const preZoomAroundLoc = this._computePreZoomAroundLoc(tr, around, panDelta, aroundElevation);
22859		this._handleMapControls({
22860			terrain,
22861			tr,
22862			deltasForHelper,
22863			preZoomAroundLoc,
22864			combinedEventsInProgress,
22865			panDelta
22866		});
22867		this._camera.applyUpdatedTransform(tr);
22868		this._map._update();
22869		if (!combinedResult.noInertia) this._inertia.record(combinedResult);
22870		this._fireEvents(combinedEventsInProgress, deactivatedHandlers, true);
22871	}
22872	/**
22873	* The gesture's anchor point: the pinch midpoint when pinching, otherwise the
22874	* pan's anchor, clamped to the center point when it does not lie on the map.
22875	*/
22876	_resolveAround(combinedResult, terrain, tr) {
22877		let around = combinedResult.pinchAround !== void 0 ? combinedResult.pinchAround : combinedResult.around;
22878		around ||= this._camera.transform.centerPoint;
22879		if (terrain && !tr.isPointOnMapSurface(around)) return {
22880			around: tr.centerPoint,
22881			aroundOnSurface: false
22882		};
22883		return {
22884			around,
22885			aroundOnSurface: true
22886		};
22887	}
22888	/**
22889	* The elevation of the plane a terrain gesture is solved on: the elevation of the
22890	* terrain point grabbed at gesture start, captured here on the gesture's first
22891	* frame. Undefined means the gesture is solved at the center's elevation instead —
22892	* when the grabbed terrain is not loaded, the anchor is the center point (a
22893	* center-point anchor is skipped by the camera helper and would lose the pan), or
22894	* the anchor plane is too close to the camera for a stable ray-plane solve.
22895	*/
22896	_terrainGestureElevation(terrain, around, aroundOnSurface, tr, combinedEventsInProgress) {
22897		if (!aroundOnSurface) return;
22898		if (!this._terrainMovement && (combinedEventsInProgress.drag || combinedEventsInProgress.zoom)) {
22899			const anchor = tr.screenTerrainPointToMercatorCoordinate(around, terrain);
22900			this._terrainGestureAnchorElevation = anchor ? anchor.z : null;
22901		}
22902		if (this._terrainGestureAnchorElevation === null) return;
22903		const elevation = this._terrainGestureAnchorElevation;
22904		if (around.distSqr(tr.centerPoint) < .01) return;
22905		if (elevation - tr.elevation >= TERRAIN_ANCHOR_MAX_CAMERA_ALTITUDE_FRACTION * (tr.getCameraAltitude() - tr.elevation)) return;
22906		return elevation;
22907	}
22908	/**
22909	* The location that was under `around` before this frame's deltas — the point the
22910	* camera helper re-anchors after zooming — solved at `aroundElevation` when a
22911	* terrain gesture provides one. When rotating about the center point, the
22912	* transform's center is used directly to avoid numerical issues near the horizon.
22913	*/
22914	_computePreZoomAroundLoc(tr, around, panDelta, aroundElevation) {
22915		if (around.distSqr(tr.centerPoint) < .01) return tr.center;
22916		const aroundPreviousPoint = panDelta ? around.sub(panDelta) : around;
22917		if (aroundElevation !== void 0) return tr.screenPointToLocationAtElevation(aroundPreviousPoint, aroundElevation);
22918		return tr.screenPointToLocation(aroundPreviousPoint);
22919	}
22920	_handleMapControls({ terrain, tr, deltasForHelper, preZoomAroundLoc, combinedEventsInProgress, panDelta }) {
22921		const cameraHelper = this._camera.cameraHelper;
22922		cameraHelper.handleMapControlsRollPitchBearingZoom(deltasForHelper, tr);
22923		if (!terrain) {
22924			cameraHelper.handleMapControlsPan(deltasForHelper, tr, preZoomAroundLoc);
22925			return;
22926		}
22927		if (cameraHelper.useGlobeControls) {
22928			if (!this._terrainMovement && (combinedEventsInProgress.drag || combinedEventsInProgress.zoom)) {
22929				this._terrainMovement = true;
22930				this._camera.elevationFreeze = true;
22931			}
22932			cameraHelper.handleMapControlsPan(deltasForHelper, tr, preZoomAroundLoc);
22933			return;
22934		}
22935		if (!this._terrainMovement && (combinedEventsInProgress.drag || combinedEventsInProgress.zoom)) {
22936			this._terrainMovement = true;
22937			this._camera.elevationFreeze = true;
22938			cameraHelper.handleMapControlsPan(deltasForHelper, tr, preZoomAroundLoc);
22939			return;
22940		}
22941		if (deltasForHelper.aroundElevation === void 0 && combinedEventsInProgress.drag && this._terrainMovement && panDelta) {
22942			tr.setCenter(tr.screenPointToLocation(tr.centerPoint.sub(panDelta)));
22943			return;
22944		}
22945		cameraHelper.handleMapControlsPan(deltasForHelper, tr, preZoomAroundLoc);
22946	}
22947	_fireEvents(newEventsInProgress, deactivatedHandlers, allowEndAnimation) {
22948		const wasMoving = isMoving(this._eventsInProgress);
22949		const nowMoving = isMoving(newEventsInProgress);
22950		const startEvents = {};
22951		for (const eventName in newEventsInProgress) {
22952			const { originalEvent } = newEventsInProgress[eventName];
22953			if (!this._eventsInProgress[eventName]) startEvents[`${eventName}start`] = originalEvent;
22954			this._eventsInProgress[eventName] = newEventsInProgress[eventName];
22955		}
22956		if (!wasMoving && nowMoving) this._fireEvent("movestart", nowMoving.originalEvent);
22957		for (const name in startEvents) this._fireEvent(name, startEvents[name]);
22958		if (nowMoving) this._fireEvent("move", nowMoving.originalEvent);
22959		for (const eventName in newEventsInProgress) {
22960			const { originalEvent } = newEventsInProgress[eventName];
22961			this._fireEvent(eventName, originalEvent);
22962		}
22963		const endEvents = {};
22964		let originalEndEvent;
22965		for (const eventName in this._eventsInProgress) {
22966			const { handlerName, originalEvent } = this._eventsInProgress[eventName];
22967			if (!this._handlersById[handlerName].isActive()) {
22968				delete this._eventsInProgress[eventName];
22969				originalEndEvent = deactivatedHandlers[handlerName] || originalEvent;
22970				endEvents[`${eventName}
22970end`] = originalEndEvent;
22971			}
22972		}
22973		for (const name in endEvents) this._fireEvent(name, endEvents[name]);
22974		const stillMoving = isMoving(this._eventsInProgress);
22975		const finishedMoving = (wasMoving || nowMoving) && !stillMoving;
22976		if (finishedMoving && this._terrainMovement) {
22977			this._camera.elevationFreeze = false;
22978			this._terrainMovement = false;
22979			this._terrainGestureAnchorElevation = null;
22980			const tr = this._camera.getTransformForUpdate();
22981			if (this._map.getCenterClampedToGround()) tr.recalculateZoomAndCenter(this._map.terrain);
22982			this._camera.applyUpdatedTransform(tr);
22983		}
22984		if (allowEndAnimation && finishedMoving) {
22985			this._updatingCamera = true;
22986			const inertialEase = this._inertia._onMoveEnd(this._map.dragPan._inertiaOptions);
22987			const shouldSnapToNorth = (bearing) => bearing !== 0 && -this._bearingSnap < bearing && bearing < this._bearingSnap;
22988			if (inertialEase && (inertialEase.essential || !browser.prefersReducedMotion)) {
22989				if (shouldSnapToNorth(inertialEase.bearing || this._map.getBearing())) inertialEase.bearing = 0;
22990				inertialEase.freezeElevation = true;
22991				this._map.easeTo(inertialEase, { originalEvent: originalEndEvent });
22992			} else {
22993				this._map.fire(new MapMovementEvent("moveend", { originalEvent: originalEndEvent }));
22994				if (shouldSnapToNorth(this._map.getBearing())) this._map.resetNorth();
22995			}
22996			this._updatingCamera = false;
22997		}
22998	}
22999	_fireEvent(type, e) {
23000		this._map.fire(new MapMovementEvent(type, e ? { originalEvent: e } : {}));
23001	}
23002	_requestFrame() {
23003		this._map.triggerRepaint();
23004		return this._map._renderTaskQueue.add((timeStamp) => {
23005			delete this._frameId;
23006			this.handleEvent(new RenderFrameEvent("renderFrame", { timeStamp }));
23007			this._applyChanges();
23008		});
23009	}
23010	_triggerRenderFrame() {
23011		if (this._frameId === void 0) this._frameId = this._requestFrame();
23012	}
23013};
23014//#endregion
23015//#region src/ui/camera.ts
23016var Camera = class extends Evented {
23017	constructor(options) {
23018		super();
23019		this._renderFrameCallback = () => {
23020			const t = Math.min((now() - this._easeStart) / this._easeOptions.duration, 1);
23021			this._onEaseFrame(this._easeOptions.easing(t));
23022			if (t < 1 && this._easeFrameId) this._easeFrameId = this._requestRenderFrame(this._renderFrameCallback);
23023			else this.stop();
23024		};
23025		this.transform = new MercatorTransform();
23026		this.cameraHelper = new MercatorCameraHelper();
23027		if (options.minZoom !== void 0) this.transform.setMinZoom(options.minZoom);
23028		if (options.maxZoom !== void 0) this.transform.setMaxZoom(options.maxZoom);
23029		if (options.minPitch !== void 0) this.transform.setMinPitch(options.minPitch);
23030		if (options.maxPitch !== void 0) this.transform.setMaxPitch(options.maxPitch);
23031		if (options.renderWorldCopies !== void 0) this.transform.setRenderWorldCopies(options.renderWorldCopies);
23032		if (options.transformConstrain !== null) this.transform.setConstrainOverride(options.transformConstrain);
23033		this._moving = false;
23034		this._zooming = false;
23035		this._bearingSnap = options.bearingSnap;
23036		this._zoomSnap = options.zoomSnap;
23037		this._requestRenderFrame = options.requestRenderFrame;
23038		this._cancelRenderFrame = options.cancelRenderFrame;
23039		this.terrain = options.terrain;
23040		this._centerClampedToGround = options.centerClampedToGround ?? true;
23041		this.transformCameraUpdate = options.transformCameraUpdate ?? null;
23042		this._stopHandlers = options.stopHandlers ?? (() => {});
23043		this.on("moveend", () => {
23044			delete this._requestedCameraState;
23045		}
vendor: 11,108 bytes, lines 23045-23351
23045);
23046	}
23047	migrateProjection(newTransform, newCameraHelper) {
23048		newTransform.apply(this.transform, true);
23049		this.transform = newTransform;
23050		this.cameraHelper = newCameraHelper;
23051		if (this._requestedCameraState) {
23052			const requestedCameraState = newTransform.clone();
23053			requestedCameraState.apply(this._requestedCameraState, true);
23054			this._requestedCameraState = requestedCameraState;
23055		}
23056	}
23057	getCenter() {
23058		return new LngLat(this.transform.center.lng, this.transform.center.lat);
23059	}
23060	setCenter(center, eventData) {
23061		return this.jumpTo({ center }, eventData);
23062	}
23063	getCenterElevation() {
23064		return this.transform.elevation;
23065	}
23066	setCenterElevation(elevation, eventData) {
23067		this.jumpTo({ elevation }, eventData);
23068		return this;
23069	}
23070	getCenterClampedToGround() {
23071		return this._centerClampedToGround;
23072	}
23073	setCenterClampedToGround(centerClampedToGround) {
23074		this._centerClampedToGround = centerClampedToGround;
23075	}
23076	panBy(offset, options, eventData) {
23077		offset = Point.convert(offset).mult(-1);
23078		return this.panTo(this.transform.center, extend({ offset }, options), eventData);
23079	}
23080	panTo(lnglat, options, eventData) {
23081		return this.easeTo(extend({ center: lnglat }, options), eventData);
23082	}
23083	getZoom() {
23084		return this.transform.zoom;
23085	}
23086	setZoom(zoom, eventData) {
23087		this.jumpTo({ zoom }, eventData);
23088		return this;
23089	}
23090	zoomTo(zoom, options, eventData) {
23091		return this.easeTo(extend({ zoom }, options), eventData);
23092	}
23093	zoomIn(options, eventData) {
23094		this.zoomTo(evaluateZoomSnap(this.getZoom() + 1, this._zoomSnap), options, eventData);
23095		return this;
23096	}
23097	zoomOut(options, eventData) {
23098		this.zoomTo(evaluateZoomSnap(this.getZoom() - 1, this._zoomSnap), options, eventData);
23099		return this;
23100	}
23101	getVerticalFieldOfView() {
23102		return this.transform.fov;
23103	}
23104	setVerticalFieldOfView(fov, eventData) {
23105		if (fov != this.transform.fov) {
23106			this.transform.setFov(fov);
23107			this.fire(new MapMovementEvent("movestart", eventData)).fire(new MapMovementEvent("move", eventData)).fire(new MapMovementEvent("moveend", eventData));
23108		}
23109		return this;
23110	}
23111	getBearing() {
23112		return this.transform.bearing;
23113	}
23114	setZoomSnap(snap) {
23115		this._zoomSnap = snap;
23116		return this;
23117	}
23118	getZoomSnap() {
23119		return this._zoomSnap;
23120	}
23121	setBearing(bearing, eventData) {
23122		this.jumpTo({ bearing }, eventData);
23123		return this;
23124	}
23125	getPadding() {
23126		return this.transform.padding;
23127	}
23128	setPadding(padding, eventData) {
23129		this.jumpTo({ padding }, eventData);
23130		return this;
23131	}
23132	rotateTo(bearing, options, eventData) {
23133		return this.easeTo(extend({ bearing }, options), eventData);
23134	}
23135	resetNorth(options, eventData) {
23136		this.rotateTo(0, extend({ duration: 1e3 }, options), eventData);
23137		return this;
23138	}
23139	resetNorthPitch(options, eventData) {
23140		this.easeTo(extend({
23141			bearing: 0,
23142			pitch: 0,
23143			roll: 0,
23144			duration: 1e3
23145		}, options), eventData);
23146		return this;
23147	}
23148	snapToNorth(options, eventData) {
23149		if (Math.abs(this.getBearing()) < this._bearingSnap) return this.resetNorth(options, eventData);
23150		return this;
23151	}
23152	getPitch() {
23153		return this.transform.pitch;
23154	}
23155	setPitch(pitch, eventData) {
23156		this.jumpTo({ pitch }, eventData);
23157		return this;
23158	}
23159	getRoll() {
23160		return this.transform.roll;
23161	}
23162	setRoll(roll, eventData) {
23163		this.jumpTo({ roll }, eventData);
23164		return this;
23165	}
23166	cameraForBounds(bounds, options) {
23167		bounds = LngLatBounds.convert(bounds).adjustAntiMeridian();
23168		const bearing = options?.bearing || 0;
23169		return this._cameraForBoxAndBearing(bounds.getNorthWest(), bounds.getSouthEast(), bearing, options);
23170	}
23171	/**
23172	* @internal
23173	* Calculate the center of these two points in the viewport and use
23174	* the highest zoom level up to and including {@link Map.getMaxZoom} that fits
23175	* the AABB defined by these points in the viewport at the specified bearing.
23176	* @param p0 - First point
23177	* @param p1 - Second point
23178	* @param bearing - Desired map bearing at end of animation, in degrees
23179	* @param options - the camera options
23180	* @returns If map is able to fit to provided bounds, returns `center`, `zoom`, and `bearing`.
23181	*      If map is unable to fit, method will warn and return undefined.
23182	* @example
23183	* ```ts
23184	* let p0 = [-79, 43];
23185	* let p1 = [-73, 45];
23186	* let bearing = 90;
23187	* let newCameraTransform = map._cameraForBoxAndBearing(p0, p1, bearing, {
23188	*   padding: {top: 10, bottom:25, left: 15, right: 5}
23189	* });
23190	* ```
23191	*/
23192	_cameraForBoxAndBearing(p0, p1, bearing, options) {
23193		const defaultPadding = {
23194			top: 0,
23195			bottom: 0,
23196			right: 0,
23197			left: 0
23198		};
23199		options = extend({
23200			padding: defaultPadding,
23201			offset: [0, 0],
23202			maxZoom: this.transform.maxZoom
23203		}, options);
23204		if (typeof options.padding === "number") {
23205			const p = options.padding;
23206			options.padding = {
23207				top: p,
23208				bottom: p,
23209				right: p,
23210				left: p
23211			};
23212		}
23213		const padding = extend(defaultPadding, options.padding);
23214		options.padding = padding;
23215		const tr = this.transform;
23216		const bounds = new LngLatBounds(p0, p1);
23217		const result = this.cameraHelper.cameraForBoxAndBearing(options, padding, bounds, bearing, tr);
23218		if (result && this._zoomSnap) result.zoom = evaluateZoomSnap(result.zoom, this._zoomSnap, -1);
23219		return result;
23220	}
23221	fitBounds(bounds, options, eventData) {
23222		return this._fitInternal(this.cameraForBounds(bounds, options), options, eventData);
23223	}
23224	fitScreenCoordinates(p0, p1, bearing, options, eventData) {
23225		return this._fitInternal(this._cameraForBoxAndBearing(this.transform.screenPointToLocation(Point.convert(p0)), this.transform.screenPointToLocation(Point.convert(p1)), bearing, options), options, eventData);
23226	}
23227	_fitInternal(calculatedOptions, options, eventData) {
23228		if (!calculatedOptions) return this;
23229		options = extend(calculatedOptions, options);
23230		delete options.padding;
23231		return options.linear ? this.easeTo(options, eventData) : this.flyTo(options, eventData);
23232	}
23233	jumpTo(options, eventData) {
23234		this.stop();
23235		if (options.zoom !== void 0 && this._zoomSnap) options.zoom = evaluateZoomSnap(options.zoom, this._zoomSnap);
23236		const tr = this.getTransformForUpdate();
23237		let bearingChanged = false, pitchChanged = false;
23238		let rollChanged = false;
23239		const oldZoom = tr.zoom;
23240		if (this.terrain) tr.setElevation(this.terrain.getElevationForLngLat(options.center ? LngLat.convert(options.center) : tr.center, tr));
23241		this.cameraHelper.handleJumpToCenterZoom(tr, options);
23242		const zoomChanged = tr.zoom !== oldZoom;
23243		if (options.elevation !== void 0 && tr.elevation !== +options.elevation) tr.setElevation(+options.elevation);
23244		if (options.bearing !== void 0 && tr.bearing !== +options.bearing) {
23245			bearingChanged = true;
23246			tr.setBearing(+options.bearing);
23247		}
23248		if (options.pitch !== void 0 && tr.pitch !== +options.pitch) {
23249			pitchChanged = true;
23250			tr.setPitch(+options.pitch);
23251		}
23252		if (options.roll !== void 0 && tr.roll !== +options.roll) {
23253			rollChanged = true;
23254			tr.setRoll(+options.roll);
23255		}
23256		if (options.padding != null && !tr.isPaddingEqual(options.padding)) tr.setPadding(options.padding);
23257		this.applyUpdatedTransform(tr);
23258		this.fire(new MapMovementEvent("movestart", eventData)).fire(new MapMovementEvent("move", eventData));
23259		if (zoomChanged) this.fire(new MapMovementEvent("zoomstart", eventData)).fire(new MapMovementEvent("zoom", eventData)).fire(new MapMovementEvent("zoomend", eventData));
23260		if (bearingChanged) this.fire(new MapMovementEvent("rotatestart", eventData)).fire(new MapMovementEvent("rotate", eventData)).fire(new MapMovementEvent("rotateend", eventData));
23261		if (pitchChanged) this.fire(new MapMovementEvent("pitchstart", eventData)).fire(new MapMovementEvent("pitch", eventData)).fire(new MapMovementEvent("pitchend", eventData));
23262		if (rollChanged) this.fire(new MapMovementEvent("rollstart", eventData)).fire(new MapMovementEvent("roll", eventData)).fire(new MapMovementEvent("rollend", eventData));
23263		return this.fire(new MapMovementEvent("moveend", eventData));
23264	}
23265	/**
23266	* Calculates camera options for moving a geographic anchor to a screen point without
23267	* changing this camera.
23268	*/
23269	calculateAnchoredCameraOptions(options) {
23270		const tr = this.transform.clone();
23271		const anchor = LngLat.convert(options.anchorLocation);
23272		const target = Point.convert(options.anchorScreenPoint);
23273		const deltas = {
23274			panDelta: target.sub(this.transform.locationToScreenPoint(anchor, this.terrain)),
23275			zoomDelta: options.zoom === void 0 ? 0 : options.zoom - tr.zoom,
23276			bearingDelta: 0,
23277			pitchDelta: 0,
23278			rollDelta: 0,
23279			around: target,
23280			aroundElevation: this.terrain?.getElevationForLngLat(anchor, tr)
23281		};
23282		this.cameraHelper.handleMapControlsRollPitchBearingZoom(deltas, tr);
23283		this.cameraHelper.handleMapControlsPan(deltas, tr, anchor);
23284		return {
23285			center: tr.center,
23286			zoom: tr.zoom
23287		};
23288	}
23289	calculateCameraOptionsFromCameraLngLatAltRotation(cameraLngLat, cameraAlt, bearing, pitch, roll) {
23290		const centerInfo = this.transform.calculateCenterFromCameraLngLatAlt(cameraLngLat, cameraAlt, bearing, pitch);
23291		return {
23292			center: centerInfo.center,
23293			elevation: centerInfo.elevation,
23294			zoom: centerInfo.zoom,
23295			bearing,
23296			pitch,
23297			roll
23298		};
23299	}
23300	easeTo(options, eventData) {
23301		this._stop(false, options.easeId);
23302		options = extend({
23303			offset: [0, 0],
23304			duration: 500,
23305			easing: defaultEasing
23306		}, options);
23307		if (options.zoom !== void 0 && this._zoomSnap) options.zoom = evaluateZoomSnap(options.zoom, this._zoomSnap);
23308		if (options.animate === false || !options.essential && browser.prefersReducedMotion) options.duration = 0;
23309		const tr = this.getTransformForUpdate();
23310		const startBearing = this.getBearing(), startPitch = tr.pitch, startRoll = tr.roll, bearing = options.bearing !== void 0 ? this._normalizeBearing(options.bearing, startBearing) : startBearing, pitch = options.pitch !== void 0 ? +options.pitch : startPitch, roll = options.roll !== void 0 ? this._normalizeBearing(options.roll, startRoll) : startRoll, padding = options.padding !== void 0 ? options.padding : tr.padding;
23311		const offsetAsPoint = Point.convert(options.offset);
23312		let around, aroundPoint;
23313		if (options.around) {
23314			around = LngLat.convert(options.around);
23315			aroundPoint = tr.locationToScreenPoint(around);
23316		}
23317		const currently = {
23318			moving: this._moving,
23319			zooming: this._zooming,
23320			rotating: this._rotating,
23321			pitching: this._pitching,
23322			rolling: this._rolling
23323		};
23324		const easeHandler = this.cameraHelper.handleEaseTo(tr, {
23325			bearing,
23326			pitch,
23327			roll,
23328			padding,
23329			around,
23330			aroundPoint,
23331			offsetAsPoint,
23332			offset: options.offset,
23333			zoom: options.zoom,
23334			center: options.center
23335		});
23336		this._rotating ||= startBearing !== bearing;
23337		this._pitching ||= pitch !== startPitch;
23338		this._rolling ||= roll !== startRoll;
23339		this._padding = !tr.isPaddingEqual(padding);
23340		this._zooming ||= easeHandler.isZooming;
23341		this._easeId = options.easeId;
23342		this._prepareEase(eventData, options.noMoveStart, currently);
23343		if (this.terrain) this._prepareElevation(easeHandler.elevationCenter);
23344		this._ease((k) => {
23345			easeHandler.easeFunc(k);
23346			if (this.terrain && !options.freezeElevation) this._updateElevation(k);
23347			this.applyUpdatedTransform(tr);
23348			this._fireMoveEvents(eventData);
23349		}, (interruptingEaseId) => {
23350			if (this.terrain && options.freezeElevation) this._finalizeElevation();
23351			
vendor: 9,851 bytes, lines 23351-23585
23351this._afterEase(eventData, interruptingEaseId);
23352		}, options);
23353		return this;
23354	}
23355	_prepareEase(eventData, noMoveStart, currently = {}) {
23356		this._moving = true;
23357		if (!noMoveStart && !currently.moving) this.fire(new MapMovementEvent("movestart", eventData));
23358		if (this._zooming && !currently.zooming) this.fire(new MapMovementEvent("zoomstart", eventData));
23359		if (this._rotating && !currently.rotating) this.fire(new MapMovementEvent("rotatestart", eventData));
23360		if (this._pitching && !currently.pitching) this.fire(new MapMovementEvent("pitchstart", eventData));
23361		if (this._rolling && !currently.rolling) this.fire(new MapMovementEvent("rollstart", eventData));
23362	}
23363	_prepareElevation(center) {
23364		this._elevationCenter = center;
23365		this._elevationStart = this.transform.elevation;
23366		this._elevationTarget = this.terrain.getElevationForLngLat(center, this.transform);
23367		this.elevationFreeze = true;
23368	}
23369	_updateElevation(k) {
23370		if (this._elevationStart === void 0 || this._elevationCenter === void 0) this._prepareElevation(this.transform.center);
23371		this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._elevationCenter, this.transform.tileZoom));
23372		const elevation = this.terrain.getElevationForLngLat(this._elevationCenter, this.transform);
23373		if (k < 1 && elevation !== this._elevationTarget) {
23374			const pitch1 = this._elevationTarget - this._elevationStart;
23375			const pitch2 = (elevation - (pitch1 * k + this._elevationStart)) / (1 - k);
23376			this._elevationStart += k * (pitch1 - pitch2);
23377			this._elevationTarget = elevation;
23378		}
23379		this.transform.setElevation(interpolateFactory.number(this._elevationStart, this._elevationTarget, k));
23380	}
23381	_finalizeElevation() {
23382		this.elevationFreeze = false;
23383		if (this.getCenterClampedToGround()) this.transform.recalculateZoomAndCenter(this.terrain);
23384	}
23385	/**
23386	* @internal
23387	* Called when the camera is about to be manipulated.
23388	* If `transformCameraUpdate` is specified or terrain is enabled, a copy of
23389	* the current transform is created to track the accumulated changes.
23390	* This underlying transform represents the "desired state" proposed by input handlers / animations / UI controls.
23391	* It may differ from the state used for rendering (`this.transform`).
23392	* @returns Transform to apply changes to
23393	*/
23394	getTransformForUpdate() {
23395		if (!this.transformCameraUpdate && !this.terrain) return this.transform;
23396		this._requestedCameraState ||= this.transform.clone();
23397		return this._requestedCameraState;
23398	}
23399	/**
23400	* @internal
23401	* Checks the given transform for the camera being below terrain surface and
23402	* returns new pitch and zoom to fix that.
23403	*
23404	* With the new pitch and zoom, the camera will be at the same ground
23405	* position but at higher altitude. It will still point to the same spot on
23406	* the map.
23407	*
23408	* @param tr - The transform to check.
23409	*/
23410	_elevateCameraIfInsideTerrain(tr) {
23411		if (!this.terrain && tr.elevation >= 0 && tr.pitch <= 90) return {};
23412		const cameraLngLat = tr.getCameraLngLat();
23413		const cameraAltitude = tr.getCameraAltitude();
23414		const minAltitude = this.terrain ? this.terrain.getElevationForLngLatZoom(cameraLngLat, tr.zoom) : 0;
23415		if (cameraAltitude < minAltitude) {
23416			const newCamera = tr.calculateCameraOptionsFromTo(cameraLngLat, minAltitude, tr.center, tr.elevation);
23417			return {
23418				pitch: newCamera.pitch,
23419				zoom: newCamera.zoom
23420			};
23421		}
23422		return {};
23423	}
23424	/**
23425	* @internal
23426	* Called after the camera is done being manipulated.
23427	* @param tr - the requested camera end state
23428	* If the camera is inside terrain, it gets elevated.
23429	* Call `transformCameraUpdate` if present, and then apply the "approved" changes.
23430	*/
23431	applyUpdatedTransform(tr) {
23432		const modifiers = [];
23433		modifiers.push((tr) => this._elevateCameraIfInsideTerrain(tr));
23434		if (this.transformCameraUpdate) modifiers.push((tr) => this.transformCameraUpdate(tr));
23435		if (!modifiers.length) return;
23436		const finalTransform = tr.clone();
23437		for (const modifier of modifiers) {
23438			const nextTransform = finalTransform.clone();
23439			const { center, zoom, roll, pitch, bearing, elevation } = modifier(nextTransform);
23440			if (center) nextTransform.setCenter(center);
23441			if (elevation !== void 0) nextTransform.setElevation(elevation);
23442			if (zoom !== void 0) nextTransform.setZoom(zoom);
23443			if (roll !== void 0) nextTransform.setRoll(roll);
23444			if (pitch !== void 0) nextTransform.setPitch(pitch);
23445			if (bearing !== void 0) nextTransform.setBearing(bearing);
23446			finalTransform.apply(nextTransform, false);
23447		}
23448		this.transform.apply(finalTransform, false);
23449	}
23450	_fireMoveEvents(eventData) {
23451		this.fire(new MapMovementEvent("move", eventData));
23452		if (this._zooming) this.fire(new MapMovementEvent("zoom", eventData));
23453		if (this._rotating) this.fire(new MapMovementEvent("rotate", eventData));
23454		if (this._pitching) this.fire(new MapMovementEvent("pitch", eventData));
23455		if (this._rolling) this.fire(new MapMovementEvent("roll", eventData));
23456	}
23457	_afterEase(eventData, easeId) {
23458		if (this._easeId && easeId && this._easeId === easeId) return;
23459		delete this._easeId;
23460		const wasZooming = this._zooming;
23461		const wasRotating = this._rotating;
23462		const wasPitching = this._pitching;
23463		const wasRolling = this._rolling;
23464		this._moving = false;
23465		this._zooming = false;
23466		this._rotating = false;
23467		this._pitching = false;
23468		this._rolling = false;
23469		this._padding = false;
23470		if (wasZooming) this.fire(new MapMovementEvent("zoomend", eventData));
23471		if (wasRotating) this.fire(new MapMovementEvent("rotateend", eventData));
23472		if (wasPitching) this.fire(new MapMovementEvent("pitchend", eventData));
23473		if (wasRolling) this.fire(new MapMovementEvent("rollend", eventData));
23474		this.fire(new MapMovementEvent("moveend", eventData));
23475	}
23476	flyTo(options, eventData) {
23477		if (!options.essential && browser.prefersReducedMotion) {
23478			const coercedOptions = pick(options, [
23479				"center",
23480				"zoom",
23481				"bearing",
23482				"pitch",
23483				"roll",
23484				"elevation",
23485				"padding"
23486			]);
23487			return this.jumpTo(coercedOptions, eventData);
23488		}
23489		this.stop();
23490		options = extend({
23491			offset: [0, 0],
23492			speed: 1.2,
23493			curve: 1.42,
23494			easing: defaultEasing
23495		}, options);
23496		if (options.zoom !== void 0 && this._zoomSnap) options.zoom = evaluateZoomSnap(options.zoom, this._zoomSnap);
23497		const tr = this.getTransformForUpdate(), startBearing = tr.bearing, startPitch = tr.pitch, startRoll = tr.roll, startPadding = tr.padding;
23498		const bearing = options.bearing !== void 0 ? this._normalizeBearing(options.bearing, startBearing) : startBearing;
23499		const pitch = options.pitch !== void 0 ? +options.pitch : startPitch;
23500		const roll = options.roll !== void 0 ? this._normalizeBearing(options.roll, startRoll) : startRoll;
23501		const padding = options.padding !== void 0 ? options.padding : tr.padding;
23502		const offsetAsPoint = Point.convert(options.offset);
23503		let pointAtOffset = tr.centerPoint.add(offsetAsPoint);
23504		const locationAtOffset = tr.screenPointToLocation(pointAtOffset);
23505		const flyToHandler = this.cameraHelper.handleFlyTo(tr, {
23506			bearing,
23507			pitch,
23508			roll,
23509			padding,
23510			locationAtOffset,
23511			offsetAsPoint,
23512			center: options.center,
23513			minZoom: options.minZoom,
23514			zoom: options.zoom
23515		});
23516		let rho = options.curve;
23517		const w0 = Math.max(tr.width, tr.height);
23518		const w1 = w0 / flyToHandler.scaleOfZoom;
23519		const u1 = flyToHandler.pixelPathLength;
23520		const wMax = w0 / flyToHandler.scaleOfMinZoom;
23521		rho = Math.min(rho, Math.sqrt(wMax / u1 * 2));
23522		const rho2 = rho * rho;
23523		/**
23524		* ráµ¢: Returns the zoom-out factor at one end of the animation.
23525		*
23526		* @param descent - `true` for the descent, `false` for the ascent
23527		*/
23528		function zoomOutFactor(descent) {
23529			const b = (w1 * w1 - w0 * w0 + (descent ? -1 : 1) * rho2 * rho2 * u1 * u1) / (2 * (descent ? w1 : w0) * rho2 * u1);
23530			return Math.log(Math.sqrt(b * b + 1) - b);
23531		}
23532		function sinh(n) {
23533			return (Math.exp(n) - Math.exp(-n)) / 2;
23534		}
23535		function cosh(n) {
23536			return (Math.exp(n) + Math.exp(-n)) / 2;
23537		}
23538		function tanh(n) {
23539			return sinh(n) / cosh(n);
23540		}
23541		const r0 = zoomOutFactor(false);
23542		let w = function(s) {
23543			return cosh(r0) / cosh(r0 + rho * s);
23544		};
23545		let u = function(s) {
23546			return w0 * ((cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2) / u1;
23547		};
23548		let S = (zoomOutFactor(true) - r0) / rho;
23549		if (Math.abs(u1) < 2e-6 || !isFinite(S)) {
23550			if (Math.abs(w0 - w1) < 1e-6) return this.easeTo(options, eventData);
23551			const k = w1 < w0 ? -1 : 1;
23552			S = Math.abs(Math.log(w1 / w0)) / rho;
23553			u = () => 0;
23554			w = (s) => Math.exp(k * rho * s);
23555		}
23556		if (options.duration !== void 0) options.duration = +options.duration;
23557		else {
23558			const V = options.screenSpeed !== void 0 ? +options.screenSpeed / rho : +options.speed;
23559			options.duration = 1e3 * S / V;
23560		}
23561		if (options.maxDuration && options.duration > options.maxDuration) options.duration = 0;
23562		this._zooming = true;
23563		this._rotating = startBearing !== bearing;
23564		this._pitching = pitch !== startPitch;
23565		this._rolling = roll !== startRoll;
23566		this._padding = !tr.isPaddingEqual(padding);
23567		this._prepareEase(eventData, false);
23568		if (this.terrain) this._prepareElevation(flyToHandler.targetCenter);
23569		this._ease((k) => {
23570			const s = k * S;
23571			const scale = 1 / w(s);
23572			const centerFactor = u(s);
23573			if (this._rotating) tr.setBearing(interpolateFactory.number(startBearing, bearing, k));
23574			if (this._pitching) tr.setPitch(interpolateFactory.number(startPitch, pitch, k));
23575			if (this._rolling) tr.setRoll(interpolateFactory.number(startRoll, roll, k));
23576			if (this._padding) {
23577				tr.interpolatePadding(startPadding, padding, k);
23578				pointAtOffset = tr.centerPoint.add(offsetAsPoint);
23579			}
23580			flyToHandler.easeFunc(k, scale, centerFactor, pointAtOffset);
23581			if (this.terrain && !options.freezeElevation) this._updateElevation(k);
23582			this.applyUpdatedTransform(tr);
23583			this._fireMoveEvents(eventData);
23584		}, () => {
23585			if (this.terrain && options.freezeElevation) this._finalizeElevation()
vendor: 65,568 bytes, lines 23585-25308
23585;
23586			this._afterEase(eventData);
23587		}, options);
23588		return this;
23589	}
23590	isEasing() {
23591		return !!this._easeFrameId;
23592	}
23593	stop(allowGestures) {
23594		return this._stop(allowGestures);
23595	}
23596	_stop(allowGestures, easeId) {
23597		if (this._easeFrameId) {
23598			this._cancelRenderFrame(this._easeFrameId);
23599			delete this._easeFrameId;
23600			delete this._onEaseFrame;
23601		}
23602		if (this._onEaseEnd) {
23603			const onEaseEnd = this._onEaseEnd;
23604			delete this._onEaseEnd;
23605			onEaseEnd.call(this, easeId);
23606		}
23607		if (!allowGestures) this._stopHandlers();
23608		return this;
23609	}
23610	_ease(frame, finish, options) {
23611		if (options.animate === false || options.duration === 0) {
23612			frame(1);
23613			finish();
23614		} else {
23615			this._easeStart = now();
23616			this._easeOptions = options;
23617			this._onEaseFrame = frame;
23618			this._onEaseEnd = finish;
23619			this._easeFrameId = this._requestRenderFrame(this._renderFrameCallback);
23620		}
23621	}
23622	_normalizeBearing(bearing, currentBearing) {
23623		bearing = wrap(bearing, -180, 180);
23624		const diff = Math.abs(bearing - currentBearing);
23625		if (Math.abs(bearing - 360 - currentBearing) < diff) bearing -= 360;
23626		if (Math.abs(bearing + 360 - currentBearing) < diff) bearing += 360;
23627		return bearing;
23628	}
23629	isMoving() {
23630		return this._moving;
23631	}
23632	isZooming() {
23633		return this._zooming;
23634	}
23635	isRotating() {
23636		return this._rotating;
23637	}
23638};
23639//#endregion
23640//#region src/ui/control/attribution_control.ts
23641const defaultAttributionControlOptions = {
23642	compact: true,
23643	customAttribution: "<a href=\"https://maplibre.org/\" target=\"_blank\">MapLibre</a>"
23644};
23645/**
23646* An `AttributionControl` control presents the map's attribution information. By default, the attribution control is expanded (regardless of map width).
23647* @group Markers and Controls
23648* @example
23649* ```ts
23650* let map = new Map({attributionControl: false})
23651*     .addControl(new AttributionControl({
23652*         compact: true
23653*     }));
23654* ```
23655* @see [Change the default position for attribution](https://maplibre.org/maplibre-gl-js/docs/examples/change-the-default-position-for-attribution/)
23656*/
23657var AttributionControl = class {
23658	/**
23659	* @param options - the attribution options
23660	*/
23661	constructor(options = defaultAttributionControlOptions) {
23662		this._toggleAttribution = () => {
23663			if (this._container.classList.contains("maplibregl-compact")) {
23664				if (this._container.classList.contains("maplibregl-compact-show")) {
23665					this._container.setAttribute("open", "");
23666					this._container.classList.remove("maplibregl-compact-show");
23667				} else {
23668					this._container.classList.add("maplibregl-compact-show");
23669					this._container.removeAttribute("open");
23670				}
23671			}
23672		};
23673		this._updateData = (e) => {
23674			if (e && (e.type === "terrain" || e.dataType === "style" || e.dataType === "source" && (e.sourceDataType === "metadata" || e.sourceDataType === "visibility"))) this._updateAttributions();
23675		};
23676		this._updateCompact = () => {
23677			if (this._map.getCanvasContainer().offsetWidth <= 640 || this._compact) {
23678				if (this._compact === false) this._container.setAttribute("open", "");
23679				else if (!this._container.classList.contains("maplibregl-compact") && !this._container.classList.contains("maplibregl-attrib-empty")) {
23680					this._container.setAttribute("open", "");
23681					this._container.classList.add("maplibregl-compact", "maplibregl-compact-show");
23682				}
23683			} else {
23684				this._container.setAttribute("open", "");
23685				if (this._container.classList.contains("maplibregl-compact")) this._container.classList.remove("maplibregl-compact", "maplibregl-compact-show");
23686			}
23687		};
23688		this._updateCompactMinimize = () => {
23689			if (this._container.classList.contains("maplibregl-compact")) {
23690				if (this._container.classList.contains("maplibregl-compact-show")) this._container.classList.remove("maplibregl-compact-show");
23691			}
23692		};
23693		this.options = options;
23694	}
23695	getDefaultPosition() {
23696		return "bottom-right";
23697	}
23698	/** {@inheritDoc IControl.onAdd} */
23699	onAdd(map) {
23700		this._map = map;
23701		this._compact = this.options.compact;
23702		this._container = DOM.create("details", "maplibregl-ctrl maplibregl-ctrl-attrib");
23703		this._compactButton = DOM.create("summary", "maplibregl-ctrl-attrib-button", this._container);
23704		this._compactButton.addEventListener("click", this._toggleAttribution);
23705		this._setElementTitle(this._compactButton, "ToggleAttribution");
23706		this._innerContainer = DOM.create("div", "maplibregl-ctrl-attrib-inner", this._container);
23707		this._updateAttributions();
23708		this._updateCompact();
23709		this._map.on("styledata", this._updateData);
23710		this._map.on("sourcedata", this._updateData);
23711		this._map.on("terrain", this._updateData);
23712		this._map.on("resize", this._updateCompact);
23713		this._map.on("drag", this._updateCompactMinimize);
23714		return this._container;
23715	}
23716	/** {@inheritDoc IControl.onRemove} */
23717	onRemove() {
23718		this._container.remove();
23719		this._map.off("styledata", this._updateData);
23720		this._map.off("sourcedata", this._updateData);
23721		this._map.off("terrain", this._updateData);
23722		this._map.off("resize", this._updateCompact);
23723		this._map.off("drag", this._updateCompactMinimize);
23724		this._map = void 0;
23725		this._compact = void 0;
23726		this._attribHTML = void 0;
23727	}
23728	_setElementTitle(element, title) {
23729		const str = this._map._getUIString(`AttributionControl.${title}`);
23730		element.title = str;
23731		element.setAttribute("aria-label", str);
23732	}
23733	_updateAttributions() {
23734		if (!this._map.style) return;
23735		let attributions = [];
23736		if (this.options.customAttribution) {
23737			if (Array.isArray(this.options.customAttribution)) attributions = attributions.concat(this.options.customAttribution.map((attribution) => {
23738				if (typeof attribution !== "string") return "";
23739				return attribution;
23740			}));
23741			else if (typeof this.options.customAttribution === "string") attributions.push(this.options.customAttribution);
23742		}
23743		if (this._map.style.stylesheet) {
23744			const stylesheet = this._map.style.stylesheet;
23745			this.styleOwner = stylesheet.owner;
23746			this.styleId = stylesheet.id;
23747		}
23748		const tileManagers = this._map.style.tileManagers;
23749		for (const id in tileManagers) {
23750			const tileManager = tileManagers[id];
23751			if (tileManager.used || tileManager.usedForTerrain) {
23752				const source = tileManager.getSource();
23753				if (source.attribution && !attributions.includes(source.attribution)) attributions.push(source.attribution);
23754			}
23755		}
23756		attributions = attributions.filter((e) => String(e).trim());
23757		attributions.sort((a, b) => a.length - b.length);
23758		attributions = attributions.filter((attrib, i) => {
23759			for (let j = i + 1; j < attributions.length; j++) if (attributions[j].includes(attrib)) return false;
23760			return true;
23761		});
23762		const attribHTML = attributions.join(" | ");
23763		if (attribHTML === this._attribHTML) return;
23764		this._attribHTML = attribHTML;
23765		if (attributions.length) {
23766			this._innerContainer.replaceChildren(DOM.sanitize(attribHTML));
23767			this._container.classList.remove("maplibregl-attrib-empty");
23768		} else this._container.classList.add("maplibregl-attrib-empty");
23769		this._updateCompact();
23770		this._editLink = null;
23771	}
23772};
23773//#endregion
23774//#region src/ui/control/logo_control.ts
23775/**
23776* A `LogoControl` is a control that adds the watermark.
23777*
23778* @group Markers and Controls
23779*
23780* @example
23781* ```ts
23782* map.addControl(new LogoControl({compact: false}));
23783* ```
23784**/
23785var LogoControl = class {
23786	/**
23787	* @param options - the control's options
23788	*/
23789	constructor(options = {}) {
23790		this._updateCompact = () => {
23791			const containerChildren = this._container.children;
23792			if (containerChildren.length) {
23793				const anchor = containerChildren[0];
23794				if (this._map.getCanvasContainer().offsetWidth <= 640 || this._compact) {
23795					if (this._compact !== false) anchor.classList.add("maplibregl-compact");
23796				} else anchor.classList.remove("maplibregl-compact");
23797			}
23798		};
23799		this.options = options;
23800	}
23801	getDefaultPosition() {
23802		return "bottom-left";
23803	}
23804	/** {@inheritDoc IControl.onAdd} */
23805	onAdd(map) {
23806		this._map = map;
23807		this._compact = this.options?.compact;
23808		this._container = DOM.create("div", "maplibregl-ctrl");
23809		const anchor = DOM.create("a", "maplibregl-ctrl-logo");
23810		anchor.target = "_blank";
23811		anchor.rel = "noopener nofollow";
23812		anchor.href = "https://maplibre.org/";
23813		anchor.setAttribute("aria-label", this._map._getUIString("LogoControl.Title"));
23814		anchor.setAttribute("rel", "noopener nofollow");
23815		this._container.appendChild(anchor);
23816		this._container.style.display = "block";
23817		this._map.on("resize", this._updateCompact);
23818		this._updateCompact();
23819		return this._container;
23820	}
23821	/** {@inheritDoc IControl.onRemove} */
23822	onRemove() {
23823		this._container.remove();
23824		this._map.off("resize", this._updateCompact);
23825		this._map = void 0;
23826		this._compact = void 0;
23827	}
23828};
23829//#endregion
23830//#region src/util/task_queue.ts
23831var TaskQueue = class {
23832	constructor() {
23833		this._queue = [];
23834		this._id = 0;
23835		this._cleared = false;
23836		this._currentlyRunning = false;
23837	}
23838	add(callback) {
23839		const id = ++this._id;
23840		this._queue.push({
23841			callback,
23842			id,
23843			cancelled: false
23844		});
23845		return id;
23846	}
23847	remove(id) {
23848		const running = this._currentlyRunning;
23849		const queue = running ? this._queue.concat(running) : this._queue;
23850		for (const task of queue) if (task.id === id) {
23851			task.cancelled = true;
23852			return;
23853		}
23854	}
23855	run(timeStamp = 0) {
23856		if (this._currentlyRunning) throw new Error("Attempting to run(), but is already running.");
23857		const queue = this._currentlyRunning = this._queue;
23858		this._queue = [];
23859		let failed = false;
23860		let firstError;
23861		for (const task of queue) {
23862			if (task.cancelled) continue;
23863			try {
23864				task.callback(timeStamp);
23865			} catch (error) {
23866				if (!failed) {
23867					failed = true;
23868					firstError = error;
23869				}
23870			}
23871			if (this._cleared) break;
23872		}
23873		this._cleared = false;
23874		this._currentlyRunning = false;
23875		if (failed) throw firstError;
23876	}
23877	clear() {
23878		if (this._currentlyRunning) this._cleared = true;
23879		this._queue = [];
23880	}
23881};
23882//#endregion
23883//#region src/webgl/rtt_fingerprint.ts
23884/**
23885* What a render-to-texture tile's textures differ in from the state this frame would render them from,
23886* least to most severe: `zoom` (the same layers and source tiles, evaluated at another zoom), `sourceTiles`
23887* (other source tiles under the terrain tile), `visibleLayers` (a layer entered or left its zoom range) and
23888* `revision` (the source data changed, or the textures were rendered without this source).
23889*
23890* @internal
23891*/
23892const RTT_DIFFERENCES = [
23893	"none",
23894	"zoom",
23895	"sourceTiles",
23896	"visibleLayers",
23897	"revision"
23898];
23899/**
23900* Immutable value describing the state a render-to-texture tile's textures were rendered from: the source
23901* tiles drawn into them, the source data revision, the map zoom (zoom-dependent style properties are
23902* evaluated then) and the ids of the layers visible at that zoom.
23903*
23904* @internal
23905*/
23906var RTTFingerprint = class {
23907	constructor(coords, revision, zoom, visibleLayerIds) {
23908		this._tileKeys = coords.map((c) => c.key).sort().join();
23909		this._revision = revision;
23910		this._zoom = zoom;
23911		this._visibleLayerIds = visibleLayerIds;
23912	}
23913	/**
23914	* The most severe difference between this fingerprint and the one the textures were rendered from,
23915	* `none` when they match. A missing fingerprint counts as a revision difference.
23916	*/
23917	difference(other) {
23918		if (this._revision !== other?._revision) return "revision";
23919		if (this._visibleLayerIds !== other._visibleLayerIds) return "visibleLayers";
23920		if (this._tileKeys !== other._tileKeys) return "sourceTiles";
23921		return this._zoom === other._zoom ? "none" : "zoom";
23922	}
23923};
23924//#endregion
23925//#region src/webgl/render_to_texture.ts
23926/**
23927* lookup table which layers should rendered to texture
23928*/
23929const LAYERS_TO_TEXTURES = {
23930	background: true,
23931	fill: true,
23932	line: true,
23933	raster: true,
23934	hillshade: true,
23935	"color-relief": true
23936};
23937/**
23938* @internal
23939* Renders RTT-eligible layers into per-tile cached textures, then drapes
23940* them onto the terrain mesh. Slots live on each Tile so their lifetime
23941* tracks the tile itself; the underlying FBO+texture handles are recycled
23942* via the painter's RTT pool.
23943*/
23944var RenderToTexture = class {
23945	constructor(painter, terrain) {
23946		this.needsFollowUpFrame = false;
23947		this.painter = painter;
23948		this.terrain = terrain;
23949		this.rttSize = terrain.tileManager.tileSize * terrain.qualityFactor;
23950	}
23951	getTexture(tile) {
23952		return tile.getRTT(this._stacks.length - 1).texture;
23953	}
23954	/**
23955	* Collects the frame's tiles, layers and source fingerprints and releases the textures that need
23956	* re-rendering. Textures that differ only by zoom are kept while the zoom is changing or the map is moving
23957	* and re-rendered one tile per frame once it has stopped, nearest to the camera first; textures with other
23958	* source tiles under them are re-rendered one per frame too, `needsFollowUpFrame` bringing in the rest.
23959	* Textures whose visible layer set changed (a layer entered or left its zoom range) are kept while the zoom
23960	* is changing and then all re-rendered in the same frame, since a tile-by-tile change would show; a source
23961	* data change re-renders immediately.
23962	*/
23963	prepareForRender(style, zoom) {
23964		const zoomChanged = zoom !== this._lastPrepareZoom;
23965		this._lastPrepareZoom = zoom;
23966		this._stacks = [];
23967		this._prevType = null;
23968		this._rttTiles = [];
23969		this._renderableTiles = this.terrain.tileManager.getRenderableTiles();
23970		this._renderableLayerIds = style._order.filter((id) => !style._layers[id].isHidden(zoom));
23971		const visibleLayerIds = this._renderableLayerIds.join();
23972		const rttSourceIds = /* @__PURE__ */ new Set();
23973		for (const layerId of this._renderableLayerIds) {
23974			const layer = style._layers[layerId];
23975			const source = layer.source;
23976			if (source && LAYERS_TO_TEXTURES[layer.type]) rttSourceIds.add(source);
23977		}
23978		this._coordsAscending = {};
23979		this._rttFingerprints = {};
23980		for (const sourceId of rttSourceIds) {
23981			const tileManager = style.tileManagers[sourceId];
23982			if (!tileManager) continue;
23983			this._coordsAscending[sourceId] = {};
23984			const coordsAscending = this._coordsAscending[sourceId];
23985			const source = tileManager.getSource();
23986			const terrainTileRanges = source instanceof ImageSource ? source.terrainTileRanges : null;
23987			for (const tileID of tileManager.getVisibleCoordinates()) {
23988				const keys = this.terrain.tileManager.getTerrainCoords(tileID, terrainTileRanges);
23989				for (const key in keys) {
23990					coordsAscending[key] ||= [];
23991					coordsAscending[key].push(keys[key]);
23992				}
23993			}
23994			this._rttFingerprints[sourceId] = {};
23995			const fingerprints = this._rttFingerprints[sourceId];
23996			const revision = tileManager.getState().revision;
23997			for (const key in coordsAscending) fingerprints[key] = new RTTFingerprint(coordsAscending[key], revision, zoom, visibleLayerIds);
23998		}
23999		this.needsFollowUpFrame = false;
24000		const moving = zoomChanged || this.painter.options.moving;
24001		let staleTileReleased = false;
24002		for (const tile of this._renderableTiles) {
24003			const difference = this._textureDifference(tile);
24004			if (difference === "none") continue;
24005			if (difference === "zoom" && moving || difference === "visibleLayers" && zoomChanged) this.needsFollowUpFrame = true;
24006			else if (difference === "zoom" || difference === "sourceTiles") {
24007				if (staleTileReleased) this.needsFollowUpFrame = true;
24008				else tile.releaseRTT(this.painter);
24009				staleTileReleased = true;
24010			} else tile.releaseRTT(this.painter);
24011		}
24012	}
24013	/**
24014	* The most severe difference, over the sources rendered to texture, between the tile's cached textures
24015	* and what this frame would render into them.
24016	*/
24017	_textureDifference(tile) {
24018		let worst = "none";
24019		for (const source in this._rttFingerprints) {
24020			const frameFingerprint = this._rttFingerprints[source][tile.tileID.key];
24021			if (!frameFingerprint) continue;
24022			const difference = frameFingerprint.difference(tile.rttFingerprint[source]);
24023			if (RTT_DIFFERENCES.indexOf(difference) > RTT_DIFFERENCES.indexOf(worst)) worst = difference;
24024		}
24025		return worst;
24026	}
24027	/**
24028	* due that switching textures is relatively slow, the render
24029	* layer-by-layer context is not practicable. To bypass this problem
24030	* this lines of code stack all layers and later render all at once.
24031	* Because of the stylesheet possibility to mixing render-to-texture layers
24032	* and 'live'-layers (f.e. symbols) it is necessary to create more stacks. For example
24033	* a symbol-layer is in between of fill-layers.
24034	* @param layer - the layer to render
24035	* @param renderContext - shared state for the current render
24036	* @returns if true layer is rendered to texture, otherwise false
24037	*/
24038	renderLayer(layer, renderContext) {
24039		if (layer.isHidden(this.painter.transform.zoom)) return false;
24040		const type = layer.type;
24041		const painter = this.painter;
24042		const isLastLayer = this._renderableLayerIds[this._renderableLayerIds.length - 1] === layer.id;
24043		if (LAYERS_TO_TEXTURES[type]) {
24044			if (!this._prevType || !LAYERS_TO_TEXTURES[this._prevType]) this._stacks.push([]);
24045			this._prevType = type;
24046			this._stacks[this._stacks.length - 1].push(layer.id);
24047			if (!isLastLayer) return true;
24048		}
24049		if (LAYERS_TO_TEXTURES[this._prevType] || LAYERS_TO_TEXTURES[type] && isLastLayer) {
24050			this._prevType = type;
24051			const stack = this._stacks.length - 1, layers = this._stacks[stack] || [];
24052			renderContext.isRenderingToTexture = true;
24053			for (const tile of this._renderableTiles) {
24054				this._rttTiles.push(tile);
24055				if (tile.getRTT(stack)) continue;
24056				const obj = tile.acquireRTT(painter, stack, this.rttSize);
24057				painter.bindRTT(obj);
24058				painter.context.clear({
24059					color: Color.transparent,
24060					stencil: 0
24061				});
24062				painter.currentStencilSource = void 0;
24063				for (const layerId of layers) {
24064					const layer = painter.style._layers[layerId];
24065					const coords = layer.source ? this._coordsAscending[layer.source][tile.tileID.key] : [tile.tileID];
24066					painter.context.viewport.set([
24067						0,
24068						0,
24069						this.rttSize,
24070						this.rttSize
24071					]);
24072					painter.renderTileClippingMasks(layer, coords);
24073					painter.renderLayer(painter, painter.style.tileManagers[layer.source], layer, coords, renderContext);
24074					if (layer.source) tile.rttFingerprint[layer.source] = this._rttFingerprints[layer.source][tile.tileID.key];
24075				}
24076				obj.texture.generateMipmap();
24077			}
24078			renderContext.isRenderingToTexture = false;
24079			drawTerrain(this.painter, this.terrain, this._rttTiles, renderContext);
24080			this._rttTiles = [];
24081			return LAYERS_TO_TEXTURES[type];
24082		}
24083		return false;
24084	}
24085};
24086//#endregion
24087//#region src/ui/default_locale.ts
24088const defaultLocale = {
24089	"AttributionControl.ToggleAttribution": "Toggle attribution",
24090	"AttributionControl.MapFeedback": "Map feedback",
24091	"FullscreenControl.Enter": "Enter fullscreen",
24092	"FullscreenControl.Exit": "Exit fullscreen",
24093	"GeolocateControl.FindMyLocation": "Find my location",
24094	"GeolocateControl.LocationNotAvailable": "Location not available",
24095	"LogoControl.Title": "MapLibre logo",
24096	"Map.Title": "Map",
24097	"Marker.Title": "Map marker",
24098	"NavigationControl.ResetBearing": "Drag to rotate map, click to reset north",
24099	"NavigationControl.ZoomIn": "Zoom in",
24100	"NavigationControl.ZoomOut": "Zoom out",
24101	"Popup.Close": "Close popup",
24102	"ScaleControl.Feet": "ft",
24103	"ScaleControl.Meters": "m",
24104	"ScaleControl.Kilometers": "km",
24105	"ScaleControl.Miles": "mi",
24106	"ScaleControl.NauticalMiles": "nm",
24107	"GlobeControl.Enable": "Enable globe",
24108	"GlobeControl.Disable": "Disable globe",
24109	"TerrainControl.Enable": "Enable terrain",
24110	"TerrainControl.Disable": "Disable terrain",
24111	"CooperativeGesturesHandler.WindowsHelpText": "Use Ctrl + scroll to zoom the map",
24112	"CooperativeGesturesHandler.MacHelpText": "Use ⌘ + scroll to zoom the map",
24113	"CooperativeGesturesHandler.MobileHelpText": "Use two fingers to move the map"
24114};
24115//#endregion
24116//#region src/ui/map.ts
24117const version$1 = version$2;
24118const defaultMinZoom = -2;
24119const defaultMaxZoom = 22;
24120const defaultMinPitch = 0;
24121const defaultMaxPitch = 60;
24122const defaultAnisotropicFilterPitch = 20;
24123const maxPitchThreshold = 180;
24124const defaultOptions$4 = {
24125	hash: false,
24126	interactive: true,
24127	bearingSnap: 7,
24128	zoomSnap: 0,
24129	attributionControl: defaultAttributionControlOptions,
24130	maplibreLogo: false,
24131	refreshExpiredTiles: true,
24132	canvasContextAttributes: {
24133		antialias: false,
24134		preserveDrawingBuffer: false,
24135		powerPreference: "high-performance",
24136		failIfMajorPerformanceCaveat: false,
24137		desynchronized: false,
24138		contextType: void 0
24139	},
24140	scrollZoom: true,
24141	minZoom: defaultMinZoom,
24142	maxZoom: defaultMaxZoom,
24143	minPitch: defaultMinPitch,
24144	maxPitch: defaultMaxPitch,
24145	boxZoom: true,
24146	dragRotate: true,
24147	dragPan: true,
24148	keyboard: true,
24149	doubleClickZoom: true,
24150	touchZoomRotate: true,
24151	touchPitch: true,
24152	cooperativeGestures: false,
24153	trackResize: true,
24154	center: [0, 0],
24155	elevation: 0,
24156	zoom: 0,
24157	bearing: 0,
24158	pitch: 0,
24159	roll: 0,
24160	renderWorldCopies: true,
24161	maxTileCacheSize: null,
24162	maxTileCacheZoomLevels: config.MAX_TILE_CACHE_ZOOM_LEVELS,
24163	transformRequest: null,
24164	transformCameraUpdate: null,
24165	transformConstrain: null,
24166	fadeDuration: 300,
24167	crossSourceCollisions: true,
24168	clickTolerance: 3,
24169	localIdeographFontFamily: "sans-serif",
24170	pitchWithRotate: true,
24171	rollEnabled: false,
24172	rotateSpeed: .8,
24173	pitchSpeed: -.5,
24174	reduceMotion: void 0,
24175	validateStyle: true,
24176	/**Because GL MAX_TEXTURE_SIZE is usually at least 4096px. */
24177	maxCanvasSize: [4096, 4096],
24178	cancelPendingTileRequestsWhileZooming: true,
24179	centerClampedToGround: true,
24180	terrainSkirtLength: "auto",
24181	zoomLevelsToOverscale: 4,
24182	anisotropicFilterPitch: defaultAnisotropicFilterPitch
24183};
24184/**
24185* The `Map` object represents the map on your page. It exposes methods
24186* and properties that enable you to programmatically change the map,
24187* and fires events as users interact with it.
24188*
24189* You create a `Map` by specifying a `container` and other options, see {@link MapOptions} for the full list.
24190* Then MapLibre GL JS initializes the map on the page and returns your `Map` object.
24191*
24192* @group Main
24193*
24194* @example
24195* ```ts
24196* let map = new Map({
24197*   container: 'map',
24198*   center: [-122.420679, 37.772537],
24199*   zoom: 13,
24200*   style: style_object,
24201*   hash: true,
24202*   transformRequest: (url, resourceType)=> {
24203*     if(resourceType === 'Source' && url.startsWith('http://myHost')) {
24204*       return {
24205*        url: url.replace('http', 'https'),
24206*        headers: { 'my-custom-header': true},
24207*        credentials: 'include'  // Include cookies for cross-origin requests
24208*      }
24209*     }
24210*   }
24211* });
24212* ```
24213* @see [Display a map](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-map/)
24214*/
24215var Map$1 = class extends Evented {
24216	/**
24217	* @internal
24218	* The window that owns the map container element, for cross-window support.
24219	* Returns `typeof window` to include global constructors like ResizeObserver.
24220	*/
24221	get _ownerWindow() {
24222		return this._container?.ownerDocument?.defaultView || window;
24223	}
24224	constructor(options) {
24225		super();
24226		this._idleTriggered = false;
24227		this._crossFadingFactor = 1;
24228		this._renderTaskQueue = new TaskQueue();
24229		this._controls = [];
24230		this._mapId = uniqueId();
24231		this._styleUrl = null;
24232		this._missingStyleImageResolver = null;
24233		this._lostContextStyle = {
24234			style: null,
24235			images: null
24236		};
24237		this._contextLost = (event) => {
24238			event.preventDefault();
24239			if (this._frameRequest) {
24240				this._frameRequest.abort();
24241				this._frameRequest = null;
24242			}
24243			this.painter.destroy();
24244			this._lostContextStyle = this._getStyleAndImages();
24245			if (!this.style) {
24246				this.fire(new MapContextEvent("webglcontextlost", { originalEvent: event }));
24247				return;
24248			}
24249			for (const layer of Object.values(this.style._layers)) {
24250				if (layer.type === "custom") console.warn(`Custom layer with id '${layer.id}' cannot be restored after WebGL context loss. You will need to re-add it manually after context restoration.`);
24251				if (layer._listeners) for (const [event] of Object.entries(layer._listeners)) console.warn(`Custom layer with id '${layer.id}' had event listeners for event '${event}' which cannot be restored after WebGL context loss. You will need to re-add them manually after context restoration.`);
24252			}
24253			this.style.destroy();
24254			this.style = null;
24255			this.fire(new MapContextEvent("webglcontextlost", { originalEvent: event }));
24256		};
24257		this._contextRestored = (event) => {
24258			if (this._lostContextStyle.style) this.setStyle(this._lostContextStyle.style, { diff: false });
24259			if (this._lostContextStyle.images && this.style) {
24260				this.style.imageManager.images = this._lostContextStyle.images;
24261				for (const id in this._lostContextStyle.images) {
24262					const image = this._lostContextStyle.images[id];
24263					if (image.isWebGLImage) this.style.imageManager.updateImage(id, image, false);
24264				}
24265			}
24266			this._lostContextStyle = {
24267				style: null,
24268				images: null
24269			};
24270			try {
24271				this._setupPainter();
24272			} catch (error) {
24273				this.fire(new ErrorEvent(error));
24274				return;
24275			}
24276			this.resize();
24277			this._update();
24278			this._resizeInternal();
24279			this.fire(new MapContextEvent("webglcontextrestored", { originalEvent: event }));
24280		};
24281		this._onMapScroll = (event) => {
24282			if (event.target !== this._container) return;
24283			this._container.scrollTop = 0;
24284			this._container.scrollLeft = 0;
24285			return false;
24286		};
24287		this._onWindowOnline = () => {
24288			this._update();
24289		};
24290		const resolvedOptions = {
24291			...defaultOptions$4,
24292			...options,
24293			canvasContextAttributes: {
24294				...defaultOptions$4.canvasContextAttributes,
24295				...options.canvasContextAttributes
24296			}
24297		};
24298		if (resolvedOptions.minZoom != null && resolvedOptions.maxZoom != null && resolvedOptions.minZoom > resolvedOptions.maxZoom) throw new Error("maxZoom must be greater than or equal to minZoom");
24299		if (resolvedOptions.minPitch != null && resolvedOptions.maxPitch != null && resolvedOptions.minPitch > resolvedOptions.maxPitch) throw new Error("maxPitch must be greater than or equal to minPitch");
24300		if (resolvedOptions.minPitch != null && resolvedOptions.minPitch < defaultMinPitch) throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`);
24301		if (resolvedOptions.maxPitch != null && resolvedOptions.maxPitch > maxPitchThreshold) throw new Error(`maxPitch must be less than or equal to ${maxPitchThreshold}`);
24302		this._camera = new Camera({
24303			minZoom: resolvedOptions.minZoom,
24304			maxZoom: resolvedOptions.maxZoom,
24305			minPitch: resolvedOptions.minPitch,
24306			maxPitch: resolvedOptions.maxPitch,
24307			bearingSnap: resolvedOptions.bearingSnap,
24308			zoomSnap: resolvedOptions.zoomSnap,
24309			renderWorldCopies: resolvedOptions.renderWorldCopies,
24310			centerClampedToGround: resolvedOptions.centerClampedToGround,
24311			terrain: this.terrain,
24312			transformConstrain: resolvedOptions.transformConstrain,
24313			requestRenderFrame: (callback) => this._requestRenderFrame(callback),
24314			cancelRenderFrame: (id) => this._cancelRenderFrame(id),
24315			transformCameraUpdate: resolvedOptions.transformCameraUpdate,
24316			stopHandlers: () => this._handlers?.stop(false)
24317		});
24318		this._camera.setEventedParent(this);
24319		this._interactive = resolvedOptions.interactive;
24320		this._maxTileCacheSize = resolvedOptions.maxTileCacheSize;
24321		this._maxTileCacheZoomLevels = resolvedOptions.maxTileCacheZoomLevels;
24322		this._canvasContextAttributes = { ...resolvedOptions.canvasContextAttributes };
24323		this._trackResize = resolvedOptions.trackResize === true;
24324		this._terrainSkirtLength = resolvedOptions.terrainSkirtLength;
24325		this._refreshExpiredTiles = resolvedOptions.refreshExpiredTiles === true;
24326		this._fadeDuration = resolvedOptions.fadeDuration;
24327		this._crossSourceCollisions = resolvedOptions.crossSourceCollisions === true;
24328		this._collectResourceTiming = resolvedOptions.collectResourceTiming === true;
24329		this._locale = {
24330			...defaultLocale,
24331			...resolvedOptions.locale
24332		};
24333		this._clickTolerance = resolvedOptions.clickTolerance;
24334		this._overridePixelRatio = resolvedOptions.pixelRatio;
24335		this._maxCanvasSize = resolvedOptions.maxCanvasSize;
24336		this._zoomLevelsToOverscale = resolvedOptions.zoomLevelsToOverscale;
24337		this.cancelPendingTileRequestsWhileZooming = resolvedOptions.cancelPendingTileRequestsWhileZooming === true;
24338		this.setAnisotropicFilterPitch(resolvedOptions.anisotropicFilterPitch);
24339		if (resolvedOptions.reduceMotion !== void 0) browser.prefersReducedMotion = resolvedOptions.reduceMotion;
24340		this._requestManager = new RequestManager(resolvedOptions.transformRequest);
24341		this._container = this._resolveContainer(resolvedOptions.container);
24342		if (resolvedOptions.maxBounds) this.setMaxBounds(resolvedOptions.maxBounds);
24343		this._setupContainer();
24344		try {
24345			this._setupPainter();
24346		} catch (error) {
24347			this._cleanupContainer();
24348			throw error;
24349		}
24350		this._imageQueueHandle = ImageRequest.addThrottleControl(() => this.isMoving());
24351		this.on("move", () => this._update(false));
24352		this.on("moveend", () => this._update(false));
24353		this.on("zoom", () => this._update(true));
24354		this.on("terrain", () => {
24355			this.painter.markTerrainDepthDirty();
24356			this._update(true);
24357		});
24358		this.once("idle", () => this._idleTriggered = true);
24359		this._handlers = new HandlerManager(this, this._camera, resolvedOptions);
24360		if (typeof window !== "undefined") {
24361			this._ownerWindow.addEventListener("online", this._onWindowOnline, false);
24362			this._setupResizeObserver();
24363		}
24364		const hashName = typeof resolvedOptions.hash === "string" && resolvedOptions.hash || void 0;
24365		this._hash = resolvedOptions.hash ? new Hash(hashName).addTo(this) : void 0;
24366		if (!this._hash?._onHashChange()) {
24367			this.jumpTo({
24368				center: resolvedOptions.center,
24369				elevation: resolvedOptions.elevation,
24370				zoom: resolvedOptions.zoom,
24371				bearing: resolvedOptions.bearing,
24372				pitch: resolvedOptions.pitch,
24373				roll: resolvedOptions.roll
24374			});
24375			if (resolvedOptions.bounds) {
24376				this.resize();
24377				this.fitBounds(resolvedOptions.bounds, extend({}, resolvedOptions.fitBoundsOptions, { duration: 0 }));
24378			}
24379		}
24380		const shouldConstrainUsingMercatorTransform = typeof resolvedOptions.style === "string" || !(resolvedOptions.style?.projection?.type === "globe");
24381		this.resize(null, shouldConstrainUsingMercatorTransform);
24382		this._localIdeographFontFamily = resolvedOptions.localIdeographFontFamily;
24383		this._validateStyle = resolvedOptions.validateStyle;
24384		if (resolvedOptions.style) this.setStyle(resolvedOptions.style, { localIdeographFontFamily: resolvedOptions.localIdeographFontFamily });
24385		if (resolvedOptions.attributionControl) this.addControl(new AttributionControl(typeof resolvedOptions.attributionControl === "boolean" ? void 0 : resolvedOptions.attributionControl));
24386		if (resolvedOptions.maplibreLogo) this.addControl(new LogoControl(), resolvedOptions.logoPosition);
24387		this.on("style.load", () => {
24388			if (!shouldConstrainUsingMercatorTransform) this._resizeTransform();
24389			if (this._camera.transform.unmodified) {
24390				const coercedOptions = pick(this.style.stylesheet, [
24391					"center",
24392					"zoom",
24393					"bearing",
24394					"pitch",
24395					"roll"
24396				]);
24397				this.jumpTo(coercedOptions);
24398			}
24399		});
24400		this.on("data", (event) => {
24401			this._update(event.dataType === "style");
24402			this.fire(event.dataType === "style" ? new MapStyleDataEvent("styledata", event) : new MapSourceDataEvent("sourcedata", event));
24403		});
24404		this.on("dataloading", (event) => {
24405			this.fire(event.dataType === "style" ? new MapStyleDataEvent("styledataloading", event) : new MapSourceDataEvent("sourcedataloading", event));
24406		});
24407		this.on("dataabort", (event) => {
24408			this.fire(new MapSourceDataEvent("sourcedataabort", event));
24409		});
24410	}
24411	/**
24412	* @internal
24413	* Returns a unique number for this map instance which is used for the MapLoadEvent
24414	* to make sure we only fire one event per instantiated map object.
24415	* @returns the uniq map ID
24416	*/
24417	_getMapId() {
24418		return this._mapId;
24419	}
24420	/**
24421	* Sets a global state property that can be retrieved with the [`global-state` expression](https://maplibre.org/maplibre-style-spec/expressions/#global-state).
24422	* If the value is null, it resets the property to its default value defined in the [`state` style property](https://maplibre.org/maplibre-style-spec/root/#state).
24423	*
24424	* @param propertyName - The name of the state property to set.
24425	* @param value - The value of the state property to set.
24426	*/
24427	setGlobalStateProperty(propertyName, value) {
24428		this.style.setGlobalStateProperty(propertyName, value);
24429		return this._update(true);
24430	}
24431	/**
24432	* Returns the global map state
24433	*
24434	* @returns The map state object.
24435	*/
24436	getGlobalState() {
24437		return this.style.getGlobalState();
24438	}
24439	/**
24440	* Adds an {@link IControl} to the map, calling `control.onAdd(this)`.
24441	*
24442	* An {@link ErrorEvent} will be fired if the control is invalid.
24443	*
24444	* @param control - The {@link IControl} to add.
24445	* @param position - position on the map to which the control will be added.
24446	* Valid values are `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. Defaults to `'top-right'`.
24447	* @example
24448	* Add zoom and rotation controls to the map.
24449	* ```ts
24450	* map.addControl(new NavigationControl());
24451	* ```
24452	* @see [Display map navigation controls](https://maplibre.org/maplibre-gl-js/docs/examples/display-map-navigation-controls/)
24453	*/
24454	addControl(control, position) {
24455		if (position === void 0) {
24456			if (control.getDefaultPosition) position = control.getDefaultPosition();
24457			else position = "top-right";
24458		}
24459		if (!control?.onAdd) return this.fire(new ErrorEvent(/* @__PURE__ */ new Error("Invalid argument to map.addControl(). Argument must be a control with onAdd and onRemove methods.")));
24460		const controlElement = control.onAdd(this);
24461		this._controls.push(control);
24462		const positionContainer = this._controlPositions[position];
24463		if (position.includes("bottom")) positionContainer.insertBefore(controlElement, positionContainer.firstChild);
24464		else positionContainer.appendChild(controlElement);
24465		return this;
24466	}
24467	/**
24468	* Removes the control from the map.
24469	*
24470	* An {@link ErrorEvent} will be fired if the control is invalid.
24471	*
24472	* @param control - The {@link IControl} to remove.
24473	* @example
24474	* ```ts
24475	* // Define a new navigation control.
24476	* let navigation = new NavigationControl();
24477	* // Add zoom and rotation controls to the map.
24478	* map.addControl(navigation);
24479	* // Remove zoom and rotation controls from the map.
24480	* map.removeControl(navigation);
24481	* ```
24482	*/
24483	removeControl(control) {
24484		if (!control?.onRemove) return this.fire(new ErrorEvent(/* @__PURE__ */ new Error("Invalid argument to map.removeControl(). Argument must be a control with onAdd and onRemove methods.")));
24485		const ci = this._controls.indexOf(control);
24486		if (ci > -1) this._controls.splice(ci, 1);
24487		control.onRemove(this);
24488		return this;
24489	}
24490	/**
24491	* Checks if a control exists on the map.
24492	*
24493	* @param control - The {@link IControl} to check.
24494	* @returns true if map contains control.
24495	* @example
24496	* ```ts
24497	* // Define a new navigation control.
24498	* let navigation = new NavigationControl();
24499	* // Add zoom and rotation controls to the map.
24500	* map.addControl(navigation);
24501	* // Check that the navigation control exists on the map.
24502	* map.hasControl(navigation);
24503	* ```
24504	*/
24505	hasControl(control) {
24506		return this._controls.includes(control);
24507	}
24508	/**
24509	* Returns an array of `OverscaledTileID` objects that cover the current viewport for a given tile size.
24510	* This method is useful for determining which tiles are visible in the current viewport.
24511	*
24512	* @param options - Options for calculating the covering tiles.
24513	* @returns An array of `OverscaledTileID` objects.
24514	* @example
24515	* ```ts
24516	* // Get the tiles to cover the view for a 512x512px tile source
24517	* const tiles = map.coveringTiles({tileSize: 512});
24518	* ```
24519	*/
24520	coveringTiles(options) {
24521		return coveringTiles(this._camera.transform, options);
24522	}
24523	/**
24524	* Sets the callback used to defer camera updates or apply arbitrary constraints.
24525	* If specified, this Camera instance can be used as a stateless component in React etc.
24526	*/
24527	setTransformCameraUpdate(value) {
24528		this._camera.transformCameraUpdate = value;
24529	}
24530	/**
24531	* Returns the map's geographical centerpoint.
24532	*
24533	* @returns The map's geographical centerpoint.
24534	* @example
24535	* Return a LngLat object such as `{lng: 0, lat: 0}`
24536	* ```ts
24537	* let center = map.getCenter();
24538	* // access longitude and latitude values directly
24539	* let {lng, lat} = map.getCenter();
24540	* ```
24541	*/
24542	getCenter() {
24543		return new LngLat(this._camera.transform.center.lng, this._camera.transform.center.lat);
24544	}
24545	/**
24546	* Sets the map's geographical centerpoint. Equivalent to `jumpTo({center: center})`.
24547	*
24548	* Triggers the following events: `movestart` and `moveend`.
24549	*
24550	* @param center - The centerpoint to set.
24551	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24552	* @example
24553	* ```ts
24554	* map.setCenter([-74, 38]);
24555	* ```
24556	*/
24557	setCenter(center, eventData) {
24558		this._camera.setCenter(center, eventData);
24559		return this;
24560	}
24561	/**
24562	* Returns the elevation of the map's center point.
24563	*
24564	* @returns The elevation of the map's center point, in meters above sea level.
24565	*/
24566	getCenterElevation() {
24567		return this._camera.transform.elevation;
24568	}
24569	/**
24570	* Sets the elevation of the map's center point, in meters above sea level. Equivalent to `jumpTo({elevation: elevation})`.
24571	*
24572	* Triggers the following events: `movestart` and `moveend`.
24573	*
24574	* @param elevation - The elevation to set, in meters above sea level.
24575	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24576	*/
24577	setCenterElevation(elevation, eventData) {
24578		this._camera.setCenterElevation(elevation, eventData);
24579		return this;
24580	}
24581	/**
24582	* Sets the value of `centerClampedToGround`.
24583	*
24584	* If true, the elevation of the center point will automatically be set to the terrain elevation
24585	* (or zero if terrain is not enabled). If false, the elevation of the center point will default
24586	* to sea level and will not automatically update. Defaults to true. Needs to be set to false to
24587	* keep the camera above ground when pitch \> 90 degrees.
24588	*/
24589	setCenterClampedToGround(centerClampedToGround) {
24590		this._camera.setCenterClampedToGround(centerClampedToGround);
24591	}
24592	/**
24593	* Pans the map by the specified offset.
24594	*
24595	* Triggers the following events: `movestart` and `moveend`.
24596	*
24597	* @param offset - `x` and `y` coordinates by which to pan the map.
24598	* @param options - Options object
24599	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24600	* @see [Navigate the map with game-like controls](https://maplibre.org/maplibre-gl-js/docs/examples/navigate-the-map-with-game-like-controls/)
24601	*/
24602	panBy(offset, options, eventData) {
24603		this._camera.panBy(offset, options, eventData);
24604		return this;
24605	}
24606	/**
24607	* Pans the map to the specified location with an animated transition.
24608	*
24609	* Triggers the following events: `movestart` and `moveend`.
24610	*
24611	* @param lnglat - The location to pan the map to.
24612	* @param options - Options describing the destination and animation of the transition.
24613	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24614	* @example
24615	* ```ts
24616	* map.panTo([-74, 38]);
24617	* // Specify that the panTo animation should last 5000 milliseconds.
24618	* map.panTo([-74, 38], {duration: 5000});
24619	* ```
24620	* @see [Update a feature in realtime](https://maplibre.org/maplibre-gl-js/docs/examples/update-a-feature-in-realtime/)
24621	*/
24622	panTo(lnglat, options, eventData) {
24623		this._camera.panTo(lnglat, options, eventData);
24624		return this;
24625	}
24626	/**
24627	* Returns the map's current zoom level.
24628	*
24629	* @returns The map's current zoom level.
24630	* @example
24631	* ```ts
24632	* map.getZoom();
24633	* ```
24634	*/
24635	getZoom() {
24636		return this._camera.transform.zoom;
24637	}
24638	/**
24639	* Sets the map's zoom level. Equivalent to `jumpTo({zoom: zoom})`.
24640	*
24641	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`.
24642	*
24643	* @param zoom - The zoom level to set (0-20).
24644	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24645	* @example
24646	* Zoom to the zoom level 5 without an animated transition
24647	* ```ts
24648	* map.setZoom(5);
24649	* ```
24650	*/
24651	setZoom(zoom, eventData) {
24652		this._camera.setZoom(zoom, eventData);
24653		return this;
24654	}
24655	/**
24656	* Zooms the map to the specified zoom level, with an animated transition.
24657	*
24658	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`.
24659	*
24660	* @param zoom - The zoom level to transition to.
24661	* @param options - Options object
24662	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24663	* @example
24664	* ```ts
24665	* // Zoom to the zoom level 5 without an animated transition
24666	* map.zoomTo(5);
24667	* // Zoom to the zoom level 8 with an animated transition
24668	* map.zoomTo(8, {
24669	*   duration: 2000,
24670	*   offset: [100, 50]
24671	* });
24672	* ```
24673	*/
24674	zoomTo(zoom, options, eventData) {
24675		this._camera.zoomTo(zoom, options, eventData);
24676		return this;
24677	}
24678	/**
24679	* Incrementally increases the map's zoom level by 1, first snapping to the nearest `zoomSnap` increment.
24680	*
24681	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`.
24682	*
24683	* @param options - Options object
24684	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24685	* @example
24686	* Zoom the map in one level with a custom animation duration
24687	* ```ts
24688	* map.zoomIn({duration: 1000});
24689	* ```
24690	*/
24691	zoomIn(options, eventData) {
24692		this._camera.zoomIn(options, eventData);
24693		return this;
24694	}
24695	/**
24696	* Decreases the map's zoom level by 1, first snapping to the nearest `zoomSnap` increment.
24697	*
24698	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`.
24699	*
24700	* @param options - Options object
24701	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24702	* @example
24703	* Zoom the map out one level with a custom animation offset
24704	* ```ts
24705	* map.zoomOut({offset: [80, 60]});
24706	* ```
24707	*/
24708	zoomOut(options, eventData) {
24709		this._camera.zoomOut(options, eventData);
24710		return this;
24711	}
24712	/**
24713	* Returns the map's current vertical field of view, in degrees.
24714	*
24715	* @returns The map's current vertical field of view.
24716	* @defaultValue 36.87
24717	* @example
24718	* ```ts
24719	* const verticalFieldOfView = map.getVerticalFieldOfView();
24720	* ```
24721	*/
24722	getVerticalFieldOfView() {
24723		return this._camera.transform.fov;
24724	}
24725	/**
24726	* Sets the map's vertical field of view, in degrees.
24727	*
24728	* Triggers the following events: `movestart`, `move`, and `moveend`.
24729	*
24730	* @param fov - The vertical field of view to set, in degrees (0-180).
24731	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24732	* @defaultValue 36.87
24733	* @example
24734	* Change vertical field of view to 30 degrees
24735	* ```ts
24736	* map.setVerticalFieldOfView(30);
24737	* ```
24738	*/
24739	setVerticalFieldOfView(fov, eventData) {
24740		this._camera.setVerticalFieldOfView(fov, eventData);
24741		return this;
24742	}
24743	/**
24744	* Returns the map's current bearing. The bearing is the compass direction that is "up"; for example, a bearing
24745	* of 90° orients the map so that east is up.
24746	*
24747	* @returns The map's current bearing.
24748	* @see [Navigate the map with game-like controls](https://maplibre.org/maplibre-gl-js/docs/examples/navigate-the-map-with-game-like-controls/)
24749	*/
24750	getBearing() {
24751		return this._camera.transform.bearing;
24752	}
24753	/**
24754	* Sets the map's bearing (rotation). The bearing is the compass direction that is "up"; for example, a bearing
24755	* of 90° orients the map so that east is up.
24756	*
24757	* Equivalent to `jumpTo({bearing: bearing})`.
24758	*
24759	* Triggers the following events: `movestart`, `moveend`, and `rotate`.
24760	*
24761	* @param bearing - The desired bearing.
24762	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24763	* @example
24764	* Rotate the map to 90 degrees
24765	* ```ts
24766	* map.setBearing(90);
24767	* ```
24768	*/
24769	setBearing(bearing, eventData) {
24770		this._camera.setBearing(bearing, eventData);
24771		return this;
24772	}
24773	/**
24774	* Returns the map's current zoom snap level.
24775	*
24776	* @returns The map's current zoom snap level.
24777	*/
24778	getZoomSnap() {
24779		return this._camera.getZoomSnap();
24780	}
24781	/**
24782	* Sets the map's zoom snap level.
24783	*
24784	* @param snap - The zoom snap level to set.
24785	*/
24786	setZoomSnap(snap) {
24787		this._camera.setZoomSnap(snap);
24788		return this;
24789	}
24790	/**
24791	* Returns the current padding applied around the map viewport.
24792	*
24793	* @returns The current padding around the map viewport.
24794	*/
24795	getPadding() {
24796		return this._camera.transform.padding;
24797	}
24798	/**
24799	* Sets the padding in pixels around the viewport.
24800	*
24801	* Equivalent to `jumpTo({padding: padding})`.
24802	*
24803	* Triggers the following events: `movestart` and `moveend`.
24804	*
24805	* @param padding - The desired padding.
24806	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24807	* @example
24808	* Sets a left padding of 300px, and a top padding of 50px
24809	* ```ts
24810	* map.setPadding({ left: 300, top: 50 });
24811	* ```
24812	*/
24813	setPadding(padding, eventData) {
24814		this._camera.setPadding(padding, eventData);
24815		return this;
24816	}
24817	/**
24818	* Rotates the map to the specified bearing, with an animated transition. The bearing is the compass direction
24819	* that is "up"; for example, a bearing of 90° orients the map so that east is up.
24820	*
24821	* Triggers the following events: `movestart`, `moveend`, and `rotate`.
24822	*
24823	* @param bearing - The desired bearing.
24824	* @param options - Options object
24825	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24826	*/
24827	rotateTo(bearing, options, eventData) {
24828		this._camera.rotateTo(bearing, options, eventData);
24829		return this;
24830	}
24831	/**
24832	* Rotates the map so that north is up (0° bearing), with an animated transition.
24833	*
24834	* Triggers the following events: `movestart`, `moveend`, and `rotate`.
24835	*
24836	* @param options - Options object
24837	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24838	*/
24839	resetNorth(options, eventData) {
24840		this._camera.resetNorth(options, eventData);
24841		return this;
24842	}
24843	/**
24844	* Rotates and pitches the map so that north is up (0° bearing) and pitch and roll are 0°, with an animated transition.
24845	*
24846	* Triggers the following events: `movestart`, `move`, `moveend`, `pitchstart`, `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`.
24847	*
24848	* @param options - Options object
24849	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24850	*/
24851	resetNorthPitch(options, eventData) {
24852		this._camera.resetNorthPitch(options, eventData);
24853		return this;
24854	}
24855	/**
24856	* Snaps the map so that north is up (0° bearing), if the current bearing is close enough to it (i.e. within the
24857	* `bearingSnap` threshold).
24858	*
24859	* Triggers the following events: `movestart`, `moveend`, and `rotate`.
24860	*
24861	* @param options - Options object
24862	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24863	*/
24864	snapToNorth(options, eventData) {
24865		this._camera.snapToNorth(options, eventData);
24866		return this;
24867	}
24868	/**
24869	* Returns the map's current pitch (tilt).
24870	*
24871	* @returns The map's current pitch, measured in degrees away from the plane of the screen.
24872	*/
24873	getPitch() {
24874		return this._camera.transform.pitch;
24875	}
24876	/**
24877	* Sets the map's pitch (tilt). Equivalent to `jumpTo({pitch: pitch})`.
24878	*
24879	* Triggers the following events: `movestart`, `moveend`, `pitchstart`, and `pitchend`.
24880	*
24881	* @param pitch - The pitch to set, measured in degrees away from the plane of the screen (0-60).
24882	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24883	*/
24884	setPitch(pitch, eventData) {
24885		this._camera.setPitch(pitch, eventData);
24886		return this;
24887	}
24888	/**
24889	* Returns the map's current roll angle.
24890	*
24891	* @returns The map's current roll, measured in degrees about the camera boresight.
24892	*/
24893	getRoll() {
24894		return this._camera.transform.roll;
24895	}
24896	/**
24897	* Sets the map's roll angle. Equivalent to `jumpTo({roll: roll})`.
24898	*
24899	* Triggers the following events: `movestart`, `moveend`, `rollstart`, and `rollend`.
24900	*
24901	* @param roll - The roll to set, measured in degrees about the camera boresight
24902	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24903	*/
24904	setRoll(roll, eventData) {
24905		this._camera.setRoll(roll, eventData);
24906		return this;
24907	}
24908	/**
24909	* @param bounds - Calculate the center for these bounds in the viewport and use
24910	* the highest zoom level up to and including {@link Map.getMaxZoom} that fits
24911	* in the viewport. LngLatBounds represent a box that is always axis-aligned with bearing 0.
24912	* Bounds will be taken in `[sw, ne]` order. Southwest point will always be to the left of the northeast point.
24913	* @param options - Options object
24914	* @returns If map is able to fit to provided bounds, returns `center`, `zoom`, and `bearing`.
24915	* If map is unable to fit, method will warn and return undefined.
24916	* @example
24917	* ```ts
24918	* let bbox = [[-79, 43], [-73, 45]];
24919	* let newCameraTransform = map.cameraForBounds(bbox, {
24920	*   padding: {top: 10, bottom:25, left: 15, right: 5}
24921	* });
24922	* ```
24923	*/
24924	cameraForBounds(bounds, options) {
24925		return this._camera.cameraForBounds(bounds, options);
24926	}
24927	/**
24928	* Pans and zooms the map to contain its visible area within the specified geographical bounds.
24929	* This function will also reset the map's bearing to 0 if bearing is nonzero.
24930	*
24931	* Triggers the following events: `movestart` and `moveend`.
24932	*
24933	* @param bounds - Center these bounds in the viewport and use the highest
24934	* zoom level up to and including {@link Map.getMaxZoom} that fits them in the viewport.
24935	* Bounds will be taken in `[sw, ne]` order. Southwest point will always be to the left of the northeast point.
24936	* @param options - Options supports all properties from {@link AnimationOptions} and {@link CameraOptions} in addition to the fields below.
24937	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24938	* @example
24939	* ```ts
24940	* let bbox = [[-79, 43], [-73, 45]];
24941	* map.fitBounds(bbox, {
24942	*   padding: {top: 10, bottom:25, left: 15, right: 5}
24943	* });
24944	* ```
24945	* @see [Fit a map to a bounding box](https://maplibre.org/maplibre-gl-js/docs/examples/fit-a-map-to-a-bounding-box/)
24946	*/
24947	fitBounds(bounds, options, eventData) {
24948		this._camera.fitBounds(bounds, options, eventData);
24949		return this;
24950	}
24951	/**
24952	* Pans, rotates and zooms the map to to fit the box made by points p0 and p1
24953	* once the map is rotated to the specified bearing. To zoom without rotating,
24954	* pass in the current map bearing.
24955	*
24956	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend` and `rotate`.
24957	*
24958	* @param p0 - First point on screen, in pixel coordinates
24959	* @param p1 - Second point on screen, in pixel coordinates
24960	* @param bearing - Desired map bearing at end of animation, in degrees
24961	* @param options - Options object
24962	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24963	* @example
24964	* ```ts
24965	* let p0 = [220, 400];
24966	* let p1 = [500, 900];
24967	* map.fitScreenCoordinates(p0, p1, map.getBearing(), {
24968	*   padding: {top: 10, bottom:25, left: 15, right: 5}
24969	* });
24970	* ```
24971	* @see Used by {@link BoxZoomHandler}
24972	*/
24973	fitScreenCoordinates(p0, p1, bearing, options, eventData) {
24974		this._camera.fitScreenCoordinates(p0, p1, bearing, options, eventData);
24975		return this;
24976	}
24977	/**
24978	* Changes any combination of center, zoom, bearing, pitch, and roll, without
24979	* an animated transition. The map will retain its current values for any
24980	* details not specified in `options`.
24981	*
24982	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`,
24983	* `pitch`, `pitchend`, `rollstart`, `roll`, `rollend` and `rotate`.
24984	*
24985	* @param options - Options object
24986	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24987	* @example
24988	* ```ts
24989	* // jump to coordinates at current zoom
24990	* map.jumpTo({center: [0, 0]});
24991	* // jump with zoom, pitch, and bearing options
24992	* map.jumpTo({
24993	*   center: [0, 0],
24994	*   zoom: 8,
24995	*   pitch: 45,
24996	*   bearing: 90
24997	* });
24998	* ```
24999	* @see [Jump to a series of locations](https://maplibre.org/maplibre-gl-js/docs/examples/jump-to-a-series-of-locations/)
25000	* @see [Update a feature in realtime](https://maplibre.org/maplibre-gl-js/docs/examples/update-a-feature-in-realtime/)
25001	*/
25002	jumpTo(options, eventData) {
25003		this._camera.jumpTo(options, eventData);
25004		return this;
25005	}
25006	/**
25007	* Calculates constrained camera options that place a geographic anchor at a screen point
25008	* without changing the map, using the same logic as MapLibre's interaction handlers.
25009	*
25010	* @param options - Anchor and optional zoom.
25011	* @returns Camera options that can be passed to {@link Map.jumpTo}.
25012	* @example
25013	* ```ts
25014	* const cameraOptions = map.calculateAnchoredCameraOptions({
25015	*   anchorLocation: map.unproject(pointerDownPosition),
25016	*   anchorScreenPoint: currentPointerPosition,
25017	*   zoom: map.getZoom() + 1,
25018	* });
25019	* map.jumpTo(cameraOptions);
25020	* ```
25021	*/
25022	calculateAnchoredCameraOptions(options) {
25023		return this._camera.calculateAnchoredCameraOptions(options);
25024	}
25025	/**
25026	* Given a camera position and rotation, calculates zoom and center point and returns them as {@link CameraOptions}.
25027	* @param cameraLngLat - The lng, lat of the camera to look from
25028	* @param cameraAlt - The altitude of the camera to look from, in meters above sea level
25029	* @param bearing - Bearing of the camera, in degrees
25030	* @param pitch - Pitch of the camera, in degrees
25031	* @param roll - Roll of the camera, in degrees
25032	* @returns the calculated camera options
25033	* @example
25034	* ```ts
25035	* // Calculate options to look from camera position(1°, 0°, 1000m) with bearing = 90°, pitch = 30°, and roll = 45°
25036	* const cameraLngLat = new LngLat(1, 0);
25037	* const cameraAltitude = 1000;
25038	* const bearing = 90;
25039	* const pitch = 30;
25040	* const roll = 45;
25041	* const cameraOptions = map.calculateCameraOptionsFromCameraLngLatAltRotation(cameraLngLat, cameraAltitude, bearing, pitch, roll);
25042	* // Apply calculated options
25043	* map.jumpTo(cameraOptions);
25044	* ```
25045	*/
25046	calculateCameraOptionsFromCameraLngLatAltRotation(cameraLngLat, cameraAlt, bearing, pitch, roll) {
25047		return this._camera.calculateCameraOptionsFromCameraLngLatAltRotation(cameraLngLat, cameraAlt, bearing, pitch, roll);
25048	}
25049	/**
25050	* Changes any combination of `center`, `zoom`, `bearing`, `pitch`, `roll`, and `padding` with an animated transition
25051	* between old and new values. The map will retain its current values for any
25052	* details not specified in `options`.
25053	*
25054	* !!! note "Reduced Motion"
25055	*     The transition will happen instantly if the user has enabled
25056	*     the `reduced motion` accessibility feature enabled in their operating system,
25057	*     unless `options` includes `essential: true`.
25058	*
25059	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`,
25060	* `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`.
25061	*
25062	* @param options - Options describing the destination and animation of the transition.
25063	* Accepts {@link CameraOptions} and {@link AnimationOptions}.
25064	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
25065	* @see [Navigate the map with game-like controls](https://maplibre.org/maplibre-gl-js/docs/examples/navigate-the-map-with-game-like-controls/)
25066	*/
25067	easeTo(options, eventData) {
25068		this._camera.easeTo(options, eventData);
25069		return this;
25070	}
25071	/**
25072	* Changes any combination of center, zoom, bearing, pitch, and roll, animating the transition along a curve that
25073	* evokes flight. The animation seamlessly incorporates zooming and panning to help
25074	* the user maintain her bearings even after traversing a great distance.
25075	*
25076	* !!! note "Reduced Motion"
25077	*     The animation will be skipped, and this will behave equivalently to `jumpTo`
25078	*     if the user has the `reduced motion` accessibility feature enabled in their operating system,
25079	*     unless 'options' includes `essential: true`.
25080	*
25081	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`,
25082	* `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`.
25083	*
25084	* @param options - Options describing the destination and animation of the transition.
25085	* Accepts {@link CameraOptions}, {@link AnimationOptions},
25086	* and the following additional options.
25087	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
25088	* @example
25089	* ```ts
25090	* // fly with default options to null island
25091	* map.flyTo({center: [0, 0], zoom: 9});
25092	* // using flyTo options
25093	* map.flyTo({
25094	*   center: [0, 0],
25095	*   zoom: 9,
25096	*   speed: 0.2,
25097	*   curve: 1,
25098	*   easing(t) {
25099	*     return t;
25100	*   }
25101	* });
25102	* ```
25103	* @see [Fly to a location](https://maplibre.org/maplibre-gl-js/docs/examples/fly-to-a-location/)
25104	* @see [Slowly fly to a location](https://maplibre.org/maplibre-gl-js/docs/examples/slowly-fly-to-a-location/)
25105	* @see [Fly to a location based on scroll position](https://maplibre.org/maplibre-gl-js/docs/examples/fly-to-a-location-based-on-scroll-position/)
25106	*/
25107	flyTo(options, eventData) {
25108		this._camera.flyTo(options, eventData);
25109		return this;
25110	}
25111	/**
25112	* Stops any animated transition underway.
25113	*/
25114	stop() {
25115		this._camera.stop();
25116		return this;
25117	}
25118	/**
25119	* Gets the elevation at a given location, in meters above sea level.
25120	* Returns null if terrain is not enabled.
25121	* If terrain is enabled with some exaggeration value, the value returned here will be reflective of (multiplied by) that exaggeration value.
25122	* This method should be used for proper positioning of custom 3d objects, as explained [here](https://maplibre.org/maplibre-gl-js/docs/examples/adding-3d-models-using-threejs-on-terrain/)
25123	* @param lngLatLike - `[x, y]` or LngLat coordinates of the location
25124	* @returns elevation in meters
25125	*/
25126	queryTerrainElevation(lngLatLike) {
25127		if (!this.terrain) return null;
25128		return this.terrain.getElevationForLngLat(LngLat.convert(lngLatLike), this._camera.transform);
25129	}
25130	/**
25131	* Returns the value of `centerClampedToGround`.
25132	*
25133	* If true, the elevation of the center point will automatically be set to the terrain elevation
25134	* (or zero if terrain is not enabled). If false, the elevation of the center point will default
25135	* to sea level and will not automatically update. Defaults to true. Needs to be set to false to
25136	* keep the camera above ground when pitch \> 90 degrees.
25137	*/
25138	getCenterClampedToGround() {
25139		return this._camera.getCenterClampedToGround();
25140	}
25141	/**
25142	* Given a camera 'from' position and a position to look at (`to`), calculates zoom and camera rotation and returns them as {@link CameraOptions}.
25143	* Under `globe` and `vertical-perspective` the calculation follows the sphere while the map renders as a globe, keeping the point looked at on the sea-level sphere; `altitudeTo` only becomes the center elevation.
25144	* @param from - The camera to look from
25145	* @param altitudeFrom - The altitude of the camera to look from
25146	* @param to - The center to look at
25147	* @param altitudeTo - Optional altitude of the center to look at. If none given the ground height will be used.
25148	* @returns the calculated camera options
25149	* @example
25150	* ```ts
25151	* // Calculate options to look from (1°, 0°, 1000m) to (1°, 1°, 0m)
25152	* const cameraLngLat = new LngLat(1, 0);
25153	* const cameraAltitude = 1000;
25154	* const targetLngLat = new LngLat(1, 1);
25155	* const targetAltitude = 0;
25156	* const cameraOptions = map.calculateCameraOptionsFromTo(cameraLngLat, cameraAltitude, targetLngLat, targetAltitude);
25157	* // Apply calculated options
25158	* map.jumpTo(cameraOptions);
25159	* ```
25160	*/
25161	calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo) {
25162		if (altitudeTo == null && this.terrain) altitudeTo = this.terrain.getElevationForLngLat(to, this._camera.transform);
25163		return this._camera.transform.calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo ?? 0);
25164	}
25165	/**
25166	* Resizes the map according to the dimensions of its
25167	* `container` element.
25168	*
25169	* Checks if the map container size changed and updates the map if it has changed.
25170	* With the default `trackResize: true`, container size changes are picked up automatically,
25171	* including a container that becomes visible after being hidden with CSS. Call this method
25172	* explicitly when `trackResize` is `false`, or when the map's size changes in a way the
25173	* container's `ResizeObserver` cannot observe.
25174	*
25175	* Triggers the following events: `movestart`, `move`, `moveend`, and `resize`.
25176	*
25177	* @param eventData - Additional properties to be passed to `movestart`, `move`, `resize`, and `moveend`
25178	* events that get triggered as a result of resize. This can be useful for differentiating the
25179	* source of an event (for example, user-initiated or programmatically-triggered events).
25180	* @example
25181	* Resize a map with `trackResize` disabled when its container is shown after being hidden with CSS.
25182	* ```ts
25183	* let mapDiv = document.getElementById('map');
25184	* if (mapDiv.style.visibility === 'visible') map.resize();
25185	* ```
25186	*/
25187	resize(eventData, constrainTransform = true) {
25188		if (this._lostContextStyle.style !== null) return this;
25189		this._resizeInternal(constrainTransform);
25190		const fireMoving = !this._camera._moving;
25191		if (fireMoving) {
25192			this.stop();
25193			this.fire(new MapMovementEvent("movestart", eventData)).fire(new MapMovementEvent("move", eventData));
25194		}
25195		this.fire(new MapLibreEvent("resize", eventData));
25196		if (fireMoving) this.fire(new MapMovementEvent("moveend", eventData));
25197		return this;
25198	}
25199	/**
25200	* Resizes the map according to the dimensions of its
25201	* `container` element.
25202	*
25203	* It does not trigger any events, and does not check for context loss.
25204	*
25205	* It is used internally and by {@link Map.resize}.
25206	*
25207	* @internal
25208	*
25209	* @param constrainTransform - whether to constrain the transform after resizing.
25210	*/
25211	_resizeInternal(constrainTransform = true) {
25212		const [width, height] = this._containerDimensions();
25213		const clampedPixelRatio = this._getClampedPixelRatio(width, height);
25214		this._resizeCanvas(width, height, clampedPixelRatio);
25215		this.painter.resize(width, height, clampedPixelRatio);
25216		if (this.painter.overLimit()) {
25217			const gl = this.painter.context.gl;
25218			this._maxCanvasSize = [gl.drawingBufferWidth, gl.drawingBufferHeight];
25219			const clampedPixelRatio = this._getClampedPixelRatio(width, height);
25220			this._resizeCanvas(width, height, clampedPixelRatio);
25221			this.painter.resize(width, height, clampedPixelRatio);
25222		}
25223		this._resizeTransform(constrainTransform);
25224	}
25225	_resizeTransform(constrainTransform = true) {
25226		const [width, height] = this._containerDimensions();
25227		this._camera.transform.resize(width, height, constrainTransform);
25228		this._camera._requestedCameraState?.resize(width, height, constrainTransform);
25229	}
25230	/**
25231	* @internal
25232	* Return the map's pixel ratio eventually scaled down to respect maxCanvasSize.
25233	* Internally you should use this and not getPixelRatio().
25234	* Warns once when the ratio is scaled down. The message carries no sizes: this runs on every
25235	* resize and `warnOnce` de-duplicates by message, so sizes would warn on every drag frame.
25236	*/
25237	_getClampedPixelRatio(width, height) {
25238		const { 0: maxCanvasWidth, 1: maxCanvasHeight } = this._maxCanvasSize;
25239		const pixelRatio = this.getPixelRatio();
25240		const canvasWidth = width * pixelRatio;
25241		const canvasHeight = height * pixelRatio;
25242		const widthScaleFactor = canvasWidth > maxCanvasWidth ? maxCanvasWidth / canvasWidth : 1;
25243		const heightScaleFactor = canvasHeight > maxCanvasHeight ? maxCanvasHeight / canvasHeight : 1;
25244		const scaleFactor = Math.min(widthScaleFactor, heightScaleFactor);
25245		if (scaleFactor < 1) warnOnce("The canvas is larger than maxCanvasSize and is rendered at a lower pixel ratio to fit. Increase maxCanvasSize, within MAX_TEXTURE_SIZE, to render at full resolution.");
25246		return scaleFactor * pixelRatio;
25247	}
25248	/**
25249	* Returns the map's pixel ratio.
25250	* Note that the pixel ratio actually applied may be lower to respect maxCanvasSize.
25251	* @returns The pixel ratio.
25252	*/
25253	getPixelRatio() {
25254		return this._overridePixelRatio ?? devicePixelRatio;
25255	}
25256	/**
25257	* Sets the map's pixel ratio. This allows to override `devicePixelRatio`.
25258	* After this call, the canvas' `width` attribute will be `container.clientWidth * pixelRatio`
25259	* and its height attribute will be `container.clientHeight * pixelRatio`.
25260	* Set this to null to disable `devicePixelRatio` override.
25261	* Note that the pixel ratio actually applied may be lower to respect maxCanvasSize.
25262	* @param pixelRatio - The pixel ratio.
25263	*/
25264	setPixelRatio(pixelRatio) {
25265		this._overridePixelRatio = pixelRatio;
25266		this.resize();
25267	}
25268	/**
25269	* Returns the map's geographical bounds. When the bearing or pitch is non-zero, the visible region is not
25270	* an axis-aligned rectangle, and the result is the smallest bounds that encompasses the visible region.
25271	* @returns The geographical bounds of the map as {@link LngLatBounds}.
25272	* @example
25273	* ```ts
25274	* let bounds = map.getBounds();
25275	* ```
25276	*/
25277	getBounds() {
25278		return this._camera.transform.getBounds();
25279	}
25280	/**
25281	* Returns the maximum geographical bounds the map is constrained to, or `null` if none set.
25282	* @returns The map object.
25283	* @example
25284	* ```ts
25285	* let maxBounds = map.getMaxBounds();
25286	* ```
25287	*/
25288	getMaxBounds() {
25289		return this._camera.transform.getMaxBounds();
25290	}
25291	/**
25292	* Sets or clears the map's geographical bounds.
25293	*
25294	* Pan and zoom operations are constrained within these bounds.
25295	* If a pan or zoom is performed that would
25296	* display regions outside these bounds, the map will
25297	* instead display a position and zoom level
25298	* as close as possible to the operation's request while still
25299	* remaining within the bounds.
25300	*
25301	* @param bounds - The maximum bounds to set. If `null` or `undefined` is provided, the function removes the map's maximum bounds.
25302	* @example
25303	* Define bounds that conform to the `LngLatBoundsLike` object as set the max bounds.
25304	* ```ts
25305	* let bounds = [
25306	*   [-74.04728, 40.68392], // [west, south]
25307	*   [-73.91058, 40.87764]  // [east, north]
25308	* ];
vendor: 24,954 bytes, lines 25309-25876
25309	* map.setMaxBounds(bounds);
25310	* ```
25311	*/
25312	setMaxBounds(bounds) {
25313		this._camera.transform.setMaxBounds(LngLatBounds.convert(bounds));
25314		return this._update();
25315	}
25316	/**
25317	* Sets or clears the map's minimum zoom level.
25318	* If the map's current zoom level is lower than the new minimum,
25319	* the map will zoom to the new minimum and trigger the following events:
25320	* `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`.
25321	*
25322	* It is not always possible to zoom out and reach the set `minZoom`.
25323	* Other factors such as map height may restrict zooming. For example,
25324	* if the map is 512px tall it will not be possible to zoom below zoom 0
25325	* no matter what the `minZoom` is set to.
25326	*
25327	* A {@link ErrorEvent} event will be fired if minZoom is out of bounds.
25328	*
25329	* @param minZoom - The minimum zoom level to set (-2 - 24).
25330	* If `null` or `undefined` is provided, the function removes the current minimum zoom (i.e. sets it to -2).
25331	* @example
25332	* ```ts
25333	* map.setMinZoom(12.25);
25334	* ```
25335	*/
25336	setMinZoom(minZoom) {
25337		minZoom = minZoom === null || minZoom === void 0 ? defaultMinZoom : minZoom;
25338		if (minZoom >= defaultMinZoom && minZoom <= this._camera.transform.maxZoom) {
25339			const zoomBefore = this._camera.transform.zoom;
25340			const tr = this._camera.getTransformForUpdate();
25341			tr.setMinZoom(minZoom);
25342			this._camera.applyUpdatedTransform(tr);
25343			this._update();
25344			if (zoomBefore !== this._camera.transform.zoom) this.fire(new MapMovementEvent("zoomstart")).fire(new MapMovementEvent("zoom")).fire(new MapMovementEvent("zoomend")).fire(new MapMovementEvent("movestart")).fire(new MapMovementEvent("move")).fire(new MapMovementEvent("moveend"));
25345			return this;
25346		} else throw new Error(`minZoom must be between ${defaultMinZoom} and the current maxZoom, inclusive`);
25347	}
25348	/**
25349	* Returns the map's minimum allowable zoom level.
25350	*
25351	* @param constrained - If `true`, returns the effective minimum zoom after applying the map's viewport constraints.
25352	* If `false` or omitted, returns the configured minimum zoom.
25353	* @returns minZoom
25354	* @example
25355	* ```ts
25356	* let minZoom = map.getMinZoom();
25357	* ```
25358	*/
25359	getMinZoom(constrained = false) {
25360		const transform = this._camera.transform;
25361		return constrained ? transform.applyConstrain(transform.center, transform.minZoom).zoom : transform.minZoom;
25362	}
25363	/**
25364	* Sets or clears the map's maximum zoom level.
25365	* If the map's current zoom level is higher than the new maximum,
25366	* the map will zoom to the new maximum and trigger the following events:
25367	* `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`.
25368	*
25369	* A {@link ErrorEvent} event will be fired if minZoom is out of bounds.
25370	*
25371	* @param maxZoom - The maximum zoom level to set.
25372	* If `null` or `undefined` is provided, the function removes the current maximum zoom (sets it to 22).
25373	* @example
25374	* ```ts
25375	* map.setMaxZoom(18.75);
25376	* ```
25377	*/
25378	setMaxZoom(maxZoom) {
25379		maxZoom = maxZoom === null || maxZoom === void 0 ? defaultMaxZoom : maxZoom;
25380		if (maxZoom >= this._camera.transform.minZoom) {
25381			const zoomBefore = this._camera.transform.zoom;
25382			const tr = this._camera.getTransformForUpdate();
25383			tr.setMaxZoom(maxZoom);
25384			this._camera.applyUpdatedTransform(tr);
25385			this._update();
25386			if (zoomBefore !== this._camera.transform.zoom) this.fire(new MapMovementEvent("zoomstart")).fire(new MapMovementEvent("zoom")).fire(new MapMovementEvent("zoomend")).fire(new MapMovementEvent("movestart")).fire(new MapMovementEvent("move")).fire(new MapMovementEvent("moveend"));
25387			return this;
25388		} else throw new Error("maxZoom must be greater than the current minZoom");
25389	}
25390	/**
25391	* Returns the map's maximum allowable zoom level.
25392	*
25393	* @returns The maxZoom
25394	* @example
25395	* ```ts
25396	* let maxZoom = map.getMaxZoom();
25397	* ```
25398	*/
25399	getMaxZoom() {
25400		return this._camera.transform.maxZoom;
25401	}
25402	/**
25403	* Sets or clears the map's minimum pitch.
25404	* If the map's current pitch is lower than the new minimum,
25405	* the map will pitch to the new minimum and trigger the following events:
25406	* `movestart`, `move`, `moveend`, `pitchstart`, `pitch`, and `pitchend`.
25407	*
25408	* A {@link ErrorEvent} event will be fired if minPitch is out of bounds.
25409	*
25410	* @param minPitch - The minimum pitch to set (0-180). Values greater than 60 degrees are experimental and may result in rendering issues. If you encounter any, please raise an issue with details in the MapLibre project.
25411	* If `null` or `undefined` is provided, the function removes the current minimum pitch (i.e. sets it to 0).
25412	*/
25413	setMinPitch(minPitch) {
25414		minPitch = minPitch === null || minPitch === void 0 ? defaultMinPitch : minPitch;
25415		if (minPitch < defaultMinPitch) throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`);
25416		if (minPitch >= defaultMinPitch && minPitch <= this._camera.transform.maxPitch) {
25417			const pitchBefore = this._camera.transform.pitch;
25418			const tr = this._camera.getTransformForUpdate();
25419			tr.setMinPitch(minPitch);
25420			this._camera.applyUpdatedTransform(tr);
25421			this._update();
25422			if (pitchBefore !== this._camera.transform.pitch) this.fire(new MapMovementEvent("pitchstart")).fire(new MapMovementEvent("pitch")).fire(new MapMovementEvent("pitchend")).fire(new MapMovementEvent("movestart")).fire(new MapMovementEvent("move")).fire(new MapMovementEvent("moveend"));
25423			return this;
25424		} else throw new Error(`minPitch must be between ${defaultMinPitch} and the current maxPitch, inclusive`);
25425	}
25426	/**
25427	* Returns the map's minimum allowable pitch.
25428	*
25429	* @returns The minPitch
25430	*/
25431	getMinPitch() {
25432		return this._camera.transform.minPitch;
25433	}
25434	/**
25435	* Sets or clears the map's maximum pitch.
25436	* If the map's current pitch is higher than the new maximum,
25437	* the map will pitch to the new maximum and trigger the following events:
25438	* `movestart`, `move`, `moveend`, `pitchstart`, `pitch`, and `pitchend`.
25439	*
25440	* A {@link ErrorEvent} event will be fired if maxPitch is out of bounds.
25441	*
25442	* @param maxPitch - The maximum pitch to set (0-180). Values greater than 60 degrees are experimental and may result in rendering issues. If you encounter any, please raise an issue with details in the MapLibre project.
25443	* If `null` or `undefined` is provided, the function removes the current maximum pitch (sets it to 60).
25444	*/
25445	setMaxPitch(maxPitch) {
25446		maxPitch = maxPitch === null || maxPitch === void 0 ? defaultMaxPitch : maxPitch;
25447		if (maxPitch > maxPitchThreshold) throw new Error(`maxPitch must be less than or equal to ${maxPitchThreshold}`);
25448		if (maxPitch >= this._camera.transform.minPitch) {
25449			const pitchBefore = this._camera.transform.pitch;
25450			const tr = this._camera.getTransformForUpdate();
25451			tr.setMaxPitch(maxPitch);
25452			this._camera.applyUpdatedTransform(tr);
25453			this._update();
25454			if (pitchBefore !== this._camera.transform.pitch) this.fire(new MapMovementEvent("pitchstart")).fire(new MapMovementEvent("pitch")).fire(new MapMovementEvent("pitchend")).fire(new MapMovementEvent("movestart")).fire(new MapMovementEvent("move")).fire(new MapMovementEvent("moveend"));
25455			return this;
25456		} else throw new Error("maxPitch must be greater than the current minPitch");
25457	}
25458	/**
25459	* Returns the map's maximum allowable pitch.
25460	*
25461	* @returns The maxPitch
25462	*/
25463	getMaxPitch() {
25464		return this._camera.transform.maxPitch;
25465	}
25466	/**
25467	* Returns the map's anisotropic filter pitch.
25468	* If the map is pitched beyond this threshold, anisotropic filtering will be applied to all raster layers.
25469	*
25470	* @returns The anisotropicFilterPitch
25471	* @example
25472	* ```ts
25473	* let anisotropicFilterPitch = map.getAnisotropicFilterPitch();
25474	* ```
25475	*/
25476	getAnisotropicFilterPitch() {
25477		return this._anisotropicFilterPitch;
25478	}
25479	/**
25480	* Sets the map's anisotropic filter pitch or reverts it to its default.
25481	*
25482	* A {@link ErrorEvent} event will be fired if anisotropicFilterPitch is out of bounds.
25483	*
25484	* @param anisotropicFilterPitch - The pitch above which to apply anisotropic filtering to the map's raster layers (0-180).
25485	* If `null` or `undefined` is provided, the function reverts to the default pitch threshold (20).
25486	*
25487	*
25488	* @example
25489	* ```ts
25490	* map.setAnisotropicFilterPitch(85);
25491	* ```
25492	*/
25493	setAnisotropicFilterPitch(anisotropicFilterPitch) {
25494		anisotropicFilterPitch = anisotropicFilterPitch === null || anisotropicFilterPitch === void 0 ? defaultAnisotropicFilterPitch : anisotropicFilterPitch;
25495		if (anisotropicFilterPitch > maxPitchThreshold) throw new Error(`anisotropicFilterPitch must be less than or equal to ${maxPitchThreshold}`);
25496		if (anisotropicFilterPitch < defaultMinPitch) throw new Error(`anisotropicFilterPitch must be greater than or equal to ${defaultMinPitch}`);
25497		this._anisotropicFilterPitch = anisotropicFilterPitch;
25498		return this._update();
25499	}
25500	/**
25501	* 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`:
25502	*
25503	* - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire
25504	* container, there will be blank space beyond 180 and -180 degrees longitude.
25505	* - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the
25506	* map and the other on the left edge of the map) at every zoom level.
25507	* @returns The renderWorldCopies
25508	* @example
25509	* ```ts
25510	* let worldCopiesRendered = map.getRenderWorldCopies();
25511	* ```
25512	* @see [Render world copies](https://maplibre.org/maplibre-gl-js/docs/examples/render-world-copies/)
25513	*/
25514	getRenderWorldCopies() {
25515		return this._camera.transform.renderWorldCopies;
25516	}
25517	/**
25518	* Sets the state of `renderWorldCopies`.
25519	*
25520	* @param renderWorldCopies - If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`:
25521	*
25522	* - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire
25523	* container, there will be blank space beyond 180 and -180 degrees longitude.
25524	* - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the
25525	* map and the other on the left edge of the map) at every zoom level.
25526	*
25527	* `undefined` is treated as `true`, `null` is treated as `false`.
25528	* @example
25529	* ```ts
25530	* map.setRenderWorldCopies(true);
25531	* ```
25532	* @see [Render world copies](https://maplibre.org/maplibre-gl-js/docs/examples/render-world-copies/)
25533	*/
25534	setRenderWorldCopies(renderWorldCopies) {
25535		this._camera.transform.setRenderWorldCopies(renderWorldCopies);
25536		return this._update();
25537	}
25538	/** Sets or clears the callback overriding how the map constrains the viewport's lnglat and zoom to respect the longitude and latitude bounds.
25539	*
25540	* @param constrain - A {@link TransformConstrainFunction} callback defining how the viewport should respect the bounds.
25541	*
25542	* `null` clears the callback and reverts the constrain to the map transform's default constrain function.
25543	* @example
25544	* ```ts
25545	* function customTransformConstrain(lngLat, zoom) {
25546	*   return {center: lngLat, zoom: zoom ?? 0};
25547	* };
25548	* map.setTransformConstrain(customTransformConstrain);
25549	* ```
25550	* @see [Customize the map transform constrain](https://maplibre.org/maplibre-gl-js/docs/examples/customize-the-map-transform-constrain/)
25551	*/
25552	setTransformConstrain(constrain) {
25553		this._camera.transform.setConstrainOverride(constrain);
25554		return this._update();
25555	}
25556	/**
25557	* Returns a [Point](https://github.com/mapbox/point-geometry) representing pixel coordinates, relative to the map's `container`,
25558	* that correspond to the specified geographical location.
25559	*
25560	* A location behind the camera has no corresponding pixel. For such a location the
25561	* returned point is outside the viewport, on the side through which the location left
25562	* the screen, one viewport width or height away from the edge.
25563	*
25564	* @param lnglat - The geographical location to project.
25565	* @returns The [Point](https://github.com/mapbox/point-geometry) corresponding to `lnglat`, relative to the map's `container`.
25566	* @example
25567	* ```ts
25568	* let coordinate = [-122.420679, 37.772537];
25569	* let point = map.project(coordinate);
25570	* ```
25571	*/
25572	project(lnglat) {
25573		return this._camera.transform.locationToScreenPoint(LngLat.convert(lnglat), this.style && this.terrain);
25574	}
25575	/**
25576	* Returns a {@link LngLat} representing geographical coordinates that correspond
25577	* to the specified pixel coordinates.
25578	*
25579	* @param point - The pixel coordinates to unproject.
25580	* @returns The {@link LngLat} corresponding to `point`.
25581	* @example
25582	* ```ts
25583	* map.on('click', (e) => {
25584	*   // When the map is clicked, get the geographic coordinate.
25585	*   let coordinate = map.unproject(e.point);
25586	* });
25587	* ```
25588	*/
25589	unproject(point) {
25590		return this._camera.transform.screenPointToLocation(Point.convert(point), this.terrain);
25591	}
25592	/**
25593	* Returns true if the map is panning, zooming, rotating, or pitching due to a camera animation or user gesture.
25594	* @returns true if the map is moving.
25595	* @example
25596	* ```ts
25597	* let isMoving = map.isMoving();
25598	* ```
25599	*/
25600	isMoving() {
25601		return this._camera.isMoving() || this._handlers?.isMoving() || false;
25602	}
25603	/**
25604	* Returns true if the map is zooming due to a camera animation or user gesture.
25605	* @returns true if the map is zooming.
25606	* @example
25607	* ```ts
25608	* let isZooming = map.isZooming();
25609	* ```
25610	*/
25611	isZooming() {
25612		return this._camera.isZooming() || this._handlers?.isZooming() || false;
25613	}
25614	/**
25615	* Returns true if the map is rotating due to a camera animation or user gesture.
25616	* @returns true if the map is rotating.
25617	* @example
25618	* ```ts
25619	* map.isRotating();
25620	* ```
25621	*/
25622	isRotating() {
25623		return this._camera.isRotating() || this._handlers?.isRotating() || false;
25624	}
25625	_createDelegatedListener(type, layerIds, listener) {
25626		if (type === "mouseenter" || type === "mouseover") {
25627			let mousein = false;
25628			const mousemove = (e) => {
25629				const existingLayers = layerIds.filter((layerId) => this.getLayer(layerId));
25630				const features = existingLayers.length !== 0 ? this.queryRenderedFeatures(e.point, { layers: existingLayers }) : [];
25631				if (!features.length) mousein = false;
25632				else if (!mousein) {
25633					mousein = true;
25634					listener.call(this, new MapMouseEvent(type, this, e.originalEvent, { features }));
25635				}
25636			};
25637			const mouseout = () => {
25638				mousein = false;
25639			};
25640			return {
25641				layers: layerIds,
25642				listener,
25643				delegates: {
25644					mousemove,
25645					mouseout
25646				}
25647			};
25648		} else if (type === "mouseleave" || type === "mouseout") {
25649			let mousein = false;
25650			const mousemove = (e) => {
25651				const existingLayers = layerIds.filter((layerId) => this.getLayer(layerId));
25652				if ((existingLayers.length !== 0 ? this.queryRenderedFeatures(e.point, { layers: existingLayers }) : []).length) mousein = true;
25653				else if (mousein) {
25654					mousein = false;
25655					listener.call(this, new MapMouseEvent(type, this, e.originalEvent));
25656				}
25657			};
25658			const mouseout = (e) => {
25659				if (mousein) {
25660					mousein = false;
25661					listener.call(this, new MapMouseEvent(type, this, e.originalEvent));
25662				}
25663			};
25664			return {
25665				layers: layerIds,
25666				listener,
25667				delegates: {
25668					mousemove,
25669					mouseout
25670				}
25671			};
25672		} else {
25673			const delegate = (e) => {
25674				const existingLayers = layerIds.filter((layerId) => this.getLayer(layerId));
25675				const features = existingLayers.length !== 0 ? this.queryRenderedFeatures(e.point, { layers: existingLayers }) : [];
25676				if (features.length) {
25677					e.features = features;
25678					listener.call(this, e);
25679					delete e.features;
25680				}
25681			};
25682			return {
25683				layers: layerIds,
25684				listener,
25685				delegates: { [type]: delegate }
25686			};
25687		}
25688	}
25689	_saveDelegatedListener(type, delegatedListener) {
25690		this._delegatedListeners ||= {};
25691		this._delegatedListeners[type] ||= [];
25692		this._delegatedListeners[type].push(delegatedListener);
25693	}
25694	_removeDelegatedListener(type, layerIds, listener) {
25695		if (!this._delegatedListeners?.[type]) return;
25696		const listeners = this._delegatedListeners[type];
25697		for (let i = 0; i < listeners.length; i++) {
25698			const delegatedListener = listeners[i];
25699			if (delegatedListener.listener === listener && delegatedListener.layers.length === layerIds.length && delegatedListener.layers.every((layerId) => layerIds.includes(layerId))) {
25700				for (const event in delegatedListener.delegates) this.off(event, delegatedListener.delegates[event]);
25701				listeners.splice(i, 1);
25702				return;
25703			}
25704		}
25705	}
25706	on(type, layerIdsOrListener, listener) {
25707		if (listener === void 0) return super.on(type, layerIdsOrListener);
25708		const layerIds = typeof layerIdsOrListener === "string" ? [layerIdsOrListener] : layerIdsOrListener;
25709		const delegatedListener = this._createDelegatedListener(type, layerIds, listener);
25710		this._saveDelegatedListener(type, delegatedListener);
25711		for (const event in delegatedListener.delegates) this.on(event, delegatedListener.delegates[event]);
25712		return { unsubscribe: () => {
25713			this._removeDelegatedListener(type, layerIds, listener);
25714		} };
25715	}
25716	once(type, layerIdsOrListener, listener) {
25717		if (listener === void 0) return super.once(type, layerIdsOrListener);
25718		const layerIds = typeof layerIdsOrListener === "string" ? [layerIdsOrListener] : layerIdsOrListener;
25719		const delegatedListener = this._createDelegatedListener(type, layerIds, listener);
25720		for (const key in delegatedListener.delegates) {
25721			const delegate = delegatedListener.delegates[key];
25722			delegatedListener.delegates[key] = (...args) => {
25723				this._removeDelegatedListener(type, layerIds, listener);
25724				delegate(...args);
25725			};
25726		}
25727		this._saveDelegatedListener(type, delegatedListener);
25728		for (const event in delegatedListener.delegates) this.once(event, delegatedListener.delegates[event]);
25729		return this;
25730	}
25731	off(type, layerIdsOrListener, listener) {
25732		if (listener === void 0) return super.off(type, layerIdsOrListener);
25733		const layerIds = typeof layerIdsOrListener === "string" ? [layerIdsOrListener] : layerIdsOrListener;
25734		this._removeDelegatedListener(type, layerIds, listener);
25735		return this;
25736	}
25737	/**
25738	* Returns an array of MapGeoJSONFeature objects
25739	* representing visible features that satisfy the query parameters.
25740	*
25741	* @param geometryOrOptions - (optional) The geometry of the query region in pixel points within the map viewport:
25742	* either a single pixel point or a pair of top-left and bottom-right pixel points describing a bounding box.
25743	* The origin of the pixel points is at the top-left of the map viewport.
25744	* Omitting this parameter (i.e. calling {@link Map.queryRenderedFeatures} with zero arguments,
25745	* or with only a `options` argument) is equivalent to passing a bounding box encompassing the entire
25746	* map viewport.
25747	* The geometryOrOptions can receive a {@link QueryRenderedFeaturesOptions} only to support a situation where the function receives only one parameter which is the options parameter.
25748	* @param options - (optional) Options object.
25749	*
25750	* @returns An array of MapGeoJSONFeature objects.
25751	*
25752	* The `properties` value of each returned feature object contains the properties of its source feature. For GeoJSON sources, only
25753	* string and numeric property values are supported (i.e. `null`, `Array`, and `Object` values are not supported).
25754	*
25755	* Each feature includes top-level `layer`, `source`, and `sourceLayer` properties. The `layer` property is an object
25756	* representing the style layer to  which the feature belongs. Layout and paint properties in this object contain values
25757	* which are fully evaluated for the given zoom level and feature.
25758	*
25759	* Only features that are currently rendered are included. Some features will **not** be included, like:
25760	*
25761	* - Features from layers whose `visibility` property is `"none"`.
25762	* - Features from layers whose zoom range excludes the current zoom level.
25763	* - Symbol features that have been hidden due to text or icon collision.
25764	*
25765	* Features from all other layers are included, including features that may have no visible
25766	* contribution to the rendered result; for example, because the layer's opacity or color alpha component is set to
25767	* 0.
25768	*
25769	* The topmost rendered feature appears first in the returned array, and subsequent features are sorted by
25770	* descending z-order. Features that are rendered multiple times (due to wrapping across the antemeridian at low
25771	* zoom levels) are returned only once (though subject to the following caveat).
25772	*
25773	* Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature
25774	* geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple
25775	* times in query results. For example, suppose there is a highway running through the bounding rectangle of a query.
25776	* The results of the query will be those parts of the highway that lie within the map tiles covering the bounding
25777	* rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile
25778	* will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple
25779	* tiles due to tile buffering.
25780	*
25781	* @example
25782	* Find all features at a point
25783	* ```ts
25784	* let features = map.queryRenderedFeatures(
25785	*   [20, 35],
25786	*   { layers: ['my-layer-name'] }
25787	* );
25788	* ```
25789	*
25790	* @example
25791	* Find all features within a static bounding box
25792	* ```ts
25793	* let features = map.queryRenderedFeatures(
25794	*   [[10, 20], [30, 50]],
25795	*   { layers: ['my-layer-name'] }
25796	* );
25797	* ```
25798	*
25799	* @example
25800	* Find all features within a bounding box around a point
25801	* ```ts
25802	* let width = 10;
25803	* let height = 20;
25804	* let features = map.queryRenderedFeatures([
25805	*   [point.x - width / 2, point.y - height / 2],
25806	*   [point.x + width / 2, point.y + height / 2]
25807	* ], { layers: ['my-layer-name'] });
25808	* ```
25809	*
25810	* @example
25811	* Query all rendered features from a single layer
25812	* ```ts
25813	* let features = map.queryRenderedFeatures({ layers: ['my-layer-name'] });
25814	* ```
25815	* @see [Get features under the mouse pointer](https://maplibre.org/maplibre-gl-js/docs/examples/get-features-under-the-mouse-pointer/)
25816	*/
25817	queryRenderedFeatures(geometryOrOptions, options) {
25818		if (!this.style) return [];
25819		let queryGeometry;
25820		const isGeometry = geometryOrOptions instanceof Point || Array.isArray(geometryOrOptions);
25821		const geometry = isGeometry ? geometryOrOptions : [[0, 0], [this._camera.transform.width, this._camera.transform.height]];
25822		options ||= (isGeometry ? {} : geometryOrOptions) || {};
25823		if (geometry instanceof Point || typeof geometry[0] === "number") queryGeometry = [Point.convert(geometry)];
25824		else {
25825			const tl = Point.convert(geometry[0]);
25826			const br = Point.convert(geometry[1]);
25827			queryGeometry = [
25828				tl,
25829				new Point(br.x, tl.y),
25830				br,
25831				new Point(tl.x, br.y),
25832				tl
25833			];
25834		}
25835		return this.style.queryRenderedFeatures(queryGeometry, options, this._camera.transform);
25836	}
25837	/**
25838	* Returns an array of MapGeoJSONFeature objects
25839	* representing features within the specified vector tile or GeoJSON source that satisfy the query parameters.
25840	*
25841	* @param sourceId - The ID of the vector tile or GeoJSON source to query.
25842	* @param parameters - The options object.
25843	* @returns An array of MapGeoJSONFeature objects.
25844	*
25845	* In contrast to {@link Map.queryRenderedFeatures}, this function returns all features matching the query parameters,
25846	* whether or not they are rendered by the current style (i.e. visible). The domain of the query includes all currently-loaded
25847	* vector tiles and GeoJSON source tiles: this function does not check tiles outside the currently
25848	* visible viewport.
25849	*
25850	* Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature
25851	* geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple
25852	* times in query results. For example, suppose there is a highway running through the bounding rectangle of a query.
25853	* The results of the query will be those parts of the highway that lie within the map tiles covering the bounding
25854	* rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile
25855	* will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple
25856	* tiles due to tile buffering.
25857	*
25858	* @example
25859	* Find all features in one source layer in a vector source
25860	* ```ts
25861	* let features = map.querySourceFeatures('your-source-id', {
25862	*   sourceLayer: 'your-source-layer'
25863	* });
25864	* ```
25865	*
25866	*/
25867	querySourceFeatures(sourceId, parameters) {
25868		return this.style.querySourceFeatures(sourceId, parameters);
25869	}
25870	/**
25871	* Updates the map's MapLibre style object with a new value.
25872	*
25873	* If a style is already set when this is used and options.diff is set to true, the map renderer will attempt to compare the given style
25874	* against the map's current state and perform only the changes necessary to make the map style match the desired state. Changes in sprites
25875	* (images used for icons and patterns) and glyphs (fonts for label text) **cannot** be diffed. If the sprites or fonts used in the current
25876	* style and the given style are different in any way, the map renderer will force a full update, remov
vendor: 8,825 bytes, lines 25876-26158
25876ing the current style and building
25877	* the given one from scratch.
25878	*
25879	*
25880	* @param style - A JSON object conforming to the schema described in the
25881	* [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/), or a URL to such JSON.
25882	* @param options - The options object.
25883	*
25884	* @example
25885	* ```ts
25886	* map.setStyle("https://demotiles.maplibre.org/style.json");
25887	*
25888	* map.setStyle('https://demotiles.maplibre.org/style.json', {
25889	*   transformStyle: (previousStyle, nextStyle) => ({
25890	*       ...nextStyle,
25891	*       sources: {
25892	*           ...nextStyle.sources,
25893	*           // copy a source from previous style
25894	*           'osm': previousStyle.sources.osm
25895	*       },
25896	*       layers: [
25897	*           // background layer
25898	*           nextStyle.layers[0],
25899	*           // copy a layer from previous style
25900	*           previousStyle.layers[0],
25901	*           // other layers from the next style
25902	*           ...nextStyle.layers.slice(1).map(layer => {
25903	*               // hide the layers we don't need from demotiles style
25904	*               if (layer.id.startsWith('geolines')) {
25905	*                   layer.layout = {...layer.layout || {}, visibility: 'none'};
25906	*               // filter out US polygons
25907	*               } else if (layer.id.startsWith('coastline') || layer.id.startsWith('countries')) {
25908	*                   layer.filter = ['!=', ['get', 'ADM0_A3'], 'USA'];
25909	*               }
25910	*               return layer;
25911	*           })
25912	*       ]
25913	*   })
25914	* });
25915	* ```
25916	*/
25917	setStyle(style, options) {
25918		options = extend({}, {
25919			localIdeographFontFamily: this._localIdeographFontFamily,
25920			validate: this._validateStyle
25921		}, options);
25922		this._styleUrl = typeof style === "string" ? style : null;
25923		if (options.diff !== false && options.localIdeographFontFamily === this._localIdeographFontFamily && this.style && style) {
25924			this._diffStyle(style, options);
25925			return this;
25926		} else {
25927			this._localIdeographFontFamily = options.localIdeographFontFamily;
25928			return this._updateStyle(style, options);
25929		}
25930	}
25931	/**
25932	*  Updates the requestManager's transform request with a new function
25933	*
25934	* @param transformRequest - A callback run before the Map makes a request for an external URL. The callback can be used to modify the url, set headers, or set the credentials property for cross-origin requests.
25935	* Expected to return an object with a `url` property and optionally `headers` and `credentials` properties
25936	*
25937	* @example
25938	* ```ts
25939	* map.setTransformRequest((url: string, resourceType: string) => {});
25940	* ```
25941	*/
25942	setTransformRequest(transformRequest) {
25943		this._requestManager.setTransformRequest(transformRequest);
25944		return this;
25945	}
25946	_getUIString(key) {
25947		const str = this._locale[key];
25948		if (str == null) throw new Error(`Missing UI string '${key}'`);
25949		return str;
25950	}
25951	_updateStyle(style, options) {
25952		this._diffStyleRequest?.abort();
25953		this._diffStyleRequest = null;
25954		if (options.transformStyle && this.style && !this.style._loaded) {
25955			this.style.once("style.load", () => this._updateStyle(style, options));
25956			return;
25957		}
25958		const previousStyle = this.style && options.transformStyle ? this.style.serialize() : void 0;
25959		if (this.style) {
25960			this.style.setEventedParent(null);
25961			this.style._remove(!style);
25962		}
25963		if (!style) {
25964			if (this._frameRequest) {
25965				this._frameRequest.abort();
25966				this._frameRequest = null;
25967			}
25968			this.style?.projection?.destroy();
25969			delete this.style;
25970			return this;
25971		} else this.style = new Style(this, options || {});
25972		this.style.setEventedParent(this, { style: this.style });
25973		if (typeof style === "string") this.style.loadURL(style, options, previousStyle);
25974		else this.style.loadJSON(style, options, previousStyle);
25975		return this;
25976	}
25977	_lazyInitEmptyStyle() {
25978		if (!this.style) {
25979			this.style = new Style(this, {});
25980			this.style.setEventedParent(this, { style: this.style });
25981			this.style.loadEmpty();
25982		}
25983	}
25984	async _diffStyle(style, options) {
25985		this._diffStyleRequest?.abort();
25986		if (typeof style === "string") {
25987			const url = style;
25988			this._diffStyleRequest = new AbortController();
25989			const abortController = this._diffStyleRequest;
25990			try {
25991				const request = await this._requestManager.transformRequest(url, "Style");
25992				if (abortController.signal.aborted) {
25993					this._diffStyleRequest = null;
25994					return;
25995				}
25996				const response = await getJSON(request, abortController);
25997				this._diffStyleRequest = null;
25998				this._updateDiff(response.data, options);
25999			} catch (error) {
26000				this._diffStyleRequest = null;
26001				if (!isAbortError(error)) this.fire(new ErrorEvent(ensureError(error)));
26002			}
26003		} else if (typeof style === "object") {
26004			this._diffStyleRequest = null;
26005			this._updateDiff(style, options);
26006		}
26007	}
26008	_updateDiff(style, options) {
26009		try {
26010			if (this.style.setState(style, options)) this._update(true);
26011		} catch (e) {
26012			warnOnce(`Unable to perform style diff: ${ensureError(e).message}.  Rebuilding the style from scratch.`);
26013			this._updateStyle(style, options);
26014		}
26015	}
26016	/**
26017	* Returns the map's MapLibre style object, a JSON object which can be used to recreate the map's style.
26018	*
26019	* @returns The map's style JSON object.
26020	*
26021	* @example
26022	* ```ts
26023	* let styleJson = map.getStyle();
26024	* ```
26025	*
26026	*/
26027	getStyle() {
26028		if (this.style) return this.style.serialize();
26029	}
26030	/**
26031	* Returns the URL the map's style was loaded from.
26032	*
26033	* @returns The URL given to {@link Map.setStyle} or the `style` map option, or `null` when the style was given as an object or the map has no style.
26034	*
26035	* @example
26036	* ```ts
26037	* const styleUrl = map.getStyleUrl();
26038	* ```
26039	*/
26040	getStyleUrl() {
26041		return this._styleUrl;
26042	}
26043	/**
26044	* @internal
26045	* Returns the map's style and cloned images to restore context.
26046	* @returns An object containing the style and images.
26047	*/
26048	_getStyleAndImages() {
26049		if (this.style) return {
26050			style: this.style.serialize(),
26051			images: this.style.imageManager.cloneImages()
26052		};
26053		return {
26054			style: null,
26055			images: {}
26056		};
26057	}
26058	/**
26059	* Returns a Boolean indicating whether the map's style is fully loaded.
26060	*
26061	* @returns A Boolean indicating whether the style is fully loaded.
26062	*
26063	* @example
26064	* ```ts
26065	* let styleLoadStatus = map.isStyleLoaded();
26066	* ```
26067	*/
26068	isStyleLoaded() {
26069		if (!this.style) {
26070			warnOnce("There is no style added to the map.");
26071			return;
26072		}
26073		return this.style.loaded();
26074	}
26075	/**
26076	* Adds a source to the map's style.
26077	*
26078	* Events triggered:
26079	*
26080	* Triggers the `source.add` event.
26081	*
26082	* @param id - The ID of the source to add. Must not conflict with existing sources.
26083	* @param source - The source object, conforming to the
26084	* MapLibre Style Specification's [source definition](https://maplibre.org/maplibre-style-spec/sources) or
26085	* {@link CanvasSourceSpecification}.
26086	* @example
26087	* ```ts
26088	* map.addSource('my-data', {
26089	*   type: 'vector',
26090	*   url: 'https://demotiles.maplibre.org/tiles/tiles.json'
26091	* });
26092	* ```
26093	* @example
26094	* ```ts
26095	* map.addSource('my-data', {
26096	*   "type": "geojson",
26097	*   "data": {
26098	*     "type": "Feature",
26099	*     "geometry": {
26100	*       "type": "Point",
26101	*       "coordinates": [-77.0396, 38.8891]
26102	*     },
26103	*     "properties": {
26104	*       "title": "Washington DC",
26105	*       "marker-symbol": "monument"
26106	*     }
26107	*   }
26108	* });
26109	* ```
26110	* @see GeoJSON source: [Add live realtime data](https://maplibre.org/maplibre-gl-js/docs/examples/add-live-realtime-data/)
26111	*/
26112	addSource(id, source) {
26113		this._lazyInitEmptyStyle();
26114		this.style.addSource(id, source);
26115		return this._update(true);
26116	}
26117	/**
26118	* Returns a Boolean indicating whether the source is loaded. Returns `true` if the source with
26119	* the given ID in the map's style has no outstanding network requests, otherwise `false`.
26120	*
26121	* A {@link ErrorEvent} event will be fired if there is no source with the specified ID.
26122	*
26123	* @param id - The ID of the source to be checked.
26124	* @returns A Boolean indicating whether the source is loaded.
26125	* @example
26126	* ```ts
26127	* let sourceLoaded = map.isSourceLoaded('bathymetry-data');
26128	* ```
26129	*/
26130	isSourceLoaded(id) {
26131		const tileManager = this.style?.tileManagers[id];
26132		if (tileManager === void 0) {
26133			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`There is no tile manager with ID '${id}'`)));
26134			return;
26135		}
26136		return tileManager.loaded();
26137	}
26138	/**
26139	* Loads a 3D terrain mesh, based on a "raster-dem" source.
26140	*
26141	* Triggers the `terrain` event.
26142	*
26143	* @param options - Options object.
26144	* @example
26145	* ```ts
26146	* map.setTerrain({ source: 'terrain' });
26147	* ```
26148	*/
26149	setTerrain(options, styleOptions = {}) {
26150		this.style._checkLoaded();
26151		if (options && validateAndEmit(this, validateStyle.terrain, { value: options }, styleOptions)) return this;
26152		if (this._terrainDataCallback) this.style.off("data", this._terrainDataCallback);
26153		if (!options) {
26154			if (this.terrain) this.terrain.destroy();
26155			this.terrain = null;
26156			this.painter.renderToTexture = null;
26157			this.painter.destroyRTTResources();
26158			this._camera.
26158terrain = null;
26159			this._camera.transform.setMinElevationForCurrentTile(0);
26160			if (this.getCenterClampedToGround()) this._camera.transform.setElevation(0);
26161		} else {
26162			const tileManager = this.style.tileManagers[options.source];
26163			if (!tileManager) throw new Error(`cannot load terrain, because there exists no source with ID: ${options.source}`);
26164			if (this.terrain === null) tileManager.reload();
26165			for (const index in this.style._layers) {
26166				const thisLayer = this.style._layers[index];
26167				if (thisLayer.type === "hillshade" && thisLayer.source === options.source) warnOnce("You are using the same source for a hillshade layer and for 3D terrain. Please consider using two separate sources to improve rendering quality.");
26168				if (thisLayer.type === "color-relief" && thisLayer.source === options.source) warnOnce("You are using the same source for a color-relief layer and for 3D terrain. Please consider using two separate sources to improve rendering quality.");
26169			}
26170			if (this.terrain) this.terrain.destroy();
26171			this.terrain = new Terrain(this.painter, tileManager, options, this._terrainSkirtLength);
26172			this.painter.renderToTexture = new RenderToTexture(this.painter, this.terrain);
26173			this._camera.terrain = this.terrain;
26174			this._camera.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._camera.transform.center, this._camera.transform.tileZoom));
26175			this._camera.transform.setElevation(this.terrain.getElevationForLngLat(this._camera.transform.center, this._camera.transform));
26176			this._terrainDataCallback = (e) => this._handleTerrainDataEvent(e, options.source);
26177			this.style.on("data", this._terrainDataCallback);
26178		}
26179		this.style.triggerSymbolPlacement();
26180		this.fire(new MapTerrainEvent({ terrain: options }));
26181		return this;
26182	}
26183	_handleTerrainDataEvent(event, terrainSourceId) {
26184		if (event.dataType === "style") {
26185			this.terrain.tileManager.releaseAllRTT();
26186			return;
26187		}
26188		const isTerrainSourceEvent = event.sourceId === terrainSourceId;
26189		if (isTerrainSourceEvent) {
26190			this.terrain.resetElevationCache();
26191			this.style.triggerSymbolPlacement();
26192		}
26193		if (isTerrainSourceEvent && event.tile) this.painter.markTerrainDepthDirty();
26194		if (isTerrainSourceEvent && event.tile && !this._camera.elevationFreeze) {
26195			this._camera.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._camera.transform.center, this._camera.transform.tileZoom));
26196			if (this.getCenterClampedToGround()) this._camera.transform.setElevation(this.terrain.getElevationForLngLat(this._camera.transform.center, this._camera.transform)
vendor: 35,539 bytes, lines 26196-27126
26196);
26197		}
26198		if (!event.tile) return;
26199		if (event.source?.type === "image") {
26200			this.terrain.tileManager.releaseAllRTT();
26201			return;
26202		}
26203		this.terrain.tileManager.releaseRTT(event.tile.tileID);
26204	}
26205	/**
26206	* Get the terrain-options if terrain is loaded
26207	* @returns the TerrainSpecification passed to setTerrain
26208	* @example
26209	* ```ts
26210	* map.getTerrain(); // { source: 'terrain' };
26211	* ```
26212	*/
26213	getTerrain() {
26214		return this.terrain?.options ?? null;
26215	}
26216	/**
26217	* Returns a Boolean indicating whether all tiles in the viewport from all sources on
26218	* the style are loaded.
26219	*
26220	* @returns A Boolean indicating whether all tiles are loaded.
26221	* @example
26222	* ```ts
26223	* let tilesLoaded = map.areTilesLoaded();
26224	* ```
26225	*/
26226	areTilesLoaded() {
26227		const tileManagers = this.style?.tileManagers;
26228		for (const tileManager of Object.values(tileManagers)) if (!tileManager.areTilesLoaded()) return false;
26229		return true;
26230	}
26231	/**
26232	* Removes a source from the map's style.
26233	*
26234	* @param id - The ID of the source to remove.
26235	* @example
26236	* ```ts
26237	* map.removeSource('bathymetry-data');
26238	* ```
26239	*/
26240	removeSource(id) {
26241		this.style.removeSource(id);
26242		return this._update(true);
26243	}
26244	/**
26245	* Returns the source with the specified ID in the map's style.
26246	*
26247	* This method is often used to update a source using the instance members for the relevant
26248	* source type as defined in classes that derive from {@link Source}.
26249	* For example, setting the `data` for a GeoJSON source or updating the `url` and `coordinates`
26250	* of an image source.
26251	*
26252	* @param id - The ID of the source to get.
26253	* @returns The style source with the specified ID or `undefined` if the ID
26254	* corresponds to no existing sources.
26255	* The shape of the object varies by source type.
26256	* A list of options for each source type is available on the MapLibre Style Specification's
26257	* [Sources](https://maplibre.org/maplibre-style-spec/sources/) page.
26258	* @example
26259	* ```ts
26260	* let sourceObject = map.getSource('points');
26261	* ```
26262	* @see [Create a draggable point](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-draggable-point/)
26263	* @see [Animate a point](https://maplibre.org/maplibre-gl-js/docs/examples/animate-a-point/)
26264	* @see [Add live realtime data](https://maplibre.org/maplibre-gl-js/docs/examples/add-live-realtime-data/)
26265	*/
26266	getSource(id) {
26267		return this.style?.getSource(id);
26268	}
26269	/**
26270	* Change the tile Level of Detail behavior of the specified source. These parameters have no effect when
26271	* pitch == 0, and the largest effect when the horizon is visible on screen.
26272	*
26273	* @param maxZoomLevelsOnScreen - The maximum number of distinct zoom levels allowed on screen at a time.
26274	* There will generally be fewer zoom levels on the screen, the maximum can only be reached when the horizon
26275	* is at the top of the screen. Increasing the maximum number of zoom levels causes the zoom level to decay
26276	* faster toward the horizon.
26277	* @param tileCountMaxMinRatio - The ratio of the maximum number of tiles loaded (at high pitch) to the minimum
26278	* number of tiles loaded. Increasing this ratio allows more tiles to be loaded at high pitch angles. If the ratio
26279	* would otherwise be exceeded, the zoom level is reduced uniformly to keep the number of tiles within the limit.
26280	* @param sourceId - The ID of the source to set tile LOD parameters for. All sources will be updated if unspecified.
26281	* If `sourceId` is specified but a corresponding source does not exist, an error is thrown.
26282	* @example
26283	* ```ts
26284	* map.setSourceTileLodParams(4.0, 3.0, 'terrain');
26285	* ```
26286	* @see [Modify Level of Detail behavior](https://maplibre.org/maplibre-gl-js/docs/examples/level-of-detail-control/)
26287	
26288	*/
26289	setSourceTileLodParams(maxZoomLevelsOnScreen, tileCountMaxMinRatio, sourceId) {
26290		if (sourceId) {
26291			const source = this.getSource(sourceId);
26292			if (!source) throw new Error(`There is no source with ID "${sourceId}", cannot set LOD parameters`);
26293			source.calculateTileZoom = createCalculateTileZoomFunction(Math.max(1, maxZoomLevelsOnScreen), Math.max(1, tileCountMaxMinRatio));
26294		} else for (const id in this.style.tileManagers) this.style.tileManagers[id].getSource().calculateTileZoom = createCalculateTileZoomFunction(Math.max(1, maxZoomLevelsOnScreen), Math.max(1, tileCountMaxMinRatio));
26295		this._update(true);
26296		return this;
26297	}
26298	/**
26299	* Triggers a reload of the selected tiles
26300	*
26301	* @param sourceId - The ID of the source
26302	* @param tileIds - An array of tile IDs to be reloaded. If not defined, all tiles will be reloaded.
26303	* @example
26304	* ```ts
26305	* map.refreshTiles('satellite', [{x:1024, y: 1023, z: 11}, {x:1023, y: 1023, z: 11}]);
26306	* ```
26307	*/
26308	refreshTiles(sourceId, tileIds) {
26309		const tileManager = this.style.tileManagers[sourceId];
26310		if (!tileManager) throw new Error(`There is no tile manager with ID "${sourceId}", cannot refresh tile`);
26311		if (tileIds === void 0) tileManager.reload(true);
26312		else tileManager.refreshTiles(tileIds.map((tileId) => {
26313			return new CanonicalTileID(tileId.z, tileId.x, tileId.y);
26314		}));
26315	}
26316	/**
26317	* Add an image to the style. This image can be displayed on the map like any other icon in the style's
26318	* sprite using the image's ID with
26319	* [`icon-image`](https://maplibre.org/maplibre-style-spec/layers/#layout-symbol-icon-image),
26320	* [`background-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-background-background-pattern),
26321	* [`fill-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-fill-fill-pattern),
26322	* or [`line-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-line-line-pattern).
26323	*
26324	* A {@link ErrorEvent} event will be fired if the image parameter is invalid or there is not enough space in the sprite to add this image.
26325	*
26326	* @param id - The ID of the image.
26327	* @param image - The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data`
26328	* properties with the same format as `ImageData`.
26329	* @param options - Options object.
26330	* @example
26331	* ```ts
26332	* // If the style's sprite does not already contain an image with ID 'cat',
26333	* // add the image 'cat-icon.png' to the style's sprite with the ID 'cat'.
26334	* const image = await map.loadImage('https://upload.wikimedia.org/wikipedia/commons/thumb/6/60/Cat_silhouette.svg/400px-Cat_silhouette.svg.png');
26335	* if (!map.hasImage('cat')) map.addImage('cat', image.data);
26336	*
26337	* // Add a stretchable image that can be used with `icon-text-fit`
26338	* // In this example, the image is 600px wide by 400px high.
26339	* const image = await map.loadImage('https://upload.wikimedia.org/wikipedia/commons/8/89/Black_and_White_Boxed_%28bordered%29.png');
26340	* if (map.hasImage('border-image')) return;
26341	* map.addImage('border-image', image.data, {
26342	*     content: [16, 16, 300, 384], // place text over left half of image, avoiding the 16px border
26343	*     stretchX: [[16, 584]], // stretch everything horizontally except the 16px border
26344	*     stretchY: [[16, 384]], // stretch everything vertically except the 16px border
26345	* });
26346	* ```
26347	* @see Use `HTMLImageElement`: [Add an icon to the map](https://maplibre.org/maplibre-gl-js/docs/examples/add-an-icon-to-the-map/)
26348	* @see Use `ImageData`: [Add a generated icon to the map](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-generated-icon-to-the-map/)
26349	*/
26350	addImage(id, image, options = {}) {
26351		this._lazyInitEmptyStyle();
26352		const styleImage = this._createStyleImage(image, options);
26353		if (!styleImage) return this;
26354		this.style.addImage(id, styleImage);
26355		if (styleImage.userImage?.onAdd) styleImage.userImage.onAdd(this, id);
26356		return this;
26357	}
26358	/**
26359	* Sets a callback that is invoked when an icon or pattern needed by the style is missing.
26360	*
26361	* The resolver typically loads or generates the image and registers it with {@link Map.addImage}.
26362	* MapLibre awaits the returned promise before treating the image as missing, so async work is
26363	* supported. If the image is still missing afterwards, the `styleimagemissing` event is fired.
26364	*
26365	* @param resolver - Callback used to resolve missing images, or `null` to remove the resolver.
26366	* @example
26367	* ```ts
26368	* map.setMissingStyleImageResolver(async (id) => {
26369	*     const response = await fetch(`/icons/${id}.png`);
26370	*     const image = await createImageBitmap(await response.blob());
26371	*     map.addImage(id, image, {pixelRatio: 2});
26372	* });
26373	* ```
26374	*/
26375	setMissingStyleImageResolver(resolver) {
26376		this._missingStyleImageResolver = resolver;
26377		this.style?.setMissingImageResolver(resolver);
26378		return this;
26379	}
26380	_createStyleImage(image, options = {}) {
26381		const { pixelRatio = 1, sdf = false, stretchX, stretchY, content, textFitWidth, textFitHeight } = options;
26382		const version = 0;
26383		if (image instanceof HTMLImageElement || isImageBitmap(image)) {
26384			const { width, height, data } = browser.getImageData(image);
26385			return {
26386				data: new RGBAImage({
26387					width,
26388					height
26389				}, data),
26390				pixelRatio,
26391				stretchX,
26392				stretchY,
26393				content,
26394				textFitWidth,
26395				textFitHeight,
26396				sdf,
26397				version
26398			};
26399		} else if (image.width === void 0 || image.height === void 0) {
26400			this.fire(new ErrorEvent(/* @__PURE__ */ 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`")));
26401			return null;
26402		} else {
26403			const { width, height, data } = image;
26404			const userImage = image;
26405			const isWebGLImage = isStyleImageWebGLData(userImage.data);
26406			return {
26407				data: isWebGLImage ? new RGBAImage({
26408					width,
26409					height
26410				}) : new RGBAImage({
26411					width,
26412					height
26413				}, new Uint8Array(data)),
26414				pixelRatio,
26415				stretchX,
26416				stretchY,
26417				content,
26418				textFitWidth,
26419				textFitHeight,
26420				sdf,
26421				version,
26422				isWebGLImage,
26423				userImage
26424			};
26425		}
26426	}
26427	/**
26428	* Update an existing image in a style. This image can be displayed on the map like any other icon in the style's
26429	* sprite using the image's ID with
26430	* [`icon-image`](https://maplibre.org/maplibre-style-spec/layers/#layout-symbol-icon-image),
26431	* [`background-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-background-background-pattern),
26432	* [`fill-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-fill-fill-pattern),
26433	* or [`line-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-line-line-pattern).
26434	*
26435	* An {@link ErrorEvent} will be fired if the image parameter is invalid.
26436	*
26437	* @param id - The ID of the image.
26438	* @param image - The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data`
26439	* properties with the same format as `ImageData`.
26440	* @example
26441	* ```ts
26442	* // If an image with the ID 'cat' already exists in the style's sprite,
26443	* // replace that image with a new image, 'other-cat-icon.png'.
26444	* if (map.hasImage('cat')) map.updateImage('cat', './other-cat-icon.png');
26445	* ```
26446	*/
26447	updateImage(id, image) {
26448		const existingImage = this.style.getImage(id);
26449		if (!existingImage) return this.fire(new ErrorEvent(/* @__PURE__ */ new Error("The map has no image with that id. If you are adding a new image use `map.addImage(...)` instead.")));
26450		const { width, height, data } = image instanceof HTMLImageElement || isImageBitmap(image) ? browser.getImageData(image) : image;
26451		if (width === void 0 || height === void 0) return this.fire(new ErrorEvent(/* @__PURE__ */ 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`")));
26452		if (width !== existingImage.data.width || height !== existingImage.data.height) return this.fire(new ErrorEvent(/* @__PURE__ */ new Error("The width and height of the updated image must be that same as the previous version of the image")));
26453		existingImage.isWebGLImage = isStyleImageWebGLData(data);
26454		if (existingImage.isWebGLImage) existingImage.userImage = image;
26455		else {
26456			const copy = !(image instanceof HTMLImageElement || isImageBitmap(image));
26457			existingImage.data.replace(data, copy);
26458		}
26459		this.style.updateImage(id, existingImage);
26460		return this;
26461	}
26462	/**
26463	* Returns an image, specified by ID, currently available in the map.
26464	* This includes both images from the style's original sprite
26465	* and any images that have been added at runtime using {@link Map.addImage}.
26466	*
26467	* @param id - The ID of the image.
26468	* @returns An image in the map with the specified ID.
26469	*
26470	* @example
26471	* ```ts
26472	* let coffeeShopIcon = map.getImage("coffee_cup");
26473	* ```
26474	*/
26475	getImage(id) {
26476		return this.style.getImage(id);
26477	}
26478	/**
26479	* Check whether or not an image with a specific ID exists in the style. This checks both images
26480	* in the style's original sprite and any images
26481	* that have been added at runtime using {@link Map.addImage}.
26482	*
26483	* An {@link ErrorEvent} will be fired if the image ID is missing.
26484	*
26485	* @param id - The ID of the image.
26486	*
26487	* @returns A Boolean indicating whether the image exists.
26488	* @example
26489	* Check if an image with the ID 'cat' exists in the style's sprite.
26490	* ```ts
26491	* let catIconExists = map.hasImage('cat');
26492	* ```
26493	*/
26494	hasImage(id) {
26495		if (!id) {
26496			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("Missing required image id")));
26497			return false;
26498		}
26499		return !!this.style.getImage(id);
26500	}
26501	/**
26502	* Remove an image from a style. This can be an image from the style's original
26503	* sprite or any images
26504	* that have been added at runtime using {@link Map.addImage}.
26505	*
26506	* @param id - The ID of the image.
26507	*
26508	* @example
26509	* ```ts
26510	* // If an image with the ID 'cat' exists in
26511	* // the style's sprite, remove it.
26512	* if (map.hasImage('cat')) map.removeImage('cat');
26513	* ```
26514	*/
26515	removeImage(id) {
26516		this.style.removeImage(id);
26517	}
26518	/**
26519	* Load an image from an external URL to be used with {@link Map.addImage}. External
26520	* domains must support [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS).
26521	*
26522	* @param url - The URL of the image file. Image file must be in png, webp, or jpg format.
26523	* @returns a promise that is resolved when the image is loaded, or rejected when the response has no image data (for example an HTTP 204)
26524	*
26525	* @example
26526	* Load an image from an external URL.
26527	* ```ts
26528	* const response = await map.loadImage('https://picsum.photos/50/50');
26529	* // Add the loaded image to the style's sprite with the ID 'photo'.
26530	* map.addImage('photo', response.data);
26531	* ```
26532	* @see [Add an icon to the map](https://maplibre.org/maplibre-gl-js/docs/examples/add-an-icon-to-the-map/)
26533	*/
26534	async loadImage(url) {
26535		const response = await ImageRequest.getImage(await this._requestManager.transformRequest(url, "Image"), new AbortController());
26536		if (!response.data) throw new Error(`Could not load image ${url}: the response is empty`);
26537		return response;
26538	}
26539	/**
26540	* Returns an Array of strings containing the IDs of all images currently available in the map.
26541	* This includes both images from the style's original sprite
26542	* and any images that have been added at runtime using {@link Map.addImage}.
26543	*
26544	* @returns An Array of strings containing the names of all sprites/images currently available in the map.
26545	*
26546	* @example
26547	* ```ts
26548	* let allImages = map.listImages();
26549	* ```
26550	*/
26551	listImages() {
26552		return this.style?.listImages() ?? [];
26553	}
26554	/**
26555	* Adds a [MapLibre style layer](https://maplibre.org/maplibre-style-spec/layers)
26556	* to the map's style.
26557	*
26558	* A layer defines how data from a specified source will be styled. Read more about layer types
26559	* and available paint and layout properties in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/layers).
26560	*
26561	* @param layer - The layer to add,
26562	* conforming to either the MapLibre Style Specification's [layer definition](https://maplibre.org/maplibre-style-spec/layers) or,
26563	* less commonly, the {@link CustomLayerInterface} specification. Can also be a layer definition with an embedded source definition.
26564	* The MapLibre Style Specification's layer definition is appropriate for most layers.
26565	*
26566	* @param beforeId - The ID of an existing layer to insert the new layer before,
26567	* resulting in the new layer appearing visually beneath the existing layer.
26568	* If this argument is not specified, the layer will be appended to the end of the layers array
26569	* and appear visually above all other layers.
26570	*
26571	* @example
26572	* Add a circle layer with a vector source
26573	* ```ts
26574	* map.addLayer({
26575	*   id: 'points-of-interest',
26576	*   source: {
26577	*     type: 'vector',
26578	*     url: 'https://demotiles.maplibre.org/tiles/tiles.json'
26579	*   },
26580	*   'source-layer': 'poi_label',
26581	*   type: 'circle',
26582	*   paint: {
26583	*     // MapLibre Style Specification paint properties
26584	*   },
26585	*   layout: {
26586	*     // MapLibre Style Specification layout properties
26587	*   }
26588	* });
26589	* ```
26590	*
26591	* @example
26592	* Define a source before using it to create a new layer
26593	* ```ts
26594	* map.addSource('state-data', {
26595	*   type: 'geojson',
26596	*   data: 'path/to/data.geojson'
26597	* });
26598	*
26599	* map.addLayer({
26600	*   id: 'states',
26601	*   // References the GeoJSON source defined above
26602	*   // and does not require a `source-layer`
26603	*   source: 'state-data',
26604	*   type: 'symbol',
26605	*   layout: {
26606	*     // Set the label content to the
26607	*     // feature's `name` property
26608	*     text-field: ['get', 'name']
26609	*   }
26610	* });
26611	* ```
26612	*
26613	* @example
26614	* Add a new symbol layer before an existing layer
26615	* ```ts
26616	* map.addLayer({
26617	*   id: 'states',
26618	*   // References a source that's already been defined
26619	*   source: 'state-data',
26620	*   type: 'symbol',
26621	*   layout: {
26622	*     // Set the label content to the
26623	*     // feature's `name` property
26624	*     text-field: ['get', 'name']
26625	*   }
26626	* // Add the layer before the existing `cities` layer
26627	* }, 'cities');
26628	* ```
26629	* @see [Create and style clusters](https://maplibre.org/maplibre-gl-js/docs/examples/create-and-style-clusters/)
26630	* @see [Add a vector tile source](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-vector-tile-source/)
26631	* @see [Add a WMS source](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-wms-source/)
26632	*/
26633	addLayer(layer, beforeId) {
26634		this._lazyInitEmptyStyle();
26635		this.style.addLayer(layer, beforeId);
26636		return this._update(true);
26637	}
26638	/**
26639	* Moves a layer to a different z-position.
26640	*
26641	* @param id - The ID of the layer to move.
26642	* @param beforeId - The ID of an existing layer to insert the new layer before. When viewing the map, the `id` layer will appear beneath the `beforeId` layer. If `beforeId` is omitted, the layer will be appended to the end of the layers array and appear above all other layers on the map.
26643	*
26644	* @example
26645	* Move a layer with ID 'polygon' before the layer with ID 'country-label'. The `polygon` layer will appear beneath the `country-label` layer on the map.
26646	* ```ts
26647	* map.moveLayer('polygon', 'country-label');
26648	* ```
26649	*/
26650	moveLayer(id, beforeId) {
26651		this.style.moveLayer(id, beforeId);
26652		return this._update(true);
26653	}
26654	/**
26655	* Removes the layer with the given ID from the map's style.
26656	*
26657	* An {@link ErrorEvent} will be fired if no such layer exists.
26658	*
26659	* @param id - The ID of the layer to remove
26660	*
26661	* @example
26662	* If a layer with ID 'state-data' exists, remove it.
26663	* ```ts
26664	* if (map.getLayer('state-data')) map.removeLayer('state-data');
26665	* ```
26666	*/
26667	removeLayer(id) {
26668		this.style.removeLayer(id);
26669		return this._update(true);
26670	}
26671	/**
26672	* Returns the layer with the specified ID in the map's style.
26673	*
26674	* @param id - The ID of the layer to get.
26675	* @returns The layer with the specified ID, or `undefined`
26676	* if the ID corresponds to no existing layers.
26677	*
26678	* @example
26679	* ```ts
26680	* let stateDataLayer = map.getLayer('state-data');
26681	* ```
26682	* @see [Filter symbols by toggling a list](https://maplibre.org/maplibre-gl-js/docs/examples/filter-symbols-by-toggling-a-list/)
26683	* @see [Filter symbols by text input](https://maplibre.org/maplibre-gl-js/docs/examples/filter-symbols-by-text-input/)
26684	*/
26685	getLayer(id) {
26686		return this.style?.getLayer(id);
26687	}
26688	/**
26689	* Return the ids of all layers currently in the style, including custom layers, in order.
26690	*
26691	* @returns ids of layers, in order
26692	*
26693	* @example
26694	* ```ts
26695	* const orderedLayerIds = map.getLayersOrder();
26696	* ```
26697	*/
26698	getLayersOrder() {
26699		return this.style?.getLayersOrder() ?? [];
26700	}
26701	/**
26702	* Sets the zoom extent for the specified style layer. The zoom extent includes the
26703	* [minimum zoom level](https://maplibre.org/maplibre-style-spec/layers/#minzoom)
26704	* and [maximum zoom level](https://maplibre.org/maplibre-style-spec/layers/#maxzoom))
26705	* at which the layer will be rendered.
26706	*
26707	* !!! note
26708	*     For style layers using vector sources, style layers cannot be rendered at zoom levels lower than the
26709	*     minimum zoom level of the _source layer_ because the data does not exist at those zoom levels. If the minimum
26710	*     zoom level of the source layer is higher than the minimum zoom level defined in the style layer, the style
26711	*     layer will not be rendered at all zoom levels in the zoom range.
26712	*
26713	* @param layerId - The ID of the layer to which the zoom extent will be applied.
26714	* @param minzoom - The minimum zoom to set (0-24).
26715	* @param maxzoom - The maximum zoom to set (0-24).
26716	*
26717	* @example
26718	* ```ts
26719	* map.setLayerZoomRange('my-layer', 2, 5);
26720	* ```
26721	*/
26722	setLayerZoomRange(layerId, minzoom, maxzoom) {
26723		this.style.setLayerZoomRange(layerId, minzoom, maxzoom);
26724		return this._update(true);
26725	}
26726	/**
26727	* Sets the filter for the specified style layer.
26728	*
26729	* Filters control which features a style layer renders from its source.
26730	* Any feature for which the filter expression evaluates to `true` will be
26731	* rendered on the map. Those that are false will be hidden.
26732	*
26733	* Use `setFilter` to show a subset of your source data.
26734	*
26735	* To clear the filter, pass `null` or `undefined` as the second parameter.
26736	*
26737	* @param layerId - The ID of the layer to which the filter will be applied.
26738	* @param filter - The filter, conforming to the MapLibre Style Specification's
26739	* [filter definition](https://maplibre.org/maplibre-style-spec/layers/#filter).  If `null` or `undefined` is provided, the function removes any existing filter from the layer.
26740	* @param options - Options object.
26741	*
26742	* @example
26743	* Display only features with the 'name' property 'USA'
26744	* ```ts
26745	* map.setFilter('my-layer', ['==', ['get', 'name'], 'USA']);
26746	* ```
26747	* @example
26748	* Display only features with five or more 'available-spots'
26749	* ```ts
26750	* map.setFilter('bike-docks', ['>=', ['get', 'available-spots'], 5]);
26751	* ```
26752	* @example
26753	* Remove the filter for the 'bike-docks' style layer
26754	* ```ts
26755	* map.setFilter('bike-docks', null);
26756	* ```
26757	* @see [Create a timeline animation](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-time-slider/)
26758	*/
26759	setFilter(layerId, filter, options = {}) {
26760		this.style?.setFilter(layerId, filter, options);
26761		return this._update(true);
26762	}
26763	/**
26764	* Returns the filter applied to the specified style layer.
26765	*
26766	* @param layerId - The ID of the style layer whose filter to get.
26767	* @returns The layer's filter.
26768	*/
26769	getFilter(layerId) {
26770		return this.style.getFilter(layerId);
26771	}
26772	/**
26773	* Sets the value of a paint property in the specified style layer.
26774	*
26775	* @param layerId - The ID of the layer to set the paint property in.
26776	* @param name - The name of the paint property to set.
26777	* @param value - The value of the paint property to set.
26778	* Must be of a type appropriate for the property, as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/).
26779	* Pass `null` to unset the existing value.
26780	* @param options - Options object.
26781	* @example
26782	* ```ts
26783	* map.setPaintProperty('my-layer', 'fill-color', '#faafee');
26784	* ```
26785	* @see [Change a layer's color with buttons](https://maplibre.org/maplibre-gl-js/docs/examples/change-a-layers-color-with-buttons/)
26786	* @see [Create a draggable point](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-draggable-point/)
26787	*/
26788	setPaintProperty(layerId, name, value, options = {}) {
26789		this.style?.setPaintProperty(layerId, name, value, options);
26790		return this._update(true);
26791	}
26792	/**
26793	* Returns the value of a paint property in the specified style layer.
26794	*
26795	* @param layerId - The ID of the layer to get the paint property from.
26796	* @param name - The name of a paint property to get.
26797	* @returns The value of the specified paint property.
26798	*/
26799	getPaintProperty(layerId, name) {
26800		return this.style.getPaintProperty(layerId, name);
26801	}
26802	/**
26803	* Sets the value of a layout property in the specified style layer.
26804	*
26805	* @param layerId - The ID of the layer to set the layout property in.
26806	* @param name - The name of the layout property to set.
26807	* @param value - The value of the layout property. Must be of a type appropriate for the property, as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/).
26808	* @param options - The options object.
26809	* @example
26810	* ```ts
26811	* map.setLayoutProperty('my-layer', 'visibility', 'none');
26812	* ```
26813	*/
26814	setLayoutProperty(layerId, name, value, options = {}) {
26815		this.style.setLayoutProperty(layerId, name, value, options);
26816		return this._update(true);
26817	}
26818	/**
26819	* Returns the value of a layout property in the specified style layer.
26820	*
26821	* @param layerId - The ID of the layer to get the layout property from.
26822	* @param name - The name of the layout property to get.
26823	* @returns The value of the specified layout property.
26824	*/
26825	getLayoutProperty(layerId, name) {
26826		return this.style.getLayoutProperty(layerId, name);
26827	}
26828	/**
26829	* Sets the value of the style's glyphs property. Pass a falsy value (null or undefined)
26830	* to unset glyphs.
26831	*
26832	* @param glyphsUrl - Glyph URL to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/glyphs/).
26833	* @param options - Options object.
26834	* @example
26835	* ```ts
26836	* map.setGlyphs('https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf');
26837	* ```
26838	*/
26839	setGlyphs(glyphsUrl, options = {}) {
26840		this._lazyInitEmptyStyle();
26841		this.style.setGlyphs(glyphsUrl, options);
26842		return this._update(true);
26843	}
26844	/**
26845	* Returns the value of the style's glyphs URL
26846	*
26847	* @returns glyphs Style's glyphs url, or `null` if glyphs are unset.
26848	*/
26849	getGlyphs() {
26850		return this.style.getGlyphsUrl();
26851	}
26852	/**
26853	* Sets the value of the style's `font-faces` property, which points at the font files used to
26854	* draw text that the style's `glyphs` URL does not cover. Pass a falsy value (null or undefined)
26855	* to unset it.
26856	*
26857	* The files are handed to the browser's CSS Font Loading API, so any format the browser can
26858	* render text with may be used, and requests for them go through `transformRequest` as glyph
26859	* requests do. Text is drawn a grapheme cluster at a time, so a letter and the marks written on
26860	* it are handed to the browser's text engine together and come back as the one shape they are
26861	* written as -- which is what a `glyphs` URL, serving one codepoint at a time, cannot do.
26862	*
26863	* @param fontFaces - The font faces to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/root/#font-faces).
26864	* A declaration this cannot make sense of is skipped with a warning, as is a font file that
26865	* fails to load, so the text it would have drawn falls back to the `glyphs` URL.
26866	* @example
26867	* ```ts
26868	* map.setFontFaces({
26869	*     'Noto Sans Regular': [
26870	*         {url: 'https://example.com/NotoSansKhmer-Regular.ttf', 'unicode-range': ['U+1780-17FF']}
26871	*     ]
26872	* });
26873	* ```
26874	*/
26875	setFontFaces(fontFaces) {
26876		this._lazyInitEmptyStyle();
26877		this.style.setFontFaces(fontFaces);
26878		return this._update(true);
26879	}
26880	/**
26881	* Returns the value of the style's `font-faces` property.
26882	*
26883	* @returns The style's font faces, or `null` if it declares none.
26884	*/
26885	getFontFaces() {
26886		return this.style.getFontFaces();
26887	}
26888	/**
26889	* Adds a sprite to the map's style. Fires the `style` event.
26890	*
26891	* @param id - The ID of the sprite to add. Must not conflict with existing sprites.
26892	* @param url - The URL to load the sprite from
26893	* @param options - Options object.
26894	* @example
26895	* ```ts
26896	* map.addSprite('sprite-two', 'http://example.com/sprite-two');
26897	* ```
26898	*/
26899	addSprite(id, url, options = {}) {
26900		this._lazyInitEmptyStyle();
26901		this.style.addSprite(id, url, options, (err) => {
26902			if (!err) this._update(true);
26903		});
26904		return this;
26905	}
26906	/**
26907	* Removes the sprite from the map's style. Fires the `style` event.
26908	*
26909	* @param id - The ID of the sprite to remove. If the sprite is declared as a single URL, the ID must be "default".
26910	* @example
26911	* ```ts
26912	* map.removeSprite('sprite-two');
26913	* map.removeSprite('default');
26914	* ```
26915	*/
26916	removeSprite(id) {
26917		this._lazyInitEmptyStyle();
26918		this.style.removeSprite(id);
26919		return this._update(true);
26920	}
26921	/**
26922	* Returns the as-is value of the style's sprite.
26923	*
26924	* @returns style's sprite list of id-url pairs
26925	*/
26926	getSprite() {
26927		return this.style.getSprite();
26928	}
26929	/**
26930	* Sets the value of the style's sprite property.
26931	*
26932	* @param spriteUrl - Sprite URL to set.
26933	* @param options - Options object.
26934	* @example
26935	* ```ts
26936	* map.setSprite('YOUR_SPRITE_URL');
26937	* ```
26938	*/
26939	setSprite(spriteUrl, options = {}) {
26940		this._lazyInitEmptyStyle();
26941		this.style.setSprite(spriteUrl, options, (err) => {
26942			if (!err) this._update(true);
26943		});
26944		return this;
26945	}
26946	/**
26947	* Sets the any combination of light values.
26948	*
26949	* @param light - Light properties to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/light).
26950	* @param options - Options object.
26951	*
26952	* @example
26953	* ```ts
26954	* let layerVisibility = map.getLayoutProperty('my-layer', 'visibility');
26955	* ```
26956	*/
26957	setLight(light, options = {}) {
26958		this._lazyInitEmptyStyle();
26959		this.style.setLight(light, options);
26960		return this._update(true);
26961	}
26962	/**
26963	* Returns the value of the light object.
26964	*
26965	* @returns light Light properties of the style.
26966	*/
26967	getLight() {
26968		return this.style.getLight();
26969	}
26970	/**
26971	* Sets the value of style's sky properties.
26972	*
26973	* @param sky - Sky properties to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/sky/).
26974	* @param options - Options object.
26975	*
26976	* @example
26977	* ```ts
26978	* map.setSky({'atmosphere-blend': 1.0});
26979	* ```
26980	*/
26981	setSky(sky, options = {}) {
26982		this._lazyInitEmptyStyle();
26983		this.style.setSky(sky, options);
26984		return this._update(true);
26985	}
26986	/**
26987	* Returns the value of the style's sky.
26988	*
26989	* @returns the sky properties of the style.
26990	* @example
26991	* ```ts
26992	* map.getSky();
26993	* ```
26994	*/
26995	getSky() {
26996		return this.style.getSky();
26997	}
26998	/**
26999	* Sets the `state` of a feature.
27000	* A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime.
27001	* When using this method, the `state` object is merged with any existing key-value pairs in the feature's state.
27002	* Features are identified by their `feature.id` attribute, which can be any number or string.
27003	*
27004	* This method can only be used with sources that have a `feature.id` attribute. The `feature.id` attribute can be defined in three ways:
27005	*
27006	* - For vector or GeoJSON sources, including an `id` attribute in the original data file.
27007	* - For vector or GeoJSON sources, using the [`promoteId`](https://maplibre.org/maplibre-style-spec/sources/#promoteid) option at the time the source is defined.
27008	* - For GeoJSON sources, using the [`generateId`](https://maplibre.org/maplibre-style-spec/sources/#generateid) option to auto-assign an `id` based on the feature's index in the source data. If you change feature data using `map.getSource('some id').setData(..)`, you may need to re-apply state taking into account updated `id` values.
27009	*
27010	* !!! note
27011	*     You can use the [`feature-state` expression](https://maplibre.org/maplibre-style-spec/expressions/#feature-state) to access the values in a feature's state object for the purposes of styling.
27012	*
27013	* @param feature - Feature identifier. Feature objects returned from
27014	* {@link Map.queryRenderedFeatures} or event handlers can be used as feature identifiers.
27015	* @param state - A set of key-value pairs. The values should be valid JSON types.
27016	*
27017	* @example
27018	* ```ts
27019	* // When the mouse moves over the `my-layer` layer, update
27020	* // the feature state for the feature under the mouse
27021	* map.on('mousemove', 'my-layer', (e) => {
27022	*   if (e.features.length > 0) {
27023	*     map.setFeatureState({
27024	*       source: 'my-source',
27025	*       sourceLayer: 'my-source-layer',
27026	*       id: e.features[0].id,
27027	*     }, {
27028	*       hover: true
27029	*     });
27030	*   }
27031	* });
27032	* ```
27033	* @see [Create a hover effect](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-hover-effect/)
27034	*/
27035	setFeatureState(feature, state) {
27036		this.style.setFeatureState(feature, state);
27037		return this._update();
27038	}
27039	/**
27040	* Removes the `state` of a feature, setting it back to the default behavior.
27041	* If only a `target.source` is specified, it will remove the state for all features from that source.
27042	* If `target.id` is also specified, it will remove all keys for that feature's state.
27043	* If `key` is also specified, it removes only that key from that feature's state.
27044	* Features are identified by their `feature.id` attribute, which can be any number or string.
27045	*
27046	* @param target - Identifier of where to remove state. It can be a source, a feature, or a specific key of feature.
27047	* Feature objects returned from {@link Map.queryRenderedFeatures} or event handlers can be used as feature identifiers.
27048	* @param key - (optional) The key in the feature state to reset.
27049	* @example
27050	* Reset the entire state object for all features in the `my-source` source
27051	* ```ts
27052	* map.removeFeatureState({
27053	*   source: 'my-source'
27054	* });
27055	* ```
27056	*
27057	* @example
27058	* When the mouse leaves the `my-layer` layer,
27059	* reset the entire state object for the
27060	* feature under the mouse
27061	* ```ts
27062	* map.on('mouseleave', 'my-layer', (e) => {
27063	*   map.removeFeatureState({
27064	*     source: 'my-source',
27065	*     sourceLayer: 'my-source-layer',
27066	*     id: e.features[0].id
27067	*   });
27068	* });
27069	* ```
27070	*
27071	* @example
27072	* When the mouse leaves the `my-layer` layer,
27073	* reset only the `hover` key-value pair in the
27074	* state for the feature under the mouse
27075	* ```ts
27076	* map.on('mouseleave', 'my-layer', (e) => {
27077	*   map.removeFeatureState({
27078	*     source: 'my-source',
27079	*     sourceLayer: 'my-source-layer',
27080	*     id: e.features[0].id
27081	*   }, 'hover');
27082	* });
27083	* ```
27084	*/
27085	removeFeatureState(target, key) {
27086		this.style.removeFeatureState(target, key);
27087		return this._update();
27088	}
27089	/**
27090	* Gets the `state` of a feature.
27091	* A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime.
27092	* Features are identified by their `feature.id` attribute, which can be any number or string.
27093	*
27094	* !!! note
27095	*     To access the values in a feature's state object for the purposes of styling the feature, use the [`feature-state` expression](https://maplibre.org/maplibre-style-spec/expressions/#feature-state).
27096	*
27097	* @param feature - Feature identifier. Feature objects returned from
27098	* {@link Map.queryRenderedFeatures} or event handlers can be used as feature identifiers.
27099	* @returns The state of the feature: a set of key-value pairs that was assigned to the feature at runtime.
27100	*
27101	* @example
27102	* When the mouse moves over the `my-layer` layer,
27103	* get the feature state for the feature under the mouse
27104	* ```ts
27105	* map.on('mousemove', 'my-layer', (e) => {
27106	*   if (e.features.length > 0) {
27107	*     map.getFeatureState({
27108	*       source: 'my-source',
27109	*       sourceLayer: 'my-source-layer',
27110	*       id: e.features[0].id
27111	*     });
27112	*   }
27113	* });
27114	* ```
27115	*/
27116	getFeatureState(feature) {
27117		return this.style.getFeatureState(feature);
27118	}
27119	/**
27120	* Returns the map's containing HTML element.
27121	*
27122	* @returns The map's container.
27123	*/
27124	getContainer() {
27125		return this._container;
27126	}
vendor: 5,325 bytes, lines 27127-27251
27127	/**
27128	* Returns the HTML element containing the map's `<canvas>` element.
27129	*
27130	* If you want to add non-GL overlays to the map, you should append them to this element.
27131	*
27132	* This is the element to which event bindings for map interactivity (such as panning and zooming) are
27133	* attached. It will receive bubbled events from child elements such as the `<canvas>`, but not from
27134	* map controls.
27135	*
27136	* @returns The container of the map's `<canvas>`.
27137	* @see [Create a draggable point](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-draggable-point/)
27138	*/
27139	getCanvasContainer() {
27140		return this._canvasContainer;
27141	}
27142	/**
27143	* Returns the map's `<canvas>` element.
27144	*
27145	* @returns The map's `<canvas>` element.
27146	* @see [Measure distances](https://maplibre.org/maplibre-gl-js/docs/examples/measure-distances/)
27147	* @see [Display a popup on hover](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-hover/)
27148	* @see [Center the map on a clicked symbol](https://maplibre.org/maplibre-gl-js/docs/examples/center-the-map-on-a-clicked-symbol/)
27149	*/
27150	getCanvas() {
27151		return this._canvas;
27152	}
27153	_containerDimensions() {
27154		let width = 0;
27155		let height = 0;
27156		if (this._container) {
27157			width = this._container.clientWidth || 400;
27158			height = this._container.clientHeight || 300;
27159		}
27160		return [width, height];
27161	}
27162	/**
27163	* Determines if the initial resize event should be handled based on the container's dimensions.
27164	*
27165	* @returns `true` if the initial resize event should be handled, `false` otherwise.
27166	*/
27167	_shouldHandleInitialResize() {
27168		if (!this._container?.clientWidth || !this._container.clientHeight) return false;
27169		const [width, height] = this._containerDimensions();
27170		return width !== this._camera.transform.width || height !== this._camera.transform.height;
27171	}
27172	/**
27173	* @internal
27174	* Sets up the ResizeObserver to track container size changes.
27175	* Uses the owning window's ResizeObserver for cross-window support.
27176	*/
27177	_setupResizeObserver() {
27178		let initialResizeEventCaptured = false;
27179		const throttledResizeCallback = throttle((entries) => {
27180			if (this._trackResize && !this._removed) {
27181				this.resize(entries);
27182				this.redraw();
27183			}
27184		}, 50);
27185		const ResizeObserverClass = this._ownerWindow.ResizeObserver ?? ResizeObserver;
27186		this._resizeObserver = new ResizeObserverClass((entries) => {
27187			if (!initialResizeEventCaptured) {
27188				initialResizeEventCaptured = true;
27189				if (!this._shouldHandleInitialResize()) return;
27190			}
27191			throttledResizeCallback(entries);
27192		});
27193		this._resizeObserver.observe(this._container);
27194	}
27195	/**
27196	* @internal
27197	* Resolves the container option to an HTMLElement.
27198	* Supports string ID, HTMLElement, or cross-window elements (using nodeType check).
27199	*/
27200	_resolveContainer(container) {
27201		if (typeof container === "string") {
27202			const element = document.getElementById(container);
27203			if (!element) throw new Error(`Container '${container}' not found.`);
27204			return element;
27205		}
27206		if (container instanceof HTMLElement) return container;
27207		if (container && typeof container === "object" && container.nodeType === 1) return container;
27208		throw new Error("Invalid type: 'container' must be a String or HTMLElement.");
27209	}
27210	_setupContainer() {
27211		const dimensions = this._containerDimensions();
27212		const clampedPixelRatio = this._getClampedPixelRatio(dimensions[0], dimensions[1]);
27213		const container = this._container;
27214		container.classList.add("maplibregl-map");
27215		const canvasContainer = this._canvasContainer = DOM.create("div", "maplibregl-canvas-container", container);
27216		if (this._interactive) canvasContainer.classList.add("maplibregl-interactive");
27217		this._canvas = DOM.create("canvas", "maplibregl-canvas", canvasContainer);
27218		this._canvas.addEventListener("webglcontextlost", this._contextLost, false);
27219		this._canvas.addEventListener("webglcontextrestored", this._contextRestored, false);
27220		this._canvas.setAttribute("tabindex", this._interactive ? "0" : "-1");
27221		this._canvas.setAttribute("aria-label", this._getUIString("Map.Title"));
27222		this._canvas.setAttribute("role", "region");
27223		this._resizeCanvas(dimensions[0], dimensions[1], clampedPixelRatio);
27224		const controlContainer = this._controlContainer = DOM.create("div", "maplibregl-control-container", container);
27225		const positions = this._controlPositions = {};
27226		for (const positionName of [
27227			"top-left",
27228			"top-right",
27229			"bottom-left",
27230			"bottom-right"
27231		]) positions[positionName] = DOM.create("div", `maplibregl-ctrl-${positionName} `, controlContainer);
27232		this._container.addEventListener("scroll", this._onMapScroll, false);
27233	}
27234	/**
27235	* @internal
27236	* Reverses the DOM mutations of {@link Map._setupContainer}, returning the container element to its pre-construction state.
27237	*/
27238	_cleanupContainer() {
27239		this._canvas.removeEventListener("webglcontextrestored", this._contextRestored, false);
27240		this._canvas.removeEventListener("webglcontextlost", this._contextLost, false);
27241		this._canvasContainer.remove();
27242		this._controlContainer.remove();
27243		this._container.removeEventListener("scroll", this._onMapScroll, false);
27244		this._container.classList.remove("maplibregl-map");
27245	}
27246	_resizeCanvas(width, height, pixelRatio) {
27247		this._canvas.width = Math.floor(pixelRatio * width);
27248		this._canvas.height = Math.floor(pixelRatio * height);
27249		this._canvas.style.width = `${width}px`;
27250		this._canvas.style.height = `${height}px`;
27251	}
vendor: 109,361 bytes, lines 27252-30142
27252	/**
27253	* @internal
27254	* Creates the WebGL2 context and the painter. Throws {@link GPUInitializationError} when the context cannot be created.
27255	*/
27256	_setupPainter() {
27257		const attributes = {
27258			...this._canvasContextAttributes,
27259			alpha: true,
27260			depth: true,
27261			stencil: true,
27262			premultipliedAlpha: true
27263		};
27264		let creationEvent = null;
27265		this._canvas.addEventListener("webglcontextcreationerror", (event) => {
27266			creationEvent = event;
27267		}, { once: true });
27268		const gl = this._canvas.getContext("webgl2", attributes);
27269		if (!gl) throw new GPUInitializationError(attributes, creationEvent);
27270		this.painter = new Painter(gl, this._camera.transform);
27271	}
27272	/**
27273	* @internal
27274	* Creates a new specialized transform instance from a projection instance and migrates
27275	* to this new transform, carrying over all the properties of the old transform (center, pitch, etc.).
27276	* When the style's projection is changed (or first set), this function should be called.
27277	*/
27278	migrateProjection(newTransform, newCameraHelper) {
27279		this._camera.migrateProjection(newTransform, newCameraHelper);
27280		this.painter.transform = newTransform;
27281		this.fire(new MapProjectionEvent({ newProjection: this.style.projection.name }));
27282	}
27283	/**
27284	* Returns a Boolean indicating whether the map is fully loaded.
27285	*
27286	* Returns `false` if the style is not yet fully loaded,
27287	* or if there has been a change to the sources or style that
27288	* has not yet fully loaded.
27289	*
27290	* @returns A Boolean indicating whether the map is fully loaded.
27291	*/
27292	loaded() {
27293		return !this._styleDirty && !this._sourcesDirty && !!this.style && this.style.loaded();
27294	}
27295	/**
27296	* @internal
27297	* Update this map's style and sources, and re-render the map.
27298	*
27299	* @param updateStyle - mark the map's style for reprocessing as
27300	* well as its sources
27301	*/
27302	_update(updateStyle) {
27303		if (!this.style?._loaded) return this;
27304		this._styleDirty ||= updateStyle;
27305		this._sourcesDirty = true;
27306		this.triggerRepaint();
27307		return this;
27308	}
27309	/**
27310	* @internal
27311	* Request that the given callback be executed during the next render
27312	* frame.  Schedule a render frame if one is not already scheduled.
27313	*
27314	* @returns An id that can be used to cancel the callback
27315	*/
27316	_requestRenderFrame(callback) {
27317		this._update();
27318		return this._renderTaskQueue.add(callback);
27319	}
27320	_cancelRenderFrame(id) {
27321		this._renderTaskQueue.remove(id);
27322	}
27323	/**
27324	* @internal
27325	* Call when a (re-)render of the map is required:
27326	*
27327	* - The style has changed (`setPaintProperty()`, etc.)
27328	* - Source data has changed (e.g. tiles have finished loading)
27329	* - The map has is moving (or just finished moving)
27330	* - A transition is in progress
27331	*
27332	* @param paintStartTimeStamp - The time when the animation frame began executing.
27333	*/
27334	_render(paintStartTimeStamp) {
27335		const fadeDuration = this._idleTriggered ? this._fadeDuration : 0;
27336		const isGlobeRendering = this.style.projection?.transitionState > 0;
27337		this.painter.context.setDirty();
27338		this.painter.setBaseState();
27339		this._renderTaskQueue.run(paintStartTimeStamp);
27340		if (this._removed) return;
27341		let crossFading = false;
27342		if (this.style && this._styleDirty) {
27343			this._styleDirty = false;
27344			const zoom = this._camera.transform.zoom;
27345			const currentTime = now();
27346			this.style.zoomHistory.update(zoom, currentTime);
27347			const parameters = new EvaluationParameters(zoom, {
27348				now: currentTime,
27349				fadeDuration,
27350				zoomHistory: this.style.zoomHistory,
27351				transition: this.style.getTransition()
27352			});
27353			const factor = parameters.crossFadingFactor();
27354			if (factor !== 1 || factor !== this._crossFadingFactor) {
27355				crossFading = true;
27356				this._crossFadingFactor = factor;
27357			}
27358			this.style.update(parameters);
27359		}
27360		const globeRenderingChanged = this.style.projection?.transitionState > 0 !== isGlobeRendering;
27361		this._camera.transform.setTransitionState(this.style.projection?.transitionState);
27362		if (this.style && (this._sourcesDirty || globeRenderingChanged)) {
27363			this._sourcesDirty = false;
27364			this.style._updateSources(this._camera.transform);
27365		}
27366		if (this.terrain) {
27367			if (this.terrain.tileManager.update(this._camera.transform, this.terrain)) this.terrain.resetElevationCache();
27368			this._camera.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._camera.transform.center, this._camera.transform.tileZoom));
27369			if (!this._camera.elevationFreeze && this.getCenterClampedToGround()) this._camera.transform.setElevation(this.terrain.getElevationForLngLat(this._camera.transform.center, this._camera.transform));
27370		} else {
27371			this._camera.transform.setMinElevationForCurrentTile(0);
27372			if (this.getCenterClampedToGround()) this._camera.transform.setElevation(0);
27373		}
27374		this._placementDirty = this.style?._updatePlacement(this._camera.transform, this.showCollisionBoxes, fadeDuration, this._crossSourceCollisions, globeRenderingChanged);
27375		this.painter.render(this.style, {
27376			showTileBoundaries: this.showTileBoundaries,
27377			showOverdrawInspector: this._showOverdrawInspector,
27378			rotating: this.isRotating(),
27379			zooming: this.isZooming(),
27380			moving: this.isMoving(),
27381			fadeDuration,
27382			showPadding: this.showPadding,
27383			anisotropicFilterPitch: this.getAnisotropicFilterPitch()
27384		});
27385		this.fire(new MapLibreEvent("render"));
27386		if (this.loaded() && !this._loaded) {
27387			this._loaded = true;
27388			this.fire(new MapLibreEvent("load"));
27389		}
27390		if (this.style && (this.style.hasTransitions() || crossFading)) this._styleDirty = true;
27391		if (this.style && !this._placementDirty) this.style._releaseSymbolFadeTiles();
27392		const somethingDirty = this._sourcesDirty || this._styleDirty || this._placementDirty || this.painter.renderToTexture?.needsFollowUpFrame;
27393		if (somethingDirty || this._repaint) this.triggerRepaint();
27394		else if (!this.isMoving() && this.loaded()) this.fire(new MapLibreEvent("idle"));
27395		if (this._loaded && !this._fullyLoaded && !somethingDirty) this._fullyLoaded = true;
27396		return this;
27397	}
27398	/**
27399	* Force a synchronous redraw of the map.
27400	* @example
27401	* ```ts
27402	* map.redraw();
27403	* ```
27404	*/
27405	redraw() {
27406		if (this.style) {
27407			if (this._frameRequest) {
27408				this._frameRequest.abort();
27409				this._frameRequest = null;
27410			}
27411			this._render(0);
27412		}
27413		return this;
27414	}
27415	/**
27416	* Clean up and release all internal resources associated with this map.
27417	*
27418	* This includes DOM elements, event bindings, web workers, and WebGL resources.
27419	*
27420	* Use this method when you are done using the map and wish to ensure that it no
27421	* longer consumes browser resources. Afterwards, you must not call any other
27422	* methods on the map.
27423	*/
27424	remove() {
27425		if (this._hash) this._hash.remove();
27426		for (const control of this._controls) control.onRemove(this);
27427		this._controls = [];
27428		if (this._frameRequest) {
27429			this._frameRequest.abort();
27430			this._frameRequest = null;
27431		}
27432		this._renderTaskQueue.clear();
27433		this._diffStyleRequest?.abort();
27434		this.painter.destroy();
27435		this._handlers.destroy();
27436		this.setStyle(null);
27437		if (typeof window !== "undefined") this._ownerWindow.removeEventListener("online", this._onWindowOnline, false);
27438		ImageRequest.removeThrottleControl(this._imageQueueHandle);
27439		this._resizeObserver?.disconnect();
27440		const extension = this.painter.context.gl.getExtension("WEBGL_lose_context");
27441		if (extension?.loseContext) extension.loseContext();
27442		this._cleanupContainer();
27443		this._removed = true;
27444		this.fire(new MapLibreEvent("remove"));
27445	}
27446	/**
27447	* Trigger the rendering of a single frame. Use this method with custom layers to
27448	* repaint the map when the layer changes. Calling this multiple times before the
27449	* next frame is rendered will still result in only a single frame being rendered.
27450	* @example
27451	* ```ts
27452	* map.triggerRepaint();
27453	* ```
27454	* @see [Add a 3D model](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-3d-model-using-threejs/)
27455	* @see [Add an animated icon to the map](https://maplibre.org/maplibre-gl-js/docs/examples/add-an-animated-icon-to-the-map/)
27456	*/
27457	triggerRepaint() {
27458		if (this.style && !this._frameRequest) {
27459			this._frameRequest = new AbortController();
27460			browser.frame(this._frameRequest, (paintStartTimeStamp) => {
27461				this._frameRequest = null;
27462				try {
27463					this._render(paintStartTimeStamp);
27464				} catch (error) {
27465					if (!isAbortError(error)) throw error;
27466				}
27467			}, () => {}, this._ownerWindow);
27468		}
27469	}
27470	/**
27471	* Gets and sets a Boolean indicating whether the map will render an outline
27472	* around each tile and the tile ID. These tile boundaries are useful for
27473	* debugging.
27474	*
27475	* The uncompressed file size of the first vector source is drawn in the top left
27476	* corner of each tile, next to the tile ID.
27477	*
27478	* @example
27479	* ```ts
27480	* map.showTileBoundaries = true;
27481	* ```
27482	*/
27483	get showTileBoundaries() {
27484		return !!this._showTileBoundaries;
27485	}
27486	set showTileBoundaries(value) {
27487		if (this._showTileBoundaries === value) return;
27488		this._showTileBoundaries = value;
27489		this._update();
27490	}
27491	/**
27492	* Gets and sets a Boolean indicating whether the map will visualize
27493	* the padding offsets.
27494	*/
27495	get showPadding() {
27496		return !!this._showPadding;
27497	}
27498	set showPadding(value) {
27499		if (this._showPadding === value) return;
27500		this._showPadding = value;
27501		this._update();
27502	}
27503	/**
27504	* Gets and sets a Boolean indicating whether the map will render boxes
27505	* around all symbols in the data source, revealing which symbols
27506	* were rendered or which were hidden due to collisions.
27507	* This information is useful for debugging.
27508	*/
27509	get showCollisionBoxes() {
27510		return !!this._showCollisionBoxes;
27511	}
27512	set showCollisionBoxes(value) {
27513		if (this._showCollisionBoxes === value) return;
27514		this._showCollisionBoxes = value;
27515		if (value) this.style._generateCollisionBoxes();
27516		else this._update();
27517	}
27518	/**
27519	* Gets and sets a Boolean indicating whether the map should color-code
27520	* each fragment to show how many times it has been shaded.
27521	* White fragments have been shaded 8 or more times.
27522	* Black fragments have been shaded 0 times.
27523	* This information is useful for debugging.
27524	*/
27525	get showOverdrawInspector() {
27526		return !!this._showOverdrawInspector;
27527	}
27528	set showOverdrawInspector(value) {
27529		if (this._showOverdrawInspector === value) return;
27530		this._showOverdrawInspector = value;
27531		this._update();
27532	}
27533	/**
27534	* Gets and sets a Boolean indicating whether the map will
27535	* continuously repaint. This information is useful for analyzing performance.
27536	*/
27537	get repaint() {
27538		return !!this._repaint;
27539	}
27540	set repaint(value) {
27541		if (this._repaint !== value) {
27542			this._repaint = value;
27543			this.triggerRepaint();
27544		}
27545	}
27546	get vertices() {
27547		return !!this._vertices;
27548	}
27549	set vertices(value) {
27550		this._vertices = value;
27551		this._update();
27552	}
27553	/**
27554	* Returns the package version of the library
27555	* @returns Package version of the library
27556	*/
27557	get version() {
27558		return version$1;
27559	}
27560	/**
27561	* Returns the elevation for the point where the camera is looking.
27562	* This value corresponds to:
27563	* "meters above sea level" * "exaggeration"
27564	* @returns The elevation.
27565	*/
27566	getCameraTargetElevation() {
27567		return this._camera.transform.elevation;
27568	}
27569	/**
27570	* Gets the {@link ProjectionSpecification}.
27571	* @returns the projection specification.
27572	* @example
27573	* ```ts
27574	* let projection = map.getProjection();
27575	* ```
27576	*/
27577	getProjection() {
27578		return this.style.getProjection();
27579	}
27580	/**
27581	* Sets the {@link ProjectionSpecification}.
27582	* @param projection - the projection specification to set
27583	* @returns
27584	*/
27585	setProjection(projection) {
27586		this._lazyInitEmptyStyle();
27587		this.style.setProjection(projection);
27588		return this._update(true);
27589	}
27590};
27591//#endregion
27592//#region src/ui/control/navigation_control.ts
27593const defaultOptions$3 = {
27594	showCompass: true,
27595	showZoom: true,
27596	visualizePitch: false,
27597	visualizeRoll: true
27598};
27599/**
27600* A `NavigationControl` control contains zoom buttons and a compass.
27601*
27602* @group Markers and Controls
27603*
27604* @example
27605* ```ts
27606* let nav = new NavigationControl();
27607* map.addControl(nav, 'top-left');
27608* ```
27609* @see [Display map navigation controls](https://maplibre.org/maplibre-gl-js/docs/examples/display-map-navigation-controls/)
27610*/
27611var NavigationControl = class {
27612	/**
27613	* @param options - the control's options
27614	*/
27615	constructor(options) {
27616		this._updateZoomButtons = () => {
27617			const zoom = this._map.getZoom();
27618			const isMax = zoom === this._map.getMaxZoom();
27619			const isMin = zoom === this._map.getMinZoom(true);
27620			this._zoomInButton.disabled = isMax;
27621			this._zoomOutButton.disabled = isMin;
27622			this._zoomInButton.setAttribute("aria-disabled", isMax.toString());
27623			this._zoomOutButton.setAttribute("aria-disabled", isMin.toString());
27624		};
27625		this._rotateCompassArrow = () => {
27626			const pitch = this._map.getPitch();
27627			const roll = this._map.getRoll();
27628			const bearing = this._map.getBearing();
27629			const pitchScale = 1 / Math.pow(Math.cos(degreesToRadians(pitch)), .5);
27630			if (this.options.visualizePitch && this.options.visualizeRoll) {
27631				this._compassIcon.style.transform = `scale(${pitchScale}) rotateZ(${-roll}deg) rotateX(${pitch}deg) rotateZ(${-bearing}deg)`;
27632				return;
27633			}
27634			if (this.options.visualizePitch) {
27635				this._compassIcon.style.transform = `scale(${pitchScale}) rotateX(${pitch}deg) rotateZ(${-bearing}deg)`;
27636				return;
27637			}
27638			if (this.options.visualizeRoll) {
27639				this._compassIcon.style.transform = `rotate(${-bearing - roll}deg)`;
27640				return;
27641			}
27642			this._compassIcon.style.transform = `rotate(${-bearing}deg)`;
27643		};
27644		this._setButtonTitle = (button, title) => {
27645			const str = this._map._getUIString(`NavigationControl.${title}`);
27646			button.title = str;
27647			button.setAttribute("aria-label", str);
27648		};
27649		this.options = extend({}, defaultOptions$3, options);
27650		this._container = DOM.create("div", "maplibregl-ctrl maplibregl-ctrl-group");
27651		this._container.addEventListener("contextmenu", (e) => e.preventDefault());
27652		if (this.options.showZoom) {
27653			this._zoomInButton = this._createButton("maplibregl-ctrl-zoom-in", (e) => this._map.zoomIn({}, { originalEvent: e }));
27654			DOM.create("span", "maplibregl-ctrl-icon", this._zoomInButton).setAttribute("aria-hidden", "true");
27655			this._zoomOutButton = this._createButton("maplibregl-ctrl-zoom-out", (e) => this._map.zoomOut({}, { originalEvent: e }));
27656			DOM.create("span", "maplibregl-ctrl-icon", this._zoomOutButton).setAttribute("aria-hidden", "true");
27657		}
27658		if (this.options.showCompass) {
27659			this._compass = this._createButton("maplibregl-ctrl-compass", (e) => {
27660				if (this.options.visualizePitch) this._map.resetNorthPitch({}, { originalEvent: e });
27661				else this._map.resetNorth({}, { originalEvent: e });
27662			});
27663			this._compassIcon = DOM.create("span", "maplibregl-ctrl-icon", this._compass);
27664			this._compassIcon.setAttribute("aria-hidden", "true");
27665		}
27666	}
27667	/** {@inheritDoc IControl.onAdd} */
27668	onAdd(map) {
27669		this._map = map;
27670		if (this.options.showZoom) {
27671			this._setButtonTitle(this._zoomInButton, "ZoomIn");
27672			this._setButtonTitle(this._zoomOutButton, "ZoomOut");
27673			this._map.on("move", this._updateZoomButtons);
27674			this._updateZoomButtons();
27675		}
27676		if (this.options.showCompass) {
27677			this._setButtonTitle(this._compass, "ResetBearing");
27678			if (this.options.visualizePitch) this._map.on("pitch", this._rotateCompassArrow);
27679			if (this.options.visualizeRoll) this._map.on("roll", this._rotateCompassArrow);
27680			this._map.on("rotate", this._rotateCompassArrow);
27681			this._rotateCompassArrow();
27682			this._handler = new MouseRotateWrapper(this._map, this._compass, this.options.visualizePitch);
27683		}
27684		return this._container;
27685	}
27686	/** {@inheritDoc IControl.onRemove} */
27687	onRemove() {
27688		this._container.remove();
27689		if (this.options.showZoom) this._map.off("move", this._updateZoomButtons);
27690		if (this.options.showCompass) {
27691			if (this.options.visualizePitch) this._map.off("pitch", this._rotateCompassArrow);
27692			if (this.options.visualizeRoll) this._map.off("roll", this._rotateCompassArrow);
27693			this._map.off("rotate", this._rotateCompassArrow);
27694			this._handler.off();
27695			delete this._handler;
27696		}
27697		delete this._map;
27698	}
27699	_createButton(className, fn) {
27700		const a = DOM.create("button", className, this._container);
27701		a.type = "button";
27702		a.addEventListener("click", fn);
27703		return a;
27704	}
27705};
27706var MouseRotateWrapper = class {
27707	constructor(map, element, pitch = false) {
27708		this.mousedown = (e) => {
27709			this.startMove(e, DOM.mousePos(this.element, e));
27710			window.addEventListener("mousemove", this.mousemove);
27711			window.addEventListener("mouseup", this.mouseup);
27712		};
27713		this.mousemove = (e) => {
27714			this.move(e, DOM.mousePos(this.element, e));
27715		};
27716		this.mouseup = (e) => {
27717			this._rotatePitchHandler.dragEnd(e);
27718			this.offTemp();
27719		};
27720		this.touchstart = (e) => {
27721			if (e.targetTouches.length !== 1) this.reset();
27722			else {
27723				this._startPos = this._lastPos = DOM.touchPos(this.element, e.targetTouches)[0];
27724				this.startMove(e, this._startPos);
27725				window.addEventListener("touchmove", this.touchmove, { passive: false });
27726				window.addEventListener("touchend", this.touchend);
27727			}
27728		};
27729		this.touchmove = (e) => {
27730			if (e.targetTouches.length !== 1) this.reset();
27731			else {
27732				this._lastPos = DOM.touchPos(this.element, e.targetTouches)[0];
27733				this.move(e, this._lastPos);
27734			}
27735		};
27736		this.touchend = (e) => {
27737			if (e.targetTouches.length === 0 && this._startPos && this._lastPos && this._startPos.dist(this._lastPos) < this._clickTolerance) this.element.click();
27738			delete this._startPos;
27739			delete this._lastPos;
27740			this.offTemp();
27741		};
27742		this.reset = () => {
27743			this._rotatePitchHandler.reset();
27744			delete this._startPos;
27745			delete this._lastPos;
27746			this.offTemp();
27747		};
27748		this._clickTolerance = 10;
27749		this.element = element;
27750		const moveStateManager = new MouseOrTouchMoveStateManager();
27751		this._rotatePitchHandler = new DragHandler({
27752			clickTolerance: 3,
27753			move: (lastPoint, currentPoint) => {
27754				const rect = element.getBoundingClientRect();
27755				const center = new Point((rect.bottom - rect.top) / 2, (rect.right - rect.left) / 2);
27756				return {
27757					bearingDelta: getAngleDelta(new Point(lastPoint.x, currentPoint.y), currentPoint, center),
27758					pitchDelta: pitch ? (currentPoint.y - lastPoint.y) * -.5 : void 0
27759				};
27760			},
27761			moveStateManager,
27762			enable: true,
27763			assignEvents: () => {}
27764		});
27765		this.map = map;
27766		element.addEventListener("mousedown", this.mousedown);
27767		element.addEventListener("touchstart", this.touchstart, { passive: false });
27768		element.addEventListener("touchcancel", this.reset);
27769	}
27770	startMove(e, point) {
27771		this._rotatePitchHandler.dragStart(e, point);
27772		DOM.disableDrag();
27773	}
27774	move(e, point) {
27775		const map = this.map;
27776		const { bearingDelta, pitchDelta } = this._rotatePitchHandler.dragMove(e, point) || {};
27777		if (bearingDelta) map.setBearing(map.getBearing() + bearingDelta);
27778		if (pitchDelta) map.setPitch(map.getPitch() + pitchDelta);
27779	}
27780	off() {
27781		const element = this.element;
27782		element.removeEventListener("mousedown", this.mousedown);
27783		element.removeEventListener("touchstart", this.touchstart);
27784		window.removeEventListener("touchmove", this.touchmove);
27785		window.removeEventListener("touchend", this.touchend);
27786		element.removeEventListener("touchcancel", this.reset);
27787		this.offTemp();
27788	}
27789	offTemp() {
27790		DOM.enableDrag();
27791		window.removeEventListener("mousemove", this.mousemove);
27792		window.removeEventListener("mouseup", this.mouseup);
27793		window.removeEventListener("touchmove", this.touchmove);
27794		window.removeEventListener("touchend", this.touchend);
27795	}
27796};
27797//#endregion
27798//#region src/util/geolocation_support.ts
27799let supportsGeolocation;
27800async function checkGeolocationSupport(forceRecalculation = false) {
27801	if (supportsGeolocation !== void 0 && !forceRecalculation) return supportsGeolocation;
27802	if (window.navigator.permissions === void 0) {
27803		supportsGeolocation = !!window.navigator.geolocation;
27804		return supportsGeolocation;
27805	}
27806	try {
27807		supportsGeolocation = (await window.navigator.permissions.query({ name: "geolocation" })).state !== "denied";
27808	} catch {
27809		supportsGeolocation = !!window.navigator.geolocation;
27810	}
27811	return supportsGeolocation;
27812}
27813//#endregion
27814//#region src/util/smart_wrap.ts
27815/**
27816* Given a LngLat, prior projected position, and a transform, return a new LngLat shifted
27817* n × 360° east or west for some n ≥ 0 such that:
27818*
27819* * the projected location of the result is on screen, if possible, and secondarily:
27820* * the difference between the projected location of the result and the prior position
27821*   is minimized.
27822*
27823* The object is to preserve perceived object constancy for Popups and Markers as much as
27824* possible; they should avoid shifting large distances across the screen, even when the
27825* map center changes by ±360° due to automatic wrapping, and when about to go off screen,
27826* should wrap just enough to avoid doing so.
27827*/
27828function smartWrap(lngLat, priorPos, transform, useNormalWrap = false) {
27829	if (useNormalWrap || !transform.getCoveringTilesDetailsProvider().allowWorldCopies()) return lngLat?.wrap();
27830	const originalLngLat = new LngLat(lngLat.lng, lngLat.lat);
27831	lngLat = new LngLat(lngLat.lng, lngLat.lat);
27832	if (priorPos) {
27833		const left = new LngLat(lngLat.lng - 360, lngLat.lat);
27834		const right = new LngLat(lngLat.lng + 360, lngLat.lat);
27835		const delta = transform.locationToScreenPoint(lngLat).distSqr(priorPos);
27836		if (transform.locationToScreenPoint(left).distSqr(priorPos) < delta) lngLat = left;
27837		else if (transform.locationToScreenPoint(right).distSqr(priorPos) < delta) lngLat = right;
27838	}
27839	while (Math.abs(lngLat.lng - transform.center.lng) > 180) {
27840		const pos = transform.locationToScreenPoint(lngLat);
27841		if (pos.x >= 0 && pos.y >= 0 && pos.x <= transform.width && pos.y <= transform.height) break;
27842		if (lngLat.lng > transform.center.lng) lngLat.lng -= 360;
27843		else lngLat.lng += 360;
27844	}
27845	if (lngLat.lng !== originalLngLat.lng && transform.isPointOnMapSurface(transform.locationToScreenPoint(lngLat))) return lngLat;
27846	return originalLngLat;
27847}
27848//#endregion
27849//#region src/ui/anchor.ts
27850const anchorTranslate = {
27851	"center": "translate(-50%,-50%)",
27852	"top": "translate(-50%,0)",
27853	"top-left": "translate(0,0)",
27854	"top-right": "translate(-100%,0)",
27855	"bottom": "translate(-50%,-100%)",
27856	"bottom-left": "translate(0,-100%)",
27857	"bottom-right": "translate(-100%,-100%)",
27858	"left": "translate(0,-50%)",
27859	"right": "translate(-100%,-50%)"
27860};
27861function applyAnchorClass(element, anchor, prefix) {
27862	const classList = element.classList;
27863	for (const key in anchorTranslate) classList.remove(`maplibregl-${prefix}-anchor-${key}`);
27864	classList.add(`maplibregl-${prefix}-anchor-${anchor}`);
27865}
27866//#endregion
27867//#region src/ui/marker.ts
27868/**
27869* Screen-pixel deltas applied when a focused draggable marker is moved with the arrow keys.
27870*/
27871const ARROW_KEY_DELTAS = {
27872	ArrowLeft: [-1, 0],
27873	ArrowRight: [1, 0],
27874	ArrowUp: [0, -1],
27875	ArrowDown: [0, 1]
27876};
27877const KEYBOARD_DRAG_SMALL_STEP = 1;
27878const KEYBOARD_DRAG_LARGE_STEP = 10;
27879/** Fill color of the default marker when {@link MarkerOptions.color} is not given. */
27880const DEFAULT_MARKER_COLOR = "#3FB1CE";
27881/** Height in CSS pixels of the default marker SVG at scale 1. */
27882const DEFAULT_MARKER_HEIGHT = 41;
27883/** Width in CSS pixels of the default marker SVG at scale 1. */
27884const DEFAULT_MARKER_WIDTH = 27;
27885const SVG_NS = "http://www.w3.org/2000/svg";
27886/** Creates an SVG element with the given attributes, set in the order given, and children. */
27887function svgElement(tag, attributes, children = []) {
27888	const element = DOM.createNS(SVG_NS, tag);
27889	for (const name in attributes) element.setAttributeNS(null, name, attributes[name]);
27890	for (const child of children) element.appendChild(child);
27891	return element;
27892}
27893/** The default marker SVG at scale 1 in the default color, built on first use and cloned per marker. */
27894let defaultMarkerTemplate;
27895/**
27896* Returns the shared default marker SVG, building it the first time it is asked for. Assembling
27897* the pin takes about fifty DOM calls, so each default {@link Marker} deep-clones this template
27898* and overrides only its size and its fill, see {@link defaultMarkerFillGroup}.
27899*/
27900function getDefaultMarkerTemplate() {
27901	if (defaultMarkerTemplate) return defaultMarkerTemplate;
27902	const shadow = svgElement("g", {
27903		transform: "translate(3.0, 29.0)",
27904		fill: "#000000"
27905	}, [
27906		["10.5", "5.25002273"],
27907		["10.5", "5.25002273"],
27908		["9.5", "4.77275007"],
27909		["8.5", "4.29549936"],
27910		["7.5", "3.81822308"],
27911		["6.5", "3.34094679"],
27912		["5.5", "2.86367051"],
27913		["4.5", "2.38636864"]
27914	].map(([rx, ry]) => svgElement("ellipse", {
27915		opacity: "0.04",
27916		cx: "10.5",
27917		cy: "5.80029008",
27918		rx,
27919		ry
27920	})));
27921	const background = svgElement("g", { fill: DEFAULT_MARKER_COLOR }, [svgElement("path", { d: "M27,13.5 C27,19.074644 20.250001,27.000002 14.75,34.500002 C14.016665,35.500004 12.983335,35.500004 12.25,34.500002 C6.7499993,27.000002 0,19.222562 0,13.5 C0,6.0441559 6.0441559,0 13.5,0 C20.955844,0 27,6.0441559 27,13.5 Z" })]);
27922	const border = svgElement("g", {
27923		opacity: "0.25",
27924		fill: "#000000"
27925	}, [svgElement("path", { d: "M13.5,0 C6.0441559,0 0,6.0441559 0,13.5 C0,19.222562 6.7499993,27 12.25,34.5 C13,35.522727 14.016664,35.500004 14.75,34.5 C20.250001,27 27,19.074644 27,13.5 C27,6.0441559 20.955844,0 13.5,0 Z M13.5,1 C20.415404,1 26,6.584596 26,13.5 C26,15.898657 24.495584,19.181431 22.220703,22.738281 C19.945823,26.295132 16.705119,30.142167 13.943359,33.908203 C13.743445,34.180814 13.612715,34.322738 13.5,34.441406 C13.387285,34.322738 13.256555,34.180814 13.056641,33.908203 C10.284481,30.127985 7.4148684,26.314159 5.015625,22.773438 C2.6163816,19.232715 1,15.953538 1,13.5 C1,6.584596 6.584596,1 13.5,1 Z" })]);
27926	const maki = svgElement("g", {
27927		transform: "translate(6.0, 7.0)",
27928		fill: "#FFFFFF"
27929	});
27930	const circles = svgElement("g", { transform: "translate(8.0, 8.0)" }, [svgElement("circle", {
27931		fill: "#000000",
27932		opacity: "0.25",
27933		cx: "5.5",
27934		cy: "5.5",
27935		r: "5.4999962"
27936	}), svgElement("circle", {
27937		fill: "#FFFFFF",
27938		cx: "5.5",
27939		cy: "5.5",
27940		r: "5.4999962"
27941	})]);
27942	defaultMarkerTemplate = svgElement("svg", {
27943		display: "block",
27944		height: `${DEFAULT_MARKER_HEIGHT}px`,
27945		width: `${DEFAULT_MARKER_WIDTH}px`,
27946		viewBox: `0 0 ${DEFAULT_MARKER_WIDTH} ${DEFAULT_MARKER_HEIGHT}`
27947	}, [svgElement("g", { "fill-rule": "nonzero" }, [
27948		shadow,
27949		background,
27950		border,
27951		maki,
27952		circles
27953	])]);
27954	return defaultMarkerTemplate;
27955}
27956/** The group of a default marker SVG that carries the pin's fill color: the second child of its single top-level group. */
27957function defaultMarkerFillGroup(svg) {
27958	return svg.firstElementChild.children[1];
27959}
27960/**
27961* The event class for marker drag events (`dragstart`, `drag` and `dragend`).
27962*
27963* @group Event Related
27964*/
27965var MarkerDragEvent = class extends Event {};
27966/**
27967* The event class for the marker `click` event.
27968*
27969* @group Event Related
27970*/
27971var MarkerClickEvent = class extends Event {};
27972/**
27973* Creates a marker component
27974*
27975* @group Markers and Controls
27976*
27977* @example
27978* ```ts
27979* let marker = new Marker()
27980*   .setLngLat([30.5, 50.5])
27981*   .addTo(map);
27982* ```
27983*
27984* @example
27985* Set options
27986* ```ts
27987* let marker = new Marker({
27988*     color: "#FFFFFF",
27989*     draggable: true
27990*   }).setLngLat([30.5, 50.5])
27991*   .addTo(map);
27992* ```
27993* @see [Add a default marker](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-default-marker/)
27994* @see [Add custom icons with Markers](https://maplibre.org/maplibre-gl-js/docs/examples/add-custom-icons-with-markers/)
27995* @see [Create a draggable Marker](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-draggable-marker/)
27996* @see [Animate a marker](https://maplibre.org/maplibre-gl-js/docs/examples/animate-a-marker/)
27997* @see [Attach a popup to a marker instance](https://maplibre.org/maplibre-gl-js/docs/examples/attach-a-popup-to-a-marker-instance/)
27998*
27999* ## Events
28000*
28001* **Event** `dragstart` of type {@link MarkerDragEvent} will be fired when dragging starts.
28002*
28003* **Event** `drag` of type {@link MarkerDragEvent} will be fired while dragging.
28004*
28005* **Event** `dragend` of type {@link MarkerDragEvent} will be fired when the marker is finished being dragged.
28006*
28007* **Event** `click` of type {@link MarkerClickEvent} will be fired when the marker is clicked.
28008*
28009* ## CSS Classes
28010*
28011* **CSS class** `maplibregl-marker-covered` is toggled on the marker element when the marker
28012* is hidden behind 3D terrain or on the back of a globe.
28013* Use this class to apply custom styles to covered markers.
28014*
28015* @example
28016* ```css
28017* .maplibregl-marker-covered {
28018*     pointer-events: none;
28019*     cursor: default;
28020* }
28021* ```
28022*/
28023var Marker = class extends Evented {
28024	/**
28025	* @param options - the options
28026	*/
28027	constructor(options) {
28028		super();
28029		this._onClick = (e) => {
28030			this.fire(new MarkerClickEvent("click", { originalEvent: e }));
28031		};
28032		this._onKeyPress = (e) => {
28033			if (e.code === "Space" || e.code === "Enter") this.togglePopup();
28034		};
28035		this._onKeyDown = (e) => {
28036			if (!this._defaultMarker || !this._draggable || !this._map || !this._lngLat) return;
28037			if (e.composedPath()[0] !== this._element) return;
28038			if (e.altKey || e.ctrlKey || e.metaKey) return;
28039			const delta = ARROW_KEY_DELTAS[e.key];
28040			if (!delta) return;
28041			e.preventDefault();
28042			e.stopPropagation();
28043			const step = e.shiftKey ? KEYBOARD_DRAG_LARGE_STEP : KEYBOARD_DRAG_SMALL_STEP;
28044			const pos = this._map.project(this._lngLat);
28045			this.setLngLat(this._map.unproject(new Point(pos.x + delta[0] * step, pos.y + delta[1] * step)));
28046			if (!this._keyboardDragActive) {
28047				this._keyboardDragActive = true;
28048				this.fire(new MarkerDragEvent("dragstart"));
28049			}
28050			this.fire(new MarkerDragEvent("drag"));
28051		};
28052		this._onKeyUp = (e) => {
28053			if (!ARROW_KEY_DELTAS[e.key]) return;
28054			this._endKeyboardDrag();
28055		};
28056		this._onBlur = () => {
28057			this._endKeyboardDrag();
28058		};
28059		this._onMapClick = (e) => {
28060			const targetElement = e.originalEvent.target;
28061			const element = this._element;
28062			if (this._popup && (targetElement === element || element.contains(targetElement))) this.togglePopup();
28063		};
28064		this._updateCoveredUnthrottled = () => {
28065			const terrain = this._map?.terrain;
28066			if (!terrain || !this._isInViewport()) return;
28067			const covered = this._isCovered(terrain);
28068			if (covered && this._popup?.isOpen()) this._popup.remove();
28069			this._element.style.opacity = covered ? this._opacityWhenCovered : this._opacity;
28070			this._element.classList.toggle("maplibregl-marker-covered", covered);
28071		};
28072		this._updateCovered = throttle(this._updateCoveredUnthrottled, 100);
28073		this._update = (e) => {
28074			if (!this._map) return;
28075			this._lngLat = smartWrap(this._lngLat, this._flatPos, this._map._camera.transform);
28076			this._flatPos = this._pos = this._map.project(this._lngLat)._add(this._offset);
28077			if (this._map.terrain) this._flatPos = this._map._camera.transform.locationToScreenPoint(this._lngLat)._add(this._offset);
28078			let rotation = "";
28079			if (this._rotationAlignment === "viewport" || this._rotationAlignment === "auto") rotation = `rotateZ(${this._rotation}deg)`;
28080			else if (this._rotationAlignment === "map") rotation = `rotateZ(${this._rotation - this._map.getBearing()}deg)`;
28081			let pitch = "";
28082			if (this._pitchAlignment === "viewport" || this._pitchAlignment === "auto") pitch = "rotateX(0deg)";
28083			else if (this._pitchAlignment === "map") pitch = `rotateX(${this._map.getPitch()}deg)`;
28084			if (!this._subpixelPositioning && e?.type !== "move") this._pos = this._pos.round();
28085			this._element.style.transform = `${anchorTranslate[this._anchor]} translate(${this._pos.x}px, ${this._pos.y}px) ${pitch} ${rotation}`;
28086			this._updateOpacity();
28087		};
28088		this._onMove = (e) => {
28089			if (!this._isDragging) {
28090				const clickTolerance = this._clickTolerance || this._map._clickTolerance;
28091				this._isDragging = e.point.dist(this._pointerdownPos) >= clickTolerance;
28092			}
28093			if (!this._isDragging) return;
28094			this._pos = e.point.sub(this._positionDelta);
28095			this._lngLat = this._map.unproject(this._pos);
28096			this.setLngLat(this._lngLat);
28097			this._element.style.pointerEvents = "none";
28098			if (this._state === "pending") {
28099				this._state = "active";
28100				this.fire(new MarkerDragEvent("dragstart"));
28101			}
28102			this.fire(new MarkerDragEvent("drag"));
28103		};
28104		this._onUp = () => {
28105			this._element.style.pointerEvents = "auto";
28106			this._positionDelta = null;
28107			this._pointerdownPos = null;
28108			this._isDragging = false;
28109			this._map.off("mousemove", this._onMove);
28110			this._map.off("touchmove", this._onMove);
28111			if (this._state === "active") this.fire(new MarkerDragEvent("dragend"));
28112			this._state = "inactive";
28113		};
28114		this._addDragHandler = (e) => {
28115			if (this._element.contains(e.originalEvent.target)) {
28116				e.preventDefault();
28117				this._positionDelta = e.point.sub(this._pos).add(this._offset);
28118				this._pointerdownPos = e.point;
28119				this._state = "pending";
28120				this._map.on("mousemove", this._onMove);
28121				this._map.on("touchmove", this._onMove);
28122				this._map.once("mouseup", this._onUp);
28123				this._map.once("touchend", this._onUp);
28124			}
28125		};
28126		this._anchor = options?.anchor || "center";
28127		this._color = options?.color || DEFAULT_MARKER_COLOR;
28128		this._scale = options?.scale || 1;
28129		this._draggable = options?.draggable || false;
28130		this._clickTolerance = options?.clickTolerance || 0;
28131		this._subpixelPositioning = options?.subpixelPositioning || false;
28132		this._isDragging = false;
28133		this._roleManaged = false;
28134		this._tabIndexManaged = false;
28135		this._keyboardDragActive = false;
28136		this._state = "inactive";
28137		this._rotation = options?.rotation || 0;
28138		this._rotationAlignment = options?.rotationAlignment || "auto";
28139		this._pitchAlignment = options?.pitchAlignment && options.pitchAlignment !== "auto" ? options.pitchAlignment : this._rotationAlignment;
28140		this.setOpacity(options?.opacity, options?.opacityWhenCovered);
28141		if (!options?.element) {
28142			this._defaultMarker = true;
28143			this._element = DOM.create("div");
28144			const svg = getDefaultMarkerTemplate().cloneNode(true);
28145			svg.setAttributeNS(null, "height", `${DEFAULT_MARKER_HEIGHT * this._scale}px`);
28146			svg.setAttributeNS(null, "width", `${DEFAULT_MARKER_WIDTH * this._scale}px`);
28147			defaultMarkerFillGroup(svg).setAttributeNS(null, "fill", this._color);
28148			this._element.appendChild(svg);
28149			this._offset = Point.convert(options?.offset || [0, -14]);
28150		} else {
28151			this._element = options.element;
28152			this._offset = Point.convert(options?.offset || [0, 0]);
28153		}
28154		this._element.classList.add("maplibregl-marker");
28155		this._element.addEventListener("dragstart", (e) => {
28156			e.preventDefault();
28157		});
28158		this._element.addEventListener("mousedown", (e) => {
28159			e.preventDefault();
28160		});
28161		applyAnchorClass(this._element, this._anchor, "marker");
28162		if (options?.className) for (const name of options.className.split(" ")) this._element.classList.add(name);
28163		this._popup = null;
28164	}
28165	/**
28166	* Attaches the `Marker` to a `Map` object.
28167	* @param map - The MapLibre GL JS map to add the marker to.
28168	* @example
28169	* ```ts
28170	* let marker = new Marker()
28171	*   .setLngLat([30.5, 50.5])
28172	*   .addTo(map); // add the marker to the map
28173	* ```
28174	*/
28175	addTo(map) {
28176		this.remove();
28177		this._map = map;
28178		if (this._defaultMarker && !this._element.hasAttribute("aria-label")) this._element.setAttribute("aria-label", map._getUIString("Marker.Title"));
28179		this._updateAccessibilityRole();
28180		map.getCanvasContainer().appendChild(this._element);
28181		map.on("move", this._update);
28182		map.on("moveend", this._update);
28183		map.on("terrain", this._update);
28184		map.on("projectiontransition", this._update);
28185		map.on("idle", this._update);
28186		this._element.addEventListener("click", this._onClick);
28187		this.setDraggable(this._draggable);
28188		this._update();
28189		this._map.on("click", this._onMapClick);
28190		return this;
28191	}
28192	/**
28193	* Removes the marker from a map
28194	* @example
28195	* ```ts
28196	* let marker = new Marker().addTo(map);
28197	* marker.remove();
28198	* ```
28199	*/
28200	remove() {
28201		if (this._map) {
28202			this._map.off("click", this._onMapClick);
28203			this._map.off("move", this._update);
28204			this._map.off("moveend", this._update);
28205			this._map.off("terrain", this._update);
28206			this._map.off("projectiontransition", this._update);
28207			this._map.off("idle", this._update);
28208			this._map.off("mousedown", this._addDragHandler);
28209			this._map.off("touchstart", this._addDragHandler);
28210			this._map.off("mouseup", this._onUp);
28211			this._map.off("touchend", this._onUp);
28212			this._map.off("mousemove", this._onMove);
28213			this._map.off("touchmove", this._onMove);
28214			delete this._map;
28215		}
28216		this._element.removeEventListener("click", this._onClick);
28217		this._element.removeEventListener("keydown", this._onKeyDown);
28218		this._element.removeEventListener("keyup", this._onKeyUp);
28219		this._element.removeEventListener("blur", this._onBlur);
28220		this._element.removeEventListener("keypress", this._onKeyPress);
28221		this._keyboardDragActive = false;
28222		this._element.remove();
28223		if (this._popup) this._popup.remove();
28224		return this;
28225	}
28226	/**
28227	* Get the marker's geographical location.
28228	*
28229	* The longitude of the result may differ by a multiple of 360 degrees from the longitude previously
28230	* set by `setLngLat` because `Marker` wraps the anchor longitude across copies of the world to keep
28231	* the marker on screen.
28232	*
28233	* @returns A {@link LngLat} describing the marker's location.
28234	* @example
28235	* ```ts
28236	* // Store the marker's longitude and latitude coordinates in a variable
28237	* let lngLat = marker.getLngLat();
28238	* // Print the marker's longitude and latitude values in the console
28239	* console.log('Longitude: ' + lngLat.lng + ', Latitude: ' + lngLat.lat )
28240	* ```
28241	* @see [Create a draggable Marker](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-draggable-marker/)
28242	*/
28243	getLngLat() {
28244		return this._lngLat;
28245	}
28246	/**
28247	* Set the marker's geographical position and move it.
28248	* @param lnglat - A {@link LngLat} describing where the marker should be located.
28249	* @example
28250	* Create a new marker, set the longitude and latitude, and add it to the map
28251	* ```ts
28252	* new Marker()
28253	*   .setLngLat([-65.017, -16.457])
28254	*   .addTo(map);
28255	* ```
28256	* @see [Add custom icons with Markers](https://maplibre.org/maplibre-gl-js/docs/examples/add-custom-icons-with-markers/)
28257	* @see [Create a draggable Marker](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-draggable-marker/)
28258	*/
28259	setLngLat(lnglat) {
28260		this._lngLat = LngLat.convert(lnglat);
28261		this._pos = null;
28262		this._update();
28263		if (this._popup) this._popup.setLngLat(this._lngLat);
28264		return this;
28265	}
28266	/**
28267	* Returns the `Marker`'s HTML element.
28268	* @returns element
28269	*/
28270	getElement() {
28271		return this._element;
28272	}
28273	/**
28274	* Binds a {@link Popup} to the {@link Marker}.
28275	* @param popup - An instance of the {@link Popup} class. If undefined or null, any popup
28276	* set on this {@link Marker} instance is unset.
28277	* @example
28278	* ```ts
28279	* let marker = new Marker()
28280	*  .setLngLat([0, 0])
28281	*  .setPopup(new Popup().setHTML("<h1>Hello World!</h1>")) // add popup
28282	*  .addTo(map);
28283	* ```
28284	* @see [Attach a popup to a marker instance](https://maplibre.org/maplibre-gl-js/docs/examples/attach-a-popup-to-a-marker-instance/)
28285	*/
28286	setPopup(popup) {
28287		if (this._popup) {
28288			this._popup.remove();
28289			this._popup = null;
28290			this._element.removeEventListener("keypress", this._onKeyPress);
28291		}
28292		if (popup) {
28293			if (!("offset" in popup.options)) {
28294				const markerRadius = 13.5;
28295				const linearOffset = Math.abs(markerRadius) / Math.SQRT2;
28296				popup.options.offset = this._defaultMarker ? {
28297					"top": [0, 0],
28298					"top-left": [0, 0],
28299					"top-right": [0, 0],
28300					"bottom": [0, -38.1],
28301					"bottom-left": [linearOffset, (24.6 + linearOffset) * -1],
28302					"bottom-right": [-linearOffset, (24.6 + linearOffset) * -1],
28303					"left": [markerRadius, -24.6],
28304					"right": [-13.5, -24.6]
28305				} : this._offset;
28306			}
28307			this._popup = popup;
28308			this._element.addEventListener("keypress", this._onKeyPress);
28309		}
28310		this._updateTabIndex();
28311		this._updateAccessibilityRole();
28312		return this;
28313	}
28314	/**
28315	* Set the option to allow subpixel positioning of the marker by passing a boolean
28316	*
28317	* @param value - when set to `true`, subpixel positioning is enabled for the marker.
28318	*
28319	* @example
28320	* ```ts
28321	* let marker = new Marker()
28322	* marker.setSubpixelPositioning(true);
28323	* ```
28324	*/
28325	setSubpixelPositioning(value) {
28326		this._subpixelPositioning = value;
28327		return this;
28328	}
28329	_endKeyboardDrag() {
28330		if (this._keyboardDragActive) {
28331			this._keyboardDragActive = false;
28332			this.fire(new MarkerDragEvent("dragend"));
28333		}
28334	}
28335	/**
28336	* Returns the {@link Popup} instance that is bound to the {@link Marker}.
28337	* @returns popup
28338	* @example
28339	* ```ts
28340	* let marker = new Marker()
28341	*  .setLngLat([0, 0])
28342	*  .setPopup(new Popup().setHTML("<h1>Hello World!</h1>"))
28343	*  .addTo(map);
28344	*
28345	* console.log(marker.getPopup()); // return the popup instance
28346	* ```
28347	*/
28348	getPopup() {
28349		return this._popup;
28350	}
28351	/**
28352	* Opens or closes the {@link Popup} instance that is bound to the {@link Marker}, depending on the current state of the {@link Popup}.
28353	* @example
28354	* ```ts
28355	* let marker = new Marker()
28356	*  .setLngLat([0, 0])
28357	*  .setPopup(new Popup().setHTML("<h1>Hello World!</h1>"))
28358	*  .addTo(map);
28359	*
28360	* marker.togglePopup(); // toggle popup open or closed
28361	* ```
28362	*/
28363	togglePopup() {
28364		const popup = this._popup;
28365		if (this._element.style.opacity === this._opacityWhenCovered) return this;
28366		if (!popup) return this;
28367		else if (popup.isOpen()) popup.remove();
28368		else {
28369			popup.setLngLat(this._lngLat);
28370			popup.addTo(this._map);
28371		}
28372		return this;
28373	}
28374	_updateOpacity() {
28375		if (!this._isInViewport()) return;
28376		const terrain = this._map?.terrain;
28377		const occluded = this._map._camera.transform.isLocationOccluded(this._lngLat);
28378		if (!terrain || occluded) {
28379			const targetOpacity = occluded ? this._opacityWhenCovered : this._opacity;
28380			if (this._element.style.opacity !== targetOpacity) {
28381				this._element.style.opacity = targetOpacity;
28382				this._element.classList.toggle("maplibregl-marker-covered", occluded);
28383			}
28384			return;
28385		}
28386		this._updateCovered();
28387	}
28388	/**
28389	* @internal
28390	* Whether the viewport shows the marker's position. There is nothing to compute for one it does not.
28391	*/
28392	_isInViewport() {
28393		const { width, height } = this._map._camera.transform;
28394		return !!this._pos && this._pos.x >= 0 && this._pos.y >= 0 && this._pos.x <= width && this._pos.y <= height;
28395	}
28396	/**
28397	* @internal
28398	* Whether the terrain covers the marker's center: the location raised by the height `offset` lifts the element
28399	* above it on screen, or the location itself when the offset lowers the element.
28400	*/
28401	_isCovered(terrain) {
28402		const transform = this._map._camera.transform;
28403		const elevation = terrain.getElevationForLngLat(this._lngLat, transform);
28404		const metersToCenter = Math.max(0, -this._offset.y) / transform.pixelsPerMeter;
28405		const elevationToCenter = Math.sin(this._map.getPitch() * Math.PI / 180) * metersToCenter;
28406		return transform.isLocationOccluded(this._lngLat, terrain, elevation + elevationToCenter);
28407	}
28408	/**
28409	* Get the marker's offset.
28410	* @returns The marker's screen coordinates in pixels.
28411	*/
28412	getOffset() {
28413		return this._offset;
28414	}
28415	/**
28416	* Sets the offset of the marker
28417	* @param offset - The offset in pixels as a {@link PointLike} object to apply relative to the element's center. Negatives indicate left and up.
28418	*/
28419	setOffset(offset) {
28420		this._offset = Point.convert(offset);
28421		this._update();
28422		return this;
28423	}
28424	/**
28425	* Adds a CSS class to the marker element.
28426	*
28427	* @param className - on-empty string with CSS class name to add to marker element
28428	*
28429	* @example
28430	* ```
28431	* let marker = new Marker()
28432	* marker.addClassName('some-class')
28433	* ```
28434	*/
28435	addClassName(className) {
28436		this._element.classList.add(className);
28437	}
28438	/**
28439	* Removes a CSS class from the marker element.
28440	*
28441	* @param className - Non-empty string with CSS class name to remove from marker element
28442	*
28443	* @example
28444	* ```ts
28445	* let marker = new Marker()
28446	* marker.removeClassName('some-class')
28447	* ```
28448	*/
28449	removeClassName(className) {
28450		this._element.classList.remove(className);
28451	}
28452	/**
28453	* Add or remove the given CSS class on the marker element, depending on whether the element currently has that class.
28454	*
28455	* @param className - Non-empty string with CSS class name to add/remove
28456	*
28457	* @returns if the class was removed return false, if class was added, then return true
28458	*
28459	* @example
28460	* ```ts
28461	* let marker = new Marker()
28462	* marker.toggleClassName('toggleClass')
28463	* ```
28464	*/
28465	toggleClassName(className) {
28466		return this._element.classList.toggle(className);
28467	}
28468	/**
28469	* Sets the `draggable` property and functionality of the marker.
28470	* A draggable default marker is also keyboard focusable and movable with the
28471	* arrow keys (see {@link MarkerOptions.draggable}); custom marker elements keep
28472	* their focusability and keyboard behavior application-owned.
28473	* @param shouldBeDraggable - Turns drag functionality on/off
28474	*/
28475	setDraggable(shouldBeDraggable) {
28476		this._draggable = !!shouldBeDraggable;
28477		this._element.classList.toggle("maplibregl-marker-draggable", this._draggable);
28478		if (this._map) {
28479			if (shouldBeDraggable) {
28480				this._map.on("mousedown", this._addDragHandler);
28481				this._map.on("touchstart", this._addDragHandler);
28482			} else {
28483				this._map.off("mousedown", this._addDragHandler);
28484				this._map.off("touchstart", this._addDragHandler);
28485			}
28486		}
28487		if (this._defaultMarker) {
28488			if (this._draggable) {
28489				this._element.addEventListener("keydown", this._onKeyDown);
28490				this._element.addEventListener("keyup", this._onKeyUp);
28491				this._element.addEventListener("blur", this._onBlur);
28492			} else {
28493				this._element.removeEventListener("keydown", this._onKeyDown);
28494				this._element.removeEventListener("keyup", this._onKeyUp);
28495				this._element.removeEventListener("blur", this._onBlur);
28496				this._endKeyboardDrag();
28497			}
28498		}
28499		this._updateTabIndex();
28500		this._updateAccessibilityRole();
28501		return this;
28502	}
28503	/**
28504	* Returns true if the marker can be dragged
28505	* @returns True if the marker is draggable.
28506	*/
28507	isDraggable() {
28508		return this._draggable;
28509	}
28510	/**
28511	* Keep the marker element focusable while it has built-in keyboard behavior.
28512	* A popup makes any marker element interactive; dragging only manages
28513	* focusability for the default marker so custom elements stay
28514	* application-owned (#7790). A tabindex supplied by the application is
28515	* never added, changed, or removed here.
28516	*/
28517	_updateTabIndex() {
28518		if (!!this._popup || this._defaultMarker && this._draggable) {
28519			if (!this._element.hasAttribute("tabindex")) {
28520				this._element.setAttribute("tabindex", "0");
28521				this._tabIndexManaged = true;
28522			}
28523		} else if (this._tabIndexManaged) {
28524			if (this._element.getAttribute("tabindex") === "0") this._element.removeAttribute("tabindex");
28525			this._tabIndexManaged = false;
28526		}
28527	}
28528	/**
28529	* Keep the default marker role aligned with interactivity.
28530	* Default markers need a role because `aria-label` is set in {@link Marker.addTo}.
28531	* Non-interactive markers use `role=img`; interactive ones (draggable or with a popup) use `role=button`.
28532	* Click listeners are application-owned and do not automatically change the role.
28533	* Custom marker elements are left alone so applications own their a11y tree.
28534	* Explicit roles set by the application are preserved.
28535	*/
28536	_updateAccessibilityRole() {
28537		if (!this._defaultMarker) return;
28538		if (this._element.hasAttribute("role") && !this._roleManaged) return;
28539		const role = this._draggable || !!this._popup ? "button" : "img";
28540		this._element.setAttribute("role", role);
28541		this._roleManaged = true;
28542	}
28543	/**
28544	* Sets the `rotation` property of the marker.
28545	* @param rotation - The rotation angle of the marker (clockwise, in degrees), relative to its respective {@link Marker.setRotationAlignment} setting.
28546	*/
28547	setRotation(rotation) {
28548		this._rotation = rotation || 0;
28549		this._update();
28550		return this;
28551	}
28552	/**
28553	* Returns the current rotation angle of the marker (in degrees).
28554	* @returns The current rotation angle of the marker.
28555	*/
28556	getRotation() {
28557		return this._rotation;
28558	}
28559	/**
28560	* Sets the `rotationAlignment` property of the marker.
28561	* @param alignment - Sets the `rotationAlignment` property of the marker. defaults to 'auto'
28562	*/
28563	setRotationAlignment(alignment) {
28564		this._rotationAlignment = alignment || "auto";
28565		this._update();
28566		return this;
28567	}
28568	/**
28569	* Returns the current `rotationAlignment` property of the marker.
28570	* @returns The current rotational alignment of the marker.
28571	*/
28572	getRotationAlignment() {
28573		return this._rotationAlignment;
28574	}
28575	/**
28576	* Sets the `pitchAlignment` property of the marker.
28577	* @param alignment - Sets the `pitchAlignment` property of the marker. If alignment is 'auto', it will automatically match `rotationAlignment`.
28578	*/
28579	setPitchAlignment(alignment) {
28580		this._pitchAlignment = alignment && alignment !== "auto" ? alignment : this._rotationAlignment;
28581		this._update();
28582		return this;
28583	}
28584	/**
28585	* Returns the current `pitchAlignment` property of the marker.
28586	* @returns The current pitch alignment of the marker in degrees.
28587	*/
28588	getPitchAlignment() {
28589		return this._pitchAlignment;
28590	}
28591	/**
28592	* Sets the `opacity` and `opacityWhenCovered` properties of the marker.
28593	* When called without arguments, resets opacity and opacityWhenCovered to defaults
28594	* @param opacity - Sets the `opacity` property of the marker.
28595	* @param opacityWhenCovered - Sets the `opacityWhenCovered` property of the marker.
28596	*/
28597	setOpacity(opacity, opacityWhenCovered) {
28598		if (this._opacity === void 0 || opacity === void 0 && opacityWhenCovered === void 0) {
28599			this._opacity = "1";
28600			this._opacityWhenCovered = "0.2";
28601		}
28602		if (opacity !== void 0) this._opacity = String(opacity);
28603		if (opacityWhenCovered !== void 0) this._opacityWhenCovered = String(opacityWhenCovered);
28604		if (this._map) this._updateOpacity();
28605		return this;
28606	}
28607};
28608//#endregion
28609//#region src/ui/control/geolocate_control.ts
28610const defaultOptions$2 = {
28611	positionOptions: {
28612		enableHighAccuracy: false,
28613		maximumAge: 0,
28614		timeout: 6e3
28615	},
28616	fitBoundsOptions: { maxZoom: 15 },
28617	trackUserLocation: false,
28618	showAccuracyCircle: true,
28619	showUserLocation: true
28620};
28621let numberOfWatches = 0;
28622let noTimeout = false;
28623/**
28624* The event class for geolocate control state events
28625* (`trackuserlocationstart`, `trackuserlocationend`, `userlocationfocus` and `userlocationlostfocus`).
28626*
28627* @group Event Related
28628*/
28629var GeolocateEvent = class extends Event {};
28630/**
28631* The event class for the geolocate control `geolocate` and `outofmaxbounds` events.
28632* Carries the [Position](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPosition) returned by the Geolocation API.
28633*
28634* @group Event Related
28635*/
28636var GeolocatePositionEvent = class extends Event {};
28637/**
28638* The event class for the geolocate control `error` event.
28639* Carries the [PositionError](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError) returned by the Geolocation API.
28640*
28641* @group Event Related
28642*/
28643var GeolocateErrorEvent = class extends Event {};
28644/**
28645* A `GeolocateControl` control provides a button that uses the browser's geolocation
28646* API to locate the user on the map.
28647*
28648* Not all browsers support geolocation,
28649* and some users may disable the feature. Geolocation support for modern
28650* browsers including Chrome requires sites to be served over HTTPS. If
28651* geolocation support is not available, the `GeolocateControl` will show
28652* as disabled.
28653*
28654* The zoom level applied will depend on the accuracy of the geolocation provided by the device.
28655*
28656* The `GeolocateControl` has two modes. If `trackUserLocation` is `false` (default) the control acts as a button, which when pressed will set the map's camera to target the user location. If the user moves, the map won't update. This is most suited for the desktop. If `trackUserLocation` is `true` the control acts as a toggle button that when active the user's location is actively monitored for changes. In this mode the `GeolocateControl` has three interaction states:
28657* * active - the map's camera automatically updates as the user's location changes, keeping the location dot in the center. Initial state and upon clicking the `GeolocateControl` button.
28658* * passive - the user's location dot automatically updates, but the map's camera does not. Occurs upon the user initiating a map movement.
28659* * disabled - occurs if Geolocation is not available, disabled or denied.
28660*
28661* These interaction states can't be controlled programmatically, rather they are set based on user interactions.
28662*
28663* ## State Diagram
28664* ![GeolocateControl state diagram](https://github.com/maplibre/maplibre-gl-js/assets/3269297/78e720e5-d781-4da8-9803-a7a0e6aaaa9f)
28665*
28666* @group Markers and Controls
28667*
28668* @example
28669* ```ts
28670* map.addControl(new GeolocateControl({
28671*     positionOptions: {
28672*         enableHighAccuracy: true
28673*     },
28674*     trackUserLocation: true
28675* }));
28676* ```
28677* @see [Locate the user](https://maplibre.org/maplibre-gl-js/docs/examples/locate-the-user/)
28678*
28679* ## Events
28680*
28681* **Event** `trackuserlocationend` of type {@link Event} will be fired when the `GeolocateControl` changes to the background state, which happens when a user changes the camera during an active position lock. This only applies when `trackUserLocation` is `true`. In the background state, the dot on the map will update with location updates but the camera will not.
28682*
28683* **Event** `trackuserlocationstart` of type {@link Event} will be fired when the `GeolocateControl` changes to the active lock state, which happens either upon first obtaining a successful Geolocation API position for the user (a `geolocate` event will follow), or the user clicks the geolocate button when in the background state which uses the last known position to recenter the map and enter active lock state (no `geolocate` event will follow unless the users's location changes).
28684*
28685* **Event** `userlocationlostfocus` of type {@link Event} will be fired when the `GeolocateControl` changes to the background state, which happens when a user changes the camera during an active position lock. This only applies when `trackUserLocation` is `true`. In the background state, the dot on the map will update with location updates but the camera will not.
28686*
28687* **Event** `userlocationfocus` of type {@link Event} will be fired when the `GeolocateControl` changes to the active lock state, which happens upon the user clicks the geolocate button when in the background state which uses the last known position to recenter the map and enter active lock state.
28688*
28689* **Event** `geolocate` of type {@link Event} will be fired on each Geolocation API position update which returned as success.
28690* `data` - The returned [Position](https://developer.mozilla.org/en-US/docs/Web/API/Position) object from the callback in [Geolocation.getCurrentPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) or [Geolocation.watchPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition).
28691*
28692* **Event** `error` of type {@link Event} will be fired on each Geolocation API position update which returned as an error.
28693* `data` - The returned [PositionError](https://developer.mozilla.org/en-US/docs/Web/API/PositionError) object from the callback in [Geolocation.getCurrentPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) or [Geolocation.watchPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition).
28694*
28695* **Event** `outofmaxbounds` of type {@link Event} will be fired on each Geolocation API position update which returned as success but user position is out of map `maxBounds`.
28696* `data` - The returned [Position](https://developer.mozilla.org/en-US/docs/Web/API/Position) object from the callback in [Geolocation.getCurrentPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) or [Geolocation.watchPosition()](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition).
28697*
28698* @example
28699* ```ts
28700* // Initialize the geolocate control.
28701* let geolocate = new GeolocateControl({
28702*   positionOptions: {
28703*       enableHighAccuracy: true
28704*   },
28705*   trackUserLocation: true
28706* });
28707* // Add the control to the map.
28708* map.addControl(geolocate);
28709* // Set an event listener that fires
28710* // when a trackuserlocationend event occurs.
28711* geolocate.on('trackuserlocationend', () => {
28712*   console.log('A trackuserlocationend event has occurred.')
28713* });
28714* ```
28715*
28716* @example
28717* ```ts
28718* // Initialize the geolocate control.
28719* let geolocate = new GeolocateControl({
28720*   positionOptions: {
28721*       enableHighAccuracy: true
28722*   },
28723*   trackUserLocation: true
28724* });
28725* // Add the control to the map.
28726* map.addControl(geolocate);
28727* // Set an event listener that fires
28728* // when a trackuserlocationstart event occurs.
28729* geolocate.on('trackuserlocationstart', () => {
28730*   console.log('A trackuserlocationstart event has occurred.')
28731* });
28732* ```
28733*
28734* @example
28735* ```ts
28736* // Initialize the geolocate control.
28737* let geolocate = new GeolocateControl({
28738*   positionOptions: {
28739*       enableHighAccuracy: true
28740*   },
28741*   trackUserLocation: true
28742* });
28743* // Add the control to the map.
28744* map.addControl(geolocate);
28745* // Set an event listener that fires
28746* // when an userlocationlostfocus event occurs.
28747* geolocate.on('userlocationlostfocus', function() {
28748*   console.log('An userlocationlostfocus event has occurred.')
28749* });
28750* ```
28751*
28752* @example
28753* ```ts
28754* // Initialize the geolocate control.
28755* let geolocate = new GeolocateControl({
28756*   positionOptions: {
28757*       enableHighAccuracy: true
28758*   },
28759*   trackUserLocation: true
28760* });
28761* // Add the control to the map.
28762* map.addControl(geolocate);
28763* // Set an event listener that fires
28764* // when an userlocationfocus event occurs.
28765* geolocate.on('userlocationfocus', function() {
28766*   console.log('An userlocationfocus event has occurred.')
28767* });
28768* ```
28769*
28770* @example
28771* ```ts
28772* // Initialize the geolocate control.
28773* let geolocate = new GeolocateControl({
28774*   positionOptions: {
28775*       enableHighAccuracy: true
28776*   },
28777*   trackUserLocation: true
28778* });
28779* // Add the control to the map.
28780* map.addControl(geolocate);
28781* // Set an event listener that fires
28782* // when a geolocate event occurs.
28783* geolocate.on('geolocate', () => {
28784*   console.log('A geolocate event has occurred.')
28785* });
28786* ```
28787*
28788* @example
28789* ```ts
28790* // Initialize the geolocate control.
28791* let geolocate = new GeolocateControl({
28792*   positionOptions: {
28793*       enableHighAccuracy: true
28794*   },
28795*   trackUserLocation: true
28796* });
28797* // Add the control to the map.
28798* map.addControl(geolocate);
28799* // Set an event listener that fires
28800* // when an error event occurs.
28801* geolocate.on('error', () => {
28802*   console.log('An error event has occurred.')
28803* });
28804* ```
28805*
28806* @example
28807* ```ts
28808* // Initialize the geolocate control.
28809* let geolocate = new GeolocateControl({
28810*   positionOptions: {
28811*       enableHighAccuracy: true
28812*   },
28813*   trackUserLocation: true
28814* });
28815* // Add the control to the map.
28816* map.addControl(geolocate);
28817* // Set an event listener that fires
28818* // when an outofmaxbounds event occurs.
28819* geolocate.on('outofmaxbounds', () => {
28820*   console.log('An outofmaxbounds event has occurred.')
28821* });
28822* ```
28823*/
28824var GeolocateControl = class extends Evented {
28825	/**
28826	* @param options - the control's options
28827	*/
28828	constructor(options) {
28829		super();
28830		this._onSuccess = (position) => {
28831			if (!this._map) return;
28832			if (this._isOutOfMapMaxBounds(position)) {
28833				this._setErrorState();
28834				this.fire(new GeolocatePositionEvent("outofmaxbounds", position));
28835				this._updateMarker();
28836				this._finish();
28837				return;
28838			}
28839			if (this.options.trackUserLocation) {
28840				this._lastKnownPosition = position;
28841				switch (this._watchState) {
28842					case "WAITING_ACTIVE":
28843					case "ACTIVE_LOCK":
28844					case "ACTIVE_ERROR":
28845						this._watchState = "ACTIVE_LOCK";
28846						this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting");
28847						this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active-error");
28848						this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active");
28849						break;
28850					case "BACKGROUND":
28851					case "BACKGROUND_ERROR":
28852						this._watchState = "BACKGROUND";
28853						this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting");
28854						this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background-error");
28855						this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-background");
28856						break;
28857					default: throw new Error(`Unexpected watchState ${this._watchState}`);
28858				}
28859			}
28860			if (this.options.showUserLocation && this._watchState !== "OFF") this._updateMarker(position);
28861			if (!this.options.trackUserLocation || this._watchState === "ACTIVE_LOCK") this._updateCamera(position);
28862			if (this.options.showUserLocation) this._dotElement.classList.remove("maplibregl-user-location-dot-stale");
28863			this.fire(new GeolocatePositionEvent("geolocate", position));
28864			this._finish();
28865		};
28866		this._updateCamera = (position) => {
28867			const center = new LngLat(position.coords.longitude, position.coords.latitude);
28868			const radius = position.coords.accuracy;
28869			const bearing = this._map.getBearing();
28870			const options = extend({ bearing }, this.options.fitBoundsOptions);
28871			const newBounds = LngLatBounds.fromLngLat(center, radius);
28872			this._map.fitBounds(newBounds, options, { geolocateSource: true });
28873		};
28874		this._updateMarker = (position) => {
28875			if (position) {
28876				const center = new LngLat(position.coords.longitude, position.coords.latitude);
28877				this._accuracyCircleMarker.setLngLat(center).addTo(this._map);
28878				this._userLocationDotMarker.setLngLat(center).addTo(this._map);
28879				this._accuracy = position.coords.accuracy;
28880				this._updateCircleRadiusIfNeeded();
28881			} else {
28882				this._userLocationDotMarker.remove();
28883				this._accuracyCircleMarker.remove();
28884			}
28885		};
28886		this._onUpdate = () => {
28887			this._updateCircleRadiusIfNeeded();
28888		};
28889		this._onError = (error) => {
28890			if (!this._map) return;
28891			if (error.code === 1) {
28892				this._watchState = "OFF";
28893				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting");
28894				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active");
28895				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active-error");
28896				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background");
28897				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background-error");
28898				this._geolocateButton.disabled = true;
28899				const title = this._map._getUIString("GeolocateControl.LocationNotAvailable");
28900				this._geolocateButton.title = title;
28901				this._geolocateButton.setAttribute("aria-label", title);
28902				if (this._geolocationWatchID !== void 0) this._clearWatch();
28903			} else if (error.code === 3 && noTimeout) return;
28904			else this._setErrorState();
28905			if (this._watchState !== "OFF" && this.options.showUserLocation) this._dotElement.classList.add("maplibregl-user-location-dot-stale");
28906			this.fire(new GeolocateErrorEvent("error", error));
28907			this._finish();
28908		};
28909		this._finish = () => {
28910			if (this._timeoutId) clearTimeout(this._timeoutId);
28911			this._timeoutId = void 0;
28912		};
28913		this._onMoveStart = (event) => {
28914			if (!this._map) return;
28915			const fromResize = event?.[0] instanceof ResizeObserverEntry;
28916			if (!event.geolocateSource && this._watchState === "ACTIVE_LOCK" && !fromResize && !this._map.isZooming()) {
28917				this._watchState = "BACKGROUND";
28918				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-background");
28919				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active");
28920				this.fire(new GeolocateEvent("trackuserlocationend"));
28921				this.fire(new GeolocateEvent("userlocationlostfocus"));
28922			}
28923		};
28924		this._setupUI = () => {
28925			if (!this._map) return;
28926			this._container.addEventListener("contextmenu", (e) => {
28927				e.preventDefault();
28928			});
28929			this._geolocateButton = DOM.create("button", "maplibregl-ctrl-geolocate", this._container);
28930			DOM.create("span", "maplibregl-ctrl-icon", this._geolocateButton).setAttribute("aria-hidden", "true");
28931			this._geolocateButton.type = "button";
28932			this._geolocateButton.disabled = true;
28933		};
28934		this._finishSetupUI = (supported) => {
28935			if (!this._map) return;
28936			if (supported === false) {
28937				warnOnce("Geolocation support is not available so the GeolocateControl will be disabled.");
28938				const title = this._map._getUIString("GeolocateControl.LocationNotAvailable");
28939				this._geolocateButton.disabled = true;
28940				this._geolocateButton.title = title;
28941				this._geolocateButton.setAttribute("aria-label", title);
28942			} else {
28943				const title = this._map._getUIString("GeolocateControl.FindMyLocation");
28944				this._geolocateButton.disabled = false;
28945				this._geolocateButton.title = title;
28946				this._geolocateButton.setAttribute("aria-label", title);
28947			}
28948			if (this.options.trackUserLocation) {
28949				this._geolocateButton.setAttribute("aria-pressed", "false");
28950				this._watchState = "OFF";
28951			}
28952			if (this.options.showUserLocation) {
28953				this._dotElement = DOM.create("div", "maplibregl-user-location-dot");
28954				this._userLocationDotMarker = new Marker({ element: this._dotElement });
28955				this._circleElement = DOM.create("div", "maplibregl-user-location-accuracy-circle");
28956				this._accuracyCircleMarker = new Marker({
28957					element: this._circleElement,
28958					pitchAlignment: "map"
28959				});
28960				if (this.options.trackUserLocation) this._watchState = "OFF";
28961				this._map.on("zoom", this._onUpdate);
28962				this._map.on("move", this._onUpdate);
28963				this._map.on("rotate", this._onUpdate);
28964				this._map.on("pitch", this._onUpdate);
28965			}
28966			this._geolocateButton.addEventListener("click", () => this.trigger());
28967			this._setup = true;
28968			if (this.options.trackUserLocation) this._map.on("movestart", this._onMoveStart);
28969		};
28970		this.options = extend({}, defaultOptions$2, options);
28971	}
28972	/** {@inheritDoc IControl.onAdd} */
28973	onAdd(map) {
28974		this._map = map;
28975		this._container = DOM.create("div", "maplibregl-ctrl maplibregl-ctrl-group");
28976		this._setupUI();
28977		checkGeolocationSupport().then((supported) => this._finishSetupUI(supported));
28978		return this._container;
28979	}
28980	/** {@inheritDoc IControl.onRemove} */
28981	onRemove() {
28982		if (this._geolocationWatchID !== void 0) {
28983			window.navigator.geolocation.clearWatch(this._geolocationWatchID);
28984			this._geolocationWatchID = void 0;
28985		}
28986		if (this.options.showUserLocation && this._userLocationDotMarker) this._userLocationDotMarker.remove();
28987		if (this.options.showAccuracyCircle && this._accuracyCircleMarker) this._accuracyCircleMarker.remove();
28988		this._container.remove();
28989		this._map.off("movestart", this._onMoveStart);
28990		this._map.off("zoom", this._onUpdate);
28991		this._map.off("move", this._onUpdate);
28992		this._map.off("rotate", this._onUpdate);
28993		this._map.off("pitch", this._onUpdate);
28994		this._map = void 0;
28995		numberOfWatches = 0;
28996		noTimeout = false;
28997	}
28998	/**
28999	* Check if the Geolocation API Position is outside the map's `maxBounds`.
29000	*
29001	* @param position - the Geolocation API Position
29002	* @returns `true` if position is outside the map's `maxBounds`, otherwise returns `false`.
29003	*/
29004	_isOutOfMapMaxBounds(position) {
29005		const bounds = this._map.getMaxBounds();
29006		const coordinates = position.coords;
29007		return bounds && (coordinates.longitude < bounds.getWest() || coordinates.longitude > bounds.getEast() || coordinates.latitude < bounds.getSouth() || coordinates.latitude > bounds.getNorth());
29008	}
29009	_setErrorState() {
29010		switch (this._watchState) {
29011			case "WAITING_ACTIVE":
29012				this._watchState = "ACTIVE_ERROR";
29013				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active");
29014				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active-error");
29015				break;
29016			case "ACTIVE_LOCK":
29017				this._watchState = "ACTIVE_ERROR";
29018				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active");
29019				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active-error");
29020				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting");
29021				break;
29022			case "BACKGROUND":
29023				this._watchState = "BACKGROUND_ERROR";
29024				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background");
29025				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-background-error");
29026				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting");
29027				break;
29028			case "ACTIVE_ERROR":
29029			case "BACKGROUND_ERROR": break;
29030			case "OFF":
29031			case void 0: break;
29032			default: throw new Error(`Unexpected watchState ${this._watchState}`);
29033		}
29034	}
29035	_updateCircleRadiusIfNeeded() {
29036		const userLocation = this._userLocationDotMarker.getLngLat();
29037		if (!this.options.showUserLocation || !this.options.showAccuracyCircle || !this._accuracy || !userLocation) return;
29038		const screenPosition = this._map.project(userLocation);
29039		const userLocationWith100Px = this._map.unproject([screenPosition.x + 100, screenPosition.y]);
29040		const pixelsToMeters = userLocation.distanceTo(userLocationWith100Px) / 100;
29041		const circleDiameter = 2 * this._accuracy / pixelsToMeters;
29042		this._circleElement.style.width = `${circleDiameter.toFixed(2)}px`;
29043		this._circleElement.style.height = `${circleDiameter.toFixed(2)}px`;
29044	}
29045	/**
29046	* Programmatically request and move the map to the user's location.
29047	*
29048	* @returns `false` if called before control was added to a map, otherwise returns `true`.
29049	* @example
29050	* ```ts
29051	* // Initialize the geolocate control.
29052	* let geolocate = new GeolocateControl({
29053	*  positionOptions: {
29054	*    enableHighAccuracy: true
29055	*  },
29056	*  trackUserLocation: true
29057	* });
29058	* // Add the control to the map.
29059	* map.addControl(geolocate);
29060	* map.on('load', () => {
29061	*   geolocate.trigger();
29062	* });
29063	* ```
29064	*/
29065	trigger() {
29066		if (!this._setup) {
29067			warnOnce("Geolocate control triggered before added to a map");
29068			return false;
29069		}
29070		if (this.options.trackUserLocation) {
29071			switch (this._watchState) {
29072				case "OFF":
29073					this._watchState = "WAITING_ACTIVE";
29074					this.fire(new GeolocateEvent("trackuserlocationstart"));
29075					break;
29076				case "WAITING_ACTIVE":
29077				case "ACTIVE_LOCK":
29078				case "ACTIVE_ERROR":
29079				case "BACKGROUND_ERROR":
29080					numberOfWatches--;
29081					noTimeout = false;
29082					this._watchState = "OFF";
29083					this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting");
29084					this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active");
29085					this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active-error");
29086					this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background");
29087					this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background-error");
29088					this.fire(new GeolocateEvent("trackuserlocationend"));
29089					break;
29090				case "BACKGROUND":
29091					this._watchState = "ACTIVE_LOCK";
29092					this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background");
29093					if (this._lastKnownPosition) this._updateCamera(this._lastKnownPosition);
29094					this.fire(new GeolocateEvent("trackuserlocationstart"));
29095					this.fire(new GeolocateEvent("userlocationfocus"));
29096					break;
29097				default: throw new Error(`Unexpected watchState ${this._watchState}`);
29098			}
29099			switch (this._watchState) {
29100				case "WAITING_ACTIVE":
29101					this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting");
29102					this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active");
29103					break;
29104				case "ACTIVE_LOCK":
29105					this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active");
29106					break;
29107				case "OFF": break;
29108				default: throw new Error(`Unexpected watchState ${this._watchState}`);
29109			}
29110			if (this._watchState === "OFF" && this._geolocationWatchID !== void 0) this._clearWatch();
29111			else if (this._geolocationWatchID === void 0) {
29112				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting");
29113				this._geolocateButton.setAttribute("aria-pressed", "true");
29114				numberOfWatches++;
29115				let positionOptions;
29116				if (numberOfWatches > 1) {
29117					positionOptions = {
29118						maximumAge: 6e5,
29119						timeout: 0
29120					};
29121					noTimeout = true;
29122				} else {
29123					positionOptions = this.options.positionOptions;
29124					noTimeout = false;
29125				}
29126				this._geolocationWatchID = window.navigator.geolocation.watchPosition(this._onSuccess, this._onError, positionOptions);
29127			}
29128		} else {
29129			window.navigator.geolocation.getCurrentPosition(this._onSuccess, this._onError, this.options.positionOptions);
29130			this._timeoutId = setTimeout(this._finish, 1e4);
29131		}
29132		return true;
29133	}
29134	_clearWatch() {
29135		window.navigator.geolocation.clearWatch(this._geolocationWatchID);
29136		this._geolocationWatchID = void 0;
29137		this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting");
29138		this._geolocateButton.setAttribute("aria-pressed", "false");
29139		if (this.options.showUserLocation) this._updateMarker(null);
29140	}
29141};
29142//#endregion
29143//#region src/ui/control/scale_control.ts
29144const defaultOptions$1 = {
29145	maxWidth: 100,
29146	unit: "metric"
29147};
29148/**
29149* A `ScaleControl` control displays the ratio of a distance on the map to the corresponding distance on the ground.
29150*
29151* @group Markers and Controls
29152*
29153* @example
29154* ```ts
29155* let scale = new ScaleControl({
29156*     maxWidth: 80,
29157*     unit: 'imperial'
29158* });
29159* map.addControl(scale);
29160*
29161* scale.setUnit('metric');
29162* ```
29163*/
29164var ScaleControl = class {
29165	/**
29166	* @param options - the control's options
29167	*/
29168	constructor(options) {
29169		this._onMove = () => {
29170			updateScale(this._map, this._container, this.options);
29171		};
29172		this.setUnit = (unit) => {
29173			this.options.unit = unit;
29174			updateScale(this._map, this._container, this.options);
29175		};
29176		this.options = {
29177			...defaultOptions$1,
29178			...options
29179		};
29180	}
29181	getDefaultPosition() {
29182		return "bottom-left";
29183	}
29184	/** {@inheritDoc IControl.onAdd} */
29185	onAdd(map) {
29186		this._map = map;
29187		this._container = DOM.create("div", "maplibregl-ctrl maplibregl-ctrl-scale", map.getContainer());
29188		this._map.on("move", this._onMove);
29189		this._onMove();
29190		return this._container;
29191	}
29192	/** {@inheritDoc IControl.onRemove} */
29193	onRemove() {
29194		this._container.remove();
29195		this._map.off("move", this._onMove);
29196		this._map = void 0;
29197	}
29198};
29199function updateScale(map, container, options) {
29200	const optWidth = options?.maxWidth || 100;
29201	const { width: containerWidth, height: containerHeight } = map._camera.transform;
29202	const y = containerHeight / 2;
29203	const x = containerWidth / 2;
29204	const left = map.unproject([x - optWidth / 2, y]);
29205	const right = map.unproject([x + optWidth / 2, y]);
29206	const globeWidth = Math.round(map.project(right).x - map.project(left).x);
29207	const maxWidth = Math.min(optWidth, globeWidth, containerWidth);
29208	const maxMeters = left.distanceTo(right);
29209	if (options?.unit === "imperial") {
29210		const maxFeet = 3.2808 * maxMeters;
29211		if (maxFeet > 5280) setScale(container, maxWidth, maxFeet / 5280, map._getUIString("ScaleControl.Miles"));
29212		else setScale(container, maxWidth, maxFeet, map._getUIString("ScaleControl.Feet"));
29213	} else if (options?.unit === "nautical") setScale(container, maxWidth, maxMeters / 1852, map._getUIString("ScaleControl.NauticalMiles"));
29214	else if (maxMeters >= 1e3) setScale(container, maxWidth, maxMeters / 1e3, map._getUIString("ScaleControl.Kilometers"));
29215	else setScale(container, maxWidth, maxMeters, map._getUIString("ScaleControl.Meters"));
29216}
29217function setScale(container, maxWidth, maxDistance, unit) {
29218	const distance = getRoundNum(maxDistance);
29219	const ratio = distance / maxDistance;
29220	container.style.width = `${maxWidth * ratio}px`;
29221	const label = `${distance}\u00a0${unit}`;
29222	if (container.textContent !== label) container.textContent = label;
29223}
29224function getDecimalRoundNum(d) {
29225	const multiplier = Math.pow(10, Math.ceil(-Math.log(d) / Math.LN10));
29226	return Math.round(d * multiplier) / multiplier;
29227}
29228function getRoundNum(num) {
29229	const pow10 = Math.pow(10, `${Math.floor(num)}`.length - 1);
29230	let d = num / pow10;
29231	d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : d >= 1 ? 1 : getDecimalRoundNum(d);
29232	return pow10 * d;
29233}
29234//#endregion
29235//#region src/ui/control/fullscreen_control.ts
29236/**
29237* The event class for fullscreen control events (`fullscreenstart` and `fullscreenend`).
29238*
29239* @group Event Related
29240*/
29241var FullscreenEvent = class extends Event {};
29242/**
29243* A `FullscreenControl` control contains a button for toggling the map in and out of fullscreen mode.
29244* When [requestFullscreen](https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullscreen) is not supported, fullscreen is handled via CSS properties.
29245* The map's `cooperativeGestures` option is temporarily disabled while the map
29246* is in fullscreen mode, and is restored when the map exist fullscreen mode.
29247*
29248* @group Markers and Controls
29249* @param options - the full screen control options
29250*
29251* @example
29252* ```ts
29253* map.addControl(new FullscreenControl({container: document.querySelector('body')}));
29254* ```
29255* @see [View a fullscreen map](https://maplibre.org/maplibre-gl-js/docs/examples/view-a-fullscreen-map/)
29256*
29257* ## Events
29258*
29259* **Event** `fullscreenstart` of type {@link FullscreenEvent} will be fired when fullscreen mode has started.
29260*
29261* **Event** `fullscreenend` of type {@link FullscreenEvent} will be fired when fullscreen mode has ended.
29262*/
29263var FullscreenControl = class extends Evented {
29264	/**
29265	* @param options - the control's options
29266	*/
29267	constructor(options = {}) {
29268		super();
29269		this._onFullscreenChange = () => {
29270			let fullscreenElement = window.document.fullscreenElement || window.document.webkitFullscreenElement;
29271			while (fullscreenElement?.shadowRoot?.fullscreenElement) fullscreenElement = fullscreenElement.shadowRoot.fullscreenElement;
29272			if (fullscreenElement === this._container !== this._fullscreen) this._handleFullscreenChange();
29273		};
29274		this._onClickFullscreen = () => {
29275			if (this._isFullscreen()) this._exitFullscreen();
29276			else this._requestFullscreen();
29277		};
29278		this._fullscreen = false;
29279		this._pseudo = options.pseudo ?? false;
29280		if (options?.container) {
29281			if (options.container instanceof HTMLElement) this._container = options.container;
29282			else warnOnce("Full screen control 'container' must be a DOM element.");
29283		}
29284		if ("onfullscreenchange" in document) this._fullscreenchange = "fullscreenchange";
29285		else if ("onmozfullscreenchange" in document) this._fullscreenchange = "mozfullscreenchange";
29286		else if ("onwebkitfullscreenchange" in document) this._fullscreenchange = "webkitfullscreenchange";
29287		else if ("onmsfullscreenchange" in document) this._fullscreenchange = "MSFullscreenChange";
29288	}
29289	/** {@inheritDoc IControl.onAdd} */
29290	onAdd(map) {
29291		this._map = map;
29292		this._container ||= this._map.getContainer();
29293		this._controlContainer = DOM.create("div", "maplibregl-ctrl maplibregl-ctrl-group");
29294		this._setupUI();
29295		return this._controlContainer;
29296	}
29297	/** {@inheritDoc IControl.onRemove} */
29298	onRemove() {
29299		this._controlContainer.remove();
29300		this._map = null;
29301		window.document.removeEventListener(this._fullscreenchange, this._onFullscreenChange);
29302	}
29303	_setupUI() {
29304		const button = this._fullscreenButton = DOM.create("button", "maplibregl-ctrl-fullscreen", this._controlContainer);
29305		DOM.create("span", "maplibregl-ctrl-icon", button).setAttribute("aria-hidden", "true");
29306		button.type = "button";
29307		this._updateTitle();
29308		this._fullscreenButton.addEventListener("click", this._onClickFullscreen);
29309		window.document.addEventListener(this._fullscreenchange, this._onFullscreenChange);
29310	}
29311	_updateTitle() {
29312		const title = this._getTitle();
29313		this._fullscreenButton.setAttribute("aria-label", title);
29314		this._fullscreenButton.title = title;
29315	}
29316	_getTitle() {
29317		return this._map._getUIString(this._isFullscreen() ? "FullscreenControl.Exit" : "FullscreenControl.Enter");
29318	}
29319	_isFullscreen() {
29320		return this._fullscreen;
29321	}
29322	_handleFullscreenChange() {
29323		this._fullscreen = !this._fullscreen;
29324		this._fullscreenButton.classList.toggle("maplibregl-ctrl-shrink");
29325		this._fullscreenButton.classList.toggle("maplibregl-ctrl-fullscreen");
29326		this._updateTitle();
29327		if (this._fullscreen) {
29328			this.fire(new FullscreenEvent("fullscreenstart"));
29329			this._prevCooperativeGesturesEnabled = this._map.cooperativeGestures.isEnabled();
29330			this._map.cooperativeGestures.disable();
29331		} else {
29332			this.fire(new FullscreenEvent("fullscreenend"));
29333			if (this._prevCooperativeGesturesEnabled) this._map.cooperativeGestures.enable();
29334		}
29335	}
29336	_exitFullscreen() {
29337		if (this._pseudo) this._togglePseudoFullScreen();
29338		else if (window.document.exitFullscreen) window.document.exitFullscreen();
29339		else if (window.document.webkitCancelFullScreen) window.document.webkitCancelFullScreen();
29340		else this._togglePseudoFullScreen();
29341	}
29342	_requestFullscreen() {
29343		if (this._pseudo) this._togglePseudoFullScreen();
29344		else if (this._container.requestFullscreen) this._container.requestFullscreen();
29345		else if (this._container.webkitRequestFullscreen) this._container.webkitRequestFullscreen();
29346		else this._togglePseudoFullScreen();
29347	}
29348	_togglePseudoFullScreen() {
29349		this._container.classList.toggle("maplibregl-pseudo-fullscreen");
29350		this._handleFullscreenChange();
29351		this._map.resize();
29352	}
29353};
29354//#endregion
29355//#region src/ui/control/terrain_control.ts
29356/**
29357* A `TerrainControl` control contains a button for turning the terrain on and off.
29358*
29359* @group Markers and Controls
29360*
29361* @example
29362* ```ts
29363* let map = new Map({TerrainControl: false})
29364*     .addControl(new TerrainControl({
29365*         source: "terrain"
29366*     }));
29367* ```
29368* @see [3D Terrain](https://maplibre.org/maplibre-gl-js/docs/examples/3d-terrain/)
29369* @see [Create a Heatmap layer on a globe with terrain elevation](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-heatmap-layer-on-a-globe-with-terrain-elevation/)
29370* @see [Display a hybrid satellite map with terrain elevation](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-hybrid-satellite-map-with-terrain-elevation/)
29371* @see [Sky, Fog, Terrain](https://maplibre.org/maplibre-gl-js/docs/examples/sky-fog-terrain/)
29372*/
29373var TerrainControl = class {
29374	/**
29375	* @param options - the control's options
29376	*/
29377	constructor(options) {
29378		this._toggleTerrain = () => {
29379			if (this._map.getTerrain()) this._map.setTerrain(null);
29380			else this._map.setTerrain(this.options);
29381			this._updateTerrainIcon();
29382		};
29383		this._updateTerrainIcon = () => {
29384			this._terrainButton.classList.remove("maplibregl-ctrl-terrain");
29385			this._terrainButton.classList.remove("maplibregl-ctrl-terrain-enabled");
29386			if (this._map.terrain) {
29387				this._terrainButton.classList.add("maplibregl-ctrl-terrain-enabled");
29388				this._terrainButton.title = this._map._getUIString("TerrainControl.Disable");
29389			} else {
29390				this._terrainButton.classList.add("maplibregl-ctrl-terrain");
29391				this._terrainButton.title = this._map._getUIString("TerrainControl.Enable");
29392			}
29393		};
29394		this.options = options;
29395	}
29396	/** {@inheritDoc IControl.onAdd} */
29397	onAdd(map) {
29398		this._map = map;
29399		this._container = DOM.create("div", "maplibregl-ctrl maplibregl-ctrl-group");
29400		this._terrainButton = DOM.create("button", "maplibregl-ctrl-terrain", this._container);
29401		DOM.create("span", "maplibregl-ctrl-icon", this._terrainButton).setAttribute("aria-hidden", "true");
29402		this._terrainButton.type = "button";
29403		this._terrainButton.addEventListener("click", this._toggleTerrain);
29404		this._updateTerrainIcon();
29405		this._map.on("terrain", this._updateTerrainIcon);
29406		return this._container;
29407	}
29408	/** {@inheritDoc IControl.onRemove} */
29409	onRemove() {
29410		this._container.remove();
29411		this._map.off("terrain", this._updateTerrainIcon);
29412		this._map = void 0;
29413	}
29414};
29415//#endregion
29416//#region src/ui/control/globe_control.ts
29417/**
29418* A `GlobeControl` control contains a button for toggling the map projection between "mercator" and "globe".
29419*
29420* @group Markers and Controls
29421*
29422* @example
29423* ```ts
29424* let map = new Map()
29425*     .addControl(new GlobeControl());
29426* ```
29427*
29428* @see [Display a globe with a fill extrusion layer](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-globe-with-a-fill-extrusion-layer/)
29429* @see [Sky, Fog, Terrain](https://maplibre.org/maplibre-gl-js/docs/examples/sky-fog-terrain/)
29430*/
29431var GlobeControl = class {
29432	constructor() {
29433		this._toggleProjection = () => {
29434			const currentProjection = this._map.getProjection()?.type;
29435			if (currentProjection === "mercator" || !currentProjection) this._map.setProjection({ type: "globe" });
29436			else this._map.setProjection({ type: "mercator" });
29437			this._updateGlobeIcon();
29438		};
29439		this._updateGlobeIcon = () => {
29440			this._globeButton.classList.remove("maplibregl-ctrl-globe");
29441			this._globeButton.classList.remove("maplibregl-ctrl-globe-enabled");
29442			if (this._map.getProjection()?.type === "globe") {
29443				this._globeButton.classList.add("maplibregl-ctrl-globe-enabled");
29444				this._globeButton.title = this._map._getUIString("GlobeControl.Disable");
29445			} else {
29446				this._globeButton.classList.add("maplibregl-ctrl-globe");
29447				this._globeButton.title = this._map._getUIString("GlobeControl.Enable");
29448			}
29449		};
29450	}
29451	/** {@inheritDoc IControl.onAdd} */
29452	onAdd(map) {
29453		this._map = map;
29454		this._container = DOM.create("div", "maplibregl-ctrl maplibregl-ctrl-group");
29455		this._globeButton = DOM.create("button", "maplibregl-ctrl-globe", this._container);
29456		DOM.create("span", "maplibregl-ctrl-icon", this._globeButton).setAttribute("aria-hidden", "true");
29457		this._globeButton.type = "button";
29458		this._globeButton.addEventListener("click", this._toggleProjection);
29459		this._updateGlobeIcon();
29460		this._map.on("styledata", this._updateGlobeIcon);
29461		this._map.on("projectiontransition", this._updateGlobeIcon);
29462		return this._container;
29463	}
29464	/** {@inheritDoc IControl.onRemove} */
29465	onRemove() {
29466		this._container.remove();
29467		this._map.off("styledata", this._updateGlobeIcon);
29468		this._map.off("projectiontransition", this._updateGlobeIcon);
29469		this._globeButton.removeEventListener("click", this._toggleProjection);
29470		this._map = void 0;
29471	}
29472};
29473//#endregion
29474//#region src/ui/popup.ts
29475const defaultOptions = {
29476	closeButton: true,
29477	closeOnClick: true,
29478	focusAfterOpen: true,
29479	className: "",
29480	maxWidth: "240px",
29481	subpixelPositioning: false,
29482	locationOccludedOpacity: void 0,
29483	padding: void 0
29484};
29485const focusQuerySelector = [
29486	"a[href]",
29487	"[tabindex]:not([tabindex='-1'])",
29488	"[contenteditable]:not([contenteditable='false'])",
29489	"button:not([disabled])",
29490	"input:not([disabled])",
29491	"select:not([disabled])",
29492	"textarea:not([disabled])"
29493].join(", ");
29494/**
29495* The event class for popup events (`open` and `close`).
29496*
29497* @group Event Related
29498*/
29499var PopupEvent = class extends Event {};
29500/**
29501* A popup component.
29502*
29503* @group Markers and Controls
29504*
29505*
29506* @example
29507* Create a popup
29508* ```ts
29509* let popup = new Popup();
29510* // Set an event listener that will fire
29511* // any time the popup is opened
29512* popup.on('open', () => {
29513*   console.log('popup was opened');
29514* });
29515* ```
29516*
29517* @example
29518* Create a popup
29519* ```ts
29520* let popup = new Popup();
29521* // Set an event listener that will fire
29522* // any time the popup is closed
29523* popup.on('close', () => {
29524*   console.log('popup was closed');
29525* });
29526* ```
29527*
29528* @example
29529* ```ts
29530* let markerHeight = 50, markerRadius = 10, linearOffset = 25;
29531* let popupOffsets = {
29532*  'top': [0, 0],
29533*  'top-left': [0,0],
29534*  'top-right': [0,0],
29535*  'bottom': [0, -markerHeight],
29536*  'bottom-left': [linearOffset, (markerHeight - markerRadius + linearOffset) * -1],
29537*  'bottom-right': [-linearOffset, (markerHeight - markerRadius + linearOffset) * -1],
29538*  'left': [markerRadius, (markerHeight - markerRadius) * -1],
29539*  'right': [-markerRadius, (markerHeight - markerRadius) * -1]
29540*  };
29541* let popup = new Popup({offset: popupOffsets, className: 'my-class'})
29542*   .setLngLat(e.lngLat)
29543*   .setHTML("<h1>Hello World!</h1>")
29544*   .setMaxWidth("300px")
29545*   .addTo(map);
29546* ```
29547* @see [Display a popup](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup/)
29548* @see [Display a popup on hover](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-hover/)
29549* @see [Display a popup on click](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-click/)
29550* @see [Attach a popup to a marker instance](https://maplibre.org/maplibre-gl-js/docs/examples/attach-a-popup-to-a-marker-instance/)
29551* @see [Show polygon information on click](https://maplibre.org/maplibre-gl-js/docs/examples/show-polygon-information-on-click/)
29552*
29553* ## Events
29554*
29555* **Event** `open` of type {@link PopupEvent} will be fired when the popup is opened manually or programmatically.
29556*
29557* **Event** `close` of type {@link PopupEvent} will be fired when the popup is closed manually or programmatically.
29558*/
29559var Popup = class extends Evented {
29560	/**
29561	* @param options - the options
29562	*/
29563	constructor(options) {
29564		super();
29565		this._updateOpacity = () => {
29566			if (this.options.locationOccludedOpacity === void 0) return;
29567			if (this._map._camera.transform.isLocationOccluded(this.getLngLat())) this._container.style.opacity = `${this.options.locationOccludedOpacity}`;
29568			else this._container.style.opacity = "";
29569		};
29570		this.remove = () => {
29571			if (this._content) this._content.remove();
29572			if (this._container) {
29573				this._container.remove();
29574				delete this._container;
29575			}
29576			if (this._map) {
29577				this._map.off("move", this._update);
29578				this._map.off("move", this._onClose);
29579				this._map.off("click", this._onClose);
29580				this._map.off("remove", this.remove);
29581				this._map.off("terrain", this._update);
29582				this._map.off("projectiontransition", this._update);
29583				this._map.off("idle", this._update);
29584				this._map.off("mousemove", this._update);
29585				this._map.off("mouseup", this._update);
29586				this._map.off("drag", this._update);
29587				this._map._canvasContainer.classList.remove("maplibregl-track-pointer");
29588				delete this._map;
29589				this.fire(new PopupEvent("close"));
29590			}
29591			return this;
29592		};
29593		this._update = (event) => {
29594			const hasPosition = this._lngLat || this._trackPointer;
29595			if (!this._map || !hasPosition || !this._content) return;
29596			if (!this._container) {
29597				this._container = DOM.create("div", "maplibregl-popup", this._map.getContainer());
29598				this._tip = DOM.create("div", "maplibregl-popup-tip", this._container);
29599				this._container.appendChild(this._content);
29600				if (this.options.className) for (const name of this.options.className.split(" ")) this._container.classList.add(name);
29601				if (this._closeButton) this._closeButton.setAttribute("aria-label", this._map._getUIString("Popup.Close"));
29602				if (this._trackPointer) this._container.classList.add("maplibregl-popup-track-pointer");
29603			}
29604			if (this.options.maxWidth && this._container.style.maxWidth !== this.options.maxWidth) this._container.style.maxWidth = this.options.maxWidth;
29605			this._lngLat = smartWrap(this._lngLat, this._flatPos, this._map._camera.transform, this._trackPointer);
29606			let cursor;
29607			if (event && "point" in event && event.point) cursor = event.point;
29608			if (this._trackPointer && !cursor) return;
29609			const pos = this._flatPos = this._pos = this._trackPointer && cursor ? cursor : this._map.project(this._lngLat);
29610			if (this._map.terrain) this._flatPos = this._trackPointer && cursor ? cursor : this._map._camera.transform.locationToScreenPoint(this._lngLat);
29611			let anchor = this.options.anchor;
29612			const offset = normalizeOffset(this.options.offset);
29613			if (!anchor) {
29614				const width = this._container.offsetWidth;
29615				const height = this._container.offsetHeight;
29616				const padding = normalizePadding(this.options.padding);
29617				let anchorComponents;
29618				if (pos.y + offset.bottom.y < height + padding.top) anchorComponents = ["top"];
29619				else if (pos.y > this._map._camera.transform.height - height - padding.bottom) anchorComponents = ["bottom"];
29620				else anchorComponents = [];
29621				if (pos.x < width / 2 + padding.left) anchorComponents.push("left");
29622				else if (pos.x > this._map._camera.transform.width - width / 2 - padding.right) anchorComponents.push("right");
29623				if (anchorComponents.length === 0) anchor = "bottom";
29624				else anchor = anchorComponents.join("-");
29625			}
29626			let offsetedPos = pos.add(offset[anchor]);
29627			if (!this.options.subpixelPositioning) offsetedPos = offsetedPos.round();
29628			this._container.style.transform = `${anchorTranslate[anchor]} translate(${offsetedPos.x}px,${offsetedPos.y}px)`;
29629			applyAnchorClass(this._container, anchor, "popup");
29630			this._updateOpacity();
29631		};
29632		this._onClose = () => {
29633			this.remove();
29634		};
29635		this.options = extend(Object.create(defaultOptions), options);
29636	}
29637	/**
29638	* Adds the popup to a map.
29639	*
29640	* @param map - The MapLibre GL JS map to add the popup to.
29641	* @example
29642	* ```ts
29643	* new Popup()
29644	*   .setLngLat([0, 0])
29645	*   .setHTML("<h1>Null Island</h1>")
29646	*   .addTo(map);
29647	* ```
29648	* @see [Display a popup](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup/)
29649	* @see [Display a popup on hover](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-hover/)
29650	* @see [Display a popup on click](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-click/)
29651	* @see [Show polygon information on click](https://maplibre.org/maplibre-gl-js/docs/examples/show-polygon-information-on-click/)
29652	*/
29653	addTo(map) {
29654		if (this._map) this.remove();
29655		this._map = map;
29656		if (this.options.closeOnClick) this._map.on("click", this._onClose);
29657		if (this.options.closeOnMove) this._map.on("move", this._onClose);
29658		this._map.on("remove", this.remove);
29659		this._map.on("terrain", this._update);
29660		this._map.on("projectiontransition", this._update);
29661		this._map.on("idle", this._update);
29662		this._update();
29663		this._focusFirstElement();
29664		if (this._trackPointer) {
29665			this._map.on("mousemove", this._update);
29666			this._map.on("mouseup", this._update);
29667			if (this._container) this._container.classList.add("maplibregl-popup-track-pointer");
29668			this._map._canvasContainer.classList.add("maplibregl-track-pointer");
29669		} else this._map.on("move", this._update);
29670		this.fire(new PopupEvent("open"));
29671		return this;
29672	}
29673	/**
29674	* @returns `true` if the popup is open, `false` if it is closed.
29675	*/
29676	isOpen() {
29677		return !!this._map;
29678	}
29679	/**
29680	* Returns the geographical location of the popup's anchor.
29681	*
29682	* The longitude of the result may differ by a multiple of 360 degrees from the longitude previously
29683	* set by `setLngLat` because `Popup` wraps the anchor longitude across copies of the world to keep
29684	* the popup on screen.
29685	*
29686	* @returns The geographical location of the popup's anchor.
29687	*/
29688	getLngLat() {
29689		return this._lngLat;
29690	}
29691	/**
29692	* Sets the geographical location of the popup's anchor, and moves the popup to it. Replaces trackPointer() behavior.
29693	*
29694	* @param lnglat - The geographical location to set as the popup's anchor.
29695	*/
29696	setLngLat(lnglat) {
29697		this._lngLat = LngLat.convert(lnglat);
29698		this._pos = null;
29699		this._flatPos = null;
29700		this._trackPointer = false;
29701		this._update();
29702		if (this._map) {
29703			this._map.on("move", this._update);
29704			this._map.off("mousemove", this._update);
29705			if (this._container) this._container.classList.remove("maplibregl-popup-track-pointer");
29706			this._map._canvasContainer.classList.remove("maplibregl-track-pointer");
29707		}
29708		return this;
29709	}
29710	/**
29711	* Tracks the popup anchor to the cursor position on screens with a pointer device (it will be hidden on touchscreens). Replaces the `setLngLat` behavior.
29712	* For most use cases, set `closeOnClick` and `closeButton` to `false`.
29713	* @example
29714	* ```ts
29715	* let popup = new Popup({ closeOnClick: false, closeButton: false })
29716	*   .setHTML("<h1>Hello World!</h1>")
29717	*   .trackPointer()
29718	*   .addTo(map);
29719	* ```
29720	*/
29721	trackPointer() {
29722		this._trackPointer = true;
29723		this._pos = null;
29724		this._flatPos = null;
29725		this._update();
29726		if (this._map) {
29727			this._map.off("move", this._update);
29728			this._map.on("mousemove", this._update);
29729			this._map.on("drag", this._update);
29730			if (this._container) this._container.classList.add("maplibregl-popup-track-pointer");
29731			this._map._canvasContainer.classList.add("maplibregl-track-pointer");
29732		}
29733		return this;
29734	}
29735	/**
29736	* Returns the `Popup`'s HTML element.
29737	* @example
29738	* Change the `Popup` element's font size
29739	* ```ts
29740	* let popup = new Popup()
29741	*   .setLngLat([-96, 37.8])
29742	*   .setHTML("<p>Hello World!</p>")
29743	*   .addTo(map);
29744	* let popupElem = popup.getElement();
29745	* popupElem.style.fontSize = "25px";
29746	* ```
29747	* @returns element
29748	*/
29749	getElement() {
29750		return this._container;
29751	}
29752	/**
29753	* Sets the popup's content to a string of text.
29754	*
29755	* This function creates a [Text](https://developer.mozilla.org/en-US/docs/Web/API/Text) node in the DOM,
29756	* so it cannot insert raw HTML. Use this method for security against XSS
29757	* if the popup content is user-provided.
29758	*
29759	* @param text - Textual content for the popup.
29760	* @example
29761	* ```ts
29762	* let popup = new Popup()
29763	*   .setLngLat(e.lngLat)
29764	*   .setText('Hello, world!')
29765	*   .addTo(map);
29766	* ```
29767	*/
29768	setText(text) {
29769		return this.setDOMContent(document.createTextNode(text));
29770	}
29771	/**
29772	* Sets the popup's content to the HTML provided as a string.
29773	*
29774	* This method does not perform HTML filtering or sanitization, and must be
29775	* used only with trusted content. Consider {@link Popup.setText} if
29776	* the content is an untrusted text string.
29777	*
29778	* @param html - A string representing HTML content for the popup.
29779	* @example
29780	* ```ts
29781	* let popup = new Popup()
29782	*   .setLngLat(e.lngLat)
29783	*   .setHTML("<h1>Hello World!</h1>")
29784	*   .addTo(map);
29785	* ```
29786	* @see [Display a popup](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup/)
29787	* @see [Display a popup on hover](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-hover/)
29788	* @see [Display a popup on click](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-click/)
29789	* @see [Attach a popup to a marker instance](https://maplibre.org/maplibre-gl-js/docs/examples/attach-a-popup-to-a-marker-instance/)
29790	*/
29791	setHTML(html) {
29792		const frag = document.createDocumentFragment();
29793		const temp = document.createElement("body");
29794		let child;
29795		temp.innerHTML = html;
29796		while (true) {
29797			child = temp.firstChild;
29798			if (!child) break;
29799			frag.appendChild(child);
29800		}
29801		return this.setDOMContent(frag);
29802	}
29803	/**
29804	* Returns the popup's maximum width.
29805	*
29806	* @returns The maximum width of the popup.
29807	*/
29808	getMaxWidth() {
29809		return this._container?.style.maxWidth;
29810	}
29811	/**
29812	* Sets the popup's maximum width. This is setting the CSS property `max-width`.
29813	* Available values can be found here: https://developer.mozilla.org/en-US/docs/Web/CSS/max-width
29814	*
29815	* @param maxWidth - A string representing the value for the maximum width.
29816	*/
29817	setMaxWidth(maxWidth) {
29818		this.options.maxWidth = maxWidth;
29819		this._update();
29820		return this;
29821	}
29822	/**
29823	* Sets the popup's content to the element provided as a DOM node.
29824	*
29825	* @param htmlNode - A DOM node to be used as content for the popup.
29826	* @example
29827	* Create an element with the popup content
29828	* ```ts
29829	* let div = document.createElement('div');
29830	* div.innerHTML = 'Hello, world!';
29831	* let popup = new Popup()
29832	*   .setLngLat(e.lngLat)
29833	*   .setDOMContent(div)
29834	*   .addTo(map);
29835	* ```
29836	*/
29837	setDOMContent(htmlNode) {
29838		if (this._content) {
29839			while (this._content.hasChildNodes()) if (this._content.firstChild) this._content.removeChild(this._content.firstChild);
29840		} else this._content = DOM.create("div", "maplibregl-popup-content", this._container);
29841		this._content.appendChild(htmlNode);
29842		this._createCloseButton();
29843		this._update();
29844		this._focusFirstElement();
29845		return this;
29846	}
29847	/**
29848	* Adds a CSS class to the popup container element.
29849	*
29850	* @param className - Non-empty string with CSS class name to add to popup container
29851	*
29852	* @example
29853	* ```ts
29854	* let popup = new Popup()
29855	* popup.addClassName('some-class')
29856	* ```
29857	*/
29858	addClassName(className) {
29859		if (this._container) this._container.classList.add(className);
29860		return this;
29861	}
29862	/**
29863	* Removes a CSS class from the popup container element.
29864	*
29865	* @param className - Non-empty string with CSS class name to remove from popup container
29866	*
29867	* @example
29868	* ```ts
29869	* let popup = new Popup()
29870	* popup.removeClassName('some-class')
29871	* ```
29872	*/
29873	removeClassName(className) {
29874		if (this._container) this._container.classList.remove(className);
29875		return this;
29876	}
29877	/**
29878	* Sets the popup's offset.
29879	*
29880	* @param offset - Sets the popup's offset.
29881	*/
29882	setOffset(offset) {
29883		this.options.offset = offset;
29884		this._update();
29885		return this;
29886	}
29887	/**
29888	* Add or remove the given CSS class on the popup container, depending on whether the container currently has that class.
29889	*
29890	* @param className - Non-empty string with CSS class name to add/remove
29891	*
29892	* @returns if the class was removed return false, if class was added, then return true, undefined if there is no container
29893	*
29894	* @example
29895	* ```ts
29896	* let popup = new Popup()
29897	* popup.toggleClassName('toggleClass')
29898	* ```
29899	*/
29900	toggleClassName(className) {
29901		if (this._container) return this._container.classList.toggle(className);
29902	}
29903	/**
29904	* Set the option to allow subpixel positioning of the popup by passing a boolean
29905	*
29906	* @param value - When boolean is true, subpixel positioning is enabled for the popup.
29907	*
29908	* @example
29909	* ```ts
29910	* let popup = new Popup()
29911	* popup.setSubpixelPositioning(true);
29912	* ```
29913	*/
29914	setSubpixelPositioning(value) {
29915		this.options.subpixelPositioning = value;
29916	}
29917	/**
29918	* Sets the popup's padding constraints for positioning.
29919	*
29920	* @param padding - The padding to apply as a {@link PaddingOptions} object.
29921	* @example
29922	* ```ts
29923	* popup.setPadding({ top: 10, right: 20, bottom: 30, left: 40 });
29924	* ```
29925	*/
29926	setPadding(padding) {
29927		this.options.padding = padding;
29928		this._update();
29929	}
29930	_createCloseButton() {
29931		if (this.options.closeButton) {
29932			this._closeButton = DOM.create("button", "maplibregl-popup-close-button", this._content);
29933			this._closeButton.type = "button";
29934			this._closeButton.innerHTML = "&#215;";
29935			this._closeButton.addEventListener("click", this._onClose);
29936		}
29937	}
29938	_focusFirstElement() {
29939		if (!this.options.focusAfterOpen || !this._container) return;
29940		const firstFocusable = this._container.querySelector(focusQuerySelector);
29941		if (firstFocusable) firstFocusable.focus();
29942	}
29943};
29944function normalizeOffset(offset) {
29945	if (!offset) return normalizeOffset(new Point(0, 0));
29946	else if (typeof offset === "number") {
29947		const cornerOffset = Math.round(Math.abs(offset) / Math.SQRT2);
29948		return {
29949			"center": new Point(0, 0),
29950			"top": new Point(0, offset),
29951			"top-left": new Point(cornerOffset, cornerOffset),
29952			"top-right": new Point(-cornerOffset, cornerOffset),
29953			"bottom": new Point(0, -offset),
29954			"bottom-left": new Point(cornerOffset, -cornerOffset),
29955			"bottom-right": new Point(-cornerOffset, -cornerOffset),
29956			"left": new Point(offset, 0),
29957			"right": new Point(-offset, 0)
29958		};
29959	} else if (offset instanceof Point || Array.isArray(offset)) {
29960		const convertedOffset = Point.convert(offset);
29961		return {
29962			"center": convertedOffset,
29963			"top": convertedOffset,
29964			"top-left": convertedOffset,
29965			"top-right": convertedOffset,
29966			"bottom": convertedOffset,
29967			"bottom-left": convertedOffset,
29968			"bottom-right": convertedOffset,
29969			"left": convertedOffset,
29970			"right": convertedOffset
29971		};
29972	} else return {
29973		"center": Point.convert(offset["center"] || [0, 0]),
29974		"top": Point.convert(offset["top"] || [0, 0]),
29975		"top-left": Point.convert(offset["top-left"] || [0, 0]),
29976		"top-right": Point.convert(offset["top-right"] || [0, 0]),
29977		"bottom": Point.convert(offset["bottom"] || [0, 0]),
29978		"bottom-left": Point.convert(offset["bottom-left"] || [0, 0]),
29979		"bottom-right": Point.convert(offset["bottom-right"] || [0, 0]),
29980		"left": Point.convert(offset["left"] || [0, 0]),
29981		"right": Point.convert(offset["right"] || [0, 0])
29982	};
29983}
29984function normalizePadding(padding) {
29985	if (!padding) return {
29986		top: 0,
29987		right: 0,
29988		bottom: 0,
29989		left: 0
29990	};
29991	return {
29992		top: padding.top ?? 0,
29993		right: padding.right ?? 0,
29994		bottom: padding.bottom ?? 0,
29995		left: padding.left ?? 0
29996	};
29997}
29998//#endregion
29999//#region src/index.ts
30000const version = version$2;
30001/**
30002* Sets the map's [RTL text plugin](https://www.mapbox.com/mapbox-gl-js/plugins/#mapbox-gl-rtl-text).
30003*
30004* @deprecated MapLibre shapes Arabic and reorders bidirectional text itself, so nothing has to be
30005* loaded for right-to-left languages to be drawn correctly. A plugin set here still replaces the
30006* built-in implementation, but this will be removed in a future release.
30007* @param pluginURL - URL pointing to the Mapbox RTL text plugin source.
30008* @param lazy - If set to `true`, maplibre will defer loading the plugin until rtl text is encountered,
30009* rtl text will then be rendered only after the plugin finishes loading.
30010* @example
30011* ```ts
30012* setRTLTextPlugin('https://unpkg.com/@mapbox/[email protected]/dist/mapbox-gl-rtl-text.js', false);
30013* ```
30014* @see [Add support for right-to-left scripts](https://maplibre.org/maplibre-gl-js/docs/examples/add-support-for-right-to-left-scripts/)
30015* @see [Display and style rich text labels](https://maplibre.org/maplibre-gl-js/docs/examples/display-and-style-rich-text-labels/)
30016*/
30017function setRTLTextPlugin(pluginURL, lazy) {
30018	return rtlMainThreadPluginFactory().setRTLTextPlugin(pluginURL, lazy);
30019}
30020/**
30021* Gets the map's [RTL text plugin](https://www.mapbox.com/mapbox-gl-js/plugins/#mapbox-gl-rtl-text) status.
30022* The status can be `unavailable` (i.e. not requested or removed), `loading`, `loaded` or `error`.
30023* If the status is `loaded` and the plugin is requested again, an error will be thrown.
30024*
30025* @deprecated The status says nothing about whether right-to-left text can be drawn, which it always
30026* can be. It reports only on a plugin set through the deprecated {@link setRTLTextPlugin}.
30027* @example
30028* ```ts
30029* const pluginStatus = getRTLTextPluginStatus();
30030* ```
30031*/
30032function getRTLTextPluginStatus() {
30033	return rtlMainThreadPluginFactory().getRTLTextPluginStatus();
30034}
30035/**
30036* Returns the package version of the library
30037* @returns Package version of the library
30038*/
30039function getVersion() {
30040	return version;
30041}
30042/**
30043* Gets the number of web workers instantiated on a page with GL JS maps.
30044* By default, workerCount is 1 except for Safari browser where it is set to half the number of CPU cores (capped at 3).
30045* Make sure to set this property before creating any map instances for it to have effect.
30046*
30047* @returns Number of workers currently configured.
30048* @example
30049* ```ts
30050* const workerCount = getWorkerCount()
30051* ```
30052*/
30053function getWorkerCount() {
30054	return WorkerPool.workerCount;
30055}
30056/**
30057* Sets the number of web workers instantiated on a page with GL JS maps.
30058* By default, workerCount is 1 except for Safari browser where it is set to half the number of CPU cores (capped at 3).
30059* Make sure to set this property before creating any map instances for it to have effect.
30060*
30061* @example
30062* ```ts
30063* setWorkerCount(2);
30064* ```
30065*/
30066function setWorkerCount(count) {
30067	WorkerPool.workerCount = count;
30068}
30069/**
30070* Gets and sets the maximum number of images (raster tiles, sprites, icons) to load in parallel,
30071* which affects performance in raster-heavy maps. 16 by default.
30072*
30073* @returns Number of parallel requests currently configured.
30074* @example
30075* ```ts
30076* getMaxParallelImageRequests();
30077* ```
30078*/
30079function getMaxParallelImageRequests() {
30080	return config.MAX_PARALLEL_IMAGE_REQUESTS;
30081}
30082/**
30083* Sets the maximum number of images (raster tiles, sprites, icons) to load in parallel,
30084* which affects performance in raster-heavy maps. 16 by default.
30085*
30086* @example
30087* ```ts
30088* setMaxParallelImageRequests(10);
30089* ```
30090*/
30091function setMaxParallelImageRequests(numRequests) {
30092	config.MAX_PARALLEL_IMAGE_REQUESTS = numRequests;
30093}
30094/**
30095* Gets the worker url
30096* @returns The worker url
30097*/
30098function getWorkerUrl() {
30099	return config.WORKER_URL;
30100}
30101/**
30102* Sets the worker url
30103*/
30104function setWorkerUrl(value) {
30105	config.WORKER_URL = value;
30106}
30107/**
30108* Allows loading javascript code in the worker thread.
30109* *Note* that since this is using some very internal classes and flows it is considered experimental and can break at any point.
30110*
30111* It can be useful for the following examples:
30112* 1. Using `self.addProtocol` in the worker thread - note that you might need to also register the protocol on the main thread.
30113* 2. Using `self.registerWorkerSource(workerSource: WorkerSource)` to register a worker source, which should come with `addSourceType` usually.
30114* 3. using `self.actor.registerMessageHandler` to override some internal worker operations
30115* @param workerUrl - the worker url e.g. a url of a javascript file to load in the worker
30116* @returns
30117*
30118* @example
30119* ```ts
30120* // below is an example of sending a js file to the worker to load the method there
30121* // Note that you'll need to call the global function `addProtocol` in the worker to register the protocol there.
30122* // add-protocol-worker.js
30123* async function loadFn(params, abortController) {
30124*     const t = await fetch(`https://${params.url.split("://")[1]}`);
30125*     if (t.status == 200) {
30126*         const buffer = await t.arrayBuffer();
30127*         return {data: buffer}
30128*     } else {
30129*         throw new Error(`Tile fetch error: ${t.statusText}`);
30130*     }
30131* }
30132* self.addProtocol('custom', loadFn);
30133*
30134* // main.js
30135* importScriptInWorkers('add-protocol-worker.js');
30136* ```
30137*/
30138async function importScriptInWorkers(workerUrl) {
30139	await getGlobalDispatcher().broadcast("IS", workerUrl);
30140}
30141//#endregion
30142export { AJAXError, AttributionControl, BoxZoomHandler, CanvasSource, CooperativeGesturesHandler, DoubleClickZoomHandler, DragPanHandler, DragRotateHandler, EXTENT, EdgeInsets, ErrorEvent, Event, Evented, FullscreenControl, FullscreenEvent, GPUInitializationError, GeoJSONSource, GeolocateControl, GeolocateErrorEvent, GeolocateEvent, GeolocatePositionEvent, GlobeControl, Hash, ImageSource, KeyboardHandler, LngLat, LngLatBounds, LogoControl, Map$1 as Map, Map$1 as MapLibreMap, MapBoxZoomEvent, MapContextEvent, MapLibreEvent, MapMouseEvent, MapMovementEvent, MapProjectionEvent, MapSourceDataEvent, MapStyleDataEvent, MapStyleImageMissingEvent, MapStyleLoadEvent, MapTerrainEvent, MapTouchEvent, MapWheelEvent, Marker, MarkerClickEvent, MarkerDragEvent, MercatorCoordinate, NavigationControl, Point, Popup, PopupEvent, RasterDEMTileSource, RasterTileSource, ScaleControl, ScrollZoomHandler, Style, TerrainControl, TwoFingersTouchPitchHandler, TwoFingersTouchRotateHandler, TwoFingersTouchZoomHandler, TwoFingersTouchZoomRotateHandler, VectorTileSource, VideoSource, addProtocol, addSourceType, clearPrewarmedResources, config, createTileMesh, getGlobalDispatcher, getMaxParallelImageRequests, getRTLTextPluginStatus, getVersion, getWorkerCount, getWorkerUrl, importScriptInWorkers, isTimeFrozen, now, prewarm, removeProtocol, restoreNow, setMaxParallelImageRequests, setNow, setRTLTextPlugin, setWorkerCount, setWorkerUrl };

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.