PageSourceSearch

https://censusmapper.ca/_next/static/media/maplibre-gl-dev.1a7_yrl5a26dr.mjs

js censusmapper.ca collected 2026-10-02 04:33:37 UTC 1,244,733 bytes, 30,240 lines download raw bytes

vendor: 1,244,733 bytes, lines 1-30240
1/**
2* MapLibre GL JS
3* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v6.11.1/LICENSE.txt
4*/
5import { $ 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.1";
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
201const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
202const ALLOWED_TAGS = /* @__PURE__ */ new Set([
203	"a",
204	"abbr",
205	"b",
206	"bdi",
207	"bdo",
208	"br",
209	"cite",
210	"code",
211	"del",
212	"div",
213	"em",
214	"i",
215	"img",
216	"ins",
217	"kbd",
218	"li",
219	"mark",
220	"ol",
221	"p",
222	"q",
223	"s",
224	"samp",
225	"small",
226	"span",
227	"strong",
228	"sub",
229	"sup",
230	"time",
231	"u",
232	"ul",
233	"var",
234	"wbr"
235]);
236const ALLOWED_ATTRIBUTES = /* @__PURE__ */ new Set([
237	"alt",
238	"class",
239	"datetime",
240	"dir",
241	"height",
242	"href",
243	"hreflang",
244	"lang",
245	"referrerpolicy",
246	"rel",
247	"role",
248	"src",
249	"target",
250	"title",
251	"translate",
252	"type",
253	"width"
254]);
255const URL_ATTRIBUTES = /* @__PURE__ */ new Set(["href", "src"]);
256const ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set([
257	"http:",
258	"https:",
259	"mailto:"
260]);
261/**
262* Relative URLs have to resolve against something before their protocol can be read. Only the protocol of the
263* result is used, so the base never leaks into the sanitized markup.
264*/
265const RELATIVE_URL_BASE = "https://maplibre.invalid/";
266let elementInternals;
267/**
268* The `Element` members the sanitizer uses, read off the prototype rather than off the element it is looking at.
269* A form control named `children`, `remove` or `localName` shadows the same-named property of its own form element,
270* which would otherwise let untrusted markup pick what the sanitizer walking that tree sees.
271*
272* They are read on first use rather than when this module is loaded, since the module is also imported where there
273* is no DOM at all, such as during server side rendering.
274*/
275function getElementInternals() {
276	elementInternals ??= {
277		getAttribute: Element.prototype.getAttribute,
278		getAttributeNames: Element.prototype.getAttributeNames,
279		querySelectorAll: Element.prototype.querySelectorAll,
280		remove: Element.prototype.remove,
281		removeAttribute: Element.prototype.removeAttribute,
282		localName: Object.getOwnPropertyDescriptor(Element.prototype, "localName").get,
283		namespaceURI: Object.getOwnPropertyDescriptor(Element.prototype, "namespaceURI").get
284	};
285	return elementInternals;
286}
287var DOM = class DOM {
288	static {
289		this.docStyle = typeof window !== "undefined" && window.document?.documentElement.style;
290	}
291	static {
292		this.selectProp = !DOM.docStyle || "userSelect" in DOM.docStyle ? "userSelect" : "webkitUserSelect";
293	}
294	static create(tagName, className, container) {
295		const el = window.document.createElement(tagName);
296		if (className !== void 0) el.className = className;
297		if (container) container.appendChild(el);
298		return el;
299	}
300	static createNS(namespaceURI, tagName) {
301		return window.document.createElementNS(namespaceURI, tagName);
302	}
303	static disableDrag() {
304		if (DOM.docStyle && DOM.selectProp) {
305			DOM.userSelect = DOM.docStyle[DOM.selectProp];
306			DOM.docStyle[DOM.selectProp] = "none";
307		}
308	}
309	static enableDrag() {
310		if (DOM.docStyle && DOM.selectProp) DOM.docStyle[DOM.selectProp] = DOM.userSelect;
311	}
312	static suppressClickInternal(e) {
313		e.preventDefault();
314		e.stopPropagation();
315		window.removeEventListener("click", DOM.suppressClickInternal, true);
316	}
317	static suppressClick() {
318		window.addEventListener("click", DOM.suppressClickInternal, true);
319		window.setTimeout(() => {
320			window.removeEventListener("click", DOM.suppressClickInternal, true);
321		}, 0);
322	}
323	static getScale(element) {
324		const rect = element.getBoundingClientRect();
325		return {
326			x: rect.width / element.offsetWidth || 1,
327			y: rect.height / element.offsetHeight || 1,
328			boundingClientRect: rect
329		};
330	}
331	static getPoint(el, scale, e) {
332		const rect = scale.boundingClientRect;
333		return new Point((e.clientX - rect.left) / scale.x - el.clientLeft, (e.clientY - rect.top) / scale.y - el.clientTop);
334	}
335	static mousePos(el, e) {
336		const scale = DOM.getScale(el);
337		return DOM.getPoint(el, scale, e);
338	}
339	static touchPos(el, touches) {
340		const points = [];
341		const scale = DOM.getScale(el);
342		for (const touch of touches) points.push(DOM.getPoint(el, scale, touch));
343		return points;
344	}
345	/**
346	* Sanitize an untrusted HTML string, such as the attribution a remote TileJSON asks the map to display.
347	*
348	* Only the elements in `ALLOWED_TAGS` and the attributes in `ALLOWED_ATTRIBUTES` survive; anything else is
349	* dropped along with its subtree. An allow list is used rather than a list of known-dangerous markup because
350	* the latter silently permits whatever it has not heard of yet, including markup added to HTML after it was
351	* written.
352	*
353	* The sanitized nodes are returned rather than a string: serializing and re-parsing is not a round trip, so
354	* a string result lets carefully nested markup mutate into a different - and no longer sanitized - tree.
355	*/
356	static sanitize(str) {
357		const body = new DOMParser().parseFromString(str, "text/html").body;
358		const { querySelectorAll, remove } = getElementInternals();
359		for (const element of Array.from(querySelectorAll.call(body, "*"))) if (DOM.isAllowedElement(element)) DOM.removeDisallowedAttributes(element);
360		else remove.call(element);
361		const fragment = document.createDocumentFragment();
362		fragment.append(...body.childNodes);
363		return fragment;
364	}
365	/**
366	* An element is allowed only when it is plain HTML markup on the allow list. Elements in the SVG and MathML
367	* namespaces are rejected even when their local name is allowed, since foreign content follows different
368	* parsing rules and is what makes most mutation attacks possible in the first place.
369	*/
370	static isAllowedElement(element) {
371		const { namespaceURI, localName } = getElementInternals();
372		return namespaceURI.call(element) === HTML_NAMESPACE && ALLOWED_TAGS.has(localName.call(element));
373	}
374	static removeDisallowedAttributes(element) {
375		const { getAttributeNames, getAttribute, removeAttribute } = getElementInternals();
376		for (const name of getAttributeNames.call(element)) {
377			if (DOM.isAllowedAttribute(name, getAttribute.call(element, name))) continue;
378			removeAttribute.call(element, name);
379		}
380	}
381	/**
382	* `href` and `src` are further restricted to the protocols in `ALLOWED_PROTOCOLS`, so that `javascript:` and
383	* `data:` URLs cannot turn an otherwise harmless link or image into a script. The protocol is taken from a
384	* parsed URL rather than matched against the raw string, since the URL parser is what the browser will apply
385	* and it ignores tabs, newlines and leading control characters that a string comparison would trip over.
386	*/
387	static isAllowedAttribute(name, value) {
388		if (!ALLOWED_ATTRIBUTES.has(name) && !name.startsWith("aria-")) return false;
389		if (!URL_ATTRIBUTES.has(name)) return true;
390		try {
391			return ALLOWED_PROTOCOLS.has(new URL(value, RELATIVE_URL_BASE).protocol);
392		} catch {
393			return false;
394		}
395	}
396};
397//#endregion
398//#region src/util/image_request.ts
399let ImageRequest;
400(function(_ImageRequest) {
401	let imageRequestQueue;
402	let currentParallelImageRequests;
403	let throttleControlCallbackHandleCounter;
404	let throttleControlCallbacks;
405	_ImageRequest.resetRequestQueue = () => {
406		imageRequestQueue = [];
407		currentParallelImageRequests = 0;
408		throttleControlCallbackHandleCounter = 0;
409		throttleControlCallbacks = {};
410	};
411	_ImageRequest.addThrottleControl = (callback) => {
412		const handle = throttleControlCallbackHandleCounter++;
413		throttleControlCallbacks[handle] = callback;
414		return handle;
415	};
416	_ImageRequest.removeThrottleControl = (callbackHandle) => {
417		delete throttleControlCallbacks[callbackHandle];
418		processQueue();
419	};
420	/**
421	* Check to see if any of the installed callbacks are requesting the queue
422	* to be throttled.
423	* @returns `true` if any callback is causing the queue to be throttled.
424	*/
425	const isThrottled = () => {
426		for (const key of Object.keys(throttleControlCallbacks)) if (throttleControlCallbacks[key]()) return true;
427		return false;
428	};
429	async function transformAndGetImage(requestManager, url, resourceType, abortController, supportImageRefresh = true, imageBitmapOptions) {
430		const requestParameters = await requestManager.transformRequest(url, resourceType);
431		throwIfAborted(abortController.signal);
432		return ImageRequest.getImage(requestParameters, abortController, supportImageRefresh, imageBitmapOptions);
433	}
434	_ImageRequest.transformAndGetImage = transformAndGetImage;
435	_ImageRequest.getImage = (requestParameters, abortController, supportImageRefresh = true, imageBitmapOptions) => {
436		return new Promise((resolve, reject) => {
437			requestParameters.headers ||= {};
438			requestParameters.headers.accept = "image/webp,*/*";
439			extend(requestParameters, { type: "image" });
440			const request = {
441				abortController,
442				requestParameters,
443				supportImageRefresh,
444				imageBitmapOptions,
445				state: "queued",
446				onError: (error) => {
447					reject(error);
448				},
449				onSuccess: (response) => {
450					resolve(response);
451				}
452			};
453			imageRequestQueue.push(request);
454			processQueue();
455		});
456	};
457	const arrayBufferToCanvasImageSource = (data, imageBitmapOptions) => {
458		if (typeof createImageBitmap === "function") return arrayBufferToImageBitmap(data, imageBitmapOptions);
459		else return arrayBufferToImage(data);
460	};
461	/**
462	* Runs one queued image request.
463	*
464	* An empty response body (e.g. HTTP 204 for a tile without content) resolves with
465	* `data: null`, keeping the expiry headers, so callers can handle the absence
466	* explicitly and re-request when the headers say so.
467	*/
468	const doImageRequest = async (itemInQueue) => {
469		itemInQueue.state = "running";
470		const { requestParameters, supportImageRefresh, imageBitmapOptions, onError, onSuccess, abortController } = itemInQueue;
471		const canUseHTMLImageElement = supportImageRefresh === false && !imageBitmapOptions && !isWorker(self) && !getProtocol(requestParameters.url) && (!requestParameters.headers || Object.keys(requestParameters.headers).reduce((acc, item) => acc && item === "accept", true));
472		currentParallelImageRequests++;
473		const getImagePromise = canUseHTMLImageElement ? getImageUsingHtmlImage(requestParameters, abortController) : makeRequest(requestParameters, abortController);
474		try {
475			const response = await getImagePromise;
476			delete itemInQueue.abortController;
477			itemInQueue.state = "completed";
478			if (response.data instanceof HTMLImageElement || isImageBitmap(response.data)) onSuccess(response);
479			else if (!response.data || response.data.byteLength === 0) onSuccess({
480				data: null,
481				cacheControl: response.cacheControl,
482				expires: response.expires
483			});
484			else onSuccess({
485				data: await arrayBufferToCanvasImageSource(response.data, imageBitmapOptions),
486				cacheControl: response.cacheControl,
487				expires: response.expires
488			});
489		} catch (err) {
490			delete itemInQueue.abortController;
491			onError(ensureError(err));
492		} finally {
493			currentParallelImageRequests--;
494			processQueue();
495		}
496	};
497	/**
498	* Process some number of items in the image request queue.
499	*/
500	const processQueue = () => {
501		const maxImageRequests = isThrottled() ? config.MAX_PARALLEL_IMAGE_REQUESTS_PER_FRAME : config.MAX_PARALLEL_IMAGE_REQUESTS;
502		for (let numImageRequests = currentParallelImageRequests; numImageRequests < maxImageRequests && imageRequestQueue.length > 0; numImageRequests++) {
503			const topItemInQueue = imageRequestQueue.shift();
504			if (topItemInQueue.abortController.signal.aborted) {
505				numImageRequests--;
506				continue;
507			}
508			doImageRequest(topItemInQueue);
509		}
510	};
511	const getImageUsingHtmlImage = (requestParameters, abortController) => {
512		return new Promise((resolve, reject) => {
513			const image = new Image();
514			const url = requestParameters.url;
515			const credentials = requestParameters.credentials;
516			if (credentials && credentials === "include") image.crossOrigin = "use-credentials";
517			else if (credentials && credentials === "same-origin" || !sameOrigin(url)) image.crossOrigin = "anonymous";
518			abortController.signal.addEventListener("abort", () => {
519				image.src = "";
520				reject(new AbortError(abortController.signal.reason));
521			});
522			image.fetchPriority = "high";
523			image.onload = () => {
524				image.onerror = image.onload = null;
525				resolve({ data: image });
526			};
527			image.onerror = () => {
528				image.onerror = image.onload = null;
529				if (abortController.signal.aborted) return;
530				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."));
531			};
532			image.src = url;
533		});
534	};
535})(ImageRequest || (ImageRequest = {}));
536ImageRequest.resetRequestQueue();
537var RequestManager = class {
538	constructor(transformRequestFn) {
539		this._transformRequestFn = transformRequestFn ?? null;
540	}
541	transformRequest(url, type) {
542		if (this._transformRequestFn) return this._transformRequestFn(url, type) || { url };
543		return { url };
544	}
545	setTransformRequest(transformRequest) {
546		this._transformRequestFn = transformRequest;
547	}
548};
549//#endregion
550//#region src/ui/events.ts
551/**
552* The base event for MapLibre
553*
554* @group Event Related
555*/
556var MapLibreEvent = class extends Event {};
557/**
558* `MapMovementEvent` is the event type for the camera-transition map events:
559* `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `rotatestart`, `rotate`, `rotateend`,
560* `dragstart`, `drag`, `dragend`, `pitchstart`, `pitch`, `pitchend`, `rollstart`, `roll` and `rollend`.
561* These are fired as the map's view changes, as a result of either user interaction or methods such
562* as {@link Map.jumpTo} / {@link Map.flyTo}.
563*
564* @group Event Related
565*/
566var MapMovementEvent = class extends MapLibreEvent {};
567/**
568* The `style.load` event, fired once the map's style has fully loaded or changed.
569*
570* @group Event Related
571*/
572var MapStyleLoadEvent = class extends MapLibreEvent {
573	constructor(data = {}) {
574		super("style.load", data);
575	}
576};
577/**
578* The style data event
579*
580* @group Event Related
581*/
582var MapStyleDataEvent = class extends MapLibreEvent {
583	constructor(type, data = {}) {
584		super(type, data);
585		this.dataType = "style";
586	}
587};
588/**
589* A `MapSourceDataEvent` is emitted with the source-related `data`, `dataloading`, `dataabort`,
590* `sourcedata`, `sourcedataloading` and `sourcedataabort` events. Its `dataType` is always `'source'`.
591*
592* Possible values for `sourceDataType`s are:
593*
594* - `'metadata'`: indicates that any necessary source metadata has been loaded (such as TileJSON) and it is ok to start loading tiles
595* - `'content'`: indicates the source data has changed (such as when source.setData() has been called on GeoJSONSource)
596* - `'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')
597* - `'idle'`: indicates that no new source data has been fetched (but the source has done loading)
598*
599* @group Event Related
600*
601* @example
602* ```ts
603* // The sourcedata event is an example of a MapSourceDataEvent.
604* // Set up an event listener on the map.
605* map.on('sourcedata', (e) => {
606*    if (e.isSourceLoaded) {
607*        // Do something when the source has finished loading
608*    }
609* });
610* ```
611*/
612var MapSourceDataEvent = class extends MapLibreEvent {
613	constructor(type, data = {}) {
614		super(type, data);
615		this.dataType = "source";
616	}
617};
618/**
619* `MapMouseEvent` is the event type for mouse-related map events.
620*
621* @group Event Related
622*
623* @example
624* ```ts
625* // The `click` event is an example of a `MapMouseEvent`.
626* // Set up an event listener on the map.
627* map.on('click', (e) => {
628*   // The event object (e) contains information like the
629*   // coordinates of the point on the map that was clicked.
630*   console.log('A click event has occurred at ' + e.lngLat);
631* });
632* ```
633*/
634var MapMouseEvent = class extends MapLibreEvent {
635	/**
636	* Prevents subsequent default processing of the event by the map.
637	*
638	* Calling this method will prevent the following default map behaviors:
639	*
640	*   * On `mousedown` events, the behavior of {@link DragPanHandler}
641	*   * On `mousedown` events, the behavior of {@link DragRotateHandler}
642	*   * On `mousedown` events, the behavior of {@link BoxZoomHandler}
643	*   * On `dblclick` events, the behavior of {@link DoubleClickZoomHandler}
644	*
645	*/
646	preventDefault() {
647		this._defaultPrevented = true;
648	}
649	/**
650	* `true` if `preventDefault` has been called.
651	*/
652	get defaultPrevented() {
653		return this._defaultPrevented;
654	}
655	constructor(type, map, originalEvent, data = {}) {
656		originalEvent = originalEvent instanceof MouseEvent ? originalEvent : new MouseEvent(type, originalEvent);
657		const point = DOM.mousePos(map.getCanvas(), originalEvent);
658		const lngLat = map.unproject(point);
659		super(type, extend({
660			point,
661			lngLat,
662			originalEvent
663		}, data));
664		this._defaultPrevented = false;
665		this.target = map;
666	}
667};
668/**
669* `MapTouchEvent` is the event type for touch-related map events.
670*
671* @group Event Related
672*/
673var MapTouchEvent = class extends MapLibreEvent {
674	/**
675	* Prevents subsequent default processing of the event by the map.
676	*
677	* Calling this method will prevent the following default map behaviors:
678	*
679	*   * On `touchstart` events, the behavior of {@link DragPanHandler}
680	*   * On `touchstart` events, the behavior of {@link TwoFingersTouchZoomRotateHandler}
681	*
682	*/
683	preventDefault() {
684		this._defaultPrevented = true;
685	}
686	/**
687	* `true` if `preventDefault` has been called.
688	*/
689	get defaultPrevented() {
690		return this._defaultPrevented;
691	}
692	constructor(type, map, originalEvent) {
693		const touches = type === "touchend" ? originalEvent.changedTouches : originalEvent.touches;
694		const points = DOM.touchPos(map.getCanvasContainer(), touches);
695		const lngLats = points.map((t) => map.unproject(t));
696		const point = points.reduce((prev, curr, i, arr) => {
697			return prev.add(curr.div(arr.length));
698		}, new Point(0, 0));
699		const lngLat = map.unproject(point);
700		super(type, {
701			points,
702			point,
703			lngLats,
704			lngLat,
705			originalEvent
706		});
707		this._defaultPrevented = false;
708	}
709};
710/**
711* `MapWheelEvent` is the event type for the `wheel` map event.
712*
713* @group Event Related
714*/
715var MapWheelEvent = class extends MapLibreEvent {
716	/**
717	* Prevents subsequent default processing of the event by the map.
718	*
719	* Calling this method will prevent the behavior of {@link ScrollZoomHandler}.
720	*/
721	preventDefault() {
722		this._defaultPrevented = true;
723	}
724	/**
725	* `true` if `preventDefault` has been called.
726	*/
727	get defaultPrevented() {
728		return this._defaultPrevented;
729	}
730	/** */
731	constructor(map, originalEvent) {
732		super("wheel", { originalEvent });
733		this._defaultPrevented = false;
734	}
735};
736/**
737* A `MapBoxZoomEvent` is the event type for the boxzoom-related map events emitted by the {@link BoxZoomHandler}.
738*
739* @group Event Related
740*/
741var MapBoxZoomEvent = class extends MapLibreEvent {};
742/**
743* The terrain event
744*
745* @group Event Related
746*/
747var MapTerrainEvent = class extends MapLibreEvent {
748	constructor(data = {}) {
749		super("terrain", data);
750	}
751};
752/**
753* The map projection event
754*
755* @group Event Related
756*/
757var MapProjectionEvent = class extends MapLibreEvent {
758	constructor(data = {}) {
759		super("projectiontransition", data);
760	}
761};
762/**
763* An event related to the web gl context
764*
765* @group Event Related
766*/
767var MapContextEvent = class extends MapLibreEvent {};
768/**
769* The style image missing event, fired when an image is still missing after the missing style image
770* resolver has been given a chance to supply it. To load or generate images on demand,
771* use {@link Map.setMissingStyleImageResolver}.
772* Event listeners cannot resolve the missing image for the current request.
773*
774* @group Event Related
775*
776* @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/)
777*/
778var MapStyleImageMissingEvent = class extends MapLibreEvent {
779	constructor(data = {}) {
780		super("styleimagemissing", data);
781	}
782};
783//#endregion
784//#region src/util/style.ts
785/**
786* Takes a SpriteSpecification value and returns it in its array form. If `undefined` is passed as an input value, an
787* empty array is returned.
788* duplicated entries with identical id/url will be removed in returned array
789* @param sprite - optional sprite to coerce
790* @returns an empty array in case `undefined` is passed; id-url pairs otherwise
791*/
792function coerceSpriteToArray(sprite) {
793	const resultArray = [];
794	if (typeof sprite === "string") resultArray.push({
795		id: "default",
796		url: sprite
797	});
798	else if (sprite && sprite.length > 0) {
799		const dedupArray = [];
800		for (const { id, url } of sprite) {
801			const key = `${id}${url}`;
802			if (!dedupArray.includes(key)) {
803				dedupArray.push(key);
804				resultArray.push({
805					id,
806					url
807				});
808			}
809		}
810	}
811	return resultArray;
812}
813//#endregion
814//#region src/style/load_sprite.ts
815function normalizeSpriteURL(url, format, extension) {
816	try {
817		const parsed = new URL(url);
818		parsed.pathname += `${format}${extension}`;
819		return parsed.toString();
820	} catch {
821		throw new Error(`Invalid sprite URL "${url}", must be absolute. Modify style specification directly or use TransformStyleFunction to correct the issue dynamically`);
822	}
823}
824async function loadSprite(originalSprite, requestManager, pixelRatio, abortController) {
825	const spriteArray = coerceSpriteToArray(originalSprite);
826	const format = pixelRatio > 1 ? "@2x" : "";
827	const jsonsMap = {};
828	const imagesMap = {};
829	for (const { id, url } of spriteArray) {
830		const jsonRequestParameters = await requestManager.transformRequest(normalizeSpriteURL(url, format, ".json"), "SpriteJSON");
831		jsonsMap[id] = getJSON(jsonRequestParameters, abortController);
832		const imageRequestParameters = await requestManager.transformRequest(normalizeSpriteURL(url, format, ".png"), "SpriteImage");
833		imagesMap[id] = ImageRequest.getImage(imageRequestParameters, abortController);
834	}
835	await Promise.all([...Object.values(jsonsMap), ...Object.values(imagesMap)]);
836	return doOnceCompleted(jsonsMap, imagesMap);
837}
838/**
839* @param jsonsMap - JSON data map
840* @param imagesMap - image data map
841*/
842async function doOnceCompleted(jsonsMap, imagesMap) {
843	const result = {};
844	for (const spriteName in jsonsMap) {
845		result[spriteName] = {};
846		const image = (await imagesMap[spriteName]).data;
847		if (!image) throw new Error(`Could not load sprite image for ${spriteName}: the response is empty`);
848		const context = browser.getImageCanvasContext(image);
849		const json = (await jsonsMap[spriteName]).data;
850		for (const id in json) {
851			const { width, height, x, y, sdf, pixelRatio, stretchX, stretchY, content, textFitWidth, textFitHeight } = json[id];
852			const spriteData = {
853				width,
854				height,
855				x,
856				y,
857				context
858			};
859			result[spriteName][id] = {
860				data: null,
861				pixelRatio,
862				sdf,
863				stretchX,
864				stretchY,
865				content,
866				textFitWidth,
867				textFitHeight,
868				spriteData
869			};
870		}
871	}
872	return result;
873}
874//#endregion
875//#region src/render/image_manager.ts
876/**
877* Owns every image of the style - the ones the sprites bring in as well as the ones added at
878* runtime - and tracks requests for them from the tile workers, sending responses when the
879* requests are fulfilled.
880*/
881var ImageManager = class extends Evented {
882	constructor() {
883		super();
884		this.images = {};
885		this.updateVersion = 0;
886		this.loaded = false;
887		this.requestors = [];
888		this.missingImageResolver = null;
889		this._spriteImagesIds = {};
890		this._imagesIds = null;
891		this._renderCallbacksDispatchedThisFrame = {};
892	}
893	destroy() {
894		for (const id of Object.keys(this.images)) this.removeImage(id);
895		this._spriteImagesIds = {};
896	}
897	isLoaded() {
898		return this.loaded;
899	}
900	setLoaded(loaded) {
901		if (this.loaded === loaded) return;
902		this.loaded = loaded;
903		if (loaded) {
904			for (const { ids, promiseResolve } of this.requestors) promiseResolve(this._getImagesForIds(ids));
905			this.requestors = [];
906		}
907	}
908	getImage(id) {
909		const image = this.images[id];
910		if (image && !image.data && image.spriteData) {
911			const spriteData = image.spriteData;
912			image.data = new RGBAImage({
913				width: spriteData.width,
914				height: spriteData.height
915			}, spriteData.context.getImageData(spriteData.x, spriteData.y, spriteData.width, spriteData.height).data);
916			image.spriteData = null;
917		}
918		return image;
919	}
920	addImage(id, image) {
921		if (this.images[id]) throw new Error(`Image id ${id} already exist, use updateImage instead`);
922		if (this._validate(id, image)) {
923			this.images[id] = image;
924			this._imagesIds = null;
925			if (image.isWebGLImage) this.updateImage(id, image, false);
926		}
927	}
928	_validate(id, image) {
929		let valid = true;
930		const data = image.data || image.spriteData;
931		if (!this._validateStretch(image.stretchX, data?.width)) {
932			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Image "${id}" has invalid "stretchX" value`)));
933			valid = false;
934		}
935		if (!this._validateStretch(image.stretchY, data?.height)) {
936			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Image "${id}" has invalid "stretchY" value`)));
937			valid = false;
938		}
939		if (!this._validateContent(image.content, image)) {
940			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Image "${id}" has invalid "content" value`)));
941			valid = false;
942		}
943		return valid;
944	}
945	_validateStretch(stretch, size) {
946		if (!stretch) return true;
947		let last = 0;
948		for (const part of stretch) {
949			if (part[0] < last || part[1] < part[0] || size < part[1]) return false;
950			last = part[1];
951		}
952		return true;
953	}
954	_validateContent(content, image) {
955		if (!content) return true;
956		if (content.length !== 4) return false;
957		const spriteData = image.spriteData;
958		const width = spriteData?.width || image.data.width;
959		const height = spriteData?.height || image.data.height;
960		if (content[0] < 0 || width < content[0]) return false;
961		if (content[1] < 0 || height < content[1]) return false;
962		if (content[2] < 0 || width < content[2]) return false;
963		if (content[3] < 0 || height < content[3]) return false;
964		if (content[2] < content[0]) return false;
965		return content[3] >= content[1];
966	}
967	/**
968	* Replaces an image that is already known under this id, bumping its version so that the
969	* atlases holding it notice.
970	*
971	* The old record is read directly rather than through {@link ImageManager.getImage}, which
972	* would force a synchronous decode of the sprite data of the very image being replaced. Its
973	* size therefore comes from whichever of the two the pixels are still in, since an image that
974	* came from a sprite and was never rendered has not been decoded yet, and its version is
975	* defaulted, since images start out without one.
976	*
977	* @param validate - whether to reject an image of a different size than the one it replaces
978	*/
979	updateImage(id, image, validate = true) {
980		const oldImage = this.images[id];
981		if (validate) {
982			const oldData = oldImage.data || oldImage.spriteData;
983			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}).`);
984		}
985		image.version = (oldImage.version ?? 0) + 1;
986		this.images[id] = image;
987		this.updateVersion++;
988	}
989	removeImage(id) {
990		const image = this.images[id];
991		if (!image) return;
992		delete this.images[id];
993		this._imagesIds = null;
994		if (image.userImage?.onRemove) image.userImage.onRemove();
995	}
996	/**
997	* @returns the ids of every image currently held, sprite images and runtime ones alike. The
998	* returned array is shared between callers and must not be modified.
999	*/
1000	listImages() {
1001		this._imagesIds ??= Object.keys(this.images);
1002		return this._imagesIds;
1003	}
1004	/**
1005	* Images of the `default` sprite keep their plain id, the ones of any other sprite are
1006	* namespaced by their sprite's id.
1007	*/
1008	_getSpriteImageId(spriteId, imageId) {
1009		return spriteId === "default" ? imageId : `${spriteId}:${imageId}`;
1010	}
1011	/**
1012	* Takes over the images of a single sprite: adds the new ones, updates the ones that are
1013	* already known, and removes the ones that a previous load of this same sprite had brought in
1014	* but that are not part of it anymore.
1015	*
1016	* @param spriteId - the id of the sprite the images belong to
1017	* @param images - the sprite's images, keyed by their id within the sprite
1018	* @returns the ids of the images that this sprite now provides, and the ids of the ones that
1019	* were removed because they are no longer part of it
1020	*/
1021	setSpriteImages(spriteId, images) {
1022		const previousIds = this._spriteImagesIds[spriteId] ?? [];
1023		const loaded = [];
1024		for (const id in images) {
1025			const imageId = this._getSpriteImageId(spriteId, id);
1026			loaded.push(imageId);
1027			if (imageId in this.images) this.updateImage(imageId, images[id], false);
1028			else this.addImage(imageId, images[id]);
1029		}
1030		const loadedIds = new Set(loaded);
1031		const removed = previousIds.filter((imageId) => !loadedIds.has(imageId));
1032		for (const imageId of removed) this.removeImage(imageId);
1033		this._spriteImagesIds[spriteId] = loaded;
1034		return {
1035			loaded,
1036			removed
1037		};
1038	}
1039	/**
1040	* Removes all the images a single sprite has brought in.
1041	* @returns the ids of the removed images
1042	*/
1043	removeSpriteImages(spriteId) {
1044		const removed = this._spriteImagesIds[spriteId] ?? [];
1045		for (const imageId of removed) this.removeImage(imageId);
1046		delete this._spriteImagesIds[spriteId];
1047		return removed;
1048	}
1049	/**
1050	* Removes the images of every sprite, leaving the images added at runtime alone.
1051	* @returns the ids of the removed images
1052	*/
1053	removeAllSpriteImages() {
1054		const removed = Object.values(this._spriteImagesIds).flat();
1055		for (const imageId of removed) this.removeImage(imageId);
1056		this._spriteImagesIds = {};
1057		return removed;
1058	}
1059	setMissingImageResolver(resolver) {
1060		this.missingImageResolver = resolver;
1061	}
1062	getImages(ids) {
1063		return new Promise((resolve, _reject) => {
1064			let hasAllDependencies = true;
1065			if (!this.isLoaded()) {
1066				for (const id of ids) if (!this.images[id]) hasAllDependencies = false;
1067			}
1068			if (this.isLoaded() || hasAllDependencies) resolve(this._getImagesForIds(ids));
1069			else this.requestors.push({
1070				ids,
1071				promiseResolve: resolve
1072			});
1073		});
1074	}
1075	async _getImagesForIds(ids) {
1076		const unresolvedIds = new Set(ids.filter((id) => !this.getImage(id)));
1077		const resolver = this.missingImageResolver;
1078		if (resolver) await Promise.allSettled(Array.from(unresolvedIds, (id) => resolver(id)));
1079		const response = {};
1080		for (const id of ids) {
1081			const image = this.getImage(id);
1082			if (image) {
1083				unresolvedIds.delete(id);
1084				response[id] = {
1085					data: image.data.clone(),
1086					pixelRatio: image.pixelRatio,
1087					sdf: image.sdf,
1088					version: image.version,
1089					stretchX: image.stretchX,
1090					stretchY: image.stretchY,
1091					content: image.content,
1092					textFitWidth: image.textFitWidth,
1093					textFitHeight: image.textFitHeight,
1094					hasRenderCallback: Boolean(image.userImage?.render),
1095					isWebGLImage: image.isWebGLImage
1096				};
1097			}
1098		}
1099		for (const id of unresolvedIds) {
1100			this.fire(new MapStyleImageMissingEvent({ id }));
1101			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.`);
1102		}
1103		return response;
1104	}
1105	beginFrame() {
1106		this._renderCallbacksDispatchedThisFrame = {};
1107	}
1108	/**
1109	* Re-renders the images among `ids` that were added with a `render` callback (see
1110	* `StyleImageInterface`), at most once per frame each.
1111	*/
1112	dispatchRenderCallbacks(ids) {
1113		for (const id of ids) {
1114			if (this._renderCallbacksDispatchedThisFrame[id]) continue;
1115			this._renderCallbacksDispatchedThisFrame[id] = true;
1116			const image = this.getImage(id);
1117			if (!image) warnOnce(`Image with ID: "${id}" was not found`);
1118			if (renderStyleImage(image)) this.updateImage(id, image);
1119		}
1120	}
1121	cloneImages() {
1122		const clonedImages = {};
1123		for (const id in this.images) {
1124			const image = this.images[id];
1125			clonedImages[id] = {
1126				...image,
1127				data: image.data ? image.data.clone() : null
1128			};
1129		}
1130		return clonedImages;
1131	}
1132};
1133//#endregion
1134//#region src/render/pattern_atlas.ts
1135/**
1136* When copied into the atlas texture, pattern images are padded by one pixel on each side with a
1137* copy of the image data wrapped from the opposite side. This ensures the correct behavior of
1138* GL_LINEAR texture sampling mode.
1139*/
1140const PADDING = 1;
1141/**
1142* A texture atlas of the pattern images that are drawn without a tile's own {@link ImageAtlas},
1143* i.e. `background-pattern`. Entries are packed on demand and re-packed whenever one of them is
1144* added or changes, which is rare enough for the repacking cost not to matter.
1145*/
1146var PatternAtlas = class {
1147	constructor(imageManager) {
1148		this._imageManager = imageManager;
1149		this._entries = {};
1150		this._image = new RGBAImage({
1151			width: 1,
1152			height: 1
1153		});
1154		this._dirty = true;
1155	}
1156	destroy() {
1157		if (this._texture) {
1158			this._texture.destroy();
1159			this._texture = null;
1160		}
1161		this._entries = {};
1162		this._image = new RGBAImage({
1163			width: 1,
1164			height: 1
1165		});
1166		this._dirty = true;
1167	}
1168	getPixelSize() {
1169		const { width, height } = this._image;
1170		return {
1171			width,
1172			height
1173		};
1174	}
1175	/**
1176	* @returns the position of the pattern within the atlas, or `null` if there is no such image
1177	*/
1178	getPattern(id) {
1179		const image = this._imageManager.getImage(id);
1180		if (!image) return null;
1181		const entry = this._entries[id];
1182		if (entry?.image !== image) {
1183			const bin = {
1184				w: image.data.width + 2,
1185				h: image.data.height + 2,
1186				x: 0,
1187				y: 0
1188			};
1189			this._entries[id] = {
1190				bin,
1191				position: new ImagePosition(bin, image),
1192				image
1193			};
1194		} else if (entry.position.version !== image.version) entry.position.version = image.version;
1195		else return entry.position;
1196		this._update();
1197		return this._entries[id].position;
1198	}
1199	bind(context) {
1200		const gl = context.gl;
1201		if (!this._texture) {
1202			this._texture = new Texture(context, this._image, gl.RGBA);
1203			this._dirty = false;
1204		} else if (this._dirty) {
1205			this._texture.update(this._image);
1206			this._dirty = false;
1207		}
1208		this._texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
1209	}
1210	_update() {
1211		for (const id in this._entries) if (!this._imageManager.getImage(id)) delete this._entries[id];
1212		const bins = [];
1213		for (const id in this._entries) bins.push(this._entries[id].bin);
1214		const { w, h } = potpack(bins);
1215		const dst = this._image;
1216		dst.resize({
1217			width: w || 1,
1218			height: h || 1
1219		});
1220		for (const id in this._entries) {
1221			const { bin } = this._entries[id];
1222			const x = bin.x + PADDING;
1223			const y = bin.y + PADDING;
1224			const src = this._entries[id].image.data;
1225			const w = src.width;
1226			const h = src.height;
1227			RGBAImage.copy(src, dst, {
1228				x: 0,
1229				y: 0
1230			}, {
1231				x,
1232				y
1233			}, {
1234				width: w,
1235				height: h
1236			});
1237			RGBAImage.copy(src, dst, {
1238				x: 0,
1239				y: h - 1
1240			}, {
1241				x,
1242				y: y - 1
1243			}, {
1244				width: w,
1245				height: 1
1246			});
1247			RGBAImage.copy(src, dst, {
1248				x: 0,
1249				y: 0
1250			}, {
1251				x,
1252				y: y + h
1253			}, {
1254				width: w,
1255				height: 1
1256			});
1257			RGBAImage.copy(src, dst, {
1258				x: w - 1,
1259				y: 0
1260			}, {
1261				x: x - 1,
1262				y
1263			}, {
1264				width: 1,
1265				height: h
1266			});
1267			RGBAImage.copy(src, dst, {
1268				x: 0,
1269				y: 0
1270			}, {
1271				x: x + w,
1272				y
1273			}, {
1274				width: 1,
1275				height: h
1276			});
1277		}
1278		this._dirty = true;
1279	}
1280};
1281//#endregion
1282//#region src/render/font_face_manager.ts
1283const MAX_CODE_POINT = 1114111;
1284/**
1285* What a font file covers when the style does not narrow it down with `unicode-range`.
1286*/
1287const DEFAULT_UNICODE_RANGE = {
1288	start: 0,
1289	end: MAX_CODE_POINT
1290};
1291/**
1292* Makes the family names unique across every map on the page, since `document.fonts` is shared.
1293*/
1294let nextFamilyId = 0;
1295/**
1296* Parses one `unicode-range` entry, in the CSS grammar the style specification borrows: `U+A5`,
1297* `U+0-10FFFF` or `U+4??`.
1298* @param value - a single `unicode-range` entry
1299* @returns the codepoints it covers, or `null` if it is not a range this can make sense of
1300*/
1301function parseUnicodeRange(value) {
1302	const wildcard = /^u\+([0-9a-f]*)(\?+)$/i.exec(value);
1303	if (wildcard) {
1304		const [, prefix, questionMarks] = wildcard;
1305		if (prefix.length + questionMarks.length > 6) return null;
1306		return clampRange(parseInt(`${prefix}${"0".repeat(questionMarks.length)}`, 16), parseInt(`${prefix}${"f".repeat(questionMarks.length)}`, 16));
1307	}
1308	const explicit = /^u\+([0-9a-f]{1,6})(?:-([0-9a-f]{1,6}))?$/i.exec(value);
1309	if (!explicit) return null;
1310	const start = parseInt(explicit[1], 16);
1311	return clampRange(start, explicit[2] === void 0 ? start : parseInt(explicit[2], 16));
1312}
1313function clampRange(start, end) {
1314	if (start > end || start > MAX_CODE_POINT) return null;
1315	return {
1316		start,
1317		end: Math.min(end, MAX_CODE_POINT)
1318	};
1319}
1320/**
1321* Whether the style lets this file cover the given codepoint.
1322*/
1323function covers(face, codePoint) {
1324	return face.ranges.some(({ start, end }) => codePoint >= start && codePoint <= end);
1325}
1326/**
1327* Keeps the font files a style declares in its
1328* [`font-faces`](https://maplibre.org/maplibre-style-spec/root/#font-faces) property, and hands the
1329* {@link GlyphManager} the CSS family to draw a given codepoint with.
1330*
1331* The files go to the browser's CSS Font Loading API, so any format it can render text with works.
1332* Each is registered under a family name of our own making, so that a style cannot restyle the
1333* surrounding page and a codepoint can be pinned to one file rather than to whatever font matching
1334* picks.
1335*
1336* Rasterizing happens a grapheme cluster at a time, so a letter reaches the browser's text engine
1337* with its marks. Shaping across cluster boundaries still needs the RTL text plugin.
1338*/
1339var FontFaceManager = class {
1340	constructor(requestManager) {
1341		this.requestManager = requestManager;
1342		this._faces = {};
1343		this._registered = /* @__PURE__ */ new Set();
1344	}
1345	/**
1346	* Replaces the declared font faces. Nothing is downloaded here: each file waits until a
1347	* codepoint it covers is actually drawn.
1348	*/
1349	setFontFaces(fontFaces) {
1350		this._unregisterAll();
1351		this._faces = {};
1352		for (const [fontName, declaration] of Object.entries(fontFaces ?? {})) {
1353			const declarations = Array.isArray(declaration) ? declaration : [declaration];
1354			this._faces[fontName] = declarations.map((entry) => this._declareFontFace(fontName, entry)).filter((face) => face !== null);
1355		}
1356	}
1357	/**
1358	* Whether the style declared any font face at all, so that the common case of a style without
1359	* `font-faces` costs nothing.
1360	*/
1361	hasFontFaces() {
1362		return Object.keys(this._faces).length > 0;
1363	}
1364	/**
1365	* Finds the font file to draw a codepoint with: each name of the `text-font` stack in turn, and
1366	* within a name each declared file, until one covers it. A file that fails to load is skipped,
1367	* the specification asking for unsupported fonts to be ignored.
1368	*
1369	* @returns the CSS family to draw with, or `null` to leave the codepoint to the `glyphs` URL
1370	*/
1371	async getFontFamily(fontStack, codePoint) {
1372		for (const fontName of fontStack.split(",")) for (const face of this._faces[fontName.trim()] ?? []) {
1373			if (!covers(face, codePoint)) continue;
1374			face.loaded ??= this._loadFontFace(face);
1375			if (await face.loaded) return face.family;
1376		}
1377		return null;
1378	}
1379	/**
1380	* Turns one declaration into a face to draw with, or `null` if there is nothing usable in it.
1381	*
1382	* A bare URL is the same as a face that names no `unicode-range`: it covers every codepoint.
1383	*/
1384	_declareFontFace(fontName, declaration) {
1385		const face = typeof declaration === "string" ? { url: declaration } : declaration;
1386		if (typeof face?.url !== "string") {
1387			warnOnce(`Ignoring the font face declared for "${fontName}": it has no URL.`);
1388			return null;
1389		}
1390		const family = `maplibre-gl-font-face-${nextFamilyId++}`;
1391		const unicodeRange = face["unicode-range"];
1392		if (!unicodeRange?.length) return {
1393			url: face.url,
1394			ranges: [DEFAULT_UNICODE_RANGE],
1395			family
1396		};
1397		const ranges = [];
1398		for (const entry of unicodeRange) {
1399			const range = parseUnicodeRange(entry);
1400			if (!range) {
1401				warnOnce(`Ignoring the unicode range "${entry}" of the font face at ${face.url}: it is not a valid range.`);
1402				continue;
1403			}
1404			ranges.push(range);
1405		}
1406		if (!ranges.length) return null;
1407		return {
1408			url: face.url,
1409			ranges,
1410			family
1411		};
1412	}
1413	/**
1414	* Downloads a declared file and hands it to the browser, reporting whether it can be drawn with.
1415	* A failure is not an error: its codepoints fall through to the next file, then to `glyphs`.
1416	*/
1417	async _loadFontFace(face) {
1418		if (typeof FontFace === "undefined" || typeof document === "undefined" || !document.fonts) {
1419			warnOnce(`Ignoring the font face at ${face.url}: this environment has no CSS Font Loading API.`);
1420			return false;
1421		}
1422		let fontFace;
1423		try {
1424			fontFace = new FontFace(face.family, await this._downloadFontFile(face.url));
1425			if (!Object.values(this._faces).some((faces) => faces.includes(face))) return false;
1426			document.fonts.add(fontFace);
1427			this._registered.add(fontFace);
1428			await fontFace.load();
1429			return true;
1430		} catch (e) {
1431			if (fontFace) this._unregister(fontFace);
1432			warnOnce(`Ignoring the font face at ${face.url}: ${ensureError(e).message}`);
1433			return false;
1434		}
1435	}
1436	/**
1437	* Downloads a font file, giving the map's `transformRequest` a chance to rewrite the request the
1438	* same way it gets one for a glyph range.
1439	*/
1440	async _downloadFontFile(url) {
1441		const request = await this.requestManager.transformRequest(url, "Glyphs");
1442		const response = await getArrayBuffer(request, new AbortController());
1443		if (!response?.data) throw new Error(`the response was empty for the font file at ${url}`);
1444		return response.data;
1445	}
1446	_unregister(fontFace) {
1447		document.fonts?.delete(fontFace);
1448		this._registered.delete(fontFace);
1449	}
1450	_unregisterAll() {
1451		for (const fontFace of this._registered) document.fonts?.delete(fontFace);
1452		this._registered.clear();
1453	}
1454	destroy() {
1455		this._unregisterAll();
1456		this._faces = {};
1457	}
1458};
1459//#endregion
1460//#region node_modules/@mapbox/tiny-sdf/index.js
1461const INF = 0x56bc75e2d63100000;
1462const alphaTable = /* @__PURE__ */ new Float64Array(256);
1463for (let i = 0; i < 256; i++) {
1464	const d = .5 - Math.pow(i / 255, 1 / 2.2);
1465	alphaTable[i] = d * Math.abs(d);
1466}
1467alphaTable[255] = -0x56bc75e2d63100000;
1468var TinySDF = class {
1469	constructor({ fontSize = 24, buffer = 3, radius = 8, cutoff = .25, fontFamily = "sans-serif", fontWeight = "normal", fontStyle = "normal", lang = null } = {}) {
1470		this.buffer = buffer;
1471		this.radius = radius;
1472		this.cutoff = cutoff;
1473		this.lang = lang;
1474		const size = this.size = fontSize + buffer * 4;
1475		const canvas = this._createCanvas(size);
1476		const ctx = this.ctx = canvas.getContext("2d", { willReadFrequently: true });
1477		ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;
1478		ctx.textBaseline = "alphabetic";
1479		ctx.textAlign = "left";
1480		ctx.fillStyle = "black";
1481		this.gridOuter = new Float64Array(size * size);
1482		this.gridInner = new Float64Array(size * size);
1483		this.f = new Float64Array(size);
1484		this.z = new Float64Array(size + 1);
1485		this.v = new Uint16Array(size);
1486	}
1487	_createCanvas(size) {
1488		if (typeof OffscreenCanvas !== "undefined") return new OffscreenCanvas(size, size);
1489		const canvas = document.createElement("canvas");
1490		canvas.width = canvas.height = size;
1491		return canvas;
1492	}
1493	draw(char) {
1494		const { width: glyphAdvance, actualBoundingBoxAscent, actualBoundingBoxDescent, actualBoundingBoxLeft, actualBoundingBoxRight } = this.ctx.measureText(char);
1495		const glyphTop = Math.ceil(actualBoundingBoxAscent);
1496		const glyphLeft = Math.floor(-actualBoundingBoxLeft);
1497		const glyphWidth = Math.max(0, Math.min(this.size - this.buffer, Math.ceil(actualBoundingBoxRight) - glyphLeft));
1498		const glyphHeight = Math.max(0, Math.min(this.size - this.buffer, glyphTop + Math.ceil(actualBoundingBoxDescent)));
1499		const width = glyphWidth + 2 * this.buffer;
1500		const height = glyphHeight + 2 * this.buffer;
1501		const len = Math.max(width * height, 0);
1502		const data = new Uint8ClampedArray(len);
1503		const glyph = {
1504			data,
1505			width,
1506			height,
1507			glyphWidth,
1508			glyphHeight,
1509			glyphTop,
1510			glyphLeft,
1511			glyphAdvance
1512		};
1513		if (glyphWidth === 0 || glyphHeight === 0) return glyph;
1514		const { ctx, buffer, gridInner, gridOuter } = this;
1515		if (this.lang) ctx.lang = this.lang;
1516		ctx.clearRect(buffer, buffer, glyphWidth, glyphHeight);
1517		ctx.fillText(char, buffer - glyphLeft, buffer + glyphTop);
1518		const imgData = ctx.getImageData(buffer, buffer, glyphWidth, glyphHeight);
1519		gridOuter.fill(INF, 0, len);
1520		gridInner.fill(0, 0, len);
1521		let imgIdx = 3;
1522		for (let y = 0; y < glyphHeight; y++) {
1523			let j = (y + buffer) * width + buffer;
1524			for (let x = 0; x < glyphWidth; x++, imgIdx += 4, j++) {
1525				const a = imgData.data[imgIdx];
1526				if (a === 0) continue;
1527				const t = alphaTable[a];
1528				gridOuter[j] = Math.max(0, t);
1529				gridInner[j] = Math.max(0, -t);
1530			}
1531		}
1532		edt(gridOuter, 0, 0, width, height, width, this.f, this.v, this.z);
1533		const pad = Math.min(buffer, 1);
1534		edt(gridInner, buffer - pad, buffer - pad, glyphWidth + 2 * pad, glyphHeight + 2 * pad, width, this.f, this.v, this.z);
1535		const scale = 255 / this.radius;
1536		const base = 255 * (1 - this.cutoff);
1537		for (let i = 0; i < len; i++) {
1538			const d = Math.sqrt(gridOuter[i]) - Math.sqrt(gridInner[i]);
1539			data[i] = Math.round(base - scale * d);
1540		}
1541		return glyph;
1542	}
1543};
1544function edt(data, x0, y0, width, height, gridSize, f, v, z) {
1545	for (let x = x0; x < x0 + width; x++) edt1d(data, y0 * gridSize + x, gridSize, height, f, v, z);
1546	for (let y = y0; y < y0 + height; y++) edt1d(data, y * gridSize + x0, 1, width, f, v, z);
1547}
1548function edt1d(grid, offset, stride, length, f, v, z) {
1549	v[0] = 0;
1550	z[0] = -0x56bc75e2d63100000;
1551	z[1] = INF;
1552	f[0] = grid[offset];
1553	for (let q = 1, k = 0, s = 0; q < length; q++) {
1554		f[q] = grid[offset + q * stride];
1555		const q2 = q * q;
1556		do {
1557			const r = v[k];
1558			s = (f[q] - f[r] + q2 - r * r) / (q - r) / 2;
1559		} while (s <= z[k] && --k > -1);
1560		k++;
1561		v[k] = q;
1562		z[k] = s;
1563		z[k + 1] = INF;
1564	}
1565	for (let q = 0, k = 0; q < length; q++) {
1566		while (z[k + 1] < q) k++;
1567		const r = v[k];
1568		const qr = q - r;
1569		grid[offset + q * stride] = f[r] + qr * qr;
1570	}
1571}
1572//#endregion
1573//#region src/render/glyph_manager.ts
1574/**
1575* The style specification hard-codes some last resort fonts as a default fontstack.
1576*/
1577const defaultStack = latest.layout_symbol["text-font"].default.join(",");
1578/**
1579* The CSS generic font family closest to `defaultStack`.
1580*/
1581const defaultGenericFontFamily = "sans-serif";
1582/**
1583* Scale factor for client-generated glyphs.
1584*
1585* Client-generated glyphs are rendered at 2× because CJK glyphs are more detailed than others.
1586*/
1587const textureScale = 2;
1588const defaultCreateRasterizer = (options, padding) => {
1589	const tinySDF = new TinySDF(options);
1590	tinySDF.buffer = padding;
1591	return tinySDF;
1592};
1593/**
1594* How wide a grapheme cluster may be drawn, in multiples of the font size.
1595*
1596* TinySDF sizes its canvas for a single character and cuts off the rest, but a cluster is a whole
1597* syllable: Burmese `လား` is nearly twice the font size. Three ems fits the ones that occur.
1598*/
1599const clusterEmsWide = 3;
1600var GlyphManager = class {
1601	constructor(requestManager, localIdeographFontFamily, lang, createRasterizer = defaultCreateRasterizer) {
1602		this.requestManager = requestManager;
1603		this.localIdeographFontFamily = localIdeographFontFamily;
1604		this.entries = {};
1605		this.lang = lang;
1606		this.fontFaceManager = new FontFaceManager(requestManager);
1607		this.createRasterizer = createRasterizer;
1608	}
1609	setURL(url) {
1610		this.url = url;
1611	}
1612	/**
1613	* Replaces the font files the style declares in its `font-faces` property, dropping every glyph
1614	* drawn with the previous ones.
1615	*/
1616	setFontFaces(fontFaces) {
1617		this.fontFaceManager.setFontFaces(fontFaces);
1618		this.entries = {};
1619	}
1620	async getGlyphs(glyphs) {
1621		const glyphsPromises = [];
1622		for (const stack in glyphs) for (const id of glyphs[stack]) glyphsPromises.push(this._getAndCacheGlyphsPromise(stack, id));
1623		const updatedGlyphs = await Promise.all(glyphsPromises);
1624		const result = {};
1625		for (const { stack, id, glyph } of updatedGlyphs) {
1626			result[stack] ||= {};
1627			result[stack][id] = glyph && {
1628				id: glyph.id,
1629				bitmap: glyph.bitmap.clone(),
1630				metrics: glyph.metrics
1631			};
1632		}
1633		return result;
1634	}
1635	/**
1636	* Gets one glyph, asked for by grapheme cluster so that a letter and its marks are drawn as the
1637	* one shape they are written as.
1638	*
1639	* A cluster is drawn from a font rather than fetched, because a glyphs URL serves codepoints and
1640	* has no way to serve the one shape they are written as. A file the style pinned with
1641	* `font-faces` draws it where one covers it, and the local fonts otherwise. For a single
1642	* codepoint a declared file still wins over the glyphs URL and the local fallbacks.
1643	*/
1644	async _getAndCacheGlyphsPromise(stack, id) {
1645		this.entries[stack] ??= {
1646			glyphs: {},
1647			requests: {},
1648			ranges: {}
1649		};
1650		const entry = this.entries[stack];
1651		let glyph = entry.glyphs[id];
1652		if (glyph !== void 0) return {
1653			stack,
1654			id,
1655			glyph
1656		};
1657		const codePoint = id.codePointAt(0);
1658		const fontFaceFamily = this.fontFaceManager.hasFontFaces() ? await this.fontFaceManager.getFontFamily(stack, codePoint) : null;
1659		if (fontFaceFamily) {
1660			glyph = entry.glyphs[id] = await this._drawGlyph(entry, stack, id, fontFaceFamily);
1661			return {
1662				stack,
1663				id,
1664				glyph
1665			};
1666		}
1667		if (!this.url || isCluster(id) || this._charUsesLocalIdeographFontFamily(codePoint)) {
1668			glyph = entry.glyphs[id] = await this._drawGlyph(entry, stack, id);
1669			return {
1670				stack,
1671				id,
1672				glyph
1673			};
1674		}
1675		return await this._downloadAndCacheRangePromise(stack, id);
1676	}
1677	/**
1678	* Gets a glyph from the server-side cache, downloading the PBF range it falls in if need be.
1679	*
1680	* Only reached for a single codepoint. What comes back is keyed by codepoint, as the file is,
1681	* and is cached by cluster -- which for one codepoint is the character itself.
1682	*/
1683	async _downloadAndCacheRangePromise(stack, id) {
1684		const codePoint = id.codePointAt(0);
1685		const entry = this.entries[stack];
1686		const range = Math.floor(codePoint / 256);
1687		if (entry.ranges[range]) return {
1688			stack,
1689			id,
1690			glyph: null
1691		};
1692		entry.requests[range] ||= this._loadGlyphRange(stack, range);
1693		try {
1694			const response = await entry.requests[range];
1695			for (const responseId in response) entry.glyphs[String.fromCodePoint(+responseId)] = response[+responseId];
1696			entry.ranges[range] = true;
1697			return {
1698				stack,
1699				id,
1700				glyph: response[codePoint] || null
1701			};
1702		} catch (e) {
1703			const glyph = entry.glyphs[id] = await this._drawGlyph(entry, stack, id);
1704			this._warnOnMissingGlyphRange(glyph, range, codePoint, ensureError(e));
1705			return {
1706				stack,
1707				id,
1708				glyph
1709			};
1710		}
1711	}
1712	/**
1713	* Downloads one range of 256 codepoints from the glyphs URL and parses the glyphs out of it.
1714	*/
1715	async _loadGlyphRange(fontstack, range) {
1716		const begin = range * 256;
1717		const end = begin + 255;
1718		const request = await this.requestManager.transformRequest(this.url.replace("{fontstack}", fontstack).replace("{range}", `${begin}-${end}`), "Glyphs");
1719		const response = await getArrayBuffer(request, new AbortController());
1720		if (!response?.data) throw new Error(`Could not load glyph range. range: ${range}, ${begin}-${end}`);
1721		const glyphs = {};
1722		for (const glyph of parseGlyphPbf(response.data)) glyphs[glyph.id] = glyph;
1723		return glyphs;
1724	}
1725	_warnOnMissingGlyphRange(glyph, range, id, err) {
1726		const begin = range * 256;
1727		const end = begin + 255;
1728		const codePoint = id.toString(16).padStart(4, "0").toUpperCase();
1729		warnOnce(`Unable to load glyph range ${range}, ${begin}-${end}. Rendering codepoint U+${codePoint} locally instead. ${err}`);
1730	}
1731	/**
1732	* Returns whether the given codepoint should be rendered locally.
1733	*/
1734	_charUsesLocalIdeographFontFamily(id) {
1735		return !!this.localIdeographFontFamily && codePointUsesLocalIdeographFontFamily(id);
1736	}
1737	/**
1738	* Draws a glyph offscreen using TinySDF, created lazily. The whole cluster goes to TinySDF, which
1739	* is what lets the browser's text engine place a letter's marks on it.
1740	*
1741	* @param fontFaceFamily - the CSS family of the `font-faces` file covering this codepoint, if any
1742	*/
1743	async _drawGlyph(entry, stack, id, fontFaceFamily) {
1744		const char = (await this._getTinySDF(entry, stack, id, fontFaceFamily)).draw(id);
1745		/**
1746		* TinySDF's "top" is the distance from the alphabetic baseline to the top of the glyph.
1747		* Server-generated fonts specify "top" relative to an origin above the em box (the origin
1748		* comes from FreeType, but I'm unclear on exactly how it's derived)
1749		* ref: https://github.com/mapbox/sdf-glyph-foundry
1750		*
1751		* Server fonts don't yet include baseline information, so we can't line up exactly with them
1752		* (and they don't line up with each other)
1753		* ref: https://github.com/mapbox/node-fontnik/pull/160
1754		*
1755		* To approximately align TinySDF glyphs with server-provided glyphs, we use this baseline adjustment
1756		* factor calibrated to be in between DIN Pro and Arial Unicode (but closer to Arial Unicode)
1757		*/
1758		const topAdjustment = 27.5;
1759		const leftAdjustment = .5;
1760		const isControl = /^\p{gc=Cf}+$/u.test(id);
1761		return {
1762			id: id.codePointAt(0),
1763			bitmap: new AlphaImage({
1764				width: char.width || 60,
1765				height: char.height || 60
1766			}, char.data),
1767			metrics: {
1768				width: isControl ? 0 : char.glyphWidth / textureScale || 24,
1769				height: char.glyphHeight / textureScale || 24,
1770				left: char.glyphLeft / textureScale + leftAdjustment || 0,
1771				top: char.glyphTop / textureScale - topAdjustment || -8,
1772				advance: isControl ? 0 : char.glyphAdvance / textureScale || 24,
1773				isDoubleResolution: true
1774			}
1775		};
1776	}
1777	/**
1778	* Returns the TinySDF that draws this grapheme, created lazily. A stack keeps one per font
1779	* selection, so a fallback cannot bleed into the rest of the text, and one more per font file
1780	* for the clusters drawn from it, which need a wider canvas.
1781	*
1782	* A font file carries its own weight and style, so neither is sniffed out of the family name.
1783	* Where no file covers the grapheme, `localIdeographFontFamily` beats the last resort fontstack.
1784	*/
1785	_getTinySDF(entry, stack, id, fontFaceFamily) {
1786		const cluster = isCluster(id);
1787		if (fontFaceFamily) {
1788			const cache = cluster ? "clusterTinySDFs" : "fontFaceTinySDFs";
1789			entry[cache] ??= {};
1790			entry[cache][fontFaceFamily] ||= this._createTinySDF(fontFaceFamily, false, cluster ? clusterEmsWide : 1);
1791			return entry[cache][fontFaceFamily];
1792		}
1793		const usesLocalIdeographFontFamily = stack === defaultStack && this.localIdeographFontFamily !== "" && this._charUsesLocalIdeographFontFamily(id.codePointAt(0));
1794		const family = usesLocalIdeographFontFamily ? this.localIdeographFontFamily : stack;
1795		if (cluster) {
1796			entry.clusterTinySDFs ??= {};
1797			entry.clusterTinySDFs[family] ||= this._createTinySDF(family, true, clusterEmsWide);
1798			return entry.clusterTinySDFs[family];
1799		}
1800		const cache = usesLocalIdeographFontFamily ? "ideographTinySDF" : "tinySDF";
1801		entry[cache] ||= this._createTinySDF(family);
1802		return entry[cache];
1803	}
1804	/**
1805	* Builds the TinySDF that draws with a given font selection.
1806	*
1807	* TinySDF derives its canvas from `fontSize + buffer * 4`, so the buffer is the only way in to a
1808	* wider one. It also stands for the padding the atlas expects to be `GLYPH_PBF_BORDER`, so the
1809	* two are passed separately.
1810	*
1811	* @param emsWide - how wide the glyphs may be, in font sizes, before TinySDF cuts them off
1812	*/
1813	async _createTinySDF(stack, sniffFontStyles = true, emsWide = 1) {
1814		const fontFamilies = stack ? stack.split(",") : [];
1815		fontFamilies.push(defaultGenericFontFamily);
1816		const fontFamily = fontFamilies.map((fontName) => /[-\w]+/.test(fontName) ? fontName : `'${CSS.escape(fontName)}'`).join(",");
1817		const fontSize = 48;
1818		const fontWeight = sniffFontStyles ? this._fontWeight(fontFamilies[0]) : void 0;
1819		const fontStyle = sniffFontStyles ? this._fontStyle(fontFamilies[0]) : "normal";
1820		if (typeof document !== "undefined" && document.fonts?.load) try {
1821			await document.fonts.load(`${fontStyle} ${fontWeight || "normal"} ${fontSize}px ${fontFamily}`);
1822		} catch (e) {
1823			warnOnce(`Failed to load font "${fontFamily}": ${ensureError(e).message}`);
1824		}
1825		const padding = 6;
1826		return this.createRasterizer({
1827			fontSize,
1828			buffer: Math.max(padding, Math.ceil(fontSize * (emsWide - 1) / 4)),
1829			radius: 16,
1830			cutoff: .25,
1831			fontFamily,
1832			fontWeight,
1833			fontStyle,
1834			lang: this.lang
1835		}, padding);
1836	}
1837	/**
1838	* Sniffs the font style out of a font family name.
1839	*/
1840	_fontStyle(fontFamily) {
1841		if (/italic/i.test(fontFamily)) return "italic";
1842		else if (/oblique/i.test(fontFamily)) return "oblique";
1843		return "normal";
1844	}
1845	/**
1846	* Sniffs the font weight out of a font family name.
1847	*/
1848	_fontWeight(fontFamily) {
1849		const weightsByName = {
1850			thin: 100,
1851			hairline: 100,
1852			"extra light": 200,
1853			"ultra light": 200,
1854			light: 300,
1855			normal: 400,
1856			regular: 400,
1857			medium: 500,
1858			semibold: 600,
1859			demibold: 600,
1860			bold: 700,
1861			"extra bold": 800,
1862			"ultra bold": 800,
1863			black: 900,
1864			heavy: 900,
1865			"extra black": 950,
1866			"ultra black": 950
1867		};
1868		let match;
1869		for (const [name, weight] of Object.entries(weightsByName)) if (new RegExp(`\\b${name}\\b`, "i").test(fontFamily)) match = `${weight}`;
1870		return match;
1871	}
1872	destroy() {
1873		for (const stack in this.entries) {
1874			const entry = this.entries[stack];
1875			entry.tinySDF = null;
1876			entry.ideographTinySDF = null;
1877			entry.fontFaceTinySDFs = {};
1878			entry.glyphs = {};
1879			entry.requests = {};
1880			entry.ranges = {};
1881		}
1882		this.entries = {};
1883		this.fontFaceManager.destroy();
1884	}
1885};
1886//#endregion
1887//#region src/style/light_properties.g.ts
1888let properties$2;
1889const getProperties$2 = () => properties$2 = properties$2 || new Properties({
1890	"anchor": new DataConstantProperty(latest["light"]["anchor"], "anchor"),
1891	"position": new DataConstantProperty(latest["light"]["position"], "position"),
1892	"color": new DataConstantProperty(latest["light"]["color"], "color"),
1893	"intensity": new DataConstantProperty(latest["light"]["intensity"], "intensity")
1894});
1895//#endregion
1896//#region src/style/light.ts
1897var Light = class extends Evented {
1898	constructor(lightOptions, globalState) {
1899		super();
1900		this._transitionable = new Transitionable(getProperties$2(), "light", globalState);
1901		this.setLight(lightOptions);
1902		this._transitioning = this._transitionable.untransitioned();
1903	}
1904	getLight() {
1905		return this._transitionable.serialize();
1906	}
1907	/**
1908	* Gets the light position in cartesian coordinates.
1909	*/
1910	getCartesianPosition() {
1911		return sphericalToCartesian(this.properties.get("position"));
1912	}
1913	setLight(light, options = {}) {
1914		if (this._validate(validateStyle.light, light, options)) return;
1915		this._transitionable.setValues(light);
1916	}
1917	updateTransitions(parameters) {
1918		this._transitioning = this._transitionable.transitioned(parameters, this._transitioning);
1919	}
1920	hasTransition() {
1921		return this._transitioning.hasTransition();
1922	}
1923	applyGlobalStateChange(refs, priorGlobalState, parameters) {
1924		this._transitioning = this._transitionable.applyGlobalStateChange(refs, priorGlobalState, this._transitioning, parameters);
1925	}
1926	recalculate(parameters) {
1927		this.properties = this._transitioning.possiblyEvaluate(parameters);
1928	}
1929	_validate(validate, value, options) {
1930		return validateAndEmit(this, validate, { value }, options);
1931	}
1932};
1933//#endregion
1934//#region src/style/sky_properties.g.ts
1935let properties$1;
1936const getProperties$1 = () => properties$1 = properties$1 || new Properties({
1937	"sky-color": new DataConstantProperty(latest["sky"]["sky-color"], "sky-color"),
1938	"horizon-color": new DataConstantProperty(latest["sky"]["horizon-color"], "horizon-color"),
1939	"fog-color": new DataConstantProperty(latest["sky"]["fog-color"], "fog-color"),
1940	"fog-ground-blend": new DataConstantProperty(latest["sky"]["fog-ground-blend"], "fog-ground-blend"),
1941	"horizon-fog-blend": new DataConstantProperty(latest["sky"]["horizon-fog-blend"], "horizon-fog-blend"),
1942	"sky-horizon-blend": new DataConstantProperty(latest["sky"]["sky-horizon-blend"], "sky-horizon-blend"),
1943	"atmosphere-blend": new DataConstantProperty(latest["sky"]["atmosphere-blend"], "atmosphere-blend")
1944});
1945//#endregion
1946//#region src/style/sky.ts
1947var Sky = class extends Evented {
1948	constructor(sky, globalState) {
1949		super();
1950		this._transitionable = new Transitionable(getProperties$1(), "sky", globalState);
1951		this.setSky(sky);
1952		this._transitioning = this._transitionable.untransitioned();
1953		this.recalculate(new EvaluationParameters(0));
1954	}
1955	/**
1956	* Validates and applies the sky. Returns false, after firing `error`, when
1957	* the value was rejected and nothing changed.
1958	*/
1959	setSky(sky, options = {}) {
1960		if (this._validate(validateStyle.sky, sky, options)) return false;
1961		sky ||= {
1962			"sky-color": "transparent",
1963			"horizon-color": "transparent",
1964			"fog-color": "transparent",
1965			"fog-ground-blend": 1,
1966			"atmosphere-blend": 0
1967		};
1968		this._transitionable.setValues(sky);
1969		return true;
1970	}
1971	getSky() {
1972		return this._transitionable.serialize();
1973	}
1974	updateTransitions(parameters) {
1975		this._transitioning = this._transitionable.transitioned(parameters, this._transitioning);
1976	}
1977	hasTransition() {
1978		return this._transitioning.hasTransition();
1979	}
1980	applyGlobalStateChange(refs, priorGlobalState, parameters) {
1981		this._transitioning = this._transitionable.applyGlobalStateChange(refs, priorGlobalState, this._transitioning, parameters);
1982	}
1983	recalculate(parameters) {
1984		this.properties = this._transitioning.possiblyEvaluate(parameters);
1985	}
1986	_validate(validate, value, options = {}) {
1987		return validateAndEmit(this, validate, { value }, options);
1988	}
1989	/**
1990	* Currently fog is a very simple implementation, and should only used
1991	* to create an atmosphere near the horizon.
1992	* But because the fog is drawn from the far-clipping-plane to
1993	* map-center, and because the fog does nothing know about the horizon,
1994	* this method does a fadeout in respect of pitch. So, when the horizon
1995	* gets out of view, which is at about pitch 70, this methods calculates
1996	* the corresponding opacity values. Below pitch 60 the fog is completely
1997	* invisible.
1998	*/
1999	calculateFogBlendOpacity(pitch) {
2000		if (pitch < 60) return 0;
2001		if (pitch < 70) return (pitch - 60) / 10;
2002		return 1;
2003	}
2004};
2005//#endregion
2006//#region src/render/line_atlas.ts
2007/**
2008* @internal
2009* A LineAtlas lets us reuse rendered dashed lines
2010* by writing many of them to a texture and then fetching their positions
2011* using {@link LineAtlas.getDash}.
2012*
2013* @param width - the width
2014* @param height - the height
2015*/
2016var LineAtlas = class {
2017	constructor(width, height) {
2018		this.width = width;
2019		this.height = height;
2020		this.nextRow = 0;
2021		this.data = new Uint8Array(this.width * this.height);
2022		this.dashEntry = {};
2023	}
2024	/**
2025	* Get or create a dash line pattern.
2026	*
2027	* @param dasharray - the key (represented by numbers) to get the dash texture
2028	* @param round - whether to add circle caps in between dash segments
2029	* @returns position of dash texture in {@link DashEntry}
2030	*/
2031	getDash(dasharray, round) {
2032		const key = dasharray.join(",") + String(round);
2033		this.dashEntry[key] ||= this.addDash(dasharray, round);
2034		return this.dashEntry[key];
2035	}
2036	getDashRanges(dasharray, lineAtlasWidth, stretch) {
2037		const oddDashArray = dasharray.length % 2 === 1;
2038		const ranges = [];
2039		let left = oddDashArray ? -dasharray[dasharray.length - 1] * stretch : 0;
2040		let right = dasharray[0] * stretch;
2041		let isDash = true;
2042		ranges.push({
2043			left,
2044			right,
2045			isDash,
2046			zeroLength: dasharray[0] === 0
2047		});
2048		let currentDashLength = dasharray[0];
2049		for (let i = 1; i < dasharray.length; i++) {
2050			isDash = !isDash;
2051			const dashLength = dasharray[i];
2052			left = currentDashLength * stretch;
2053			currentDashLength += dashLength;
2054			right = currentDashLength * stretch;
2055			ranges.push({
2056				left,
2057				right,
2058				isDash,
2059				zeroLength: dashLength === 0
2060			});
2061		}
2062		return ranges;
2063	}
2064	addRoundDash(ranges, stretch, n) {
2065		const halfStretch = stretch / 2;
2066		for (let y = -n; y <= n; y++) {
2067			const row = this.nextRow + n + y;
2068			const index = this.width * row;
2069			let currIndex = 0;
2070			let range = ranges[currIndex];
2071			for (let x = 0; x < this.width; x++) {
2072				if (x / range.right > 1) range = ranges[++currIndex];
2073				const distLeft = Math.abs(x - range.left);
2074				const distRight = Math.abs(x - range.right);
2075				const minDist = Math.min(distLeft, distRight);
2076				let signedDistance;
2077				const distMiddle = y / n * (halfStretch + 1);
2078				if (range.isDash) {
2079					const distEdge = halfStretch - Math.abs(distMiddle);
2080					signedDistance = Math.sqrt(minDist * minDist + distEdge * distEdge);
2081				} else signedDistance = halfStretch - Math.sqrt(minDist * minDist + distMiddle * distMiddle);
2082				this.data[index + x] = Math.max(0, Math.min(255, signedDistance + 128));
2083			}
2084		}
2085	}
2086	addRegularDash(ranges) {
2087		for (let i = ranges.length - 1; i >= 0; --i) {
2088			const part = ranges[i];
2089			const next = ranges[i + 1];
2090			if (part.zeroLength) ranges.splice(i, 1);
2091			else if (next?.isDash === part.isDash) {
2092				next.left = part.left;
2093				ranges.splice(i, 1);
2094			}
2095		}
2096		const first = ranges[0];
2097		const last = ranges[ranges.length - 1];
2098		if (first.isDash === last.isDash) {
2099			first.left = last.left - this.width;
2100			last.right = first.right + this.width;
2101		}
2102		const index = this.width * this.nextRow;
2103		let currIndex = 0;
2104		let range = ranges[currIndex];
2105		for (let x = 0; x < this.width; x++) {
2106			if (x / range.right > 1) range = ranges[++currIndex];
2107			const distLeft = Math.abs(x - range.left);
2108			const distRight = Math.abs(x - range.right);
2109			const minDist = Math.min(distLeft, distRight);
2110			const signedDistance = range.isDash ? minDist : -minDist;
2111			this.data[index + x] = Math.max(0, Math.min(255, signedDistance + 128));
2112		}
2113	}
2114	addDash(dasharray, round) {
2115		const n = round ? 7 : 0;
2116		const height = 2 * n + 1;
2117		if (this.nextRow + height > this.height) {
2118			warnOnce("LineAtlas out of space");
2119			return null;
2120		}
2121		let length = 0;
2122		for (const dash of dasharray) length += dash;
2123		if (length !== 0) {
2124			const stretch = this.width / length;
2125			const ranges = this.getDashRanges(dasharray, this.width, stretch);
2126			if (round) this.addRoundDash(ranges, stretch, n);
2127			else this.addRegularDash(ranges);
2128		}
2129		const dashEntry = {
2130			y: this.nextRow + n,
2131			height: 2 * n,
2132			width: length
2133		};
2134		this.nextRow += height;
2135		this.dirty = true;
2136		return dashEntry;
2137	}
2138	bind(context) {
2139		const gl = context.gl;
2140		if (!this.texture) {
2141			this.texture = gl.createTexture();
2142			gl.bindTexture(gl.TEXTURE_2D, this.texture);
2143			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
2144			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
2145			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
2146			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
2147			gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, this.width, this.height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, this.data);
2148		} else {
2149			gl.bindTexture(gl.TEXTURE_2D, this.texture);
2150			if (this.dirty) {
2151				this.dirty = false;
2152				gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, this.width, this.height, gl.ALPHA, gl.UNSIGNED_BYTE, this.data);
2153			}
2154		}
2155	}
2156};
2157//#endregion
2158//#region src/util/web_worker.ts
2159function isCrossOrigin(url) {
2160	if (!url) return false;
2161	const loc = globalThis.location;
2162	if (!loc) return false;
2163	try {
2164		return new URL(url, loc.href).origin !== loc.origin;
2165	} catch {
2166		return false;
2167	}
2168}
2169function defaultWorkerUrl() {
2170	const moduleUrl = import.meta.url;
2171	if (!/^https?:/.test(moduleUrl)) return "";
2172	const workerName = moduleUrl.endsWith("-dev.mjs") ? "maplibre-gl-worker-dev.mjs" : "maplibre-gl-worker.mjs";
2173	return new URL(`./${workerName}`, moduleUrl).href;
2174}
2175function createWorker(url, asModule) {
2176	if (asModule) try {
2177		return new Worker(url, { type: "module" });
2178	} catch (e) {
2179		console.warn("Module worker not supported, falling back to classic worker", e);
2180	}
2181	return new Worker(url);
2182}
2183async function fetchAsBlobUrl(url) {
2184	const response = await fetch(url);
2185	if (!response.ok) throw new Error(`Failed to fetch worker script (${response.status}): ${url}`);
2186	const code = await response.text();
2187	const blob = new Blob([code], { type: "text/javascript" });
2188	return URL.createObjectURL(blob);
2189}
2190function importAsBlobUrl(url) {
2191	const blob = new Blob([`import ${JSON.stringify(new URL(url, import.meta.url).href)}`], { type: "text/javascript" });
2192	return URL.createObjectURL(blob);
2193}
2194async function workerFactory() {
2195	const url = config.WORKER_URL || defaultWorkerUrl();
2196	const asModule = url?.endsWith(".cjs") ? false : true;
2197	if (!isCrossOrigin(url)) return createWorker(url, asModule);
2198	if (asModule) {
2199		const blobUrl = importAsBlobUrl(url);
2200		try {
2201			return createWorker(blobUrl, asModule);
2202		} finally {
2203			URL.revokeObjectURL(blobUrl);
2204		}
2205	}
2206	const blobUrl = await fetchAsBlobUrl(url);
2207	try {
2208		return createWorker(blobUrl, asModule);
2209	} finally {
2210		URL.revokeObjectURL(blobUrl);
2211	}
2212}
2213//#endregion
2214//#region src/util/worker_pool.ts
2215const PRELOAD_POOL_ID = "maplibre_preloaded_worker_pool";
2216/**
2217* Constructs a worker pool.
2218*/
2219var WorkerPool = class WorkerPool {
2220	constructor() {
2221		this.active = {};
2222		this.workersPromise = null;
2223	}
2224	async acquire(mapId) {
2225		this.active[mapId] = true;
2226		if (!this.workersPromise) {
2227			const promises = [];
2228			while (promises.length < WorkerPool.workerCount) promises.push(workerFactory());
2229			this.workersPromise = Promise.all(promises);
2230		}
2231		return (await this.workersPromise).slice();
2232	}
2233	release(mapId) {
2234		delete this.active[mapId];
2235		if (this.numActive() === 0 && this.workersPromise) {
2236			const promise = this.workersPromise;
2237			this.workersPromise = null;
2238			promise.then((workers) => {
2239				for (const w of workers) w.terminate();
2240			});
2241		}
2242	}
2243	isPreloaded() {
2244		return !!this.active[PRELOAD_POOL_ID];
2245	}
2246	numActive() {
2247		return Object.keys(this.active).length;
2248	}
2249};
2250const availableLogicalProcessors = Math.floor(browser.hardwareConcurrency / 2);
2251WorkerPool.workerCount = isSafari(globalThis) ? Math.max(Math.min(availableLogicalProcessors, 3), 1) : 1;
2252//#endregion
2253//#region src/util/global_worker_pool.ts
2254let globalWorkerPool;
2255/**
2256* Creates (if necessary) and returns the single, global WorkerPool instance
2257* to be shared across each Map
2258*/
2259function getGlobalWorkerPool() {
2260	globalWorkerPool ||= new WorkerPool();
2261	return globalWorkerPool;
2262}
2263/**
2264* Initializes resources like WebWorkers that can be shared across maps to lower load
2265* times in some situations. `setWorkerUrl()` and `setWorkerCount()`, if being
2266* used, must be set before `prewarm()` is called to have an effect.
2267*
2268* By default, the lifecycle of these resources is managed automatically, and they are
2269* lazily initialized when a Map is first created. By invoking `prewarm()`, these
2270* resources will be created ahead of time, and will not be cleared when the last Map
2271* is removed from the page. This allows them to be re-used by new Map instances that
2272* are created later. They can be manually cleared by calling
2273* `clearPrewarmedResources()`. This is only necessary if your web page remains
2274* active but stops using maps altogether.
2275*
2276* This is primarily useful when using GL-JS maps in a single page app, wherein a user
2277* would navigate between various views that can cause Map instances to constantly be
2278* created and destroyed.
2279*
2280* @example
2281* ```ts
2282* prewarm()
2283* ```
2284*/
2285function prewarm() {
2286	getGlobalWorkerPool().acquire(PRELOAD_POOL_ID);
2287}
2288/**
2289* Clears up resources that have previously been created by `prewarm()`.
2290* Note that this is typically not necessary. You should only call this function
2291* if you expect the user of your app to not return to a Map view at any point
2292* in your application.
2293*
2294* @example
2295* ```ts
2296* clearPrewarmedResources()
2297* ```
2298*/
2299function clearPrewarmedResources() {
2300	const pool = globalWorkerPool;
2301	if (pool) {
2302		if (pool.isPreloaded() && pool.numActive() === 1) {
2303			pool.release(PRELOAD_POOL_ID);
2304			globalWorkerPool = null;
2305		} 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()");
2306	}
2307}
2308//#endregion
2309//#region src/util/dispatcher.ts
2310/**
2311* Responsible for sending messages from a {@link Source} to an associated worker source (usually with the same name).
2312*/
2313var Dispatcher = class extends Evented {
2314	constructor(workerPool, mapId) {
2315		super();
2316		this.workerPool = workerPool;
2317		this.actors = [];
2318		this.currentActor = 0;
2319		this.id = mapId;
2320		this.removed = false;
2321		this.workerErrorSubscriptions = [];
2322		this.actorsPromise = this.initActors(mapId);
2323	}
2324	async initActors(mapId) {
2325		const workers = await this.workerPool.acquire(mapId);
2326		if (this.removed) return [];
2327		this.actors = workers.map((worker, i) => {
2328			this.workerErrorSubscriptions.push(subscribe(worker, "error", () => {
2329				this.fire(new ErrorEvent(/* @__PURE__ */ new Error("Worker failed to load. Check that the worker URL is correct.")));
2330			}, false));
2331			const actor = new Actor(worker, mapId);
2332			actor.name = `Worker ${i}`;
2333			return actor;
2334		});
2335		if (!this.actors.length) throw new Error("No actors found");
2336		return this.actors;
2337	}
2338	/**
2339	* Broadcast a message to all Workers.
2340	*/
2341	async broadcast(type, data) {
2342		const actors = await this.actorsPromise;
2343		return Promise.all(actors.map((actor) => actor.sendAsync({
2344			type,
2345			data
2346		})));
2347	}
2348	/**
2349	* Acquires an actor to dispatch messages to. The actors are distributed in round-robin fashion.
2350	* @returns An actor object backed by a web worker for processing messages.
2351	*/
2352	async getActor() {
2353		const actors = await this.actorsPromise;
2354		this.currentActor = (this.currentActor + 1) % actors.length;
2355		return actors[this.currentActor];
2356	}
2357	async waitForInitComplete() {
2358		if (this.actors.length === 0) await this.actorsPromise;
2359	}
2360	getReadyActor() {
2361		this.currentActor = (this.currentActor + 1) % this.actors.length;
2362		return this.actors[this.currentActor];
2363	}
2364	remove(mapRemoved = true) {
2365		this.removed = true;
2366		for (const actor of this.actors) actor.remove();
2367		for (const subscription of this.workerErrorSubscriptions) subscription.unsubscribe();
2368		this.actors = [];
2369		this.workerErrorSubscriptions = [];
2370		this.setEventedParent(null);
2371		if (mapRemoved) this.workerPool.release(this.id);
2372	}
2373	async registerMessageHandler(type, handler) {
2374		const actors = await this.actorsPromise;
2375		for (const actor of actors) actor.registerMessageHandler(type, handler);
2376	}
2377	async unregisterMessageHandler(type) {
2378		const actors = await this.actorsPromise;
2379		for (const actor of actors) actor.unregisterMessageHandler(type);
2380	}
2381};
2382let globalDispatcher;
2383/**
2384* This function is used to get the global dispatcher that is shared across all maps instances.
2385* 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.
2386* If you import a script into the worker and need to send a message to the workers to pass some parameters for example,
2387* you can use this function to get the global dispatcher and send a message to the workers.
2388* @returns The global dispatcher instance.
2389*/
2390function getGlobalDispatcher() {
2391	if (!globalDispatcher) {
2392		globalDispatcher = new Dispatcher(getGlobalWorkerPool(), GLOBAL_DISPATCHER_ID);
2393		globalDispatcher.registerMessageHandler("GR", (_mapId, params, abortController) => {
2394			return makeRequest(params, abortController);
2395		});
2396	}
2397	return globalDispatcher;
2398}
2399//#endregion
2400//#region src/source/query_features.ts
2401function getPixelPosMatrix(transform, tileID) {
2402	const t = create$1();
2403	translate(t, t, [
2404		1,
2405		1,
2406		0
2407	]);
2408	scale(t, t, [
2409		transform.width * .5,
2410		transform.height * .5,
2411		1
2412	]);
2413	if (transform.calculatePosMatrix) return multiply(t, t, transform.calculatePosMatrix(tileID.toUnwrapped()));
2414	else return t;
2415}
2416function queryIncludes3DLayer(layers, styleLayers, sourceID) {
2417	if (layers) for (const layerID of layers) {
2418		const layer = styleLayers[layerID];
2419		if (layer?.source === sourceID && layer.type === "fill-extrusion") return true;
2420	}
2421	else for (const key in styleLayers) {
2422		const layer = styleLayers[key];
2423		if (layer.source === sourceID && layer.type === "fill-extrusion") return true;
2424	}
2425	return false;
2426}
2427function queryRenderedFeatures(tileManager, styleLayers, serializedLayers, queryGeometry, params, transform, getElevation) {
2428	const has3DLayer = queryIncludes3DLayer(params?.layers ?? null, styleLayers, tileManager.id);
2429	const maxPitchScaleFactor = transform.maxPitchScaleFactor();
2430	const tilesIn = tileManager.tilesIn(queryGeometry, maxPitchScaleFactor, has3DLayer);
2431	tilesIn.sort(sortTilesIn);
2432	const renderedFeatureLayers = [];
2433	for (const tileIn of tilesIn) renderedFeatureLayers.push({
2434		wrappedTileID: tileIn.tileID.wrapped().key,
2435		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)
2436	});
2437	return convertFeaturesToMapFeatures(mergeRenderedFeatureLayers(renderedFeatureLayers), tileManager);
2438}
2439function queryRenderedSymbols(styleLayers, serializedLayers, tileManagers, queryGeometry, params, collisionIndex, retainedQueryData) {
2440	const result = {};
2441	const renderedSymbols = collisionIndex.queryRenderedSymbols(queryGeometry);
2442	const bucketQueryData = [];
2443	for (const bucketInstanceId of Object.keys(renderedSymbols).map(Number)) bucketQueryData.push(retainedQueryData[bucketInstanceId]);
2444	bucketQueryData.sort(sortTilesIn);
2445	for (const queryData of bucketQueryData) {
2446		const bucketSymbols = queryData.featureIndex.lookupSymbolFeatures(renderedSymbols[queryData.bucketInstanceId], serializedLayers, queryData.bucketIndex, queryData.sourceLayerIndex, {
2447			filterSpec: params.filter,
2448			globalState: params.globalState
2449		}, params.layers, params.availableImages, styleLayers);
2450		for (const layerID in bucketSymbols) {
2451			result[layerID] ||= [];
2452			const layerSymbols = bucketSymbols[layerID];
2453			layerSymbols.sort((a, b) => {
2454				const featureSortOrder = queryData.featureSortOrder;
2455				if (featureSortOrder) {
2456					const sortedA = featureSortOrder.indexOf(a.featureIndex);
2457					return featureSortOrder.indexOf(b.featureIndex) - sortedA;
2458				} else return b.featureIndex - a.featureIndex;
2459			});
2460			for (const symbolFeature of layerSymbols) result[layerID].push(symbolFeature);
2461		}
2462	}
2463	return convertFeaturesToMapFeaturesMultiple(result, styleLayers, tileManagers);
2464}
2465function querySourceFeatures(tileManager, params) {
2466	const tiles = tileManager.getRenderableIds().map((id) => {
2467		return tileManager.getTileByID(id);
2468	});
2469	const result = [];
2470	const dataTiles = {};
2471	for (const tile of tiles) {
2472		const dataID = tile.tileID.canonical.key;
2473		if (!dataTiles[dataID]) {
2474			dataTiles[dataID] = true;
2475			tile.querySourceFeatures(result, params);
2476		}
2477	}
2478	return result;
2479}
2480function sortTilesIn(a, b) {
2481	const idA = a.tileID;
2482	const idB = b.tileID;
2483	return idA.overscaledZ - idB.overscaledZ || idA.canonical.y - idB.canonical.y || idA.wrap - idB.wrap || idA.canonical.x - idB.canonical.x;
2484}
2485function mergeRenderedFeatureLayers(tiles) {
2486	const result = {};
2487	const wrappedIDLayerMap = {};
2488	for (const { queryResults, wrappedTileID } of tiles) {
2489		wrappedIDLayerMap[wrappedTileID] ||= {};
2490		const wrappedIDLayers = wrappedIDLayerMap[wrappedTileID];
2491		for (const layerID in queryResults) {
2492			const tileFeatures = queryResults[layerID];
2493			wrappedIDLayers[layerID] ||= {};
2494			const wrappedIDFeatures = wrappedIDLayers[layerID];
2495			result[layerID] ||= [];
2496			for (const tileFeature of tileFeatures) if (!wrappedIDFeatures[tileFeature.featureIndex]) {
2497				wrappedIDFeatures[tileFeature.featureIndex] = true;
2498				result[layerID].push(tileFeature);
2499			}
2500		}
2501	}
2502	return result;
2503}
2504function convertFeaturesToMapFeatures(result, tileManager) {
2505	for (const layerID in result) for (const featureWrapper of result[layerID]) convertFeatureToMapFeature(featureWrapper, tileManager);
2506	return result;
2507}
2508function convertFeaturesToMapFeaturesMultiple(result, styleLayers, tileManagers) {
2509	for (const layerName in result) for (const featureWrapper of result[layerName]) {
2510		const tileManager = tileManagers[styleLayers[layerName].source];
2511		convertFeatureToMapFeature(featureWrapper, tileManager);
2512	}
2513	return result;
2514}
2515function convertFeatureToMapFeature(featureWrapper, tileManager) {
2516	const feature = featureWrapper.feature;
2517	const state = tileManager.getFeatureState(feature.layer["source-layer"], feature.id);
2518	feature.source = feature.layer.source;
2519	if (feature.layer["source-layer"]) feature.sourceLayer = feature.layer["source-layer"];
2520	feature.state = state;
2521}
2522//#endregion
2523//#region src/source/load_tilejson.ts
2524async function loadTileJson(options, requestManager, abortController, targetWindow) {
2525	let tileJSON = options;
2526	if (options.url) tileJSON = (await getJSON(await requestManager.transformRequest(options.url, "Source"), abortController)).data;
2527	else await browser.frameAsync(abortController, targetWindow);
2528	if (!tileJSON) return null;
2529	const result = pick(extend(tileJSON, options), [
2530		"tiles",
2531		"minzoom",
2532		"maxzoom",
2533		"attribution",
2534		"bounds",
2535		"scheme",
2536		"tileSize",
2537		"encoding"
2538	]);
2539	if ("vector_layers" in tileJSON && tileJSON.vector_layers) result.vectorLayerIds = tileJSON.vector_layers.map((layer) => layer.id);
2540	return result;
2541}
2542//#endregion
2543//#region src/geo/lng_lat_bounds.ts
2544/**
2545* A `LngLatBounds` object represents a geographical bounding box,
2546* defined by its southwest and northeast points in longitude and latitude.
2547*
2548* If no arguments are provided to the constructor, a `null` bounding box is created.
2549*
2550* Note that any MapLibre GL method that accepts a `LngLatBounds` object as an argument or option
2551* can also accept an `Array` of two {@link LngLatLike} constructs and will perform an implicit conversion.
2552* This flexible type is documented as {@link LngLatBoundsLike}.
2553*
2554* @group Geography and Geometry
2555*
2556* @example
2557* ```ts
2558* let sw = new LngLat(-73.9876, 40.7661);
2559* let ne = new LngLat(-73.9397, 40.8002);
2560* let llb = new LngLatBounds(sw, ne);
2561* ```
2562* @see [Fit to the bounds of a LineString](https://maplibre.org/maplibre-gl-js/docs/examples/fit-to-the-bounds-of-a-linestring/)
2563*/
2564var LngLatBounds = class LngLatBounds {
2565	/**
2566	* @param sw - The southwest corner of the bounding box.
2567	* OR array of 4 numbers in the order of  west, south, east, north
2568	* OR array of 2 LngLatLike: `[sw, ne]`
2569	* @param ne - The northeast corner of the bounding box.
2570	* @example
2571	* ```ts
2572	* let sw = new LngLat(-73.9876, 40.7661);
2573	* let ne = new LngLat(-73.9397, 40.8002);
2574	* let llb = new LngLatBounds(sw, ne);
2575	* ```
2576	* OR
2577	* ```ts
2578	* let llb = new LngLatBounds([-73.9876, 40.7661, -73.9397, 40.8002]);
2579	* ```
2580	* OR
2581	* ```ts
2582	* let llb = new LngLatBounds([sw, ne]);
2583	* ```
2584	*/
2585	constructor(sw, ne) {
2586		if (!sw) {} else if (ne) this.setSouthWest(sw).setNorthEast(ne);
2587		else if (Array.isArray(sw)) {
2588			if (sw.length === 4) this.setSouthWest([sw[0], sw[1]]).setNorthEast([sw[2], sw[3]]);
2589			else this.setSouthWest(sw[0]).setNorthEast(sw[1]);
2590		}
2591	}
2592	/**
2593	* Set the northeast corner of the bounding box
2594	*
2595	* @param ne - a {@link LngLatLike} object describing the northeast corner of the bounding box.
2596	*/
2597	setNorthEast(ne) {
2598		this._ne = ne instanceof LngLat ? new LngLat(ne.lng, ne.lat) : LngLat.convert(ne);
2599		return this;
2600	}
2601	/**
2602	* Set the southwest corner of the bounding box
2603	*
2604	* @param sw - a {@link LngLatLike} object describing the southwest corner of the bounding box.
2605	*/
2606	setSouthWest(sw) {
2607		this._sw = sw instanceof LngLat ? new LngLat(sw.lng, sw.lat) : LngLat.convert(sw);
2608		return this;
2609	}
2610	/**
2611	* Extend the bounds to include a given LngLatLike or LngLatBoundsLike.
2612	*
2613	* @param obj - object to extend to
2614	*/
2615	extend(obj) {
2616		const sw = this._sw, ne = this._ne;
2617		let sw2, ne2;
2618		if (obj instanceof LngLat) {
2619			sw2 = obj;
2620			ne2 = obj;
2621		} else if (obj instanceof LngLatBounds) {
2622			sw2 = obj._sw;
2623			ne2 = obj._ne;
2624			if (!sw2 || !ne2) return this;
2625		} else {
2626			if (Array.isArray(obj)) {
2627				if (obj.length === 4 || obj.every(Array.isArray)) {
2628					const lngLatBoundsObj = obj;
2629					return this.extend(LngLatBounds.convert(lngLatBoundsObj));
2630				} else {
2631					const lngLatObj = obj;
2632					return this.extend(LngLat.convert(lngLatObj));
2633				}
2634			} else if (obj && ("lng" in obj || "lon" in obj) && "lat" in obj) return this.extend(LngLat.convert(obj));
2635			return this;
2636		}
2637		if (!sw && !ne) {
2638			this._sw = new LngLat(sw2.lng, sw2.lat);
2639			this._ne = new LngLat(ne2.lng, ne2.lat);
2640		} else {
2641			sw.lng = Math.min(sw2.lng, sw.lng);
2642			sw.lat = Math.min(sw2.lat, sw.lat);
2643			ne.lng = Math.max(ne2.lng, ne.lng);
2644			ne.lat = Math.max(ne2.lat, ne.lat);
2645		}
2646		return this;
2647	}
2648	/**
2649	* Returns the geographical coordinate equidistant from the bounding box's corners.
2650	*
2651	* @returns The bounding box's center.
2652	* @example
2653	* ```ts
2654	* let llb = new LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]);
2655	* llb.getCenter(); // = LngLat {lng: -73.96365, lat: 40.78315}
2656	* ```
2657	*/
2658	getCenter() {
2659		return new LngLat((this._sw.lng + this._ne.lng) / 2, (this._sw.lat + this._ne.lat) / 2);
2660	}
2661	/**
2662	* Returns the southwest corner of the bounding box.
2663	*
2664	* @returns The southwest corner of the bounding box.
2665	*/
2666	getSouthWest() {
2667		return this._sw;
2668	}
2669	/**
2670	* Returns the northeast corner of the bounding box.
2671	*
2672	* @returns The northeast corner of the bounding box.
2673	*/
2674	getNorthEast() {
2675		return this._ne;
2676	}
2677	/**
2678	* Returns the northwest corner of the bounding box.
2679	*
2680	* @returns The northwest corner of the bounding box.
2681	*/
2682	getNorthWest() {
2683		return new LngLat(this.getWest(), this.getNorth());
2684	}
2685	/**
2686	* Returns the southeast corner of the bounding box.
2687	*
2688	* @returns The southeast corner of the bounding box.
2689	*/
2690	getSouthEast() {
2691		return new LngLat(this.getEast(), this.getSouth());
2692	}
2693	/**
2694	* Returns the west edge of the bounding box.
2695	*
2696	* @returns The west edge of the bounding box.
2697	*/
2698	getWest() {
2699		return this._sw.lng;
2700	}
2701	/**
2702	* Returns the south edge of the bounding box.
2703	*
2704	* @returns The south edge of the bounding box.
2705	*/
2706	getSouth() {
2707		return this._sw.lat;
2708	}
2709	/**
2710	* Returns the east edge of the bounding box.
2711	*
2712	* @returns The east edge of the bounding box.
2713	*/
2714	getEast() {
2715		return this._ne.lng;
2716	}
2717	/**
2718	* Returns the north edge of the bounding box.
2719	*
2720	* @returns The north edge of the bounding box.
2721	*/
2722	getNorth() {
2723		return this._ne.lat;
2724	}
2725	/**
2726	* Returns the bounding box represented as an array.
2727	*
2728	* @returns The bounding box represented as an array, consisting of the
2729	* southwest and northeast coordinates of the bounding represented as arrays of numbers.
2730	* @example
2731	* ```ts
2732	* let llb = new LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]);
2733	* llb.toArray(); // = [[-73.9876, 40.7661], [-73.9397, 40.8002]]
2734	* ```
2735	*/
2736	toArray() {
2737		return [this._sw.toArray(), this._ne.toArray()];
2738	}
2739	/**
2740	* Return the bounding box represented as a string.
2741	*
2742	* @returns The bounding box represents as a string of the format
2743	* `'LngLatBounds(LngLat(lng, lat), LngLat(lng, lat))'`.
2744	* @example
2745	* ```ts
2746	* let llb = new LngLatBounds([-73.9876, 40.7661], [-73.9397, 40.8002]);
2747	* llb.toString(); // = "LngLatBounds(LngLat(-73.9876, 40.7661), LngLat(-73.9397, 40.8002))"
2748	* ```
2749	*/
2750	toString() {
2751		return `LngLatBounds(${this._sw.toString()}, ${this._ne.toString()})`;
2752	}
2753	/**
2754	* Check if the bounding box is an empty/`null`-type box.
2755	*
2756	* @returns True if bounds have been defined, otherwise false.
2757	*/
2758	isEmpty() {
2759		return !(this._sw && this._ne);
2760	}
2761	/**
2762	* Check if the point is within the bounding box.
2763	*
2764	* @param lnglat - geographic point to check against.
2765	* @returns `true` if the point is within the bounding box.
2766	* @example
2767	* ```ts
2768	* let llb = new LngLatBounds(
2769	*   new LngLat(-73.9876, 40.7661),
2770	*   new LngLat(-73.9397, 40.8002)
2771	* );
2772	*
2773	* let ll = new LngLat(-73.9567, 40.7789);
2774	*
2775	* console.log(llb.contains(ll)); // = true
2776	* ```
2777	*/
2778	contains(lnglat) {
2779		const { lng, lat } = LngLat.convert(lnglat);
2780		const containsLatitude = this._sw.lat <= lat && lat <= this._ne.lat;
2781		let containsLongitude = this._sw.lng <= lng && lng <= this._ne.lng;
2782		if (this._sw.lng > this._ne.lng) containsLongitude = this._sw.lng >= lng && lng >= this._ne.lng;
2783		return containsLatitude && containsLongitude;
2784	}
2785	/**
2786	* Checks if this bounding box intersects with another bounding box.
2787	*
2788	* Returns true if the bounding boxes share any area, including cases where
2789	* they only touch along an edge or at a corner.
2790	*
2791	* This method properly handles cases where either or both bounding boxes cross
2792	* the antimeridian (date line).
2793	*/
2794	intersects(other) {
2795		other = LngLatBounds.convert(other);
2796		if (!(other.getNorth() >= this.getSouth() && other.getSouth() <= this.getNorth())) return false;
2797		const thisSpan = Math.abs(this.getEast() - this.getWest());
2798		const otherSpan = Math.abs(other.getEast() - other.getWest());
2799		if (thisSpan >= 360 || otherSpan >= 360) return true;
2800		const thisWest = wrap(this.getWest(), -180, 180);
2801		const thisEast = wrap(this.getEast(), -180, 180);
2802		const otherWest = wrap(other.getWest(), -180, 180);
2803		const otherEast = wrap(other.getEast(), -180, 180);
2804		const thisWraps = thisWest > thisEast;
2805		const otherWraps = otherWest > otherEast;
2806		if (thisWraps && otherWraps) return true;
2807		if (thisWraps) return otherEast >= thisWest || otherWest <= thisEast;
2808		if (otherWraps) return thisEast >= otherWest || thisWest <= otherEast;
2809		return otherWest <= thisEast && otherEast >= thisWest;
2810	}
2811	/**
2812	* Converts an array to a `LngLatBounds` object.
2813	*
2814	* If a `LngLatBounds` object is passed in, the function returns it unchanged.
2815	*
2816	* Internally, the function calls {@link LngLat.convert} to convert arrays to `LngLat` values.
2817	*
2818	* @param input - An array of two coordinates to convert, or a `LngLatBounds` object to return.
2819	* @returns A new `LngLatBounds` object, if a conversion occurred, or the original `LngLatBounds` object.
2820	* @example
2821	* ```ts
2822	* let arr = [[-73.9876, 40.7661], [-73.9397, 40.8002]];
2823	* let llb = LngLatBounds.convert(arr); // = LngLatBounds {_sw: LngLat {lng: -73.9876, lat: 40.7661}, _ne: LngLat {lng: -73.9397, lat: 40.8002}}
2824	* ```
2825	*/
2826	static convert(input) {
2827		if (input instanceof LngLatBounds) return input;
2828		if (!input) return input;
2829		return new LngLatBounds(input);
2830	}
2831	/**
2832	* Returns a `LngLatBounds` from the coordinates extended by a given `radius`. The returned `LngLatBounds` completely contains the `radius`.
2833	*
2834	* @param center - center coordinates of the new bounds.
2835	* @param radius - Distance in meters from the coordinates to extend the bounds.
2836	* @returns A new `LngLatBounds` object representing the coordinates extended by the `radius`.
2837	* @example
2838	* ```ts
2839	* let center = new LngLat(-73.9749, 40.7736);
2840	* LngLatBounds.fromLngLat(100).toArray(); // = [[-73.97501862141328, 40.77351016847229], [-73.97478137858673, 40.77368983152771]]
2841	* ```
2842	*/
2843	static fromLngLat(center, radius = 0) {
2844		const latAccuracy = 360 * radius / 40075017, lngAccuracy = latAccuracy / Math.cos(Math.PI / 180 * center.lat);
2845		return new LngLatBounds(new LngLat(center.lng - lngAccuracy, center.lat - latAccuracy), new LngLat(center.lng + lngAccuracy, center.lat + latAccuracy));
2846	}
2847	/**
2848	* Adjusts the given bounds to handle the case where the bounds cross the 180th meridian (antimeridian).
2849	*
2850	* @returns The adjusted LngLatBounds
2851	* @example
2852	* ```ts
2853	* let bounds = new LngLatBounds([175.813127, -20.157768], [-178. 340903, -15.449124]);
2854	* let adjustedBounds = bounds.adjustAntiMeridian();
2855	* // adjustedBounds will be: [[175.813127, -20.157768], [181.659097, -15.449124]]
2856	* ```
2857	*/
2858	adjustAntiMeridian() {
2859		const sw = new LngLat(this._sw.lng, this._sw.lat);
2860		const ne = new LngLat(this._ne.lng, this._ne.lat);
2861		if (sw.lng > ne.lng) return new LngLatBounds(sw, new LngLat(ne.lng + 360, ne.lat));
2862		return new LngLatBounds(sw, ne);
2863	}
2864};
2865//#endregion
2866//#region src/tile/tile_bounds.ts
2867var TileBounds = class {
2868	constructor(bounds, minzoom, maxzoom) {
2869		this.bounds = LngLatBounds.convert(this.validateBounds(bounds));
2870		this.minzoom = minzoom || 0;
2871		this.maxzoom = maxzoom || 24;
2872	}
2873	validateBounds(bounds) {
2874		if (!Array.isArray(bounds) || bounds.length !== 4) return [
2875			-180,
2876			-90,
2877			180,
2878			90
2879		];
2880		return [
2881			Math.max(-180, bounds[0]),
2882			Math.max(-90, bounds[1]),
2883			Math.min(180, bounds[2]),
2884			Math.min(90, bounds[3])
2885		];
2886	}
2887	contains(tileID) {
2888		const worldSize = Math.pow(2, tileID.z);
2889		const level = {
2890			minX: Math.floor(mercatorXfromLng(this.bounds.getWest()) * worldSize),
2891			minY: Math.floor(mercatorYfromLat(this.bounds.getNorth()) * worldSize),
2892			maxX: Math.ceil(mercatorXfromLng(this.bounds.getEast()) * worldSize),
2893			maxY: Math.ceil(mercatorYfromLat(this.bounds.getSouth()) * worldSize)
2894		};
2895		return tileID.x >= level.minX && tileID.x < level.maxX && tileID.y >= level.minY && tileID.y < level.maxY;
2896	}
2897};
2898//#endregion
2899//#region src/source/vector_tile_source.ts
2900/**
2901* 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/).
2902* (See the [Style Specification](https://maplibre.org/maplibre-style-spec/) for detailed documentation of options.)
2903*
2904* @group Sources
2905*
2906* @example
2907* ```ts
2908* map.addSource('some id', {
2909*     type: 'vector',
2910*     url: 'https://demotiles.maplibre.org/tiles/tiles.json'
2911* });
2912* ```
2913*
2914* @example
2915* ```ts
2916* map.addSource('some id', {
2917*     type: 'vector',
2918*     tiles: ['https://d25uarhxywzl1j.cloudfront.net/v0.1/{z}/{x}/{y}.mvt'],
2919*     minzoom: 6,
2920*     maxzoom: 14
2921* });
2922* ```
2923*
2924* @example
2925* ```ts
2926* map.getSource('some id').setUrl("https://demotiles.maplibre.org/tiles/tiles.json");
2927* ```
2928*
2929* @example
2930* ```ts
2931* map.getSource('some id').setTiles(['https://d25uarhxywzl1j.cloudfront.net/v0.1/{z}/{x}/{y}.mvt']);
2932* ```
2933* @see [Add a vector tile source](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-vector-tile-source/)
2934*/
2935var VectorTileSource = class extends Evented {
2936	constructor(id, options, dispatcher, eventedParent) {
2937		super();
2938		this.id = id;
2939		this.dispatcher = dispatcher;
2940		this.type = "vector";
2941		this.minzoom = 0;
2942		this.maxzoom = 22;
2943		this.scheme = "xyz";
2944		this.tileSize = 512;
2945		this.reparseOverscaled = true;
2946		this.isTileClipped = true;
2947		this._loaded = false;
2948		extend(this, pick(options, [
2949			"url",
2950			"scheme",
2951			"tileSize",
2952			"promoteId",
2953			"encoding"
2954		]));
2955		this._options = extend({ type: "vector" }, options);
2956		this._collectResourceTiming = options.collectResourceTiming;
2957		if (this.tileSize !== 512) throw new Error("vector tile sources must have a tileSize of 512");
2958		this.setEventedParent(eventedParent);
2959	}
2960	async load(sourceDataChanged = false) {
2961		this._loaded = false;
2962		this.fire(new MapSourceDataEvent("dataloading"));
2963		this._tileJSONRequest = new AbortController();
2964		try {
2965			const tileJSON = await loadTileJson(this._options, this.map._requestManager, this._tileJSONRequest, this.map._ownerWindow);
2966			this._tileJSONRequest = null;
2967			this._loaded = true;
2968			if (tileJSON) {
2969				extend(this, tileJSON);
2970				if (tileJSON.bounds) this.tileBounds = new TileBounds(tileJSON.bounds, this.minzoom, this.maxzoom);
2971				this.fire(new MapSourceDataEvent("data", { sourceDataType: "metadata" }));
2972				this.fire(new MapSourceDataEvent("data", {
2973					sourceDataType: "content",
2974					sourceDataChanged
2975				}));
2976			}
2977		} catch (err) {
2978			this._tileJSONRequest = null;
2979			this._loaded = true;
2980			if (!isAbortError(err)) this.fire(new ErrorEvent(ensureError(err)));
2981		}
2982	}
2983	loaded() {
2984		return this._loaded;
2985	}
2986	hasTile(tileID) {
2987		return !this.tileBounds || this.tileBounds.contains(tileID.canonical);
2988	}
2989	onAdd(map) {
2990		this.map = map;
2991		this.load();
2992	}
2993	setSourceProperty(callback) {
2994		if (this._tileJSONRequest) this._tileJSONRequest.abort();
2995		callback();
2996		this.load(true);
2997	}
2998	/**
2999	* Sets the source `tiles` property and re-renders the map.
3000	*
3001	* @param tiles - An array of one or more tile source URLs, as in the TileJSON spec.
3002	*/
3003	setTiles(tiles) {
3004		this.setSourceProperty(() => {
3005			this.tiles = tiles;
3006			this._options.tiles = tiles;
3007		});
3008		return this;
3009	}
3010	/**
3011	* Sets the source `url` property and re-renders the map.
3012	*
3013	* @param url - A URL to a TileJSON resource. Supported protocols are `http:` and `https:`.
3014	*/
3015	setUrl(url) {
3016		this.setSourceProperty(() => {
3017			this.url = url;
3018			this._options.url = url;
3019		});
3020		return this;
3021	}
3022	onRemove() {
3023		if (this._tileJSONRequest) {
3024			this._tileJSONRequest.abort();
3025			this._tileJSONRequest = null;
3026		}
3027	}
3028	serialize() {
3029		return extend({}, this._options);
3030	}
3031	async loadTile(tile) {
3032		const url = tile.tileID.canonical.url(this.tiles, this.map.getPixelRatio(), this.scheme);
3033		const params = {
3034			request: await this.map._requestManager.transformRequest(url, "Tile"),
3035			uid: tile.uid,
3036			tileID: tile.tileID,
3037			zoom: tile.tileID.overscaledZ,
3038			tileSize: this.tileSize * tile.tileID.overscaleFactor(),
3039			type: this.type,
3040			source: this.id,
3041			pixelRatio: this.map.getPixelRatio(),
3042			showCollisionBoxes: this.map.showCollisionBoxes,
3043			promoteId: this.promoteId,
3044			subdivisionGranularity: this.map.style.projection.subdivisionGranularity,
3045			encoding: this.encoding,
3046			overzoomParameters: await this._getOverzoomParameters(tile),
3047			etag: tile.etag
3048		};
3049		params.request.collectResourceTiming = this._collectResourceTiming;
3050		await this.dispatcher.waitForInitComplete();
3051		if (tile.aborted) return;
3052		let messageType = "RT";
3053		if (!tile.actor || tile.state === "expired") {
3054			tile.actor = this.dispatcher.getReadyActor();
3055			messageType = "LT";
3056		} else if (tile.state === "loading") return new Promise((resolve, reject) => {
3057			tile.reloadPromise = {
3058				resolve,
3059				reject
3060			};
3061		});
3062		tile.abortController = new AbortController();
3063		try {
3064			const data = await tile.actor.sendAsync({
3065				type: messageType,
3066				data: params
3067			}, tile.abortController);
3068			delete tile.abortController;
3069			if (tile.aborted) return;
3070			this._afterTileLoadWorkerResponse(tile, data);
3071			const result = {};
3072			if (data?.etagUnmodified) result.unmodified = true;
3073			return result;
3074		} catch (err) {
3075			delete tile.abortController;
3076			if (tile.aborted || isAbortError(err)) return;
3077			if (err && err.status !== 404) throw err;
3078			this._afterTileLoadWorkerResponse(tile, null);
3079		}
3080	}
3081	/**
3082	* When the requested tile has a higher canonical Z than source maxzoom, pass overzoom parameters so worker can load the
3083	* deepest tile at source max zoom to generate sub tiles using geojsonvt for highest performance on vector overscaling
3084	*/
3085	async _getOverzoomParameters(tile) {
3086		if (tile.tileID.canonical.z <= this.maxzoom) return;
3087		if (this.map._zoomLevelsToOverscale === void 0) return;
3088		const maxZoomTileID = tile.tileID.scaledTo(this.maxzoom).canonical;
3089		const maxZoomTileUrl = maxZoomTileID.url(this.tiles, this.map.getPixelRatio(), this.scheme);
3090		return {
3091			maxZoomTileID,
3092			overzoomRequest: await this.map._requestManager.transformRequest(maxZoomTileUrl, "Tile")
3093		};
3094	}
3095	_afterTileLoadWorkerResponse(tile, data) {
3096		if (data?.resourceTiming) tile.resourceTiming = data.resourceTiming;
3097		if (data && this.map._refreshExpiredTiles) tile.setExpiryData(data);
3098		tile.etag = data?.etag;
3099		tile.loadVectorData(data, this.map.painter);
3100		if (tile.reloadPromise) {
3101			const reloadPromise = tile.reloadPromise;
3102			tile.reloadPromise = null;
3103			this.loadTile(tile).then(reloadPromise.resolve).catch(reloadPromise.reject);
3104		}
3105	}
3106	async abortTile(tile) {
3107		if (tile.abortController) {
3108			tile.abortController.abort();
3109			delete tile.abortController;
3110		}
3111		if (tile.actor) await tile.actor.sendAsync({
3112			type: "AT",
3113			data: {
3114				uid: tile.uid,
3115				type: this.type,
3116				source: this.id
3117			}
3118		});
3119	}
3120	async unloadTile(tile) {
3121		tile.unloadVectorData();
3122		if (tile.actor) await tile.actor.sendAsync({
3123			type: "RMT",
3124			data: {
3125				uid: tile.uid,
3126				type: this.type,
3127				source: this.id
3128			}
3129		});
3130	}
3131	hasTransition() {
3132		return false;
3133	}
3134};
3135//#endregion
3136//#region src/source/raster_tile_source.ts
3137/**
3138* A source containing raster tiles (See the [raster source documentation](https://maplibre.org/maplibre-style-spec/sources/#raster) for detailed documentation of options.)
3139*
3140* @group Sources
3141*
3142* \> ℹ️ **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.
3143*
3144* @example
3145* ```ts
3146* map.addSource('raster-source', {
3147*     'type': 'raster',
3148*     'tiles': ['https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg'],
3149*     'tileSize': 256, // Set this to match tile server output to avoid blurry rendering
3150* });
3151* ```
3152*
3153* @example
3154* ```ts
3155* map.addSource('wms-test-source', {
3156*      'type': 'raster',
3157* // use the tiles option to specify a WMS tile source URL
3158*      'tiles': [
3159*          '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'
3160*      ],
3161*      'tileSize': 256 // Important for WMS if tiles are 256px
3162* });
3163* ```
3164* @see [Add a raster tile source](https://maplibre.org/maplibre-gl-js/docs/examples/map-tiles/)
3165* @see [Add a WMS source](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-wms-source/)
3166* @see [Display a satellite map](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-satellite-map/)
3167*/
3168var RasterTileSource = class extends Evented {
3169	constructor(id, options, dispatcher, eventedParent) {
3170		super();
3171		this.id = id;
3172		this.dispatcher = dispatcher;
3173		this.setEventedParent(eventedParent);
3174		this.type = "raster";
3175		this.minzoom = 0;
3176		this.maxzoom = 22;
3177		this.roundZoom = true;
3178		this.scheme = "xyz";
3179		this.tileSize = 512;
3180		this._loaded = false;
3181		this._premultiplyAlpha = true;
3182		this._options = extend({ type: "raster" }, options);
3183		extend(this, pick(options, [
3184			"url",
3185			"scheme",
3186			"tileSize"
3187		]));
3188	}
3189	async load(sourceDataChanged = false) {
3190		this._loaded = false;
3191		this.fire(new MapSourceDataEvent("dataloading"));
3192		this._tileJSONRequest = new AbortController();
3193		try {
3194			const tileJSON = await loadTileJson(this._options, this.map._requestManager, this._tileJSONRequest, this.map._ownerWindow);
3195			this._tileJSONRequest = null;
3196			this._loaded = true;
3197			if (tileJSON) {
3198				extend(this, tileJSON);
3199				if (tileJSON.bounds) this.tileBounds = new TileBounds(tileJSON.bounds, this.minzoom, this.maxzoom);
3200				this.fire(new MapSourceDataEvent("data", { sourceDataType: "metadata" }));
3201				this.fire(new MapSourceDataEvent("data", {
3202					sourceDataType: "content",
3203					sourceDataChanged
3204				}));
3205			}
3206		} catch (err) {
3207			this._tileJSONRequest = null;
3208			this._loaded = true;
3209			if (!isAbortError(err)) this.fire(new ErrorEvent(ensureError(err)));
3210		}
3211	}
3212	loaded() {
3213		return this._loaded;
3214	}
3215	onAdd(map) {
3216		this.map = map;
3217		this.load();
3218	}
3219	onRemove() {
3220		if (this._tileJSONRequest) {
3221			this._tileJSONRequest.abort();
3222			this._tileJSONRequest = null;
3223		}
3224	}
3225	setSourceProperty(callback) {
3226		if (this._tileJSONRequest) {
3227			this._tileJSONRequest.abort();
3228			this._tileJSONRequest = null;
3229		}
3230		callback();
3231		this.load(true);
3232	}
3233	/**
3234	* Sets the source `tiles` property and re-renders the map.
3235	*
3236	* @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/)
3237	*/
3238	setTiles(tiles) {
3239		this.setSourceProperty(() => {
3240			this.tiles = tiles;
3241			this._options.tiles = tiles;
3242		});
3243		return this;
3244	}
3245	/**
3246	* Sets the source `url` property and re-renders the map.
3247	*
3248	* @param url - A URL to a TileJSON resource. Supported protocols are `http:` and `https:`.
3249	*/
3250	setUrl(url) {
3251		this.setSourceProperty(() => {
3252			this.url = url;
3253			this._options.url = url;
3254		});
3255		return this;
3256	}
3257	serialize() {
3258		return extend({}, this._options);
3259	}
3260	/**
3261	* Sets whether alpha premultiplication is applied to raster tile images.
3262	* Set to `false` to preserve exact RGBA byte values when alpha carries data instead of opacity.
3263	*
3264	* @param premultiplyAlpha - If `false`, disables alpha premultiplication for raster tile image decode and texture upload.
3265	* @example
3266	* ```ts
3267	* map.getSource<RasterTileSource>('raster-source').setPremultiplyAlpha(false);
3268	* ```
3269	*/
3270	setPremultiplyAlpha(premultiplyAlpha) {
3271		if (this._premultiplyAlpha === premultiplyAlpha) return this;
3272		this.setSourceProperty(() => {
3273			this._premultiplyAlpha = premultiplyAlpha;
3274		});
3275		return this;
3276	}
3277	hasTile(tileID) {
3278		return !this.tileBounds || this.tileBounds.contains(tileID.canonical);
3279	}
3280	async loadTile(tile) {
3281		const url = tile.tileID.canonical.url(this.tiles, this.map.getPixelRatio(), this.scheme);
3282		const premultiply = this._premultiplyAlpha;
3283		const imageBitmapOptions = premultiply ? void 0 : { premultiplyAlpha: "none" };
3284		tile.abortController = new AbortController();
3285		try {
3286			const response = await ImageRequest.transformAndGetImage(this.map._requestManager, url, "Tile", tile.abortController, this.map._refreshExpiredTiles, imageBitmapOptions);
3287			delete tile.abortController;
3288			if (tile.aborted) {
3289				tile.state = "unloaded";
3290				return;
3291			}
3292			if (response) {
3293				if (this.map._refreshExpiredTiles && (response.cacheControl || response.expires)) tile.setExpiryData({
3294					cacheControl: response.cacheControl,
3295					expires: response.expires
3296				});
3297				const context = this.map.painter.context;
3298				const gl = context.gl;
3299				const img = response.data ?? new RGBAImage({
3300					width: 1,
3301					height: 1
3302				}, /* @__PURE__ */ new Uint8Array(4));
3303				tile.texture = this.map.painter.getTileTexture(img.width);
3304				if (tile.texture) tile.texture.update(img, {
3305					useMipmap: true,
3306					premultiply
3307				});
3308				else {
3309					tile.texture = new Texture(context, img, gl.RGBA, {
3310						useMipmap: true,
3311						premultiply
3312					});
3313					tile.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
3314				}
3315				tile.state = "loaded";
3316			}
3317		} catch (err) {
3318			delete tile.abortController;
3319			if (tile.aborted) tile.state = "unloaded";
3320			else if (err) {
3321				tile.state = "errored";
3322				throw err;
3323			}
3324		}
3325	}
3326	async abortTile(tile) {
3327		if (tile.abortController) {
3328			tile.abortController.abort();
3329			delete tile.abortController;
3330		}
3331	}
3332	async unloadTile(tile) {
3333		if (tile.texture) this.map.painter.saveTileTexture(tile.texture);
3334	}
3335	hasTransition() {
3336		return false;
3337	}
3338};
3339//#endregion
3340//#region src/source/raster_dem_tile_source.ts
3341/**
3342* A source containing raster DEM tiles (See the [Style Specification](https://maplibre.org/maplibre-style-spec/) for detailed documentation of options.)
3343* This source can be used to show hillshading and 3D terrain
3344*
3345* @group Sources
3346*
3347* @example
3348* ```ts
3349* map.addSource('raster-dem-source', {
3350*      type: 'raster-dem',
3351*      url: 'https://demotiles.maplibre.org/terrain-tiles/tiles.json',
3352*      tileSize: 256
3353* });
3354* ```
3355* @see [3D Terrain](https://maplibre.org/maplibre-gl-js/docs/examples/3d-terrain/)
3356*/
3357var RasterDEMTileSource = class extends RasterTileSource {
3358	constructor(id, options, dispatcher, eventedParent) {
3359		super(id, options, dispatcher, eventedParent);
3360		this.type = "raster-dem";
3361		this.maxzoom = 22;
3362		this._options = extend({ type: "raster-dem" }, options);
3363		this.encoding = options.encoding || "mapbox";
3364		this.redFactor = options.redFactor;
3365		this.greenFactor = options.greenFactor;
3366		this.blueFactor = options.blueFactor;
3367		this.baseShift = options.baseShift;
3368	}
3369	async loadTile(tile) {
3370		const url = tile.tileID.canonical.url(this.tiles, this.map.getPixelRatio(), this.scheme);
3371		tile.neighboringTiles = this._getNeighboringTiles(tile.tileID);
3372		tile.abortController = new AbortController();
3373		try {
3374			const response = await ImageRequest.transformAndGetImage(this.map._requestManager, url, "Tile", tile.abortController, this.map._refreshExpiredTiles, { colorSpaceConversion: "none" });
3375			delete tile.abortController;
3376			if (tile.aborted) {
3377				tile.state = "unloaded";
3378				return;
3379			}
3380			if (response) {
3381				if (this.map._refreshExpiredTiles && (response.cacheControl || response.expires)) tile.setExpiryData({
3382					cacheControl: response.cacheControl,
3383					expires: response.expires
3384				});
3385				if (!response.data) {
3386					tile.state = "loaded";
3387					return;
3388				}
3389				const img = response.data;
3390				const rawImageData = isImageBitmap(img) && offscreenCanvasSupported() ? img : await this.readImageNow(img);
3391				const params = {
3392					type: this.type,
3393					uid: tile.uid,
3394					source: this.id,
3395					rawImageData,
3396					encoding: this.encoding,
3397					redFactor: this.redFactor,
3398					greenFactor: this.greenFactor,
3399					blueFactor: this.blueFactor,
3400					baseShift: this.baseShift
3401				};
3402				if (tile.actor && tile.state !== "expired" && tile.state !== "reloading") return;
3403				await this.dispatcher.waitForInitComplete();
3404				if (!tile.actor || tile.state === "expired") tile.actor = this.dispatcher.getReadyActor();
3405				tile.dem = await tile.actor.sendAsync({
3406					type: "LDT",
3407					data: params
3408				});
3409				tile.needsHillshadePrepare = true;
3410				tile.needsTerrainPrepare = true;
3411				tile.needsColorReliefPrepare = true;
3412				tile.state = "loaded";
3413			}
3414		} catch (err) {
3415			delete tile.abortController;
3416			if (tile.aborted) tile.state = "unloaded";
3417			else if (err) {
3418				tile.state = "errored";
3419				throw err;
3420			}
3421		}
3422	}
3423	async readImageNow(img) {
3424		if (typeof VideoFrame !== "undefined" && isOffscreenCanvasDistorted()) {
3425			const width = img.width + 4;
3426			const height = img.height + 4;
3427			try {
3428				return new RGBAImage({
3429					width,
3430					height
3431				}, await readImageUsingVideoFrame(img, -2, -2, width, height));
3432			} catch {}
3433		}
3434		return browser.getImageData(img, 2);
3435	}
3436	_getNeighboringTiles(tileID) {
3437		const canonical = tileID.canonical;
3438		const dim = Math.pow(2, canonical.z);
3439		const px = (canonical.x - 1 + dim) % dim;
3440		const pxw = canonical.x === 0 ? tileID.wrap - 1 : tileID.wrap;
3441		const nx = (canonical.x + 1 + dim) % dim;
3442		const nxw = canonical.x + 1 === dim ? tileID.wrap + 1 : tileID.wrap;
3443		const neighboringTiles = {};
3444		neighboringTiles[new OverscaledTileID(tileID.overscaledZ, pxw, canonical.z, px, canonical.y).key] = { backfilled: false };
3445		neighboringTiles[new OverscaledTileID(tileID.overscaledZ, nxw, canonical.z, nx, canonical.y).key] = { backfilled: false };
3446		if (canonical.y > 0) {
3447			neighboringTiles[new OverscaledTileID(tileID.overscaledZ, pxw, canonical.z, px, canonical.y - 1).key] = { backfilled: false };
3448			neighboringTiles[new OverscaledTileID(tileID.overscaledZ, tileID.wrap, canonical.z, canonical.x, canonical.y - 1).key] = { backfilled: false };
3449			neighboringTiles[new OverscaledTileID(tileID.overscaledZ, nxw, canonical.z, nx, canonical.y - 1).key] = { backfilled: false };
3450		}
3451		if (canonical.y + 1 < dim) {
3452			neighboringTiles[new OverscaledTileID(tileID.overscaledZ, pxw, canonical.z, px, canonical.y + 1).key] = { backfilled: false };
3453			neighboringTiles[new OverscaledTileID(tileID.overscaledZ, tileID.wrap, canonical.z, canonical.x, canonical.y + 1).key] = { backfilled: false };
3454			neighboringTiles[new OverscaledTileID(tileID.overscaledZ, nxw, canonical.z, nx, canonical.y + 1).key] = { backfilled: false };
3455		}
3456		return neighboringTiles;
3457	}
3458	async unloadTile(tile) {
3459		if (tile.demTexture) this.map.painter.saveTileTexture(tile.demTexture);
3460		if (tile.fbo) {
3461			tile.fbo.destroy();
3462			delete tile.fbo;
3463		}
3464		if (tile.dem) delete tile.dem;
3465		delete tile.neighboringTiles;
3466		tile.state = "unloaded";
3467		if (tile.actor) await tile.actor.sendAsync({
3468			type: "RDT",
3469			data: {
3470				type: this.type,
3471				uid: tile.uid,
3472				source: this.id
3473			}
3474		});
3475	}
3476};
3477//#endregion
3478//#region src/source/geojson_source_diff.ts
3479function getFeatureId(feature, promoteId) {
3480	return promoteId ? feature.properties[promoteId] : feature.id;
3481}
3482/**
3483* Converts a GeoJSON object into a map of feature IDs to GeoJSON features.
3484* @param data - The GeoJSON object to convert.
3485* @param promoteId - If set, the feature id will be set to the promoteId property value.
3486* @returns A map of feature IDs to GeoJSON features, or `undefined` if the GeoJSON object is not a valid updateable object.
3487*
3488* Features must have unique identifiers to be updateable. IDs can come from:
3489* - The feature's `id` property (standard GeoJSON)
3490* - A promoted property specified by `promoteId` (e.g., a "name" property)
3491*/
3492function toUpdateable(data, promoteId) {
3493	const updateable = /* @__PURE__ */ new Map();
3494	if (data == null) return updateable;
3495	if (data.type == null) return updateable;
3496	if (data.type === "Feature") {
3497		const id = getFeatureId(data, promoteId);
3498		if (id == null) return void 0;
3499		updateable.set(id, data);
3500		return updateable;
3501	}
3502	if (data.type === "FeatureCollection") {
3503		const seenIds = /* @__PURE__ */ new Set();
3504		for (const feature of data.features) {
3505			const id = getFeatureId(feature, promoteId);
3506			if (id == null) return void 0;
3507			if (seenIds.has(id)) return void 0;
3508			seenIds.add(id);
3509			updateable.set(id, feature);
3510		}
3511		return updateable;
3512	}
3513}
3514/**
3515* Mutates updateable and applies a {@link GeoJSONSourceDiff}. Operations are processed in a specific order to ensure predictable behavior:
3516* 1. Remove operations (removeAll, remove)
3517* 2. Add operations (add)
3518* 3. Update operations (update)
3519* @returns an array of geometries that were affected by the diff - with the exception of removeAll which does not track any affected geometries.
3520*/
3521function applySourceDiff(updateable, diff, promoteId) {
3522	const affectedGeometries = [];
3523	if (diff.removeAll) updateable.clear();
3524	else if (diff.remove) for (const id of diff.remove) {
3525		const existing = updateable.get(id);
3526		if (!existing) continue;
3527		affectedGeometries.push(existing.geometry);
3528		updateable.delete(id);
3529	}
3530	if (diff.add) for (const feature of diff.add) {
3531		const id = getFeatureId(feature, promoteId);
3532		if (id == null) continue;
3533		const existing = updateable.get(id);
3534		if (existing) affectedGeometries.push(existing.geometry);
3535		affectedGeometries.push(feature.geometry);
3536		updateable.set(id, feature);
3537	}
3538	if (diff.update) for (const update of diff.update) {
3539		const existing = updateable.get(update.id);
3540		if (!existing) continue;
3541		const changeGeometry = !!update.newGeometry;
3542		const changeProps = update.removeAllProperties || update.removeProperties?.length > 0 || update.addOrUpdateProperties?.length > 0;
3543		if (!changeGeometry && !changeProps) continue;
3544		affectedGeometries.push(existing.geometry);
3545		const feature = { ...existing };
3546		updateable.set(update.id, feature);
3547		if (changeGeometry) {
3548			affectedGeometries.push(update.newGeometry);
3549			feature.geometry = update.newGeometry;
3550		}
3551		if (changeProps) {
3552			if (update.removeAllProperties) feature.properties = {};
3553			else feature.properties = { ...feature.properties || {} };
3554			if (update.removeProperties) for (const key of update.removeProperties) delete feature.properties[key];
3555			if (update.addOrUpdateProperties) for (const { key, value } of update.addOrUpdateProperties) feature.properties[key] = value;
3556		}
3557	}
3558	return affectedGeometries;
3559}
3560/**
3561* Merge two GeoJSONSourceDiffs, considering the order of operations as specified above (remove, add, update).
3562*
3563* `add` features of a promoteId source carry their id in a property, so `promoteId` is needed to key them.
3564*/
3565function mergeSourceDiffs(prevDiff, nextDiff, promoteId) {
3566	if (!prevDiff) return nextDiff || {};
3567	if (!nextDiff) return prevDiff || {};
3568	const prev = diffToHashed(prevDiff, promoteId);
3569	const next = diffToHashed(nextDiff, promoteId);
3570	resolveMergeConflicts(prev, next);
3571	const merged = {};
3572	if (prev.removeAll || next.removeAll) merged.removeAll = true;
3573	merged.remove = /* @__PURE__ */ new Set([...prev.remove, ...next.remove]);
3574	merged.add = new Map([...prev.add, ...next.add]);
3575	merged.update = new Map([...prev.update, ...next.update]);
3576	if (merged.remove.size && merged.add.size) for (const id of merged.add.keys()) merged.remove.delete(id);
3577	return hashedToDiff(merged);
3578}
3579/**
3580* Resolve merge conflicts between two GeoJSONSourceDiffs considering the ordering above (remove/add/update).
3581*
3582* - If you `removeAll` and then `add` features in the same diff, the added features will be kept.
3583* - Updates only apply to features that exist after removes and adds have been processed.
3584*/
3585function resolveMergeConflicts(prev, next) {
3586	if (next.removeAll) {
3587		prev.add.clear();
3588		prev.update.clear();
3589		prev.remove.clear();
3590		next.remove.clear();
3591	}
3592	for (const id of next.remove) {
3593		prev.add.delete(id);
3594		prev.update.delete(id);
3595	}
3596	for (const [id, nextUpdate] of next.update) {
3597		const prevUpdate = prev.update.get(id);
3598		if (!prevUpdate) continue;
3599		next.update.set(id, mergeFeatureDiffs(prevUpdate, nextUpdate));
3600		prev.update.delete(id);
3601	}
3602}
3603/**
3604* Merge two feature diffs for the same feature id, considering the order of operations as specified above (remove, add/update).
3605*/
3606function mergeFeatureDiffs(prev, next) {
3607	const merged = { id: prev.id };
3608	if (next.removeAllProperties) {
3609		delete prev.removeProperties;
3610		delete prev.addOrUpdateProperties;
3611		delete next.removeProperties;
3612	}
3613	if (next.removeProperties && prev.addOrUpdateProperties) {
3614		const removedProperties = new Set(next.removeProperties);
3615		prev.addOrUpdateProperties = prev.addOrUpdateProperties.filter((prop) => !removedProperties.has(prop.key));
3616	}
3617	if (prev.removeAllProperties || next.removeAllProperties) merged.removeAllProperties = true;
3618	if (prev.removeProperties || next.removeProperties) merged.removeProperties = [...prev.removeProperties || [], ...next.removeProperties || []];
3619	if (prev.addOrUpdateProperties || next.addOrUpdateProperties) merged.addOrUpdateProperties = [...prev.addOrUpdateProperties || [], ...next.addOrUpdateProperties || []];
3620	if (prev.newGeometry || next.newGeometry) merged.newGeometry = next.newGeometry || prev.newGeometry;
3621	return merged;
3622}
3623/**
3624* @internal
3625* Convert a GeoJSONSourceDiff to an idempotent hashed representation using Sets and Maps
3626* @param promoteId - If set, `add` features are keyed by that property instead of by `id`.
3627*/
3628function diffToHashed(diff, promoteId) {
3629	if (!diff) return {};
3630	const hashed = {};
3631	hashed.removeAll = diff.removeAll;
3632	hashed.remove = new Set(diff.remove || []);
3633	hashed.add = new Map(diff.add?.map((feature) => [getFeatureId(feature, promoteId), feature]));
3634	hashed.update = new Map(diff.update?.map((update) => [update.id, update]));
3635	return hashed;
3636}
3637/**
3638* @internal
3639* Convert a hashed GeoJSONSourceDiff back to the array-based representation
3640*/
3641function hashedToDiff(hashed) {
3642	const diff = {};
3643	if (hashed.removeAll) diff.removeAll = hashed.removeAll;
3644	if (hashed.remove) diff.remove = Array.from(hashed.remove);
3645	if (hashed.add) diff.add = Array.from(hashed.add.values());
3646	if (hashed.update) diff.update = Array.from(hashed.update.values());
3647	return diff;
3648}
3649//#endregion
3650//#region src/util/geojson_bounds.ts
3651function extractCoordinates(coords) {
3652	if (!coords || coords.length === 0) return [];
3653	if (typeof coords[0] === "number") return [coords];
3654	return coords.flatMap((c) => extractCoordinates(c));
3655}
3656function getCoordinatesFromGeometry(geometry) {
3657	if (geometry.type === "GeometryCollection") return geometry.geometries.flatMap((g) => getCoordinatesFromGeometry(g));
3658	return extractCoordinates(geometry.coordinates);
3659}
3660/**
3661* Calculates the bounding box of GeoJSON data.
3662* @param data - The GeoJSON data to calculate bounds for
3663* @returns The bounding box of the GeoJSON data
3664*/
3665function getGeoJSONBounds(data) {
3666	const bounds = new LngLatBounds();
3667	let coordinates;
3668	switch (data.type) {
3669		case "FeatureCollection":
3670			coordinates = data.features.flatMap((f) => getCoordinatesFromGeometry(f.geometry));
3671			break;
3672		case "Feature":
3673			coordinates = getCoordinatesFromGeometry(data.geometry);
3674			break;
3675		default: coordinates = getCoordinatesFromGeometry(data);
3676	}
3677	if (coordinates.length === 0) return bounds;
3678	for (const coordinate of coordinates) {
3679		const [lng, lat] = coordinate;
3680		bounds.extend([lng, lat]);
3681	}
3682	return bounds;
3683}
3684//#endregion
3685//#region src/tile/tile_id_to_lng_lat_bounds.ts
3686function tileIdToLngLatBounds({ x, y, z }, buffer = 0) {
3687	const lngMin = lngFromMercatorX((x - buffer) / Math.pow(2, z));
3688	const latMin = latFromMercatorY((y + 1 + buffer) / Math.pow(2, z));
3689	const lngMax = lngFromMercatorX((x + 1 + buffer) / Math.pow(2, z));
3690	const latMax = latFromMercatorY((y - buffer) / Math.pow(2, z));
3691	return new LngLatBounds([lngMin, latMin], [lngMax, latMax]);
3692}
3693//#endregion
3694//#region src/source/geojson_source.ts
3695/**
3696* A source containing GeoJSON.
3697* (See the [Style Specification](https://maplibre.org/maplibre-style-spec/#sources-geojson) for detailed documentation of options.)
3698*
3699* 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.
3700*
3701* @group Sources
3702*
3703* @example
3704* ```ts
3705* map.addSource('some id', {
3706*     type: 'geojson',
3707*     data: 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_ports.geojson'
3708* });
3709* ```
3710*
3711* @example
3712* ```ts
3713* map.addSource('some id', {
3714*    type: 'geojson',
3715*    data: {
3716*        "type": "FeatureCollection",
3717*        "features": [{
3718*            "type": "Feature",
3719*            "properties": {},
3720*            "geometry": {
3721*                "type": "Point",
3722*                "coordinates": [
3723*                    -76.53063297271729,
3724*                    39.18174077994108
3725*                ]
3726*            }
3727*        }]
3728*    }
3729* });
3730* ```
3731*
3732* @example
3733* ```ts
3734* map.getSource('some id').setData({
3735*   "type": "FeatureCollection",
3736*   "features": [{
3737*       "type": "Feature",
3738*       "properties": { "name": "Null Island" },
3739*       "geometry": {
3740*           "type": "Point",
3741*           "coordinates": [ 0, 0 ]
3742*       }
3743*   }]
3744* });
3745* ```
3746* @see [Draw GeoJSON points](https://maplibre.org/maplibre-gl-js/docs/examples/draw-geojson-points/)
3747* @see [Add a GeoJSON line](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-geojson-line/)
3748* @see [Create a heatmap from points](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-heatmap-layer/)
3749* @see [Create and style clusters](https://maplibre.org/maplibre-gl-js/docs/examples/create-and-style-clusters/)
3750*/
3751var GeoJSONSource = class extends Evented {
3752	/** @internal */
3753	constructor(id, options, dispatcher, eventedParent) {
3754		super();
3755		this.id = id;
3756		this.type = "geojson";
3757		this.minzoom = 0;
3758		this.maxzoom = 18;
3759		this.tileSize = 512;
3760		this.isTileClipped = true;
3761		this.reparseOverscaled = true;
3762		this._removed = false;
3763		this._isUpdatingWorker = false;
3764		this._pendingWorkerUpdate = { data: options.data };
3765		this.actorPromise = dispatcher.getActor();
3766		this.setEventedParent(eventedParent);
3767		this._data = typeof options.data === "string" ? { url: options.data } : { geojson: options.data };
3768		this._options = extend({}, options);
3769		this._collectResourceTiming = options.collectResourceTiming;
3770		if (options.maxzoom !== void 0) this.maxzoom = options.maxzoom;
3771		if (options.type) this.type = options.type;
3772		if (options.attribution) this.attribution = options.attribution;
3773		this.promoteId = options.promoteId;
3774		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}".`);
3775		this.workerOptions = extend({
3776			source: this.id,
3777			geojsonVtOptions: {
3778				buffer: this._pixelsToTileUnits(options.buffer !== void 0 ? options.buffer : 128),
3779				tolerance: this._pixelsToTileUnits(options.tolerance !== void 0 ? options.tolerance : .375),
3780				extent: EXTENT,
3781				maxZoom: this.maxzoom,
3782				lineMetrics: options.lineMetrics || false,
3783				generateId: options.generateId || false,
3784				promoteId: this._promoteIdKey,
3785				cluster: options.cluster || false,
3786				clusterOptions: {
3787					maxZoom: this._getClusterMaxZoom(options.clusterMaxZoom),
3788					minPoints: Math.max(2, options.clusterMinPoints || 2),
3789					extent: EXTENT,
3790					radius: this._pixelsToTileUnits(options.clusterRadius || 50),
3791					log: false,
3792					generateId: options.generateId || false
3793				}
3794			},
3795			clusterProperties: options.clusterProperties,
3796			filter: options.filter
3797		}, options.workerOptions);
3798	}
3799	/** The `promoteId` property name, when it is a plain string. */
3800	get _promoteIdKey() {
3801		return typeof this.promoteId === "string" ? this.promoteId : void 0;
3802	}
3803	_hasPendingWorkerUpdate() {
3804		return this._pendingWorkerUpdate.data !== void 0 || this._pendingWorkerUpdate.diff !== void 0 || this._pendingWorkerUpdate.updateCluster;
3805	}
3806	_pixelsToTileUnits(pixelValue) {
3807		return pixelValue * (EXTENT / this.tileSize);
3808	}
3809	_tileUnitsToPixels(tileUnitValue) {
3810		return tileUnitValue / (EXTENT / this.tileSize);
3811	}
3812	_getClusterMaxZoom(clusterMaxZoom) {
3813		const effectiveClusterMaxZoom = clusterMaxZoom ? Math.round(clusterMaxZoom) : this.maxzoom - 1;
3814		if (!(Number.isInteger(clusterMaxZoom) || clusterMaxZoom === void 0)) warnOnce(`Integer expected for option 'clusterMaxZoom': provided value "${clusterMaxZoom}" rounded to "${effectiveClusterMaxZoom}"`);
3815		return effectiveClusterMaxZoom;
3816	}
3817	async load() {
3818		await this._updateWorkerData();
3819	}
3820	onAdd(map) {
3821		this.map = map;
3822		this.load();
3823	}
3824	/**
3825	* Sets the GeoJSON data and re-renders the map.
3826	*
3827	* @param data - A GeoJSON data object or a URL to one. The latter is preferable in the case of large GeoJSON files.
3828	*/
3829	setData(data) {
3830		this._data = typeof data === "string" ? { url: data } : { geojson: data };
3831		this._pendingWorkerUpdate = { data };
3832		return this._updateWorkerData();
3833	}
3834	/**
3835	* Updates the source's GeoJSON, and re-renders the map.
3836	*
3837	* For sources with lots of features, this method can be used to make updates more quickly.
3838	*
3839	* This approach requires unique IDs for every feature in the source. The IDs can either be specified on the feature,
3840	* or by using the promoteId option to specify which property should be used as the ID.
3841	*
3842	* It is an error to call updateData on a source that did not have unique IDs for each of its features already.
3843	*
3844	* Updates are applied on a best-effort basis, updating an ID that does not exist will not result in an error.
3845	*
3846	* @param diff - The changes that need to be applied.
3847	*/
3848	updateData(diff) {
3849		this._pendingWorkerUpdate.diff = mergeSourceDiffs(this._pendingWorkerUpdate.diff, diff, this._promoteIdKey);
3850		return this._updateWorkerData();
3851	}
3852	/**
3853	* Allows to get the source's actual GeoJSON data.
3854	*
3855	* @returns a promise which resolves to the source's actual GeoJSON data
3856	*/
3857	async getData() {
3858		if (this._data.url) await this.once("data");
3859		if (this._data.geojson) return this._data.geojson;
3860		return {
3861			type: "FeatureCollection",
3862			features: Array.from(this._data.updateable.values())
3863		};
3864	}
3865	/**
3866	* Allows getting the source's boundaries.
3867	* If there's a problem with the source's data, it will return an empty {@link LngLatBounds}.
3868	* @returns a promise which resolves to the source's boundaries
3869	*/
3870	async getBounds() {
3871		return getGeoJSONBounds(await this.getData());
3872	}
3873	/**
3874	* To disable/enable clustering on the source options
3875	* @param options - The options to set
3876	* @example
3877	* ```ts
3878	* map.getSource('some id').setClusterOptions({cluster: false});
3879	* map.getSource('some id').setClusterOptions({cluster: false, clusterRadius: 50, clusterMaxZoom: 14});
3880	* ```
3881	*/
3882	setClusterOptions(options) {
3883		this.workerOptions.geojsonVtOptions.cluster = options.cluster;
3884		if (options.clusterRadius !== void 0) this.workerOptions.geojsonVtOptions.clusterOptions.radius = this._pixelsToTileUnits(options.clusterRadius);
3885		if (options.clusterMaxZoom !== void 0) this.workerOptions.geojsonVtOptions.clusterOptions.maxZoom = this._getClusterMaxZoom(options.clusterMaxZoom);
3886		this._pendingWorkerUpdate.updateCluster = true;
3887		return this._updateWorkerData();
3888	}
3889	/**
3890	* Gets the cluster options currently configured on the source.
3891	* The returned values mirror the options accepted by `setClusterOptions`.
3892	*
3893	* @returns the source's current cluster options
3894	* @example
3895	* ```ts
3896	* const {cluster, clusterMaxZoom, clusterRadius} = map.getSource('some id').getClusterOptions();
3897	* ```
3898	*/
3899	getClusterOptions() {
3900		const { cluster, clusterOptions } = this.workerOptions.geojsonVtOptions;
3901		return {
3902			cluster,
3903			clusterMaxZoom: clusterOptions.maxZoom,
3904			clusterRadius: this._tileUnitsToPixels(clusterOptions.radius)
3905		};
3906	}
3907	/**
3908	* For clustered sources, fetches the zoom at which the given cluster expands.
3909	*
3910	* @param clusterId - The value of the cluster's `cluster_id` property.
3911	* @returns a promise that is resolved with the zoom number
3912	*/
3913	async getClusterExpansionZoom(clusterId) {
3914		return (await this.actorPromise).sendAsync({
3915			type: "GCEZ",
3916			data: {
3917				type: this.type,
3918				clusterId,
3919				source: this.id
3920			}
3921		});
3922	}
3923	/**
3924	* For clustered sources, fetches the children of the given cluster on the next zoom level (as an array of GeoJSON features).
3925	*
3926	* @param clusterId - The value of the cluster's `cluster_id` property.
3927	* @returns a promise that is resolved when the features are retrieved
3928	*/
3929	async getClusterChildren(clusterId) {
3930		return (await this.actorPromise).sendAsync({
3931			type: "GCC",
3932			data: {
3933				type: this.type,
3934				clusterId,
3935				source: this.id
3936			}
3937		});
3938	}
3939	/**
3940	* For clustered sources, fetches the original points that belong to the cluster (as an array of GeoJSON features).
3941	*
3942	* @param clusterId - The value of the cluster's `cluster_id` property.
3943	* @param limit - The maximum number of features to return.
3944	* @param offset - The number of features to skip (e.g. for pagination).
3945	* @returns a promise that is resolved when the features are retrieved
3946	* @example
3947	* Retrieve cluster leaves on click
3948	* ```ts
3949	* map.on('click', 'clusters', (e) => {
3950	*   let features = map.queryRenderedFeatures(e.point, {
3951	*     layers: ['clusters']
3952	*   });
3953	*
3954	*   let clusterId = features[0].properties.cluster_id;
3955	*   let pointCount = features[0].properties.point_count;
3956	*   let clusterSource = map.getSource('clusters');
3957	*
3958	*   const features = await clusterSource.getClusterLeaves(clusterId, pointCount);
3959	*   // Print cluster leaves in the console
3960	*   console.log('Cluster leaves:', features);
3961	* });
3962	* ```
3963	*/
3964	async getClusterLeaves(clusterId, limit, offset) {
3965		return (await this.actorPromise).sendAsync({
3966			type: "GCL",
3967			data: {
3968				type: this.type,
3969				source: this.id,
3970				clusterId,
3971				limit,
3972				offset
3973			}
3974		});
3975	}
3976	/**
3977	* Responsible for invoking WorkerSource's geojson.loadData target, which
3978	* handles loading the geojson data and preparing to serve it up as tiles,
3979	* using geojson-vt or supercluster as appropriate.
3980	*/
3981	async _updateWorkerData() {
3982		if (this._isUpdatingWorker) return this._updatePromise;
3983		if (!this._hasPendingWorkerUpdate()) {
3984			warnOnce(`No pending worker updates for GeoJSONSource ${this.id}.`);
3985			return;
3986		}
3987		const { data, diff, updateCluster } = this._pendingWorkerUpdate;
3988		const params = this._getLoadGeoJSONParameters(data, diff, updateCluster);
3989		if (data !== void 0) this._pendingWorkerUpdate.data = void 0;
3990		else if (diff) this._pendingWorkerUpdate.diff = void 0;
3991		else if (updateCluster) this._pendingWorkerUpdate.updateCluster = void 0;
3992		this._updatePromise = this._dispatchWorkerUpdate(params);
3993		await this._updatePromise;
3994	}
3995	/**
3996	* Create the parameters object that will be sent to the worker and used to load GeoJSON.
3997	*/
3998	async _getLoadGeoJSONParameters(data, diff, updateCluster) {
3999		const params = extend({
4000			type: this.type,
4001			source: this.id
4002		}, this.workerOptions);
4003		if (typeof data === "string") {
4004			params.request = await this.map._requestManager.transformRequest(browser.resolveURL(data), "Source");
4005			params.request.collectResourceTiming = this._collectResourceTiming;
4006			return params;
4007		}
4008		if (data !== void 0) {
4009			params.data = data;
4010			return params;
4011		}
4012		if (diff) {
4013			params.dataDiff = diff;
4014			return params;
4015		}
4016		if (updateCluster) {
4017			params.updateCluster = true;
4018			return params;
4019		}
4020	}
4021	/**
4022	* Send the worker update data from the main thread to the worker
4023	*/
4024	async _dispatchWorkerUpdate(optionsPromise) {
4025		this._isUpdatingWorker = true;
4026		this.fire(new MapSourceDataEvent("dataloading"));
4027		try {
4028			const options = await optionsPromise;
4029			const result = await (await this.actorPromise).sendAsync({
4030				type: "LD",
4031				data: options
4032			});
4033			this._isUpdatingWorker = false;
4034			if (this._removed || result.abandoned) {
4035				this.fire(new MapSourceDataEvent("dataabort"));
4036				return;
4037			}
4038			if (result.data) this._data = { geojson: result.data };
4039			const affectedGeometries = this._applyDiffToSource(options.dataDiff);
4040			const shouldReloadTileOptions = this._getShouldReloadTileOptions(affectedGeometries);
4041			const eventData = {};
4042			this._applyResourceTiming(eventData, result);
4043			this.fire(new MapSourceDataEvent("data", {
4044				...eventData,
4045				sourceDataType: "metadata"
4046			}));
4047			this.fire(new MapSourceDataEvent("data", {
4048				...eventData,
4049				sourceDataType: "content",
4050				shouldReloadTileOptions
4051			}));
4052		} catch (err) {
4053			this._isUpdatingWorker = false;
4054			if (this._removed) {
4055				this.fire(new MapSourceDataEvent("dataabort"));
4056				return;
4057			}
4058			this.fire(new ErrorEvent(ensureError(err)));
4059		} finally {
4060			if (this._hasPendingWorkerUpdate()) await this._updateWorkerData();
4061		}
4062	}
4063	/**
4064	* Apply resource timing data to the event object.
4065	*/
4066	_applyResourceTiming(eventData, result) {
4067		if (!this._collectResourceTiming) return;
4068		const timingData = result.resourceTiming?.[this.id];
4069		if (!timingData) return;
4070		const resourceTiming = timingData.slice(0);
4071		if (!resourceTiming?.length) return;
4072		extend(eventData, { resourceTiming });
4073	}
4074	/**
4075	* Apply a diff to this source's data and return the affected feature geometries.
4076	* @param diff - The {@link GeoJSONSourceDiff} to apply.
4077	* @returns The affected geometries, or undefined if the diff is not applicable or all geometries are affected.
4078	*/
4079	_applyDiffToSource(diff) {
4080		if (!diff) return;
4081		const promoteId = this._promoteIdKey;
4082		if (!this._data.url && !this._data.updateable) {
4083			const updateable = toUpdateable(this._data.geojson, promoteId);
4084			if (!updateable) throw new Error(`GeoJSONSource "${this.id}": GeoJSON data is not compatible with updateData`);
4085			this._data = { updateable };
4086		}
4087		if (!this._data.updateable) return;
4088		const affectedGeometries = applySourceDiff(this._data.updateable, diff, promoteId);
4089		if (diff.removeAll || this._options.cluster) return;
4090		return affectedGeometries;
4091	}
4092	/**
4093	* Get options for use in determining whether to reload a tile based on the modified features.
4094	* @param affectedGeometries - The feature geometries affected by the update.
4095	* @returns A {@link GeoJSONSourceShouldReloadTileOptions} object which contains an array of affected bounds caused by the update.
4096	*/
4097	_getShouldReloadTileOptions(affectedGeometries) {
4098		if (!affectedGeometries) return void 0;
4099		return { affectedBounds: affectedGeometries.filter(Boolean).map((g) => getGeoJSONBounds(g)) };
4100	}
4101	/**
4102	* Determine whether a tile should be reloaded based on a set of options associated with a {@link MapSourceDataChangedEvent}.
4103	* @internal
4104	*/
4105	shouldReloadTile(tile, { affectedBounds }) {
4106		if (tile.state === "loading") return true;
4107		if (tile.state === "unloaded") return false;
4108		const { buffer, extent } = this.workerOptions.geojsonVtOptions;
4109		const tileBounds = tileIdToLngLatBounds(tile.tileID.canonical, buffer / extent);
4110		for (const bounds of affectedBounds) if (tileBounds.intersects(bounds)) return true;
4111		return false;
4112	}
4113	loaded() {
4114		return !this._isUpdatingWorker && !this._hasPendingWorkerUpdate();
4115	}
4116	async loadTile(tile) {
4117		const message = !tile.actor ? "LT" : "RT";
4118		tile.actor = await this.actorPromise;
4119		const params = {
4120			type: this.type,
4121			uid: tile.uid,
4122			tileID: tile.tileID,
4123			zoom: tile.tileID.overscaledZ,
4124			maxZoom: this.maxzoom,
4125			tileSize: this.tileSize,
4126			source: this.id,
4127			pixelRatio: this.map.getPixelRatio(),
4128			showCollisionBoxes: this.map.showCollisionBoxes,
4129			promoteId: this.promoteId,
4130			subdivisionGranularity: this.map.style.projection.subdivisionGranularity
4131		};
4132		tile.abortController = new AbortController();
4133		try {
4134			const data = await (await this.actorPromise).sendAsync({
4135				type: message,
4136				data: params
4137			}, tile.abortController);
4138			delete tile.abortController;
4139			tile.unloadVectorData();
4140			if (!tile.aborted) tile.loadVectorData(data, this.map.painter, message === "RT");
4141		} catch (err) {
4142			delete tile.abortController;
4143			if (tile.aborted || isAbortError(err)) return;
4144			throw err;
4145		}
4146	}
4147	async abortTile(tile) {
4148		if (tile.abortController) {
4149			tile.abortController.abort();
4150			delete tile.abortController;
4151		}
4152		tile.aborted = true;
4153	}
4154	async unloadTile(tile) {
4155		tile.unloadVectorData();
4156		await (await this.actorPromise).sendAsync({
4157			type: "RMT",
4158			data: {
4159				uid: tile.uid,
4160				type: this.type,
4161				source: this.id
4162			}
4163		});
4164	}
4165	onRemove() {
4166		this._removed = true;
4167		this.actorPromise.then((actor) => actor.sendAsync({
4168			type: "RS",
4169			data: {
4170				type: this.type,
4171				source: this.id
4172			}
4173		}));
4174	}
4175	serialize() {
4176		return extend({}, this._options, {
4177			type: this.type,
4178			data: this._data.updateable ? {
4179				type: "FeatureCollection",
4180				features: Array.from(this._data.updateable.values())
4181			} : this._data.url || this._data.geojson
4182		});
4183	}
4184	hasTransition() {
4185		return false;
4186	}
4187};
4188//#endregion
4189//#region src/webgl/program/raster_program.ts
4190/** The bilinear warp, which is also the affine one that a rectangle or a parallelogram needs. */
4191const bilinearImageWarp = [
4192	0,
4193	0,
4194	1
4195];
4196const rasterUniforms = (context, locations) => ({
4197	"u_tl_parent": new Uniform2f(context, locations.u_tl_parent),
4198	"u_scale_parent": new Uniform1f(context, locations.u_scale_parent),
4199	"u_buffer_scale": new Uniform1f(context, locations.u_buffer_scale),
4200	"u_image_warp": new Uniform3f(context, locations.u_image_warp),
4201	"u_fade_t": new Uniform1f(context, locations.u_fade_t),
4202	"u_opacity": new Uniform1f(context, locations.u_opacity),
4203	"u_image0": new Uniform1i(context, locations.u_image0),
4204	"u_image1": new Uniform1i(context, locations.u_image1),
4205	"u_brightness_low": new Uniform1f(context, locations.u_brightness_low),
4206	"u_brightness_high": new Uniform1f(context, locations.u_brightness_high),
4207	"u_saturation_factor": new Uniform1f(context, locations.u_saturation_factor),
4208	"u_contrast_factor": new Uniform1f(context, locations.u_contrast_factor),
4209	"u_spin_weights": new Uniform3f(context, locations.u_spin_weights),
4210	"u_coords_top": new Uniform4f(context, locations.u_coords_top),
4211	"u_coords_bottom": new Uniform4f(context, locations.u_coords_bottom)
4212});
4213const rasterUniformValues = (parentTL, parentScaleBy, fade, layer, cornerCoords, imageWarp) => ({
4214	"u_tl_parent": parentTL,
4215	"u_scale_parent": parentScaleBy,
4216	"u_buffer_scale": 1,
4217	"u_image_warp": imageWarp,
4218	"u_fade_t": fade.mix,
4219	"u_opacity": fade.opacity * layer.paint.get("raster-opacity"),
4220	"u_image0": 0,
4221	"u_image1": 1,
4222	"u_brightness_low": layer.paint.get("raster-brightness-min"),
4223	"u_brightness_high": layer.paint.get("raster-brightness-max"),
4224	"u_saturation_factor": saturationFactor(layer.paint.get("raster-saturation")),
4225	"u_contrast_factor": contrastFactor(layer.paint.get("raster-contrast")),
4226	"u_spin_weights": spinWeights(layer.paint.get("raster-hue-rotate")),
4227	"u_coords_top": [
4228		cornerCoords[0].x,
4229		cornerCoords[0].y,
4230		cornerCoords[1].x,
4231		cornerCoords[1].y
4232	],
4233	"u_coords_bottom": [
4234		cornerCoords[3].x,
4235		cornerCoords[3].y,
4236		cornerCoords[2].x,
4237		cornerCoords[2].y
4238	]
4239});
4240function spinWeights(angle) {
4241	angle *= Math.PI / 180;
4242	const s = Math.sin(angle);
4243	const c = Math.cos(angle);
4244	return [
4245		(2 * c + 1) / 3,
4246		(-Math.sqrt(3) * s - c + 1) / 3,
4247		(Math.sqrt(3) * s - c + 1) / 3
4248	];
4249}
4250function contrastFactor(contrast) {
4251	return contrast > 0 ? 1 / (1 - contrast) : 1 + contrast;
4252}
4253function saturationFactor(saturation) {
4254	return saturation > 0 ? 1 - 1 / (1.001 - saturation) : -saturation;
4255}
4256//#endregion
4257//#region src/render/mesh.ts
4258var Mesh = class {
4259	constructor(vertexBuffer, indexBuffer, segments) {
4260		this.vertexBuffer = vertexBuffer;
4261		this.indexBuffer = indexBuffer;
4262		this.segments = segments;
4263	}
4264	destroy() {
4265		this.vertexBuffer.destroy();
4266		this.indexBuffer.destroy();
4267		this.segments.destroy();
4268		this.vertexBuffer = null;
4269		this.indexBuffer = null;
4270		this.segments = null;
4271	}
4272};
4273//#endregion
4274//#region src/data/pos_attributes.ts
4275const posAttributes = createLayout([{
4276	name: "a_pos",
4277	type: "Int16",
4278	components: 2
4279}]);
4280//#endregion
4281//#region src/util/create_tile_mesh.ts
4282/**
4283* The size of border region for stencil masks, in internal tile coordinates.
4284* Used for globe rendering.
4285*/
4286const EXTENT_STENCIL_BORDER = EXTENT / 128;
4287/**
4288* @internal
4289* Creates a mesh of a quad that covers the entire tile (covering positions in range 0..EXTENT),
4290* is optionally subdivided into finer quads, optionally includes a border
4291* and optionally extends to the north and/or special pole vertices.
4292* Also allocates and populates WebGL buffers for the mesh.
4293* Forces 16 bit indices that are used throughout MapLibre.
4294* @param context - The WebGL context wrapper.
4295* @param options - Specify options for tile mesh creation such as granularity or border.
4296* @returns The mesh vertices and indices, already allocated and uploaded into WebGL buffers.
4297*/
4298function createTileMeshWithBuffers(context, options) {
4299	const tileMesh = createTileMesh(options, "16bit");
4300	const vertices = PosArray.deserialize({
4301		arrayBuffer: tileMesh.vertices,
4302		length: tileMesh.vertices.byteLength / 2 / 2
4303	});
4304	const indices = TriangleIndexArray.deserialize({
4305		arrayBuffer: tileMesh.indices,
4306		length: tileMesh.indices.byteLength / 2 / 3
4307	});
4308	return new Mesh(context.createVertexBuffer(vertices, posAttributes.members), context.createIndexBuffer(indices), SegmentVector.simpleSegment(0, 0, vertices.length, indices.length));
4309}
4310/**
4311* Creates a mesh of a quad that covers the entire tile (covering positions in range 0..EXTENT),
4312* is optionally subdivided into finer quads, optionally includes a border
4313* and optionally extends to the north and/or special pole vertices.
4314* Additionally the resulting mesh indices type can be specified using `forceIndicesSize`.
4315* @example
4316* ```
4317* // Creating a mesh for a tile that can be used for raster layers, hillshade, etc.
4318* const meshBuffers = createTileMesh({
4319*     granularity: map.style.projection.subdivisionGranularity.tile.getGranularityForZoomLevel(tileID.z),
4320*     generateBorders: true,
4321*     extendToNorthPole: tileID.y === 0,
4322*     extendToSouthPole: tileID.y === (1 << tileID.z) - 1,
4323* }, '16bit');
4324* ```
4325* @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/)
4326* @param options - Specify options for tile mesh creation such as granularity or border.
4327* @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.
4328* @returns Typed arrays of the mesh vertices and indices.
4329*/
4330function createTileMesh(options, forceIndicesSize) {
4331	const granularity = options.granularity !== void 0 ? Math.max(options.granularity, 1) : 1;
4332	const quadsPerAxisX = granularity + (options.generateBorders ? 2 : 0);
4333	const quadsPerAxisY = granularity + (options.extendToNorthPole || options.generateBorders ? 1 : 0) + (options.extendToSouthPole || options.generateBorders ? 1 : 0);
4334	const verticesPerAxisX = quadsPerAxisX + 1;
4335	const verticesPerAxisY = quadsPerAxisY + 1;
4336	const offsetX = options.generateBorders ? -1 : 0;
4337	const offsetY = options.generateBorders || options.extendToNorthPole ? -1 : 0;
4338	const endX = granularity + (options.generateBorders ? 1 : 0);
4339	const endY = granularity + (options.generateBorders || options.extendToSouthPole ? 1 : 0);
4340	const vertexCount = verticesPerAxisX * verticesPerAxisY;
4341	const indexCount = quadsPerAxisX * quadsPerAxisY * 6;
4342	const overflows16bitIndices = verticesPerAxisX * verticesPerAxisY > 65536;
4343	if (overflows16bitIndices && forceIndicesSize === "16bit") throw new Error("Granularity is too large and meshes would not fit inside 16 bit vertex indices.");
4344	const use32bitIndices = overflows16bitIndices || forceIndicesSize === "32bit";
4345	const vertices = new Int16Array(vertexCount * 2);
4346	let vertexId = 0;
4347	for (let y = offsetY; y <= endY; y++) for (let x = offsetX; x <= endX; x++) {
4348		let vx = x / granularity * EXTENT;
4349		if (x === -1) vx = -EXTENT_STENCIL_BORDER;
4350		if (x === granularity + 1) vx = EXTENT + EXTENT_STENCIL_BORDER;
4351		let vy = y / granularity * EXTENT;
4352		if (y === -1) vy = options.extendToNorthPole ? NORTH_POLE_Y : -EXTENT_STENCIL_BORDER;
4353		if (y === granularity + 1) vy = options.extendToSouthPole ? SOUTH_POLE_Y : EXTENT + EXTENT_STENCIL_BORDER;
4354		vertices[vertexId++] = vx;
4355		vertices[vertexId++] = vy;
4356	}
4357	const indices = use32bitIndices ? new Uint32Array(indexCount) : new Uint16Array(indexCount);
4358	let indexId = 0;
4359	for (let y = 0; y < quadsPerAxisY; y++) for (let x = 0; x < quadsPerAxisX; x++) {
4360		const v0 = x + y * verticesPerAxisX;
4361		const v1 = x + 1 + y * verticesPerAxisX;
4362		const v2 = x + (y + 1) * verticesPerAxisX;
4363		const v3 = x + 1 + (y + 1) * verticesPerAxisX;
4364		indices[indexId++] = v0;
4365		indices[indexId++] = v2;
4366		indices[indexId++] = v1;
4367		indices[indexId++] = v1;
4368		indices[indexId++] = v2;
4369		indices[indexId++] = v3;
4370	}
4371	return {
4372		vertices: vertices.buffer.slice(0),
4373		indices: indices.buffer.slice(0),
4374		uses32bitIndices: use32bitIndices
4375	};
4376}
4377//#endregion
4378//#region src/source/image_source.ts
4379/**
4380* How many grid cells per axis the mesh of a subdivided quad has. Within a cell the bilinear warp
4381* is still approximated by one mapping per triangle, so the error falls off with the cell size.
4382*/
4383const SUBDIVIDED_QUAD_GRANULARITY = 16;
4384/**
4385* How much a warp may foreshorten the image, as the ratio between the largest and the smallest
4386* homogeneous denominator over the quad, while staying purely projective. Ordinary oblique imagery
4387* stays well below it.
4388*/
4389const PROJECTIVE_FORESHORTENING = 4;
4390/**
4391* The foreshortening at which a warp is taken to be purely bilinear.
4392*
4393* The denominator vanishes on the warp's vanishing line, and that line touches the quad exactly when
4394* the quad degenerates into a triangle, so foreshortening grows without bound as a corner approaches
4395* the diagonal between its neighbours. Left alone, such a quad keeps a valid but wildly lopsided
4396* warp, squeezing nearly the whole image into a sliver along one edge and magnifying a handful of
4397* texels over the rest of the quad.
4398*/
4399const BILINEAR_FORESHORTENING = 512;
4400/**
4401* A data source containing an image.
4402* (See the [Style Specification](https://maplibre.org/maplibre-style-spec/#sources-image) for detailed documentation of options.)
4403*
4404* @group Sources
4405*
4406* @example
4407* ```ts
4408* // add to map
4409* map.addSource('some id', {
4410*    type: 'image',
4411*    url: 'https://www.maplibre.org/images/foo.png',
4412*    coordinates: [
4413*        [-76.54, 39.18],
4414*        [-76.52, 39.18],
4415*        [-76.52, 39.17],
4416*        [-76.54, 39.17]
4417*    ]
4418* });
4419*
4420* // update coordinates
4421* let mySource = map.getSource('some id');
4422* mySource.setCoordinates([
4423*     [-76.54335737228394, 39.18579907229748],
4424*     [-76.52803659439087, 39.1838364847587],
4425*     [-76.5295386314392, 39.17683392507606],
4426*     [-76.54520273208618, 39.17876344106642]
4427* ]);
4428*
4429* // update url and coordinates simultaneously
4430* mySource.updateImage({
4431*    url: 'https://www.maplibre.org/images/bar.png',
4432*    coordinates: [
4433*        [-76.54335737228394, 39.18579907229748],
4434*        [-76.52803659439087, 39.1838364847587],
4435*        [-76.5295386314392, 39.17683392507606],
4436*        [-76.54520273208618, 39.17876344106642]
4437*    ]
4438* })
4439*
4440* // update with an already-decoded image (no network request)
4441* const bitmap = await createImageBitmap(myCanvas);
4442* mySource.updateImage({image: bitmap});
4443*
4444* // create an empty source (no url), then feed it entirely via updateImage
4445* map.addSource('empty id', {
4446*    type: 'image',
4447*    coordinates: [
4448*        [-76.54, 39.18],
4449*        [-76.52, 39.18],
4450*        [-76.52, 39.17],
4451*        [-76.54, 39.17]
4452*    ]
4453* });
4454* (map.getSource('empty id') as ImageSource).updateImage({image: bitmap});
4455*
4456* map.removeSource('some id');  // remove
4457* ```
4458*/
4459var ImageSource = class extends Evented {
4460	/** @internal */
4461	constructor(id, options, dispatcher, eventedParent) {
4462		super();
4463		this.imageWarp = bilinearImageWarp;
4464		this.flippedWindingOrder = false;
4465		this._warp = "auto";
4466		this._imageDirty = false;
4467		this._subdividedQuad = false;
4468		this._subdividedMesh = null;
4469		this.id = id;
4470		this.dispatcher = dispatcher;
4471		this.coordinates = options.coordinates;
4472		this.type = "image";
4473		this.minzoom = 0;
4474		this.maxzoom = 22;
4475		this.tileSize = 512;
4476		this.tiles = {};
4477		this._loaded = false;
4478		this.setEventedParent(eventedParent);
4479		this.options = options;
4480	}
4481	async load(newCoordinates) {
4482		if (this.options.url === void 0) {
4483			this._loaded = true;
4484			this._finishLoading();
4485			return;
4486		}
4487		this._loaded = false;
4488		this.fire(new MapSourceDataEvent("dataloading"));
4489		this.url = this.options.url;
4490		this._abortController = new AbortController();
4491		try {
4492			const image = await ImageRequest.transformAndGetImage(this.map._requestManager, this.url, "Image", this._abortController);
4493			this._abortController = null;
4494			this._loaded = true;
4495			if (image?.data) {
4496				this._setImage(image.data);
4497				if (newCoordinates) this.coordinates = newCoordinates;
4498				this._finishLoading();
4499			} else this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Could not load image ${this.url}: the response is empty`)));
4500		} catch (err) {
4501			if (isAbortError(err)) return;
4502			this._abortController = null;
4503			this._loaded = true;
4504			this.fire(new ErrorEvent(ensureError(err)));
4505		}
4506	}
4507	loaded() {
4508		return this._loaded;
4509	}
4510	/**
4511	* Updates the image and, optionally, the coordinates. To avoid having the image flash after changing,
4512	* set the `raster-fade-duration` paint property on the raster layer to 0.
4513	*
4514	* Provide exactly one of `url` (to fetch a new image over the network) or `image` (an
4515	* already-decoded `HTMLImageElement`, `HTMLCanvasElement`, `ImageBitmap` or `ImageData` to
4516	* display directly, without a network request).
4517	*
4518	* @param options - The options object.
4519	*/
4520	updateImage(options) {
4521		if (this._abortController) {
4522			this._abortController.abort();
4523			this._abortController = null;
4524		}
4525		if ("image" in options) {
4526			this._loaded = true;
4527			this._setImage(options.image);
4528			if (options.coordinates) this.coordinates = options.coordinates;
4529			this._finishLoading();
4530			return this;
4531		}
4532		if (!options.url) return this;
4533		this.options.url = options.url;
4534		this.load(options.coordinates);
4535		return this;
4536	}
4537	/** Loaded tiles hold `this.texture`, so the wrapper has to outlive the images in it. */
4538	_setImage(image) {
4539		this.image = image;
4540		this._imageDirty = true;
4541	}
4542	/**
4543	* @internal
4544	* The mesh the image is drawn with, or null to use the tile mesh of the current projection.
4545	*
4546	* The raster vertex shader evaluates {@link imageWarp} per vertex, so a pair of triangles only
4547	* samples it at the four corners. That is enough for the affine mapping of a parallelogram, and
4548	* for a purely projective mapping, whose straight lines survive linear interpolation across the
4549	* diagonal. Anything in between is neither, and would be textured as two independently warped
4550	* halves with a visible seam along that diagonal, so it needs the warp evaluated per grid cell.
4551	*
4552	* A projection that subdivides its own tile meshes already does this, so it keeps them.
4553	*/
4554	getMesh(context, projectionSubdividesTiles) {
4555		if (!this._subdividedQuad || projectionSubdividesTiles) return null;
4556		this._subdividedMesh ??= createTileMeshWithBuffers(context, { granularity: SUBDIVIDED_QUAD_GRANULARITY });
4557		return this._subdividedMesh;
4558	}
4559	/** Teardown only: dropping the reference alone leaves the allocation to the GC. */
4560	_disposeTexture() {
4561		this.texture?.destroy();
4562		this.texture = null;
4563	}
4564	_finishLoading() {
4565		if (this.map) {
4566			this.setCoordinates(this.coordinates);
4567			this.fire(new MapSourceDataEvent("data", { sourceDataType: "metadata" }));
4568		}
4569	}
4570	onAdd(map) {
4571		this.map = map;
4572		this.load();
4573	}
4574	onRemove() {
4575		if (this._abortController) {
4576			this._abortController.abort();
4577			this._abortController = null;
4578		}
4579		this._disposeTexture();
4580		this._subdividedMesh?.destroy();
4581		this._subdividedMesh = null;
4582		this.image = null;
4583		this.tiles = {};
4584	}
4585	/**
4586	* @experimental
4587	* Sets how the image is warped onto its coordinates and re-renders the map.
4588	*
4589	* This only has an effect while the coordinates do not form a rectangle, and it is not part of
4590	* the style specification, so it does not survive `map.setStyle`.
4591	*
4592	* @param warp - The warp to use, see {@link ImageSourceWarp}.
4593	*
4594	* @example
4595	* ```ts
4596	* // Keep the image pinned to its corners while the user drags them around.
4597	* map.getSource('some id').setWarp('flat');
4598	* ```
4599	*/
4600	setWarp(warp) {
4601		if (this._warp === warp) return this;
4602		this._warp = warp;
4603		if (this.tileCoords) this.setCoordinates(this.coordinates);
4604		return this;
4605	}
4606	/**
4607	* @experimental
4608	* Returns how the image is warped onto its coordinates.
4609	*
4610	* @returns The warp in use, see {@link ImageSourceWarp}.
4611	*/
4612	getWarp() {
4613		return this._warp;
4614	}
4615	/**
4616	* Sets the image's coordinates and re-renders the map.
4617	*
4618	* @param coordinates - Four geographical coordinates,
4619	* represented as arrays of longitude and latitude numbers, which define the corners of the image.
4620	* The coordinates start at the top left corner of the image and proceed in clockwise order.
4621	* They do not have to represent a rectangle.
4622	*/
4623	setCoordinates(coordinates) {
4624		this.coordinates = coordinates;
4625		const cornerCoords = coordinates.map(MercatorCoordinate.fromLngLat);
4626		this.tileID = getCoordinatesCenterTileID(cornerCoords);
4627		this.terrainTileRanges = this._getOverlappingTileRanges(cornerCoords);
4628		this.minzoom = this.maxzoom = this.tileID.z;
4629		this.tileCoords = cornerCoords.map((coord) => this.tileID.getTilePoint(coord)._round());
4630		this.imageWarp = calculateImageWarp(this.tileCoords, this._warp);
4631		this._subdividedQuad = this.imageWarp[2] > 0 && !isParallelogram(this.tileCoords);
4632		this.flippedWindingOrder = hasWrongWindingOrder(this.tileCoords);
4633		this.fire(new MapSourceDataEvent("data", { sourceDataType: "content" }));
4634		return this;
4635	}
4636	prepare() {
4637		if (Object.keys(this.tiles).length === 0 || !this.image) return;
4638		const context = this.map.painter.context;
4639		const gl = context.gl;
4640		if (!this.texture) {
4641			this.texture = new Texture(context, this.image, gl.RGBA);
4642			this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
4643		} else if (this._imageDirty) {
4644			this.texture.update(this.image);
4645			this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
4646		}
4647		this._imageDirty = false;
4648		let newTilesLoaded = false;
4649		for (const w in this.tiles) {
4650			const tile = this.tiles[w];
4651			if (tile.state !== "loaded") {
4652				tile.state = "loaded";
4653				tile.texture = this.texture;
4654				newTilesLoaded = true;
4655			}
4656		}
4657		if (newTilesLoaded) this.fire(new MapSourceDataEvent("data", {
4658			sourceDataType: "idle",
4659			sourceId: this.id
4660		}));
4661	}
4662	async loadTile(tile) {
4663		if (this.tileID?.equals(tile.tileID.canonical)) {
4664			this.tiles[String(tile.tileID.wrap)] = tile;
4665			tile.buckets = {};
4666		} else tile.state = "errored";
4667	}
4668	serialize() {
4669		const spec = {
4670			type: "image",
4671			coordinates: this.coordinates
4672		};
4673		if (this.options.url !== void 0) spec.url = this.options.url;
4674		return spec;
4675	}
4676	hasTransition() {
4677		return false;
4678	}
4679	/**
4680	* Given a list of coordinates, determine overlapping tile ranges for all zoom levels.
4681	*
4682	* @returns Overlapping tile ranges for all zoom levels.
4683	* @internal
4684	*/
4685	_getOverlappingTileRanges(coords) {
4686		const { minX, minY, maxX, maxY } = Bounds.fromPoints(coords);
4687		const ranges = {};
4688		for (let z = 0; z <= 25; z++) {
4689			const tilesAtZoom = Math.pow(2, z);
4690			const minTileX = Math.floor(minX * tilesAtZoom);
4691			const minTileY = Math.floor(minY * tilesAtZoom);
4692			const maxTileX = Math.floor(maxX * tilesAtZoom);
4693			const maxTileY = Math.floor(maxY * tilesAtZoom);
4694			const minTileXWrapped = (minTileX % tilesAtZoom + tilesAtZoom) % tilesAtZoom;
4695			const maxTileXWrapped = maxTileX % tilesAtZoom;
4696			const minWrap = Math.floor(minTileX / tilesAtZoom);
4697			const maxWrap = Math.floor(maxTileX / tilesAtZoom);
4698			ranges[z] = {
4699				minWrap,
4700				maxWrap,
4701				minTileXWrapped,
4702				maxTileXWrapped,
4703				minTileY,
4704				maxTileY
4705			};
4706		}
4707		return ranges;
4708	}
4709};
4710/**
4711* Given a list of coordinates, get their center as a coordinate.
4712*
4713* @returns centerpoint
4714* @internal
4715*/
4716function getCoordinatesCenterTileID(coords) {
4717	const bounds = Bounds.fromPoints(coords);
4718	const dx = bounds.width();
4719	const dy = bounds.height();
4720	const zoom = Math.max(0, Math.floor(-Math.log(Math.max(dx, dy)) / Math.LN2));
4721	const tilesAtZoom = Math.pow(2, zoom);
4722	return new CanonicalTileID(zoom, Math.floor((bounds.minX + bounds.maxX) / 2 * tilesAtZoom), Math.floor((bounds.minY + bounds.maxY) / 2 * tilesAtZoom));
4723}
4724function hasWrongWindingOrder(coords) {
4725	const e0x = coords[1].x - coords[0].x;
4726	const e0y = coords[1].y - coords[0].y;
4727	const e1x = coords[2].x - coords[0].x;
4728	return e0x * (coords[2].y - coords[0].y) - e0y * e1x < 0;
4729}
4730/**
4731* How the image is warped onto its coordinates, given the corners in tile space and what the user
4732* asked for. The projective warp is the perspective view of a plane, so it is the one a photograph
4733* wants; the bilinear warp is a rubber sheet pinned at the corners, and is the only one left once
4734* the corners stop describing a perspective view at all.
4735*
4736* Based on Paul S. Heckbert, "Fundamentals of Texture Mapping and Image
4737* Warping", UCB/CSD-89-516, 1989, section 2.2.3 and appendix A.2.
4738*
4739* @see https://www2.eecs.berkeley.edu/Pubs/TechRpts/1989/5504.html
4740* @see https://www.cs.cmu.edu/~ph/texfund/texfund.pdf
4741*/
4742function calculateImageWarp(cornerCoords, warp) {
4743	if (warp === "flat" || isParallelogram(cornerCoords)) return bilinearImageWarp;
4744	const [topLeft, topRight, bottomRight, bottomLeft] = cornerCoords;
4745	const sumX = topLeft.x - topRight.x + bottomRight.x - bottomLeft.x;
4746	const sumY = topLeft.y - topRight.y + bottomRight.y - bottomLeft.y;
4747	const basis = [
4748		topRight.x - bottomRight.x,
4749		topRight.y - bottomRight.y,
4750		bottomLeft.x - bottomRight.x,
4751		bottomLeft.y - bottomRight.y
4752	];
4753	const [rightX, rightY, downX, downY] = basis;
4754	const determinant$1 = determinant(basis);
4755	const perspectiveX = (sumX * downY - downX * sumY) / determinant$1;
4756	const perspectiveY = (rightX * sumY - sumX * rightY) / determinant$1;
4757	const denominators = [
4758		1,
4759		1 + perspectiveX,
4760		1 + perspectiveX + perspectiveY,
4761		1 + perspectiveY
4762	];
4763	const foreshortening = Math.max(...denominators) / Math.min(...denominators);
4764	const blend = warp === "perspective" ? 0 : bilinearBlend(foreshortening);
4765	if (!(foreshortening >= 1 && foreshortening <= BILINEAR_FORESHORTENING) || blend >= 1) return bilinearImageWarp;
4766	return [
4767		perspectiveX,
4768		perspectiveY,
4769		blend
4770	];
4771}
4772/**
4773* How far a warp of the given foreshortening is blended towards the bilinear one, ramping from
4774* purely projective at {@link PROJECTIVE_FORESHORTENING} to purely bilinear at
4775* {@link BILINEAR_FORESHORTENING}. Blending rather than switching keeps the image continuous as the
4776* quad is reshaped: the two warps only agree for a parallelogram, and diverge by roughly a tenth of
4777* the quad per unit of foreshortening, so a switch would visibly displace the image.
4778*/
4779function bilinearBlend(foreshortening) {
4780	const ramp = (1 - PROJECTIVE_FORESHORTENING / foreshortening) / (1 - PROJECTIVE_FORESHORTENING / BILINEAR_FORESHORTENING);
4781	return Math.max(0, ramp);
4782}
4783function isParallelogram(cornerCoords) {
4784	const [topLeft, topRight, bottomRight, bottomLeft] = cornerCoords;
4785	return topLeft.x + bottomRight.x === topRight.x + bottomLeft.x && topLeft.y + bottomRight.y === topRight.y + bottomLeft.y;
4786}
4787//#endregion
4788//#region src/source/video_source.ts
4789/**
4790* A data source containing video.
4791* (See the [Style Specification](https://maplibre.org/maplibre-style-spec/#sources-video) for detailed documentation of options.)
4792*
4793* @group Sources
4794*
4795* @example
4796* ```ts
4797* // add to map
4798* map.addSource('some id', {
4799*    type: 'video',
4800*    url: [
4801*        'https://www.mapbox.com/blog/assets/baltimore-smoke.mp4',
4802*        'https://www.mapbox.com/blog/assets/baltimore-smoke.webm'
4803*    ],
4804*    coordinates: [
4805*        [-76.54, 39.18],
4806*        [-76.52, 39.18],
4807*        [-76.52, 39.17],
4808*        [-76.54, 39.17]
4809*    ]
4810* });
4811*
4812* // update
4813* let mySource = map.getSource('some id');
4814* mySource.setCoordinates([
4815*     [-76.54335737228394, 39.18579907229748],
4816*     [-76.52803659439087, 39.1838364847587],
4817*     [-76.5295386314392, 39.17683392507606],
4818*     [-76.54520273208618, 39.17876344106642]
4819* ]);
4820*
4821* map.removeSource('some id');  // remove
4822* ```
4823* @see [Add a video](https://maplibre.org/maplibre-gl-js/docs/examples/video-on-a-map/)
4824*
4825* Note that when rendered as a raster layer, the layer's `raster-fade-duration` property will cause the video to fade in.
4826* This happens when playback is started, paused and resumed, or when the video's coordinates are updated. To avoid this behavior,
4827* set the layer's `raster-fade-duration` property to `0`.
4828*/
4829var VideoSource = class extends ImageSource {
4830	constructor(id, options, dispatcher, eventedParent) {
4831		super(id, options, dispatcher, eventedParent);
4832		this._onPlayingHandler = () => {
4833			this.map?.triggerRepaint();
4834		};
4835		this.roundZoom = true;
4836		this.type = "video";
4837		this.options = options;
4838	}
4839	async load() {
4840		this._loaded = false;
4841		const options = this.options;
4842		this.urls = [];
4843		for (const url of options.urls) this.urls.push((await this.map._requestManager.transformRequest(url, "Source")).url);
4844		try {
4845			const video = await getVideo(this.urls);
4846			this._loaded = true;
4847			if (!video) return;
4848			this.video = video;
4849			this.video.loop = true;
4850			this.video.addEventListener("playing", this._onPlayingHandler);
4851			if (this.map) this.video.play();
4852			this._finishLoading();
4853		} catch (err) {
4854			this.fire(new ErrorEvent(ensureError(err)));
4855		}
4856	}
4857	/**
4858	* Pauses the video.
4859	*/
4860	pause() {
4861		if (this.video) this.video.pause();
4862	}
4863	/**
4864	* Plays the video.
4865	*/
4866	play() {
4867		if (this.video) this.video.play();
4868	}
4869	/**
4870	* Sets playback to a timestamp, in seconds.
4871	*/
4872	seek(seconds) {
4873		if (this.video) {
4874			const seekableRange = this.video.seekable;
4875			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.`)));
4876			else this.video.currentTime = seconds;
4877		}
4878	}
4879	/**
4880	* Returns the HTML `video` element.
4881	*
4882	* @returns The HTML `video` element.
4883	*/
4884	getVideo() {
4885		return this.video;
4886	}
4887	onAdd(map) {
4888		if (this.map) return;
4889		this.map = map;
4890		this.load();
4891		if (this.video) {
4892			this.video.play();
4893			this.setCoordinates(this.coordinates);
4894		}
4895	}
4896	onRemove() {
4897		super.onRemove();
4898		if (this.video) {
4899			this.video.removeEventListener("playing", this._onPlayingHandler);
4900			this.video.pause();
4901		}
4902	}
4903	/**
4904	* Sets the video's coordinates and re-renders the map.
4905	*/
4906	prepare() {
4907		if (Object.keys(this.tiles).length === 0 || this.video.readyState < 2) return;
4908		const context = this.map.painter.context;
4909		const gl = context.gl;
4910		if (!this.texture) {
4911			this.texture = new Texture(context, this.video, gl.RGBA);
4912			this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
4913		} else if (!this.video.paused) {
4914			this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
4915			gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.video);
4916		}
4917		let newTilesLoaded = false;
4918		for (const w in this.tiles) {
4919			const tile = this.tiles[w];
4920			if (tile.state !== "loaded") {
4921				tile.state = "loaded";
4922				tile.texture = this.texture;
4923				newTilesLoaded = true;
4924			}
4925		}
4926		if (newTilesLoaded) this.fire(new MapSourceDataEvent("data", {
4927			sourceDataType: "idle",
4928			sourceId: this.id
4929		}));
4930	}
4931	serialize() {
4932		return {
4933			type: "video",
4934			urls: this.urls,
4935			coordinates: this.coordinates
4936		};
4937	}
4938	hasTransition() {
4939		return this.video && !this.video.paused;
4940	}
4941};
4942//#endregion
4943//#region src/source/canvas_source.ts
4944/**
4945* A data source containing the contents of an HTML canvas. See {@link CanvasSourceSpecification} for detailed documentation of options.
4946*
4947* @group Sources
4948*
4949* @example
4950* ```ts
4951* // add to map
4952* map.addSource('some id', {
4953*    type: 'canvas',
4954*    canvas: 'idOfMyHTMLCanvas',
4955*    animate: true,
4956*    coordinates: [
4957*        [-76.54, 39.18],
4958*        [-76.52, 39.18],
4959*        [-76.52, 39.17],
4960*        [-76.54, 39.17]
4961*    ]
4962* });
4963*
4964* // update
4965* let mySource = map.getSource('some id');
4966* mySource.setCoordinates([
4967*     [-76.54335737228394, 39.18579907229748],
4968*     [-76.52803659439087, 39.1838364847587],
4969*     [-76.5295386314392, 39.17683392507606],
4970*     [-76.54520273208618, 39.17876344106642]
4971* ]);
4972*
4973* map.removeSource('some id');  // remove
4974* ```
4975*/
4976var CanvasSource = class extends ImageSource {
4977	/** @internal */
4978	constructor(id, options, dispatcher, eventedParent) {
4979		super(id, options, dispatcher, eventedParent);
4980		if (!options.coordinates) this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, "missing required property \"coordinates\"")));
4981		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")));
4982		if (options.animate && typeof options.animate !== "boolean") this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, "optional \"animate\" property must be a boolean value")));
4983		if (!options.canvas) this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, "missing required property \"canvas\"")));
4984		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")));
4985		this.options = options;
4986		this.animate = options.animate !== void 0 ? options.animate : true;
4987	}
4988	async load() {
4989		this._loaded = true;
4990		this.canvas ||= this.options.canvas instanceof HTMLCanvasElement ? this.options.canvas : document.getElementById(this.options.canvas);
4991		this.width = this.canvas.width;
4992		this.height = this.canvas.height;
4993		if (this._hasInvalidDimensions()) {
4994			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("Canvas dimensions cannot be less than or equal to zero.")));
4995			return;
4996		}
4997		this.play = function() {
4998			this._playing = true;
4999			this.map.triggerRepaint();
5000		};
5001		this.pause = function() {
5002			if (this._playing) {
5003				this.prepare();
5004				this._playing = false;
5005			}
5006		};
5007		this._finishLoading();
5008	}
5009	/**
5010	* Returns the HTML `canvas` element.
5011	*
5012	* @returns The HTML `canvas` element.
5013	*/
5014	getCanvas() {
5015		return this.canvas;
5016	}
5017	onAdd(map) {
5018		this.map = map;
5019		this.load();
5020		if (this.canvas) {
5021			if (this.animate) this.play();
5022		}
5023	}
5024	onRemove() {
5025		this._playing = false;
5026		super.onRemove();
5027	}
5028	prepare() {
5029		let resize = false;
5030		if (this.canvas.width !== this.width) {
5031			this.width = this.canvas.width;
5032			resize = true;
5033		}
5034		if (this.canvas.height !== this.height) {
5035			this.height = this.canvas.height;
5036			resize = true;
5037		}
5038		if (this._hasInvalidDimensions()) return;
5039		if (Object.keys(this.tiles).length === 0) return;
5040		const context = this.map.painter.context;
5041		const gl = context.gl;
5042		if (!this.texture) {
5043			this.texture = new Texture(context, this.canvas, gl.RGBA, { premultiply: true });
5044			this.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
5045		} else if (resize || this._playing) this.texture.update(this.canvas, { premultiply: true });
5046		let newTilesLoaded = false;
5047		for (const w in this.tiles) {
5048			const tile = this.tiles[w];
5049			if (tile.state !== "loaded") {
5050				tile.state = "loaded";
5051				tile.texture = this.texture;
5052				newTilesLoaded = true;
5053			}
5054		}
5055		if (newTilesLoaded) this.fire(new MapSourceDataEvent("data", {
5056			sourceDataType: "idle",
5057			sourceId: this.id
5058		}));
5059	}
5060	serialize() {
5061		return {
5062			type: "canvas",
5063			animate: this.animate,
5064			canvas: this.options.canvas,
5065			coordinates: this.coordinates
5066		};
5067	}
5068	hasTransition() {
5069		return this._playing;
5070	}
5071	_hasInvalidDimensions() {
5072		for (const x of [this.canvas.width, this.canvas.height]) if (isNaN(x) || x <= 0) return true;
5073		return false;
5074	}
5075};
5076//#endregion
5077//#region src/source/source.ts
5078const registeredSources = {};
5079/**
5080* Creates a tiled data source instance given an options object.
5081*
5082* @param id - The id for the source. Must not be used by any existing source.
5083* @param specification - Source options, specific to the source type (except for `options.type`, which is always required).
5084* @param source - A source definition object compliant with
5085* [`maplibre-gl-style-spec`](https://maplibre.org/maplibre-style-spec/#sources) or, for a third-party source type,
5086* with that type's requirements.
5087* @param dispatcher - A {@link Dispatcher} instance, which can be used to send messages to the workers.
5088* @returns a newly created source
5089*/
5090const create = (id, specification, dispatcher, eventedParent) => {
5091	const source = new (getSourceType(specification.type))(id, specification, dispatcher, eventedParent);
5092	if (source.id !== id) throw new Error(`Expected Source id to be ${id} instead of ${source.id}`);
5093	return source;
5094};
5095const getSourceType = (name) => {
5096	switch (name) {
5097		case "geojson": return GeoJSONSource;
5098		case "image": return ImageSource;
5099		case "raster": return RasterTileSource;
5100		case "raster-dem": return RasterDEMTileSource;
5101		case "vector": return VectorTileSource;
5102		case "video": return VideoSource;
5103		case "canvas": return CanvasSource;
5104	}
5105	return registeredSources[name];
5106};
5107const setSourceType = (name, type) => {
5108	registeredSources[name] = type;
5109};
5110/**
5111* Adds a custom source type, making it available for use with {@link Map.addSource}.
5112* @param name - The name of the source type; source definition objects use this name in the `{type: ...}` field.
5113* @param SourceType - A {@link SourceClass} - which is a constructor for the `Source` interface.
5114* @returns a promise that is resolved when the source type is ready or rejected with an error.
5115*/
5116const addSourceType = async (name, SourceType) => {
5117	if (getSourceType(name)) throw new Error(`A source type called "${name}" already exists.`);
5118	setSourceType(name, SourceType);
5119};
5120//#endregion
5121//#region src/data/bucket.ts
5122function deserialize(input, style) {
5123	const output = {};
5124	if (!style) return output;
5125	for (const bucket of input) {
5126		const layers = bucket.layerIds.map((id) => style.getLayer(id)).filter(Boolean);
5127		if (layers.length === 0) continue;
5128		bucket.layers = layers;
5129		if (bucket.stateDependentLayerIds) bucket.stateDependentLayers = bucket.stateDependentLayerIds.map((lId) => layers.filter((l) => l.id === lId)[0]);
5130		for (const layer of layers) output[layer.id] = bucket;
5131	}
5132	return output;
5133}
5134//#endregion
5135//#region src/source/rtl_text_plugin_status.ts
5136/**
5137* Fired once a plugin set through {@link setRTLTextPlugin} has loaded, so that tiles drawn before it
5138* arrived can be drawn again.
5139*
5140* @deprecated Right-to-left text is drawn without a plugin, so nothing has to be waited for.
5141*/
5142const RTLPluginLoadedEventName = "RTLPluginLoaded";
5143//#endregion
5144//#region src/source/rtl_text_plugin_main_thread.ts
5145var RTLMainThreadPlugin = class extends Evented {
5146	constructor(..._args) {
5147		super(..._args);
5148		this.status = "unavailable";
5149		this.url = null;
5150		this.dispatcher = getGlobalDispatcher();
5151	}
5152	/** Sync RTL plugin state by broadcasting a message to the worker */
5153	_syncState(statusToSend) {
5154		this.status = statusToSend;
5155		return this.dispatcher.broadcast("SRPS", {
5156			pluginStatus: statusToSend,
5157			pluginURL: this.url
5158		}).catch((e) => {
5159			this.status = "error";
5160			throw e;
5161		});
5162	}
5163	/** This one is exposed to outside */
5164	getRTLTextPluginStatus() {
5165		return this.status;
5166	}
5167	clearRTLTextPlugin() {
5168		this.status = "unavailable";
5169		this.url = null;
5170	}
5171	async setRTLTextPlugin(url, deferred = false) {
5172		if (this.url) throw new Error("setRTLTextPlugin cannot be called multiple times.");
5173		this.url = browser.resolveURL(url);
5174		if (!this.url) throw new Error(`requested url ${url} is invalid`);
5175		if (this.status === "unavailable") {
5176			if (deferred) {
5177				this.status = "deferred";
5178				this._syncState(this.status);
5179			} else return this._requestImport();
5180		} else if (this.status === "requested") return this._requestImport();
5181	}
5182	/** Send a message to worker which will import the RTL plugin script */
5183	async _requestImport() {
5184		await this._syncState("loading");
5185		this.status = "loaded";
5186		this.fire(new Event(RTLPluginLoadedEventName));
5187	}
5188	/** Start a lazy loading process of RTL plugin */
5189	lazyLoad() {
5190		if (this.status === "unavailable") this.status = "requested";
5191		else if (this.status === "deferred") this._requestImport();
5192	}
5193};
5194let rtlMainThreadPlugin = null;
5195function rtlMainThreadPluginFactory() {
5196	rtlMainThreadPlugin ||= new RTLMainThreadPlugin();
5197	return rtlMainThreadPlugin;
5198}
5199//#endregion
5200//#region src/tile/tile.ts
5201const CLOCK_SKEW_RETRY_TIMEOUT = 3e4;
5202/**
5203* A tile object is the combination of a Coordinate, which defines
5204* its place, as well as a unique ID and data tracking for its content
5205*/
5206var Tile = class {
5207	/**
5208	* @param tileID - the tile ID
5209	* @param size - The tile size
5210	*/
5211	constructor(tileID, size) {
5212		this.timeAdded = 0;
5213		this.fadeEndTime = 0;
5214		this.fadeOpacity = 1;
5215		this.tileID = tileID;
5216		this.uid = uniqueId();
5217		this.uses = 0;
5218		this.tileSize = size;
5219		this.buckets = {};
5220		this.expirationTime = null;
5221		this.queryPadding = 0;
5222		this.hasSymbolBuckets = false;
5223		this.hasRTLText = false;
5224		this.dependencies = {};
5225		this.rttObjects = [];
5226		this.rttFingerprint = {};
5227		this.expiredRequestCount = 0;
5228		this.state = "loading";
5229		this.featureStateRevision = -1;
5230	}
5231	/**
5232	* Incoming and self-fading raster tiles must remain renderable at zero opacity because
5233	* drawing advances their opacity. Only transparent departing tiles have finished fading.
5234	*/
5235	isRenderable(symbolLayer) {
5236		return this.hasData() && (!this.fadeEndTime || this.fadingDirection !== 0 || this.fadeOpacity > 0) && (symbolLayer || !this.holdingForSymbolFade());
5237	}
5238	/**
5239	* @internal
5240	* Many-to-one crossfade between a base tile and parent/ancestor tile (when zooming)
5241	*/
5242	setCrossFadeLogic({ fadingRole, fadingDirection, fadingParentID, fadeEndTime }) {
5243		this.resetFadeLogic();
5244		this.fadingRole = fadingRole;
5245		this.fadingDirection = fadingDirection;
5246		this.fadingParentID = fadingParentID;
5247		this.fadeEndTime = fadeEndTime;
5248	}
5249	/**
5250	* Self fading for edge tiles (when panning map)
5251	*/
5252	setSelfFadeLogic(fadeEndTime) {
5253		this.resetFadeLogic();
5254		this.selfFading = true;
5255		this.fadeEndTime = fadeEndTime;
5256	}
5257	resetFadeLogic() {
5258		this.fadingRole = null;
5259		this.fadingDirection = null;
5260		this.fadingParentID = null;
5261		this.selfFading = false;
5262		this.timeAdded = now();
5263		this.fadeEndTime = 0;
5264		this.fadeOpacity = 1;
5265	}
5266	wasRequested() {
5267		return this.state === "errored" || this.state === "loaded" || this.state === "reloading";
5268	}
5269	clearTextures(painter) {
5270		if (this.demTexture) painter.saveTileTexture(this.demTexture);
5271		this.demTexture = null;
5272	}
5273	/**
5274	* @internal
5275	* Returns the cached RTT object for this stack, or undefined on a cache miss.
5276	*/
5277	getRTT(stack) {
5278		return this.rttObjects[stack];
5279	}
5280	/**
5281	* @internal
5282	* Allocates a fresh RTT object from the painter's pool and stores it at the
5283	* given stack slot. Callers should check {@link getRTT} first; calling this
5284	* over an existing slot leaks the previous object.
5285	*/
5286	acquireRTT(painter, stack, size) {
5287		return this.rttObjects[stack] = painter.acquireRTT(size);
5288	}
5289	/**
5290	* @internal
5291	* Returns all cached RTT slots to the painter's pool.
5292	*/
5293	releaseRTT(painter) {
5294		if (this.rttObjects.length === 0) return;
5295		for (const obj of this.rttObjects) if (obj) painter.releaseRTT(obj);
5296		this.rttObjects.length = 0;
5297	}
5298	/**
5299	* Given a data object with a 'buffers' property, load it into
5300	* this tile's elementGroups and buffers properties and set loaded
5301	* to true. If the data is null, like in the case of an empty
5302	* GeoJSON tile, no-op but still set loaded to true.
5303	* @param data - The data from the worker
5304	* @param painter - the painter
5305	* @param justReloaded - `true` to just reload
5306	*/
5307	loadVectorData(data, painter, justReloaded) {
5308		if (data?.etagUnmodified === true) {
5309			this.state = "loaded";
5310			return;
5311		}
5312		if (this.hasData()) this.unloadVectorData();
5313		this.state = "loaded";
5314		if (!data) {
5315			this.collisionBoxArray = new CollisionBoxArray();
5316			return;
5317		}
5318		if (data.featureIndex) {
5319			this.latestFeatureIndex = data.featureIndex;
5320			if (data.rawTileData) {
5321				this.latestRawTileData = data.rawTileData;
5322				this.latestEncoding = data.encoding;
5323				this.latestFeatureIndex.rawTileData = data.rawTileData;
5324				this.latestFeatureIndex.encoding = data.encoding;
5325			} else if (this.latestRawTileData) {
5326				this.latestFeatureIndex.rawTileData = this.latestRawTileData;
5327				this.latestFeatureIndex.encoding = this.latestEncoding;
5328			}
5329		}
5330		this.collisionBoxArray = data.collisionBoxArray;
5331		this.buckets = deserialize(data.buckets, painter?.style);
5332		this.hasSymbolBuckets = false;
5333		for (const id in this.buckets) {
5334			const bucket = this.buckets[id];
5335			if (bucket instanceof SymbolBucket) {
5336				this.hasSymbolBuckets = true;
5337				if (justReloaded) bucket.justReloaded = true;
5338				else break;
5339			}
5340		}
5341		this.hasRTLText = false;
5342		if (this.hasSymbolBuckets) for (const id in this.buckets) {
5343			const bucket = this.buckets[id];
5344			if (bucket instanceof SymbolBucket) {
5345				if (bucket.hasRTLText) {
5346					this.hasRTLText = true;
5347					rtlMainThreadPluginFactory().lazyLoad();
5348					break;
5349				}
5350			}
5351		}
5352		this.queryPadding = 0;
5353		for (const id in this.buckets) {
5354			const bucket = this.buckets[id];
5355			this.queryPadding = Math.max(this.queryPadding, painter.style.getLayer(id).queryRadius(bucket));
5356		}
5357		if (data.imageAtlas) this.imageAtlas = data.imageAtlas;
5358		if (data.glyphAtlasImage) this.glyphAtlasImage = data.glyphAtlasImage;
5359		this.dashPositions = data.dashPositions;
5360	}
5361	/**
5362	* Release any data or WebGL resources referenced by this tile.
5363	*/
5364	unloadVectorData() {
5365		for (const id in this.buckets) this.buckets[id].destroy();
5366		this.buckets = {};
5367		if (this.imageAtlasTexture) this.imageAtlasTexture.destroy();
5368		if (this.glyphAtlasTexture) this.glyphAtlasTexture.destroy();
5369		this.imageAtlas = null;
5370		this.dashPositions = null;
5371		this.latestFeatureIndex = null;
5372		this.state = "unloaded";
5373	}
5374	getBucket(layer) {
5375		return this.buckets[layer.id];
5376	}
5377	upload(context) {
5378		for (const id in this.buckets) {
5379			const bucket = this.buckets[id];
5380			if (bucket.uploadPending()) bucket.upload(context);
5381		}
5382		const gl = context.gl;
5383		if (this.imageAtlas && !this.imageAtlas.uploaded) {
5384			this.imageAtlasTexture = new Texture(context, this.imageAtlas.image, gl.RGBA);
5385			this.imageAtlas.uploaded = true;
5386		}
5387		if (this.glyphAtlasImage) {
5388			this.glyphAtlasTexture = new Texture(context, this.glyphAtlasImage, gl.ALPHA);
5389			this.glyphAtlasImage = null;
5390		}
5391	}
5392	prepare(imageManager) {
5393		if (this.imageAtlas) this.imageAtlas.patchUpdatedImages(imageManager, this.imageAtlasTexture);
5394	}
5395	queryRenderedFeatures(layers, serializedLayers, sourceFeatureState, queryGeometry, cameraQueryGeometry, scale, params, transform, maxPitchScaleFactor, pixelPosMatrix, getElevation) {
5396		if (!this.latestFeatureIndex?.rawTileData) return {};
5397		return this.latestFeatureIndex.query({
5398			queryGeometry,
5399			cameraQueryGeometry,
5400			scale,
5401			tileSize: this.tileSize,
5402			pixelPosMatrix,
5403			transform,
5404			params,
5405			queryPadding: this.queryPadding * maxPitchScaleFactor,
5406			getElevation
5407		}, layers, serializedLayers, sourceFeatureState);
5408	}
5409	querySourceFeatures(result, params) {
5410		const featureIndex = this.latestFeatureIndex;
5411		if (!featureIndex?.rawTileData) return;
5412		const vtLayers = featureIndex.loadVTLayers();
5413		const sourceLayer = params?.sourceLayer ? params.sourceLayer : "";
5414		const layer = vtLayers["_geojsonTileLayer"] || vtLayers[sourceLayer];
5415		if (!layer) return;
5416		const filter = featureFilter(params?.filter, `querySourceFeatures[${sourceLayer}].filter`, params?.globalState);
5417		const { z, x, y } = this.tileID.canonical;
5418		const coord = {
5419			z,
5420			x,
5421			y
5422		};
5423		for (let i = 0; i < layer.length; i++) {
5424			const feature = layer.feature(i);
5425			if (filter.needGeometry) {
5426				const evaluationFeature = toEvaluationFeature(feature, true);
5427				if (!filter.filter(new EvaluationParameters(this.tileID.overscaledZ), evaluationFeature, this.tileID.canonical)) continue;
5428			} else if (!filter.filter(new EvaluationParameters(this.tileID.overscaledZ), feature)) continue;
5429			const id = featureIndex.getId(feature, sourceLayer);
5430			const geojsonFeature = new GeoJSONFeature(feature, z, x, y, id);
5431			geojsonFeature.tile = coord;
5432			result.push(geojsonFeature);
5433		}
5434	}
5435	hasData() {
5436		return this.state === "loaded" || this.state === "reloading" || this.state === "expired";
5437	}
5438	patternsLoaded() {
5439		return this.imageAtlas && !!Object.keys(this.imageAtlas.patternPositions).length;
5440	}
5441	setExpiryData(data) {
5442		const prior = this.expirationTime;
5443		if (data.cacheControl) {
5444			const parsedCC = parseCacheControl(data.cacheControl);
5445			if (parsedCC["max-age"]) this.expirationTime = Date.now() + parsedCC["max-age"] * 1e3;
5446		} else if (data.expires) this.expirationTime = new Date(data.expires).getTime();
5447		if (this.expirationTime) {
5448			const now = Date.now();
5449			let isExpired = false;
5450			if (this.expirationTime > now) isExpired = false;
5451			else if (!prior) isExpired = true;
5452			else if (this.expirationTime < prior) isExpired = true;
5453			else {
5454				const delta = this.expirationTime - prior;
5455				if (!delta) isExpired = true;
5456				else this.expirationTime = now + Math.max(delta, CLOCK_SKEW_RETRY_TIMEOUT);
5457			}
5458			if (isExpired) {
5459				this.expiredRequestCount++;
5460				this.state = "expired";
5461			} else this.expiredRequestCount = 0;
5462		}
5463	}
5464	getExpiryTimeout() {
5465		if (this.expirationTime) {
5466			if (this.expiredRequestCount) return 1e3 * (1 << Math.min(this.expiredRequestCount - 1, 31));
5467			else return Math.min(this.expirationTime - (/* @__PURE__ */ new Date()).getTime(), Math.pow(2, 31) - 1);
5468		}
5469	}
5470	setFeatureState(states, painter, revision) {
5471		if (!this.latestFeatureIndex?.rawTileData || Object.keys(states).length === 0) return;
5472		if (this.featureStateRevision === revision) return;
5473		this.featureStateRevision = revision;
5474		const vtLayers = this.latestFeatureIndex.loadVTLayers();
5475		for (const id in this.buckets) {
5476			if (!painter.style.hasLayer(id)) continue;
5477			const bucket = this.buckets[id];
5478			const sourceLayerId = bucket.layers[0]["sourceLayer"] || "_geojsonTileLayer";
5479			const sourceLayer = vtLayers[sourceLayerId];
5480			const sourceLayerStates = states[sourceLayerId];
5481			if (!sourceLayer || !sourceLayerStates || sourceLayerStates.length === 0) continue;
5482			bucket.update(sourceLayerStates, sourceLayer, this.imageAtlas?.patternPositions || {}, this.dashPositions || {});
5483			const layer = painter?.style?.getLayer(id);
5484			if (layer) this.queryPadding = Math.max(this.queryPadding, layer.queryRadius(bucket));
5485		}
5486	}
5487	holdingForSymbolFade() {
5488		return this.symbolFadeHoldUntil !== void 0;
5489	}
5490	symbolFadeFinished() {
5491		return !this.symbolFadeHoldUntil || this.symbolFadeHoldUntil < now();
5492	}
5493	clearSymbolFadeHold() {
5494		this.symbolFadeHoldUntil = void 0;
5495	}
5496	setSymbolHoldDuration(duration) {
5497		this.symbolFadeHoldUntil = now() + duration;
5498	}
5499	setDependencies(namespace, dependencies) {
5500		const index = {};
5501		for (const dep of dependencies) index[dep] = true;
5502		this.dependencies[namespace] = index;
5503	}
5504	hasDependency(namespaces, keys) {
5505		for (const namespace of namespaces) {
5506			const dependencies = this.dependencies[namespace];
5507			if (dependencies) {
5508				for (const key of keys) if (dependencies[key]) return true;
5509			}
5510		}
5511		return false;
5512	}
5513};
5514//#endregion
5515//#region src/source/source_state.ts
5516function featureStatesMapToArray(map) {
5517	const result = [];
5518	for (const id in map) result.push({
5519		id,
5520		state: map[id]
5521	});
5522	return result;
5523}
5524/**
5525* @internal
5526* SourceFeatureState manages the state and pending changes
5527* to features in a source, separated by source layer.
5528* stateChanges and deletedStates batch all changes to the tile (updates and removes, respectively)
5529* between coalesce() events. addFeatureState() and removeFeatureState() also update their counterpart's
5530* list of changes, such that coalesce() can apply the proper state changes while agnostic to the order of operations.
5531* In deletedStates, all null's denote complete removal of state at that scope
5532*/
5533var SourceFeatureState = class {
5534	constructor() {
5535		this.state = {};
5536		this.stateChanges = {};
5537		this.deletedStates = {};
5538		this.revision = 0;
5539	}
5540	updateState(sourceLayer, featureId, newState) {
5541		const feature = String(featureId);
5542		this.stateChanges[sourceLayer] ||= {};
5543		this.stateChanges[sourceLayer][feature] ||= {};
5544		extend(this.stateChanges[sourceLayer][feature], newState);
5545		if (this.deletedStates[sourceLayer] === null) {
5546			this.deletedStates[sourceLayer] = {};
5547			for (const ft in this.state[sourceLayer]) if (ft === feature) {
5548				this.deletedStates[sourceLayer][feature] = {};
5549				for (const prop in this.state[sourceLayer][ft]) if (newState[prop] === void 0) this.deletedStates[sourceLayer][feature][prop] = null;
5550			} else this.deletedStates[sourceLayer][ft] = null;
5551		} else if (this.deletedStates[sourceLayer]?.[feature] === null) {
5552			this.deletedStates[sourceLayer][feature] = {};
5553			for (const prop in this.state[sourceLayer][feature]) if (newState[prop] === void 0) this.deletedStates[sourceLayer][feature][prop] = null;
5554		} else for (const key in newState) if (this.deletedStates[sourceLayer]?.[feature]?.[key] === null) delete this.deletedStates[sourceLayer][feature][key];
5555	}
5556	removeFeatureState(sourceLayer, featureId, key) {
5557		if (this.deletedStates[sourceLayer] === null) return;
5558		const feature = String(featureId);
5559		this.deletedStates[sourceLayer] ||= {};
5560		if (key && featureId !== void 0) {
5561			if (this.deletedStates[sourceLayer][feature] !== null) {
5562				this.deletedStates[sourceLayer][feature] ||= {};
5563				this.deletedStates[sourceLayer][feature][key] = null;
5564			}
5565		} else if (featureId !== void 0) {
5566			if (this.stateChanges[sourceLayer]?.[feature]) {
5567				this.deletedStates[sourceLayer][feature] = {};
5568				for (key in this.stateChanges[sourceLayer][feature]) this.deletedStates[sourceLayer][feature][key] = null;
5569			} else this.deletedStates[sourceLayer][feature] = null;
5570		} else this.deletedStates[sourceLayer] = null;
5571	}
5572	getState(sourceLayer, featureId) {
5573		const feature = String(featureId);
5574		const base = this.state[sourceLayer] || {};
5575		const changes = this.stateChanges[sourceLayer] || {};
5576		const reconciledState = extend({}, base[feature], changes[feature]);
5577		if (this.deletedStates[sourceLayer] === null) return {};
5578		else if (this.deletedStates[sourceLayer]) {
5579			const featureDeletions = this.deletedStates[sourceLayer][featureId];
5580			if (featureDeletions === null) return {};
5581			for (const prop in featureDeletions) delete reconciledState[prop];
5582		}
5583		return reconciledState;
5584	}
5585	initializeTileState(tile, painter) {
5586		const layerStates = {};
5587		for (const sourceLayer in this.state) layerStates[sourceLayer] = featureStatesMapToArray(this.state[sourceLayer]);
5588		tile.setFeatureState(layerStates, painter, this.revision);
5589	}
5590	coalesceChanges(inViewTiles, painter) {
5591		const featuresChangedMap = {};
5592		for (const sourceLayer in this.stateChanges) {
5593			this.state[sourceLayer] ||= {};
5594			featuresChangedMap[sourceLayer] ||= {};
5595			for (const feature in this.stateChanges[sourceLayer]) {
5596				this.state[sourceLayer][feature] ||= {};
5597				extend(this.state[sourceLayer][feature], this.stateChanges[sourceLayer][feature]);
5598				featuresChangedMap[sourceLayer][feature] = this.state[sourceLayer][feature];
5599			}
5600		}
5601		for (const sourceLayer in this.deletedStates) {
5602			this.state[sourceLayer] ||= {};
5603			featuresChangedMap[sourceLayer] ||= {};
5604			if (this.deletedStates[sourceLayer] === null) for (const ft in this.state[sourceLayer]) {
5605				this.state[sourceLayer][ft] = {};
5606				featuresChangedMap[sourceLayer][ft] = {};
5607			}
5608			else for (const feature in this.deletedStates[sourceLayer]) {
5609				if (this.deletedStates[sourceLayer][feature] === null) this.state[sourceLayer][feature] = {};
5610				else for (const key of Object.keys(this.deletedStates[sourceLayer][feature])) delete this.state[sourceLayer][feature][key];
5611				featuresChangedMap[sourceLayer][feature] = this.state[sourceLayer][feature];
5612			}
5613		}
5614		this.stateChanges = {};
5615		this.deletedStates = {};
5616		if (Object.keys(featuresChangedMap).length === 0) return;
5617		this.revision++;
5618		const featuresChanged = {};
5619		for (const sourceLayer in featuresChangedMap) featuresChanged[sourceLayer] = featureStatesMapToArray(featuresChangedMap[sourceLayer]);
5620		inViewTiles.setFeatureState(featuresChanged, painter, this.revision);
5621	}
5622};
5623//#endregion
5624//#region src/util/primitives/aabb.ts
5625var Aabb = class Aabb {
5626	constructor(min_, max_) {
5627		this.min = min_;
5628		this.max = max_;
5629		this.center = scale$1([], add([], this.min, this.max), .5);
5630	}
5631	quadrant(index) {
5632		const split = [index % 2 === 0, index < 2];
5633		const qMin = clone(this.min);
5634		const qMax = clone(this.max);
5635		for (let axis = 0; axis < split.length; axis++) {
5636			qMin[axis] = split[axis] ? this.min[axis] : this.center[axis];
5637			qMax[axis] = split[axis] ? this.center[axis] : this.max[axis];
5638		}
5639		qMax[2] = this.max[2];
5640		return new Aabb(qMin, qMax);
5641	}
5642	distanceX(point) {
5643		return Math.max(Math.min(this.max[0], point[0]), this.min[0]) - point[0];
5644	}
5645	distanceY(point) {
5646		return Math.max(Math.min(this.max[1], point[1]), this.min[1]) - point[1];
5647	}
5648	/**
5649	* Performs a frustum-aabb intersection test.
5650	*/
5651	intersectsFrustum(frustum) {
5652		let fullyInside = true;
5653		for (const plane of frustum.planes) {
5654			const planeIntersection = this.intersectsPlane(plane);
5655			if (planeIntersection === 0) return 0;
5656			if (planeIntersection === 1) fullyInside = false;
5657		}
5658		if (fullyInside) return 2;
5659		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;
5660		return 1;
5661	}
5662	/**
5663	* Performs a halfspace-aabb intersection test.
5664	*/
5665	intersectsPlane(plane) {
5666		let distMin = plane[3];
5667		let distMax = plane[3];
5668		for (let i = 0; i < 3; i++) if (plane[i] > 0) {
5669			distMin += plane[i] * this.min[i];
5670			distMax += plane[i] * this.max[i];
5671		} else {
5672			distMax += plane[i] * this.min[i];
5673			distMin += plane[i] * this.max[i];
5674		}
5675		if (distMin >= 0) return 2;
5676		if (distMax < 0) return 0;
5677		return 1;
5678	}
5679};
5680//#endregion
5681//#region src/util/primitives/frustum.ts
5682var Frustum = class Frustum {
5683	constructor(points, planes, aabb) {
5684		this.points = points;
5685		this.planes = planes;
5686		this.aabb = aabb;
5687	}
5688	static fromInvProjectionMatrix(invProj, worldSize = 1, zoom = 0, horizonPlane, flippedNearFar) {
5689		const clipSpaceCorners = [
5690			[
5691				-1,
5692				1,
5693				-1,
5694				1
5695			],
5696			[
5697				1,
5698				1,
5699				-1,
5700				1
5701			],
5702			[
5703				1,
5704				-1,
5705				-1,
5706				1
5707			],
5708			[
5709				-1,
5710				-1,
5711				-1,
5712				1
5713			],
5714			[
5715				-1,
5716				1,
5717				1,
5718				1
5719			],
5720			[
5721				1,
5722				1,
5723				1,
5724				1
5725			],
5726			[
5727				1,
5728				-1,
5729				1,
5730				1
5731			],
5732			[
5733				-1,
5734				-1,
5735				1,
5736				1
5737			]
5738		];
5739		const frustumPlanePointIndices = flippedNearFar ? [
5740			[
5741				6,
5742				5,
5743				4
5744			],
5745			[
5746				0,
5747				1,
5748				2
5749			],
5750			[
5751				0,
5752				3,
5753				7
5754			],
5755			[
5756				2,
5757				1,
5758				5
5759			],
5760			[
5761				3,
5762				2,
5763				6
5764			],
5765			[
5766				0,
5767				4,
5768				5
5769			]
5770		] : [
5771			[
5772				0,
5773				1,
5774				2
5775			],
5776			[
5777				6,
5778				5,
5779				4
5780			],
5781			[
5782				0,
5783				3,
5784				7
5785			],
5786			[
5787				2,
5788				1,
5789				5
5790			],
5791			[
5792				3,
5793				2,
5794				6
5795			],
5796			[
5797				0,
5798				4,
5799				5
5800			]
5801		];
5802		const scale = Math.pow(2, zoom);
5803		const frustumCoords = clipSpaceCorners.map((v) => unprojectClipSpacePoint(v, invProj, worldSize, scale));
5804		if (horizonPlane) adjustFarPlaneByHorizonPlane(frustumCoords, frustumPlanePointIndices[0], horizonPlane, flippedNearFar);
5805		const frustumPlanes = frustumPlanePointIndices.map((p) => {
5806			const a = sub([], frustumCoords[p[0]], frustumCoords[p[1]]);
5807			const b = sub([], frustumCoords[p[2]], frustumCoords[p[1]]);
5808			const n = normalize([], cross([], a, b));
5809			const d = -dot(n, frustumCoords[p[1]]);
5810			return n.concat(d);
5811		});
5812		const min = [
5813			Number.POSITIVE_INFINITY,
5814			Number.POSITIVE_INFINITY,
5815			Number.POSITIVE_INFINITY
5816		];
5817		const max = [
5818			Number.NEGATIVE_INFINITY,
5819			Number.NEGATIVE_INFINITY,
5820			Number.NEGATIVE_INFINITY
5821		];
5822		for (const p of frustumCoords) for (let i = 0; i < 3; i++) {
5823			min[i] = Math.min(min[i], p[i]);
5824			max[i] = Math.max(max[i], p[i]);
5825		}
5826		return new Frustum(frustumCoords, frustumPlanes, new Aabb(min, max));
5827	}
5828};
5829function unprojectClipSpacePoint(point, invProj, worldSize, scale) {
5830	const v = transformMat4([], point, invProj);
5831	const s = 1 / v[3] / worldSize * scale;
5832	return mul(v, v, [
5833		s,
5834		s,
5835		1 / v[3],
5836		s
5837	]);
5838}
5839/**
5840* Modifies points in the supplied `frustumCoords` array so that the frustum's far plane only lies as far as the horizon,
5841* which improves frustum culling effectiveness.
5842* @param frustumCoords - Points of the frustum.
5843* @param nearPlanePointsIndices - Which indices in the `frustumCoords` form the near plane.
5844* @param horizonPlane - The horizon plane.
5845*/
5846function adjustFarPlaneByHorizonPlane(frustumCoords, nearPlanePointsIndices, horizonPlane, flippedNearFar) {
5847	const nearPlanePointsOffset = flippedNearFar ? 4 : 0;
5848	const farPlanePointsOffset = flippedNearFar ? 0 : 4;
5849	let maxDist = 0;
5850	const cornerRayLengths = [];
5851	const cornerRayNormalizedDirections = [];
5852	for (let i = 0; i < 4; i++) {
5853		const dir = sub([], frustumCoords[i + farPlanePointsOffset], frustumCoords[i + nearPlanePointsOffset]);
5854		const len = length(dir);
5855		scale$1(dir, dir, 1 / len);
5856		cornerRayLengths.push(len);
5857		cornerRayNormalizedDirections.push(dir);
5858	}
5859	for (let i = 0; i < 4; i++) {
5860		const dist = rayPlaneIntersection(frustumCoords[i + nearPlanePointsOffset], cornerRayNormalizedDirections[i], horizonPlane);
5861		if (dist !== null && dist >= 0) maxDist = Math.max(maxDist, dist);
5862		else maxDist = Math.max(maxDist, cornerRayLengths[i]);
5863	}
5864	const nearPlaneNormalized = getNormalizedNearPlane(frustumCoords, nearPlanePointsIndices);
5865	const idealFarPlaneDistanceFromNearPlane = getIdealNearFarPlaneDistance(horizonPlane, nearPlaneNormalized);
5866	if (idealFarPlaneDistanceFromNearPlane !== null) {
5867		const idealCornerRayLength = idealFarPlaneDistanceFromNearPlane / dot(cornerRayNormalizedDirections[0], nearPlaneNormalized);
5868		maxDist = Math.min(maxDist, idealCornerRayLength);
5869	}
5870	for (let i = 0; i < 4; i++) {
5871		const targetLength = Math.min(maxDist, cornerRayLengths[i]);
5872		frustumCoords[i + farPlanePointsOffset] = [
5873			frustumCoords[i + nearPlanePointsOffset][0] + cornerRayNormalizedDirections[i][0] * targetLength,
5874			frustumCoords[i + nearPlanePointsOffset][1] + cornerRayNormalizedDirections[i][1] * targetLength,
5875			frustumCoords[i + nearPlanePointsOffset][2] + cornerRayNormalizedDirections[i][2] * targetLength,
5876			1
5877		];
5878	}
5879}
5880/**
5881* Returns the near plane equation with unit length direction.
5882* @param frustumCoords - Points of the frustum.
5883* @param nearPlanePointsIndices - Which indices in the `frustumCoords` form the near plane.
5884*/
5885function getNormalizedNearPlane(frustumCoords, nearPlanePointsIndices) {
5886	const nearPlaneA = sub([], frustumCoords[nearPlanePointsIndices[0]], frustumCoords[nearPlanePointsIndices[1]]);
5887	const nearPlaneB = sub([], frustumCoords[nearPlanePointsIndices[2]], frustumCoords[nearPlanePointsIndices[1]]);
5888	const nearPlaneNormalized = [
5889		0,
5890		0,
5891		0,
5892		0
5893	];
5894	normalize(nearPlaneNormalized, cross([], nearPlaneA, nearPlaneB));
5895	nearPlaneNormalized[3] = -dot(nearPlaneNormalized, frustumCoords[nearPlanePointsIndices[0]]);
5896	return nearPlaneNormalized;
5897}
5898/**
5899* Returns the ideal distance between the frustum's near and far plane so that the far plane only lies as far as the horizon.
5900*/
5901function getIdealNearFarPlaneDistance(horizonPlane, nearPlaneNormalized) {
5902	const horizonPlaneLen = len(horizonPlane);
5903	const normalizedHorizonPlane = scale$2([], horizonPlane, 1 / horizonPlaneLen);
5904	const projectedViewDirection = sub([], nearPlaneNormalized, scale$1([], normalizedHorizonPlane, dot(nearPlaneNormalized, normalizedHorizonPlane)));
5905	const projectedViewLength = len(projectedViewDirection);
5906	if (projectedViewLength > 0) {
5907		const horizonCircleRadius = Math.sqrt(1 - normalizedHorizonPlane[3] * normalizedHorizonPlane[3]);
5908		const horizonCircleCenter = scale$1([], normalizedHorizonPlane, -normalizedHorizonPlane[3]);
5909		const pointFurthestOnHorizonCircle = add([], horizonCircleCenter, scale$1([], projectedViewDirection, horizonCircleRadius / projectedViewLength));
5910		return pointPlaneSignedDistance(nearPlaneNormalized, pointFurthestOnHorizonCircle);
5911	} else return null;
5912}
5913//#endregion
5914//#region src/geo/projection/covering_tiles.ts
5915/**
5916* A simple/heuristic function that returns whether the tile is visible under the current transform.
5917* @returns an {@link IntersectionResult}.
5918*/
5919function isTileVisible(frustum, tileBoundingVolume, plane) {
5920	const frustumTest = tileBoundingVolume.intersectsFrustum(frustum);
5921	if (!plane || frustumTest === 0) return frustumTest;
5922	const planeTest = tileBoundingVolume.intersectsPlane(plane);
5923	if (planeTest === 0) return 0;
5924	if (frustumTest === 2 && planeTest === 2) return 2;
5925	return 1;
5926}
5927/**
5928* Definite integral of cos(x)^p. The analytical solution is described in `developer-guides/covering-tiles.md`,
5929* but here the integral is evaluated numerically.
5930* @param p - the power to raise cos(x) to inside the integral
5931* @param x1 - the starting point of the integral.
5932* @param x2 - the ending point of the integral.
5933* @return the integral of cos(x)^p from x=x1 to x=x2
5934*/
5935function integralOfCosXByP(p, x1, x2) {
5936	const numPoints = 10;
5937	let sum = 0;
5938	const dx = (x2 - x1) / numPoints;
5939	for (let i = 0; i < numPoints; i++) {
5940		const x = x1 + (i + .5) / numPoints * (x2 - x1);
5941		sum += dx * Math.pow(Math.cos(x), p);
5942	}
5943	return sum;
5944}
5945function createCalculateTileZoomFunction(maxZoomLevelsOnScreen, tileCountMaxMinRatio) {
5946	return function(requestedCenterZoom, distanceToTile2D, distanceToTileZ, distanceToCenter3D, cameraVerticalFOV) {
5947		/**
5948		* Controls how tiles are loaded at high pitch angles. Higher numbers cause fewer, lower resolution
5949		* tiles to be loaded. Calculate the value that will result in the selected number of zoom levels in
5950		* the worst-case condition (when the horizon is at the top of the screen). For more information, see
5951		* `developer-guides/covering-tiles.md`
5952		*/
5953		const pitchTileLoadingBehavior = 2 * ((maxZoomLevelsOnScreen - 1) / scaleZoom(Math.cos(degreesToRadians(maxMercatorHorizonAngle - cameraVerticalFOV)) / Math.cos(degreesToRadians(maxMercatorHorizonAngle))) - 1);
5954		const centerPitch = Math.acos(distanceToTileZ / distanceToCenter3D);
5955		const tileCountPitch0 = 2 * integralOfCosXByP(pitchTileLoadingBehavior - 1, 0, degreesToRadians(cameraVerticalFOV / 2));
5956		const highestPitch = Math.min(degreesToRadians(maxMercatorHorizonAngle), centerPitch + degreesToRadians(cameraVerticalFOV / 2));
5957		const lowestPitch = Math.min(highestPitch, centerPitch - degreesToRadians(cameraVerticalFOV / 2));
5958		const tileCount = integralOfCosXByP(pitchTileLoadingBehavior - 1, lowestPitch, highestPitch);
5959		const thisTilePitch = Math.atan(distanceToTile2D / distanceToTileZ);
5960		const distanceToTile3D = Math.hypot(distanceToTile2D, distanceToTileZ);
5961		let thisTileDesiredZ = requestedCenterZoom;
5962		thisTileDesiredZ = thisTileDesiredZ + scaleZoom(distanceToCenter3D / distanceToTile3D / Math.max(.5, Math.cos(degreesToRadians(cameraVerticalFOV / 2))));
5963		thisTileDesiredZ += pitchTileLoadingBehavior * scaleZoom(Math.cos(thisTilePitch)) / 2;
5964		thisTileDesiredZ -= scaleZoom(Math.max(1, tileCount / tileCountPitch0 / tileCountMaxMinRatio)) / 2;
5965		return thisTileDesiredZ;
5966	};
5967}
5968const defaultCalculateTileZoom = createCalculateTileZoomFunction(9.314, 3);
5969/**
5970* Return what zoom level of a tile source would most closely cover the tiles displayed by this transform.
5971* @param options - The options, most importantly the source's tile size.
5972* @returns An integer zoom level at which all tiles will be visible.
5973*/
5974function coveringZoomLevel(transform, options) {
5975	const z = (options.roundZoom ? Math.round : Math.floor)(transform.zoom + scaleZoom(transform.tileSize / options.tileSize));
5976	return Math.max(0, z);
5977}
5978/**
5979* Without terrain, `getTileBoundingVolume` has no knowledge of extruded features
5980* (eg. 3D buildings): every tile's bounding box is flat at the camera's `elevation`.
5981* That's a fine approximation of what's visible for most views, but it breaks down
5982* as the frustum's bottom edge approaches horizontal (which depends on both `pitch`
5983* and `fov`, not pitch alone) - a tall building can still be poking up into view
5984* long after its tile's ground point has dropped out of the frustum.
5985*
5986* This is an assumed upper bound on real-world feature height (in metres), used to
5987* grow the elevation used for tile culling as that bottom edge nears horizontal, so
5988* such tiles are not dropped too early. It has no effect away from that edge case.
5989*/
5990const ASSUMED_MAX_FEATURE_HEIGHT_METERS = 500;
5991/**
5992* Angle between the frustum's bottom edge and the mercator horizon below which
5993* the culling elevation starts to grow.
5994*/
5995const TILE_CULLING_HORIZON_ONSET_DEGREES = 15;
5996/**
5997* Returns the elevation to use when computing tile bounding volumes for culling:
5998* `transform.elevation`, growing by up to the greater of
5999* `ASSUMED_MAX_FEATURE_HEIGHT_METERS` and `maxContentElevation` (when provided)
6000* as the frustum's bottom edge approaches the horizon, where a ground-level
6001* bounding box would cull tiles whose extruded features are still visible.
6002*/
6003function getElevationForTileCulling(transform, maxContentElevation) {
6004	const bottomEdgeDegreesAboveHorizontal = maxMercatorHorizonAngle - transform.pitch - transform.fov / 2;
6005	const proximityToHorizon = clamp((TILE_CULLING_HORIZON_ONSET_DEGREES - bottomEdgeDegreesAboveHorizontal) / TILE_CULLING_HORIZON_ONSET_DEGREES, 0, 1);
6006	const maxFeatureHeight = Math.max(ASSUMED_MAX_FEATURE_HEIGHT_METERS, maxContentElevation ?? 0);
6007	return transform.elevation + proximityToHorizon * maxFeatureHeight;
6008}
6009/**
6010* Returns a copy of the frustum with its far plane moved away from the camera by `distance`.
6011* Each far corner travels along its own camera ray until it reaches the pushed plane, so the
6012* result stays a pyramid and the corners stay consistent with the side planes. The globe
6013* frustum's far plane is fitted to the surface horizon, so elevated content beyond it would
6014* otherwise be culled while still visible.
6015*/
6016function pushFrustumFarPlane(frustum, distance, cameraPos) {
6017	const far = frustum.planes[1];
6018	const points = frustum.points.map((p) => {
6019		const distanceToFar = far[0] * p[0] + far[1] * p[1] + far[2] * p[2] + far[3];
6020		if (Math.abs(distanceToFar) > 1e-6) return p;
6021		const ray = [
6022			p[0] - cameraPos[0],
6023			p[1] - cameraPos[1],
6024			p[2] - cameraPos[2]
6025		];
6026		const rayDotNormal = far[0] * ray[0] + far[1] * ray[1] + far[2] * ray[2];
6027		if (rayDotNormal >= -1e-9) return p;
6028		const scale = distance / -rayDotNormal;
6029		return [
6030			p[0] + ray[0] * scale,
6031			p[1] + ray[1] * scale,
6032			p[2] + ray[2] * scale,
6033			p[3]
6034		];
6035	});
6036	const planes = frustum.planes.map((p, i) => i === 1 ? [
6037		p[0],
6038		p[1],
6039		p[2],
6040		p[3] + distance
6041	] : p);
6042	const min = [
6043		Number.POSITIVE_INFINITY,
6044		Number.POSITIVE_INFINITY,
6045		Number.POSITIVE_INFINITY
6046	];
6047	const max = [
6048		Number.NEGATIVE_INFINITY,
6049		Number.NEGATIVE_INFINITY,
6050		Number.NEGATIVE_INFINITY
6051	];
6052	for (const p of points) for (let i = 0; i < 3; i++) {
6053		min[i] = Math.min(min[i], p[i]);
6054		max[i] = Math.max(max[i], p[i]);
6055	}
6056	return new Frustum(points, planes, new Aabb(min, max));
6057}
6058/**
6059* The horizon culling plane and the frustum's far plane both assume content sits on the
6060* surface. Content elevated to radius r (in planet radii) stays visible up to acos(1/r)
6061* beyond the surface horizon and up to sqrt(r^2-1) farther away, so both are pushed back
6062* accordingly; without this, tiles under highly elevated symbols would be dropped while
6063* the symbols are still in view.
6064* @param frustum - The covering frustum, fitted to the surface horizon.
6065* @param plane - The horizon culling plane used by globe transform.
6066* @param cameraPos - The camera position, in the frustum's space.
6067* @param maxContentElevation - The highest elevation the content may reach, in meters.
6068*/
6069function expandCullingToContentElevation(frustum, plane, cameraPos, maxContentElevation) {
6070	const len = Math.hypot(plane[0], plane[1], plane[2]);
6071	if (len === 0) return {
6072		frustum,
6073		plane
6074	};
6075	const shellRadius = 1 + maxContentElevation / earthRadius;
6076	const horizonCos = clamp(-plane[3] / len, -1, 1);
6077	const shellCos = Math.cos(Math.acos(horizonCos) + Math.acos(1 / shellRadius));
6078	return {
6079		frustum: pushFrustumFarPlane(frustum, Math.sqrt(shellRadius * shellRadius - 1), cameraPos),
6080		plane: [
6081			plane[0],
6082			plane[1],
6083			plane[2],
6084			-shellCos * len
6085		]
6086	};
6087}
6088/**
6089* Returns a list of tiles that optimally covers the screen. Adapted for globe projection.
6090* Correctly handles LOD when moving over the antimeridian.
6091* @param transform - The transform instance.
6092* @param frustum - The covering frustum.
6093* @param plane - The clipping plane used by globe transform, or null.
6094* @param cameraCoord - The x, y, z position of the camera in MercatorCoordinates.
6095* @param centerCoord - The x, y, z position of the center point in MercatorCoordinates.
6096* @param options - Additional coveringTiles options.
6097* @param details - Interface to define required helper functions.
6098* @returns A list of tile coordinates, ordered by ascending distance from camera.
6099*/
6100function coveringTiles(transform, options) {
6101	let frustum = transform.getCameraFrustum();
6102	let plane = transform.getClippingPlane();
6103	if (plane && options.maxContentElevation > 0) ({frustum, plane} = expandCullingToContentElevation(frustum, plane, transform.cameraPosition, options.maxContentElevation));
6104	const cameraCoord = cameraMercatorCoordinate(transform);
6105	const centerCoord = MercatorCoordinate.fromLngLat(transform.center, transform.elevation);
6106	const elevationForTileCulling = getElevationForTileCulling(transform, options.maxContentElevation);
6107	const detailsProvider = transform.getCoveringTilesDetailsProvider();
6108	const allowVariableZoom = detailsProvider.allowVariableZoom(transform, options);
6109	const desiredZ = coveringZoomLevel(transform, options);
6110	const minZoom = options.minzoom || 0;
6111	const maxZoom = options.maxzoom !== void 0 ? options.maxzoom : transform.maxZoom;
6112	const nominalZ = Math.min(Math.max(0, desiredZ), maxZoom);
6113	const numTiles = Math.pow(2, nominalZ);
6114	const cameraPoint = [
6115		numTiles * cameraCoord.x,
6116		numTiles * cameraCoord.y,
6117		0
6118	];
6119	const centerPoint = [
6120		numTiles * centerCoord.x,
6121		numTiles * centerCoord.y,
6122		0
6123	];
6124	const distanceToCenter2d = Math.hypot(centerCoord.x - cameraCoord.x, centerCoord.y - cameraCoord.y);
6125	const distanceZ = Math.abs(centerCoord.z - cameraCoord.z);
6126	const distanceToCenter3d = Math.hypot(distanceToCenter2d, distanceZ);
6127	const newRootTile = (wrap) => {
6128		return {
6129			zoom: 0,
6130			x: 0,
6131			y: 0,
6132			wrap,
6133			fullyVisible: false
6134		};
6135	};
6136	const stack = [];
6137	const result = [];
6138	if (transform.renderWorldCopies && detailsProvider.allowWorldCopies()) for (let i = 1; i <= 3; i++) {
6139		stack.push(newRootTile(-i));
6140		stack.push(newRootTile(i));
6141	}
6142	stack.push(newRootTile(0));
6143	while (stack.length > 0) {
6144		const it = stack.pop();
6145		const x = it.x;
6146		const y = it.y;
6147		let fullyVisible = it.fullyVisible;
6148		const tileID = {
6149			x,
6150			y,
6151			z: it.zoom
6152		};
6153		const boundingVolume = detailsProvider.getTileBoundingVolume(tileID, it.wrap, elevationForTileCulling, options);
6154		if (!fullyVisible) {
6155			const intersectResult = isTileVisible(frustum, boundingVolume, plane);
6156			if (intersectResult === 0) continue;
6157			fullyVisible = intersectResult === 2;
6158		}
6159		const distToTile2d = detailsProvider.distanceToTile2d(cameraCoord.x, cameraCoord.y, tileID, boundingVolume);
6160		let thisTileDesiredZ = desiredZ;
6161		if (allowVariableZoom) thisTileDesiredZ = (options.calculateTileZoom || defaultCalculateTileZoom)(transform.zoom + scaleZoom(transform.tileSize / options.tileSize), distToTile2d, distanceZ, distanceToCenter3d, transform.fov);
6162		thisTileDesiredZ = (options.roundZoom ? Math.round : Math.floor)(thisTileDesiredZ);
6163		thisTileDesiredZ = Math.max(0, thisTileDesiredZ);
6164		const z = Math.min(thisTileDesiredZ, maxZoom);
6165		it.wrap = detailsProvider.getWrap(centerCoord, tileID, it.wrap);
6166		if (it.zoom >= z) {
6167			if (it.zoom < minZoom) continue;
6168			const dz = nominalZ - it.zoom;
6169			const dx = cameraPoint[0] - .5 - (x << dz);
6170			const dy = cameraPoint[1] - .5 - (y << dz);
6171			const overscaledZ = options.reparseOverscaled ? Math.max(it.zoom, thisTileDesiredZ) : it.zoom;
6172			result.push({
6173				tileID: new OverscaledTileID(it.zoom === maxZoom ? overscaledZ : it.zoom, it.wrap, it.zoom, x, y),
6174				distanceSq: sqrLen([centerPoint[0] - .5 - x, centerPoint[1] - .5 - y]),
6175				tileDistanceToCamera: Math.sqrt(dx * dx + dy * dy)
6176			});
6177			continue;
6178		}
6179		for (let i = 0; i < 4; i++) {
6180			const childX = (x << 1) + i % 2;
6181			const childY = (y << 1) + (i >> 1);
6182			const childZ = it.zoom + 1;
6183			stack.push({
6184				zoom: childZ,
6185				x: childX,
6186				y: childY,
6187				wrap: it.wrap,
6188				fullyVisible
6189			});
6190		}
6191	}
6192	return result.sort((a, b) => a.distanceSq - b.distanceSq).map((a) => a.tileID);
6193}
6194//#endregion
6195//#region src/tile/tile_manager_raster.ts
6196function isRasterType(type) {
6197	return type === "raster" || type === "image" || type === "video";
6198}
6199/**
6200* Designate fading bases and parents using a many-to-one relationship where the lower children fade in/out
6201* with their parents. Raster shaders are not currently designed for a one-to-many fade relationship.
6202*
6203* Tiles that are candidates for fading out must be loaded and rendered tiles, as loading a tile to then
6204* fade it out would not appear smoothly. The first source of truth for tile fading always starts at the
6205* ideal tile, which continually changes on map adjustment. The state of the previously rendered ideal
6206* tile plane indicates which direction to fade each part of the newer ideal plane (with varying z).
6207*
6208* For a pitched map, the back of the map can have decreasing zooms while the front can have increasing zooms.
6209* Fade logic must therefore adapt dynamically based on the previously rendered ideal tile set.
6210*/
6211function updateFadingTiles(inViewTiles, idealTileIDs, retain, maxFadingAncestorLevels, sourceMinZoom, sourceMaxZoom, rasterFadeDuration) {
6212	const currentTime = now();
6213	const edgeTileIDs = getEdgeTiles(idealTileIDs);
6214	for (const idealID of idealTileIDs) {
6215		const idealTile = inViewTiles.getTileById(idealID.key);
6216		if (idealTile.fadingDirection === 0 || idealTile.fadeOpacity === 0) idealTile.resetFadeLogic();
6217		if (updateFadingAncestor(inViewTiles, idealTile, retain, currentTime, maxFadingAncestorLevels, sourceMinZoom, rasterFadeDuration)) continue;
6218		if (updateFadingDescendents(inViewTiles, idealTile, retain, currentTime, sourceMaxZoom, rasterFadeDuration)) continue;
6219		if (updateFadingEdge(idealTile, edgeTileIDs, currentTime, rasterFadeDuration)) continue;
6220		idealTile.resetFadeLogic();
6221	}
6222}
6223/**
6224* Many-to-one cross-fade. Set 4 ideal tiles as the fading base for a rendered parent tile
6225* as the fading parent. Here the parent is fading out and the ideal tile is fading in.
6226*
6227* Parent tile - fading out                                ■                                -- Fading Parent
6228*                                   ┌──────────────┬──────┴───────┬──────────────┐
6229* Ideal tiles - fading in           ■              ■              ■              ■         -- Base Role = Incoming
6230*                             ┌───┬─┴─┬───┐  ┌───┬─┴─┬───┐  ┌───┬─┴─┬───┐  ┌───┬─┴─┬───┐
6231*                             ■   ■   ■   ■  ■   ■   ■   ■  ■   ■   ■   ■  ■   ■   ■   ■
6232*/
6233function updateFadingAncestor(inViewTiles, idealTile, retain, now, maxFadingAncestorLevels, sourceMinZoom, rasterFadeDuration) {
6234	if (!idealTile.hasData()) return false;
6235	const { tileID: idealID, fadingRole, fadingDirection, fadingParentID } = idealTile;
6236	if (fadingRole === 0 && fadingDirection === 1 && fadingParentID) {
6237		retain[fadingParentID.key] = fadingParentID;
6238		return true;
6239	}
6240	const minAncestorZ = Math.max(idealID.overscaledZ - maxFadingAncestorLevels, sourceMinZoom);
6241	for (let ancestorZ = idealID.overscaledZ - 1; ancestorZ >= minAncestorZ; ancestorZ--) {
6242		const ancestorID = idealID.scaledTo(ancestorZ);
6243		const ancestorTile = inViewTiles.getLoadedTile(ancestorID);
6244		if (!ancestorTile) continue;
6245		idealTile.setCrossFadeLogic({
6246			fadingRole: 0,
6247			fadingDirection: 1,
6248			fadingParentID: ancestorTile.tileID,
6249			fadeEndTime: now + rasterFadeDuration
6250		});
6251		ancestorTile.setCrossFadeLogic({
6252			fadingRole: 1,
6253			fadingDirection: 0,
6254			fadeEndTime: now + rasterFadeDuration
6255		});
6256		retain[ancestorID.key] = ancestorID;
6257		return true;
6258	}
6259	return false;
6260}
6261/**
6262* Many-to-one cross-fade. Search descendents of ideal tiles as the fading base with the ideal tile
6263* as the fading parent. Here the children are fading out and the ideal tile is fading in.
6264*
6265*                                                         ■
6266*                                   ┌──────────────┬──────┴───────┬──────────────┐
6267* Ideal tiles - fading in           ■              ■              ■              ■          -- Fading Parent
6268*                             ┌───┬─┴─┬───┐  ┌───┬─┴─┬───┐  ┌───┬─┴─┬───┐  ┌───┬─┴─┬───┐
6269* Child tiles - fading out    ■   ■   ■   ■  ■   ■   ■   ■  ■   ■   ■   ■  ■   ■   ■   ■    -- Base Role = Departing
6270*
6271* Try direct children first. If none found, try grandchildren. Stops at the first generation that provides a fader.
6272*/
6273function updateFadingDescendents(inViewTiles, idealTile, retain, now, sourceMaxZoom, rasterFadeDuration) {
6274	if (!idealTile.hasData()) return false;
6275	const idealChildren = idealTile.tileID.children(sourceMaxZoom);
6276	let hasFader = updateFadingChildren(inViewTiles, idealTile, idealChildren, retain, now, sourceMaxZoom, rasterFadeDuration);
6277	if (hasFader) return true;
6278	for (const childID of idealChildren) if (updateFadingChildren(inViewTiles, idealTile, childID.children(sourceMaxZoom), retain, now, sourceMaxZoom, rasterFadeDuration)) hasFader = true;
6279	return hasFader;
6280}
6281function updateFadingChildren(inViewTiles, idealTile, childIDs, retain, now, sourceMaxZoom, rasterFadeDuration) {
6282	if (childIDs[0].overscaledZ >= sourceMaxZoom) return false;
6283	let foundFader = false;
6284	for (const childID of childIDs) {
6285		const childTile = inViewTiles.getLoadedTile(childID);
6286		if (!childTile) continue;
6287		const { fadingRole, fadingDirection, fadingParentID } = childTile;
6288		if (fadingRole !== 0 || fadingDirection !== 0 || !fadingParentID) {
6289			childTile.setCrossFadeLogic({
6290				fadingRole: 0,
6291				fadingDirection: 0,
6292				fadingParentID: idealTile.tileID,
6293				fadeEndTime: now + rasterFadeDuration
6294			});
6295			idealTile.setCrossFadeLogic({
6296				fadingRole: 1,
6297				fadingDirection: 1,
6298				fadeEndTime: now + rasterFadeDuration
6299			});
6300		}
6301		retain[childID.key] = childID;
6302		foundFader = true;
6303	}
6304	return foundFader;
6305}
6306/**
6307* One-to-one self fading for unloaded edge tiles (for panning sideways on map). for loading tiles over gaps it feels
6308* more natural for them to fade in, however if they are already loaded/cached then there is no need to fade as map will
6309* look cohesive with no gaps. Note that draw_raster determines fade priority, as many-to-one fade supersedes edge fading.
6310*/
6311function updateFadingEdge(idealTile, edgeTileIDs, now, rasterFadeDuration) {
6312	const idealID = idealTile.tileID;
6313	if (idealTile.selfFading) return true;
6314	if (idealTile.hasData()) return false;
6315	if (edgeTileIDs.has(idealID)) {
6316		const fadeEndTime = now + rasterFadeDuration;
6317		idealTile.setSelfFadeLogic(fadeEndTime);
6318		return true;
6319	}
6320	return false;
6321}
6322function hasRasterTransition(inViewTiles, rasterFadeDuration) {
6323	if (rasterFadeDuration <= 0) return false;
6324	const currentTime = now();
6325	for (const tile of inViewTiles.getAllTiles()) if (tile.fadeEndTime >= currentTime) return true;
6326	return false;
6327}
6328//#endregion
6329//#region src/tile/tile_manager_raster_dem.ts
6330/**
6331* For raster terrain source, backfill DEM to eliminate visible tile boundaries
6332*/
6333function backfillDEM(tile, inViewTiles) {
6334	const renderables = inViewTiles.getRenderableIds();
6335	for (const borderId of renderables) {
6336		if (!tile.neighboringTiles?.[borderId]) continue;
6337		const borderTile = inViewTiles.getTileById(borderId);
6338		if (!tile.neighboringTiles[borderId].backfilled) fillBorder(tile, borderTile);
6339		if (borderTile.neighboringTiles?.[tile.tileID.key]?.backfilled) continue;
6340		fillBorder(borderTile, tile);
6341	}
6342}
6343function fillBorder(tile, borderTile) {
6344	tile.needsHillshadePrepare = true;
6345	tile.needsTerrainPrepare = true;
6346	tile.needsColorReliefPrepare = true;
6347	let dx = borderTile.tileID.canonical.x - tile.tileID.canonical.x;
6348	const dy = borderTile.tileID.canonical.y - tile.tileID.canonical.y;
6349	const dim = Math.pow(2, tile.tileID.canonical.z);
6350	const borderId = borderTile.tileID.key;
6351	if (dx === 0 && dy === 0) return;
6352	if (Math.abs(dy) > 1) return;
6353	if (Math.abs(dx) > 1) {
6354		if (Math.abs(dx + dim) === 1) dx += dim;
6355		else if (Math.abs(dx - dim) === 1) dx -= dim;
6356	}
6357	if (!borderTile.dem || !tile.dem) return;
6358	tile.dem.backfillBorder(borderTile.dem, dx, dy);
6359	if (tile.neighboringTiles?.[borderId]) tile.neighboringTiles[borderId].backfilled = true;
6360}
6361//#endregion
6362//#region src/tile/tile_manager_in_view_tiles.ts
6363var InViewTiles = class {
6364	constructor() {
6365		this._tiles = {};
6366	}
6367	handleWrapJump(wrapDelta) {
6368		const tiles = {};
6369		for (const id in this._tiles) {
6370			const tile = this._tiles[id];
6371			tile.tileID = tile.tileID.unwrapTo(tile.tileID.wrap + wrapDelta);
6372			tiles[tile.tileID.key] = tile;
6373		}
6374		this._tiles = tiles;
6375	}
6376	setFeatureState(featuresChanged, painter, revision) {
6377		for (const id in this._tiles) this._tiles[id].setFeatureState(featuresChanged, painter, revision);
6378	}
6379	getAllTiles() {
6380		return Object.values(this._tiles);
6381	}
6382	getAllIds(sorted = false) {
6383		if (sorted) return Object.values(this._tiles).map((tile) => tile.tileID).sort(compareTileId).map((id) => id.key);
6384		return Object.keys(this._tiles);
6385	}
6386	getTileById(key) {
6387		return this._tiles[key];
6388	}
6389	setTile(key, tile) {
6390		this._tiles[key] = tile;
6391	}
6392	deleteTileById(key) {
6393		delete this._tiles[key];
6394	}
6395	/**
6396	* Get a currently loaded tile.
6397	* - a cached tile is not a loaded tile
6398	* @returns the tile if it's in view and had data, null otherwise.
6399	*/
6400	getLoadedTile(tileID) {
6401		const tile = this.getTileById(tileID.key);
6402		if (tile?.hasData()) return tile;
6403		return null;
6404	}
6405	isIdRenderable(id, symbolLayer = false) {
6406		return this.getTileById(id)?.isRenderable(symbolLayer);
6407	}
6408	getRenderableIds(bearingInRadians = 0, symbolLayer) {
6409		const renderables = [];
6410		for (const id of this.getAllIds()) if (this.isIdRenderable(id, symbolLayer)) renderables.push(this.getTileById(id));
6411		if (symbolLayer) return renderables.sort((a_, b_) => {
6412			const a = a_.tileID;
6413			const b = b_.tileID;
6414			const rotatedA = new Point(a.canonical.x, a.canonical.y)._rotate(-bearingInRadians);
6415			const rotatedB = new Point(b.canonical.x, b.canonical.y)._rotate(-bearingInRadians);
6416			return a.overscaledZ - b.overscaledZ || rotatedB.y - rotatedA.y || rotatedB.x - rotatedA.x;
6417		}).map((tile) => tile.tileID.key);
6418		return renderables.map((tile) => tile.tileID).sort(compareTileId).map((id) => id.key);
6419	}
6420};
6421//#endregion
6422//#region src/tile/tile_manager.ts
6423/**
6424* @internal
6425* `TileManager` is responsible for
6426*
6427*  - creating an instance of `Source`
6428*  - caching tiles loaded from an instance of `Source`
6429*  - handling incoming source data events events from `Map` and coordinating updates
6430*  - providing the current renderable tile coordinates to the `Painter`
6431*  - loading the tiles needed to render a given viewport
6432*  - retaining the tiles needed as substitutes for pending loading tiles
6433*  - retaining the tiles needed for fading between parents and children (for raster sources)
6434*  - reloading tiles when source data or dependencies change
6435*  - handling tile expiration and refresh timers
6436*  - unloading cached tiles not needed to render a given viewport
6437*  - managing tile state and feature state
6438*/
6439var TileManager = class TileManager extends Evented {
6440	static {
6441		this.maxUnderzooming = 10;
6442	}
6443	static {
6444		this.maxOverzooming = 3;
6445	}
6446	constructor(id, options, dispatcher) {
6447		super();
6448		this._maxContentElevationSeen = 0;
6449		this.id = id;
6450		this.dispatcher = dispatcher;
6451		this.on("data", (e) => {
6452			this._dataHandler(e);
6453		});
6454		this.on("dataloading", () => {
6455			this._sourceErrored = false;
6456		});
6457		this.on("error", () => {
6458			this._sourceErrored = this._source.loaded();
6459		});
6460		this._source = create(id, options, dispatcher, this);
6461		this._inViewTiles = new InViewTiles();
6462		this._outOfViewCache = new TileCache(0, (tile) => this._unloadTile(tile));
6463		this._timers = {};
6464		this._maxTileCacheSize = null;
6465		this._maxTileCacheZoomLevels = null;
6466		this._rasterFadeDuration = 0;
6467		this._maxFadingAncestorLevels = 5;
6468		this._state = new SourceFeatureState();
6469		this._didEmitContent = false;
6470		this._updated = false;
6471	}
6472	onAdd(map) {
6473		this.map = map;
6474		this._maxTileCacheSize = map ? map._maxTileCacheSize : null;
6475		this._maxTileCacheZoomLevels = map ? map._maxTileCacheZoomLevels : null;
6476		if (this._source?.onAdd) this._source.onAdd(map);
6477	}
6478	onRemove(map) {
6479		for (const tile of this._inViewTiles.getAllTiles()) tile.unloadVectorData();
6480		this.clearTiles();
6481		if (this._source?.onRemove) this._source.onRemove(map);
6482		this._inViewTiles = new InViewTiles();
6483	}
6484	/**
6485	* Return true if no tile data is pending, tiles will not change unless
6486	* an additional API call is received.
6487	*/
6488	loaded() {
6489		if (this._sourceErrored) return true;
6490		if (!this._sourceLoaded) return false;
6491		if (!this._source.loaded()) return false;
6492		if ((this.used !== void 0 || this.usedForTerrain !== void 0) && !this.used && !this.usedForTerrain) return true;
6493		if (!this._updated) return false;
6494		for (const tile of this._inViewTiles.getAllTiles()) if (tile.state !== "loaded" && tile.state !== "errored") return false;
6495		return true;
6496	}
6497	getSource() {
6498		return this._source;
6499	}
6500	getState() {
6501		return this._state;
6502	}
6503	pause() {
6504		this._paused = true;
6505	}
6506	resume() {
6507		if (!this._paused) return;
6508		const shouldReload = this._shouldReloadOnResume;
6509		this._paused = false;
6510		this._shouldReloadOnResume = false;
6511		if (shouldReload) this.reload();
6512		if (this.transform) this.update(this.transform, this.terrain);
6513	}
6514	async _loadTile(tile, id, state, hadData) {
6515		try {
6516			const result = await this._source.loadTile(tile);
6517			this._tileLoaded(tile, id, state, hadData, result);
6518		} catch (err) {
6519			tile.state = "errored";
6520			if (err.status !== 404) this._source.fire(new ErrorEvent(ensureError(err), { tile }));
6521			else this.update(this.transform, this.terrain);
6522		}
6523	}
6524	_unloadTile(tile) {
6525		if (this._source.unloadTile) this._source.unloadTile(tile);
6526	}
6527	_abortTile(tile) {
6528		if (this._source.abortTile) this._source.abortTile(tile);
6529		this._source.fire(new MapSourceDataEvent("dataabort", {
6530			tile,
6531			coord: tile.tileID
6532		}));
6533	}
6534	serialize() {
6535		return this._source.serialize();
6536	}
6537	prepare(context) {
6538		if (this._source.prepare) this._source.prepare();
6539		this._state.coalesceChanges(this._inViewTiles, this.map ? this.map.painter : null);
6540		for (const tile of this._inViewTiles.getAllTiles()) {
6541			tile.upload(context);
6542			tile.prepare(this.map.style.imageManager);
6543		}
6544	}
6545	/**
6546	* Return all tile ids ordered with z-order, and cast to numbers
6547	*/
6548	getIds() {
6549		return this._inViewTiles.getAllIds(true);
6550	}
6551	getRenderableIds(symbolLayer) {
6552		return this._inViewTiles.getRenderableIds(this.transform?.bearingInRadians, symbolLayer);
6553	}
6554	hasRenderableParent(tileID) {
6555		const parentZ = tileID.overscaledZ - 1;
6556		if (parentZ >= this._source.minzoom) {
6557			const parentTile = this.getLoadedTile(tileID.scaledTo(parentZ));
6558			if (parentTile) return this._inViewTiles.isIdRenderable(parentTile.tileID.key);
6559		}
6560		return false;
6561	}
6562	/**
6563	* Reload tiles based on the current state of the source.
6564	* @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'.
6565	* @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.
6566	*/
6567	reload(sourceDataChanged, shouldReloadTileOptions = void 0) {
6568		if (this._paused) {
6569			this._shouldReloadOnResume = true;
6570			return;
6571		}
6572		this._outOfViewCache.reset();
6573		for (const id of this._inViewTiles.getAllIds()) {
6574			const tile = this._inViewTiles.getTileById(id);
6575			if (shouldReloadTileOptions && !this._source.shouldReloadTile(tile, shouldReloadTileOptions)) continue;
6576			else if (sourceDataChanged) this._reloadTile(id, tile.state === "errored" ? "loading" : "expired");
6577			else if (tile.state !== "errored") this._reloadTile(id, "reloading");
6578		}
6579	}
6580	async _reloadTile(id, state) {
6581		const tile = this._inViewTiles.getTileById(id);
6582		if (!tile) return;
6583		const hadData = tile.hasData();
6584		if (tile.state !== "loading") tile.state = state;
6585		await this._loadTile(tile, id, state, hadData);
6586	}
6587	_tileLoaded(tile, id, previousState, hadData, result) {
6588		if (!hadData) {
6589			tile.timeAdded = now();
6590			if (tile.selfFading) tile.fadeEndTime = tile.timeAdded + this._rasterFadeDuration;
6591		}
6592		if (previousState === "expired") tile.refreshedUponExpiration = true;
6593		this._setTileReloadTimer(id, tile);
6594		if (result?.unmodified) return;
6595		if (this.getSource().type === "raster-dem" && tile.dem) backfillDEM(tile, this._inViewTiles);
6596		tile.featureStateRevision = -1;
6597		this._state.initializeTileState(tile, this.map ? this.map.painter : null);
6598		if (!tile.aborted) this._source.fire(new MapSourceDataEvent("data", {
6599			tile,
6600			coord: tile.tileID
6601		}));
6602	}
6603	/**
6604	* Get a specific tile by TileID
6605	*/
6606	getTile(tileID) {
6607		return this.getTileByID(tileID.key);
6608	}
6609	/**
6610	* Get a specific tile by id
6611	*/
6612	getTileByID(id) {
6613		return this._inViewTiles.getTileById(id);
6614	}
6615	/**
6616	* Retain the uppermost loaded children of each provided target tile, within a variable covering zoom range.
6617	*
6618	* On pitched maps, different parts of the screen show different zoom levels simultaneously.
6619	* Ideal tiles are generated using coveringTiles() above, which returns the ideal tile set for
6620	* the current pitched plane, which can carry tiles of varying zooms (overscaledZ).
6621	* See: https://maplibre.org/maplibre-gl-js/docs/examples/level-of-detail-control/
6622	*
6623	* A fixed `maxCoveringZoom` on a pitched map would incorrectly intersect with some
6624	* ideal tiles and cause distant high-pitch tiles to skip their uppermost children.
6625	*
6626	* To solve this, we calculate the max covering zoom for each ideal tile separately using its
6627	* `overscaledZ`. This effectively makes the "max covering zoom plane" parallel to the
6628	* "ideal tile plane," ensuring that we correctly capture the uppermost children
6629	* of each ideal tile across the pitched view.
6630	*
6631	* Analogy: imagine two sheets of paper in 3D space:
6632	*   - one sheet = ideal tiles at varying overscaledZ
6633	*   - the second sheet = maxCoveringZoom
6634	*
6635	* @param retainTileMap - this parameters will be updated with the child tiles to keep
6636	* @param idealTilesWithoutData - which of the ideal tiles currently does not have loaded data
6637	* @return a set of tiles that need to be loaded
6638	*/
6639	_retainLoadedChildren(retainTileMap, idealTilesWithoutData) {
6640		const loadedDescendents = this._getLoadedDescendents(idealTilesWithoutData);
6641		const incomplete = /* @__PURE__ */ new Set();
6642		for (const targetID of idealTilesWithoutData) {
6643			const descendents = loadedDescendents[targetID.key];
6644			if (!descendents?.length) {
6645				incomplete.add(targetID);
6646				continue;
6647			}
6648			const maxCoveringZoom = targetID.overscaledZ + TileManager.maxOverzooming;
6649			const candidates = descendents.filter((t) => t.tileID.overscaledZ <= maxCoveringZoom);
6650			if (!candidates.length) {
6651				incomplete.add(targetID);
6652				continue;
6653			}
6654			const topZoom = Math.min(...candidates.map((t) => t.tileID.overscaledZ));
6655			const topIDs = candidates.filter((t) => t.tileID.overscaledZ === topZoom).map((t) => t.tileID);
6656			for (const tileID of topIDs) retainTileMap[tileID.key] = tileID;
6657			if (!this._areDescendentsComplete(topIDs, topZoom, targetID.overscaledZ)) incomplete.add(targetID);
6658		}
6659		return incomplete;
6660	}
6661	/**
6662	* Return dictionary of qualified loaded descendents for each provided target tile id
6663	*/
6664	_getLoadedDescendents(targetTileIDs) {
6665		const loadedDescendents = {};
6666		for (const tile of this._inViewTiles.getAllTiles().filter((tile) => tile.hasData())) for (const targetID of targetTileIDs) if (tile.tileID.isChildOf(targetID)) {
6667			loadedDescendents[targetID.key] ||= [];
6668			loadedDescendents[targetID.key].push(tile);
6669		}
6670		return loadedDescendents;
6671	}
6672	/**
6673	* Determine if tile ids fully cover the current generation.
6674	* - 1st generation: need 4 children or 1 overscaled child
6675	* - 2nd generation: need 16 children or 1 overscaled child
6676	*/
6677	_areDescendentsComplete(generationIDs, generationZ, ancestorZ) {
6678		if (generationIDs.length === 1 && generationIDs[0].isOverscaled()) return generationIDs[0].overscaledZ === generationZ;
6679		else return Math.pow(4, generationZ - ancestorZ) === generationIDs.length;
6680	}
6681	/**
6682	* Get a currently loaded tile.
6683	* - a cached tile is not a loaded tile
6684	* @returns the tile if it's in view and had data, null otherwise.
6685	*/
6686	getLoadedTile(tileID) {
6687		return this._inViewTiles.getLoadedTile(tileID);
6688	}
6689	/**
6690	* Resizes the tile cache based on the current viewport's size
6691	* or the maxTileCacheSize option passed during map creation
6692	*
6693	* Larger viewports use more tiles and need larger caches. Larger viewports
6694	* are more likely to be found on devices with more memory and on pages where
6695	* the map is more important.
6696	*/
6697	updateCacheSize(transform) {
6698		const approxTilesInView = (Math.ceil(transform.width / this._source.tileSize) + 1) * (Math.ceil(transform.height / this._source.tileSize) + 1);
6699		const commonZoomRange = this._maxTileCacheZoomLevels === null ? config.MAX_TILE_CACHE_ZOOM_LEVELS : this._maxTileCacheZoomLevels;
6700		const viewDependentMaxSize = Math.floor(approxTilesInView * commonZoomRange);
6701		const maxSize = typeof this._maxTileCacheSize === "number" ? Math.min(this._maxTileCacheSize, viewDependentMaxSize) : viewDependentMaxSize;
6702		this._outOfViewCache.setMaxSize(maxSize);
6703	}
6704	handleWrapJump(lng) {
6705		const worldDifference = (lng - (this._prevLng === void 0 ? lng : this._prevLng)) / 360;
6706		const wrapDelta = Math.round(worldDifference);
6707		this._prevLng = lng;
6708		if (wrapDelta) {
6709			this._inViewTiles.handleWrapJump(wrapDelta);
6710			this._resetTileReloadTimers();
6711		}
6712	}
6713	/**
6714	* The highest elevation this source's symbols may reach, in meters: `symbol-height-offset`
6715	* constants read from the visible symbol layers, data-driven maxima tracked by the loaded
6716	* buckets at layout time. Only data-driven layers scan the loaded tiles, so styles without
6717	* them pay nothing beyond the layer loop. The value is a high-water mark: a maximum seen once is kept even
6718	* after its tile unloads, otherwise dropping the tile would also drop the reason to keep it,
6719	* and the tile could not come back while its content is still visible. The mark resets with
6720	* the tiles in `clearTiles`.
6721	*/
6722	_updateMaxContentElevation() {
6723		let maxElevation = this._maxContentElevationSeen;
6724		const layers = this.map?.style?._layers;
6725		if (!layers) return maxElevation;
6726		const tiles = this._inViewTiles.getAllTiles();
6727		for (const layerId in layers) {
6728			const layer = layers[layerId];
6729			if (layer.type !== "symbol" || layer.source !== this.id || layer.isHidden(this.transform.zoom)) continue;
6730			const symbolLayer = layer;
6731			if (!symbolLayer.layout) continue;
6732			const heightOffset = symbolLayer.layout.get("symbol-height-offset");
6733			maxElevation = Math.max(maxElevation, heightOffset.constantOr(0));
6734			if (heightOffset.isConstant()) continue;
6735			for (const tile of tiles) {
6736				const bucket = tile.getBucket(layer);
6737				if (bucket && bucket.maxHeightOffset > maxElevation) maxElevation = bucket.maxHeightOffset;
6738			}
6739		}
6740		this._maxContentElevationSeen = maxElevation;
6741		return maxElevation;
6742	}
6743	/**
6744	* Removes tiles that are outside the viewport and adds new tiles that
6745	* are inside the viewport.
6746	*/
6747	update(transform, terrain) {
6748		if (!this._sourceLoaded || this._paused) return;
6749		this.transform = transform;
6750		this.terrain = terrain;
6751		this.updateCacheSize(transform);
6752		this.handleWrapJump(this.transform.center.lng);
6753		let idealTileIDs;
6754		if (!this.used && !this.usedForTerrain) idealTileIDs = [];
6755		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));
6756		else {
6757			idealTileIDs = coveringTiles(transform, {
6758				tileSize: this.usedForTerrain ? this.tileSize : this._source.tileSize,
6759				minzoom: this._source.minzoom,
6760				maxzoom: this._source.type === "vector" && this.map._zoomLevelsToOverscale !== void 0 ? Math.max(this._source.maxzoom, transform.maxZoom - this.map._zoomLevelsToOverscale) : this._source.maxzoom,
6761				roundZoom: this.usedForTerrain ? false : this._source.roundZoom,
6762				reparseOverscaled: this._source.reparseOverscaled,
6763				terrain,
6764				calculateTileZoom: this._source.calculateTileZoom,
6765				maxContentElevation: this._updateMaxContentElevation()
6766			});
6767			if (this._source.hasTile) idealTileIDs = idealTileIDs.filter((coord) => this._source.hasTile(coord));
6768		}
6769		if (this.usedForTerrain) idealTileIDs = this._addTerrainIdealTiles(idealTileIDs);
6770		const noPendingDataEmissions = idealTileIDs.length === 0 && !this._updated && this._didEmitContent;
6771		this._updated = true;
6772		if (noPendingDataEmissions) this.fire(new MapSourceDataEvent("data", {
6773			sourceDataType: "idle",
6774			sourceId: this.id
6775		}));
6776		const zoom = coveringZoomLevel(transform, this._source);
6777		const retain = this._updateRetainedTiles(idealTileIDs, zoom);
6778		const isRaster = isRasterType(this._source.type);
6779		if (isRaster && this._rasterFadeDuration > 0 && !terrain) updateFadingTiles(this._inViewTiles, idealTileIDs, retain, this._maxFadingAncestorLevels, this._source.minzoom, this._source.maxzoom, this._rasterFadeDuration);
6780		if (isRaster) this._cleanUpRasterTiles(retain);
6781		else this._cleanUpVectorTiles(retain);
6782	}
6783	/**
6784	* Remove raster tiles that are no longer retained
6785	*/
6786	_cleanUpRasterTiles(retain) {
6787		for (const id of this._inViewTiles.getAllIds()) if (!retain[id]) this._removeTile(id);
6788	}
6789	/**
6790	* Remove vector tiles that are no longer retained and also not needed for symbol fading
6791	*/
6792	_cleanUpVectorTiles(retain) {
6793		for (const id of this._inViewTiles.getAllIds()) {
6794			const tile = this._inViewTiles.getTileById(id);
6795			if (retain[id]) {
6796				tile.clearSymbolFadeHold();
6797				continue;
6798			}
6799			if (!tile.hasSymbolBuckets) {
6800				this._removeTile(id);
6801				continue;
6802			}
6803			if (!tile.holdingForSymbolFade()) tile.setSymbolHoldDuration(this.map._fadeDuration);
6804			else if (tile.symbolFadeFinished()) this._removeTile(id);
6805		}
6806	}
6807	/**
6808	* Add ideal tiles needed for 3D terrain rendering
6809	*/
6810	_addTerrainIdealTiles(idealTileIDs) {
6811		const ancestors = [];
6812		for (const tileID of idealTileIDs) if (tileID.canonical.z > this._source.minzoom) {
6813			const parent = tileID.scaledTo(tileID.canonical.z - 1);
6814			ancestors.push(parent);
6815			const parent2 = tileID.scaledTo(Math.max(this._source.minzoom, Math.min(tileID.canonical.z, 5)));
6816			ancestors.push(parent2);
6817		}
6818		return idealTileIDs.concat(ancestors);
6819	}
6820	releaseSymbolFadeTiles() {
6821		for (const id of this._inViewTiles.getAllIds()) if (this._inViewTiles.getTileById(id).holdingForSymbolFade()) this._removeTile(id);
6822	}
6823	/**
6824	* Set tiles to be retained on update of the source. For ideal tiles that do not have data, retain their loaded
6825	* children so they can be displayed as substitutes pending load of each ideal tile (to reduce flickering).
6826	* If no loaded children are available, fallback to seeking loaded parents as an alternative substitute.
6827	*/
6828	_updateRetainedTiles(idealTileIDs, zoom) {
6829		const idealTilesWithoutData = /* @__PURE__ */ new Set();
6830		for (const idealID of idealTileIDs) if (!this._addTile(idealID).hasData()) idealTilesWithoutData.add(idealID);
6831		const retainTileMap = idealTileIDs.reduce((acc, t) => {
6832			acc[t.key] = t;
6833			return acc;
6834		}, {});
6835		const tileIdsWithoutData = this._retainLoadedChildren(retainTileMap, idealTilesWithoutData);
6836		const checked = {};
6837		const minCoveringZoom = Math.max(zoom - TileManager.maxUnderzooming, this._source.minzoom);
6838		for (const tileID of tileIdsWithoutData) {
6839			let tile = this._inViewTiles.getTileById(tileID.key);
6840			let parentWasRequested = tile?.wasRequested();
6841			for (let overscaledZ = tileID.overscaledZ - 1; overscaledZ >= minCoveringZoom; --overscaledZ) {
6842				const parentId = tileID.scaledTo(overscaledZ);
6843				if (checked[parentId.key]) break;
6844				checked[parentId.key] = true;
6845				tile = this.getTile(parentId);
6846				if (!tile && parentWasRequested) tile = this._addTile(parentId);
6847				if (tile) {
6848					const hasData = tile.hasData();
6849					if (hasData || !this.map?.cancelPendingTileRequestsWhileZooming || parentWasRequested) retainTileMap[parentId.key] = parentId;
6850					parentWasRequested = tile.wasRequested();
6851					if (hasData) break;
6852				}
6853			}
6854		}
6855		return retainTileMap;
6856	}
6857	/**
6858	* Add a tile, given its coordinate, to the pyramid.
6859	*/
6860	_addTile(tileID) {
6861		let tile = this._inViewTiles.getTileById(tileID.key);
6862		if (tile) return tile;
6863		tile = this._outOfViewCache.getAndRemove(tileID);
6864		if (tile) {
6865			tile.resetFadeLogic();
6866			this._setTileReloadTimer(tileID.key, tile);
6867			tile.tileID = tileID;
6868			this._state.initializeTileState(tile, this.map ? this.map.painter : null);
6869		}
6870		const cached = tile;
6871		if (!tile) {
6872			tile = new Tile(tileID, this._source.tileSize * tileID.overscaleFactor());
6873			this._loadTile(tile, tileID.key, tile.state, false);
6874		}
6875		tile.uses++;
6876		this._inViewTiles.setTile(tileID.key, tile);
6877		if (!cached) this._source.fire(new MapSourceDataEvent("dataloading", {
6878			tile,
6879			coord: tile.tileID
6880		}));
6881		return tile;
6882	}
6883	/**
6884	* Set a timeout to reload the tile after it expires
6885	*/
6886	_setTileReloadTimer(id, tile) {
6887		this._clearTileReloadTimer(id);
6888		const expiryTimeout = tile.getExpiryTimeout();
6889		if (expiryTimeout) {
6890			const reload = () => {
6891				this._reloadTile(id, "expired");
6892				delete this._timers[id];
6893			};
6894			this._timers[id] = setTimeout(reload, expiryTimeout);
6895		}
6896	}
6897	_clearTileReloadTimer(id) {
6898		const timeout = this._timers[id];
6899		if (timeout) {
6900			clearTimeout(timeout);
6901			delete this._timers[id];
6902		}
6903	}
6904	_resetTileReloadTimers() {
6905		for (const id in this._timers) {
6906			clearTimeout(this._timers[id]);
6907			delete this._timers[id];
6908		}
6909		for (const id of this._inViewTiles.getAllIds()) {
6910			const tile = this._inViewTiles.getTileById(id);
6911			this._setTileReloadTimer(id, tile);
6912		}
6913	}
6914	/**
6915	* Reload any currently renderable tiles that are match one of the incoming `tileId` x/y/z
6916	*/
6917	refreshTiles(tileIds) {
6918		for (const id of this._inViewTiles.getAllIds()) {
6919			const tile = this._inViewTiles.getTileById(id);
6920			if (!this._inViewTiles.isIdRenderable(id) && tile.state != "errored") continue;
6921			if (tileIds.some((tid) => tid.equals(tile.tileID.canonical))) this._reloadTile(id, "expired");
6922		}
6923	}
6924	/**
6925	* Remove a tile, given its id, from the pyramid
6926	*/
6927	_removeTile(id) {
6928		const tile = this._inViewTiles.getTileById(id);
6929		if (!tile) return;
6930		tile.uses--;
6931		this._inViewTiles.deleteTileById(id);
6932		this._clearTileReloadTimer(id);
6933		if (tile.uses > 0) return;
6934		if (tile.hasData() && tile.state !== "reloading") this._outOfViewCache.add(tile.tileID, tile, tile.getExpiryTimeout());
6935		else {
6936			tile.aborted = true;
6937			this._abortTile(tile);
6938			this._unloadTile(tile);
6939		}
6940	}
6941	/** @internal
6942	* Handles incoming source data messages (i.e. after the source has been updated via a worker that has fired
6943	* to map.ts data event). For sources with mutable data, the 'content' event fires when the underlying data
6944	* to a source has changed. (i.e. GeoJSONSource.setData and ImageSource.setCoordinates)
6945	*/
6946	_dataHandler(e) {
6947		if (e.dataType !== "source") return;
6948		if (e.sourceDataType === "metadata") {
6949			this._sourceLoaded = true;
6950			return;
6951		}
6952		if (e.sourceDataType !== "content" || !this._sourceLoaded || this._paused) return;
6953		this.reload(e.sourceDataChanged, e.shouldReloadTileOptions);
6954		if (this.transform) this.update(this.transform, this.terrain);
6955		this._didEmitContent = true;
6956	}
6957	/**
6958	* Forgets the recorded maximum content elevation, so the next update recomputes it from the
6959	* current layers and loaded tiles. Called when a symbol layer is removed, since the removed
6960	* layer's heights would otherwise keep expanding tile coverage.
6961	*/
6962	resetMaxContentElevation() {
6963		this._maxContentElevationSeen = 0;
6964	}
6965	/**
6966	* Remove all tiles from this pyramid
6967	*/
6968	clearTiles() {
6969		this._shouldReloadOnResume = false;
6970		this._paused = false;
6971		this.resetMaxContentElevation();
6972		for (const id of this._inViewTiles.getAllIds()) this._removeTile(id);
6973		this._outOfViewCache.reset();
6974	}
6975	/**
6976	* Search through our current tiles and attempt to find the tiles that
6977	* cover the given bounds.
6978	* @param pointQueryGeometry - coordinates of the corners of bounding rectangle
6979	* @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.
6980	*/
6981	tilesIn(pointQueryGeometry, maxPitchScaleFactor, has3DLayer) {
6982		const tileResults = [];
6983		const transform = this.transform;
6984		if (!transform) return tileResults;
6985		const allowWorldCopies = transform.getCoveringTilesDetailsProvider().allowWorldCopies();
6986		const cameraPointQueryGeometry = has3DLayer ? transform.getCameraQueryGeometry(pointQueryGeometry) : pointQueryGeometry;
6987		const project = (point) => transform.screenPointToMercatorCoordinate(point, this.terrain);
6988		const queryGeometry = this.transformBbox(pointQueryGeometry, project, !allowWorldCopies);
6989		const cameraQueryGeometry = this.transformBbox(cameraPointQueryGeometry, project, !allowWorldCopies);
6990		const ids = this.getIds();
6991		const cameraBounds = Bounds.fromPoints(cameraQueryGeometry);
6992		for (const id of ids) {
6993			const tile = this._inViewTiles.getTileById(id);
6994			if (tile.holdingForSymbolFade()) continue;
6995			const tileIDs = allowWorldCopies ? [tile.tileID] : [tile.tileID.unwrapTo(-1), tile.tileID.unwrapTo(0)];
6996			const scale = Math.pow(2, transform.zoom - tile.tileID.overscaledZ);
6997			const queryPadding = maxPitchScaleFactor * tile.queryPadding * EXTENT / tile.tileSize / scale;
6998			for (const tileID of tileIDs) {
6999				const tileSpaceBounds = cameraBounds.map((point) => tileID.getTilePoint(new MercatorCoordinate(point.x, point.y)));
7000				tileSpaceBounds.expandBy(queryPadding);
7001				if (tileSpaceBounds.intersects(EXTENT_BOUNDS)) {
7002					const tileSpaceQueryGeometry = queryGeometry.map((c) => tileID.getTilePoint(c));
7003					const tileSpaceCameraQueryGeometry = cameraQueryGeometry.map((c) => tileID.getTilePoint(c));
7004					tileResults.push({
7005						tile,
7006						tileID: allowWorldCopies ? tileID : tileID.unwrapTo(0),
7007						queryGeometry: tileSpaceQueryGeometry,
7008						cameraQueryGeometry: tileSpaceCameraQueryGeometry,
7009						scale
7010					});
7011				}
7012			}
7013		}
7014		return tileResults;
7015	}
7016	transformBbox(geom, project, checkWrap) {
7017		let transformed = geom.map(project);
7018		if (checkWrap) {
7019			const bounds = Bounds.fromPoints(geom);
7020			bounds.shrinkBy(Math.min(bounds.width(), bounds.height()) * .001);
7021			const projected = bounds.map(project);
7022			if (!Bounds.fromPoints(transformed).covers(projected)) transformed = transformed.map((coord) => coord.x > .5 ? new MercatorCoordinate(coord.x - 1, coord.y, coord.z) : coord);
7023		}
7024		return transformed;
7025	}
7026	getVisibleCoordinates(symbolLayer) {
7027		const coords = this.getRenderableIds(symbolLayer).map((id) => this._inViewTiles.getTileById(id).tileID);
7028		if (this.transform) this.transform.populateCache(coords);
7029		return coords;
7030	}
7031	hasTransition() {
7032		if (this._source.hasTransition()) return true;
7033		return isRasterType(this._source.type) && hasRasterTransition(this._inViewTiles, this._rasterFadeDuration);
7034	}
7035	setRasterFadeDuration(fadeDuration) {
7036		this._rasterFadeDuration = fadeDuration;
7037	}
7038	/**
7039	* Set the value of a particular state for a feature
7040	*/
7041	setFeatureState(sourceLayer, featureId, state) {
7042		sourceLayer ||= GEOJSON_TILE_LAYER_NAME;
7043		this._state.updateState(sourceLayer, featureId, state);
7044	}
7045	/**
7046	* Resets the value of a particular state key for a feature
7047	*/
7048	removeFeatureState(sourceLayer, featureId, key) {
7049		sourceLayer ||= GEOJSON_TILE_LAYER_NAME;
7050		this._state.removeFeatureState(sourceLayer, featureId, key);
7051	}
7052	/**
7053	* Get the entire state object for a feature
7054	*/
7055	getFeatureState(sourceLayer, featureId) {
7056		sourceLayer ||= GEOJSON_TILE_LAYER_NAME;
7057		return this._state.getState(sourceLayer, featureId);
7058	}
7059	/**
7060	* Sets the set of keys that the tile depends on. This allows tiles to
7061	* be reloaded when their dependencies change.
7062	*/
7063	setDependencies(tileKey, namespace, dependencies) {
7064		const tile = this._inViewTiles.getTileById(tileKey);
7065		if (tile) tile.setDependencies(namespace, dependencies);
7066	}
7067	/**
7068	* Reloads all tiles that depend on the given keys.
7069	*/
7070	reloadTilesForDependencies(namespaces, keys) {
7071		for (const id of this._inViewTiles.getAllIds()) if (this._inViewTiles.getTileById(id).hasDependency(namespaces, keys)) this._reloadTile(id, "reloading");
7072		this._outOfViewCache.filter((tile) => !tile.hasDependency(namespaces, keys));
7073	}
7074	areTilesLoaded() {
7075		for (const tile of this._inViewTiles.getAllTiles()) if (!(tile.state === "loaded" || tile.state === "errored")) return false;
7076		return true;
7077	}
7078};
7079//#endregion
7080//#region src/symbol/path_interpolator.ts
7081var PathInterpolator = class {
7082	constructor(points_, padding_) {
7083		this.reset(points_, padding_);
7084	}
7085	reset(points_, padding_) {
7086		this.points = points_ || [];
7087		this._distances = [0];
7088		for (let i = 1; i < this.points.length; i++) this._distances[i] = this._distances[i - 1] + this.points[i].dist(this.points[i - 1]);
7089		this.length = this._distances[this._distances.length - 1];
7090		this.padding = Math.min(padding_ || 0, this.length * .5);
7091		this.paddedLength = this.length - this.padding * 2;
7092	}
7093	lerp(t) {
7094		if (this.points.length === 1) return this.points[0];
7095		t = clamp(t, 0, 1);
7096		let currentIndex = 1;
7097		let distOfCurrentIdx = this._distances[currentIndex];
7098		const distToTarget = t * this.paddedLength + this.padding;
7099		while (distOfCurrentIdx < distToTarget && currentIndex < this._distances.length) distOfCurrentIdx = this._distances[++currentIndex];
7100		const idxOfPrevPoint = currentIndex - 1;
7101		const distOfPrevIdx = this._distances[idxOfPrevPoint];
7102		const segmentLength = distOfCurrentIdx - distOfPrevIdx;
7103		const segmentT = segmentLength > 0 ? (distToTarget - distOfPrevIdx) / segmentLength : 0;
7104		return this.points[idxOfPrevPoint].mult(1 - segmentT).add(this.points[currentIndex].mult(segmentT));
7105	}
7106};
7107//#endregion
7108//#region src/symbol/symbol_elevation.ts
7109/**
7110* Resolves the elevation of a symbol at a tile coordinate, combining the terrain elevation with the
7111* symbol's `symbol-height-offset`. `symbol-height-anchor` selects the datum the offset is measured
7112* from: the terrain surface below the symbol (`ground`) or the zero elevation datum (`absolute`),
7113* in which case the terrain below the symbol is ignored.
7114*
7115* Call this where the elevation is needed, so that `getElevation` keeps returning the plain terrain
7116* elevation and never has to be substituted with a wrapper.
7117*
7118* @param getElevation - the terrain elevation function, absent when the map has no terrain
7119* @param x - the tile x coordinate to evaluate the elevation at
7120* @param y - the tile y coordinate to evaluate the elevation at
7121* @param heightOffset - the evaluated `symbol-height-offset` of the symbol, in meters
7122* @param heightAnchorGround - whether `symbol-height-anchor` is `ground`
7123* @returns the elevation in meters, or undefined when the symbol sits at the zero elevation datum,
7124* which lets the projection take its fast path that ignores the z coordinate
7125*/
7126function getSymbolElevation(getElevation, x, y, heightOffset, heightAnchorGround) {
7127	if (!heightAnchorGround) return heightOffset;
7128	if (!getElevation) return heightOffset !== 0 ? heightOffset : void 0;
7129	return getElevation(x, y) + heightOffset;
7130}
7131//#endregion
7132//#region src/symbol/grid_index.ts
7133function overlapAllowed(overlapA, overlapB) {
7134	let allowed = true;
7135	if (overlapA === "always") {} else if (overlapA === "never" || overlapB === "never") allowed = false;
7136	return allowed;
7137}
7138/**
7139* @internal
7140* GridIndex is a data structure for testing the intersection of
7141* circles and rectangles in a 2d plane.
7142* It is optimized for rapid insertion and querying.
7143* GridIndex splits the plane into a set of "cells" and keeps track
7144* of which geometries intersect with each cell. At query time,
7145* full geometry comparisons are only done for items that share
7146* at least one cell. As long as the geometries are relatively
7147* uniformly distributed across the plane, this greatly reduces
7148* the number of comparisons necessary.
7149*/
7150var GridIndex = class {
7151	constructor(width, height, cellSize) {
7152		const boxCells = this.boxCells = [];
7153		const circleCells = this.circleCells = [];
7154		this.xCellCount = Math.ceil(width / cellSize);
7155		this.yCellCount = Math.ceil(height / cellSize);
7156		for (let i = 0; i < this.xCellCount * this.yCellCount; i++) {
7157			boxCells.push([]);
7158			circleCells.push([]);
7159		}
7160		this.circleKeys = [];
7161		this.boxKeys = [];
7162		this.bboxes = [];
7163		this.circles = [];
7164		this.width = width;
7165		this.height = height;
7166		this.xScale = this.xCellCount / width;
7167		this.yScale = this.yCellCount / height;
7168		this.boxUid = 0;
7169		this.circleUid = 0;
7170	}
7171	keysLength() {
7172		return this.boxKeys.length + this.circleKeys.length;
7173	}
7174	insert(key, x1, y1, x2, y2) {
7175		this._forEachCell(x1, y1, x2, y2, this._insertBoxCell, this.boxUid++);
7176		this.boxKeys.push(key);
7177		this.bboxes.push(x1);
7178		this.bboxes.push(y1);
7179		this.bboxes.push(x2);
7180		this.bboxes.push(y2);
7181	}
7182	insertCircle(key, x, y, radius) {
7183		this._forEachCell(x - radius, y - radius, x + radius, y + radius, this._insertCircleCell, this.circleUid++);
7184		this.circleKeys.push(key);
7185		this.circles.push(x);
7186		this.circles.push(y);
7187		this.circles.push(radius);
7188	}
7189	_insertBoxCell(x1, y1, x2, y2, cellIndex, uid) {
7190		this.boxCells[cellIndex].push(uid);
7191	}
7192	_insertCircleCell(x1, y1, x2, y2, cellIndex, uid) {
7193		this.circleCells[cellIndex].push(uid);
7194	}
7195	_query(x1, y1, x2, y2, hitTest, overlapMode, predicate) {
7196		if (x2 < 0 || x1 > this.width || y2 < 0 || y1 > this.height) return [];
7197		const result = [];
7198		if (x1 <= 0 && y1 <= 0 && this.width <= x2 && this.height <= y2) {
7199			if (hitTest) return [{
7200				key: null,
7201				x1,
7202				y1,
7203				x2,
7204				y2
7205			}];
7206			for (let boxUid = 0; boxUid < this.boxKeys.length; boxUid++) result.push({
7207				key: this.boxKeys[boxUid],
7208				x1: this.bboxes[boxUid * 4],
7209				y1: this.bboxes[boxUid * 4 + 1],
7210				x2: this.bboxes[boxUid * 4 + 2],
7211				y2: this.bboxes[boxUid * 4 + 3]
7212			});
7213			for (let circleUid = 0; circleUid < this.circleKeys.length; circleUid++) {
7214				const x = this.circles[circleUid * 3];
7215				const y = this.circles[circleUid * 3 + 1];
7216				const radius = this.circles[circleUid * 3 + 2];
7217				result.push({
7218					key: this.circleKeys[circleUid],
7219					x1: x - radius,
7220					y1: y - radius,
7221					x2: x + radius,
7222					y2: y + radius
7223				});
7224			}
7225		} else {
7226			const queryArgs = {
7227				hitTest,
7228				overlapMode,
7229				seenUids: {
7230					box: {},
7231					circle: {}
7232				}
7233			};
7234			this._forEachCell(x1, y1, x2, y2, this._queryCell, result, queryArgs, predicate);
7235		}
7236		return result;
7237	}
7238	query(x1, y1, x2, y2) {
7239		return this._query(x1, y1, x2, y2, false, null);
7240	}
7241	hitTest(x1, y1, x2, y2, overlapMode, predicate) {
7242		return this._query(x1, y1, x2, y2, true, overlapMode, predicate).length > 0;
7243	}
7244	hitTestCircle(x, y, radius, overlapMode, predicate) {
7245		const x1 = x - radius;
7246		const x2 = x + radius;
7247		const y1 = y - radius;
7248		const y2 = y + radius;
7249		if (x2 < 0 || x1 > this.width || y2 < 0 || y1 > this.height) return false;
7250		const result = [];
7251		const queryArgs = {
7252			hitTest: true,
7253			overlapMode,
7254			circle: {
7255				x,
7256				y,
7257				radius
7258			},
7259			seenUids: {
7260				box: {},
7261				circle: {}
7262			}
7263		};
7264		this._forEachCell(x1, y1, x2, y2, this._queryCellCircle, result, queryArgs, predicate);
7265		return result.length > 0;
7266	}
7267	_queryCell(x1, y1, x2, y2, cellIndex, result, queryArgs, predicate) {
7268		const { seenUids, hitTest, overlapMode } = queryArgs;
7269		const boxCell = this.boxCells[cellIndex];
7270		const epsilon = 1e-6;
7271		if (boxCell !== null) {
7272			const bboxes = this.bboxes;
7273			for (const boxUid of boxCell) if (!seenUids.box[boxUid]) {
7274				seenUids.box[boxUid] = true;
7275				const offset = boxUid * 4;
7276				const key = this.boxKeys[boxUid];
7277				if (x1 <= bboxes[offset + 2] + epsilon && y1 <= bboxes[offset + 3] + epsilon && x2 >= bboxes[offset + 0] - epsilon && y2 >= bboxes[offset + 1] - epsilon && (!predicate || predicate(key))) {
7278					if (!hitTest || !overlapAllowed(overlapMode, key.overlapMode)) {
7279						result.push({
7280							key,
7281							x1: bboxes[offset],
7282							y1: bboxes[offset + 1],
7283							x2: bboxes[offset + 2],
7284							y2: bboxes[offset + 3]
7285						});
7286						if (hitTest) return true;
7287					}
7288				}
7289			}
7290		}
7291		const circleCell = this.circleCells[cellIndex];
7292		if (circleCell !== null) {
7293			const circles = this.circles;
7294			for (const circleUid of circleCell) if (!seenUids.circle[circleUid]) {
7295				seenUids.circle[circleUid] = true;
7296				const offset = circleUid * 3;
7297				const key = this.circleKeys[circleUid];
7298				if (this._circleAndRectCollide(circles[offset], circles[offset + 1], circles[offset + 2], x1, y1, x2, y2) && (!predicate || predicate(key))) {
7299					if (!hitTest || !overlapAllowed(overlapMode, key.overlapMode)) {
7300						const x = circles[offset];
7301						const y = circles[offset + 1];
7302						const radius = circles[offset + 2];
7303						result.push({
7304							key,
7305							x1: x - radius,
7306							y1: y - radius,
7307							x2: x + radius,
7308							y2: y + radius
7309						});
7310						if (hitTest) return true;
7311					}
7312				}
7313			}
7314		}
7315		return false;
7316	}
7317	_queryCellCircle(x1, y1, x2, y2, cellIndex, result, queryArgs, predicate) {
7318		const { circle, seenUids, overlapMode } = queryArgs;
7319		const boxCell = this.boxCells[cellIndex];
7320		if (boxCell !== null) {
7321			const bboxes = this.bboxes;
7322			for (const boxUid of boxCell) if (!seenUids.box[boxUid]) {
7323				seenUids.box[boxUid] = true;
7324				const offset = boxUid * 4;
7325				const key = this.boxKeys[boxUid];
7326				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)) {
7327					result.push(true);
7328					return true;
7329				}
7330			}
7331		}
7332		const circleCell = this.circleCells[cellIndex];
7333		if (circleCell !== null) {
7334			const circles = this.circles;
7335			for (const circleUid of circleCell) if (!seenUids.circle[circleUid]) {
7336				seenUids.circle[circleUid] = true;
7337				const offset = circleUid * 3;
7338				const key = this.circleKeys[circleUid];
7339				if (this._circlesCollide(circles[offset], circles[offset + 1], circles[offset + 2], circle.x, circle.y, circle.radius) && (!predicate || predicate(key)) && !overlapAllowed(overlapMode, key.overlapMode)) {
7340					result.push(true);
7341					return true;
7342				}
7343			}
7344		}
7345	}
7346	_forEachCell(x1, y1, x2, y2, fn, arg1, arg2, predicate) {
7347		const cx1 = this._convertToXCellCoord(x1);
7348		const cy1 = this._convertToYCellCoord(y1);
7349		const cx2 = this._convertToXCellCoord(x2);
7350		const cy2 = this._convertToYCellCoord(y2);
7351		for (let x = cx1; x <= cx2; x++) for (let y = cy1; y <= cy2; y++) {
7352			const cellIndex = this.xCellCount * y + x;
7353			if (fn.call(this, x1, y1, x2, y2, cellIndex, arg1, arg2, predicate)) return;
7354		}
7355	}
7356	_convertToXCellCoord(x) {
7357		return Math.max(0, Math.min(this.xCellCount - 1, Math.floor(x * this.xScale)));
7358	}
7359	_convertToYCellCoord(y) {
7360		return Math.max(0, Math.min(this.yCellCount - 1, Math.floor(y * this.yScale)));
7361	}
7362	_circlesCollide(x1, y1, r1, x2, y2, r2) {
7363		const dx = x2 - x1;
7364		const dy = y2 - y1;
7365		const bothRadii = r1 + r2;
7366		return bothRadii * bothRadii > dx * dx + dy * dy;
7367	}
7368	_circleAndRectCollide(circleX, circleY, radius, x1, y1, x2, y2) {
7369		const halfRectWidth = (x2 - x1) / 2;
7370		const distX = Math.abs(circleX - (x1 + halfRectWidth));
7371		if (distX > halfRectWidth + radius) return false;
7372		const halfRectHeight = (y2 - y1) / 2;
7373		const distY = Math.abs(circleY - (y1 + halfRectHeight));
7374		if (distY > halfRectHeight + radius) return false;
7375		if (distX <= halfRectWidth || distY <= halfRectHeight) return true;
7376		const dx = distX - halfRectWidth;
7377		const dy = distY - halfRectHeight;
7378		return dx * dx + dy * dy <= radius * radius;
7379	}
7380};
7381//#endregion
7382//#region src/util/fast_maths.ts
7383function fastInvertTransformMat4(dst, src) {
7384	const sXYSqInv = 1 / (src[0] * src[0] + src[1] * src[1] + src[2] * src[2]);
7385	const sZSqInv = 1 / (src[8] * src[8] + src[9] * src[9] + src[10] * src[10]);
7386	const s0 = src[0] * sXYSqInv;
7387	const s4 = src[4] * sXYSqInv;
7388	const s8 = src[8] * sZSqInv;
7389	const s1 = src[1] * sXYSqInv;
7390	const s5 = src[5] * sXYSqInv;
7391	const s9 = src[9] * sZSqInv;
7392	const s2 = src[2] * sXYSqInv;
7393	const s6 = src[6] * sXYSqInv;
7394	const s10 = src[10] * sZSqInv;
7395	dst[0] = s0;
7396	dst[1] = s4;
7397	dst[2] = s8;
7398	dst[4] = s1;
7399	dst[5] = s5;
7400	dst[6] = s9;
7401	dst[8] = s2;
7402	dst[9] = s6;
7403	dst[10] = s10;
7404	const t0 = src[12];
7405	const t1 = src[13];
7406	const t2 = src[14];
7407	dst[12] = -s0 * t0 - s1 * t1 - s2 * t2;
7408	dst[13] = -s4 * t0 - s5 * t1 - s6 * t2;
7409	dst[14] = -s8 * t0 - s9 * t1 - s10 * t2;
7410	dst[3] = 0;
7411	dst[7] = 0;
7412	dst[11] = 0;
7413	dst[15] = 1;
7414	return dst;
7415}
7416function fastInvertProjMat4(dst, src) {
7417	dst[0] = 1 / src[0];
7418	dst[1] = 0;
7419	dst[2] = 0;
7420	dst[3] = 0;
7421	dst[4] = 0;
7422	dst[5] = 1 / src[5];
7423	dst[6] = 0;
7424	dst[7] = 0;
7425	dst[8] = 0;
7426	dst[9] = 0;
7427	dst[10] = 0;
7428	dst[11] = 1 / src[14];
7429	dst[12] = 0;
7430	dst[13] = 0;
7431	dst[14] = -1;
7432	dst[15] = src[10] / src[14];
7433	return dst;
7434}
7435function fastInvertSkewMat4(dst, src) {
7436	const invDetXY = 1 / (src[0] * src[5] - src[1] * src[4]);
7437	dst[0] = src[5] * invDetXY;
7438	dst[1] = -src[1] * invDetXY;
7439	dst[2] = 0;
7440	dst[3] = 0;
7441	dst[4] = -src[4] * invDetXY;
7442	dst[5] = src[0] * invDetXY;
7443	dst[6] = 0;
7444	dst[7] = 0;
7445	dst[8] = 0;
7446	dst[9] = 0;
7447	dst[10] = 1 / src[10];
7448	dst[11] = 0;
7449	dst[12] = 0;
7450	dst[13] = 0;
7451	dst[14] = 0;
7452	dst[15] = 1 / src[15];
7453	return dst;
7454}
7455//#endregion
7456//#region src/symbol/projection.ts
7457/**
7458* Pre-allocate objects to avoid online allocation
7459*/
7460const tmpMat4 = create$1();
7461function getPitchedLabelPlaneMatrix(rotateWithMap, transform, pixelsToTileUnits) {
7462	const m = create$1();
7463	if (!rotateWithMap) {
7464		const { vecSouth, vecEast } = getTileSkewVectors(transform);
7465		const skew = create$2();
7466		skew[0] = vecEast[0];
7467		skew[1] = vecEast[1];
7468		skew[2] = vecSouth[0];
7469		skew[3] = vecSouth[1];
7470		invert(skew, skew);
7471		m[0] = skew[0];
7472		m[1] = skew[1];
7473		m[4] = skew[2];
7474		m[5] = skew[3];
7475	}
7476	scale(m, m, [
7477		1 / pixelsToTileUnits,
7478		1 / pixelsToTileUnits,
7479		1
7480	]);
7481	return m;
7482}
7483function getGlCoordMatrix(pitchWithMap, rotateWithMap, transform, pixelsToTileUnits) {
7484	if (pitchWithMap) {
7485		const m = create$1();
7486		if (!rotateWithMap) {
7487			const { vecSouth, vecEast } = getTileSkewVectors(transform);
7488			m[0] = vecEast[0];
7489			m[1] = vecEast[1];
7490			m[4] = vecSouth[0];
7491			m[5] = vecSouth[1];
7492		}
7493		scale(m, m, [
7494			pixelsToTileUnits,
7495			pixelsToTileUnits,
7496			1
7497		]);
7498		return m;
7499	} else return transform.pixelsToClipSpaceMatrix;
7500}
7501function getTileSkewVectors(transform) {
7502	const cosRoll = Math.cos(transform.rollInRadians);
7503	const sinRoll = Math.sin(transform.rollInRadians);
7504	const cosPitch = Math.cos(transform.pitchInRadians);
7505	const cosBearing = Math.cos(transform.bearingInRadians);
7506	const sinBearing = Math.sin(transform.bearingInRadians);
7507	const vecSouth = create$3();
7508	vecSouth[0] = -cosBearing * cosPitch * sinRoll - sinBearing * cosRoll;
7509	vecSouth[1] = -sinBearing * cosPitch * sinRoll + cosBearing * cosRoll;
7510	const vecSouthLen = length$1(vecSouth);
7511	if (vecSouthLen < 1e-9) zero(vecSouth);
7512	else scale$3(vecSouth, vecSouth, 1 / vecSouthLen);
7513	const vecEast = create$3();
7514	vecEast[0] = cosBearing * cosPitch * cosRoll - sinBearing * sinRoll;
7515	vecEast[1] = sinBearing * cosPitch * cosRoll + cosBearing * sinRoll;
7516	const vecEastLen = length$1(vecEast);
7517	if (vecEastLen < 1e-9) zero(vecEast);
7518	else scale$3(vecEast, vecEast, 1 / vecEastLen);
7519	return {
7520		vecEast,
7521		vecSouth
7522	};
7523}
7524/**
7525* Resolves the elevation of the symbol described by `projectionContext` at a tile coordinate.
7526*/
7527function elevationAt(projectionContext, x, y) {
7528	return getSymbolElevation(projectionContext.getElevation, x, y, projectionContext.heightOffset ?? 0, projectionContext.heightAnchorGround ?? true);
7529}
7530/**
7531* Projects a point using a specified matrix, including the perspective divide.
7532* Uses a fast path if `elevation` is undefined.
7533*/
7534function projectWithMatrix(x, y, matrix, elevation) {
7535	let pos;
7536	if (elevation != null) {
7537		pos = [
7538			x,
7539			y,
7540			elevation,
7541			1
7542		];
7543		transformMat4(pos, pos, matrix);
7544	} else {
7545		pos = [
7546			x,
7547			y,
7548			0,
7549			1
7550		];
7551		xyTransformMat4(pos, pos, matrix);
7552	}
7553	const w = pos[3];
7554	return {
7555		point: new Point(pos[0] / w, pos[1] / w),
7556		signedDistanceFromCamera: w,
7557		isOccluded: false
7558	};
7559}
7560function getPerspectiveRatio(cameraToCenterDistance, signedDistanceFromCamera) {
7561	return .5 + .5 * (cameraToCenterDistance / signedDistanceFromCamera);
7562}
7563function isVisible(p, clippingBuffer) {
7564	return p.x >= -clippingBuffer[0] && p.x <= clippingBuffer[0] && p.y >= -clippingBuffer[1] && p.y <= clippingBuffer[1];
7565}
7566function updateLineLabels(bucket, painter, isText, pitchedLabelPlaneMatrix, pitchedLabelPlaneMatrixInverse, pitchWithMap, keepUpright, rotateToLine, unwrappedTileID, viewportWidth, viewportHeight, translation, getElevation) {
7567	const sizeData = isText ? bucket.textSizeData : bucket.iconSizeData;
7568	const partiallyEvaluatedSize = evaluateSizeForZoom(sizeData, painter.transform.zoom);
7569	const clippingBuffer = [256 / painter.width * 2 + 1, 256 / painter.height * 2 + 1];
7570	const dynamicLayoutVertexArray = isText ? bucket.text.dynamicLayoutVertexArray : bucket.icon.dynamicLayoutVertexArray;
7571	dynamicLayoutVertexArray.clear();
7572	const lineVertexArray = bucket.lineVertexArray;
7573	const placedSymbols = isText ? bucket.text.placedSymbolArray : bucket.icon.placedSymbolArray;
7574	const aspectRatio = painter.transform.width / painter.transform.height;
7575	let useVertical = false;
7576	for (let s = 0; s < placedSymbols.length; s++) {
7577		const symbol = placedSymbols.get(s);
7578		if (symbol.hidden || symbol.writingMode === 2 && !useVertical) {
7579			hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
7580			continue;
7581		}
7582		useVertical = false;
7583		const tileAnchorPoint = new Point(symbol.anchorX, symbol.anchorY);
7584		const projectionContext = {
7585			getElevation,
7586			pitchedLabelPlaneMatrix,
7587			lineVertexArray,
7588			pitchWithMap,
7589			projectionCache: {
7590				projections: {},
7591				offsets: {},
7592				cachedAnchorPoint: void 0,
7593				anyProjectionOccluded: false
7594			},
7595			transform: painter.transform,
7596			tileAnchorPoint,
7597			unwrappedTileID,
7598			width: viewportWidth,
7599			height: viewportHeight,
7600			translation
7601		};
7602		const anchorPos = projectTileCoordinatesToClipSpace(symbol.anchorX, symbol.anchorY, projectionContext);
7603		if (!isVisible(anchorPos.point, clippingBuffer)) {
7604			hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
7605			continue;
7606		}
7607		const cameraToAnchorDistance = anchorPos.signedDistanceFromCamera;
7608		const perspectiveRatio = getPerspectiveRatio(painter.transform.cameraToCenterDistance, cameraToAnchorDistance);
7609		const fontSize = evaluateSizeForFeature(sizeData, partiallyEvaluatedSize, symbol);
7610		const pitchScaledFontSize = pitchWithMap ? fontSize * painter.transform.getPitchedTextCorrection(symbol.anchorX, symbol.anchorY, unwrappedTileID) / perspectiveRatio : fontSize * perspectiveRatio;
7611		const placeUnflipped = placeGlyphsAlongLine({
7612			projectionContext,
7613			pitchedLabelPlaneMatrixInverse,
7614			symbol,
7615			fontSize: pitchScaledFontSize,
7616			flip: false,
7617			keepUpright,
7618			glyphOffsetArray: bucket.glyphOffsetArray,
7619			dynamicLayoutVertexArray,
7620			aspectRatio,
7621			rotateToLine
7622		});
7623		useVertical = placeUnflipped.useVertical;
7624		if (placeUnflipped.notEnoughRoom || useVertical || placeUnflipped.needsFlipping && placeGlyphsAlongLine({
7625			projectionContext,
7626			pitchedLabelPlaneMatrixInverse,
7627			symbol,
7628			fontSize: pitchScaledFontSize,
7629			flip: true,
7630			keepUpright,
7631			glyphOffsetArray: bucket.glyphOffsetArray,
7632			dynamicLayoutVertexArray,
7633			aspectRatio,
7634			rotateToLine
7635		}).notEnoughRoom) hideGlyphs(symbol.numGlyphs, dynamicLayoutVertexArray);
7636	}
7637	if (isText) bucket.text.dynamicLayoutVertexBuffer.updateData(dynamicLayoutVertexArray);
7638	else bucket.icon.dynamicLayoutVertexBuffer.updateData(dynamicLayoutVertexArray);
7639}
7640function placeFirstAndLastGlyph(fontScale, glyphOffsetArray, lineOffsetX, lineOffsetY, flip, symbol, rotateToLine, projectionContext) {
7641	const glyphEndIndex = symbol.glyphStartIndex + symbol.numGlyphs;
7642	const lineStartIndex = symbol.lineStartIndex;
7643	const lineEndIndex = symbol.lineStartIndex + symbol.lineLength;
7644	const firstGlyphOffset = glyphOffsetArray.getoffsetX(symbol.glyphStartIndex);
7645	const lastGlyphOffset = glyphOffsetArray.getoffsetX(glyphEndIndex - 1);
7646	const firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, symbol.segment, lineStartIndex, lineEndIndex, projectionContext, rotateToLine);
7647	if (!firstPlacedGlyph) return null;
7648	const lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, symbol.segment, lineStartIndex, lineEndIndex, projectionContext, rotateToLine);
7649	if (!lastPlacedGlyph) return null;
7650	if (projectionContext.projectionCache.anyProjectionOccluded) return null;
7651	return {
7652		first: firstPlacedGlyph,
7653		last: lastPlacedGlyph
7654	};
7655}
7656function requiresOrientationChange(writingMode, firstPoint, lastPoint, aspectRatio) {
7657	if (writingMode === 1) {
7658		if (Math.abs(lastPoint.y - firstPoint.y) > Math.abs(lastPoint.x - firstPoint.x) * aspectRatio) return { useVertical: true };
7659	}
7660	if (writingMode === 2 ? firstPoint.y < lastPoint.y : firstPoint.x > lastPoint.x) return { needsFlipping: true };
7661	return null;
7662}
7663function placeGlyphsAlongLine(args) {
7664	const { projectionContext, pitchedLabelPlaneMatrixInverse, symbol, fontSize, flip, keepUpright, glyphOffsetArray, dynamicLayoutVertexArray, aspectRatio, rotateToLine } = args;
7665	const fontScale = fontSize / 24;
7666	const lineOffsetX = symbol.lineOffsetX * fontScale;
7667	const lineOffsetY = symbol.lineOffsetY * fontScale;
7668	let placedGlyphs;
7669	if (symbol.numGlyphs > 1) {
7670		const glyphEndIndex = symbol.glyphStartIndex + symbol.numGlyphs;
7671		const lineStartIndex = symbol.lineStartIndex;
7672		const lineEndIndex = symbol.lineStartIndex + symbol.lineLength;
7673		const firstAndLastGlyph = placeFirstAndLastGlyph(fontScale, glyphOffsetArray, lineOffsetX, lineOffsetY, flip, symbol, rotateToLine, projectionContext);
7674		if (!firstAndLastGlyph) return { notEnoughRoom: true };
7675		const firstPoint = projectFromLabelPlaneToClipSpace(firstAndLastGlyph.first.point.x, firstAndLastGlyph.first.point.y, projectionContext, pitchedLabelPlaneMatrixInverse);
7676		const lastPoint = projectFromLabelPlaneToClipSpace(firstAndLastGlyph.last.point.x, firstAndLastGlyph.last.point.y, projectionContext, pitchedLabelPlaneMatrixInverse);
7677		if (keepUpright && !flip) {
7678			const orientationChange = requiresOrientationChange(symbol.writingMode, firstPoint, lastPoint, aspectRatio);
7679			if (orientationChange) return orientationChange;
7680		}
7681		placedGlyphs = [firstAndLastGlyph.first];
7682		for (let glyphIndex = symbol.glyphStartIndex + 1; glyphIndex < glyphEndIndex - 1; glyphIndex++) {
7683			const placedGlyph = placeGlyphAlongLine(fontScale * glyphOffsetArray.getoffsetX(glyphIndex), lineOffsetX, lineOffsetY, flip, symbol.segment, lineStartIndex, lineEndIndex, projectionContext, rotateToLine);
7684			if (!placedGlyph) return { notEnoughRoom: true };
7685			placedGlyphs.push(placedGlyph);
7686		}
7687		placedGlyphs.push(firstAndLastGlyph.last);
7688	} else {
7689		if (keepUpright && !flip) {
7690			const a = projectTileCoordinatesToLabelPlane(projectionContext.tileAnchorPoint.x, projectionContext.tileAnchorPoint.y, projectionContext).point;
7691			const tileVertexIndex = symbol.lineStartIndex + symbol.segment + 1;
7692			const tileSegmentEnd = new Point(projectionContext.lineVertexArray.getx(tileVertexIndex), projectionContext.lineVertexArray.gety(tileVertexIndex));
7693			const projectedVertex = projectTileCoordinatesToLabelPlane(tileSegmentEnd.x, tileSegmentEnd.y, projectionContext);
7694			const b = projectedVertex.signedDistanceFromCamera > 0 ? projectedVertex.point : projectTruncatedLineSegmentToLabelPlane(projectionContext.tileAnchorPoint, tileSegmentEnd, a, 1, projectionContext);
7695			const clipSpaceA = projectFromLabelPlaneToClipSpace(a.x, a.y, projectionContext, pitchedLabelPlaneMatrixInverse);
7696			const clipSpaceB = projectFromLabelPlaneToClipSpace(b.x, b.y, projectionContext, pitchedLabelPlaneMatrixInverse);
7697			const orientationChange = requiresOrientationChange(symbol.writingMode, clipSpaceA, clipSpaceB, aspectRatio);
7698			if (orientationChange) return orientationChange;
7699		}
7700		const singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetArray.getoffsetX(symbol.glyphStartIndex), lineOffsetX, lineOffsetY, flip, symbol.segment, symbol.lineStartIndex, symbol.lineStartIndex + symbol.lineLength, projectionContext, rotateToLine);
7701		if (!singleGlyph || projectionContext.projectionCache.anyProjectionOccluded) return { notEnoughRoom: true };
7702		placedGlyphs = [singleGlyph];
7703	}
7704	for (const glyph of placedGlyphs) addDynamicAttributes(dynamicLayoutVertexArray, glyph.point, glyph.angle);
7705	return {};
7706}
7707/**
7708* Takes a line and direction from `previousTilePoint` to `currentTilePoint`, projects it to the correct label plane,
7709* and returns a projected point along this projected line that is `minimumLength` distance away from `previousProjectedPoint`.
7710* Projects a "virtual" vertex along a line segment.
7711* @param previousTilePoint - Line start point, in tile coordinates.
7712* @param currentTilePoint - Line end point, in tile coordinates.
7713* @param previousProjectedPoint - Projection of `previousTilePoint` into label plane
7714* @param minimumLength - Distance in the projected space along the line for the returned point.
7715* @param projectionContext - Projection context, used to get terrain's `getElevation`, and to project the points to screen pixels.
7716*/
7717function projectTruncatedLineSegmentToLabelPlane(previousTilePoint, currentTilePoint, previousProjectedPoint, minimumLength, projectionContext) {
7718	const unitVertexToBeProjected = previousTilePoint.add(previousTilePoint.sub(currentTilePoint)._unit());
7719	const projectedUnitVertex = projectTileCoordinatesToLabelPlane(unitVertexToBeProjected.x, unitVertexToBeProjected.y, projectionContext).point;
7720	const projectedUnitSegment = previousProjectedPoint.sub(projectedUnitVertex);
7721	return previousProjectedPoint.add(projectedUnitSegment._mult(minimumLength / projectedUnitSegment.mag()));
7722}
7723/**
7724* Transform a vertex from tile coordinates to label plane coordinates
7725* @param index - index of vertex to project
7726* @param projectionContext - necessary data to project a vertex
7727* @returns the vertex projected to the label plane
7728*/
7729function projectLineVertexToLabelPlane(index, projectionContext, syntheticVertexArgs) {
7730	const cache = projectionContext.projectionCache;
7731	if (cache.projections[index]) return cache.projections[index];
7732	const currentVertex = new Point(projectionContext.lineVertexArray.getx(index), projectionContext.lineVertexArray.gety(index));
7733	const projection = projectTileCoordinatesToLabelPlane(currentVertex.x, currentVertex.y, projectionContext);
7734	if (projection.signedDistanceFromCamera > 0) {
7735		cache.projections[index] = projection.point;
7736		cache.anyProjectionOccluded ||= projection.isOccluded;
7737		return projection.point;
7738	}
7739	const previousLineVertexIndex = index - syntheticVertexArgs.direction;
7740	const previousTilePoint = syntheticVertexArgs.distanceFromAnchor === 0 ? projectionContext.tileAnchorPoint : new Point(projectionContext.lineVertexArray.getx(previousLineVertexIndex), projectionContext.lineVertexArray.gety(previousLineVertexIndex));
7741	const minimumLength = syntheticVertexArgs.absOffsetX - syntheticVertexArgs.distanceFromAnchor + 1;
7742	return projectTruncatedLineSegmentToLabelPlane(previousTilePoint, currentVertex, syntheticVertexArgs.previousVertex, minimumLength, projectionContext);
7743}
7744/**
7745* Projects the given point in tile coordinates to the correct label plane.
7746* If pitchWithMap is true, the (rotated) map plane in pixels is used,
7747* otherwise screen pixels are used.
7748*/
7749function projectTileCoordinatesToLabelPlane(x, y, projectionContext) {
7750	const translatedX = x + projectionContext.translation[0];
7751	const translatedY = y + projectionContext.translation[1];
7752	let projection;
7753	if (projectionContext.pitchWithMap) {
7754		projection = projectWithMatrix(translatedX, translatedY, projectionContext.pitchedLabelPlaneMatrix, elevationAt(projectionContext, translatedX, translatedY));
7755		projection.isOccluded = false;
7756	} else {
7757		projection = projectionContext.transform.projectTileCoordinates(translatedX, translatedY, projectionContext.unwrappedTileID, elevationAt(projectionContext, translatedX, translatedY));
7758		projection.point.x = (projection.point.x * .5 + .5) * projectionContext.width;
7759		projection.point.y = (-projection.point.y * .5 + .5) * projectionContext.height;
7760	}
7761	return projection;
7762}
7763function projectFromLabelPlaneToClipSpace(x, y, projectionContext, pitchedLabelPlaneMatrixInverse) {
7764	if (projectionContext.pitchWithMap) {
7765		const pos = [
7766			x,
7767			y,
7768			0,
7769			1
7770		];
7771		transformMat4(pos, pos, pitchedLabelPlaneMatrixInverse);
7772		const tileX = pos[0] / pos[3];
7773		const tileY = pos[1] / pos[3];
7774		return projectionContext.transform.projectTileCoordinates(tileX, tileY, projectionContext.unwrappedTileID, elevationAt(projectionContext, tileX, tileY)).point;
7775	} else return {
7776		x: x / projectionContext.width * 2 - 1,
7777		y: 1 - y / projectionContext.height * 2
7778	};
7779}
7780/**
7781* Projects the given point in tile coordinates to the GL clip space (-1..1).
7782*/
7783function projectTileCoordinatesToClipSpace(x, y, projectionContext) {
7784	return projectionContext.transform.projectTileCoordinates(x, y, projectionContext.unwrappedTileID, elevationAt(projectionContext, x, y));
7785}
7786/**
7787* Calculate the normal vector for a line segment
7788* @param segmentVector - will be mutated as a tiny optimization
7789* @param offset - magnitude of resulting vector
7790* @param direction - direction of line traversal
7791* @returns a normal vector from the segment, with magnitude equal to offset amount
7792*/
7793function transformToOffsetNormal(segmentVector, offset, direction) {
7794	return segmentVector._unit()._perp()._mult(offset * direction);
7795}
7796/**
7797* Construct offset line segments for the current segment and the next segment, then extend/shrink
7798* the segments until they intersect. If the segments are parallel, then they will touch with no modification.
7799*
7800* @param index - Index of the current vertex
7801* @param prevToCurrentOffsetNormal - Normal vector of the line segment from the previous vertex to the current vertex
7802* @param currentVertex - Current (non-offset) vertex projected to the label plane
7803* @param lineStartIndex - Beginning index for the line this label is on
7804* @param lineEndIndex - End index for the line this label is on
7805* @param offsetPreviousVertex - The previous vertex projected to the label plane, and then offset along the previous segments normal
7806* @param lineOffsetY - Magnitude of the offset
7807* @param projectionContext - Necessary data for tile-to-label-plane projection
7808* @returns The point at which the current and next line segments intersect, once offset and extended/shrunk to their meeting point
7809*/
7810function findOffsetIntersectionPoint(index, prevToCurrentOffsetNormal, currentVertex, lineStartIndex, lineEndIndex, offsetPreviousVertex, lineOffsetY, projectionContext, syntheticVertexArgs) {
7811	if (projectionContext.projectionCache.offsets[index]) return projectionContext.projectionCache.offsets[index];
7812	const offsetCurrentVertex = currentVertex.add(prevToCurrentOffsetNormal);
7813	if (index + syntheticVertexArgs.direction < lineStartIndex || index + syntheticVertexArgs.direction >= lineEndIndex) {
7814		projectionContext.projectionCache.offsets[index] = offsetCurrentVertex;
7815		return offsetCurrentVertex;
7816	}
7817	const nextVertex = projectLineVertexToLabelPlane(index + syntheticVertexArgs.direction, projectionContext, syntheticVertexArgs);
7818	const currentToNextOffsetNormal = transformToOffsetNormal(nextVertex.sub(currentVertex), lineOffsetY, syntheticVertexArgs.direction);
7819	const offsetNextSegmentBegin = currentVertex.add(currentToNextOffsetNormal);
7820	const offsetNextSegmentEnd = nextVertex.add(currentToNextOffsetNormal);
7821	projectionContext.projectionCache.offsets[index] = findLineIntersection(offsetPreviousVertex, offsetCurrentVertex, offsetNextSegmentBegin, offsetNextSegmentEnd) || offsetCurrentVertex;
7822	return projectionContext.projectionCache.offsets[index];
7823}
7824function placeGlyphAlongLine(offsetX, lineOffsetX, lineOffsetY, flip, anchorSegment, lineStartIndex, lineEndIndex, projectionContext, rotateToLine) {
7825	const combinedOffsetX = flip ? offsetX - lineOffsetX : offsetX + lineOffsetX;
7826	let direction = combinedOffsetX > 0 ? 1 : -1;
7827	let angle = 0;
7828	if (flip) {
7829		direction *= -1;
7830		angle = Math.PI;
7831	}
7832	if (direction < 0) angle += Math.PI;
7833	let currentIndex = direction > 0 ? lineStartIndex + anchorSegment : lineStartIndex + anchorSegment + 1;
7834	let anchorPoint;
7835	if (projectionContext.projectionCache.cachedAnchorPoint) anchorPoint = projectionContext.projectionCache.cachedAnchorPoint;
7836	else {
7837		anchorPoint = projectTileCoordinatesToLabelPlane(projectionContext.tileAnchorPoint.x, projectionContext.tileAnchorPoint.y, projectionContext).point;
7838		projectionContext.projectionCache.cachedAnchorPoint = anchorPoint;
7839	}
7840	let currentVertex = anchorPoint;
7841	let previousVertex = anchorPoint;
7842	let offsetIntersectionPoint;
7843	let offsetPreviousVertex;
7844	let distanceFromAnchor = 0;
7845	let currentSegmentDistance = 0;
7846	const absOffsetX = Math.abs(combinedOffsetX);
7847	const pathVertices = [];
7848	let currentLineSegment;
7849	while (distanceFromAnchor + currentSegmentDistance <= absOffsetX) {
7850		currentIndex += direction;
7851		if (currentIndex < lineStartIndex || currentIndex >= lineEndIndex) return null;
7852		distanceFromAnchor += currentSegmentDistance;
7853		previousVertex = currentVertex;
7854		offsetPreviousVertex = offsetIntersectionPoint;
7855		const syntheticVertexArgs = {
7856			absOffsetX,
7857			direction,
7858			distanceFromAnchor,
7859			previousVertex
7860		};
7861		currentVertex = projectLineVertexToLabelPlane(currentIndex, projectionContext, syntheticVertexArgs);
7862		if (lineOffsetY === 0) {
7863			pathVertices.push(previousVertex);
7864			currentLineSegment = currentVertex.sub(previousVertex);
7865		} else {
7866			let prevToCurrentOffsetNormal;
7867			const prevToCurrent = currentVertex.sub(previousVertex);
7868			if (prevToCurrent.mag() === 0) prevToCurrentOffsetNormal = transformToOffsetNormal(projectLineVertexToLabelPlane(currentIndex + direction, projectionContext, syntheticVertexArgs).sub(currentVertex), lineOffsetY, direction);
7869			else prevToCurrentOffsetNormal = transformToOffsetNormal(prevToCurrent, lineOffsetY, direction);
7870			offsetPreviousVertex ||= previousVertex.add(prevToCurrentOffsetNormal);
7871			offsetIntersectionPoint = findOffsetIntersectionPoint(currentIndex, prevToCurrentOffsetNormal, currentVertex, lineStartIndex, lineEndIndex, offsetPreviousVertex, lineOffsetY, projectionContext, syntheticVertexArgs);
7872			pathVertices.push(offsetPreviousVertex);
7873			currentLineSegment = offsetIntersectionPoint.sub(offsetPreviousVertex);
7874		}
7875		currentSegmentDistance = currentLineSegment.mag();
7876	}
7877	const segmentInterpolationT = (absOffsetX - distanceFromAnchor) / currentSegmentDistance;
7878	const p = currentLineSegment._mult(segmentInterpolationT)._add(offsetPreviousVertex || previousVertex);
7879	const segmentAngle = angle + Math.atan2(currentVertex.y - previousVertex.y, currentVertex.x - previousVertex.x);
7880	pathVertices.push(p);
7881	return {
7882		point: p,
7883		angle: rotateToLine ? segmentAngle : 0,
7884		path: pathVertices
7885	};
7886}
7887const hiddenGlyphAttributes = new Float32Array([
7888	-Infinity,
7889	-Infinity,
7890	0,
7891	-Infinity,
7892	-Infinity,
7893	0,
7894	-Infinity,
7895	-Infinity,
7896	0,
7897	-Infinity,
7898	-Infinity,
7899	0
7900]);
7901function hideGlyphs(num, dynamicLayoutVertexArray) {
7902	for (let i = 0; i < num; i++) {
7903		const offset = dynamicLayoutVertexArray.length;
7904		dynamicLayoutVertexArray.resize(offset + 4);
7905		dynamicLayoutVertexArray.float32.set(hiddenGlyphAttributes, offset * 3);
7906	}
7907}
7908function xyTransformMat4(out, a, m) {
7909	const x = a[0], y = a[1];
7910	out[0] = m[0] * x + m[4] * y + m[12];
7911	out[1] = m[1] * x + m[5] * y + m[13];
7912	out[3] = m[3] * x + m[7] * y + m[15];
7913	return out;
7914}
7915/**
7916* Takes a path of points that was previously projected using the `pitchedLabelPlaneMatrix`
7917* and projects it using the map projection's (mercator/globe...) `projectTileCoordinates` function.
7918* Returns a new array of the projected points.
7919* Does not modify the input array.
7920*/
7921function projectPathSpecialProjection(projectedPath, projectionContext) {
7922	const inverseLabelPlaneMatrix = tmpMat4;
7923	fastInvertSkewMat4(inverseLabelPlaneMatrix, projectionContext.pitchedLabelPlaneMatrix);
7924	return projectedPath.map((p) => {
7925		const backProjected = projectWithMatrix(p.x, p.y, inverseLabelPlaneMatrix, elevationAt(projectionContext, p.x, p.y));
7926		const projected = projectionContext.transform.projectTileCoordinates(backProjected.point.x, backProjected.point.y, projectionContext.unwrappedTileID, elevationAt(projectionContext, backProjected.point.x, backProjected.point.y));
7927		projected.point.x = (projected.point.x * .5 + .5) * projectionContext.width;
7928		projected.point.y = (-projected.point.y * .5 + .5) * projectionContext.height;
7929		return projected;
7930	});
7931}
7932/**
7933* Takes a path of points projected to screenspace, finds the longest continuous unoccluded segment of that path
7934* and returns it.
7935* Does not modify the input array.
7936*/
7937function pathSlicedToLongestUnoccluded(path) {
7938	let longestUnoccludedStart = 0;
7939	let longestUnoccludedLength = 0;
7940	let currentUnoccludedStart = 0;
7941	let currentUnoccludedLength = 0;
7942	for (let i = 0; i < path.length; i++) if (path[i].isOccluded) {
7943		currentUnoccludedStart = i + 1;
7944		currentUnoccludedLength = 0;
7945	} else {
7946		currentUnoccludedLength++;
7947		if (currentUnoccludedLength > longestUnoccludedLength) {
7948			longestUnoccludedLength = currentUnoccludedLength;
7949			longestUnoccludedStart = currentUnoccludedStart;
7950		}
7951	}
7952	return path.slice(longestUnoccludedStart, longestUnoccludedStart + longestUnoccludedLength);
7953}
7954/**
7955* @internal
7956* A collision index used to prevent symbols from overlapping. It keep tracks of
7957* where previous symbols have been placed and is used to check if a new
7958* symbol overlaps with any previously added symbols.
7959*
7960* There are two steps to insertion: first placeCollisionBox/Circles checks if
7961* there's room for a symbol, then insertCollisionBox/Circles actually puts the
7962* symbol in the index. The two step process allows paired symbols to be inserted
7963* together even if they overlap.
7964*/
7965var CollisionIndex = class {
7966	constructor(transform, grid = new GridIndex(transform.width + 200, transform.height + 200, 25), ignoredGrid = new GridIndex(transform.width + 200, transform.height + 200, 25)) {
7967		this.transform = transform;
7968		this.grid = grid;
7969		this.ignoredGrid = ignoredGrid;
7970		this.pitchFactor = Math.cos(transform.pitch * Math.PI / 180) * transform.cameraToCenterDistance;
7971		this.screenRightBoundary = transform.width + 100;
7972		this.screenBottomBoundary = transform.height + 100;
7973		this.gridRightBoundary = transform.width + 200;
7974		this.gridBottomBoundary = transform.height + 200;
7975		this.perspectiveRatioCutoff = .6;
7976	}
7977	placeCollisionBox(collisionBox, overlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, collisionGroupPredicate, getElevation, shift, simpleProjectionMatrix, heightOffset = 0, heightAnchorGround = true) {
7978		const x = collisionBox.anchorPointX + translation[0];
7979		const y = collisionBox.anchorPointY + translation[1];
7980		const projectedPoint = this.projectAndGetPerspectiveRatio(x, y, unwrappedTileID, getSymbolElevation(getElevation, x, y, heightOffset, heightAnchorGround), simpleProjectionMatrix);
7981		const tileToViewport = textPixelRatio * projectedPoint.perspectiveRatio;
7982		let projectedBox;
7983		if (!pitchWithMap && !rotateWithMap) {
7984			const pointX = projectedPoint.x + (shift ? shift.x * tileToViewport : 0);
7985			const pointY = projectedPoint.y + (shift ? shift.y * tileToViewport : 0);
7986			projectedBox = {
7987				allPointsOccluded: false,
7988				box: [
7989					pointX + collisionBox.x1 * tileToViewport,
7990					pointY + collisionBox.y1 * tileToViewport,
7991					pointX + collisionBox.x2 * tileToViewport,
7992					pointY + collisionBox.y2 * tileToViewport
7993				]
7994			};
7995		} else projectedBox = this._projectCollisionBox(collisionBox, tileToViewport, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, projectedPoint, getElevation, shift, simpleProjectionMatrix, heightOffset, heightAnchorGround);
7996		const [tlX, tlY, brX, brY] = projectedBox.box;
7997		const occluded = pitchWithMap ? projectedBox.allPointsOccluded : projectedPoint.isOccluded;
7998		let unplaceable = occluded;
7999		unplaceable ||= projectedPoint.perspectiveRatio < this.perspectiveRatioCutoff;
8000		unplaceable ||= !this.isInsideGrid(tlX, tlY, brX, brY);
8001		if (unplaceable || overlapMode !== "always" && this.grid.hitTest(tlX, tlY, brX, brY, overlapMode, collisionGroupPredicate)) return {
8002			box: [
8003				tlX,
8004				tlY,
8005				brX,
8006				brY
8007			],
8008			placeable: false,
8009			offscreen: false,
8010			occluded
8011		};
8012		return {
8013			box: [
8014				tlX,
8015				tlY,
8016				brX,
8017				brY
8018			],
8019			placeable: true,
8020			offscreen: this.isOffscreen(tlX, tlY, brX, brY),
8021			occluded
8022		};
8023	}
8024	placeCollisionCircles(overlapMode, symbol, lineVertexArray, glyphOffsetArray, fontSize, unwrappedTileID, pitchedLabelPlaneMatrix, showCollisionCircles, pitchWithMap, collisionGroupPredicate, circlePixelDiameter, textPixelPadding, translation, getElevation) {
8025		const placedCollisionCircles = [];
8026		const tileUnitAnchorPoint = new Point(symbol.anchorX, symbol.anchorY);
8027		const perspectiveRatio = this.getPerspectiveRatio(tileUnitAnchorPoint.x, tileUnitAnchorPoint.y, unwrappedTileID, getElevation?.(tileUnitAnchorPoint.x, tileUnitAnchorPoint.y));
8028		const labelPlaneFontScale = (pitchWithMap ? fontSize * this.transform.getPitchedTextCorrection(symbol.anchorX, symbol.anchorY, unwrappedTileID) / perspectiveRatio : fontSize * perspectiveRatio) / 24;
8029		const projectionCache = {
8030			projections: {},
8031			offsets: {},
8032			cachedAnchorPoint: void 0,
8033			anyProjectionOccluded: false
8034		};
8035		const lineOffsetX = symbol.lineOffsetX * labelPlaneFontScale;
8036		const lineOffsetY = symbol.lineOffsetY * labelPlaneFontScale;
8037		const projectionContext = {
8038			getElevation,
8039			pitchedLabelPlaneMatrix,
8040			lineVertexArray,
8041			pitchWithMap,
8042			projectionCache,
8043			transform: this.transform,
8044			tileAnchorPoint: tileUnitAnchorPoint,
8045			unwrappedTileID,
8046			width: this.transform.width,
8047			height: this.transform.height,
8048			translation
8049		};
8050		const firstAndLastGlyph = placeFirstAndLastGlyph(labelPlaneFontScale, glyphOffsetArray, lineOffsetX, lineOffsetY, false, symbol, false, projectionContext);
8051		let collisionDetected = false;
8052		let inGrid = false;
8053		let entirelyOffscreen = true;
8054		if (firstAndLastGlyph) {
8055			const radius = circlePixelDiameter * .5 * perspectiveRatio + textPixelPadding;
8056			const screenPlaneMin = new Point(-100, -100);
8057			const screenPlaneMax = new Point(this.screenRightBoundary, this.screenBottomBoundary);
8058			const interpolator = new PathInterpolator();
8059			const first = firstAndLastGlyph.first;
8060			const last = firstAndLastGlyph.last;
8061			let projectedPath = [];
8062			for (let i = first.path.length - 1; i >= 1; i--) projectedPath.push(first.path[i]);
8063			for (let i = 1; i < last.path.length; i++) projectedPath.push(last.path[i]);
8064			const circleDist = radius * 2.5;
8065			if (pitchWithMap) {
8066				const screenSpacePath = this.projectPathToScreenSpace(projectedPath, projectionContext);
8067				if (screenSpacePath.some((point) => point.signedDistanceFromCamera <= 0)) projectedPath = [];
8068				else projectedPath = screenSpacePath.map((p) => p.point);
8069			}
8070			let segments = [];
8071			if (projectedPath.length > 0) {
8072				const minPoint = projectedPath[0].clone();
8073				const maxPoint = projectedPath[0].clone();
8074				for (let i = 1; i < projectedPath.length; i++) {
8075					minPoint.x = Math.min(minPoint.x, projectedPath[i].x);
8076					minPoint.y = Math.min(minPoint.y, projectedPath[i].y);
8077					maxPoint.x = Math.max(maxPoint.x, projectedPath[i].x);
8078					maxPoint.y = Math.max(maxPoint.y, projectedPath[i].y);
8079				}
8080				if (minPoint.x >= screenPlaneMin.x && maxPoint.x <= screenPlaneMax.x && minPoint.y >= screenPlaneMin.y && maxPoint.y <= screenPlaneMax.y) segments = [projectedPath];
8081				else if (maxPoint.x < screenPlaneMin.x || minPoint.x > screenPlaneMax.x || maxPoint.y < screenPlaneMin.y || minPoint.y > screenPlaneMax.y) segments = [];
8082				else segments = clipLine([projectedPath], screenPlaneMin.x, screenPlaneMin.y, screenPlaneMax.x, screenPlaneMax.y);
8083			}
8084			for (const seg of segments) {
8085				interpolator.reset(seg, radius * .25);
8086				let numCircles = 0;
8087				if (interpolator.length <= .5 * radius) numCircles = 1;
8088				else numCircles = Math.ceil(interpolator.paddedLength / circleDist) + 1;
8089				for (let i = 0; i < numCircles; i++) {
8090					const t = i / Math.max(numCircles - 1, 1);
8091					const circlePosition = interpolator.lerp(t);
8092					const centerX = circlePosition.x + 100;
8093					const centerY = circlePosition.y + 100;
8094					placedCollisionCircles.push(centerX, centerY, radius, 0);
8095					const x1 = centerX - radius;
8096					const y1 = centerY - radius;
8097					const x2 = centerX + radius;
8098					const y2 = centerY + radius;
8099					entirelyOffscreen &&= this.isOffscreen(x1, y1, x2, y2);
8100					inGrid ||= this.isInsideGrid(x1, y1, x2, y2);
8101					if (overlapMode !== "always" && this.grid.hitTestCircle(centerX, centerY, radius, overlapMode, collisionGroupPredicate)) {
8102						collisionDetected = true;
8103						if (!showCollisionCircles) return {
8104							circles: [],
8105							offscreen: false,
8106							collisionDetected
8107						};
8108					}
8109				}
8110			}
8111		}
8112		return {
8113			circles: !showCollisionCircles && collisionDetected || !inGrid || perspectiveRatio < this.perspectiveRatioCutoff ? [] : placedCollisionCircles,
8114			offscreen: entirelyOffscreen,
8115			collisionDetected
8116		};
8117	}
8118	projectPathToScreenSpace(projectedPath, projectionContext) {
8119		return pathSlicedToLongestUnoccluded(projectPathSpecialProjection(projectedPath, projectionContext));
8120	}
8121	/**
8122	* Because the geometries in the CollisionIndex are an approximation of the shape of
8123	* symbols on the map, we use the CollisionIndex to look up the symbol part of
8124	* `queryRenderedFeatures`.
8125	*/
8126	queryRenderedSymbols(viewportQueryGeometry) {
8127		if (viewportQueryGeometry.length === 0 || this.grid.keysLength() === 0 && this.ignoredGrid.keysLength() === 0) return {};
8128		const query = [];
8129		const bounds = new Bounds();
8130		for (const point of viewportQueryGeometry) {
8131			const gridPoint = new Point(point.x + 100, point.y + 100);
8132			bounds.extend(gridPoint);
8133			query.push(gridPoint);
8134		}
8135		const { minX, minY, maxX, maxY } = bounds;
8136		const features = this.grid.query(minX, minY, maxX, maxY).concat(this.ignoredGrid.query(minX, minY, maxX, maxY));
8137		const seenFeatures = {};
8138		const result = {};
8139		for (const feature of features) {
8140			const featureKey = feature.key;
8141			if (seenFeatures[featureKey.bucketInstanceId] === void 0) seenFeatures[featureKey.bucketInstanceId] = {};
8142			if (seenFeatures[featureKey.bucketInstanceId][featureKey.featureIndex]) continue;
8143			const bbox = [
8144				new Point(feature.x1, feature.y1),
8145				new Point(feature.x2, feature.y1),
8146				new Point(feature.x2, feature.y2),
8147				new Point(feature.x1, feature.y2)
8148			];
8149			if (!polygonIntersectsPolygon(query, bbox)) continue;
8150			seenFeatures[featureKey.bucketInstanceId][featureKey.featureIndex] = true;
8151			if (result[featureKey.bucketInstanceId] === void 0) result[featureKey.bucketInstanceId] = [];
8152			result[featureKey.bucketInstanceId].push(featureKey.featureIndex);
8153		}
8154		return result;
8155	}
8156	insertCollisionBox(collisionBox, overlapMode, ignorePlacement, bucketInstanceId, featureIndex, collisionGroupID) {
8157		const grid = ignorePlacement ? this.ignoredGrid : this.grid;
8158		const key = {
8159			bucketInstanceId,
8160			featureIndex,
8161			collisionGroupID,
8162			overlapMode
8163		};
8164		grid.insert(key, collisionBox[0], collisionBox[1], collisionBox[2], collisionBox[3]);
8165	}
8166	insertCollisionCircles(collisionCircles, overlapMode, ignorePlacement, bucketInstanceId, featureIndex, collisionGroupID) {
8167		const grid = ignorePlacement ? this.ignoredGrid : this.grid;
8168		const key = {
8169			bucketInstanceId,
8170			featureIndex,
8171			collisionGroupID,
8172			overlapMode
8173		};
8174		for (let k = 0; k < collisionCircles.length; k += 4) grid.insertCircle(key, collisionCircles[k], collisionCircles[k + 1], collisionCircles[k + 2]);
8175	}
8176	projectAndGetPerspectiveRatio(x, y, unwrappedTileID, elevation, simpleProjectionMatrix) {
8177		if (simpleProjectionMatrix) {
8178			let pos;
8179			if (elevation != null) {
8180				pos = [
8181					x,
8182					y,
8183					elevation,
8184					1
8185				];
8186				transformMat4(pos, pos, simpleProjectionMatrix);
8187			} else {
8188				pos = [
8189					x,
8190					y,
8191					0,
8192					1
8193				];
8194				xyTransformMat4(pos, pos, simpleProjectionMatrix);
8195			}
8196			const w = pos[3];
8197			return {
8198				x: (pos[0] / w + 1) / 2 * this.transform.width + 100,
8199				y: (-pos[1] / w + 1) / 2 * this.transform.height + 100,
8200				perspectiveRatio: .5 + .5 * (this.transform.cameraToCenterDistance / w),
8201				isOccluded: false,
8202				signedDistanceFromCamera: w
8203			};
8204		} else {
8205			const projected = this.transform.projectTileCoordinates(x, y, unwrappedTileID, elevation);
8206			return {
8207				x: (projected.point.x + 1) / 2 * this.transform.width + 100,
8208				y: (-projected.point.y + 1) / 2 * this.transform.height + 100,
8209				perspectiveRatio: .5 + .5 * (this.transform.cameraToCenterDistance / projected.signedDistanceFromCamera),
8210				isOccluded: projected.isOccluded,
8211				signedDistanceFromCamera: projected.signedDistanceFromCamera
8212			};
8213		}
8214	}
8215	getPerspectiveRatio(x, y, unwrappedTileID, elevation) {
8216		const projected = this.transform.projectTileCoordinates(x, y, unwrappedTileID, elevation);
8217		return .5 + .5 * (this.transform.cameraToCenterDistance / projected.signedDistanceFromCamera);
8218	}
8219	isOffscreen(x1, y1, x2, y2) {
8220		return x2 < 100 || x1 >= this.screenRightBoundary || y2 < 100 || y1 > this.screenBottomBoundary;
8221	}
8222	isInsideGrid(x1, y1, x2, y2) {
8223		return x2 >= 0 && x1 < this.gridRightBoundary && y2 >= 0 && y1 < this.gridBottomBoundary;
8224	}
8225	getViewportMatrix() {
8226		const m = identity([]);
8227		translate(m, m, [
8228			-100,
8229			-100,
8230			0
8231		]);
8232		return m;
8233	}
8234	/**
8235	* Applies all layout+paint properties of the given box in order to find as good approximation of its screen-space bounding box as possible.
8236	*/
8237	_projectCollisionBox(collisionBox, tileToViewport, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translation, projectedPoint, getElevation, shift, simpleProjectionMatrix, heightOffset = 0, heightAnchorGround = true) {
8238		let vecEastX = 1;
8239		let vecEastY = 0;
8240		let vecSouthX = 0;
8241		let vecSouthY = 1;
8242		const translatedAnchorX = collisionBox.anchorPointX + translation[0];
8243		const translatedAnchorY = collisionBox.anchorPointY + translation[1];
8244		if (rotateWithMap && !pitchWithMap) {
8245			const projectedEast = this.projectAndGetPerspectiveRatio(translatedAnchorX + 1, translatedAnchorY, unwrappedTileID, getSymbolElevation(getElevation, translatedAnchorX + 1, translatedAnchorY, heightOffset, heightAnchorGround), simpleProjectionMatrix);
8246			const toEastX = projectedEast.x - projectedPoint.x;
8247			const toEastY = projectedEast.y - projectedPoint.y;
8248			const angle = Math.atan(toEastY / toEastX) + (toEastX < 0 ? Math.PI : 0);
8249			const sin = Math.sin(angle);
8250			const cos = Math.cos(angle);
8251			vecEastX = cos;
8252			vecEastY = sin;
8253			vecSouthX = -sin;
8254			vecSouthY = cos;
8255		} else if (!rotateWithMap && pitchWithMap) {
8256			const skew = getTileSkewVectors(this.transform);
8257			vecEastX = skew.vecEast[0];
8258			vecEastY = skew.vecEast[1];
8259			vecSouthX = skew.vecSouth[0];
8260			vecSouthY = skew.vecSouth[1];
8261		}
8262		let basePointX = projectedPoint.x;
8263		let basePointY = projectedPoint.y;
8264		let distanceMultiplier = tileToViewport;
8265		if (pitchWithMap) {
8266			basePointX = translatedAnchorX;
8267			basePointY = translatedAnchorY;
8268			const zoomFraction = this.transform.zoom - tileID.overscaledZ;
8269			distanceMultiplier = Math.pow(2, -zoomFraction);
8270			distanceMultiplier *= this.transform.getPitchedTextCorrection(translatedAnchorX, translatedAnchorY, unwrappedTileID);
8271			if (!shift) {
8272				const distanceRatio = projectedPoint.signedDistanceFromCamera / this.transform.cameraToCenterDistance;
8273				const perspectiveRatio = clamp(.5 + .5 * distanceRatio, 0, 4);
8274				distanceMultiplier *= perspectiveRatio;
8275			}
8276		}
8277		if (shift) {
8278			basePointX += vecEastX * shift.x * distanceMultiplier + vecSouthX * shift.y * distanceMultiplier;
8279			basePointY += vecEastY * shift.x * distanceMultiplier + vecSouthY * shift.y * distanceMultiplier;
8280		}
8281		const offsetXmin = collisionBox.x1 * distanceMultiplier;
8282		const offsetXmax = collisionBox.x2 * distanceMultiplier;
8283		const offsetXhalf = (offsetXmin + offsetXmax) / 2;
8284		const offsetYmin = collisionBox.y1 * distanceMultiplier;
8285		const offsetYmax = collisionBox.y2 * distanceMultiplier;
8286		const offsetYhalf = (offsetYmin + offsetYmax) / 2;
8287		const offsetsArray = [
8288			{
8289				offsetX: offsetXmin,
8290				offsetY: offsetYmin
8291			},
8292			{
8293				offsetX: offsetXhalf,
8294				offsetY: offsetYmin
8295			},
8296			{
8297				offsetX: offsetXmax,
8298				offsetY: offsetYmin
8299			},
8300			{
8301				offsetX: offsetXmax,
8302				offsetY: offsetYhalf
8303			},
8304			{
8305				offsetX: offsetXmax,
8306				offsetY: offsetYmax
8307			},
8308			{
8309				offsetX: offsetXhalf,
8310				offsetY: offsetYmax
8311			},
8312			{
8313				offsetX: offsetXmin,
8314				offsetY: offsetYmax
8315			},
8316			{
8317				offsetX: offsetXmin,
8318				offsetY: offsetYhalf
8319			}
8320		];
8321		let points = [];
8322		for (const { offsetX, offsetY } of offsetsArray) points.push(new Point(basePointX + vecEastX * offsetX + vecSouthX * offsetY, basePointY + vecEastY * offsetX + vecSouthY * offsetY));
8323		let anyPointVisible = false;
8324		if (pitchWithMap) {
8325			const projected = points.map((p) => this.projectAndGetPerspectiveRatio(p.x, p.y, unwrappedTileID, getSymbolElevation(getElevation, p.x, p.y, heightOffset, heightAnchorGround), simpleProjectionMatrix));
8326			anyPointVisible = projected.some((p) => !p.isOccluded);
8327			points = projected.map((p) => new Point(p.x, p.y));
8328		} else anyPointVisible = true;
8329		return {
8330			box: getAABB(points),
8331			allPointsOccluded: !anyPointVisible
8332		};
8333	}
8334};
8335//#endregion
8336//#region src/symbol/placement.ts
8337var OpacityState = class {
8338	constructor(prevState, increment, placed, skipFade) {
8339		if (prevState) this.opacity = Math.max(0, Math.min(1, prevState.opacity + (prevState.placed ? increment : -increment)));
8340		else this.opacity = skipFade && placed ? 1 : 0;
8341		this.placed = placed;
8342	}
8343	isHidden() {
8344		return this.opacity === 0 && !this.placed;
8345	}
8346};
8347var JointOpacityState = class {
8348	constructor(prevState, increment, placedText, placedIcon, skipFade) {
8349		this.text = new OpacityState(prevState ? prevState.text : null, increment, placedText, skipFade);
8350		this.icon = new OpacityState(prevState ? prevState.icon : null, increment, placedIcon, skipFade);
8351	}
8352	isHidden() {
8353		return this.text.isHidden() && this.icon.isHidden();
8354	}
8355};
8356var JointPlacement = class {
8357	constructor(text, icon, skipFade) {
8358		this.text = text;
8359		this.icon = icon;
8360		this.skipFade = skipFade;
8361	}
8362};
8363var RetainedQueryData = class {
8364	constructor(bucketInstanceId, featureIndex, sourceLayerIndex, bucketIndex, tileID) {
8365		this.bucketInstanceId = bucketInstanceId;
8366		this.featureIndex = featureIndex;
8367		this.sourceLayerIndex = sourceLayerIndex;
8368		this.bucketIndex = bucketIndex;
8369		this.tileID = tileID;
8370	}
8371};
8372var CollisionGroups = class {
8373	constructor(crossSourceCollisions) {
8374		this.crossSourceCollisions = crossSourceCollisions;
8375		this.maxGroupID = 0;
8376		this.collisionGroups = {};
8377	}
8378	get(sourceID) {
8379		if (!this.crossSourceCollisions) {
8380			if (!this.collisionGroups[sourceID]) {
8381				const nextGroupID = ++this.maxGroupID;
8382				this.collisionGroups[sourceID] = {
8383					ID: nextGroupID,
8384					predicate: (key) => {
8385						return key.collisionGroupID === nextGroupID;
8386					}
8387				};
8388			}
8389			return this.collisionGroups[sourceID];
8390		} else return {
8391			ID: 0,
8392			predicate: null
8393		};
8394	}
8395};
8396function calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale) {
8397	const { horizontalAlign, verticalAlign } = getAnchorAlignment(anchor);
8398	const shiftX = -(horizontalAlign - .5) * width;
8399	const shiftY = -(verticalAlign - .5) * height;
8400	return new Point(shiftX + textOffset[0] * textBoxScale, shiftY + textOffset[1] * textBoxScale);
8401}
8402var Placement = class {
8403	constructor(transform, terrain, fadeDuration, crossSourceCollisions, prevPlacement) {
8404		this.transform = transform.clone();
8405		this.terrain = terrain;
8406		this.collisionIndex = new CollisionIndex(this.transform);
8407		this.placements = {};
8408		this.opacities = {};
8409		this.variableOffsets = {};
8410		this.stale = false;
8411		this.commitTime = 0;
8412		this.fadeDuration = fadeDuration;
8413		this.retainedQueryData = {};
8414		this.collisionGroups = new CollisionGroups(crossSourceCollisions);
8415		this.collisionCircleArrays = {};
8416		this.collisionBoxArrays = /* @__PURE__ */ new Map();
8417		this.prevPlacement = prevPlacement;
8418		if (prevPlacement) prevPlacement.prevPlacement = void 0;
8419		this.placedOrientations = {};
8420	}
8421	_getTerrainElevationFunc(tileID) {
8422		const terrain = this.terrain;
8423		if (!terrain) return void 0;
8424		return (x, y) => terrain.getElevation(tileID, x, y);
8425	}
8426	getBucketParts(results, styleLayer, tile, sortAcrossTiles) {
8427		const symbolBucket = tile.getBucket(styleLayer);
8428		const bucketFeatureIndex = tile.latestFeatureIndex;
8429		if (!symbolBucket || !bucketFeatureIndex || styleLayer.id !== symbolBucket.layerIds[0]) return;
8430		const collisionBoxArray = tile.collisionBoxArray;
8431		const layout = symbolBucket.layers[0].layout;
8432		const paint = symbolBucket.layers[0].paint;
8433		const scale = Math.pow(2, this.transform.zoom - tile.tileID.overscaledZ);
8434		const textPixelRatio = tile.tileSize / EXTENT;
8435		const unwrappedTileID = tile.tileID.toUnwrapped();
8436		const rotateWithMap = layout.get("text-rotation-alignment") === "map";
8437		const pixelsToTiles = pixelsToTileUnits(tile, 1, this.transform.zoom);
8438		const translationText = translatePosition(this.collisionIndex.transform, tile, paint.get("text-translate"), paint.get("text-translate-anchor"));
8439		const translationIcon = translatePosition(this.collisionIndex.transform, tile, paint.get("icon-translate"), paint.get("icon-translate-anchor"));
8440		const pitchedLabelPlaneMatrix = getPitchedLabelPlaneMatrix(rotateWithMap, this.transform, pixelsToTiles);
8441		this.retainedQueryData[symbolBucket.bucketInstanceId] = new RetainedQueryData(symbolBucket.bucketInstanceId, bucketFeatureIndex, symbolBucket.sourceLayerIndex, symbolBucket.index, tile.tileID);
8442		const parameters = {
8443			bucket: symbolBucket,
8444			layout,
8445			translationText,
8446			translationIcon,
8447			unwrappedTileID,
8448			pitchedLabelPlaneMatrix,
8449			scale,
8450			textPixelRatio,
8451			holdingForFade: tile.holdingForSymbolFade(),
8452			collisionBoxArray,
8453			partiallyEvaluatedTextSize: evaluateSizeForZoom(symbolBucket.textSizeData, this.transform.zoom),
8454			collisionGroup: this.collisionGroups.get(symbolBucket.sourceID)
8455		};
8456		if (sortAcrossTiles) for (const range of symbolBucket.sortKeyRanges) {
8457			const { sortKey, symbolInstanceStart, symbolInstanceEnd } = range;
8458			results.push({
8459				sortKey,
8460				symbolInstanceStart,
8461				symbolInstanceEnd,
8462				parameters
8463			});
8464		}
8465		else results.push({
8466			symbolInstanceStart: 0,
8467			symbolInstanceEnd: symbolBucket.symbolInstances.length,
8468			parameters
8469		});
8470	}
8471	attemptAnchorPlacement(textAnchorOffset, textBox, width, height, textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, tileID, unwrappedTileID, collisionGroup, textOverlapMode, symbolInstance, bucket, orientation, translationText, translationIcon, iconBox, getElevation, simpleProjectionMatrix, heightOffset, heightAnchorGround) {
8472		const anchor = TextAnchorEnum[textAnchorOffset.textAnchor];
8473		const textOffset = [textAnchorOffset.textOffset0, textAnchorOffset.textOffset1];
8474		const shift = calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale);
8475		const placedGlyphBoxes = this.collisionIndex.placeCollisionBox(textBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, shift, simpleProjectionMatrix, heightOffset, heightAnchorGround);
8476		if (iconBox) {
8477			if (!this.collisionIndex.placeCollisionBox(iconBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationIcon, collisionGroup.predicate, getElevation, shift, simpleProjectionMatrix, heightOffset, heightAnchorGround).placeable) return;
8478		}
8479		if (placedGlyphBoxes.placeable) {
8480			let prevAnchor;
8481			if (this.prevPlacement?.variableOffsets[symbolInstance.crossTileID] && this.prevPlacement?.placements[symbolInstance.crossTileID]?.text) prevAnchor = this.prevPlacement.variableOffsets[symbolInstance.crossTileID].anchor;
8482			if (symbolInstance.crossTileID === 0) throw new Error("symbolInstance.crossTileID can't be 0");
8483			this.variableOffsets[symbolInstance.crossTileID] = {
8484				textOffset,
8485				width,
8486				height,
8487				anchor,
8488				textBoxScale,
8489				prevAnchor
8490			};
8491			this.markUsedJustification(bucket, anchor, symbolInstance, orientation);
8492			if (bucket.allowVerticalPlacement) {
8493				this.markUsedOrientation(bucket, orientation, symbolInstance);
8494				this.placedOrientations[symbolInstance.crossTileID] = orientation;
8495			}
8496			return {
8497				shift,
8498				placedGlyphBoxes
8499			};
8500		}
8501	}
8502	placeLayerBucketPart(bucketPart, seenCrossTileIDs, showCollisionBoxes) {
8503		const { bucket, layout, translationText, translationIcon, unwrappedTileID, pitchedLabelPlaneMatrix, textPixelRatio, holdingForFade, collisionBoxArray, partiallyEvaluatedTextSize, collisionGroup } = bucketPart.parameters;
8504		const textOptional = layout.get("text-optional");
8505		const iconOptional = layout.get("icon-optional");
8506		const textOverlapMode = getOverlapMode(layout, "text-overlap", "text-allow-overlap");
8507		const textAlwaysOverlap = textOverlapMode === "always";
8508		const iconOverlapMode = getOverlapMode(layout, "icon-overlap", "icon-allow-overlap");
8509		const iconAlwaysOverlap = iconOverlapMode === "always";
8510		const rotateWithMap = layout.get("text-rotation-alignment") === "map";
8511		const pitchWithMap = layout.get("text-pitch-alignment") === "map";
8512		const hasIconTextFit = layout.get("icon-text-fit") !== "none";
8513		const zOrderByViewportY = layout.get("symbol-z-order") === "viewport-y";
8514		const heightAnchorGround = layout.get("symbol-height-anchor") === "ground";
8515		const alwaysShowText = textAlwaysOverlap && (iconAlwaysOverlap || !bucket.hasIconData() || iconOptional);
8516		const alwaysShowIcon = iconAlwaysOverlap && (textAlwaysOverlap || !bucket.hasTextData() || textOptional);
8517		if (!bucket.collisionArrays && collisionBoxArray) bucket.deserializeCollisionBoxes(collisionBoxArray);
8518		const tileID = this.retainedQueryData[bucket.bucketInstanceId].tileID;
8519		const getElevation = this._getTerrainElevationFunc(tileID);
8520		const simpleProjectionMatrix = this.transform.getFastPathSimpleProjectionMatrix(tileID);
8521		const placeSymbol = (symbolInstance, collisionArrays, symbolIndex) => {
8522			if (seenCrossTileIDs[symbolInstance.crossTileID]) return;
8523			if (holdingForFade) {
8524				this.placements[symbolInstance.crossTileID] = new JointPlacement(false, false, false);
8525				return;
8526			}
8527			const symbolHeightOffset = symbolInstance.heightOffset;
8528			let placeText = false;
8529			let placeIcon = false;
8530			let offscreen = true;
8531			let shift = null;
8532			let placed = {
8533				box: null,
8534				placeable: false,
8535				offscreen: null,
8536				occluded: false
8537			};
8538			let placedVerticalText = {
8539				box: null,
8540				placeable: false,
8541				offscreen: null
8542			};
8543			let placedGlyphBoxes = null;
8544			let placedGlyphCircles = null;
8545			let placedIconBoxes = null;
8546			let textFeatureIndex = 0;
8547			let verticalTextFeatureIndex = 0;
8548			let iconFeatureIndex = 0;
8549			if (collisionArrays.textFeatureIndex) textFeatureIndex = collisionArrays.textFeatureIndex;
8550			else if (symbolInstance.useRuntimeCollisionCircles) textFeatureIndex = symbolInstance.featureIndex;
8551			if (collisionArrays.verticalTextFeatureIndex) verticalTextFeatureIndex = collisionArrays.verticalTextFeatureIndex;
8552			const textBox = collisionArrays.textBox;
8553			if (textBox) {
8554				const updatePreviousOrientationIfNotPlaced = (isPlaced) => {
8555					let previousOrientation = 1;
8556					if (bucket.allowVerticalPlacement && !isPlaced && this.prevPlacement) {
8557						const prevPlacedOrientation = this.prevPlacement.placedOrientations[symbolInstance.crossTileID];
8558						if (prevPlacedOrientation) {
8559							this.placedOrientations[symbolInstance.crossTileID] = prevPlacedOrientation;
8560							previousOrientation = prevPlacedOrientation;
8561							this.markUsedOrientation(bucket, previousOrientation, symbolInstance);
8562						}
8563					}
8564					return previousOrientation;
8565				};
8566				const placeTextForPlacementModes = (placeHorizontalFn, placeVerticalFn) => {
8567					if (bucket.allowVerticalPlacement && symbolInstance.numVerticalGlyphVertices > 0 && collisionArrays.verticalTextBox) for (const placementMode of bucket.writingModes) {
8568						if (placementMode === 2) {
8569							placed = placeVerticalFn();
8570							placedVerticalText = placed;
8571						} else placed = placeHorizontalFn();
8572						if (placed?.placeable) break;
8573					}
8574					else placed = placeHorizontalFn();
8575				};
8576				const textAnchorOffsetStart = symbolInstance.textAnchorOffsetStartIndex;
8577				const textAnchorOffsetEnd = symbolInstance.textAnchorOffsetEndIndex;
8578				if (textAnchorOffsetEnd === textAnchorOffsetStart) {
8579					const placeBox = (collisionTextBox, orientation) => {
8580						const placedFeature = this.collisionIndex.placeCollisionBox(collisionTextBox, textOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, void 0, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround);
8581						if (placedFeature?.placeable) {
8582							this.markUsedOrientation(bucket, orientation, symbolInstance);
8583							this.placedOrientations[symbolInstance.crossTileID] = orientation;
8584						}
8585						return placedFeature;
8586					};
8587					const placeHorizontal = () => {
8588						return placeBox(textBox, 1);
8589					};
8590					const placeVertical = () => {
8591						const verticalTextBox = collisionArrays.verticalTextBox;
8592						if (bucket.allowVerticalPlacement && symbolInstance.numVerticalGlyphVertices > 0 && verticalTextBox) return placeBox(verticalTextBox, 2);
8593						return {
8594							box: null,
8595							offscreen: null
8596						};
8597					};
8598					placeTextForPlacementModes(placeHorizontal, placeVertical);
8599					updatePreviousOrientationIfNotPlaced(placed?.placeable);
8600				} else {
8601					let prevAnchor = TextAnchorEnum[this.prevPlacement?.variableOffsets[symbolInstance.crossTileID]?.anchor];
8602					const placeBoxForVariableAnchors = (collisionTextBox, collisionIconBox, orientation) => {
8603						const width = collisionTextBox.x2 - collisionTextBox.x1;
8604						const height = collisionTextBox.y2 - collisionTextBox.y1;
8605						const textBoxScale = symbolInstance.textBoxScale;
8606						const variableIconBox = hasIconTextFit && iconOverlapMode === "never" ? collisionIconBox : null;
8607						let placedBox = null;
8608						let placementPasses = textOverlapMode === "never" ? 1 : 2;
8609						let overlapMode = "never";
8610						if (prevAnchor) placementPasses++;
8611						for (let pass = 0; pass < placementPasses; pass++) {
8612							for (let i = textAnchorOffsetStart; i < textAnchorOffsetEnd; i++) {
8613								const textAnchorOffset = bucket.textAnchorOffsets.get(i);
8614								if (prevAnchor && textAnchorOffset.textAnchor !== prevAnchor) continue;
8615								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);
8616								if (result) {
8617									placedBox = result.placedGlyphBoxes;
8618									if (placedBox?.placeable) {
8619										placeText = true;
8620										shift = result.shift;
8621										return placedBox;
8622									}
8623								}
8624							}
8625							if (prevAnchor) prevAnchor = null;
8626							else overlapMode = textOverlapMode;
8627						}
8628						if (showCollisionBoxes && !placedBox) placedBox = {
8629							box: this.collisionIndex.placeCollisionBox(textBox, "always", textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationText, collisionGroup.predicate, getElevation, void 0, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround).box,
8630							offscreen: false,
8631							placeable: false,
8632							occluded: false
8633						};
8634						return placedBox;
8635					};
8636					const placeHorizontal = () => {
8637						return placeBoxForVariableAnchors(textBox, collisionArrays.iconBox, 1);
8638					};
8639					const placeVertical = () => {
8640						const verticalTextBox = collisionArrays.verticalTextBox;
8641						const wasPlaced = placed?.placeable;
8642						if (bucket.allowVerticalPlacement && !wasPlaced && symbolInstance.numVerticalGlyphVertices > 0 && verticalTextBox) return placeBoxForVariableAnchors(verticalTextBox, collisionArrays.verticalIconBox, 2);
8643						return {
8644							box: null,
8645							occluded: true,
8646							offscreen: null
8647						};
8648					};
8649					placeTextForPlacementModes(placeHorizontal, placeVertical);
8650					if (placed) {
8651						placeText = placed.placeable;
8652						offscreen = placed.offscreen;
8653					}
8654					const prevOrientation = updatePreviousOrientationIfNotPlaced(placed?.placeable);
8655					if (!placeText && this.prevPlacement) {
8656						const prevOffset = this.prevPlacement.variableOffsets[symbolInstance.crossTileID];
8657						if (prevOffset) {
8658							this.variableOffsets[symbolInstance.crossTileID] = prevOffset;
8659							this.markUsedJustification(bucket, prevOffset.anchor, symbolInstance, prevOrientation);
8660						}
8661					}
8662				}
8663			}
8664			placedGlyphBoxes = placed;
8665			placeText = placedGlyphBoxes?.placeable;
8666			offscreen = placedGlyphBoxes?.offscreen;
8667			if (symbolInstance.useRuntimeCollisionCircles && symbolInstance.centerJustifiedTextSymbolIndex >= 0) {
8668				const placedSymbol = bucket.text.placedSymbolArray.get(symbolInstance.centerJustifiedTextSymbolIndex);
8669				const fontSize = evaluateSizeForFeature(bucket.textSizeData, partiallyEvaluatedTextSize, placedSymbol);
8670				const textPixelPadding = layout.get("text-padding");
8671				const circlePixelDiameter = symbolInstance.collisionCircleDiameter;
8672				placedGlyphCircles = this.collisionIndex.placeCollisionCircles(textOverlapMode, placedSymbol, bucket.lineVertexArray, bucket.glyphOffsetArray, fontSize, unwrappedTileID, pitchedLabelPlaneMatrix, showCollisionBoxes, pitchWithMap, collisionGroup.predicate, circlePixelDiameter, textPixelPadding, translationText, getElevation);
8673				if (placedGlyphCircles.circles.length && placedGlyphCircles.collisionDetected && !showCollisionBoxes) warnOnce("Collisions detected, but collision boxes are not shown");
8674				placeText = textAlwaysOverlap || placedGlyphCircles.circles.length > 0 && !placedGlyphCircles.collisionDetected;
8675				offscreen &&= placedGlyphCircles.offscreen;
8676			}
8677			if (collisionArrays.iconFeatureIndex) iconFeatureIndex = collisionArrays.iconFeatureIndex;
8678			if (collisionArrays.iconBox) {
8679				const placeIconFeature = (iconBox) => {
8680					return this.collisionIndex.placeCollisionBox(iconBox, iconOverlapMode, textPixelRatio, tileID, unwrappedTileID, pitchWithMap, rotateWithMap, translationIcon, collisionGroup.predicate, getElevation, hasIconTextFit && shift ? shift : void 0, simpleProjectionMatrix, symbolHeightOffset, heightAnchorGround);
8681				};
8682				if (placedVerticalText && placedVerticalText.placeable && collisionArrays.verticalIconBox) {
8683					placedIconBoxes = placeIconFeature(collisionArrays.verticalIconBox);
8684					placeIcon = placedIconBoxes.placeable;
8685				} else {
8686					placedIconBoxes = placeIconFeature(collisionArrays.iconBox);
8687					placeIcon = placedIconBoxes.placeable;
8688				}
8689				offscreen &&= placedIconBoxes.offscreen;
8690			}
8691			const iconWithoutText = textOptional || symbolInstance.numHorizontalGlyphVertices === 0 && symbolInstance.numVerticalGlyphVertices === 0;
8692			const textWithoutIcon = iconOptional || symbolInstance.numIconVertices === 0;
8693			if (!iconWithoutText && !textWithoutIcon) placeIcon = placeText = placeIcon && placeText;
8694			else if (!textWithoutIcon) placeText = placeIcon && placeText;
8695			else if (!iconWithoutText) placeIcon &&= placeText;
8696			const hasTextBox = placeText && placedGlyphBoxes.placeable;
8697			const hasIconBox = placeIcon && placedIconBoxes.placeable;
8698			if (hasTextBox) {
8699				if (placedVerticalText && placedVerticalText.placeable && verticalTextFeatureIndex) this.collisionIndex.insertCollisionBox(placedGlyphBoxes.box, textOverlapMode, layout.get("text-ignore-placement"), bucket.bucketInstanceId, verticalTextFeatureIndex, collisionGroup.ID);
8700				else this.collisionIndex.insertCollisionBox(placedGlyphBoxes.box, textOverlapMode, layout.get("text-ignore-placement"), bucket.bucketInstanceId, textFeatureIndex, collisionGroup.ID);
8701			}
8702			if (hasIconBox) this.collisionIndex.insertCollisionBox(placedIconBoxes.box, iconOverlapMode, layout.get("icon-ignore-placement"), bucket.bucketInstanceId, iconFeatureIndex, collisionGroup.ID);
8703			if (placedGlyphCircles) {
8704				if (placeText) this.collisionIndex.insertCollisionCircles(placedGlyphCircles.circles, textOverlapMode, layout.get("text-ignore-placement"), bucket.bucketInstanceId, textFeatureIndex, collisionGroup.ID);
8705			}
8706			if (showCollisionBoxes) this.storeCollisionData(bucket.bucketInstanceId, symbolIndex, collisionArrays, placedGlyphBoxes, placedIconBoxes, placedGlyphCircles);
8707			if (symbolInstance.crossTileID === 0) throw new Error("symbolInstance.crossTileID can't be 0");
8708			if (bucket.bucketInstanceId === 0) throw new Error("bucket.bucketInstanceId can't be 0");
8709			const textVisible = (placeText || alwaysShowText) && !placedGlyphBoxes?.occluded;
8710			const iconVisible = (placeIcon || alwaysShowIcon) && !placedIconBoxes?.occluded;
8711			this.placements[symbolInstance.crossTileID] = new JointPlacement(textVisible, iconVisible, offscreen || bucket.justReloaded);
8712			seenCrossTileIDs[symbolInstance.crossTileID] = true;
8713		};
8714		if (zOrderByViewportY) {
8715			if (bucketPart.symbolInstanceStart !== 0) throw new Error("bucket.bucketInstanceId should be 0");
8716			const symbolIndexes = bucket.getSortedSymbolIndexes(-this.transform.bearingInRadians);
8717			for (let i = symbolIndexes.length - 1; i >= 0; --i) {
8718				const symbolIndex = symbolIndexes[i];
8719				placeSymbol(bucket.symbolInstances.get(symbolIndex), bucket.collisionArrays[symbolIndex], symbolIndex);
8720			}
8721		} else for (let i = bucketPart.symbolInstanceStart; i < bucketPart.symbolInstanceEnd; i++) placeSymbol(bucket.symbolInstances.get(i), bucket.collisionArrays[i], i);
8722		bucket.justReloaded = false;
8723	}
8724	storeCollisionData(bucketInstanceId, symbolIndex, collisionArrays, placedGlyphBoxes, placedIconBoxes, placedGlyphCircles) {
8725		if (collisionArrays.textBox || collisionArrays.iconBox) {
8726			let boxArray;
8727			if (this.collisionBoxArrays.has(bucketInstanceId)) boxArray = this.collisionBoxArrays.get(bucketInstanceId);
8728			else {
8729				boxArray = /* @__PURE__ */ new Map();
8730				this.collisionBoxArrays.set(bucketInstanceId, boxArray);
8731			}
8732			let realCollisionBox;
8733			if (boxArray.has(symbolIndex)) realCollisionBox = boxArray.get(symbolIndex);
8734			else {
8735				realCollisionBox = {
8736					text: null,
8737					icon: null
8738				};
8739				boxArray.set(symbolIndex, realCollisionBox);
8740			}
8741			if (collisionArrays.textBox) realCollisionBox.text = placedGlyphBoxes.box;
8742			if (collisionArrays.iconBox) realCollisionBox.icon = placedIconBoxes.box;
8743		}
8744		if (placedGlyphCircles) {
8745			let circleArray = this.collisionCircleArrays[bucketInstanceId];
8746			if (circleArray === void 0) circleArray = this.collisionCircleArrays[bucketInstanceId] = [];
8747			for (let i = 0; i < placedGlyphCircles.circles.length; i += 4) {
8748				circleArray.push(placedGlyphCircles.circles[i + 0] - 100);
8749				circleArray.push(placedGlyphCircles.circles[i + 1] - 100);
8750				circleArray.push(placedGlyphCircles.circles[i + 2]);
8751				circleArray.push(placedGlyphCircles.collisionDetected ? 1 : 0);
8752			}
8753		}
8754	}
8755	markUsedJustification(bucket, placedAnchor, symbolInstance, orientation) {
8756		const justifications = {
8757			"left": symbolInstance.leftJustifiedTextSymbolIndex,
8758			"center": symbolInstance.centerJustifiedTextSymbolIndex,
8759			"right": symbolInstance.rightJustifiedTextSymbolIndex
8760		};
8761		let autoIndex;
8762		if (orientation === 2) autoIndex = symbolInstance.verticalPlacedTextSymbolIndex;
8763		else autoIndex = justifications[getAnchorJustification(placedAnchor)];
8764		const indexes = [
8765			symbolInstance.leftJustifiedTextSymbolIndex,
8766			symbolInstance.centerJustifiedTextSymbolIndex,
8767			symbolInstance.rightJustifiedTextSymbolIndex,
8768			symbolInstance.verticalPlacedTextSymbolIndex
8769		];
8770		for (const index of indexes) if (index >= 0) {
8771			if (autoIndex >= 0 && index !== autoIndex) bucket.text.placedSymbolArray.get(index).crossTileID = 0;
8772			else bucket.text.placedSymbolArray.get(index).crossTileID = symbolInstance.crossTileID;
8773		}
8774	}
8775	markUsedOrientation(bucket, orientation, symbolInstance) {
8776		const horizontal = orientation === 1 || orientation === 3 ? orientation : 0;
8777		const vertical = orientation === 2 ? orientation : 0;
8778		const horizontalIndexes = [
8779			symbolInstance.leftJustifiedTextSymbolIndex,
8780			symbolInstance.centerJustifiedTextSymbolIndex,
8781			symbolInstance.rightJustifiedTextSymbolIndex
8782		];
8783		for (const index of horizontalIndexes) bucket.text.placedSymbolArray.get(index).placedOrientation = horizontal;
8784		if (symbolInstance.verticalPlacedTextSymbolIndex) bucket.text.placedSymbolArray.get(symbolInstance.verticalPlacedTextSymbolIndex).placedOrientation = vertical;
8785	}
8786	commit(now) {
8787		this.commitTime = now;
8788		this.zoomAtLastRecencyCheck = this.transform.zoom;
8789		const prevPlacement = this.prevPlacement;
8790		let placementChanged = false;
8791		this.prevZoomAdjustment = prevPlacement ? prevPlacement.zoomAdjustment(this.transform.zoom) : 0;
8792		const increment = prevPlacement ? prevPlacement.symbolFadeChange(now) : 1;
8793		const prevOpacities = prevPlacement ? prevPlacement.opacities : {};
8794		const prevOffsets = prevPlacement ? prevPlacement.variableOffsets : {};
8795		const prevOrientations = prevPlacement ? prevPlacement.placedOrientations : {};
8796		for (const crossTileID in this.placements) {
8797			const jointPlacement = this.placements[crossTileID];
8798			const prevOpacity = prevOpacities[crossTileID];
8799			if (prevOpacity) {
8800				this.opacities[crossTileID] = new JointOpacityState(prevOpacity, increment, jointPlacement.text, jointPlacement.icon);
8801				placementChanged ||= jointPlacement.text !== prevOpacity.text.placed;
8802				placementChanged ||= jointPlacement.icon !== prevOpacity.icon.placed;
8803			} else {
8804				this.opacities[crossTileID] = new JointOpacityState(null, increment, jointPlacement.text, jointPlacement.icon, jointPlacement.skipFade);
8805				placementChanged ||= jointPlacement.text || jointPlacement.icon;
8806			}
8807		}
8808		for (const crossTileID in prevOpacities) {
8809			const prevOpacity = prevOpacities[crossTileID];
8810			if (!this.opacities[crossTileID]) {
8811				const jointOpacity = new JointOpacityState(prevOpacity, increment, false, false);
8812				if (!jointOpacity.isHidden()) {
8813					this.opacities[crossTileID] = jointOpacity;
8814					placementChanged ||= prevOpacity.text.placed;
8815					placementChanged ||= prevOpacity.icon.placed;
8816				}
8817			}
8818		}
8819		for (const crossTileID in prevOffsets) if (!this.variableOffsets[crossTileID] && this.opacities[crossTileID] && !this.opacities[crossTileID].isHidden()) this.variableOffsets[crossTileID] = prevOffsets[crossTileID];
8820		for (const crossTileID in prevOrientations) if (!this.placedOrientations[crossTileID] && this.opacities[crossTileID] && !this.opacities[crossTileID].isHidden()) this.placedOrientations[crossTileID] = prevOrientations[crossTileID];
8821		if (prevPlacement && prevPlacement.lastPlacementChangeTime === void 0) throw new Error("Last placement time for previous placement is not defined");
8822		if (placementChanged) this.lastPlacementChangeTime = now;
8823		else if (typeof this.lastPlacementChangeTime !== "number") this.lastPlacementChangeTime = prevPlacement ? prevPlacement.lastPlacementChangeTime : now;
8824	}
8825	updateLayerOpacities(styleLayer, tiles) {
8826		const seenCrossTileIDs = {};
8827		for (const tile of tiles) {
8828			const symbolBucket = tile.getBucket(styleLayer);
8829			if (symbolBucket && tile.latestFeatureIndex && styleLayer.id === symbolBucket.layerIds[0]) this.updateBucketOpacities(symbolBucket, tile.tileID, seenCrossTileIDs, tile.collisionBoxArray);
8830		}
8831	}
8832	updateBucketOpacities(bucket, tileID, seenCrossTileIDs, collisionBoxArray) {
8833		if (bucket.hasTextData()) {
8834			bucket.text.opacityVertexArray.clear();
8835			bucket.text.hasVisibleVertices = false;
8836		}
8837		if (bucket.hasIconData()) {
8838			bucket.icon.opacityVertexArray.clear();
8839			bucket.icon.hasVisibleVertices = false;
8840		}
8841		if (bucket.hasIconCollisionBoxData()) bucket.iconCollisionBox.collisionVertexArray.clear();
8842		if (bucket.hasTextCollisionBoxData()) bucket.textCollisionBox.collisionVertexArray.clear();
8843		const layer = bucket.layers[0];
8844		const layout = layer.layout;
8845		const duplicateOpacityState = new JointOpacityState(null, 0, false, false, true);
8846		const textAllowOverlap = layout.get("text-allow-overlap");
8847		const iconAllowOverlap = layout.get("icon-allow-overlap");
8848		const hasVariablePlacement = layer._unevaluatedLayout.hasValue("text-variable-anchor") || layer._unevaluatedLayout.hasValue("text-variable-anchor-offset");
8849		const rotateWithMap = layout.get("text-rotation-alignment") === "map";
8850		const pitchWithMap = layout.get("text-pitch-alignment") === "map";
8851		const hasIconTextFit = layout.get("icon-text-fit") !== "none";
8852		const defaultOpacityState = new JointOpacityState(null, 0, textAllowOverlap && (iconAllowOverlap || !bucket.hasIconData() || layout.get("icon-optional")), iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || layout.get("text-optional")), true);
8853		if (!bucket.collisionArrays && collisionBoxArray && (bucket.hasIconCollisionBoxData() || bucket.hasTextCollisionBoxData())) bucket.deserializeCollisionBoxes(collisionBoxArray);
8854		const addOpacities = (iconOrText, numVertices, opacity) => {
8855			for (let i = 0; i < numVertices / 4; i++) iconOrText.opacityVertexArray.emplaceBack(opacity);
8856			iconOrText.hasVisibleVertices ||= opacity !== PACKED_HIDDEN_OPACITY;
8857		};
8858		const boxArrays = this.collisionBoxArrays.get(bucket.bucketInstanceId);
8859		for (let s = 0; s < bucket.symbolInstances.length; s++) {
8860			const symbolInstance = bucket.symbolInstances.get(s);
8861			const { numHorizontalGlyphVertices, numVerticalGlyphVertices, crossTileID } = symbolInstance;
8862			const isDuplicate = seenCrossTileIDs[crossTileID];
8863			let opacityState = this.opacities[crossTileID];
8864			if (isDuplicate) opacityState = duplicateOpacityState;
8865			else if (!opacityState) {
8866				opacityState = defaultOpacityState;
8867				this.opacities[crossTileID] = opacityState;
8868			}
8869			seenCrossTileIDs[crossTileID] = true;
8870			const hasText = numHorizontalGlyphVertices > 0 || numVerticalGlyphVertices > 0;
8871			const hasIcon = symbolInstance.numIconVertices > 0;
8872			const placedOrientation = this.placedOrientations[symbolInstance.crossTileID];
8873			const horizontalHidden = placedOrientation === 2;
8874			const verticalHidden = placedOrientation === 1 || placedOrientation === 3;
8875			if (hasText) {
8876				const packedOpacity = packOpacity(opacityState.text);
8877				const horizontalOpacity = horizontalHidden ? PACKED_HIDDEN_OPACITY : packedOpacity;
8878				addOpacities(bucket.text, numHorizontalGlyphVertices, horizontalOpacity);
8879				const verticalOpacity = verticalHidden ? PACKED_HIDDEN_OPACITY : packedOpacity;
8880				addOpacities(bucket.text, numVerticalGlyphVertices, verticalOpacity);
8881				const symbolHidden = opacityState.text.isHidden();
8882				const textSymbolIndexes = [
8883					symbolInstance.rightJustifiedTextSymbolIndex,
8884					symbolInstance.centerJustifiedTextSymbolIndex,
8885					symbolInstance.leftJustifiedTextSymbolIndex
8886				];
8887				for (const index of textSymbolIndexes) if (index >= 0) bucket.text.placedSymbolArray.get(index).hidden = symbolHidden || horizontalHidden ? 1 : 0;
8888				if (symbolInstance.verticalPlacedTextSymbolIndex >= 0) bucket.text.placedSymbolArray.get(symbolInstance.verticalPlacedTextSymbolIndex).hidden = symbolHidden || verticalHidden ? 1 : 0;
8889				const prevOffset = this.variableOffsets[symbolInstance.crossTileID];
8890				if (prevOffset) this.markUsedJustification(bucket, prevOffset.anchor, symbolInstance, placedOrientation);
8891				const prevOrientation = this.placedOrientations[symbolInstance.crossTileID];
8892				if (prevOrientation) {
8893					this.markUsedJustification(bucket, "left", symbolInstance, prevOrientation);
8894					this.markUsedOrientation(bucket, prevOrientation, symbolInstance);
8895				}
8896			}
8897			if (hasIcon) {
8898				const packedOpacity = packOpacity(opacityState.icon);
8899				const useHorizontal = !(hasIconTextFit && symbolInstance.verticalPlacedIconSymbolIndex && horizontalHidden);
8900				if (symbolInstance.placedIconSymbolIndex >= 0) {
8901					const horizontalOpacity = useHorizontal ? packedOpacity : PACKED_HIDDEN_OPACITY;
8902					addOpacities(bucket.icon, symbolInstance.numIconVertices, horizontalOpacity);
8903					bucket.icon.placedSymbolArray.get(symbolInstance.placedIconSymbolIndex).hidden = opacityState.icon.isHidden();
8904				}
8905				if (symbolInstance.verticalPlacedIconSymbolIndex >= 0) {
8906					const verticalOpacity = !useHorizontal ? packedOpacity : PACKED_HIDDEN_OPACITY;
8907					addOpacities(bucket.icon, symbolInstance.numVerticalIconVertices, verticalOpacity);
8908					bucket.icon.placedSymbolArray.get(symbolInstance.verticalPlacedIconSymbolIndex).hidden = opacityState.icon.isHidden();
8909				}
8910			}
8911			const realBoxes = boxArrays?.has(s) ? boxArrays.get(s) : {
8912				text: null,
8913				icon: null
8914			};
8915			if (bucket.hasIconCollisionBoxData() || bucket.hasTextCollisionBoxData()) {
8916				const collisionArrays = bucket.collisionArrays[s];
8917				if (collisionArrays) {
8918					let shift = new Point(0, 0);
8919					if (collisionArrays.textBox || collisionArrays.verticalTextBox) {
8920						let used = true;
8921						if (hasVariablePlacement) {
8922							const variableOffset = this.variableOffsets[crossTileID];
8923							if (variableOffset) {
8924								shift = calculateVariableLayoutShift(variableOffset.anchor, variableOffset.width, variableOffset.height, variableOffset.textOffset, variableOffset.textBoxScale);
8925								if (rotateWithMap) shift._rotate(pitchWithMap ? -this.transform.bearingInRadians : this.transform.bearingInRadians);
8926							} else used = false;
8927						}
8928						if (collisionArrays.textBox || collisionArrays.verticalTextBox) {
8929							let hidden;
8930							if (collisionArrays.textBox) hidden = horizontalHidden;
8931							if (collisionArrays.verticalTextBox) hidden = verticalHidden;
8932							updateCollisionVertices(bucket.textCollisionBox.collisionVertexArray, opacityState.text.placed, !used || hidden, realBoxes.text, shift.x, shift.y);
8933						}
8934					}
8935					if (collisionArrays.iconBox || collisionArrays.verticalIconBox) {
8936						const verticalIconUsed = Boolean(!verticalHidden && collisionArrays.verticalIconBox);
8937						let hidden;
8938						if (collisionArrays.iconBox) hidden = verticalIconUsed;
8939						if (collisionArrays.verticalIconBox) hidden = !verticalIconUsed;
8940						updateCollisionVertices(bucket.iconCollisionBox.collisionVertexArray, opacityState.icon.placed, hidden, realBoxes.icon, hasIconTextFit ? shift.x : 0, hasIconTextFit ? shift.y : 0);
8941					}
8942				}
8943			}
8944		}
8945		bucket.sortFeatures(-this.transform.bearingInRadians);
8946		if (this.retainedQueryData[bucket.bucketInstanceId]) this.retainedQueryData[bucket.bucketInstanceId].featureSortOrder = bucket.featureSortOrder;
8947		if (bucket.hasTextData() && bucket.text.opacityVertexBuffer) bucket.text.opacityVertexBuffer.updateData(bucket.text.opacityVertexArray);
8948		if (bucket.hasIconData() && bucket.icon.opacityVertexBuffer) bucket.icon.opacityVertexBuffer.updateData(bucket.icon.opacityVertexArray);
8949		if (bucket.hasIconCollisionBoxData() && bucket.iconCollisionBox.collisionVertexBuffer) bucket.iconCollisionBox.collisionVertexBuffer.updateData(bucket.iconCollisionBox.collisionVertexArray);
8950		if (bucket.hasTextCollisionBoxData() && bucket.textCollisionBox.collisionVertexBuffer) bucket.textCollisionBox.collisionVertexBuffer.updateData(bucket.textCollisionBox.collisionVertexArray);
8951		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`);
8952		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`);
8953		if (bucket.bucketInstanceId in this.collisionCircleArrays) {
8954			bucket.collisionCircleArray = this.collisionCircleArrays[bucket.bucketInstanceId];
8955			delete this.collisionCircleArrays[bucket.bucketInstanceId];
8956		}
8957	}
8958	symbolFadeChange(now) {
8959		return this.fadeDuration === 0 ? 1 : (now - this.commitTime) / this.fadeDuration + this.prevZoomAdjustment;
8960	}
8961	zoomAdjustment(zoom) {
8962		return Math.max(0, (this.transform.zoom - zoom) / 1.5);
8963	}
8964	hasTransitions(now) {
8965		return this.stale || now - this.lastPlacementChangeTime < this.fadeDuration;
8966	}
8967	stillRecent(now, zoom) {
8968		const durationAdjustment = this.zoomAtLastRecencyCheck === zoom ? 1 - this.zoomAdjustment(zoom) : 1;
8969		this.zoomAtLastRecencyCheck = zoom;
8970		return this.commitTime + this.fadeDuration * durationAdjustment > now;
8971	}
8972	setStale() {
8973		this.stale = true;
8974	}
8975};
8976function updateCollisionVertices(collisionVertexArray, placed, notUsed, realBox, shiftX, shiftY) {
8977	if (!realBox || realBox.length === 0) realBox = [
8978		0,
8979		0,
8980		0,
8981		0
8982	];
8983	const tlX = realBox[0] - 100;
8984	const tlY = realBox[1] - 100;
8985	const brX = realBox[2] - 100;
8986	const brY = realBox[3] - 100;
8987	collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0, tlX, tlY);
8988	collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0, brX, tlY);
8989	collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0, brX, brY);
8990	collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0, tlX, brY);
8991}
8992const shift25 = Math.pow(2, 25);
8993const shift24 = Math.pow(2, 24);
8994const shift17 = Math.pow(2, 17);
8995const shift16 = Math.pow(2, 16);
8996const shift9 = Math.pow(2, 9);
8997const shift8 = Math.pow(2, 8);
8998const shift1 = Math.pow(2, 1);
8999function packOpacity(opacityState) {
9000	if (opacityState.opacity === 0 && !opacityState.placed) return 0;
9001	else if (opacityState.opacity === 1 && opacityState.placed) return 4294967295;
9002	const targetBit = opacityState.placed ? 1 : 0;
9003	const opacityBits = Math.floor(opacityState.opacity * 127);
9004	return opacityBits * shift25 + targetBit * shift24 + opacityBits * shift17 + targetBit * shift16 + opacityBits * shift9 + targetBit * shift8 + opacityBits * shift1 + targetBit;
9005}
9006const PACKED_HIDDEN_OPACITY = 0;
9007//#endregion
9008//#region src/style/pauseable_placement.ts
9009var LayerPlacement = class {
9010	constructor(styleLayer) {
9011		this._sortAcrossTiles = styleLayer.layout.get("symbol-z-order") !== "viewport-y" && !styleLayer.layout.get("symbol-sort-key").isConstant();
9012		this._currentTileIndex = 0;
9013		this._currentPartIndex = 0;
9014		this._seenCrossTileIDs = {};
9015		this._bucketParts = [];
9016	}
9017	continuePlacement(tiles, placement, showCollisionBoxes, styleLayer, shouldPausePlacement) {
9018		const bucketParts = this._bucketParts;
9019		while (this._currentTileIndex < tiles.length) {
9020			const tile = tiles[this._currentTileIndex];
9021			placement.getBucketParts(bucketParts, styleLayer, tile, this._sortAcrossTiles);
9022			this._currentTileIndex++;
9023			if (shouldPausePlacement()) return true;
9024		}
9025		if (this._sortAcrossTiles) {
9026			this._sortAcrossTiles = false;
9027			bucketParts.sort((a, b) => a.sortKey - b.sortKey);
9028		}
9029		while (this._currentPartIndex < bucketParts.length) {
9030			const bucketPart = bucketParts[this._currentPartIndex];
9031			placement.placeLayerBucketPart(bucketPart, this._seenCrossTileIDs, showCollisionBoxes);
9032			this._currentPartIndex++;
9033			if (shouldPausePlacement()) return true;
9034		}
9035		return false;
9036	}
9037};
9038var PauseablePlacement = class {
9039	constructor(transform, terrain, order, forceFullPlacement, showCollisionBoxes, fadeDuration, crossSourceCollisions, prevPlacement) {
9040		this.placement = new Placement(transform, terrain, fadeDuration, crossSourceCollisions, prevPlacement);
9041		this._currentPlacementIndex = order.length - 1;
9042		this._forceFullPlacement = forceFullPlacement;
9043		this._showCollisionBoxes = showCollisionBoxes;
9044		this._done = false;
9045	}
9046	isDone() {
9047		return this._done;
9048	}
9049	continuePlacement(order, layers, layerTiles) {
9050		const startTime = now();
9051		const shouldPausePlacement = () => {
9052			return this._forceFullPlacement ? false : now() - startTime > 2;
9053		};
9054		while (this._currentPlacementIndex >= 0) {
9055			const layer = layers[order[this._currentPlacementIndex]];
9056			const placementZoom = this.placement.collisionIndex.transform.zoom;
9057			if (isSymbolStyleLayer(layer) && layer.layout && (!layer.minzoom || layer.minzoom <= placementZoom) && (!layer.maxzoom || layer.maxzoom > placementZoom)) {
9058				this._inProgressLayer ||= new LayerPlacement(layer);
9059				if (this._inProgressLayer.continuePlacement(layerTiles[layer.source], this.placement, this._showCollisionBoxes, layer, shouldPausePlacement)) return;
9060				delete this._inProgressLayer;
9061			}
9062			this._currentPlacementIndex--;
9063		}
9064		this._done = true;
9065	}
9066	commit(now) {
9067		this.placement.commit(now);
9068		return this.placement;
9069	}
9070};
9071//#endregion
9072//#region node_modules/kdbush/index.js
9073const ARRAY_TYPES = [
9074	Int8Array,
9075	Uint8Array,
9076	Uint8ClampedArray,
9077	Int16Array,
9078	Uint16Array,
9079	Int32Array,
9080	Uint32Array,
9081	Float32Array,
9082	Float64Array
9083];
9084/** @typedef {Int8ArrayConstructor | Uint8ArrayConstructor | Uint8ClampedArrayConstructor | Int16ArrayConstructor | Uint16ArrayConstructor | Int32ArrayConstructor | Uint32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor} TypedArrayConstructor */
9085/** @typedef {Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array} TypedArray */
9086const VERSION = 1;
9087const HEADER_SIZE = 8;
9088const STACK = /* @__PURE__ */ new Uint32Array(96);
9089var KDBush = class KDBush {
9090	/**
9091	* Creates an index from raw `ArrayBuffer` data.
9092	* @param {ArrayBufferLike} data
9093	*/
9094	static from(data) {
9095		if (!data || data.byteLength === void 0 || data.buffer) throw new Error("Data must be an instance of ArrayBuffer or SharedArrayBuffer.");
9096		const [magic, versionAndType] = new Uint8Array(data, 0, 2);
9097		if (magic !== 219) throw new Error("Data does not appear to be in a KDBush format.");
9098		const version = versionAndType >> 4;
9099		if (version !== VERSION) throw new Error(`Got v${version} data when expected v${VERSION}.`);
9100		const ArrayType = ARRAY_TYPES[versionAndType & 15];
9101		if (!ArrayType) throw new Error("Unrecognized array type.");
9102		const [nodeSize] = new Uint16Array(data, 2, 1);
9103		const [numItems] = new Uint32Array(data, 4, 1);
9104		return new KDBush(numItems, nodeSize, ArrayType, void 0, data);
9105	}
9106	/**
9107	* Creates an index that will hold a given number of items.
9108	* @param {number} numItems
9109	* @param {number} [nodeSize=64] Size of the KD-tree node (64 by default).
9110	* @param {TypedArrayConstructor} [ArrayType=Float64Array] The array type used for coordinates storage (`Float64Array` by default).
9111	* @param {ArrayBufferConstructor | SharedArrayBufferConstructor} [ArrayBufferType=ArrayBuffer] The array buffer type used for storage (`ArrayBuffer` by default).
9112	* @param {ArrayBufferLike} [data] (For internal use only)
9113	*/
9114	constructor(numItems, nodeSize = 64, ArrayType = Float64Array, ArrayBufferType = ArrayBuffer, data) {
9115		if (isNaN(numItems) || numItems < 0) throw new Error(`Unexpected numItems value: ${numItems}.`);
9116		this.numItems = +numItems;
9117		this.nodeSize = Math.min(Math.max(+nodeSize, 2), 65535);
9118		this.ArrayType = ArrayType;
9119		this.IndexArrayType = numItems < 65536 ? Uint16Array : Uint32Array;
9120		const arrayTypeIndex = ARRAY_TYPES.indexOf(this.ArrayType);
9121		const coordsByteSize = numItems * 2 * this.ArrayType.BYTES_PER_ELEMENT;
9122		const idsByteSize = numItems * this.IndexArrayType.BYTES_PER_ELEMENT;
9123		const padCoords = (8 - idsByteSize % 8) % 8;
9124		if (arrayTypeIndex < 0) throw new Error(`Unexpected typed array class: ${ArrayType}.`);
9125		if (data) {
9126			this.data = data;
9127			this.ids = new this.IndexArrayType(data, HEADER_SIZE, numItems);
9128			this.coords = new ArrayType(data, HEADER_SIZE + idsByteSize + padCoords, numItems * 2);
9129			this._pos = numItems * 2;
9130			this._finished = true;
9131		} else {
9132			const data = this.data = new ArrayBufferType(HEADER_SIZE + coordsByteSize + idsByteSize + padCoords);
9133			this.ids = new this.IndexArrayType(data, HEADER_SIZE, numItems);
9134			this.coords = new ArrayType(data, HEADER_SIZE + idsByteSize + padCoords, numItems * 2);
9135			this._pos = 0;
9136			this._finished = false;
9137			new Uint8Array(data, 0, 2).set([219, 16 + arrayTypeIndex]);
9138			new Uint16Array(data, 2, 1)[0] = nodeSize;
9139			new Uint32Array(data, 4, 1)[0] = numItems;
9140		}
9141	}
9142	/**
9143	* Add a point to the index.
9144	* @param {number} x
9145	* @param {number} y
9146	* @returns {number} An incremental index associated with the added item (starting from `0`).
9147	*/
9148	add(x, y) {
9149		const index = this._pos >> 1;
9150		this.ids[index] = index;
9151		this.coords[this._pos++] = x;
9152		this.coords[this._pos++] = y;
9153		return index;
9154	}
9155	/**
9156	* Perform indexing of the added points.
9157	*/
9158	finish() {
9159		const numAdded = this._pos >> 1;
9160		if (numAdded !== this.numItems) throw new Error(`Added ${numAdded} items when expected ${this.numItems}.`);
9161		sort(this.ids, this.coords, this.nodeSize, 0, this.numItems - 1, 0);
9162		this._finished = true;
9163		return this;
9164	}
9165	/**
9166	* Search the index for items within a given bounding box.
9167	* @param {number} minX
9168	* @param {number} minY
9169	* @param {number} maxX
9170	* @param {number} maxY
9171	* @returns {number[]} An array of indices correponding to the found items.
9172	*/
9173	range(minX, minY, maxX, maxY) {
9174		if (!this._finished) throw new Error("Data not yet indexed - call index.finish().");
9175		const { ids, coords, nodeSize } = this;
9176		STACK[0] = 0;
9177		STACK[1] = ids.length - 1;
9178		STACK[2] = 0;
9179		let sp = 3;
9180		const result = [];
9181		while (sp > 0) {
9182			const axis = STACK[--sp];
9183			const right = STACK[--sp];
9184			const left = STACK[--sp];
9185			if (right - left <= nodeSize) {
9186				for (let i = left; i <= right; i++) {
9187					const x = coords[2 * i];
9188					const y = coords[2 * i + 1];
9189					if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[i]);
9190				}
9191				continue;
9192			}
9193			const m = left + right >> 1;
9194			const x = coords[2 * m];
9195			const y = coords[2 * m + 1];
9196			if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[m]);
9197			if (axis === 0 ? minX <= x : minY <= y) {
9198				STACK[sp++] = left;
9199				STACK[sp++] = m - 1;
9200				STACK[sp++] = 1 - axis;
9201			}
9202			if (axis === 0 ? maxX >= x : maxY >= y) {
9203				STACK[sp++] = m + 1;
9204				STACK[sp++] = right;
9205				STACK[sp++] = 1 - axis;
9206			}
9207		}
9208		return result;
9209	}
9210	/**
9211	* Search the index for items within a given radius.
9212	* @param {number} qx
9213	* @param {number} qy
9214	* @param {number} r Query radius.
9215	* @returns {number[]} An array of indices correponding to the found items.
9216	*/
9217	within(qx, qy, r) {
9218		const result = [];
9219		this.withinInto(qx, qy, r, result);
9220		return result;
9221	}
9222	/**
9223	* Search the index for items within a given radius, writing matching ids into `out`
9224	* via indexed assignment (`out[i] = id`). Accepts any indexed-writable container —
9225	* a typed array sized to the expected upper bound (allocation-free, fast) or a plain
9226	* `Array` (which will grow as needed). Returns the number of matches written.
9227	* @param {number} qx
9228	* @param {number} qy
9229	* @param {number} r Query radius.
9230	* @param {number[] | TypedArray} out Container to write matching ids into.
9231	* @returns {number} The number of matches written to `out`.
9232	*/
9233	withinInto(qx, qy, r, out) {
9234		if (!this._finished) throw new Error("Data not yet indexed - call index.finish().");
9235		const { ids, coords, nodeSize } = this;
9236		STACK[0] = 0;
9237		STACK[1] = ids.length - 1;
9238		STACK[2] = 0;
9239		let sp = 3;
9240		let count = 0;
9241		const r2 = r * r;
9242		while (sp > 0) {
9243			const axis = STACK[--sp];
9244			const right = STACK[--sp];
9245			const left = STACK[--sp];
9246			if (right - left <= nodeSize) {
9247				for (let i = left; i <= right; i++) if (sqDist(coords[2 * i], coords[2 * i + 1], qx, qy) <= r2) out[count++] = ids[i];
9248				continue;
9249			}
9250			const m = left + right >> 1;
9251			const x = coords[2 * m];
9252			const y = coords[2 * m + 1];
9253			if (sqDist(x, y, qx, qy) <= r2) out[count++] = ids[m];
9254			if (axis === 0 ? qx - r <= x : qy - r <= y) {
9255				STACK[sp++] = left;
9256				STACK[sp++] = m - 1;
9257				STACK[sp++] = 1 - axis;
9258			}
9259			if (axis === 0 ? qx + r >= x : qy + r >= y) {
9260				STACK[sp++] = m + 1;
9261				STACK[sp++] = right;
9262				STACK[sp++] = 1 - axis;
9263			}
9264		}
9265		return count;
9266	}
9267};
9268/**
9269* @param {Uint16Array | Uint32Array} ids
9270* @param {TypedArray} coords
9271* @param {number} nodeSize
9272* @param {number} left
9273* @param {number} right
9274* @param {number} axis
9275*/
9276function sort(ids, coords, nodeSize, left, right, axis) {
9277	if (right - left <= nodeSize) return;
9278	const m = left + right >> 1;
9279	select(ids, coords, m, left, right, axis);
9280	sort(ids, coords, nodeSize, left, m - 1, 1 - axis);
9281	sort(ids, coords, nodeSize, m + 1, right, 1 - axis);
9282}
9283/**
9284* Custom Floyd-Rivest selection algorithm: sort ids and coords so that
9285* [left..k-1] items are smaller than k-th item (on either x or y axis)
9286* @param {Uint16Array | Uint32Array} ids
9287* @param {TypedArray} coords
9288* @param {number} k
9289* @param {number} left
9290* @param {number} right
9291* @param {number} axis
9292*/
9293function select(ids, coords, k, left, right, axis) {
9294	while (right > left) {
9295		if (right - left > 600) {
9296			const n = right - left + 1;
9297			const m = k - left + 1;
9298			const z = Math.log(n);
9299			const s = .5 * Math.exp(2 * z / 3);
9300			const sd = .5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
9301			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);
9302		}
9303		const t = coords[2 * k + axis];
9304		let i = left;
9305		let j = right;
9306		swapItem(ids, coords, left, k);
9307		if (coords[2 * right + axis] > t) swapItem(ids, coords, left, right);
9308		while (i < j) {
9309			swapItem(ids, coords, i, j);
9310			i++;
9311			j--;
9312			while (coords[2 * i + axis] < t) i++;
9313			while (coords[2 * j + axis] > t) j--;
9314		}
9315		if (coords[2 * left + axis] === t) swapItem(ids, coords, left, j);
9316		else {
9317			j++;
9318			swapItem(ids, coords, j, right);
9319		}
9320		if (j <= k) left = j + 1;
9321		if (k <= j) right = j - 1;
9322	}
9323}
9324/**
9325* @param {Uint16Array | Uint32Array} ids
9326* @param {TypedArray} coords
9327* @param {number} i
9328* @param {number} j
9329*/
9330function swapItem(ids, coords, i, j) {
9331	swap(ids, i, j);
9332	swap(coords, 2 * i, 2 * j);
9333	swap(coords, 2 * i + 1, 2 * j + 1);
9334}
9335/**
9336* @param {TypedArray} arr
9337* @param {number} i
9338* @param {number} j
9339*/
9340function swap(arr, i, j) {
9341	const tmp = arr[i];
9342	arr[i] = arr[j];
9343	arr[j] = tmp;
9344}
9345/**
9346* @param {number} ax
9347* @param {number} ay
9348* @param {number} bx
9349* @param {number} by
9350*/
9351function sqDist(ax, ay, bx, by) {
9352	const dx = ax - bx;
9353	const dy = ay - by;
9354	return dx * dx + dy * dy;
9355}
9356//#endregion
9357//#region src/symbol/cross_tile_symbol_index.ts
9358const roundingFactor = 512 / EXTENT / 2;
9359var TileLayerIndex = class {
9360	constructor(tileID, symbolInstances, bucketInstanceId) {
9361		this.tileID = tileID;
9362		this.bucketInstanceId = bucketInstanceId;
9363		this._symbolsByKey = {};
9364		const symbolInstancesByKey = /* @__PURE__ */ new Map();
9365		for (let i = 0; i < symbolInstances.length; i++) {
9366			const symbolInstance = symbolInstances.get(i);
9367			const key = symbolInstance.key;
9368			const instances = symbolInstancesByKey.get(key);
9369			if (instances) instances.push(symbolInstance);
9370			else symbolInstancesByKey.set(key, [symbolInstance]);
9371		}
9372		for (const [key, symbols] of symbolInstancesByKey) {
9373			const entry = {
9374				positions: symbols.map((symbolInstance) => ({
9375					x: Math.floor(symbolInstance.anchorX * roundingFactor),
9376					y: Math.floor(symbolInstance.anchorY * roundingFactor)
9377				})),
9378				crossTileIDs: symbols.map((v) => v.crossTileID)
9379			};
9380			if (entry.positions.length > 128) {
9381				const index = new KDBush(entry.positions.length, 16, Uint16Array);
9382				for (const { x, y } of entry.positions) index.add(x, y);
9383				index.finish();
9384				delete entry.positions;
9385				entry.index = index;
9386			}
9387			this._symbolsByKey[key] = entry;
9388		}
9389	}
9390	getScaledCoordinates(symbolInstance, childTileID) {
9391		const { x: localX, y: localY, z: localZ } = this.tileID.canonical;
9392		const { x, y, z } = childTileID.canonical;
9393		const zDifference = z - localZ;
9394		const scale = roundingFactor / Math.pow(2, zDifference);
9395		const xWorld = (x * EXTENT + symbolInstance.anchorX) * scale;
9396		const yWorld = (y * EXTENT + symbolInstance.anchorY) * scale;
9397		const xOffset = localX * EXTENT * roundingFactor;
9398		const yOffset = localY * EXTENT * roundingFactor;
9399		return {
9400			x: Math.floor(xWorld - xOffset),
9401			y: Math.floor(yWorld - yOffset)
9402		};
9403	}
9404	findMatches(symbolInstances, newTileID, zoomCrossTileIDs) {
9405		const tolerance = this.tileID.canonical.z < newTileID.canonical.z ? 1 : Math.pow(2, this.tileID.canonical.z - newTileID.canonical.z);
9406		for (let i = 0; i < symbolInstances.length; i++) {
9407			const symbolInstance = symbolInstances.get(i);
9408			if (symbolInstance.crossTileID) continue;
9409			const entry = this._symbolsByKey[symbolInstance.key];
9410			if (!entry) continue;
9411			const scaledSymbolCoord = this.getScaledCoordinates(symbolInstance, newTileID);
9412			if (entry.index) this.findMatchesForIndexedEntry(entry, symbolInstance, scaledSymbolCoord, tolerance, zoomCrossTileIDs);
9413			else if (entry.positions) this.findMatchesForNonIndexedEntry(entry, symbolInstance, scaledSymbolCoord, tolerance, zoomCrossTileIDs);
9414		}
9415	}
9416	/**
9417	* Matches a symbol instance against an entry backed by a KDBush spatial index
9418	* (used for keys with more than {@link KDBUSH_THRESHHOLD} symbols).
9419	*
9420	* Matches any symbol with the same key whose coordinates are within one grid
9421	* unit (with a 4px grid, this covers a 12px by 12px area). The lowest-index
9422	* unclaimed candidate is claimed in a single pass rather than sorting the
9423	* whole result set on every query: `range()` can return many candidates where
9424	* symbols are coincident (e.g. stacked point markers), so a per-query sort
9425	* would dominate placement cost on dense layers.
9426	*
9427	* Once a symbol is marked duplicate against a parent symbol, no other symbol
9428	* at the same zoom level may duplicate against the same parent (see issue #5993).
9429	*/
9430	findMatchesForIndexedEntry(entry, symbolInstance, scaledSymbolCoord, tolerance, zoomCrossTileIDs) {
9431		const indexes = entry.index.range(scaledSymbolCoord.x - tolerance, scaledSymbolCoord.y - tolerance, scaledSymbolCoord.x + tolerance, scaledSymbolCoord.y + tolerance);
9432		let bestIndex = Infinity;
9433		for (const candidate of indexes) if (candidate < bestIndex && !zoomCrossTileIDs[entry.crossTileIDs[candidate]]) bestIndex = candidate;
9434		if (bestIndex !== Infinity) {
9435			const crossTileID = entry.crossTileIDs[bestIndex];
9436			zoomCrossTileIDs[crossTileID] = true;
9437			symbolInstance.crossTileID = crossTileID;
9438		}
9439	}
9440	/**
9441	* Matches a symbol instance against an entry that stores its positions as a
9442	* plain array (used for keys with at most {@link KDBUSH_THRESHHOLD} symbols).
9443	*
9444	* Matches any symbol with the same key whose coordinates are within one grid
9445	* unit (with a 4px grid, this covers a 12px by 12px area). Positions are
9446	* stored in symbol-instance order, so the first unclaimed match is also the
9447	* lowest-index one.
9448	*
9449	* Once a symbol is marked duplicate against a parent symbol, no other symbol
9450	* at the same zoom level may duplicate against the same parent (see issue #5993).
9451	*/
9452	findMatchesForNonIndexedEntry(entry, symbolInstance, scaledSymbolCoord, tolerance, zoomCrossTileIDs) {
9453		for (let i = 0; i < entry.positions.length; i++) {
9454			const thisTileSymbol = entry.positions[i];
9455			const crossTileID = entry.crossTileIDs[i];
9456			if (Math.abs(thisTileSymbol.x - scaledSymbolCoord.x) <= tolerance && Math.abs(thisTileSymbol.y - scaledSymbolCoord.y) <= tolerance && !zoomCrossTileIDs[crossTileID]) {
9457				zoomCrossTileIDs[crossTileID] = true;
9458				symbolInstance.crossTileID = crossTileID;
9459				break;
9460			}
9461		}
9462	}
9463	getCrossTileIDsLists() {
9464		return Object.values(this._symbolsByKey).map(({ crossTileIDs }) => crossTileIDs);
9465	}
9466};
9467var CrossTileIDs = class {
9468	constructor() {
9469		this.maxCrossTileID = 0;
9470	}
9471	generate() {
9472		return ++this.maxCrossTileID;
9473	}
9474};
9475var CrossTileSymbolLayerIndex = class {
9476	constructor() {
9477		this.indexes = {};
9478		this.usedCrossTileIDs = {};
9479		this.lng = 0;
9480	}
9481	handleWrapJump(lng) {
9482		const wrapDelta = Math.round((lng - this.lng) / 360);
9483		if (wrapDelta !== 0) for (const zoom in this.indexes) {
9484			const zoomIndexes = this.indexes[zoom];
9485			const newZoomIndex = {};
9486			for (const key in zoomIndexes) {
9487				const index = zoomIndexes[key];
9488				index.tileID = index.tileID.unwrapTo(index.tileID.wrap + wrapDelta);
9489				newZoomIndex[index.tileID.key] = index;
9490			}
9491			this.indexes[zoom] = newZoomIndex;
9492		}
9493		this.lng = lng;
9494	}
9495	addBucket(tileID, bucket, crossTileIDs) {
9496		if (this.indexes[tileID.overscaledZ]?.[tileID.key]) {
9497			if (this.indexes[tileID.overscaledZ][tileID.key].bucketInstanceId === bucket.bucketInstanceId) return false;
9498			else this.removeBucketCrossTileIDs(tileID.overscaledZ, this.indexes[tileID.overscaledZ][tileID.key]);
9499		}
9500		for (let i = 0; i < bucket.symbolInstances.length; i++) {
9501			const symbolInstance = bucket.symbolInstances.get(i);
9502			symbolInstance.crossTileID = 0;
9503		}
9504		this.usedCrossTileIDs[tileID.overscaledZ] ||= {};
9505		const zoomCrossTileIDs = this.usedCrossTileIDs[tileID.overscaledZ];
9506		for (const zoom in this.indexes) {
9507			const zoomIndexes = this.indexes[zoom];
9508			if (Number(zoom) > tileID.overscaledZ) for (const id in zoomIndexes) {
9509				const childIndex = zoomIndexes[id];
9510				if (childIndex.tileID.isChildOf(tileID)) childIndex.findMatches(bucket.symbolInstances, tileID, zoomCrossTileIDs);
9511			}
9512			else {
9513				const parentIndex = zoomIndexes[tileID.scaledTo(Number(zoom)).key];
9514				if (parentIndex) parentIndex.findMatches(bucket.symbolInstances, tileID, zoomCrossTileIDs);
9515			}
9516		}
9517		for (let i = 0; i < bucket.symbolInstances.length; i++) {
9518			const symbolInstance = bucket.symbolInstances.get(i);
9519			if (!symbolInstance.crossTileID) {
9520				symbolInstance.crossTileID = crossTileIDs.generate();
9521				zoomCrossTileIDs[symbolInstance.crossTileID] = true;
9522			}
9523		}
9524		if (this.indexes[tileID.overscaledZ] === void 0) this.indexes[tileID.overscaledZ] = {};
9525		this.indexes[tileID.overscaledZ][tileID.key] = new TileLayerIndex(tileID, bucket.symbolInstances, bucket.bucketInstanceId);
9526		return true;
9527	}
9528	removeBucketCrossTileIDs(zoom, removedBucket) {
9529		for (const crossTileIDs of removedBucket.getCrossTileIDsLists()) for (const crossTileID of crossTileIDs) delete this.usedCrossTileIDs[zoom][crossTileID];
9530	}
9531	removeStaleBuckets(currentIDs) {
9532		let tilesChanged = false;
9533		for (const z in this.indexes) {
9534			const zoomIndexes = this.indexes[z];
9535			for (const tileKey in zoomIndexes) if (!currentIDs[zoomIndexes[tileKey].bucketInstanceId]) {
9536				this.removeBucketCrossTileIDs(z, zoomIndexes[tileKey]);
9537				delete zoomIndexes[tileKey];
9538				tilesChanged = true;
9539			}
9540		}
9541		return tilesChanged;
9542	}
9543};
9544var CrossTileSymbolIndex = class {
9545	constructor() {
9546		this.layerIndexes = {};
9547		this.crossTileIDs = new CrossTileIDs();
9548		this.maxBucketInstanceId = 0;
9549		this.bucketsInCurrentPlacement = {};
9550	}
9551	addLayer(styleLayer, tiles, lng) {
9552		let layerIndex = this.layerIndexes[styleLayer.id];
9553		if (layerIndex === void 0) layerIndex = this.layerIndexes[styleLayer.id] = new CrossTileSymbolLayerIndex();
9554		let symbolBucketsChanged = false;
9555		const currentBucketIDs = {};
9556		layerIndex.handleWrapJump(lng);
9557		for (const tile of tiles) {
9558			const symbolBucket = tile.getBucket(styleLayer);
9559			if (styleLayer.id !== symbolBucket?.layerIds[0]) continue;
9560			if (!symbolBucket.bucketInstanceId) {
9561				this.maxBucketInstanceId += 1;
9562				symbolBucket.bucketInstanceId = this.maxBucketInstanceId;
9563			}
9564			if (layerIndex.addBucket(tile.tileID, symbolBucket, this.crossTileIDs)) symbolBucketsChanged = true;
9565			currentBucketIDs[symbolBucket.bucketInstanceId] = true;
9566		}
9567		if (layerIndex.removeStaleBuckets(currentBucketIDs)) symbolBucketsChanged = true;
9568		return symbolBucketsChanged;
9569	}
9570	pruneUnusedLayers(usedLayers) {
9571		const usedLayerMap = {};
9572		for (const usedLayer of usedLayers) usedLayerMap[usedLayer] = true;
9573		for (const layerId in this.layerIndexes) if (!usedLayerMap[layerId]) delete this.layerIndexes[layerId];
9574	}
9575};
9576//#endregion
9577//#region src/shaders/glsl/_prelude.fragment.glsl.g.ts
9578var _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;};";
9579//#endregion
9580//#region src/shaders/glsl/_prelude.vertex.glsl.g.ts
9581var _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;};";
9582//#endregion
9583//#region src/shaders/glsl/background.fragment.glsl.g.ts
9584var 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}";
9585//#endregion
9586//#region src/shaders/glsl/background.vertex.glsl.g.ts
9587var background_vertex_glsl_g_default = "layout(location=0) in vec2 a_pos;void main() {gl_Position=projectTile(a_pos);}";
9588//#endregion
9589//#region src/shaders/glsl/background_pattern.fragment.glsl.g.ts
9590var 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}";
9591//#endregion
9592//#region src/shaders/glsl/background_pattern.vertex.glsl.g.ts
9593var 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);}";
9594//#endregion
9595//#region src/shaders/glsl/circle.fragment.glsl.g.ts
9596var 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}";
9597//#endregion
9598//#region src/shaders/glsl/circle.vertex.glsl.g.ts
9599var 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);}";
9600//#endregion
9601//#region src/shaders/glsl/clipping_mask.fragment.glsl.g.ts
9602var clipping_mask_fragment_glsl_g_default = "void main() {fragColor=vec4(1.0);}";
9603//#endregion
9604//#region src/shaders/shaders.ts
9605const shaders = {
9606	prelude: prepare(_prelude_fragment_glsl_g_default, _prelude_vertex_glsl_g_default),
9607	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);}"),
9608	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);}"),
9609	background: prepare(background_fragment_glsl_g_default, background_vertex_glsl_g_default),
9610	backgroundPattern: prepare(background_pattern_fragment_glsl_g_default, background_pattern_vertex_glsl_g_default),
9611	circle: prepare(circle_fragment_glsl_g_default, circle_vertex_glsl_g_default),
9612	clippingMask: prepare(clipping_mask_fragment_glsl_g_default, "layout(location=0) in vec2 a_pos;void main() {gl_Position=projectTile(a_pos);}"),
9613	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}"),
9614	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;}"),
9615	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;}"),
9616	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);}"),
9617	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;}}"),
9618	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));}"),
9619	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}"),
9620	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);}"),
9621	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}"),
9622	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}"),
9623	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);}"),
9624	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;}"),
9625	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;}"),
9626	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/pow(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;}"),
9627	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;}}"),
9628	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);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);}"),
9629	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);}"),
9630	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;}"),
9631	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);}"),
9632	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);}"),
9633	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;}"),
9634	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);}"),
9635	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;}"),
9636	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);}"),
9637	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;}"),
9638	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;}"),
9639	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;}"),
9640	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);}"),
9641	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);}")
9642};
9643/** Expand #pragmas to #ifdefs, extract attributes and uniforms */
9644function prepare(fragmentSource, vertexSource) {
9645	const re = /#pragma maplibre: ([\w]+) ([\w]+) ([\w]+) ([\w]+)/g;
9646	const vertexAttributes = vertexSource.match(/in ([\w]+) ([\w]+)/g);
9647	const fragmentUniforms = fragmentSource.match(/uniform ([\w]+) ([\w]+)([\s]*)([\w]*)/g);
9648	const vertexUniforms = vertexSource.match(/uniform ([\w]+) ([\w]+)([\s]*)([\w]*)/g);
9649	const shaderUniforms = vertexUniforms ? vertexUniforms.concat(fragmentUniforms) : fragmentUniforms;
9650	let locationCounter = vertexAttributes ? vertexAttributes.length : 0;
9651	const fragmentPragmas = {};
9652	fragmentSource = fragmentSource.replace(re, (match, operation, precision, type, name) => {
9653		fragmentPragmas[name] = true;
9654		if (operation === "define") return `
9655#ifndef HAS_UNIFORM_u_${name}
9656in ${precision} ${type} ${name};
9657#else
9658uniform ${precision} ${type} u_${name};
9659#endif
9660`;
9661		else return `
9662#ifdef HAS_UNIFORM_u_${name}
9663    ${precision} ${type} ${name} = u_${name};
9664#endif
9665`;
9666	});
9667	vertexSource = vertexSource.replace(re, (match, operation, precision, type, name) => {
9668		const attrType = type === "float" ? "vec2" : "vec4";
9669		const unpackType = name.match(/color/) ? "color" : attrType;
9670		if (fragmentPragmas[name]) {
9671			if (operation === "define") return `
9672#ifndef HAS_UNIFORM_u_${name}
9673uniform lowp float u_${name}_t;
9674layout(location = ${locationCounter++}) in ${precision} ${attrType} a_${name};
9675out ${precision} ${type} ${name};
9676#else
9677uniform ${precision} ${type} u_${name};
9678#endif
9679`;
9680			else if (unpackType === "vec4") return `
9681#ifndef HAS_UNIFORM_u_${name}
9682    ${name} = a_${name};
9683#else
9684    ${precision} ${type} ${name} = u_${name};
9685#endif
9686`;
9687			else return `
9688#ifndef HAS_UNIFORM_u_${name}
9689    ${name} = unpack_mix_${unpackType}(a_${name}, u_${name}_t);
9690#else
9691    ${precision} ${type} ${name} = u_${name};
9692#endif
9693`;
9694		} else if (operation === "define") return `
9695#ifndef HAS_UNIFORM_u_${name}
9696uniform lowp float u_${name}_t;
9697layout(location = ${locationCounter++}) in ${precision} ${attrType} a_${name};
9698#else
9699uniform ${precision} ${type} u_${name};
9700#endif
9701`;
9702		else if (unpackType === "vec4") return `
9703#ifndef HAS_UNIFORM_u_${name}
9704    ${precision} ${type} ${name} = a_${name};
9705#else
9706    ${precision} ${type} ${name} = u_${name};
9707#endif
9708`;
9709		else return `
9710#ifndef HAS_UNIFORM_u_${name}
9711    ${precision} ${type} ${name} = unpack_mix_${unpackType}(a_${name}, u_${name}_t);
9712#else
9713    ${precision} ${type} ${name} = u_${name};
9714#endif
9715`;
9716	});
9717	return {
9718		fragmentSource,
9719		vertexSource,
9720		staticAttributes: vertexAttributes,
9721		staticUniforms: shaderUniforms
9722	};
9723}
9724//#endregion
9725//#region src/geo/projection/mercator_projection.ts
9726const MercatorShaderDefine = "#define PROJECTION_MERCATOR";
9727const MercatorShaderVariantKey = "mercator";
9728var MercatorProjection = class {
9729	constructor() {
9730		this._cachedMesh = null;
9731	}
9732	get name() {
9733		return "mercator";
9734	}
9735	get useSubdivision() {
9736		return false;
9737	}
9738	get shaderVariantName() {
9739		return MercatorShaderVariantKey;
9740	}
9741	get shaderDefine() {
9742		return MercatorShaderDefine;
9743	}
9744	get shaderPreludeCode() {
9745		return shaders.projectionMercator;
9746	}
9747	get vertexShaderPreludeCode() {
9748		return shaders.projectionMercator.vertexSource;
9749	}
9750	get subdivisionGranularity() {
9751		return SubdivisionGranularitySetting.noSubdivision;
9752	}
9753	get useGlobeControls() {
9754		return false;
9755	}
9756	get transitionState() {
9757		return 0;
9758	}
9759	destroy() {}
9760	getMeshFromTileID(context, _tileID, _hasBorder, _allowPoles, _usage) {
9761		if (this._cachedMesh) return this._cachedMesh;
9762		const tileExtentArray = new PosArray();
9763		tileExtentArray.emplaceBack(0, 0);
9764		tileExtentArray.emplaceBack(EXTENT, 0);
9765		tileExtentArray.emplaceBack(0, EXTENT);
9766		tileExtentArray.emplaceBack(EXTENT, EXTENT);
9767		const tileExtentBuffer = context.createVertexBuffer(tileExtentArray, posAttributes.members);
9768		const tileExtentSegments = SegmentVector.simpleSegment(0, 0, 4, 2);
9769		const quadTriangleIndices = new TriangleIndexArray();
9770		quadTriangleIndices.emplaceBack(1, 0, 2);
9771		quadTriangleIndices.emplaceBack(1, 2, 3);
9772		const quadTriangleIndexBuffer = context.createIndexBuffer(quadTriangleIndices);
9773		this._cachedMesh = new Mesh(tileExtentBuffer, quadTriangleIndexBuffer, tileExtentSegments);
9774		return this._cachedMesh;
9775	}
9776	recalculate() {}
9777	hasTransition() {
9778		return false;
9779	}
9780};
9781//#endregion
9782//#region src/geo/edge_insets.ts
9783/**
9784* An `EdgeInset` object represents screen space padding applied to the edges of the viewport.
9785* This shifts the apparent center or the vanishing point of the map. This is useful for adding floating UI elements
9786* on top of the map and having the vanishing point shift as UI elements resize.
9787*
9788* @group Geography and Geometry
9789*/
9790var EdgeInsets = class EdgeInsets {
9791	constructor(top = 0, bottom = 0, left = 0, right = 0) {
9792		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");
9793		this.top = top;
9794		this.bottom = bottom;
9795		this.left = left;
9796		this.right = right;
9797	}
9798	/**
9799	* Interpolates the inset in-place.
9800	* This maintains the current inset value for any inset not present in `target`.
9801	* @param start - interpolation start
9802	* @param target - interpolation target
9803	* @param t - interpolation step/weight
9804	* @returns the insets
9805	*/
9806	interpolate(start, target, t) {
9807		if (target.top != null && start.top != null) this.top = interpolateFactory.number(start.top, target.top, t);
9808		if (target.bottom != null && start.bottom != null) this.bottom = interpolateFactory.number(start.bottom, target.bottom, t);
9809		if (target.left != null && start.left != null) this.left = interpolateFactory.number(start.left, target.left, t);
9810		if (target.right != null && start.right != null) this.right = interpolateFactory.number(start.right, target.right, t);
9811		return this;
9812	}
9813	/**
9814	* Utility method that computes the new apparent center or vanishing point after applying insets.
9815	* This is in pixels and with the top left being (0.0) and +y being downwards.
9816	*
9817	* @param width - the width
9818	* @param height - the height
9819	* @returns the point
9820	*/
9821	getCenter(width, height) {
9822		const x = clamp((this.left + width - this.right) / 2, 0, width);
9823		const y = clamp((this.top + height - this.bottom) / 2, 0, height);
9824		return new Point(x, y);
9825	}
9826	equals(other) {
9827		return this.top === other.top && this.bottom === other.bottom && this.left === other.left && this.right === other.right;
9828	}
9829	clone() {
9830		return new EdgeInsets(this.top, this.bottom, this.left, this.right);
9831	}
9832	/**
9833	* Returns the current state as json, useful when you want to have a
9834	* read-only representation of the inset.
9835	*
9836	* @returns state as json
9837	*/
9838	toJSON() {
9839		return {
9840			top: this.top,
9841			bottom: this.bottom,
9842			left: this.left,
9843			right: this.right
9844		};
9845	}
9846};
9847//#endregion
9848//#region src/geo/transform_helper.ts
9849/**
9850* If a path crossing the antimeridian would be shorter, extend the final coordinate so that
9851* interpolating between the two endpoints will cross it.
9852* @param center - The LngLat object of the desired center. This object will be mutated.
9853*/
9854function normalizeCenter(tr, center) {
9855	if (!tr.renderWorldCopies || tr.lngRange) return;
9856	const delta = center.lng - tr.center.lng;
9857	center.lng += delta > 180 ? -360 : delta < -180 ? 360 : 0;
9858}
9859function getTileZoom(zoom) {
9860	return Math.max(0, Math.floor(zoom));
9861}
9862/**
9863* @internal
9864* This class stores all values that define a transform's state,
9865* such as center, zoom, minZoom, etc.
9866* This can be used as a helper for implementing the ITransform interface.
9867*/
9868var TransformHelper = class {
9869	constructor(callbacks, options) {
9870		this.applyConstrain = (lngLat, zoom) => {
9871			if (this._constrainOverride !== null) return this._constrainOverride(lngLat, zoom);
9872			else return this._callbacks.defaultConstrain(lngLat, zoom);
9873		};
9874		this._callbacks = callbacks;
9875		this._tileSize = 512;
9876		this._renderWorldCopies = options?.renderWorldCopies === void 0 ? true : !!options?.renderWorldCopies;
9877		this._minZoom = options?.minZoom || 0;
9878		this._maxZoom = options?.maxZoom || 22;
9879		this._minPitch = options?.minPitch === void 0 || options?.minPitch === null ? 0 : options?.minPitch;
9880		this._maxPitch = options?.maxPitch === void 0 || options?.maxPitch === null ? 60 : options?.maxPitch;
9881		this._constrainOverride = options?.constrainOverride ?? null;
9882		this.setMaxBounds();
9883		this._width = 0;
9884		this._height = 0;
9885		this._center = new LngLat(0, 0);
9886		this._elevation = 0;
9887		this._zoom = 0;
9888		this._tileZoom = getTileZoom(this._zoom);
9889		this._scale = zoomScale(this._zoom);
9890		this._bearingInRadians = 0;
9891		this._fovInRadians = .6435011087932844;
9892		this._pitchInRadians = 0;
9893		this._rollInRadians = 0;
9894		this._unmodified = true;
9895		this._edgeInsets = new EdgeInsets();
9896		this._minElevationForCurrentTile = 0;
9897		this._autoCalculateNearFarZ = true;
9898	}
9899	apply(thatI, constrain) {
9900		this._constrainOverride = thatI.constrainOverride;
9901		this._latRange = thatI.latRange;
9902		this._lngRange = thatI.lngRange;
9903		this._width = thatI.width;
9904		this._height = thatI.height;
9905		this._center = thatI.center;
9906		this._elevation = thatI.elevation;
9907		this._minElevationForCurrentTile = thatI.minElevationForCurrentTile;
9908		this._zoom = thatI.zoom;
9909		this._tileZoom = getTileZoom(this._zoom);
9910		this._scale = zoomScale(this._zoom);
9911		this._bearingInRadians = thatI.bearingInRadians;
9912		this._fovInRadians = thatI.fovInRadians;
9913		this._pitchInRadians = thatI.pitchInRadians;
9914		this._rollInRadians = thatI.rollInRadians;
9915		this._unmodified = thatI.unmodified;
9916		this._edgeInsets = new EdgeInsets(thatI.padding.top, thatI.padding.bottom, thatI.padding.left, thatI.padding.right);
9917		this._minZoom = thatI.minZoom;
9918		this._maxZoom = thatI.maxZoom;
9919		this._minPitch = thatI.minPitch;
9920		this._maxPitch = thatI.maxPitch;
9921		this._renderWorldCopies = thatI.renderWorldCopies;
9922		this._cameraToCenterDistance = thatI.cameraToCenterDistance;
9923		this._nearZ = thatI.nearZ;
9924		this._farZ = thatI.farZ;
9925		this._autoCalculateNearFarZ = thatI.autoCalculateNearFarZ;
9926		if (constrain) this.constrainInternal();
9927		this._calcMatrices();
9928	}
9929	get pixelsToClipSpaceMatrix() {
9930		return this._pixelsToClipSpaceMatrix;
9931	}
9932	get clipSpaceToPixelsMatrix() {
9933		return this._clipSpaceToPixelsMatrix;
9934	}
9935	get minElevationForCurrentTile() {
9936		return this._minElevationForCurrentTile;
9937	}
9938	setMinElevationForCurrentTile(ele) {
9939		this._minElevationForCurrentTile = ele;
9940	}
9941	get tileSize() {
9942		return this._tileSize;
9943	}
9944	get tileZoom() {
9945		return this._tileZoom;
9946	}
9947	get scale() {
9948		return this._scale;
9949	}
9950	/**
9951	* Gets the transform's width in pixels. Use {@link resize} to set the transform's size.
9952	*/
9953	get width() {
9954		return this._width;
9955	}
9956	/**
9957	* Gets the transform's height in pixels. Use {@link resize} to set the transform's size.
9958	*/
9959	get height() {
9960		return this._height;
9961	}
9962	/**
9963	* Gets the transform's bearing in radians.
9964	*/
9965	get bearingInRadians() {
9966		return this._bearingInRadians;
9967	}
9968	get lngRange() {
9969		return this._lngRange;
9970	}
9971	get latRange() {
9972		return this._latRange;
9973	}
9974	get pixelsToGLUnits() {
9975		return this._pixelsToGLUnits;
9976	}
9977	get minZoom() {
9978		return this._minZoom;
9979	}
9980	setMinZoom(zoom) {
9981		if (this._minZoom === zoom) return;
9982		this._minZoom = zoom;
9983		const unmodified = this._unmodified;
9984		this.setZoom(this.applyConstrain(this._center, this.zoom).zoom);
9985		this._unmodified = unmodified;
9986	}
9987	get maxZoom() {
9988		return this._maxZoom;
9989	}
9990	setMaxZoom(zoom) {
9991		if (this._maxZoom === zoom) return;
9992		this._maxZoom = zoom;
9993		const unmodified = this._unmodified;
9994		this.setZoom(this.applyConstrain(this._center, this.zoom).zoom);
9995		this._unmodified = unmodified;
9996	}
9997	get minPitch() {
9998		return this._minPitch;
9999	}
10000	setMinPitch(pitch) {
10001		if (this._minPitch === pitch) return;
10002		this._minPitch = pitch;
10003		const unmodified = this._unmodified;
10004		this.setPitch(Math.max(this.pitch, pitch));
10005		this._unmodified = unmodified;
10006	}
10007	get maxPitch() {
10008		return this._maxPitch;
10009	}
10010	setMaxPitch(pitch) {
10011		if (this._maxPitch === pitch) return;
10012		this._maxPitch = pitch;
10013		const unmodified = this._unmodified;
10014		this.setPitch(Math.min(this.pitch, pitch));
10015		this._unmodified = unmodified;
10016	}
10017	get renderWorldCopies() {
10018		return this._renderWorldCopies;
10019	}
10020	setRenderWorldCopies(renderWorldCopies) {
10021		if (renderWorldCopies === void 0) renderWorldCopies = true;
10022		else if (renderWorldCopies === null) renderWorldCopies = false;
10023		this._renderWorldCopies = renderWorldCopies;
10024	}
10025	get constrainOverride() {
10026		return this._constrainOverride;
10027	}
10028	setConstrainOverride(constrain) {
10029		if (constrain === void 0) constrain = null;
10030		if (this._constrainOverride === constrain) return;
10031		this._constrainOverride = constrain;
10032		this.constrainInternal();
10033		this._calcMatrices();
10034	}
10035	get worldSize() {
10036		return this._tileSize * this._scale;
10037	}
10038	get centerOffset() {
10039		return this.centerPoint._sub(this.size._div(2));
10040	}
10041	/**
10042	* Gets the transform's dimensions packed into a Point object.
10043	*/
10044	get size() {
10045		return new Point(this._width, this._height);
10046	}
10047	get bearing() {
10048		return this._bearingInRadians / Math.PI * 180;
10049	}
10050	setBearing(bearing) {
10051		const b = wrap(bearing, -180, 180) * Math.PI / 180;
10052		if (this._bearingInRadians === b) return;
10053		this._unmodified = false;
10054		this._bearingInRadians = b;
10055		this._calcMatrices();
10056		this._rotationMatrix = create$2();
10057		rotate(this._rotationMatrix, this._rotationMatrix, -this._bearingInRadians);
10058	}
10059	get rotationMatrix() {
10060		return this._rotationMatrix;
10061	}
10062	get pitchInRadians() {
10063		return this._pitchInRadians;
10064	}
10065	get pitch() {
10066		return this._pitchInRadians / Math.PI * 180;
10067	}
10068	setPitch(pitch) {
10069		const p = clamp(pitch, this.minPitch, this.maxPitch) / 180 * Math.PI;
10070		if (this._pitchInRadians === p) return;
10071		this._unmodified = false;
10072		this._pitchInRadians = p;
10073		this._calcMatrices();
10074	}
10075	get rollInRadians() {
10076		return this._rollInRadians;
10077	}
10078	get roll() {
10079		return this._rollInRadians / Math.PI * 180;
10080	}
10081	setRoll(roll) {
10082		const r = roll / 180 * Math.PI;
10083		if (this._rollInRadians === r) return;
10084		this._unmodified = false;
10085		this._rollInRadians = r;
10086		this._calcMatrices();
10087	}
10088	get fovInRadians() {
10089		return this._fovInRadians;
10090	}
10091	get fov() {
10092		return radiansToDegrees(this._fovInRadians);
10093	}
10094	setFov(fov) {
10095		fov = clamp(fov, .1, 150);
10096		if (this.fov === fov) return;
10097		this._unmodified = false;
10098		this._fovInRadians = degreesToRadians(fov);
10099		this._calcMatrices();
10100	}
10101	get zoom() {
10102		return this._zoom;
10103	}
10104	setZoom(zoom) {
10105		const constrainedZoom = this.applyConstrain(this._center, zoom).zoom;
10106		if (this._zoom === constrainedZoom) return;
10107		this._unmodified = false;
10108		this._zoom = constrainedZoom;
10109		this._tileZoom = Math.max(0, Math.floor(constrainedZoom));
10110		this._scale = zoomScale(constrainedZoom);
10111		this.constrainInternal();
10112		this._calcMatrices();
10113	}
10114	get center() {
10115		return this._center;
10116	}
10117	setCenter(center) {
10118		if (center.lat === this._center.lat && center.lng === this._center.lng) return;
10119		this._unmodified = false;
10120		this._center = center;
10121		this.constrainInternal();
10122		this._calcMatrices();
10123	}
10124	/**
10125	* Elevation at current center point, meters above sea level
10126	*/
10127	get elevation() {
10128		return this._elevation;
10129	}
10130	setElevation(elevation) {
10131		if (elevation === this._elevation) return;
10132		this._elevation = elevation;
10133		this.constrainInternal();
10134		this._calcMatrices();
10135	}
10136	get padding() {
10137		return this._edgeInsets.toJSON();
10138	}
10139	setPadding(padding) {
10140		if (this._edgeInsets.equals(padding)) return;
10141		this._unmodified = false;
10142		this._edgeInsets.interpolate(this._edgeInsets, padding, 1);
10143		this._calcMatrices();
10144	}
10145	/**
10146	* The center of the screen in pixels with the top-left corner being (0,0)
10147	* and +y axis pointing downwards. This accounts for padding.
10148	*/
10149	get centerPoint() {
10150		return this._edgeInsets.getCenter(this._width, this._height);
10151	}
10152	/**
10153	* @internal
10154	*/
10155	get pixelsPerMeter() {
10156		return this._pixelPerMeter;
10157	}
10158	get unmodified() {
10159		return this._unmodified;
10160	}
10161	get cameraToCenterDistance() {
10162		return this._cameraToCenterDistance;
10163	}
10164	get nearZ() {
10165		return this._nearZ;
10166	}
10167	get farZ() {
10168		return this._farZ;
10169	}
10170	get autoCalculateNearFarZ() {
10171		return this._autoCalculateNearFarZ;
10172	}
10173	overrideNearFarZ(nearZ, farZ) {
10174		this._autoCalculateNearFarZ = false;
10175		this._nearZ = nearZ;
10176		this._farZ = farZ;
10177		this._calcMatrices();
10178	}
10179	clearNearFarZOverride() {
10180		this._autoCalculateNearFarZ = true;
10181		this._calcMatrices();
10182	}
10183	/**
10184	* Returns if the padding params match
10185	*
10186	* @param padding - the padding to check against
10187	* @returns true if they are equal, false otherwise
10188	*/
10189	isPaddingEqual(padding) {
10190		return this._edgeInsets.equals(padding);
10191	}
10192	/**
10193	* Helper method to update edge-insets in place
10194	*
10195	* @param start - the starting padding
10196	* @param target - the target padding
10197	* @param t - the step/weight
10198	*/
10199	interpolatePadding(start, target, t) {
10200		this._unmodified = false;
10201		this._edgeInsets.interpolate(start, target, t);
10202		this.constrainInternal();
10203		this._calcMatrices();
10204	}
10205	resize(width, height, constrain = true) {
10206		this._width = width;
10207		this._height = height;
10208		if (constrain) this.constrainInternal();
10209		this._calcMatrices();
10210	}
10211	/**
10212	* Returns the maximum geographical bounds the map is constrained to, or `null` if none set.
10213	* @returns max bounds
10214	*/
10215	getMaxBounds() {
10216		if (this._latRange?.length !== 2 || this._lngRange?.length !== 2) return null;
10217		return new LngLatBounds([this._lngRange[0], this._latRange[0]], [this._lngRange[1], this._latRange[1]]);
10218	}
10219	/**
10220	* Sets or clears the map's geographical constraints.
10221	* @param bounds - A {@link LngLatBounds} object describing the new geographic boundaries of the map.
10222	*/
10223	setMaxBounds(bounds) {
10224		if (bounds) {
10225			this._lngRange = [bounds.getWest(), bounds.getEast()];
10226			this._latRange = [bounds.getSouth(), bounds.getNorth()];
10227			this.constrainInternal();
10228		} else {
10229			this._lngRange = null;
10230			this._latRange = [-MAX_VALID_LATITUDE, MAX_VALID_LATITUDE];
10231		}
10232	}
10233	/**
10234	* When the map is pitched, some of the 3D features that intersect a query will not intersect
10235	* the query at the surface of the earth. Instead the feature may be closer and only intersect
10236	* the query because it extrudes into the air.
10237	* @param queryGeometry - For point queries, the line from the query point to the "camera point",
10238	* for other geometries, the envelope of the query geometry and the "camera point"
10239	* @returns a geometry that includes all of the original query as well as all possible ares of the
10240	* screen where the *base* of a visible extrusion could be.
10241	*
10242	*/
10243	getCameraQueryGeometry(cameraPoint, queryGeometry) {
10244		if (queryGeometry.length === 1) return [queryGeometry[0], cameraPoint];
10245		else {
10246			const { minX, minY, maxX, maxY } = Bounds.fromPoints(queryGeometry).extend(cameraPoint);
10247			return [
10248				new Point(minX, minY),
10249				new Point(maxX, minY),
10250				new Point(maxX, maxY),
10251				new Point(minX, maxY),
10252				new Point(minX, minY)
10253			];
10254		}
10255	}
10256	/**
10257	* @internal
10258	* Snaps the transform's center, zoom, etc. into the valid range.
10259	*/
10260	constrainInternal() {
10261		if (!this.center || !this._width || !this._height || this._constraining) return;
10262		this._constraining = true;
10263		const unmodified = this._unmodified;
10264		const { center, zoom } = this.applyConstrain(this.center, this.zoom);
10265		this.setCenter(center);
10266		this.setZoom(zoom);
10267		this._unmodified = unmodified;
10268		this._constraining = false;
10269	}
10270	/**
10271	* This function is called every time one of the transform's defining properties (center, pitch, etc.) changes.
10272	* This function should update the transform's internal data, such as matrices.
10273	* Any derived `_calcMatrices` function should also call the base function first. The base function only depends on the `_width` and `_height` fields.
10274	* While either dimension is zero there is no view to build, so the derived function is not called at all.
10275	*/
10276	_calcMatrices() {
10277		this._pixelPerMeter = mercatorZfromAltitude(1, this.center.lat) * this.worldSize;
10278		if (!this._width || !this._height) return;
10279		this._pixelsToGLUnits = [2 / this._width, -2 / this._height];
10280		let m = identity(/* @__PURE__ */ new Float64Array(16));
10281		scale(m, m, [
10282			this._width / 2,
10283			-this._height / 2,
10284			1
10285		]);
10286		translate(m, m, [
10287			1,
10288			-1,
10289			0
10290		]);
10291		this._clipSpaceToPixelsMatrix = m;
10292		m = identity(/* @__PURE__ */ new Float64Array(16));
10293		scale(m, m, [
10294			1,
10295			-1,
10296			1
10297		]);
10298		translate(m, m, [
10299			-1,
10300			-1,
10301			0
10302		]);
10303		scale(m, m, [
10304			2 / this._width,
10305			2 / this._height,
10306			1
10307		]);
10308		this._pixelsToClipSpaceMatrix = m;
10309		const halfFov = this.fovInRadians / 2;
10310		this._cameraToCenterDistance = .5 / Math.tan(halfFov) * this._height;
10311		this._callbacks.calcMatrices();
10312	}
10313	calculateCenterFromCameraLngLatAlt(lnglat, alt, bearing, pitch) {
10314		const cameraBearing = bearing !== void 0 ? bearing : this.bearing;
10315		const cameraPitch = pitch = pitch !== void 0 ? pitch : this.pitch;
10316		const { distanceToCenter, clampedElevation } = this._distanceToCenterFromAltElevationPitch(alt, this.elevation, cameraPitch);
10317		const { x, y } = cameraDirectionFromPitchBearing(cameraPitch, cameraBearing);
10318		const camMercator = MercatorCoordinate.fromLngLat(lnglat, alt);
10319		let metersPerMercUnit = altitudeFromMercatorZ(1, camMercator.y);
10320		let centerMercator;
10321		let dMercator;
10322		let iter = 0;
10323		const maxIter = 10;
10324		do {
10325			iter += 1;
10326			if (iter > maxIter) break;
10327			dMercator = distanceToCenter / metersPerMercUnit;
10328			const dx = x * dMercator;
10329			const dy = y * dMercator;
10330			centerMercator = new MercatorCoordinate(camMercator.x + dx, camMercator.y + dy);
10331			metersPerMercUnit = 1 / centerMercator.meterInMercatorCoordinateUnits();
10332		} while (Math.abs(distanceToCenter - dMercator * metersPerMercUnit) > 1e-12);
10333		return {
10334			center: centerMercator.toLngLat(),
10335			elevation: clampedElevation,
10336			zoom: scaleZoom(this.height / 2 / Math.tan(this.fovInRadians / 2) / dMercator / this.tileSize)
10337		};
10338	}
10339	recalculateZoomAndCenter(elevation) {
10340		if (this.elevation - elevation === 0) return;
10341		const mercUnitsPerPixel = 1 / this.worldSize;
10342		const originalPixelsPerMeter = mercatorZfromAltitude(1, this.center.lat) * this.worldSize;
10343		const originalCenterMercator = MercatorCoordinate.fromLngLat(this.center, this.elevation);
10344		const originalCenterPixelX = originalCenterMercator.x / mercUnitsPerPixel;
10345		const originalCenterPixelY = originalCenterMercator.y / mercUnitsPerPixel;
10346		const originalCenterPixelZ = originalCenterMercator.z / mercUnitsPerPixel;
10347		const cameraPitch = this.pitch;
10348		const cameraBearing = this.bearing;
10349		const { x, y, z } = cameraDirectionFromPitchBearing(cameraPitch, cameraBearing);
10350		const dCamPixel = this.cameraToCenterDistance;
10351		const camPixelX = originalCenterPixelX + dCamPixel * -x;
10352		const camPixelY = originalCenterPixelY + dCamPixel * -y;
10353		const camPixelZ = originalCenterPixelZ + dCamPixel * z;
10354		const { distanceToCenter, clampedElevation } = this._distanceToCenterFromAltElevationPitch(camPixelZ / originalPixelsPerMeter, elevation, cameraPitch);
10355		const distanceToCenterPixels = distanceToCenter * originalPixelsPerMeter;
10356		const centerPixelX = camPixelX + x * distanceToCenterPixels;
10357		const centerPixelY = camPixelY + y * distanceToCenterPixels;
10358		const center = new MercatorCoordinate(centerPixelX * mercUnitsPerPixel, centerPixelY * mercUnitsPerPixel, 0).toLngLat();
10359		const mercUnitsPerMeter = mercatorZfromAltitude(1, center.lat);
10360		const zoom = scaleZoom(this.height / 2 / Math.tan(this.fovInRadians / 2) / distanceToCenter / mercUnitsPerMeter / this.tileSize);
10361		this._elevation = clampedElevation;
10362		this._center = center;
10363		this.setZoom(zoom);
10364	}
10365	_distanceToCenterFromAltElevationPitch(alt, elevation, pitch) {
10366		const dzNormalized = -Math.cos(degreesToRadians(pitch));
10367		const altitudeAGL = alt - elevation;
10368		let distanceToCenter;
10369		let clampedElevation = elevation;
10370		if (dzNormalized * altitudeAGL >= 0 || Math.abs(dzNormalized) < .1) {
10371			distanceToCenter = 1e4;
10372			clampedElevation = alt + distanceToCenter * dzNormalized;
10373		} else distanceToCenter = -altitudeAGL / dzNormalized;
10374		return {
10375			distanceToCenter,
10376			clampedElevation
10377		};
10378	}
10379	getCameraPoint() {
10380		const pitch = this.pitchInRadians;
10381		const offset = Math.tan(pitch) * (this.cameraToCenterDistance || 1);
10382		return this.centerPoint.add(new Point(offset * Math.sin(this.rollInRadians), offset * Math.cos(this.rollInRadians)));
10383	}
10384	getMercatorTileCoordinates(overscaledTileID) {
10385		if (!overscaledTileID) return [
10386			0,
10387			0,
10388			1,
10389			1
10390		];
10391		const scale = overscaledTileID.canonical.z >= 0 ? 1 << overscaledTileID.canonical.z : Math.pow(2, overscaledTileID.canonical.z);
10392		return [
10393			overscaledTileID.canonical.x / scale,
10394			overscaledTileID.canonical.y / scale,
10395			1 / scale / EXTENT,
10396			1 / scale / EXTENT
10397		];
10398	}
10399};
10400//#endregion
10401//#region src/geo/projection/mercator_covering_tiles_details_provider.ts
10402var MercatorCoveringTilesDetailsProvider = class {
10403	distanceToTile2d(pointX, pointY, _tileID, boundingVolume) {
10404		const aabb = boundingVolume;
10405		const distanceX = aabb.distanceX([pointX, pointY]);
10406		const distanceY = aabb.distanceY([pointX, pointY]);
10407		return Math.hypot(distanceX, distanceY);
10408	}
10409	/**
10410	* Returns the wrap value for a given tile, computed so that tiles will remain loaded when crossing the antimeridian.
10411	*/
10412	getWrap(centerCoord, tileID, parentWrap) {
10413		return parentWrap;
10414	}
10415	/**
10416	* Returns the AABB of the specified tile.
10417	* @param tileID - Tile x, y and z for zoom.
10418	*/
10419	getTileBoundingVolume(tileID, wrap, elevation, options) {
10420		let minElevation = Math.min(0, elevation);
10421		let maxElevation = Math.max(0, elevation);
10422		if (options?.terrain) {
10423			const overscaledTileID = new OverscaledTileID(tileID.z, wrap, tileID.z, tileID.x, tileID.y);
10424			const minMax = options.terrain.getMinMaxElevation(overscaledTileID);
10425			minElevation = minMax.minElevation ?? minElevation;
10426			maxElevation = minMax.maxElevation ?? maxElevation;
10427		}
10428		const numTiles = 1 << tileID.z;
10429		return new Aabb([
10430			wrap + tileID.x / numTiles,
10431			tileID.y / numTiles,
10432			minElevation
10433		], [
10434			wrap + (tileID.x + 1) / numTiles,
10435			(tileID.y + 1) / numTiles,
10436			maxElevation
10437		]);
10438	}
10439	allowVariableZoom(transform, options) {
10440		const zfov = transform.fov * (Math.abs(Math.cos(transform.rollInRadians)) * transform.height + Math.abs(Math.sin(transform.rollInRadians)) * transform.width) / transform.height;
10441		const maxConstantZoomPitch = clamp(78.5 - zfov / 2, 0, 60);
10442		return !!options.terrain || transform.pitch > maxConstantZoomPitch;
10443	}
10444	allowWorldCopies() {
10445		return true;
10446	}
10447	prepareNextFrame() {}
10448};
10449//#endregion
10450//#region src/data/pos3d_attributes.ts
10451const pos3dAttributes = createLayout([{
10452	name: "a_pos3d",
10453	type: "Int16",
10454	components: 3
10455}]);
10456//#endregion
10457//#region src/tile/terrain_tile_manager.ts
10458/**
10459* @internal
10460* This class is a helper for the Terrain-class, it:
10461*
10462* - loads raster-dem tiles
10463* - manages all renderToTexture tiles.
10464* - caches previous rendered tiles.
10465* - finds all necessary renderToTexture tiles for a OverscaledTileID area
10466* - finds the corresponding raster-dem tile for OverscaledTileID
10467*/
10468var TerrainTileManager = class extends Evented {
10469	constructor(tileManager) {
10470		super();
10471		this._lastTilesetChange = now();
10472		this.tileManager = tileManager;
10473		this._tiles = {};
10474		this._renderableTilesKeys = [];
10475		this._sourceTileCache = {};
10476		this.minzoom = 0;
10477		this.maxzoom = 22;
10478		this.deltaZoom = 1;
10479		this.tileSize = tileManager._source.tileSize * 2 ** this.deltaZoom;
10480		tileManager.usedForTerrain = true;
10481		tileManager.tileSize = this.tileSize;
10482	}
10483	destruct() {
10484		this.tileManager.usedForTerrain = false;
10485		this.tileManager.tileSize = null;
10486		this.releaseAllRTT();
10487	}
10488	getSource() {
10489		return this.tileManager._source;
10490	}
10491	/**
10492	* Load Terrain Tiles, create internal render-to-texture tiles, free GPU memory.
10493	* @param transform - the operation to do
10494	* @param terrain - the terrain
10495	* @returns true when the set of renderable tiles changed
10496	*/
10497	update(transform, terrain) {
10498		this.tileManager.update(transform, terrain);
10499		this._renderableTilesKeys = [];
10500		const keys = {};
10501		let changed = false;
10502		for (const tileID of coveringTiles(transform, {
10503			tileSize: this.tileSize,
10504			minzoom: this.minzoom,
10505			maxzoom: this.maxzoom,
10506			reparseOverscaled: false,
10507			terrain
10508		})) {
10509			keys[tileID.key] = true;
10510			this._renderableTilesKeys.push(tileID.key);
10511			if (!this._tiles[tileID.key]) {
10512				tileID.terrainRttPosMatrix32f = /* @__PURE__ */ new Float32Array(16);
10513				ortho(tileID.terrainRttPosMatrix32f, 0, EXTENT, EXTENT, 0, 0, 1);
10514				this._tiles[tileID.key] = new Tile(tileID, this.tileSize);
10515				this._lastTilesetChange = now();
10516				changed = true;
10517			}
10518		}
10519		for (const key in this._tiles) if (!keys[key]) {
10520			this._tiles[key].releaseRTT(this.tileManager.map.painter);
10521			delete this._tiles[key];
10522			changed = true;
10523		}
10524		return changed;
10525	}
10526	/**
10527	* Release the RTT objects for `tileID` (and its ancestors/descendants),
10528	*/
10529	releaseRTT(tileID) {
10530		for (const key in this._tiles) {
10531			const tile = this._tiles[key];
10532			if (tile.tileID.equals(tileID) || tile.tileID.isChildOf(tileID) || tileID.isChildOf(tile.tileID)) tile.releaseRTT(this.tileManager.map.painter);
10533		}
10534	}
10535	/**
10536	* Release the A RTT objects for all tiles.
10537	*/
10538	releaseAllRTT() {
10539		for (const key in this._tiles) this._tiles[key].releaseRTT(this.tileManager.map.painter);
10540	}
10541	/**
10542	* get a list of tiles, which are loaded and should be rendered in the current scene
10543	* @returns the renderable tiles
10544	*/
10545	getRenderableTiles() {
10546		return this._renderableTilesKeys.map((key) => this.getTileByID(key));
10547	}
10548	/**
10549	* get terrain tile by the TileID key
10550	* @param id - the tile id
10551	* @returns the tile
10552	*/
10553	getTileByID(id) {
10554		return this._tiles[id];
10555	}
10556	/**
10557	* Searches for the corresponding current renderable terrain-tiles
10558	* @param tileID - the tile to look for
10559	* @returns the tiles that were found
10560	*/
10561	getTerrainCoords(tileID, terrainTileRanges) {
10562		if (terrainTileRanges) return this._getTerrainCoordsForTileRanges(tileID, terrainTileRanges);
10563		else return this._getTerrainCoordsForRegularTile(tileID);
10564	}
10565	/**
10566	* Searches for the corresponding current renderable terrain-tiles.
10567	* Includes terrain tiles that are either:
10568	* - the same as the tileID
10569	* - a parent of the tileID
10570	* - a child of the tileID
10571	* @param tileID - the tile to look for
10572	* @returns the tiles that were found
10573	*/
10574	_getTerrainCoordsForRegularTile(tileID) {
10575		const coords = {};
10576		for (const key of this._renderableTilesKeys) {
10577			const terrainTileID = this._tiles[key].tileID;
10578			const coord = tileID.clone();
10579			const mat = createMat4f64();
10580			if (terrainTileID.canonical.equals(tileID.canonical)) ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
10581			else if (terrainTileID.canonical.isChildOf(tileID.canonical)) {
10582				const dz = terrainTileID.canonical.z - tileID.canonical.z;
10583				const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz);
10584				const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz);
10585				const size = EXTENT >> dz;
10586				ortho(mat, 0, size, size, 0, 0, 1);
10587				translate(mat, mat, [
10588					-dx * size,
10589					-dy * size,
10590					0
10591				]);
10592			} else if (tileID.canonical.isChildOf(terrainTileID.canonical)) {
10593				const dz = tileID.canonical.z - terrainTileID.canonical.z;
10594				const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
10595				const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
10596				const size = EXTENT >> dz;
10597				ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
10598				translate(mat, mat, [
10599					dx * size,
10600					dy * size,
10601					0
10602				]);
10603				scale(mat, mat, [
10604					1 / 2 ** dz,
10605					1 / 2 ** dz,
10606					0
10607				]);
10608			} else continue;
10609			coord.terrainRttPosMatrix32f = new Float32Array(mat);
10610			coords[key] = coord;
10611		}
10612		return coords;
10613	}
10614	/**
10615	* Searches for the corresponding current renderable terrain-tiles.
10616	* Includes terrain tiles that are within terrain tile ranges.
10617	* @param tileID - the tile to look for
10618	* @returns the tiles that were found
10619	*/
10620	_getTerrainCoordsForTileRanges(tileID, terrainTileRanges) {
10621		const coords = {};
10622		for (const key of this._renderableTilesKeys) {
10623			const terrainTileID = this._tiles[key].tileID;
10624			if (!this._isWithinTileRanges(terrainTileID, terrainTileRanges)) continue;
10625			const coord = tileID.clone();
10626			const mat = createMat4f64();
10627			if (terrainTileID.canonical.z === tileID.canonical.z) {
10628				const dx = tileID.canonical.x - terrainTileID.canonical.x + tileID.wrap * (1 << tileID.canonical.z);
10629				const dy = tileID.canonical.y - terrainTileID.canonical.y;
10630				ortho(mat, 0, EXTENT, EXTENT, 0, 0, 1);
10631				translate(mat, mat, [
10632					dx * EXTENT,
10633					dy * EXTENT,
10634					0
10635				]);
10636			} else if (terrainTileID.canonical.z > tileID.canonical.z) {
10637				const dz = terrainTileID.canonical.z - tileID.canonical.z;
10638				const dx = terrainTileID.canonical.x - (terrainTileID.canonical.x >> dz << dz) + tileID.wrap * (1 << terrainTileID.canonical.z);
10639				const dy = terrainTileID.canonical.y - (terrainTileID.canonical.y >> dz << dz);
10640				const dx2 = tileID.canonical.x - (terrainTileID.canonical.x >> dz);
10641				const dy2 = tileID.canonical.y - (terrainTileID.canonical.y >> dz);
10642				const size = EXTENT >> dz;
10643				ortho(mat, 0, size, size, 0, 0, 1);
10644				translate(mat, mat, [
10645					-dx * size + dx2 * EXTENT,
10646					-dy * size + dy2 * EXTENT,
10647					0
10648				]);
10649			} else {
10650				const dz = tileID.canonical.z - terrainTileID.canonical.z;
10651				const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz) + tileID.wrap * (1 << tileID.canonical.z);
10652				const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
10653				const dx2 = (tileID.canonical.x >> dz) - terrainTileID.canonical.x;
10654				const dy2 = (tileID.canonical.y >> dz) - terrainTileID.canonical.y;
10655				const size = EXTENT << dz;
10656				ortho(mat, 0, size, size, 0, 0, 1);
10657				translate(mat, mat, [
10658					dx * EXTENT + dx2 * size,
10659					dy * EXTENT + dy2 * size,
10660					0
10661				]);
10662			}
10663			coord.terrainRttPosMatrix32f = new Float32Array(mat);
10664			coords[key] = coord;
10665		}
10666		return coords;
10667	}
10668	/**
10669	* find the covering raster-dem tile
10670	* @param tileID - the tile to look for
10671	* @param searchForDEM - Optional parameter to search for (parent) source tiles with loaded dem.
10672	* @returns the tile
10673	*/
10674	getSourceTile(tileID, searchForDEM) {
10675		const source = this.tileManager._source;
10676		let z = tileID.overscaledZ - this.deltaZoom;
10677		if (z > source.maxzoom) z = source.maxzoom;
10678		if (z < source.minzoom) return void 0;
10679		this._sourceTileCache[tileID.key] ||= tileID.scaledTo(z).key;
10680		let tile = this.findTileInCaches(this._sourceTileCache[tileID.key]);
10681		if (!tile?.dem && searchForDEM) while (z >= source.minzoom && !tile?.dem) tile = this.findTileInCaches(tileID.scaledTo(z--).key);
10682		return tile;
10683	}
10684	findTileInCaches(key) {
10685		let tile = this.tileManager.getTileByID(key);
10686		if (tile) return tile;
10687		tile = this.tileManager._outOfViewCache.getByKey(key);
10688		return tile;
10689	}
10690	/**
10691	* gets whether any tiles were loaded after a specific time. This is used to update the depth framebuffer.
10692	* @param time - the time
10693	* @returns true if any tiles came into view at or after the specified time
10694	*/
10695	anyTilesAfterTime(time = now()) {
10696		return this._lastTilesetChange >= time;
10697	}
10698	/**
10699	* Checks whether a tile is within the canonical tile ranges.
10700	* @param tileID - Tile to check
10701	* @param canonicalTileRanges - Canonical tile ranges
10702	* @returns
10703	*/
10704	_isWithinTileRanges(tileID, canonicalTileRanges) {
10705		const range = canonicalTileRanges[tileID.canonical.z];
10706		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);
10707	}
10708};
10709//#endregion
10710//#region src/render/terrain.ts
10711const MAX_BISECTIONS = 40;
10712const HIT_EPSILON_M = 1e-6;
10713/** Keeps the elevation bracket non-degenerate when the terrain is entirely flat, such as unloaded DEMs. */
10714const BRACKET_PADDING_M = 10;
10715/** Tile coordinates run from 0 up to but not including `EXTENT`; a point on the far edge is clamped back into the tile. */
10716const MAX_TILE_COORD = EXTENT * (1 - 1e-12);
10717/**
10718* Offset, in DEM pixels, from a tile coordinate scaled by `dim` to the pixel index `DEMData.sampleBilinear` expects.
10719* DEM pixel `i` describes the cell centred at tile coordinate `(i + 0.5) / dim`, the same placement hillshade and
10720* color-relief use, so a sample between two cell centres interpolates the pixels on either side of it.
10721*/
10722const DEM_CELL_CENTER_OFFSET = -.5;
10723/**
10724* @internal
10725* This is the main class which handles most of the 3D Terrain logic. It has the following topics:
10726*
10727* 1. loads raster-dem tiles via the internal tileManager this.tileManager
10728* 2. creates a depth-framebuffer, which is used to calculate the visibility of coordinates
10729* 3. stores all render-to-texture tiles in the this.tileManager._tiles
10730* 4. calculates the elevation for a specific tile-coordinate
10731* 5. creates a terrain-mesh
10732*
10733* A note about the GPU resource-usage:
10734*
10735* Framebuffers:
10736*
10737* - one for the depth framebuffer with the size of the map-div.
10738* - one for rendering a tile to texture with the size of tileSize (= 512x512).
10739*
10740* Textures:
10741*
10742* - one texture for an empty raster-dem tile with size 1x1
10743* - one texture for an empty depth-buffer, when terrain is disabled with size 1x1
10744* - one texture for an each loaded raster-dem with size of the source.tileSize
10745* - one texture for the depth-framebuffer with the size of the map-div.
10746* - finally for each render-to-texture tile (= this._tiles) a set of textures
10747* for each render stack (The stack-concept is documented in painter.ts).
10748*
10749* Normally there exists 1-3 Textures per tile, depending on the stylesheet.
10750* Each Textures has the size 2*tileSize (= 1024x1024). Also there exists a
10751* cache of the last 150 newest rendered tiles.
10752*
10753*/
10754var Terrain = class {
10755	constructor(painter, tileManager, options, terrainSkirtLength = "auto") {
10756		this._meshCache = {};
10757		this.painter = painter;
10758		this.tileManager = new TerrainTileManager(tileManager);
10759		this.options = options;
10760		this.exaggeration = typeof options.exaggeration === "number" ? options.exaggeration : 1;
10761		this._terrainSkirtLength = terrainSkirtLength;
10762		this.qualityFactor = 2;
10763		this.meshSize = 128;
10764		this._demMatrixCache = /* @__PURE__ */ new Map();
10765		this._elevationSamplerCache = /* @__PURE__ */ new Map();
10766	}
10767	destroy() {
10768		if (this._fbo) {
10769			this._fbo.destroy();
10770			this._fbo = null;
10771		}
10772		if (this._fboDepthTexture) {
10773			this._fboDepthTexture.destroy();
10774			this._fboDepthTexture = null;
10775		}
10776		if (this._emptyDemTexture) {
10777			this._emptyDemTexture.destroy();
10778			this._emptyDemTexture = null;
10779		}
10780		if (this._emptyDepthTexture) {
10781			this._emptyDepthTexture.destroy();
10782			this._emptyDepthTexture = null;
10783		}
10784		for (const key in this._meshCache) this._meshCache[key].destroy();
10785		this._meshCache = {};
10786		this.tileManager.destruct();
10787	}
10788	/**
10789	* Get the elevation-value from original dem-data for a given tile-coordinate.
10790	* Coordinates that fall outside `[0, extent)` are normalized to the
10791	* appropriate neighbor tile before lookup.
10792	* @param tileID - the tile to get the elevation for
10793	* @param x - x coordinate relative to the tile, may be outside `[0, extent)`
10794	* @param y - y coordinate relative to the tile, may be outside `[0, extent)`
10795	* @param extent - optional, default 8192
10796	* @returns the elevation
10797	*/
10798	getDEMElevation(tileID, x, y, extent = EXTENT) {
10799		const normalized = tileID.normalizeCoordinates(x, y, extent);
10800		if (!normalized) return 0;
10801		const sampler = this.getElevationSampler(normalized.tileID);
10802		return sampler ? sampler(normalized.x, normalized.y, extent) : 0;
10803	}
10804	/**
10805	* Get the elevation for given {@link LngLat} in respect of exaggeration.
10806	* @param lnglat - the location
10807	* @param zoom - the zoom, use {@link getElevationForLngLat} if you don't want a specific zoom level, but more accurate results.
10808	* @returns the elevation
10809	*/
10810	getElevationForLngLatZoom(lnglat, zoom) {
10811		if (!isInBoundsForZoomLngLat(zoom, lnglat.wrap())) return 0;
10812		const { tileID, mercatorX, mercatorY } = this._getOverscaledTileIDFromLngLatZoom(lnglat, zoom);
10813		return this.getElevation(tileID, mercatorX % EXTENT, mercatorY % EXTENT, EXTENT);
10814	}
10815	/**
10816	* Get the elevation for given {@link LngLat} in respect of exaggeration.
10817	* Where the location is covered by a rendered tile with loaded DEM data this samples the
10818	* rendered surface, so the result agrees with what is drawn; elsewhere it traverses up the
10819	* zoom levels to find the first tile with data to return.
10820	* @param lnglat - the location
10821	* @returns the elevation
10822	*/
10823	getElevationForLngLat(lnglat, transform) {
10824		const index = this.getCoverageIndex();
10825		if (index) {
10826			const mercator = MercatorCoordinate.fromLngLat(lnglat);
10827			const sample = sampleAt(index, this.exaggeration, mercator.x, mercator.y);
10828			if (sample.demLoaded) return sample.elevation;
10829		}
10830		const terrainCoveringTiles = coveringTiles(transform, {
10831			maxzoom: this.tileManager.maxzoom,
10832			minzoom: this.tileManager.minzoom,
10833			tileSize: 512,
10834			terrain: this
10835		});
10836		let zoom = 0;
10837		for (const tile of terrainCoveringTiles) if (tile.canonical.z > zoom) zoom = Math.min(tile.canonical.z, this.tileManager.maxzoom);
10838		return this.getElevationForLngLatZoom(lnglat, zoom);
10839	}
10840	/**
10841	* Get the elevation for given coordinate in respect of exaggeration.
10842	* @param tileID - the tile id
10843	* @param x - x coordinate relative to the tile, may be outside `[0, extent)`
10844	* @param y - y coordinate relative to the tile, may be outside `[0, extent)`
10845	* @param extent - optional, default 8192
10846	* @returns the elevation
10847	*/
10848	getElevation(tileID, x, y, extent = EXTENT) {
10849		return this.getDEMElevation(tileID, x, y, extent) * this.exaggeration;
10850	}
10851	/**
10852	* Clear CPU elevation samplers that may retain a previously selected DEM tile.
10853	* @internal
10854	*/
10855	resetElevationCache() {
10856		this._elevationSamplerCache.clear();
10857		this._coverageIndex = void 0;
10858	}
10859	/**
10860	* Index of the tiles the terrain currently renders, for sampling the terrain surface on the CPU.
10861	* Built on first use and kept until {@link resetElevationCache}.
10862	* @returns the index, or null when no terrain tile is renderable
10863	*/
10864	getCoverageIndex() {
10865		if (this._coverageIndex === void 0) this._coverageIndex = this._buildCoverageIndex();
10866		return this._coverageIndex;
10867	}
10868	_buildCoverageIndex() {
10869		const zooms = [];
10870		const samplerPerTile = /* @__PURE__ */ new Map();
10871		let minElevation = 0;
10872		let maxElevation = 0;
10873		for (const tile of this.tileManager.getRenderableTiles()) {
10874			if (!tile) continue;
10875			const { canonical, wrap } = tile.tileID;
10876			if (!zooms.includes(canonical.z)) zooms.push(canonical.z);
10877			const sampler = this.getElevationSampler(tile.tileID);
10878			samplerPerTile.set(`${wrap}/${canonical.z}/${canonical.x}/${canonical.y}`, sampler);
10879			const { minElevation: tileMin, maxElevation: tileMax } = this.getMinMaxElevation(tile.tileID);
10880			minElevation = Math.min(minElevation, tileMin ?? 0);
10881			maxElevation = Math.max(maxElevation, tileMax ?? 0);
10882		}
10883		if (samplerPerTile.size === 0) return null;
10884		zooms.sort((a, b) => b - a);
10885		return {
10886			zooms,
10887			samplerPerTile,
10888			minElevation: minElevation - BRACKET_PADDING_M,
10889			maxElevation: maxElevation + BRACKET_PADDING_M
10890		};
10891	}
10892	/**
10893	* Get a function that samples the raw DEM elevation of a tile, without exaggeration.
10894	* The sampler places DEM pixels at cell centres, matching `get_elevation` in the vertex shader prelude.
10895	* @param tileID - the tile id
10896	* @returns the sampler, or null when the tile's DEM data is not loaded
10897	*/
10898	getElevationSampler(tileID) {
10899		const key = tileID.key;
10900		const cachedSampler = this._elevationSamplerCache.get(key);
10901		if (cachedSampler) return cachedSampler;
10902		const sourceTile = this.tileManager.getSourceTile(tileID, true);
10903		const dem = sourceTile?.dem;
10904		if (!sourceTile || !dem) return null;
10905		const matrix = this._getDEMTileMatrix(tileID, sourceTile);
10906		const demPixelScaleX = matrix[0] * dem.dim;
10907		const demPixelScaleY = matrix[5] * dem.dim;
10908		const demPixelOffsetX = matrix[12] * dem.dim + DEM_CELL_CENTER_OFFSET;
10909		const demPixelOffsetY = matrix[13] * dem.dim + DEM_CELL_CENTER_OFFSET;
10910		const sampler = (x, y, extent) => {
10911			const extentScale = extent === 8192 ? 1 : EXTENT / extent;
10912			return dem.sampleBilinear(x * extentScale * demPixelScaleX + demPixelOffsetX, y * extentScale * demPixelScaleY + demPixelOffsetY);
10913		};
10914		this._elevationSamplerCache.set(key, sampler);
10915		return sampler;
10916	}
10917	/**
10918	* Get the matrix that maps a tile's coordinates into the DEM tile it is rendered with.
10919	* The transform is derived from the loaded DEM tile's own zoom level, not from the source's
10920	* declared maxzoom: getSourceTile falls back to a loaded parent tile while the deepest DEM
10921	* tile is still loading, and the scale and offset must match the tile that is actually used.
10922	* @param tileID - the tile id
10923	* @param sourceTile - the DEM tile that is used for this tile, either its own tile or a loaded parent
10924	* @returns the matrix that maps the tile's coordinates onto the DEM tile
10925	*/
10926	_getDEMTileMatrix(tileID, sourceTile) {
10927		const matrixKey = `${sourceTile.tileID.key}/${tileID.key}`;
10928		const cachedMatrix = this._demMatrixCache.get(matrixKey);
10929		if (cachedMatrix) return cachedMatrix;
10930		const dz = tileID.canonical.z - sourceTile.tileID.canonical.z;
10931		const dx = tileID.canonical.x - (tileID.canonical.x >> dz << dz);
10932		const dy = tileID.canonical.y - (tileID.canonical.y >> dz << dz);
10933		const demMatrix = fromScaling(/* @__PURE__ */ new Float64Array(16), [
10934			1 / (EXTENT << dz),
10935			1 / (EXTENT << dz),
10936			0
10937		]);
10938		translate(demMatrix, demMatrix, [
10939			dx * EXTENT,
10940			dy * EXTENT,
10941			0
10942		]);
10943		this._demMatrixCache.set(matrixKey, demMatrix);
10944		return demMatrix;
10945	}
10946	/**
10947	* returns a Terrain Object for a tile. Unless the tile corresponds to data (e.g. tile is loading), return a flat dem object
10948	* @param tileID - the tile to get the terrain for
10949	* @returns the terrain data to use in the program
10950	*/
10951	getTerrainData(tileID) {
10952		if (!this._emptyDemTexture) {
10953			const context = this.painter.context;
10954			const image = new RGBAImage({
10955				width: 1,
10956				height: 1
10957			}, /* @__PURE__ */ new Uint8Array(4));
10958			this._emptyDepthTexture = new Texture(context, image, context.gl.RGBA, { premultiply: false });
10959			this._emptyDemUnpack = [
10960				0,
10961				0,
10962				0,
10963				0
10964			];
10965			this._emptyDemTexture = new Texture(context, new RGBAImage({
10966				width: 1,
10967				height: 1
10968			}), context.gl.RGBA, { premultiply: false });
10969			this._emptyDemTexture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
10970			this._emptyDemMatrix = identity([]);
10971		}
10972		const sourceTile = this.tileManager.getSourceTile(tileID, true);
10973		if (sourceTile?.dem && (!sourceTile.demTexture || sourceTile.needsTerrainPrepare)) {
10974			const context = this.painter.context;
10975			sourceTile.demTexture ||= this.painter.getTileTexture(sourceTile.dem.stride);
10976			if (sourceTile.demTexture) sourceTile.demTexture.update(sourceTile.dem.getPixels(), { premultiply: false });
10977			else sourceTile.demTexture = new Texture(context, sourceTile.dem.getPixels(), context.gl.RGBA, { premultiply: false });
10978			sourceTile.demTexture.bind(context.gl.NEAREST, context.gl.CLAMP_TO_EDGE);
10979			sourceTile.needsTerrainPrepare = false;
10980		}
10981		const terrainMatrix = sourceTile ? this._getDEMTileMatrix(tileID, sourceTile) : this._emptyDemMatrix;
10982		return {
10983			"u_depth": 2,
10984			"u_terrain": 3,
10985			"u_terrain_dim": sourceTile?.dem?.dim || 1,
10986			"u_terrain_matrix": terrainMatrix,
10987			"u_terrain_unpack": sourceTile?.dem?.getUnpackVector() || this._emptyDemUnpack,
10988			"u_terrain_exaggeration": this.exaggeration,
10989			texture: (sourceTile?.demTexture || this._emptyDemTexture).texture,
10990			depthTexture: (this._fboDepthTexture || this._emptyDepthTexture).texture,
10991			tile: sourceTile
10992		};
10993	}
10994	/**
10995	* get a framebuffer as big as the map-div, which will be used to render depth into a texture
10996	* @returns the frame buffer
10997	*/
10998	getFramebuffer() {
10999		const painter = this.painter;
11000		const width = painter.width / devicePixelRatio;
11001		const height = painter.height / devicePixelRatio;
11002		if (this._fbo && (this._fbo.width !== width || this._fbo.height !== height)) {
11003			this._fbo.destroy();
11004			this._fboDepthTexture.destroy();
11005			delete this._fbo;
11006			delete this._fboDepthTexture;
11007		}
11008		if (!this._fboDepthTexture) {
11009			this._fboDepthTexture = new Texture(painter.context, {
11010				width,
11011				height,
11012				data: null
11013			}, painter.context.gl.RGBA, { premultiply: false });
11014			this._fboDepthTexture.bind(painter.context.gl.NEAREST, painter.context.gl.CLAMP_TO_EDGE);
11015		}
11016		if (!this._fbo) {
11017			this._fbo = painter.context.createFramebuffer(width, height, true, false);
11018			this._fbo.depthAttachment.set(painter.context.createRenderbuffer(painter.context.gl.DEPTH_COMPONENT16, width, height));
11019		}
11020		this._fbo.colorAttachment.set(this._fboDepthTexture.texture);
11021		return this._fbo;
11022	}
11023	/**
11024	* create a regular mesh which will be used by all terrain-tiles
11025	* @returns the created regular mesh
11026	*/
11027	getTerrainMesh(tileId) {
11028		const globeEnabled = this.painter.style.projection?.transitionState > 0;
11029		const northPole = globeEnabled && tileId.canonical.y === 0;
11030		const southPole = globeEnabled && tileId.canonical.y === (1 << tileId.canonical.z) - 1;
11031		const key = `m_${northPole ? "n" : ""}_${southPole ? "s" : ""}`;
11032		if (this._meshCache[key]) return this._meshCache[key];
11033		const context = this.painter.context;
11034		const vertexArray = new Pos3dArray();
11035		const indexArray = new TriangleIndexArray();
11036		const meshSize = this.meshSize;
11037		const delta = EXTENT / meshSize;
11038		const meshSize2 = meshSize * meshSize;
11039		for (let y = 0; y <= meshSize; y++) for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, y * delta, 0);
11040		for (let y = 0; y < meshSize2; y += meshSize + 1) for (let x = 0; x < meshSize; x++) {
11041			indexArray.emplaceBack(x + y, meshSize + x + y + 1, meshSize + x + y + 2);
11042			indexArray.emplaceBack(x + y, meshSize + x + y + 2, x + y + 1);
11043		}
11044		if (this._terrainSkirtLength !== "none") this._buildSkirts(vertexArray, indexArray, meshSize, delta, northPole, southPole);
11045		const mesh = new Mesh(context.createVertexBuffer(vertexArray, pos3dAttributes.members), context.createIndexBuffer(indexArray), SegmentVector.simpleSegment(0, 0, vertexArray.length, indexArray.length));
11046		this._meshCache[key] = mesh;
11047		return mesh;
11048	}
11049	/**
11050	* Calculates the height of the tile skirts for the "auto" strategy.
11051	* @see {@link MapOptions.terrainSkirtLength}
11052	* @param zoom - current zoomlevel
11053	* @returns the elevation delta in meters
11054	*/
11055	getSkirtLength(zoom) {
11056		return 2 * Math.PI * earthRadius / Math.pow(2, Math.max(zoom, 0)) / 5;
11057	}
11058	getMinTileElevationForLngLatZoom(lnglat, zoom) {
11059		if (!isInBoundsForZoomLngLat(zoom, lnglat.wrap())) return 0;
11060		const { tileID } = this._getOverscaledTileIDFromLngLatZoom(lnglat, zoom);
11061		return this.getMinMaxElevation(tileID).minElevation ?? 0;
11062	}
11063	/**
11064	* Get the minimum and maximum elevation contained in a tile. This includes any
11065	* exaggeration included in the terrain.
11066	*
11067	* @param tileID - ID of the tile to be used as a source for the min/max elevation
11068	* @returns the minimum and maximum elevation found in the tile, including the terrain's
11069	* exaggeration
11070	*/
11071	getMinMaxElevation(tileID) {
11072		const tile = this.tileManager.getSourceTile(tileID, true);
11073		const minMax = {
11074			minElevation: null,
11075			maxElevation: null
11076		};
11077		if (tile?.dem) {
11078			minMax.minElevation = tile.dem.min * this.exaggeration;
11079			minMax.maxElevation = tile.dem.max * this.exaggeration;
11080		}
11081		return minMax;
11082	}
11083	_getOverscaledTileIDFromLngLatZoom(lnglat, zoom) {
11084		const mercatorCoordinate = MercatorCoordinate.fromLngLat(lnglat.wrap());
11085		const worldSize = (1 << zoom) * EXTENT;
11086		const mercatorX = mercatorCoordinate.x * worldSize;
11087		const mercatorY = mercatorCoordinate.y * worldSize;
11088		const tileX = Math.floor(mercatorX / EXTENT), tileY = Math.floor(mercatorY / EXTENT);
11089		return {
11090			tileID: new OverscaledTileID(zoom, 0, zoom, tileX, tileY),
11091			mercatorX,
11092			mercatorY
11093		};
11094	}
11095	/** Add an extra frame around the mesh to avoid hairline gaps (stitching) on tile boundaries with different zoomlevels.
11096	* @see {@link MapOptions.terrainSkirtLength}
11097	*/
11098	_buildSkirts(vertexArray, indexArray, meshSize, delta, northPole, southPole) {
11099		const offsetTop = vertexArray.length;
11100		const offsetTopEdge = 0;
11101		const offsetBottom = offsetTop + (meshSize + 1);
11102		const offsetBottomEdge = (meshSize + 1) * meshSize;
11103		const northY = northPole ? NORTH_POLE_Y : 0;
11104		const northZ = northPole ? 0 : 1;
11105		const southY = southPole ? SOUTH_POLE_Y : EXTENT;
11106		const southZ = southPole ? 0 : 1;
11107		for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, northY, northZ);
11108		for (let x = 0; x <= meshSize; x++) vertexArray.emplaceBack(x * delta, southY, southZ);
11109		for (let x = 0; x < meshSize; x++) {
11110			indexArray.emplaceBack(offsetBottomEdge + x, offsetBottom + x, offsetBottom + x + 1);
11111			indexArray.emplaceBack(offsetBottomEdge + x, offsetBottom + x + 1, offsetBottomEdge + x + 1);
11112			indexArray.emplaceBack(offsetTopEdge + x, offsetTop + x + 1, offsetTop + x);
11113			indexArray.emplaceBack(offsetTopEdge + x, offsetTopEdge + x + 1, offsetTop + x + 1);
11114		}
11115		const offsetLeft = vertexArray.length;
11116		const offsetRight = offsetLeft + (meshSize + 1) * 2;
11117		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);
11118		for (let y = 0; y < meshSize * 2; y += 2) {
11119			indexArray.emplaceBack(offsetLeft + y, offsetLeft + y + 1, offsetLeft + y + 3);
11120			indexArray.emplaceBack(offsetLeft + y, offsetLeft + y + 3, offsetLeft + y + 2);
11121			indexArray.emplaceBack(offsetRight + y, offsetRight + y + 3, offsetRight + y + 1);
11122			indexArray.emplaceBack(offsetRight + y, offsetRight + y + 2, offsetRight + y + 3);
11123		}
11124	}
11125};
11126const NOT_COVERED = {
11127	covered: false,
11128	demLoaded: false,
11129	elevation: 0
11130};
11131/**
11132* Elevation of the rendered terrain surface at a mercator position, and whether it is covered at all.
11133* A covered tile whose DEM has not loaded yet is flat at zero, which is what the terrain mesh renders.
11134*/
11135function sampleAt(index, exaggeration, mercatorX, mercatorY) {
11136	if (mercatorY < 0 || mercatorY >= 1) return NOT_COVERED;
11137	const wrap = Math.floor(mercatorX);
11138	const wrappedX = mercatorX - wrap;
11139	for (const z of index.zooms) {
11140		const scale = 1 << z;
11141		const scaledX = wrappedX * scale;
11142		const scaledY = mercatorY * scale;
11143		const tileX = Math.floor(scaledX);
11144		const tileY = Math.floor(scaledY);
11145		const key = `${wrap}/${z}/${tileX}/${tileY}`;
11146		if (!index.samplerPerTile.has(key)) continue;
11147		const sampler = index.samplerPerTile.get(key);
11148		if (!sampler) return {
11149			covered: true,
11150			demLoaded: false,
11151			elevation: 0
11152		};
11153		return {
11154			covered: true,
11155			demLoaded: true,
11156			elevation: sampler(Math.min((scaledX - tileX) * EXTENT, MAX_TILE_COORD), Math.min((scaledY - tileY) * EXTENT, MAX_TILE_COORD), EXTENT) * exaggeration
11157		};
11158	}
11159	return NOT_COVERED;
11160}
11161/**
11162* Whether a height in meters is at or below the sampled terrain surface.
11163* The epsilon absorbs rounding when a bracket endpoint lands exactly on the surface.
11164*/
11165function isBelowTerrainSample(sample, height) {
11166	return sample.covered && height <= sample.elevation + HIT_EPSILON_M;
11167}
11168/**
11169* Narrows the bracket `[lo, hi]` around the surface crossing until it is shorter than `tolerance` in ray parameter units.
11170*/
11171function bisect(ray, isBelowTerrain, lo, hi, tolerance) {
11172	for (let j = 0; j < MAX_BISECTIONS && hi - lo > tolerance; j++) {
11173		const mid = (lo + hi) / 2;
11174		if (isBelowTerrain(ray, mid)) hi = mid;
11175		else lo = mid;
11176	}
11177	return {
11178		lo,
11179		hi
11180	};
11181}
11182//#endregion
11183//#region src/geo/projection/mercator_transform.ts
11184const TARGET_WORLD_STEP_PX = 4;
11185const MAX_SAMPLES = 512;
11186const MERCATOR_BISECT_EPSILON_WORLD_PX = .001;
11187var MercatorTransform = class MercatorTransform {
11188	get pixelsToClipSpaceMatrix() {
11189		return this._helper.pixelsToClipSpaceMatrix;
11190	}
11191	get clipSpaceToPixelsMatrix() {
11192		return this._helper.clipSpaceToPixelsMatrix;
11193	}
11194	get pixelsToGLUnits() {
11195		return this._helper.pixelsToGLUnits;
11196	}
11197	get centerOffset() {
11198		return this._helper.centerOffset;
11199	}
11200	get size() {
11201		return this._helper.size;
11202	}
11203	get rotationMatrix() {
11204		return this._helper.rotationMatrix;
11205	}
11206	get centerPoint() {
11207		return this._helper.centerPoint;
11208	}
11209	get pixelsPerMeter() {
11210		return this._helper.pixelsPerMeter;
11211	}
11212	setMinZoom(zoom) {
11213		this._helper.setMinZoom(zoom);
11214	}
11215	setMaxZoom(zoom) {
11216		this._helper.setMaxZoom(zoom);
11217	}
11218	setMinPitch(pitch) {
11219		this._helper.setMinPitch(pitch);
11220	}
11221	setMaxPitch(pitch) {
11222		this._helper.setMaxPitch(pitch);
11223	}
11224	setRenderWorldCopies(renderWorldCopies) {
11225		this._helper.setRenderWorldCopies(renderWorldCopies);
11226	}
11227	setBearing(bearing) {
11228		this._helper.setBearing(bearing);
11229	}
11230	setPitch(pitch) {
11231		this._helper.setPitch(pitch);
11232	}
11233	setRoll(roll) {
11234		this._helper.setRoll(roll);
11235	}
11236	setFov(fov) {
11237		this._helper.setFov(fov);
11238	}
11239	setZoom(zoom) {
11240		this._helper.setZoom(zoom);
11241	}
11242	setCenter(center) {
11243		this._helper.setCenter(center);
11244	}
11245	setElevation(elevation) {
11246		this._helper.setElevation(elevation);
11247	}
11248	setMinElevationForCurrentTile(elevation) {
11249		this._helper.setMinElevationForCurrentTile(elevation);
11250	}
11251	setPadding(padding) {
11252		this._helper.setPadding(padding);
11253	}
11254	interpolatePadding(start, target, t) {
11255		this._helper.interpolatePadding(start, target, t);
11256	}
11257	isPaddingEqual(padding) {
11258		return this._helper.isPaddingEqual(padding);
11259	}
11260	resize(width, height, constrain = true) {
11261		this._helper.resize(width, height, constrain);
11262	}
11263	getMaxBounds() {
11264		return this._helper.getMaxBounds();
11265	}
11266	setMaxBounds(bounds) {
11267		this._helper.setMaxBounds(bounds);
11268	}
11269	setConstrainOverride(constrain) {
11270		this._helper.setConstrainOverride(constrain);
11271	}
11272	overrideNearFarZ(nearZ, farZ) {
11273		this._helper.overrideNearFarZ(nearZ, farZ);
11274	}
11275	clearNearFarZOverride() {
11276		this._helper.clearNearFarZOverride();
11277	}
11278	getCameraQueryGeometry(queryGeometry) {
11279		return this._helper.getCameraQueryGeometry(this.getCameraPoint(), queryGeometry);
11280	}
11281	get tileSize() {
11282		return this._helper.tileSize;
11283	}
11284	get tileZoom() {
11285		return this._helper.tileZoom;
11286	}
11287	get scale() {
11288		return this._helper.scale;
11289	}
11290	get worldSize() {
11291		return this._helper.worldSize;
11292	}
11293	get width() {
11294		return this._helper.width;
11295	}
11296	get height() {
11297		return this._helper.height;
11298	}
11299	get lngRange() {
11300		return this._helper.lngRange;
11301	}
11302	get latRange() {
11303		return this._helper.latRange;
11304	}
11305	get minZoom() {
11306		return this._helper.minZoom;
11307	}
11308	get maxZoom() {
11309		return this._helper.maxZoom;
11310	}
11311	get zoom() {
11312		return this._helper.zoom;
11313	}
11314	get center() {
11315		return this._helper.center;
11316	}
11317	get minPitch() {
11318		return this._helper.minPitch;
11319	}
11320	get maxPitch() {
11321		return this._helper.maxPitch;
11322	}
11323	get pitch() {
11324		return this._helper.pitch;
11325	}
11326	get pitchInRadians() {
11327		return this._helper.pitchInRadians;
11328	}
11329	get roll() {
11330		return this._helper.roll;
11331	}
11332	get rollInRadians() {
11333		return this._helper.rollInRadians;
11334	}
11335	get bearing() {
11336		return this._helper.bearing;
11337	}
11338	get bearingInRadians() {
11339		return this._helper.bearingInRadians;
11340	}
11341	get fov() {
11342		return this._helper.fov;
11343	}
11344	get fovInRadians() {
11345		return this._helper.fovInRadians;
11346	}
11347	get elevation() {
11348		return this._helper.elevation;
11349	}
11350	get minElevationForCurrentTile() {
11351		return this._helper.minElevationForCurrentTile;
11352	}
11353	get padding() {
11354		return this._helper.padding;
11355	}
11356	get unmodified() {
11357		return this._helper.unmodified;
11358	}
11359	get renderWorldCopies() {
11360		return this._helper.renderWorldCopies;
11361	}
11362	get cameraToCenterDistance() {
11363		return this._helper.cameraToCenterDistance;
11364	}
11365	get constrainOverride() {
11366		return this._helper.constrainOverride;
11367	}
11368	get nearZ() {
11369		return this._helper.nearZ;
11370	}
11371	get farZ() {
11372		return this._helper.farZ;
11373	}
11374	get autoCalculateNearFarZ() {
11375		return this._helper.autoCalculateNearFarZ;
11376	}
11377	setTransitionState(_value) {}
11378	/**
11379	* @param options - Initial state. Ignored when `sharedHelper` is given, which already carries it.
11380	* @param sharedHelper - Camera to use instead of owning one, so that a composing transform such as
11381	* {@link GlobeTransform} keeps a single copy of the state rather than one per child. Its owner then drives
11382	* {@link _calcMatrices}, because a helper has only one `calcMatrices` callback.
11383	*/
11384	constructor(options, sharedHelper) {
11385		this._posMatrixCache = /* @__PURE__ */ new Map();
11386		this._alignedPosMatrixCache = /* @__PURE__ */ new Map();
11387		this._fogMatrixCacheF32 = /* @__PURE__ */ new Map();
11388		this.defaultConstrain = (lngLat, zoom) => {
11389			zoom = clamp(+zoom, this.minZoom, this.maxZoom);
11390			const result = {
11391				center: new LngLat(lngLat.lng, lngLat.lat),
11392				zoom
11393			};
11394			let lngRange = this._helper._lngRange;
11395			if (!this._helper._renderWorldCopies && lngRange === null) lngRange = [-179.9999999999, 180 - 1e-10];
11396			const worldSize = this.tileSize * zoomScale(result.zoom);
11397			let minY = 0;
11398			let maxY = worldSize;
11399			let minX = 0;
11400			let maxX = worldSize;
11401			let scaleY = 0;
11402			let scaleX = 0;
11403			const { x: screenWidth, y: screenHeight } = this.size;
11404			if (this._helper._latRange) {
11405				const latRange = this._helper._latRange;
11406				minY = mercatorYfromLat(latRange[1]) * worldSize;
11407				maxY = mercatorYfromLat(latRange[0]) * worldSize;
11408				if (maxY - minY < screenHeight) scaleY = screenHeight / (maxY - minY);
11409			}
11410			if (lngRange) {
11411				minX = wrap(mercatorXfromLng(lngRange[0]) * worldSize, 0, worldSize);
11412				maxX = wrap(mercatorXfromLng(lngRange[1]) * worldSize, 0, worldSize);
11413				if (maxX < minX) maxX += worldSize;
11414				if (maxX - minX < screenWidth) scaleX = screenWidth / (maxX - minX);
11415			}
11416			const { x: originalX, y: originalY } = projectToWorldCoordinates(worldSize, lngLat);
11417			let modifiedX, modifiedY;
11418			const scale = Math.max(scaleX || 0, scaleY || 0);
11419			if (scale) {
11420				const newPoint = new Point(scaleX ? (maxX + minX) / 2 : originalX, scaleY ? (maxY + minY) / 2 : originalY);
11421				result.center = unprojectFromWorldCoordinates(worldSize, newPoint).wrap();
11422				result.zoom += scaleZoom(scale);
11423				return result;
11424			}
11425			if (this._helper._latRange) {
11426				const h2 = screenHeight / 2;
11427				if (originalY - h2 < minY) modifiedY = minY + h2;
11428				if (originalY + h2 > maxY) modifiedY = maxY - h2;
11429			}
11430			if (lngRange) {
11431				const centerX = (minX + maxX) / 2;
11432				let wrappedX = originalX;
11433				if (this._helper._renderWorldCopies) wrappedX = wrap(originalX, centerX - worldSize / 2, centerX + worldSize / 2);
11434				const w2 = screenWidth / 2;
11435				if (wrappedX - w2 < minX) modifiedX = minX + w2;
11436				if (wrappedX + w2 > maxX) modifiedX = maxX - w2;
11437			}
11438			if (modifiedX !== void 0 || modifiedY !== void 0) {
11439				const newPoint = new Point(modifiedX ?? originalX, modifiedY ?? originalY);
11440				result.center = unprojectFromWorldCoordinates(worldSize, newPoint).wrap();
11441			}
11442			return result;
11443		};
11444		this.applyConstrain = (lngLat, zoom) => {
11445			return this._helper.applyConstrain(lngLat, zoom);
11446		};
11447		this._helper = sharedHelper ?? new TransformHelper({
11448			calcMatrices: () => this._calcMatrices(),
11449			defaultConstrain: (center, zoom) => {
11450				return this.defaultConstrain(center, zoom);
11451			}
11452		}, options);
11453		this._coveringTilesDetailsProvider = new MercatorCoveringTilesDetailsProvider();
11454	}
11455	clone() {
11456		const clone = new MercatorTransform();
11457		clone.apply(this, false);
11458		return clone;
11459	}
11460	apply(that, constrain) {
11461		this._helper.apply(that, constrain);
11462	}
11463	get cameraPosition() {
11464		return this._cameraPosition;
11465	}
11466	get projectionMatrix() {
11467		return this._projectionMatrix;
11468	}
11469	get modelViewProjectionMatrix() {
11470		return this._viewProjMatrix;
11471	}
11472	get inverseProjectionMatrix() {
11473		return this._invProjMatrix;
11474	}
11475	get mercatorMatrix() {
11476		return this._mercatorMatrix;
11477	}
11478	getVisibleUnwrappedCoordinates(tileID) {
11479		const result = [new UnwrappedTileID(0, tileID)];
11480		if (this._helper._renderWorldCopies) {
11481			const utl = this.screenPointToMercatorCoordinate(new Point(0, 0));
11482			const utr = this.screenPointToMercatorCoordinate(new Point(this._helper._width, 0));
11483			const ubl = this.screenPointToMercatorCoordinate(new Point(this._helper._width, this._helper._height));
11484			const ubr = this.screenPointToMercatorCoordinate(new Point(0, this._helper._height));
11485			const w0 = Math.floor(Math.min(utl.x, utr.x, ubl.x, ubr.x));
11486			const w1 = Math.floor(Math.max(utl.x, utr.x, ubl.x, ubr.x));
11487			const extraWorldCopy = 1;
11488			for (let w = w0 - extraWorldCopy; w <= w1 + extraWorldCopy; w++) {
11489				if (w === 0) continue;
11490				result.push(new UnwrappedTileID(w, tileID));
11491			}
11492		}
11493		return result;
11494	}
11495	getCameraFrustum() {
11496		return Frustum.fromInvProjectionMatrix(this._invViewProjMatrix, this.worldSize);
11497	}
11498	getClippingPlane() {
11499		return null;
11500	}
11501	getCoveringTilesDetailsProvider() {
11502		return this._coveringTilesDetailsProvider;
11503	}
11504	recalculateZoomAndCenter(terrain) {
11505		const center = this.screenPointToLocation(this.centerPoint, terrain);
11506		const elevation = terrain ? terrain.getElevationForLngLat(center, this) : 0;
11507		this._helper.recalculateZoomAndCenter(elevation);
11508	}
11509	/**
11510	* Moves the center so that `lnglat`, on the ground at `elevation` meters, renders
11511	* at the screen `point`. Both rays are cast through the same inverse pixel matrix
11512	* so its inversion error cancels out of their difference; the current-center ray
11513	* must be intersected at the center's own elevation (z=0) — intersecting it with
11514	* the elevated plane would land `(elevation - centerElevation)·tan(pitch)` away
11515	* from the center and make repeated calls drift.
11516	*/
11517	setLocationAtPoint(lnglat, point, elevation = this.elevation) {
11518		const z = elevation - this.elevation;
11519		const a = this.screenPointToMercatorCoordinateAtZ(point, z);
11520		const b = this.screenPointToMercatorCoordinateAtZ(this.centerPoint, 0);
11521		const loc = MercatorCoordinate.fromLngLat(lnglat);
11522		const newCenter = new MercatorCoordinate(loc.x - (a.x - b.x), loc.y - (a.y - b.y));
11523		this.setCenter(newCenter?.toLngLat());
11524		if (this._helper._renderWorldCopies) this.setCenter(this.center.wrap());
11525	}
11526	locationToScreenPoint(lnglat, terrain) {
11527		return terrain ? this.coordinatePoint(MercatorCoordinate.fromLngLat(lnglat), terrain.getElevationForLngLat(lnglat, this), this._pixelMatrix3D) : this.coordinatePoint(MercatorCoordinate.fromLngLat(lnglat));
11528	}
11529	screenPointToLocation(p, terrain) {
11530		return this.screenPointToMercatorCoordinate(p, terrain)?.toLngLat();
11531	}
11532	screenPointToLocationAtElevation(p, elevation) {
11533		return this.screenPointToMercatorCoordinateAtZ(p, elevation - this.elevation)?.toLngLat();
11534	}
11535	screenPointToMercatorCoordinate(p, terrain) {
11536		if (terrain) {
11537			const coordinate = this.screenTerrainPointToMercatorCoordinate(p, terrain);
11538			if (coordinate != null) return coordinate;
11539		}
11540		return this.screenPointToMercatorCoordinateAtZ(p);
11541	}
11542	/** {@inheritDoc ITransform.screenTerrainPointToMercatorCoordinate} */
11543	screenTerrainPointToMercatorCoordinate(p, terrain) {
11544		const index = terrain.getCoverageIndex();
11545		if (!index) return null;
11546		const { near, far } = this.getRaySegmentFromPixel(p);
11547		const worldSize = this.worldSize;
11548		const dx = far[0] - near[0];
11549		const dy = far[1] - near[1];
11550		const dz = far[2] - near[2];
11551		const ray = {
11552			index,
11553			exaggeration: terrain.exaggeration,
11554			near,
11555			dx,
11556			dy,
11557			dz,
11558			worldSize
11559		};
11560		let tStart = 0;
11561		let tEnd = 1;
11562		if (dz === 0) {
11563			if (near[2] > index.maxElevation || near[2] < index.minElevation) return null;
11564		} else {
11565			const tHigh = (index.maxElevation - near[2]) / dz;
11566			const tLow = (index.minElevation - near[2]) / dz;
11567			tStart = Math.max(tStart, Math.min(tHigh, tLow));
11568			tEnd = Math.min(tEnd, Math.max(tHigh, tLow));
11569			if (tStart > tEnd) return null;
11570		}
11571		const horizontalLength = Math.hypot(dx, dy);
11572		const samples = clamp(Math.ceil(horizontalLength * (tEnd - tStart) / TARGET_WORLD_STEP_PX), 1, MAX_SAMPLES);
11573		let previousT = 0;
11574		let aboveTerrain = !mercatorIsBelowTerrain(ray, 0);
11575		for (let i = 0; i <= samples; i++) {
11576			const t = tStart + (tEnd - tStart) * i / samples;
11577			if (!aboveTerrain) aboveTerrain = !mercatorIsBelowTerrain(ray, t);
11578			else if (mercatorIsBelowTerrain(ray, t)) {
11579				const { lo, hi } = bisect(ray, mercatorIsBelowTerrain, previousT, t, MERCATOR_BISECT_EPSILON_WORLD_PX / horizontalLength);
11580				const sampleLo = mercatorSampleAt(ray, lo);
11581				const sampleHi = mercatorSampleAt(ray, hi);
11582				const fLo = near[2] + lo * dz - sampleLo.elevation;
11583				const fHi = near[2] + hi * dz - sampleHi.elevation;
11584				const hit = sampleLo.covered && fLo > fHi ? clamp(lo + fLo * (hi - lo) / (fLo - fHi), lo, hi) : hi;
11585				return new MercatorCoordinate((near[0] + hit * dx) / worldSize, (near[1] + hit * dy) / worldSize, mercatorSampleAt(ray, hit).elevation);
11586			}
11587			previousT = t;
11588		}
11589		return null;
11590	}
11591	/**
11592	* Returns the segment of the ray through the given screen pixel that lies inside the view frustum.
11593	*/
11594	getRaySegmentFromPixel(p) {
11595		const coord0 = [
11596			p.x,
11597			p.y,
11598			0,
11599			1
11600		];
11601		const coord1 = [
11602			p.x,
11603			p.y,
11604			1,
11605			1
11606		];
11607		transformMat4(coord0, coord0, this._pixelMatrixInverse);
11608		transformMat4(coord1, coord1, this._pixelMatrixInverse);
11609		const w0 = coord0[3];
11610		const w1 = coord1[3];
11611		const elevation = this.elevation;
11612		return {
11613			near: [
11614				coord0[0] / w0,
11615				coord0[1] / w0,
11616				coord0[2] / w0 + elevation
11617			],
11618			far: [
11619				coord1[0] / w1,
11620				coord1[1] / w1,
11621				coord1[2] / w1 + elevation
11622			]
11623		};
11624	}
11625	/**
11626	* Intersects the ray through a screen point with the horizontal plane at `z`,
11627	* given in meters relative to the plane at the center's elevation (not mercator units).
11628	*/
11629	screenPointToMercatorCoordinateAtZ(p, z) {
11630		const targetZ = z ? z : 0;
11631		const { near, far } = this.getRaySegmentFromPixel(p);
11632		const t = near[2] === far[2] ? 0 : (targetZ + this.elevation - near[2]) / (far[2] - near[2]);
11633		return new MercatorCoordinate(interpolateFactory.number(near[0], far[0], t) / this.worldSize, interpolateFactory.number(near[1], far[1], t) / this.worldSize, targetZ);
11634	}
11635	/**
11636	* Given a coordinate, return the screen point that corresponds to it
11637	* @param coord - the coordinates
11638	* @param elevation - the elevation
11639	* @param pixelMatrix - the pixel matrix
11640	* @returns screen point. Point will be outside the viewport if the coordinate is behind the camera.
11641	*/
11642	coordinatePoint(coord, elevation = 0, pixelMatrix = this._pixelMatrix) {
11643		const p = this._coordinateClipPoint(coord, elevation, pixelMatrix);
11644		const w = p[3];
11645		if (w > 0) return new Point(p[0] / w, p[1] / w);
11646		return this._offScreenPointBehindCamera(p[0], p[1], w);
11647	}
11648	/**
11649	* The coordinate in the clip space of `pixelMatrix`: pixel x and y and NDC depth, each times `w`, which is positive
11650	* in front of the camera.
11651	*/
11652	_coordinateClipPoint(coord, elevation, pixelMatrix) {
11653		const p = [
11654			coord.x * this.worldSize,
11655			coord.y * this.worldSize,
11656			elevation,
11657			1
11658		];
11659		return transformMat4(p, p, pixelMatrix);
11660	}
11661	/**
11662	* Returns a screen point outside the viewport for a coordinate that is behind the camera.
11663	* The point lies on the boundary of the viewport enlarged by one viewport size on each side,
11664	* on whichever edge the ray from the screen centre in the coordinate's direction reaches first.
11665	* A coordinate exactly behind the camera has no direction to leave through, so it is placed straight down.
11666	* @param x - the x component of the clip space position, before the division by w
11667	* @param y - the y component of the clip space position, before the division by w
11668	* @param w - the w component of the clip space position, zero or negative
11669	* @returns screen point outside the viewport
11670	*/
11671	_offScreenPointBehindCamera(x, y, w) {
11672		const cx = this.width / 2;
11673		const cy = this.height / 2;
11674		const dx = x - cx * w;
11675		let dy = y - cy * w;
11676		if (dx === 0 && dy === 0) dy = 1;
11677		const halfExtentX = cx + this.width;
11678		const halfExtentY = cy + this.height;
11679		const scale = Math.min(dx !== 0 ? halfExtentX / Math.abs(dx) : Infinity, dy !== 0 ? halfExtentY / Math.abs(dy) : Infinity);
11680		return new Point(cx + dx * scale, cy + dy * scale);
11681	}
11682	getBounds() {
11683		const top = Math.max(0, this._helper._height / 2 - getMercatorHorizon(this));
11684		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)));
11685	}
11686	isPointOnMapSurface(p, terrain) {
11687		if (terrain) return this.screenTerrainPointToMercatorCoordinate(p, terrain) != null;
11688		return p.y > this.height / 2 - getMercatorHorizon(this);
11689	}
11690	calculatePosMatrix(tileID, aligned = false, useFloat32 = false) {
11691		const posMatrixKey = tileID.key ?? calculateTileKey(tileID.wrap, tileID.canonical.z, tileID.canonical.z, tileID.canonical.x, tileID.canonical.y);
11692		const cache = aligned ? this._alignedPosMatrixCache : this._posMatrixCache;
11693		if (cache.has(posMatrixKey)) {
11694			const matrices = cache.get(posMatrixKey);
11695			return useFloat32 ? matrices.f32 : matrices.f64;
11696		}
11697		const tileMatrix = calculateTileMatrix(tileID, this.worldSize);
11698		multiply(tileMatrix, aligned ? this._alignedProjMatrix : this._viewProjMatrix, tileMatrix);
11699		const matrices = {
11700			f64: tileMatrix,
11701			f32: new Float32Array(tileMatrix)
11702		};
11703		cache.set(posMatrixKey, matrices);
11704		return useFloat32 ? matrices.f32 : matrices.f64;
11705	}
11706	calculateFogMatrix(unwrappedTileID) {
11707		const posMatrixKey = unwrappedTileID.key;
11708		const cache = this._fogMatrixCacheF32;
11709		if (cache.has(posMatrixKey)) return cache.get(posMatrixKey);
11710		const fogMatrix = calculateTileMatrix(unwrappedTileID, this.worldSize);
11711		multiply(fogMatrix, this._fogMatrix, fogMatrix);
11712		cache.set(posMatrixKey, new Float32Array(fogMatrix));
11713		return cache.get(posMatrixKey);
11714	}
11715	calculateCenterFromCameraLngLatAlt(lnglat, alt, bearing, pitch) {
11716		return this._helper.calculateCenterFromCameraLngLatAlt(lnglat, alt, bearing, pitch);
11717	}
11718	calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo) {
11719		const fromMercator = MercatorCoordinate.fromLngLat(from, altitudeFrom);
11720		const toMercator = MercatorCoordinate.fromLngLat(to, altitudeTo);
11721		const dx = toMercator.x - fromMercator.x;
11722		const dy = toMercator.y - fromMercator.y;
11723		const dz = toMercator.z - fromMercator.z;
11724		const distance3D = Math.hypot(dx, dy, dz);
11725		if (distance3D === 0) throw new Error("Can't calculate camera options with same From and To");
11726		const groundDistance = Math.hypot(dx, dy);
11727		const zoom = scaleZoom(this.cameraToCenterDistance / distance3D / this.tileSize);
11728		const bearing = radiansToDegrees(Math.atan2(dx, -dy));
11729		let pitch = radiansToDegrees(Math.acos(groundDistance / distance3D));
11730		pitch = dz < 0 ? 90 - pitch : 90 + pitch;
11731		return {
11732			center: toMercator.toLngLat(),
11733			elevation: altitudeTo,
11734			zoom,
11735			pitch,
11736			bearing
11737		};
11738	}
11739	_calculateNearFarZ(cameraToSeaLevelDistance, limitedPitchRadians, offset) {
11740		const minElevation = Math.min(this.elevation, this.minElevationForCurrentTile, this.getCameraAltitude() - 100);
11741		const cameraToLowestPointDistance = cameraToSeaLevelDistance - minElevation * this._helper._pixelPerMeter / Math.cos(limitedPitchRadians);
11742		const lowestPlane = minElevation < 0 ? cameraToLowestPointDistance : cameraToSeaLevelDistance;
11743		const groundAngle = Math.PI / 2 + this.pitchInRadians;
11744		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);
11745		const topHalfSurfaceDistance = Math.sin(fovAboveCenter) * lowestPlane / Math.sin(clamp(Math.PI - groundAngle - fovAboveCenter, .01, Math.PI - .01));
11746		const horizon = getMercatorHorizon(this);
11747		const horizonAngle = Math.atan(horizon / this._helper.cameraToCenterDistance);
11748		const minFovCenterToHorizonRadians = degreesToRadians(90 - maxMercatorHorizonAngle);
11749		const fovCenterToHorizon = horizonAngle > minFovCenterToHorizonRadians ? 2 * horizonAngle * (.5 + offset.y / (horizon * 2)) : minFovCenterToHorizonRadians;
11750		const topHalfSurfaceDistanceHorizon = Math.sin(fovCenterToHorizon) * lowestPlane / Math.sin(clamp(Math.PI - groundAngle - fovCenterToHorizon, .01, Math.PI - .01));
11751		const topHalfMinDistance = Math.min(topHalfSurfaceDistance, topHalfSurfaceDistanceHorizon);
11752		this._helper._farZ = (Math.cos(Math.PI / 2 - limitedPitchRadians) * topHalfMinDistance + lowestPlane) * 1.01;
11753		this._helper._nearZ = this._helper._height / 50;
11754	}
11755	/**
11756	* @param calculateNearFarZ - Whether to compute the near/far Z range, or leave the range the helper already
11757	* holds. Defaults to {@link autoCalculateNearFarZ}; a composing transform such as {@link GlobeTransform}
11758	* overrides it so that its two children share a single depth range.
11759	*/
11760	_calcMatrices(calculateNearFarZ = this._helper.autoCalculateNearFarZ) {
11761		const offset = this.centerOffset;
11762		const point = projectToWorldCoordinates(this.worldSize, this.center);
11763		const x = point.x, y = point.y;
11764		const limitedPitchRadians = degreesToRadians(Math.min(this.pitch, maxMercatorHorizonAngle));
11765		const cameraToSeaLevelDistance = Math.max(this._helper.cameraToCenterDistance / 2, this._helper.cameraToCenterDistance + this._helper._elevation * this._helper._pixelPerMeter / Math.cos(limitedPitchRadians));
11766		if (calculateNearFarZ) this._calculateNearFarZ(cameraToSeaLevelDistance, limitedPitchRadians, offset);
11767		let m;
11768		m = /* @__PURE__ */ new Float64Array(16);
11769		perspective(m, this.fovInRadians, this._helper._width / this._helper._height, this._helper._nearZ, this._helper._farZ);
11770		this._invProjMatrix = /* @__PURE__ */ new Float64Array(16);
11771		fastInvertProjMat4(this._invProjMatrix, m);
11772		m[8] = -offset.x * 2 / this._helper._width;
11773		m[9] = offset.y * 2 / this._helper._height;
11774		this._projectionMatrix = clone$1(m);
11775		scale(m, m, [
11776			1,
11777			-1,
11778			1
11779		]);
11780		translate(m, m, [
11781			0,
11782			0,
11783			-this._helper.cameraToCenterDistance
11784		]);
11785		rotateZ(m, m, -this.rollInRadians);
11786		rotateX(m, m, this.pitchInRadians);
11787		rotateZ(m, m, -this.bearingInRadians);
11788		translate(m, m, [
11789			-x,
11790			-y,
11791			0
11792		]);
11793		this._mercatorMatrix = scale([], m, [
11794			this.worldSize,
11795			this.worldSize,
11796			this.worldSize
11797		]);
11798		scale(m, m, [
11799			1,
11800			1,
11801			this._helper._pixelPerMeter
11802		]);
11803		this._pixelMatrix = multiply(/* @__PURE__ */ new Float64Array(16), this.clipSpaceToPixelsMatrix, m);
11804		translate(m, m, [
11805			0,
11806			0,
11807			-this.elevation
11808		]);
11809		this._viewProjMatrix = m;
11810		this._invViewProjMatrix = invert$1([], m);
11811		const cameraPos = [
11812			0,
11813			0,
11814			-1,
11815			1
11816		];
11817		transformMat4(cameraPos, cameraPos, this._invViewProjMatrix);
11818		this._cameraPosition = [
11819			cameraPos[0] / cameraPos[3],
11820			cameraPos[1] / cameraPos[3],
11821			cameraPos[2] / cameraPos[3]
11822		];
11823		this._fogMatrix = /* @__PURE__ */ new Float64Array(16);
11824		perspective(this._fogMatrix, this.fovInRadians, this.width / this.height, cameraToSeaLevelDistance, this._helper._farZ);
11825		this._fogMatrix[8] = -offset.x * 2 / this.width;
11826		this._fogMatrix[9] = offset.y * 2 / this.height;
11827		scale(this._fogMatrix, this._fogMatrix, [
11828			1,
11829			-1,
11830			1
11831		]);
11832		translate(this._fogMatrix, this._fogMatrix, [
11833			0,
11834			0,
11835			-this.cameraToCenterDistance
11836		]);
11837		rotateZ(this._fogMatrix, this._fogMatrix, -this.rollInRadians);
11838		rotateX(this._fogMatrix, this._fogMatrix, this.pitchInRadians);
11839		rotateZ(this._fogMatrix, this._fogMatrix, -this.bearingInRadians);
11840		translate(this._fogMatrix, this._fogMatrix, [
11841			-x,
11842			-y,
11843			0
11844		]);
11845		scale(this._fogMatrix, this._fogMatrix, [
11846			1,
11847			1,
11848			this._helper._pixelPerMeter
11849		]);
11850		translate(this._fogMatrix, this._fogMatrix, [
11851			0,
11852			0,
11853			-this.elevation
11854		]);
11855		this._pixelMatrix3D = multiply(/* @__PURE__ */ new Float64Array(16), this.clipSpaceToPixelsMatrix, m);
11856		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;
11857		const alignedM = new Float64Array(m);
11858		translate(alignedM, alignedM, [
11859			dx > .5 ? dx - 1 : dx,
11860			dy > .5 ? dy - 1 : dy,
11861			0
11862		]);
11863		this._alignedProjMatrix = alignedM;
11864		m = invert$1(/* @__PURE__ */ new Float64Array(16), this._pixelMatrix);
11865		if (!m) throw new Error("failed to invert matrix");
11866		this._pixelMatrixInverse = m;
11867		this._clearMatrixCaches();
11868	}
11869	_clearMatrixCaches() {
11870		this._posMatrixCache.clear();
11871		this._alignedPosMatrixCache.clear();
11872		this._fogMatrixCacheF32.clear();
11873	}
11874	maxPitchScaleFactor() {
11875		if (!this._pixelMatrixInverse) return 1;
11876		const coord = this.screenPointToMercatorCoordinate(new Point(0, 0));
11877		const p = [
11878			coord.x * this.worldSize,
11879			coord.y * this.worldSize,
11880			0,
11881			1
11882		];
11883		return transformMat4(p, p, this._pixelMatrix)[3] / this._helper.cameraToCenterDistance;
11884	}
11885	getCameraPoint() {
11886		return this._helper.getCameraPoint();
11887	}
11888	getCameraAltitude() {
11889		return Math.cos(this.pitchInRadians) * this.cameraToCenterDistance / this.pixelsPerMeter + this.elevation;
11890	}
11891	getCameraLngLat() {
11892		const pixelPerMeter = mercatorZfromAltitude(1, this.center.lat) * this.worldSize;
11893		const cameraToCenterDistanceMeters = this._helper.cameraToCenterDistance / pixelPerMeter;
11894		return cameraMercatorCoordinateFromCenterAndRotation(this.center, this.elevation, this.pitch, this.bearing, cameraToCenterDistanceMeters).toLngLat();
11895	}
11896	getProjectionData(params) {
11897		const { overscaledTileID, aligned, applyTerrainMatrix } = params;
11898		const mercatorTileCoordinates = this._helper.getMercatorTileCoordinates(overscaledTileID);
11899		const tilePosMatrix = overscaledTileID ? this.calculatePosMatrix(overscaledTileID, aligned, true) : null;
11900		let mainMatrix;
11901		if (overscaledTileID?.terrainRttPosMatrix32f && applyTerrainMatrix) mainMatrix = overscaledTileID.terrainRttPosMatrix32f;
11902		else if (tilePosMatrix) mainMatrix = tilePosMatrix;
11903		else mainMatrix = createIdentityMat4f32();
11904		return {
11905			mainMatrix,
11906			tileMercatorCoords: mercatorTileCoordinates,
11907			clippingPlane: [
11908				0,
11909				0,
11910				0,
11911				0
11912			],
11913			projectionTransition: 0,
11914			fallbackMatrix: mainMatrix,
11915			clipAntimeridian: false
11916		};
11917	}
11918	/** {@inheritDoc ITransform.isLocationOccluded} */
11919	isLocationOccluded(lngLat, terrain, elevation) {
11920		if (!terrain?.getCoverageIndex()) return false;
11921		const location = MercatorCoordinate.fromLngLat(lngLat);
11922		elevation ??= terrain.getElevationForLngLat(lngLat, this);
11923		const clip = this._coordinateClipPoint(location, elevation, this._pixelMatrix3D);
11924		const w = clip[3];
11925		if (w <= 0 || clip[2] > w) return true;
11926		const p = new Point(clip[0] / w, clip[1] / w);
11927		const hit = this.screenTerrainPointToMercatorCoordinate(p, terrain);
11928		if (hit == null) return false;
11929		const segment = this.getRaySegmentFromPixel(p);
11930		const tLocation = raySegmentParameter(segment, location.x * this.worldSize, location.y * this.worldSize, elevation);
11931		return raySegmentParameter(segment, hit.x * this.worldSize, hit.y * this.worldSize, hit.z) < tLocation * .99;
11932	}
11933	getPixelScale() {
11934		return 1;
11935	}
11936	getCircleRadiusCorrection() {
11937		return 1;
11938	}
11939	getPitchedTextCorrection(_textAnchorX, _textAnchorY, _tileID) {
11940		return 1;
11941	}
11942	transformLightDirection(dir) {
11943		return clone(dir);
11944	}
11945	getRayDirectionFromPixel(_p) {
11946		throw new Error("Not implemented.");
11947	}
11948	projectTileCoordinates(x, y, unwrappedTileID, elevation) {
11949		const matrix = this.calculatePosMatrix(unwrappedTileID);
11950		let pos;
11951		if (elevation != null) {
11952			pos = [
11953				x,
11954				y,
11955				elevation,
11956				1
11957			];
11958			transformMat4(pos, pos, matrix);
11959		} else {
11960			pos = [
11961				x,
11962				y,
11963				0,
11964				1
11965			];
11966			xyTransformMat4(pos, pos, matrix);
11967		}
11968		const w = pos[3];
11969		return {
11970			point: new Point(pos[0] / w, pos[1] / w),
11971			signedDistanceFromCamera: w,
11972			isOccluded: false
11973		};
11974	}
11975	populateCache(coords) {
11976		for (const coord of coords) this.calculatePosMatrix(coord);
11977	}
11978	getProjectionDataForCustomLayer(applyGlobeMatrix = true) {
11979		const tileID = new OverscaledTileID(0, 0, 0, 0, 0);
11980		const rendererProjectionData = this.getProjectionData({
11981			overscaledTileID: tileID,
11982			applyGlobeMatrix
11983		});
11984		const tileMatrix = calculateTileMatrix(tileID, this.worldSize);
11985		multiply(tileMatrix, this._viewProjMatrix, tileMatrix);
11986		const scale$4 = [
11987			EXTENT,
11988			EXTENT,
11989			this.worldSize / this._helper.pixelsPerMeter
11990		];
11991		const projectionMatrixScaled = createMat4f64();
11992		scale(projectionMatrixScaled, tileMatrix, scale$4);
11993		return {
11994			...rendererProjectionData,
11995			tileMercatorCoords: [
11996				0,
11997				0,
11998				1,
11999				1
12000			],
12001			fallbackMatrix: projectionMatrixScaled,
12002			mainMatrix: projectionMatrixScaled
12003		};
12004	}
12005	getFastPathSimpleProjectionMatrix(tileID) {
12006		return this.calculatePosMatrix(tileID);
12007	}
12008};
12009function mercatorSampleAt(ray, t) {
12010	return sampleAt(ray.index, ray.exaggeration, (ray.near[0] + t * ray.dx) / ray.worldSize, (ray.near[1] + t * ray.dy) / ray.worldSize);
12011}
12012function mercatorIsBelowTerrain(ray, t) {
12013	return isBelowTerrainSample(mercatorSampleAt(ray, t), ray.near[2] + t * ray.dz);
12014}
12015/**
12016* Where the point of `segment` closest to the given world pixel position and elevation lies along it,
12017* as the fraction from `near` (0) to `far` (1).
12018*/
12019function raySegmentParameter({ near, far }, worldX, worldY, elevation) {
12020	const dx = far[0] - near[0];
12021	const dy = far[1] - near[1];
12022	const dz = far[2] - near[2];
12023	return ((worldX - near[0]) * dx + (worldY - near[1]) * dy + (elevation - near[2]) * dz) / (dx * dx + dy * dy + dz * dz);
12024}
12025//#endregion
12026//#region src/geo/projection/camera_helper.ts
12027/**
12028* @internal
12029*/
12030function cameraBoundsWarning() {
12031	warnOnce("Map cannot fit within canvas with the given bounds, padding, and/or offset.");
12032}
12033/**
12034* @internal
12035* Set a transform's rotation to a value interpolated between startEulerAngles and endEulerAngles
12036*/
12037function updateRotation(args) {
12038	if (args.useSlerp) {
12039		if (args.k < 1) {
12040			const startRotation = rollPitchBearingToQuat(args.startEulerAngles.roll, args.startEulerAngles.pitch, args.startEulerAngles.bearing);
12041			const endRotation = rollPitchBearingToQuat(args.endEulerAngles.roll, args.endEulerAngles.pitch, args.endEulerAngles.bearing);
12042			const rotation = /* @__PURE__ */ new Float64Array(4);
12043			slerp(rotation, startRotation, endRotation, args.k);
12044			const eulerAngles = getRollPitchBearing(rotation);
12045			args.tr.setRoll(eulerAngles.roll);
12046			args.tr.setPitch(eulerAngles.pitch);
12047			args.tr.setBearing(eulerAngles.bearing);
12048		} else {
12049			args.tr.setRoll(args.endEulerAngles.roll);
12050			args.tr.setPitch(args.endEulerAngles.pitch);
12051			args.tr.setBearing(args.endEulerAngles.bearing);
12052		}
12053	} else {
12054		args.tr.setRoll(interpolateFactory.number(args.startEulerAngles.roll, args.endEulerAngles.roll, args.k));
12055		args.tr.setPitch(interpolateFactory.number(args.startEulerAngles.pitch, args.endEulerAngles.pitch, args.k));
12056		args.tr.setBearing(interpolateFactory.number(args.startEulerAngles.bearing, args.endEulerAngles.bearing, args.k));
12057	}
12058}
12059function cameraForBoxAndBearing(options, padding, bounds, bearing, tr) {
12060	const edgePadding = tr.padding;
12061	const nwWorld = projectToWorldCoordinates(tr.worldSize, bounds.getNorthWest());
12062	const neWorld = projectToWorldCoordinates(tr.worldSize, bounds.getNorthEast());
12063	const seWorld = projectToWorldCoordinates(tr.worldSize, bounds.getSouthEast());
12064	const swWorld = projectToWorldCoordinates(tr.worldSize, bounds.getSouthWest());
12065	const bearingRadians = degreesToRadians(-bearing);
12066	const nwRotatedWorld = nwWorld.rotate(bearingRadians);
12067	const neRotatedWorld = neWorld.rotate(bearingRadians);
12068	const seRotatedWorld = seWorld.rotate(bearingRadians);
12069	const swRotatedWorld = swWorld.rotate(bearingRadians);
12070	const upperRight = new Point(Math.max(nwRotatedWorld.x, neRotatedWorld.x, swRotatedWorld.x, seRotatedWorld.x), Math.max(nwRotatedWorld.y, neRotatedWorld.y, swRotatedWorld.y, seRotatedWorld.y));
12071	const lowerLeft = new Point(Math.min(nwRotatedWorld.x, neRotatedWorld.x, swRotatedWorld.x, seRotatedWorld.x), Math.min(nwRotatedWorld.y, neRotatedWorld.y, swRotatedWorld.y, seRotatedWorld.y));
12072	const size = upperRight.sub(lowerLeft);
12073	const availableWidth = tr.width - (edgePadding.left + edgePadding.right + padding.left + padding.right);
12074	const availableHeight = tr.height - (edgePadding.top + edgePadding.bottom + padding.top + padding.bottom);
12075	const scaleX = availableWidth / size.x;
12076	const scaleY = availableHeight / size.y;
12077	if (scaleY < 0 || scaleX < 0) {
12078		cameraBoundsWarning();
12079		return;
12080	}
12081	const zoom = Math.min(scaleZoom(tr.scale * Math.min(scaleX, scaleY)), options.maxZoom);
12082	const offset = Point.convert(options.offset);
12083	const paddingOffsetX = (padding.left - padding.right) / 2;
12084	const paddingOffsetY = (padding.top - padding.bottom) / 2;
12085	const rotatedPaddingOffset = new Point(paddingOffsetX, paddingOffsetY).rotate(degreesToRadians(bearing));
12086	const offsetAtFinalZoom = offset.add(rotatedPaddingOffset).mult(tr.scale / zoomScale(zoom));
12087	return {
12088		center: unprojectFromWorldCoordinates(tr.worldSize, nwWorld.add(seWorld).div(2).sub(offsetAtFinalZoom)),
12089		zoom,
12090		bearing
12091	};
12092}
12093//#endregion
12094//#region src/geo/projection/mercator_camera_helper.ts
12095/**
12096* @internal
12097*/
12098var MercatorCameraHelper = class {
12099	get useGlobeControls() {
12100		return false;
12101	}
12102	handlePanInertia(pan, transform) {
12103		const offsetLength = pan.mag();
12104		const pixelsToHorizon = Math.abs(getMercatorHorizon(transform));
12105		return {
12106			easingOffset: pan.mult(Math.min(pixelsToHorizon * .75 / offsetLength, 1)),
12107			easingCenter: transform.center
12108		};
12109	}
12110	handleMapControlsRollPitchBearingZoom(deltas, tr) {
12111		if (deltas.bearingDelta) tr.setBearing(tr.bearing + deltas.bearingDelta);
12112		if (deltas.pitchDelta) tr.setPitch(tr.pitch + deltas.pitchDelta);
12113		if (deltas.rollDelta) tr.setRoll(tr.roll + deltas.rollDelta);
12114		if (deltas.zoomDelta) tr.setZoom(tr.zoom + deltas.zoomDelta);
12115	}
12116	handleMapControlsPan(deltas, tr, preZoomAroundLoc) {
12117		if (deltas.around.distSqr(tr.centerPoint) < .01) return;
12118		tr.setLocationAtPoint(preZoomAroundLoc, deltas.around, deltas.aroundElevation);
12119	}
12120	cameraForBoxAndBearing(options, padding, bounds, bearing, tr) {
12121		return cameraForBoxAndBearing(options, padding, bounds, bearing, tr);
12122	}
12123	handleJumpToCenterZoom(tr, options) {
12124		const zoom = typeof options.zoom !== "undefined" ? +options.zoom : tr.zoom;
12125		if (tr.zoom !== zoom) tr.setZoom(+options.zoom);
12126		if (options.center !== void 0) tr.setCenter(LngLat.convert(options.center));
12127	}
12128	handleEaseTo(tr, options) {
12129		const startZoom = tr.zoom;
12130		const startPadding = tr.padding;
12131		const startEulerAngles = {
12132			roll: tr.roll,
12133			pitch: tr.pitch,
12134			bearing: tr.bearing
12135		};
12136		const endEulerAngles = {
12137			roll: options.roll === void 0 ? tr.roll : options.roll,
12138			pitch: options.pitch === void 0 ? tr.pitch : options.pitch,
12139			bearing: options.bearing === void 0 ? tr.bearing : options.bearing
12140		};
12141		const optionsZoom = typeof options.zoom !== "undefined";
12142		const doPadding = !tr.isPaddingEqual(options.padding);
12143		let isZooming = false;
12144		const zoom = optionsZoom ? +options.zoom : tr.zoom;
12145		let pointAtOffset = tr.centerPoint.add(options.offsetAsPoint);
12146		const locationAtOffset = tr.screenPointToLocation(pointAtOffset);
12147		const { center, zoom: endZoom } = tr.applyConstrain(LngLat.convert(options.center || locationAtOffset), zoom ?? startZoom);
12148		normalizeCenter(tr, center);
12149		const from = projectToWorldCoordinates(tr.worldSize, locationAtOffset);
12150		const delta = projectToWorldCoordinates(tr.worldSize, center).sub(from);
12151		const finalScale = zoomScale(endZoom - startZoom);
12152		isZooming = endZoom !== startZoom;
12153		const easeFunc = (k) => {
12154			if (isZooming) tr.setZoom(interpolateFactory.number(startZoom, endZoom, k));
12155			if (!rollPitchBearingEqual(startEulerAngles, endEulerAngles)) updateRotation({
12156				startEulerAngles,
12157				endEulerAngles,
12158				tr,
12159				k,
12160				useSlerp: startEulerAngles.roll != endEulerAngles.roll
12161			});
12162			if (doPadding) {
12163				tr.interpolatePadding(startPadding, options.padding, k);
12164				pointAtOffset = tr.centerPoint.add(options.offsetAsPoint);
12165			}
12166			if (options.around) tr.setLocationAtPoint(options.around, options.aroundPoint);
12167			else {
12168				const scale = zoomScale(tr.zoom - startZoom);
12169				const speedup = Math.pow(endZoom > startZoom ? Math.min(2, finalScale) : Math.max(.5, finalScale), 1 - k);
12170				const newCenter = unprojectFromWorldCoordinates(tr.worldSize, from.add(delta.mult(k * speedup)).mult(scale));
12171				tr.setLocationAtPoint(tr.renderWorldCopies ? newCenter.wrap() : newCenter, pointAtOffset);
12172			}
12173		};
12174		return {
12175			easeFunc,
12176			isZooming,
12177			elevationCenter: center
12178		};
12179	}
12180	handleFlyTo(tr, options) {
12181		const optionsZoom = typeof options.zoom !== "undefined";
12182		const startZoom = tr.zoom;
12183		const constrained = tr.applyConstrain(LngLat.convert(options.center || options.locationAtOffset), optionsZoom ? +options.zoom : startZoom);
12184		const targetCenter = constrained.center;
12185		const targetZoom = constrained.zoom;
12186		normalizeCenter(tr, targetCenter);
12187		const startWorldSize = tr.worldSize;
12188		const from = projectToWorldCoordinates(startWorldSize, options.locationAtOffset);
12189		const delta = projectToWorldCoordinates(startWorldSize, targetCenter).sub(from);
12190		const pixelPathLength = delta.mag();
12191		const scaleOfZoom = zoomScale(targetZoom - startZoom);
12192		const requestedMinZoom = typeof options.minZoom !== "undefined" ? +options.minZoom : tr.minZoom;
12193		const effectiveMinZoom = Math.max(requestedMinZoom, tr.minZoom);
12194		const minZoomPreConstrain = Math.min(effectiveMinZoom, startZoom, targetZoom);
12195		const minZoom = tr.applyConstrain(targetCenter, minZoomPreConstrain).zoom;
12196		const scaleOfMinZoom = zoomScale(minZoom - startZoom);
12197		const easeFunc = (k, scale, centerFactor, pointAtOffset) => {
12198			tr.setZoom(k === 1 ? targetZoom : startZoom + scaleZoom(scale));
12199			const newCenter = k === 1 ? targetCenter : unprojectFromWorldCoordinates(startWorldSize, from.add(delta.mult(centerFactor)));
12200			tr.setLocationAtPoint(tr.renderWorldCopies ? newCenter.wrap() : newCenter, pointAtOffset);
12201		};
12202		return {
12203			easeFunc,
12204			scaleOfZoom,
12205			targetCenter,
12206			scaleOfMinZoom,
12207			pixelPathLength
12208		};
12209	}
12210};
12211//#endregion
12212//#region src/style/projection_properties.g.ts
12213let properties;
12214const getProperties = () => properties = properties || new Properties({ "type": new DataConstantProperty(latest["projection"]["type"], "type") });
12215//#endregion
12216//#region src/geo/projection/vertical_perspective_projection.ts
12217const VerticalPerspectiveShaderDefine = "#define GLOBE";
12218const VerticalPerspectiveShaderVariantKey = "globe";
12219const granularitySettingsGlobe = new SubdivisionGranularitySetting({
12220	fill: new SubdivisionGranularityExpression(128, 2),
12221	line: new SubdivisionGranularityExpression(512, 0),
12222	tile: new SubdivisionGranularityExpression(128, 32),
12223	stencil: new SubdivisionGranularityExpression(128, 1),
12224	circle: 3
12225});
12226var VerticalPerspectiveProjection = class {
12227	constructor() {
12228		this._tileMeshCache = {};
12229	}
12230	get name() {
12231		return "vertical-perspective";
12232	}
12233	get transitionState() {
12234		return 1;
12235	}
12236	get useSubdivision() {
12237		return true;
12238	}
12239	get shaderVariantName() {
12240		return VerticalPerspectiveShaderVariantKey;
12241	}
12242	get shaderDefine() {
12243		return VerticalPerspectiveShaderDefine;
12244	}
12245	get shaderPreludeCode() {
12246		return shaders.projectionGlobe;
12247	}
12248	get vertexShaderPreludeCode() {
12249		return shaders.projectionMercator.vertexSource;
12250	}
12251	get subdivisionGranularity() {
12252		return granularitySettingsGlobe;
12253	}
12254	get useGlobeControls() {
12255		return true;
12256	}
12257	destroy() {}
12258	_getMeshKey(options) {
12259		return `${options.granularity.toString(36)}_${options.generateBorders ? "b" : ""}${options.extendToNorthPole ? "n" : ""}${options.extendToSouthPole ? "s" : ""}`;
12260	}
12261	getMeshFromTileID(context, canonical, hasBorder, allowPoles, usage) {
12262		const granularity = (usage === "stencil" ? granularitySettingsGlobe.stencil : granularitySettingsGlobe.tile).getGranularityForZoomLevel(canonical.z);
12263		const north = canonical.y === 0 && allowPoles;
12264		const south = canonical.y === (1 << canonical.z) - 1 && allowPoles;
12265		return this._getMesh(context, {
12266			granularity,
12267			generateBorders: hasBorder,
12268			extendToNorthPole: north,
12269			extendToSouthPole: south
12270		});
12271	}
12272	_getMesh(context, options) {
12273		const key = this._getMeshKey(options);
12274		if (key in this._tileMeshCache) return this._tileMeshCache[key];
12275		const mesh = createTileMeshWithBuffers(context, options);
12276		this._tileMeshCache[key] = mesh;
12277		return mesh;
12278	}
12279	recalculate(_params) {}
12280	hasTransition() {
12281		return false;
12282	}
12283};
12284//#endregion
12285//#region src/geo/projection/globe_projection.ts
12286var GlobeProjection = class extends Evented {
12287	constructor(projection, globalState) {
12288		super();
12289		this._transitionable = new Transitionable(getProperties(), "projection", globalState);
12290		this.setProjection(projection);
12291		this._transitioning = this._transitionable.untransitioned();
12292		this.recalculate(new EvaluationParameters(0));
12293		this._mercatorProjection = new MercatorProjection();
12294		this._verticalPerspectiveProjection = new VerticalPerspectiveProjection();
12295	}
12296	get transitionState() {
12297		const currentProjectionSpecValue = this.properties.get("type");
12298		if (typeof currentProjectionSpecValue === "string" && currentProjectionSpecValue === "mercator") return 0;
12299		if (typeof currentProjectionSpecValue === "string" && currentProjectionSpecValue === "vertical-perspective") return 1;
12300		if (currentProjectionSpecValue instanceof ProjectionDefinition) {
12301			if (currentProjectionSpecValue.from === currentProjectionSpecValue.to) return currentProjectionSpecValue.from === "mercator" ? 0 : 1;
12302			if (currentProjectionSpecValue.from === "vertical-perspective" && currentProjectionSpecValue.to === "mercator") return 1 - currentProjectionSpecValue.transition;
12303			if (currentProjectionSpecValue.from === "mercator" && currentProjectionSpecValue.to === "vertical-perspective") return currentProjectionSpecValue.transition;
12304		}
12305		return 1;
12306	}
12307	get useGlobeRendering() {
12308		return this.transitionState > 0;
12309	}
12310	get currentProjection() {
12311		return this.useGlobeRendering ? this._verticalPerspectiveProjection : this._mercatorProjection;
12312	}
12313	get name() {
12314		return "globe";
12315	}
12316	get useSubdivision() {
12317		return this.currentProjection.useSubdivision;
12318	}
12319	get shaderVariantName() {
12320		return this.currentProjection.shaderVariantName;
12321	}
12322	get shaderDefine() {
12323		return this.currentProjection.shaderDefine;
12324	}
12325	get shaderPreludeCode() {
12326		return this.currentProjection.shaderPreludeCode;
12327	}
12328	get vertexShaderPreludeCode() {
12329		return this.currentProjection.vertexShaderPreludeCode;
12330	}
12331	get subdivisionGranularity() {
12332		return this.currentProjection.subdivisionGranularity;
12333	}
12334	get useGlobeControls() {
12335		return this.transitionState > 0;
12336	}
12337	destroy() {
12338		this._mercatorProjection.destroy();
12339		this._verticalPerspectiveProjection.destroy();
12340	}
12341	getMeshFromTileID(context, _tileID, _hasBorder, _allowPoles, _usage) {
12342		return this.currentProjection.getMeshFromTileID(context, _tileID, _hasBorder, _allowPoles, _usage);
12343	}
12344	setProjection(projection) {
12345		this._transitionable.setValue("type", projection?.type || "mercator");
12346	}
12347	updateTransitions(parameters) {
12348		this._transitioning = this._transitionable.transitioned(parameters, this._transitioning);
12349	}
12350	hasTransition() {
12351		return this._transitioning.hasTransition() || this.currentProjection.hasTransition();
12352	}
12353	recalculate(parameters) {
12354		this.properties = this._transitioning.possiblyEvaluate(parameters);
12355	}
12356};
12357//#endregion
12358//#region src/geo/projection/globe_utils.ts
12359function getGlobeCircumferencePixels(transform) {
12360	const radius = getGlobeRadiusPixels(transform.worldSize, transform.center.lat);
12361	return 2 * Math.PI * radius;
12362}
12363function globeDistanceOfLocationsPixels(transform, a, b) {
12364	const vecA = angularCoordinatesToSurfaceVector(a);
12365	const vecB = angularCoordinatesToSurfaceVector(b);
12366	const dot$2 = dot(vecA, vecB);
12367	const radians = Math.acos(dot$2);
12368	const circumference = getGlobeCircumferencePixels(transform);
12369	return radians / (2 * Math.PI) * circumference;
12370}
12371/**
12372* For given mercator coordinates in range 0..1, returns the angular coordinates on the sphere's surface, in radians.
12373*/
12374function mercatorCoordinatesToAngularCoordinatesRadians(mercatorX, mercatorY) {
12375	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];
12376}
12377/**
12378* For a given longitude and latitude (note: in radians) returns the normalized vector from the planet center to the specified place on the surface.
12379* @param lngRadians - Longitude in radians.
12380* @param latRadians - Latitude in radians.
12381*/
12382function angularCoordinatesRadiansToVector(lngRadians, latRadians) {
12383	const len = Math.cos(latRadians);
12384	const vec = /* @__PURE__ */ new Float64Array(3);
12385	vec[0] = Math.sin(lngRadians) * len;
12386	vec[1] = Math.sin(latRadians);
12387	vec[2] = Math.cos(lngRadians) * len;
12388	return vec;
12389}
12390/**
12391* Projects a point within a tile to the surface of the unit sphere globe.
12392* @param inTileX - X coordinate inside the tile in range [0 .. 8192].
12393* @param inTileY - Y coordinate inside the tile in range [0 .. 8192].
12394* @param tileIdX - Tile's X coordinate in range [0 .. 2^zoom - 1].
12395* @param tileIdY - Tile's Y coordinate in range [0 .. 2^zoom - 1].
12396* @param tileIdZ - Tile's zoom.
12397* @returns A 3D vector - coordinates of the projected point on a unit sphere.
12398*/
12399function projectTileCoordinatesToSphere(inTileX, inTileY, tileIdX, tileIdY, tileIdZ) {
12400	const scale = 1 / (1 << tileIdZ);
12401	const mercatorX = inTileX / EXTENT * scale + tileIdX * scale;
12402	const mercatorY = inTileY / EXTENT * scale + tileIdY * scale;
12403	const sphericalX = mod(mercatorX * Math.PI * 2 + Math.PI, Math.PI * 2);
12404	const sphericalY = 2 * Math.atan(Math.exp(Math.PI - mercatorY * Math.PI * 2)) - Math.PI * .5;
12405	const len = Math.cos(sphericalY);
12406	const vec = /* @__PURE__ */ new Float64Array(3);
12407	vec[0] = Math.sin(sphericalX) * len;
12408	vec[1] = Math.sin(sphericalY);
12409	vec[2] = Math.cos(sphericalX) * len;
12410	return vec;
12411}
12412/**
12413* For a given longitude and latitude (note: in degrees) returns the normalized vector from the planet center to the specified place on the surface.
12414*/
12415function angularCoordinatesToSurfaceVector(lngLat) {
12416	return angularCoordinatesRadiansToVector(lngLat.lng * Math.PI / 180, lngLat.lat * Math.PI / 180);
12417}
12418function getGlobeRadiusPixels(worldSize, latitudeDegrees) {
12419	return worldSize / (2 * Math.PI) / Math.cos(latitudeDegrees * Math.PI / 180);
12420}
12421/**
12422* Returns the globe center in view space, in pixels: the camera is at the origin and looks down the negative Z axis.
12423*/
12424function getGlobeCenterInViewSpace(transform) {
12425	const position = transformMat4(createVec4f64(), [
12426		0,
12427		0,
12428		0,
12429		1
12430	], transform.modelViewProjectionMatrix);
12431	transformMat4(position, position, transform.inverseProjectionMatrix);
12432	return [
12433		position[0] / position[3],
12434		position[1] / position[3],
12435		position[2] / position[3]
12436	];
12437}
12438/**
12439* Given a 3D point on the surface of a unit sphere, returns its angular coordinates in degrees.
12440* The input vector must be normalized.
12441*/
12442function sphereSurfacePointToCoordinates(surface) {
12443	const latDegrees = Math.asin(surface[1]) / Math.PI * 180;
12444	const lengthXZ = Math.sqrt(surface[0] * surface[0] + surface[2] * surface[2]);
12445	if (lengthXZ > 1e-6) {
12446		const projX = surface[0] / lengthXZ;
12447		const projZ = surface[2] / lengthXZ;
12448		const acosZ = Math.acos(projZ);
12449		const lngDegrees = (projX > 0 ? acosZ : -acosZ) / Math.PI * 180;
12450		return new LngLat(wrap(lngDegrees, -180, 180), latDegrees);
12451	} else return new LngLat(0, latDegrees);
12452}
12453/**
12454* Returns the globe orientation quaternion for the given map center and bearing.
12455* The inverse of {@link lngLatBearingFromOrientation}.
12456*/
12457function orientationFromLngLatBearing(lngLat, bearing) {
12458	return fromEuler(createVec4f64(), -lngLat.lng, -lngLat.lat, bearing);
12459}
12460/**
12461* Given a globe orientation quaternion, returns the corresponding map center and bearing.
12462* The inverse of {@link orientationFromLngLatBearing}.
12463*/
12464function lngLatBearingFromOrientation(q) {
12465	const x = q[0], y = q[1], z = q[2], w = q[3];
12466	return {
12467		lng: -Math.atan2(2 * (w * x + y * z), 1 - 2 * (x * x + y * y)) * 180 / Math.PI,
12468		lat: -Math.asin(clamp(2 * (w * y - z * x), -1, 1)) * 180 / Math.PI,
12469		bearing: Math.atan2(2 * (w * z + x * y), 1 - 2 * (y * y + z * z)) * 180 / Math.PI
12470	};
12471}
12472/** Kept just below a half turn so the center cannot land exactly on the far side of the globe. */
12473const PAN_MAX_ANGLE = Math.PI * .98;
12474/**
12475* Returns the point on the globe that a drag towards `point` should aim at.
12476*
12477* Exact tracking breaks down at the silhouette: the ray meets the globe at `asin(D sin a) - a`,
12478* whose slope runs away to infinity as the ray goes tangent, and past it there is no intersection
12479* at all. So the exact curve is left {@link PAN_FALLOFF_BAND} early and continued with a hyperbola
12480* matching it in value and slope, easing off and saturating short of the far side. Bell's virtual
12481* trackball takes the same idea of easing a sphere into a hyperbola before the rim, though it fits
12482* the curve differently: SGI's `trackball.c`, described in Henriksen, Sporring and Hornbæk,
12483* "Virtual Trackballs Revisited", IEEE TVCG 10(2):206-216, 2004.
12484*
12485* The angle comes from atan2, so a ray pointing away from the globe is a wide angle the falloff
12486* saturates rather than a case to reject.
12487* @param tr - The transform being dragged.
12488* @param point - The cursor position.
12489*/
12490function panSurfaceLocation(tr, point) {
12491	const origin = tr.cameraPosition;
12492	const distance = length(origin);
12493	if (distance <= 1) return tr.screenPointToLocation(point);
12494	const u = createVec3f64();
12495	normalize(u, origin);
12496	const direction = tr.getRayDirectionFromPixel(point);
12497	const c = -dot(direction, u);
12498	const lateral = createVec3f64();
12499	scaleAndAdd(lateral, direction, u, c);
12500	const s = length(lateral);
12501	if (s < 1e-9) return tr.screenPointToLocation(point);
12502	const angle = Math.atan2(s, c);
12503	const handoverAngle = Math.asin(1 / distance) * .9;
12504	if (angle < handoverAngle) return tr.screenPointToLocation(point);
12505	const sinHandover = distance * Math.sin(handoverAngle);
12506	const targetAtHandover = Math.asin(clamp(sinHandover, -1, 1)) - handoverAngle;
12507	const slopeAtHandover = distance * Math.cos(handoverAngle) / Math.sqrt(Math.max(1 - sinHandover * sinHandover, 1e-12)) - 1;
12508	const room = PAN_MAX_ANGLE - targetAtHandover;
12509	const excess = angle - handoverAngle;
12510	const target = targetAtHandover + room * (slopeAtHandover * excess) / (room + slopeAtHandover * excess);
12511	const e = createVec3f64();
12512	scale$1(e, lateral, 1 / s);
12513	const surface = createVec3f64();
12514	scale$1(surface, u, Math.cos(clamp(target, 0, PAN_MAX_ANGLE)));
12515	scaleAndAdd(surface, surface, e, Math.sin(clamp(target, 0, PAN_MAX_ANGLE)));
12516	normalize(surface, surface);
12517	return sphereSurfacePointToCoordinates(surface);
12518}
12519/**
12520* Rotates the globe so that the given location appears at the given screen point, by composing
12521* versors, the unit quaternions that represent rotations. Unlike the bearing-preserving
12522* {@link ITransform.setLocationAtPoint}, this stays smooth near and across the poles, and keeps
12523* panning once the cursor leaves the globe.
12524*
12525* Note: the delta rotation's axis is in the surface-vector frame of
12526* {@link angularCoordinatesToSurfaceVector}, while the orientation quaternion uses the Euler frame
12527* of {@link orientationFromLngLatBearing}, hence the component swizzle where they combine. Zoom is
12528* adjusted to keep the planet the same size, as `setLocationAtPoint` does.
12529* @param tr - The transform to rotate.
12530* @param lnglat - The location to bring under `point`.
12531* @param point - The screen point that `lnglat` should appear at.
12532* @param panDelta - The drag's pixel delta. Used to re-derive the previous cursor location through
12533* {@link panSurfaceLocation}, since both ends of the rotation must come from the same mapping.
12534* @param fixedBearing - Applies the swing only, keeping the bearing fixed, which is what dragging
12535* the globe does. Pass `false` to apply the twist about the view axis as well, so that the grabbed
12536* location tracks the cursor exactly and the bearing drifts with it.
12537*/
12538function versorSetLocationAtPoint(tr, lnglat, point, panDelta, fixedBearing = true) {
12539	const pointLngLat = panSurfaceLocation(tr, point);
12540	let sourceLngLat = lnglat;
12541	if (panDelta) sourceLngLat = panSurfaceLocation(tr, point.sub(panDelta));
12542	else if (!tr.isPointOnMapSurface(point)) return;
12543	const vecToPixelCurrent = angularCoordinatesToSurfaceVector(pointLngLat);
12544	const vecToTarget = angularCoordinatesToSurfaceVector(sourceLngLat);
12545	const centerQuat = orientationFromLngLatBearing(tr.center, tr.bearing);
12546	const w = cross(createVec3f64(), vecToTarget, vecToPixelCurrent);
12547	const l = Math.sqrt(dot(w, w));
12548	const t = Math.acos(clamp(dot(vecToTarget, vecToPixelCurrent), -1, 1)) / 2;
12549	const s = Math.sin(t);
12550	const delta = l ? fromValues(w[1] / l * s, -w[0] / l * s, w[2] / l * s, Math.cos(t)) : fromValues(0, 0, 0, 1);
12551	const { lng: newCenterLng, lat: newCenterLat, bearing: newBearing } = lngLatBearingFromOrientation(multiply$1(createVec4f64(), centerQuat, delta));
12552	const oldLat = tr.center.lat;
12553	const oldZoom = tr.zoom;
12554	const finalLat = clamp(newCenterLat, -90, 90);
12555	const finalLng = fixedBearing ? fixedBearingLongitude(tr, point, panDelta, newCenterLng) : newCenterLng;
12556	tr.setCenter(new LngLat(wrap(finalLng, -180, 180), finalLat));
12557	if (!fixedBearing) tr.setBearing(newBearing);
12558	tr.setZoom(oldZoom + getZoomAdjustment(oldLat, tr.center.lat));
12559}
12560/**
12561* Returns the center longitude for a bearing-preserving drag.
12562*
12563* The swing longitude becomes ill-conditioned near the pole and the grabbed location slips away
12564* from the cursor, so within the last ~12 degrees of latitude the cursor is treated as turning a
12565* dial around the pole, blended in with a smoothstep anchored on {@link MAX_VALID_LATITUDE}, the
12566* highest latitude the center can reach. The sweep comes from the raw pixel delta rather than a
12567* round-tripped previous cursor position, which would lose its sign to cancellation at the pole,
12568* and is damped within {@link DIAL_MIN_RADIUS_PIXELS} so it eases to nothing there instead of
12569* being dropped, which would leave a spot where the drag could not move at all.
12570* @param tr - The transform being dragged.
12571* @param point - The cursor position.
12572* @param panDelta - The drag's pixel delta, or undefined to use the swing longitude only.
12573* @param newCenterLng - The swing target longitude.
12574* @returns the center longitude to apply.
12575*/
12576function fixedBearingLongitude(tr, point, panDelta, newCenterLng) {
12577	const oldLng = tr.center.lng;
12578	const oldLat = tr.center.lat;
12579	const poleLat = oldLat >= 0 ? 90 : -90;
12580	const polePoint = tr.locationToScreenPoint(new LngLat(0, poleLat));
12581	const rx = point.x - polePoint.x, ry = point.y - polePoint.y;
12582	const r2 = rx * rx + ry * ry;
12583	const tRamp = clamp(1 - (MAX_VALID_LATITUDE - Math.abs(oldLat)) / 12, 0, 1);
12584	const dial = tRamp * tRamp * (3 - 2 * tRamp);
12585	const dLngSwing = mod(newCenterLng - oldLng + 180, 360) - 180;
12586	let dLngDial = 0;
12587	if (dial > 0 && panDelta) {
12588		const dTheta = (rx * panDelta.y - ry * panDelta.x) / Math.max(r2, 400);
12589		dLngDial = (poleLat > 0 ? 1 : -1) * dTheta * 180 / Math.PI;
12590	}
12591	return oldLng + (1 - dial) * dLngSwing + dial * dLngDial;
12592}
12593/**
12594* Given a normalized horizon plane in Ax+By+Cz+D=0 format, compute the center and radius of
12595* the circle in that plain that contains the entire visible portion of the unit sphere from horizon
12596* to horizon.
12597* @param horizonPlane - The plane that passes through visible horizon in Ax + By + Cz + D = 0 format where mag(A,B,C)=1
12598* @returns the center point and radius of the disc that passes through the entire visible horizon
12599*/
12600function horizonPlaneToCenterAndRadius(horizonPlane) {
12601	const center = createVec3f64();
12602	center[0] = horizonPlane[0] * -horizonPlane[3];
12603	center[1] = horizonPlane[1] * -horizonPlane[3];
12604	center[2] = horizonPlane[2] * -horizonPlane[3];
12605	return {
12606		center,
12607		radius: Math.sqrt(1 - horizonPlane[3] * horizonPlane[3])
12608	};
12609}
12610/**
12611* Computes the closest point on a sphere to `point`.
12612* @param center - Center of the sphere
12613* @param radius - Radius of the sphere
12614* @param point - Point inside or outside the sphere
12615* @returns A 3d vector of the point on the sphere closest to `point`
12616*/
12617function clampToSphere(center, radius, point) {
12618	const relativeToCenter = createVec3f64();
12619	sub(relativeToCenter, point, center);
12620	const clamped = createVec3f64();
12621	scaleAndAdd(clamped, center, relativeToCenter, radius / len(relativeToCenter));
12622	return clamped;
12623}
12624function planetScaleAtLatitude(latitudeDegrees) {
12625	return Math.cos(latitudeDegrees * Math.PI / 180);
12626}
12627/**
12628* Computes how much to modify zoom to keep the globe size constant when changing latitude.
12629* @param transform - An instance of any transform. Does not have any relation on the computed values.
12630* @param oldLat - Latitude before change, in degrees.
12631* @param newLat - Latitude after change, in degrees.
12632* @returns A value to add to zoom level used for old latitude to keep same planet radius at new latitude.
12633*/
12634function getZoomAdjustment(oldLat, newLat) {
12635	const oldCircumference = planetScaleAtLatitude(oldLat);
12636	const newCircumference = planetScaleAtLatitude(newLat);
12637	return scaleZoom(newCircumference / oldCircumference);
12638}
12639function getDegreesPerPixel(worldSize, lat) {
12640	return 360 / getGlobeCircumferencePixels({
12641		worldSize,
12642		center: { lat }
12643	});
12644}
12645/**
12646* Returns transform's new center rotation after applying panning.
12647* @param panDelta - Panning delta, in same units as what is supplied to {@link HandlerManager}.
12648* @param tr - Current transform. This object is not modified by the function.
12649* @returns New center location to set to the map's transform to apply the specified panning.
12650*/
12651function computeGlobePanCenter(panDelta, tr) {
12652	const rotatedPanDelta = panDelta.rotate(tr.bearingInRadians);
12653	const normalizedGlobeZoom = tr.zoom + getZoomAdjustment(tr.center.lat, 0);
12654	const lngSpeed = lerp(1 / planetScaleAtLatitude(tr.center.lat), 1 / planetScaleAtLatitude(Math.min(Math.abs(tr.center.lat), 60)), remapSaturate(normalizedGlobeZoom, 7, 3, 0, 1));
12655	const panningDegreesPerPixel = getDegreesPerPixel(tr.worldSize, tr.center.lat);
12656	return new LngLat(tr.center.lng - rotatedPanDelta.x * panningDegreesPerPixel * lngSpeed, clamp(tr.center.lat + rotatedPanDelta.y * panningDegreesPerPixel, -MAX_VALID_LATITUDE, MAX_VALID_LATITUDE));
12657}
12658/**
12659* Integration of `1 / cos(x)`.
12660*/
12661function integrateSecX(x) {
12662	const xHalf = .5 * x;
12663	const sin = Math.sin(xHalf);
12664	const cos = Math.cos(xHalf);
12665	return Math.log(sin + cos) - Math.log(cos - sin);
12666}
12667/**
12668* Interpolates globe center between two locations while preserving apparent rotation speed during interpolation.
12669* @param start - The starting location of the interpolation.
12670* @param deltaLng - Longitude delta to the end of the interpolation.
12671* @param deltaLat - Latitude delta to the end of the interpolation.
12672* @param t - The interpolation point in [0..1], where 0 is starting location, 1 is end location and other values are in between.
12673* @returns The interpolated location.
12674*/
12675function interpolateLngLatForGlobe(start, deltaLng, deltaLat, t) {
12676	const interpolatedLat = start.lat + deltaLat * t;
12677	if (Math.abs(deltaLat) > 1) {
12678		const endLat = start.lat + deltaLat;
12679		const samplePointStart = (Math.sign(endLat) !== Math.sign(start.lat) ? -Math.abs(start.lat) : Math.abs(start.lat)) * Math.PI / 180;
12680		const samplePointEnd = Math.abs(start.lat + deltaLat) * Math.PI / 180;
12681		const valueT = integrateSecX(samplePointStart + t * (samplePointEnd - samplePointStart));
12682		const valueStart = integrateSecX(samplePointStart);
12683		const valueEnd = integrateSecX(samplePointEnd);
12684		const newT = (valueT - valueStart) / (valueEnd - valueStart);
12685		const interpolatedLng = start.lng + deltaLng * newT;
12686		return new LngLat(interpolatedLng, interpolatedLat);
12687	} else {
12688		const interpolatedLng = start.lng + deltaLng * t;
12689		return new LngLat(interpolatedLng, interpolatedLat);
12690	}
12691}
12692/**
12693* Returns the two intersection points of the ray and a sphere centered at the origin,
12694* or null if no intersection occurs.
12695* The intersections are encoded as the parameter for parametric ray equation,
12696* with `tMin` being the first intersection and `tMax` being the second.
12697* The quadratic is solved as suggested in Ray Tracing Gems, chapter 7, since the
12698* schoolbook approach leads to floating point precision issues:
12699* https://www.realtimerendering.com/raytracinggems/rtg/index.html
12700* @param origin - The ray origin.
12701* @param direction - The normalized ray direction.
12702* @param radius - The sphere radius, defaults to the unit sphere of the planet.
12703*/
12704function raySphereIntersection(origin, direction, radius = 1) {
12705	const originDotDirection = dot(origin, direction);
12706	const radiusSquared = radius * radius;
12707	const inner = createVec3f64();
12708	const scaledDir = createVec3f64();
12709	scale$1(scaledDir, direction, originDotDirection);
12710	sub(inner, origin, scaledDir);
12711	const discriminant = radiusSquared - dot(inner, inner);
12712	if (discriminant < 0) return null;
12713	const c = dot(origin, origin) - radiusSquared;
12714	const q = -originDotDirection + (originDotDirection < 0 ? 1 : -1) * Math.sqrt(discriminant);
12715	const t0 = c / q;
12716	const t1 = q;
12717	return {
12718		tMin: Math.min(t0, t1),
12719		tMax: Math.max(t0, t1)
12720	};
12721}
12722/** Height of the atmosphere relative to the globe radius, 100 km for Earth. */
12723const ATMOSPHERE_HEIGHT_TO_GLOBE_RADIUS = 1e5 / 6371e3;
12724/**
12725* Returns how much of the atmosphere to draw for a camera at the given altitude above the globe.
12726* It is 0 inside the atmosphere and reaches 1 at ten times the atmosphere height. The sky shader
12727* fades out along the same curve.
12728*/
12729function getAtmosphereAltitudeBlend(altitude, globeRadius) {
12730	const atmosphereHeight = globeRadius * ATMOSPHERE_HEIGHT_TO_GLOBE_RADIUS;
12731	const x = clamp((altitude - atmosphereHeight) / (9 * atmosphereHeight), 0, 1);
12732	return x * x * (3 - 2 * x);
12733}
12734//#endregion
12735//#region src/util/primitives/bounding_volume_cache.ts
12736var BoundingVolumeCache = class {
12737	constructor(boundingVolumeFactory) {
12738		this._cachePrevious = /* @__PURE__ */ new Map();
12739		this._cache = /* @__PURE__ */ new Map();
12740		this._hadAnyChanges = false;
12741		this._boundingVolumeFactory = boundingVolumeFactory;
12742	}
12743	/**
12744	* Prepares bounding volume cache for next frame. Call at the beginning of a frame.
12745	* Bounding volume of any tile accesses in the last frame is kept in the cache, other (unaccessed) bounding volumes are deleted.
12746	*/
12747	swapBuffers() {
12748		if (!this._hadAnyChanges) return;
12749		const oldCache = this._cachePrevious;
12750		this._cachePrevious = this._cache;
12751		this._cache = oldCache;
12752		this._cache.clear();
12753		this._hadAnyChanges = false;
12754	}
12755	/**
12756	* Returns the bounding volume of the specified tile, fetching it from cache or creating it using the factory function if needed.
12757	* @param tileID - Tile x, y and z for zoom.
12758	*/
12759	getTileBoundingVolume(tileID, wrap, elevation, options) {
12760		const key = `${tileID.z}_${tileID.x}_${tileID.y}_${options?.terrain ? "t" : ""}_${Math.round(elevation)}`;
12761		const cached = this._cache.get(key);
12762		if (cached) return cached;
12763		const cachedPrevious = this._cachePrevious.get(key);
12764		if (cachedPrevious) {
12765			this._cache.set(key, cachedPrevious);
12766			return cachedPrevious;
12767		}
12768		const boundingVolume = this._boundingVolumeFactory(tileID, wrap, elevation, options);
12769		this._cache.set(key, boundingVolume);
12770		this._hadAnyChanges = true;
12771		return boundingVolume;
12772	}
12773};
12774//#endregion
12775//#region src/util/primitives/convex_volume.ts
12776/**
12777* A general convex bounding volume, defined by a set of points.
12778*/
12779var ConvexVolume = class ConvexVolume {
12780	/**
12781	* Creates an instance of a general convex bounding volume.
12782	* Note that the provided points array is used *as is*, its contents are not copied!
12783	*
12784	* Additionally, an AABB must be provided for rejecting frustum intersections.
12785	* This AABB does not need to bound this convex volume (it may be smaller),
12786	* but it *must* accurately bound the actual shape this volume is approximating.
12787	* @param points - Points forming the convex shape. Note that this array reference is used *as is*, its contents are not copied!
12788	* @param min - The bounding AABB's min point.
12789	* @param max - The bounding AABB's min point.
12790	*/
12791	constructor(points, planes, min, max) {
12792		this.min = min;
12793		this.max = max;
12794		this.points = points;
12795		this.planes = planes;
12796	}
12797	/**
12798	* Creates a convex BV equivalent to the specified AABB.
12799	* @param min - The AABB's min point.
12800	* @param max - The AABB's max point.
12801	*/
12802	static fromAabb(min, max) {
12803		const points = [];
12804		for (let i = 0; i < 8; i++) points.push([
12805			(i >> 0 & 1) === 1 ? max[0] : min[0],
12806			(i >> 1 & 1) === 1 ? max[1] : min[1],
12807			(i >> 2 & 1) === 1 ? max[2] : min[2]
12808		]);
12809		return new ConvexVolume(points, [
12810			[
12811				-1,
12812				0,
12813				0,
12814				max[0]
12815			],
12816			[
12817				1,
12818				0,
12819				0,
12820				-min[0]
12821			],
12822			[
12823				0,
12824				-1,
12825				0,
12826				max[1]
12827			],
12828			[
12829				0,
12830				1,
12831				0,
12832				-min[1]
12833			],
12834			[
12835				0,
12836				0,
12837				-1,
12838				max[2]
12839			],
12840			[
12841				0,
12842				0,
12843				1,
12844				-min[2]
12845			]
12846		], min, max);
12847	}
12848	/**
12849	* Creates a convex bounding volume that is actually an oriented bounding box created from the specified center, half-size and rotation angles.
12850	* @param center - Center of the OBB.
12851	* @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.
12852	* @param angles - The rotation of the box. Euler angles, in degrees.
12853	*/
12854	static fromCenterSizeAngles(center, halfSize, angles) {
12855		const q = fromEuler([], angles[0], angles[1], angles[2]);
12856		const axisX = transformQuat([], [
12857			halfSize[0],
12858			0,
12859			0
12860		], q);
12861		const axisY = transformQuat([], [
12862			0,
12863			halfSize[1],
12864			0
12865		], q);
12866		const axisZ = transformQuat([], [
12867			0,
12868			0,
12869			halfSize[2]
12870		], q);
12871		const min = [...center];
12872		const max = [...center];
12873		for (let i = 0; i < 8; i++) for (let axis = 0; axis < 3; axis++) {
12874			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);
12875			min[axis] = Math.min(min[axis], point);
12876			max[axis] = Math.max(max[axis], point);
12877		}
12878		const points = [];
12879		for (let i = 0; i < 8; i++) {
12880			const p = [...center];
12881			add(p, p, scale$1([], axisX, (i >> 0 & 1) === 1 ? 1 : -1));
12882			add(p, p, scale$1([], axisY, (i >> 1 & 1) === 1 ? 1 : -1));
12883			add(p, p, scale$1([], axisZ, (i >> 2 & 1) === 1 ? 1 : -1));
12884			points.push(p);
12885		}
12886		return new ConvexVolume(points, [
12887			[...axisX, -dot(axisX, points[0])],
12888			[...axisY, -dot(axisY, points[0])],
12889			[...axisZ, -dot(axisZ, points[0])],
12890			[
12891				-axisX[0],
12892				-axisX[1],
12893				-axisX[2],
12894				-dot(axisX, points[7])
12895			],
12896			[
12897				-axisY[0],
12898				-axisY[1],
12899				-axisY[2],
12900				-dot(axisY, points[7])
12901			],
12902			[
12903				-axisZ[0],
12904				-axisZ[1],
12905				-axisZ[2],
12906				-dot(axisZ, points[7])
12907			]
12908		], min, max);
12909	}
12910	/**
12911	* Performs an approximate frustum-obb intersection test.
12912	*/
12913	intersectsFrustum(frustum) {
12914		let fullyInside = true;
12915		const boxPointCount = this.points.length;
12916		const boxPlaneCount = this.planes.length;
12917		const frustumPlaneCount = frustum.planes.length;
12918		const frustumPointCount = frustum.points.length;
12919		for (let i = 0; i < frustumPlaneCount; i++) {
12920			const plane = frustum.planes[i];
12921			let boxPointsPassed = 0;
12922			for (let j = 0; j < boxPointCount; j++) {
12923				const point = this.points[j];
12924				if (plane[0] * point[0] + plane[1] * point[1] + plane[2] * point[2] + plane[3] >= 0) boxPointsPassed++;
12925			}
12926			if (boxPointsPassed === 0) return 0;
12927			if (boxPointsPassed < boxPointCount) fullyInside = false;
12928		}
12929		if (fullyInside) return 2;
12930		for (let i = 0; i < boxPlaneCount; i++) {
12931			const plane = this.planes[i];
12932			let frustumPointsPassed = 0;
12933			for (let j = 0; j < frustumPointCount; j++) {
12934				const point = frustum.points[j];
12935				if (plane[0] * point[0] + plane[1] * point[1] + plane[2] * point[2] + plane[3] >= 0) frustumPointsPassed++;
12936			}
12937			if (frustumPointsPassed === 0) return 0;
12938		}
12939		return 1;
12940	}
12941	/**
12942	* Performs an intersection test with a halfspace.
12943	*/
12944	intersectsPlane(plane) {
12945		const pointCount = this.points.length;
12946		let positivePoints = 0;
12947		for (let i = 0; i < pointCount; i++) {
12948			const point = this.points[i];
12949			if (plane[0] * point[0] + plane[1] * point[1] + plane[2] * point[2] + plane[3] >= 0) positivePoints++;
12950		}
12951		if (positivePoints === pointCount) return 2;
12952		if (positivePoints === 0) return 0;
12953		return 1;
12954	}
12955};
12956//#endregion
12957//#region src/geo/projection/globe_covering_tiles_details_provider.ts
12958/**
12959* Computes distance of a point to a tile in an arbitrary axis.
12960* World is assumed to have size 1, distance returned is to the nearer tile edge.
12961* @param point - Point position.
12962* @param tile - Tile position.
12963* @param tileSize - Tile size.
12964*/
12965function distanceToTileSimple(point, tile, tileSize) {
12966	const delta = point - tile;
12967	return delta < 0 ? -delta : Math.max(0, delta - tileSize);
12968}
12969function distanceToTileWrapX(pointX, pointY, tileCornerX, tileCornerY, tileSize) {
12970	const tileCornerToPointX = pointX - tileCornerX;
12971	let distanceX;
12972	if (tileCornerToPointX < 0) distanceX = Math.min(-tileCornerToPointX, 1 + tileCornerToPointX - tileSize);
12973	else if (tileCornerToPointX > tileSize) distanceX = Math.min(Math.max(tileCornerToPointX - tileSize, 0), 1 - tileCornerToPointX);
12974	else distanceX = 0;
12975	return Math.max(distanceX, distanceToTileSimple(pointY, tileCornerY, tileSize));
12976}
12977var GlobeCoveringTilesDetailsProvider = class {
12978	constructor() {
12979		this._boundingVolumeCache = new BoundingVolumeCache(this._computeTileBoundingVolume);
12980	}
12981	/**
12982	* Prepares the internal bounding volume cache for the next frame.
12983	*/
12984	prepareNextFrame() {
12985		this._boundingVolumeCache.swapBuffers();
12986	}
12987	/**
12988	* Returns the distance of a point to a square tile. If the point is inside the tile, returns 0.
12989	* Assumes the world to be of size 1.
12990	* Handles distances on a sphere correctly: X is wrapped when crossing the antimeridian,
12991	* when crossing the poles Y is mirrored and X is shifted by half world size.
12992	*/
12993	distanceToTile2d(pointX, pointY, tileID, _bv) {
12994		const scale = 1 << tileID.z;
12995		const tileMercatorSize = 1 / scale;
12996		const tileCornerX = tileID.x / scale;
12997		const tileCornerY = tileID.y / scale;
12998		const halfWorld = .5;
12999		let smallestDistance = 2;
13000		smallestDistance = Math.min(smallestDistance, distanceToTileWrapX(pointX, pointY, tileCornerX, tileCornerY, tileMercatorSize));
13001		smallestDistance = Math.min(smallestDistance, distanceToTileWrapX(pointX, pointY, tileCornerX + halfWorld, -tileCornerY - tileMercatorSize, tileMercatorSize));
13002		smallestDistance = Math.min(smallestDistance, distanceToTileWrapX(pointX, pointY, tileCornerX + halfWorld, 2 - tileCornerY - tileMercatorSize, tileMercatorSize));
13003		return smallestDistance;
13004	}
13005	/**
13006	* Returns the wrap value for a given tile, computed so that tiles will remain loaded when crossing the antimeridian.
13007	*/
13008	getWrap(centerCoord, tileID, _parentWrap) {
13009		const scale = 1 << tileID.z;
13010		const tileMercatorSize = 1 / scale;
13011		const tileX = tileID.x / scale;
13012		const distanceCurrent = distanceToTileSimple(centerCoord.x, tileX, tileMercatorSize);
13013		const distanceLeft = distanceToTileSimple(centerCoord.x, tileX - 1, tileMercatorSize);
13014		const distanceRight = distanceToTileSimple(centerCoord.x, tileX + 1, tileMercatorSize);
13015		const distanceSmallest = Math.min(distanceCurrent, distanceLeft, distanceRight);
13016		if (distanceSmallest === distanceRight) return 1;
13017		if (distanceSmallest === distanceLeft) return -1;
13018		return 0;
13019	}
13020	allowVariableZoom(transform, options) {
13021		return coveringZoomLevel(transform, options) > 4;
13022	}
13023	allowWorldCopies() {
13024		return false;
13025	}
13026	getTileBoundingVolume(tileID, wrap, elevation, options) {
13027		return this._boundingVolumeCache.getTileBoundingVolume(tileID, wrap, elevation, options);
13028	}
13029	/**
13030	* Computes the bounding volume of a tile for culling. The volume reaches up to `elevation`,
13031	* which carries the content elevation allowance (e.g. elevated symbols); terrain elevations
13032	* widen the volume but never shrink that allowance.
13033	*/
13034	_computeTileBoundingVolume(tileID, wrap, elevation, options) {
13035		let minElevation = Math.min(0, elevation);
13036		let maxElevation = Math.max(0, elevation);
13037		if (options?.terrain) {
13038			const overscaledTileID = new OverscaledTileID(tileID.z, wrap, tileID.z, tileID.x, tileID.y);
13039			const minMax = options.terrain.getMinMaxElevation(overscaledTileID);
13040			minElevation = minMax.minElevation ?? minElevation;
13041			maxElevation = Math.max(minMax.maxElevation ?? maxElevation, maxElevation);
13042		}
13043		minElevation /= earthRadius;
13044		maxElevation /= earthRadius;
13045		minElevation += 1;
13046		maxElevation += 1;
13047		if (tileID.z <= 0) return ConvexVolume.fromAabb([
13048			-maxElevation,
13049			-maxElevation,
13050			-maxElevation
13051		], [
13052			maxElevation,
13053			maxElevation,
13054			maxElevation
13055		]);
13056		else if (tileID.z === 1) return ConvexVolume.fromAabb([
13057			tileID.x === 0 ? -maxElevation : 0,
13058			tileID.y === 0 ? 0 : -maxElevation,
13059			-maxElevation
13060		], [
13061			tileID.x === 0 ? 0 : maxElevation,
13062			tileID.y === 0 ? maxElevation : 0,
13063			maxElevation
13064		]);
13065		else {
13066			const corners = [
13067				projectTileCoordinatesToSphere(0, 0, tileID.x, tileID.y, tileID.z),
13068				projectTileCoordinatesToSphere(EXTENT, 0, tileID.x, tileID.y, tileID.z),
13069				projectTileCoordinatesToSphere(EXTENT, EXTENT, tileID.x, tileID.y, tileID.z),
13070				projectTileCoordinatesToSphere(0, EXTENT, tileID.x, tileID.y, tileID.z)
13071			];
13072			const extremesPoints = [];
13073			for (const c of corners) extremesPoints.push(scale$1([], c, maxElevation));
13074			if (maxElevation !== minElevation) for (const c of corners) extremesPoints.push(scale$1([], c, minElevation));
13075			if (tileID.y === 0) extremesPoints.push([
13076				0,
13077				1,
13078				0
13079			]);
13080			if (tileID.y === (1 << tileID.z) - 1) extremesPoints.push([
13081				0,
13082				-1,
13083				0
13084			]);
13085			const aabbMin = [
13086				1,
13087				1,
13088				1
13089			];
13090			const aabbMax = [
13091				-1,
13092				-1,
13093				-1
13094			];
13095			for (const c of extremesPoints) for (let i = 0; i < 3; i++) {
13096				aabbMin[i] = Math.min(aabbMin[i], c[i]);
13097				aabbMax[i] = Math.max(aabbMax[i], c[i]);
13098			}
13099			const center = projectTileCoordinatesToSphere(EXTENT / 2, EXTENT / 2, tileID.x, tileID.y, tileID.z);
13100			const centerEast = cross([], [
13101				0,
13102				1,
13103				0
13104			], center);
13105			normalize(centerEast, centerEast);
13106			const north = cross([], center, centerEast);
13107			normalize(north, north);
13108			const axisEast = cross([], corners[2], corners[1]);
13109			normalize(axisEast, axisEast);
13110			const axisWest = cross([], corners[0], corners[3]);
13111			normalize(axisWest, axisWest);
13112			extremesPoints.push(scale$1([], center, maxElevation));
13113			if (tileID.y >= (1 << tileID.z) / 2) extremesPoints.push(scale$1([], projectTileCoordinatesToSphere(EXTENT / 2, 0, tileID.x, tileID.y, tileID.z), maxElevation));
13114			if (tileID.y < (1 << tileID.z) / 2) extremesPoints.push(scale$1([], projectTileCoordinatesToSphere(EXTENT / 2, EXTENT, tileID.x, tileID.y, tileID.z), maxElevation));
13115			const upDownMinMax = findAxisMinMax(center, extremesPoints);
13116			const northSouthMinMax = findAxisMinMax(north, extremesPoints);
13117			const planeUp = [
13118				-center[0],
13119				-center[1],
13120				-center[2],
13121				upDownMinMax.max
13122			];
13123			const planeDown = [
13124				center[0],
13125				center[1],
13126				center[2],
13127				-upDownMinMax.min
13128			];
13129			const planeNorth = [
13130				-north[0],
13131				-north[1],
13132				-north[2],
13133				northSouthMinMax.max
13134			];
13135			const planeSouth = [
13136				north[0],
13137				north[1],
13138				north[2],
13139				-northSouthMinMax.min
13140			];
13141			const planeEast = [...axisEast, 0];
13142			const planeWest = [...axisWest, 0];
13143			const points = [];
13144			if (tileID.y === 0) points.push(threePlaneIntersection(planeWest, planeEast, planeUp), threePlaneIntersection(planeWest, planeEast, planeDown));
13145			else points.push(threePlaneIntersection(planeNorth, planeEast, planeUp), threePlaneIntersection(planeNorth, planeEast, planeDown), threePlaneIntersection(planeNorth, planeWest, planeUp), threePlaneIntersection(planeNorth, planeWest, planeDown));
13146			if (tileID.y === (1 << tileID.z) - 1) points.push(threePlaneIntersection(planeWest, planeEast, planeUp), threePlaneIntersection(planeWest, planeEast, planeDown));
13147			else points.push(threePlaneIntersection(planeSouth, planeEast, planeUp), threePlaneIntersection(planeSouth, planeEast, planeDown), threePlaneIntersection(planeSouth, planeWest, planeUp), threePlaneIntersection(planeSouth, planeWest, planeDown));
13148			return new ConvexVolume(points, [
13149				planeUp,
13150				planeDown,
13151				planeNorth,
13152				planeSouth,
13153				planeEast,
13154				planeWest
13155			], aabbMin, aabbMax);
13156		}
13157	}
13158};
13159function findAxisMinMax(axis, points) {
13160	let min = Infinity;
13161	let max = -Infinity;
13162	for (const c of points) {
13163		const dot$1 = dot(axis, c);
13164		min = Math.min(min, dot$1);
13165		max = Math.max(max, dot$1);
13166	}
13167	return {
13168		min,
13169		max
13170	};
13171}
13172//#endregion
13173//#region src/geo/projection/vertical_perspective_transform.ts
13174const GLOBE_SAMPLES = 256;
13175const GLOBE_BISECT_EPSILON_T = 1e-12;
13176/** Latitudes outside the mercator range project past the world edge; the globe mesh still covers them. */
13177const MAX_MERCATOR_Y = 1 - 1e-9;
13178/** Two points on the unit globe closer than this, a micrometre, are the same point. */
13179const SAME_POINT_DISTANCE = 1e-12;
13180/** A camera whose horizontal offset is this small relative to its distance is straight above the center. */
13181const STRAIGHT_ABOVE_RATIO = 1e-9;
13182var VerticalPerspectiveTransform = class VerticalPerspectiveTransform {
13183	get pixelsToClipSpaceMatrix() {
13184		return this._helper.pixelsToClipSpaceMatrix;
13185	}
13186	get clipSpaceToPixelsMatrix() {
13187		return this._helper.clipSpaceToPixelsMatrix;
13188	}
13189	get pixelsToGLUnits() {
13190		return this._helper.pixelsToGLUnits;
13191	}
13192	get centerOffset() {
13193		return this._helper.centerOffset;
13194	}
13195	get size() {
13196		return this._helper.size;
13197	}
13198	get rotationMatrix() {
13199		return this._helper.rotationMatrix;
13200	}
13201	get centerPoint() {
13202		return this._helper.centerPoint;
13203	}
13204	get pixelsPerMeter() {
13205		return this._helper.pixelsPerMeter;
13206	}
13207	setMinZoom(zoom) {
13208		this._helper.setMinZoom(zoom);
13209	}
13210	setMaxZoom(zoom) {
13211		this._helper.setMaxZoom(zoom);
13212	}
13213	setMinPitch(pitch) {
13214		this._helper.setMinPitch(pitch);
13215	}
13216	setMaxPitch(pitch) {
13217		this._helper.setMaxPitch(pitch);
13218	}
13219	setRenderWorldCopies(renderWorldCopies) {
13220		this._helper.setRenderWorldCopies(renderWorldCopies);
13221	}
13222	setBearing(bearing) {
13223		this._helper.setBearing(bearing);
13224	}
13225	setPitch(pitch) {
13226		this._helper.setPitch(pitch);
13227	}
13228	setRoll(roll) {
13229		this._helper.setRoll(roll);
13230	}
13231	setFov(fov) {
13232		this._helper.setFov(fov);
13233	}
13234	setZoom(zoom) {
13235		this._helper.setZoom(zoom);
13236	}
13237	setCenter(center) {
13238		this._helper.setCenter(center);
13239	}
13240	setElevation(elevation) {
13241		this._helper.setElevation(elevation);
13242	}
13243	setMinElevationForCurrentTile(elevation) {
13244		this._helper.setMinElevationForCurrentTile(elevation);
13245	}
13246	setPadding(padding) {
13247		this._helper.setPadding(padding);
13248	}
13249	interpolatePadding(start, target, t) {
13250		this._helper.interpolatePadding(start, target, t);
13251	}
13252	isPaddingEqual(padding) {
13253		return this._helper.isPaddingEqual(padding);
13254	}
13255	resize(width, height) {
13256		this._helper.resize(width, height);
13257	}
13258	getMaxBounds() {
13259		return this._helper.getMaxBounds();
13260	}
13261	setMaxBounds(bounds) {
13262		this._helper.setMaxBounds(bounds);
13263	}
13264	setConstrainOverride(constrain) {
13265		this._helper.setConstrainOverride(constrain);
13266	}
13267	overrideNearFarZ(nearZ, farZ) {
13268		this._helper.overrideNearFarZ(nearZ, farZ);
13269	}
13270	clearNearFarZOverride() {
13271		this._helper.clearNearFarZOverride();
13272	}
13273	getCameraQueryGeometry(queryGeometry) {
13274		return this._helper.getCameraQueryGeometry(this.getCameraPoint(), queryGeometry);
13275	}
13276	get tileSize() {
13277		return this._helper.tileSize;
13278	}
13279	get tileZoom() {
13280		return this._helper.tileZoom;
13281	}
13282	get scale() {
13283		return this._helper.scale;
13284	}
13285	get worldSize() {
13286		return this._helper.worldSize;
13287	}
13288	get width() {
13289		return this._helper.width;
13290	}
13291	get height() {
13292		return this._helper.height;
13293	}
13294	get lngRange() {
13295		return this._helper.lngRange;
13296	}
13297	get latRange() {
13298		return this._helper.latRange;
13299	}
13300	get minZoom() {
13301		return this._helper.minZoom;
13302	}
13303	get maxZoom() {
13304		return this._helper.maxZoom;
13305	}
13306	get zoom() {
13307		return this._helper.zoom;
13308	}
13309	get center() {
13310		return this._helper.center;
13311	}
13312	get minPitch() {
13313		return this._helper.minPitch;
13314	}
13315	get maxPitch() {
13316		return this._helper.maxPitch;
13317	}
13318	get pitch() {
13319		return this._helper.pitch;
13320	}
13321	get pitchInRadians() {
13322		return this._helper.pitchInRadians;
13323	}
13324	get roll() {
13325		return this._helper.roll;
13326	}
13327	get rollInRadians() {
13328		return this._helper.rollInRadians;
13329	}
13330	get bearing() {
13331		return this._helper.bearing;
13332	}
13333	get bearingInRadians() {
13334		return this._helper.bearingInRadians;
13335	}
13336	get fov() {
13337		return this._helper.fov;
13338	}
13339	get fovInRadians() {
13340		return this._helper.fovInRadians;
13341	}
13342	get elevation() {
13343		return this._helper.elevation;
13344	}
13345	get minElevationForCurrentTile() {
13346		return this._helper.minElevationForCurrentTile;
13347	}
13348	get padding() {
13349		return this._helper.padding;
13350	}
13351	get unmodified() {
13352		return this._helper.unmodified;
13353	}
13354	get renderWorldCopies() {
13355		return this._helper.renderWorldCopies;
13356	}
13357	get constrainOverride() {
13358		return this._helper.constrainOverride;
13359	}
13360	get nearZ() {
13361		return this._helper.nearZ;
13362	}
13363	get farZ() {
13364		return this._helper.farZ;
13365	}
13366	get autoCalculateNearFarZ() {
13367		return this._helper.autoCalculateNearFarZ;
13368	}
13369	setTransitionState(_value) {}
13370	/**
13371	* @param options - Initial state. Ignored when `sharedHelper` is given, which already carries it.
13372	* @param sharedHelper - Camera to use instead of owning one, so that a composing transform such as
13373	* {@link GlobeTransform} keeps a single copy of the state rather than one per child. Its owner then drives
13374	* {@link _calcMatrices}, because a helper has only one `calcMatrices` callback.
13375	*/
13376	constructor(options, sharedHelper) {
13377		this._cachedClippingPlane = createVec4f64();
13378		this._projectionMatrix = createIdentityMat4f64();
13379		this._globeViewProjMatrix32f = createIdentityMat4f32();
13380		this._globeViewProjMatrixF64 = createIdentityMat4f64();
13381		this._globeViewProjMatrixF64Inverted = createIdentityMat4f64();
13382		this._globeProjMatrixInverted = createIdentityMat4f64();
13383		this._cameraPosition = createVec3f64();
13384		this.defaultConstrain = (lngLat, zoom) => {
13385			const constrainedLat = clamp(lngLat.lat, -MAX_VALID_LATITUDE, MAX_VALID_LATITUDE);
13386			const constrainedZoom = clamp(+zoom, this.minZoom + getZoomAdjustment(0, constrainedLat), this.maxZoom);
13387			return {
13388				center: new LngLat(lngLat.lng, constrainedLat),
13389				zoom: constrainedZoom
13390			};
13391		};
13392		this.applyConstrain = (lngLat, zoom) => {
13393			return this._helper.applyConstrain(lngLat, zoom);
13394		};
13395		this._helper = sharedHelper ?? new TransformHelper({
13396			calcMatrices: () => this._calcMatrices(),
13397			defaultConstrain: (center, zoom) => {
13398				return this.defaultConstrain(center, zoom);
13399			}
13400		}, options);
13401		this._coveringTilesDetailsProvider = new GlobeCoveringTilesDetailsProvider();
13402	}
13403	clone() {
13404		const clone = new VerticalPerspectiveTransform();
13405		clone.apply(this, false);
13406		return clone;
13407	}
13408	apply(that, constrain) {
13409		this._helper.apply(that, constrain);
13410	}
13411	get projectionMatrix() {
13412		return this._projectionMatrix;
13413	}
13414	get modelViewProjectionMatrix() {
13415		return this._globeViewProjMatrixF64;
13416	}
13417	get inverseProjectionMatrix() {
13418		return this._globeProjMatrixInverted;
13419	}
13420	get cameraPosition() {
13421		const copy = createVec3f64();
13422		copy[0] = this._cameraPosition[0];
13423		copy[1] = this._cameraPosition[1];
13424		copy[2] = this._cameraPosition[2];
13425		return copy;
13426	}
13427	get cameraToCenterDistance() {
13428		return this._helper.cameraToCenterDistance;
13429	}
13430	getProjectionData(params) {
13431		const { overscaledTileID, applyGlobeMatrix } = params;
13432		const mercatorTileCoordinates = this._helper.getMercatorTileCoordinates(overscaledTileID);
13433		return {
13434			mainMatrix: this._globeViewProjMatrix32f,
13435			tileMercatorCoords: mercatorTileCoordinates,
13436			clippingPlane: this._cachedClippingPlane,
13437			projectionTransition: applyGlobeMatrix ? 1 : 0,
13438			fallbackMatrix: this._globeViewProjMatrix32f,
13439			clipAntimeridian: overscaledTileID?.canonical.z === 0
13440		};
13441	}
13442	_computeClippingPlane(globeRadiusPixels) {
13443		const pitch = this.pitchInRadians;
13444		const distanceCameraToB = this.cameraToCenterDistance / globeRadiusPixels;
13445		const radius = 1;
13446		const distanceCameraToA = Math.sin(pitch) * distanceCameraToB;
13447		const distanceAtoC = Math.cos(pitch) * distanceCameraToB + radius;
13448		const tangentPlaneDistanceToC = radius / Math.sqrt(distanceCameraToA * distanceCameraToA + distanceAtoC * distanceAtoC) * radius;
13449		let vectorCtoCamX = -distanceCameraToA;
13450		let vectorCtoCamY = distanceAtoC;
13451		const vectorCtoCamLength = Math.sqrt(vectorCtoCamX * vectorCtoCamX + vectorCtoCamY * vectorCtoCamY);
13452		vectorCtoCamX /= vectorCtoCamLength;
13453		vectorCtoCamY /= vectorCtoCamLength;
13454		const planeVector = [
13455			0,
13456			vectorCtoCamX,
13457			vectorCtoCamY
13458		];
13459		rotateZ$1(planeVector, planeVector, [
13460			0,
13461			0,
13462			0
13463		], -this.bearingInRadians);
13464		rotateX$1(planeVector, planeVector, [
13465			0,
13466			0,
13467			0
13468		], -1 * this.center.lat * Math.PI / 180);
13469		rotateY(planeVector, planeVector, [
13470			0,
13471			0,
13472			0
13473		], this.center.lng * Math.PI / 180);
13474		const scale = 1 / length(planeVector);
13475		scale$1(planeVector, planeVector, scale);
13476		return [...planeVector, -tangentPlaneDistanceToC * scale];
13477	}
13478	/** {@inheritDoc ITransform.isLocationOccluded} */
13479	isLocationOccluded(lngLat, terrain, elevation) {
13480		const coverage = terrain?.getCoverageIndex();
13481		elevation ??= coverage ? terrain.getElevationForLngLat(lngLat, this) : 0;
13482		const location = raisedSurfaceVector(lngLat, elevation);
13483		if (!this.isSurfacePointVisible(location)) return true;
13484		if (!coverage) return false;
13485		const p = this._projectSurfacePointToScreen(location);
13486		const origin = this.cameraPosition;
13487		const direction = this.getRayDirectionFromPixel(p);
13488		const tLocation = rayParameter(origin, direction, location);
13489		if (tLocation <= 0) return true;
13490		const hit = this.screenTerrainPointToMercatorCoordinate(p, terrain);
13491		return hit != null && rayParameter(origin, direction, raisedSurfaceVector(hit.toLngLat(), hit.z)) < tLocation * .99;
13492	}
13493	transformLightDirection(dir) {
13494		const sphereX = this._helper._center.lng * Math.PI / 180;
13495		const sphereY = this._helper._center.lat * Math.PI / 180;
13496		const len = Math.cos(sphereY);
13497		const spherePos = [
13498			Math.sin(sphereX) * len,
13499			Math.sin(sphereY),
13500			Math.cos(sphereX) * len
13501		];
13502		const axisRight = [
13503			spherePos[2],
13504			0,
13505			-spherePos[0]
13506		];
13507		const axisDown = [
13508			0,
13509			0,
13510			0
13511		];
13512		cross(axisDown, axisRight, spherePos);
13513		normalize(axisRight, axisRight);
13514		normalize(axisDown, axisDown);
13515		const transformed = [
13516			axisRight[0] * dir[0] + axisDown[0] * dir[1] + spherePos[0] * dir[2],
13517			axisRight[1] * dir[0] + axisDown[1] * dir[1] + spherePos[1] * dir[2],
13518			axisRight[2] * dir[0] + axisDown[2] * dir[1] + spherePos[2] * dir[2]
13519		];
13520		const normalized = [
13521			0,
13522			0,
13523			0
13524		];
13525		normalize(normalized, transformed);
13526		return normalized;
13527	}
13528	getPixelScale() {
13529		return 1 / Math.cos(this._helper._center.lat * Math.PI / 180);
13530	}
13531	getCircleRadiusCorrection() {
13532		return Math.cos(this._helper._center.lat * Math.PI / 180);
13533	}
13534	getPitchedTextCorrection(textAnchorX, textAnchorY, tileID) {
13535		const mercator = tileCoordinatesToMercatorCoordinates(textAnchorX, textAnchorY, tileID.canonical);
13536		const angular = mercatorCoordinatesToAngularCoordinatesRadians(mercator.x, mercator.y);
13537		return this.getCircleRadiusCorrection() / Math.cos(angular[1]);
13538	}
13539	projectTileCoordinates(x, y, unwrappedTileID, elevation) {
13540		const canonical = unwrappedTileID.canonical;
13541		const spherePos = projectTileCoordinatesToSphere(x, y, canonical.x, canonical.y, canonical.z);
13542		const vectorMultiplier = 1 + (elevation ?? 0) / earthRadius;
13543		const elevatedX = spherePos[0] * vectorMultiplier;
13544		const elevatedY = spherePos[1] * vectorMultiplier;
13545		const elevatedZ = spherePos[2] * vectorMultiplier;
13546		const pos = [
13547			elevatedX,
13548			elevatedY,
13549			elevatedZ,
13550			1
13551		];
13552		transformMat4(pos, pos, this._globeViewProjMatrixF64);
13553		let isOccluded;
13554		if (vectorMultiplier <= 1) {
13555			const plane = this._cachedClippingPlane;
13556			isOccluded = plane[0] * spherePos[0] + plane[1] * spherePos[1] + plane[2] * spherePos[2] + plane[3] < 0;
13557		} else isOccluded = this._isLineOfSightBlocked(elevatedX, elevatedY, elevatedZ);
13558		return {
13559			point: new Point(pos[0] / pos[3], pos[1] / pos[3]),
13560			signedDistanceFromCamera: pos[3],
13561			isOccluded
13562		};
13563	}
13564	/**
13565	* True when the segment from the camera to the given point, both in unit-globe coordinates, passes through the planet.
13566	*/
13567	_isLineOfSightBlocked(x, y, z) {
13568		const cam = this._cameraPosition;
13569		const dx = x - cam[0];
13570		const dy = y - cam[1];
13571		const dz = z - cam[2];
13572		const lengthSq = dx * dx + dy * dy + dz * dz;
13573		if (lengthSq === 0) return false;
13574		const t = clamp(-(cam[0] * dx + cam[1] * dy + cam[2] * dz) / lengthSq, 0, 1);
13575		const cx = cam[0] + t * dx;
13576		const cy = cam[1] + t * dy;
13577		const cz = cam[2] + t * dz;
13578		return cx * cx + cy * cy + cz * cz < 1;
13579	}
13580	/**
13581	* @param calculateNearFarZ - Whether to compute the near/far Z range, or leave the range the helper already
13582	* holds. Defaults to {@link autoCalculateNearFarZ}; a composing transform such as {@link GlobeTransform}
13583	* overrides it so that its two children share a single depth range.
13584	*/
13585	_calcMatrices(calculateNearFarZ = this._helper.autoCalculateNearFarZ) {
13586		const globeRadiusPixels = getGlobeRadiusPixels(this.worldSize, this.center.lat);
13587		const globeMatrix = createMat4f64();
13588		if (calculateNearFarZ) {
13589			this._helper._nearZ = .5;
13590			this._helper._farZ = this.cameraToCenterDistance + globeRadiusPixels * 2;
13591		}
13592		perspective(globeMatrix, this.fovInRadians, this.width / this.height, this._helper._nearZ, this._helper._farZ);
13593		const offset = this.centerOffset;
13594		globeMatrix[8] = -offset.x * 2 / this._helper._width;
13595		globeMatrix[9] = offset.y * 2 / this._helper._height;
13596		this._projectionMatrix = clone$1(globeMatrix);
13597		this._globeProjMatrixInverted = createMat4f64();
13598		invert$1(this._globeProjMatrixInverted, globeMatrix);
13599		translate(globeMatrix, globeMatrix, [
13600			0,
13601			0,
13602			-this.cameraToCenterDistance
13603		]);
13604		rotateZ(globeMatrix, globeMatrix, this.rollInRadians);
13605		rotateX(globeMatrix, globeMatrix, -this.pitchInRadians);
13606		rotateZ(globeMatrix, globeMatrix, this.bearingInRadians);
13607		translate(globeMatrix, globeMatrix, [
13608			0,
13609			0,
13610			-globeRadiusPixels
13611		]);
13612		const scaleVec = createVec3f64();
13613		scaleVec[0] = globeRadiusPixels;
13614		scaleVec[1] = globeRadiusPixels;
13615		scaleVec[2] = globeRadiusPixels;
13616		rotateX(globeMatrix, globeMatrix, this.center.lat * Math.PI / 180);
13617		rotateY$1(globeMatrix, globeMatrix, -this.center.lng * Math.PI / 180);
13618		scale(globeMatrix, globeMatrix, scaleVec);
13619		this._globeViewProjMatrixF64 = globeMatrix;
13620		this._globeViewProjMatrix32f = new Float32Array(globeMatrix);
13621		this._globeViewProjMatrixF64Inverted = createMat4f64();
13622		invert$1(this._globeViewProjMatrixF64Inverted, globeMatrix);
13623		const zero = createVec3f64();
13624		this._cameraPosition = createVec3f64();
13625		this._cameraPosition[2] = this.cameraToCenterDistance / globeRadiusPixels;
13626		rotateZ$1(this._cameraPosition, this._cameraPosition, zero, -this.rollInRadians);
13627		rotateX$1(this._cameraPosition, this._cameraPosition, zero, this.pitchInRadians);
13628		rotateZ$1(this._cameraPosition, this._cameraPosition, zero, -this.bearingInRadians);
13629		add(this._cameraPosition, this._cameraPosition, [
13630			0,
13631			0,
13632			1
13633		]);
13634		rotateX$1(this._cameraPosition, this._cameraPosition, zero, -this.center.lat * Math.PI / 180);
13635		rotateY(this._cameraPosition, this._cameraPosition, zero, this.center.lng * Math.PI / 180);
13636		this._cachedClippingPlane = this._computeClippingPlane(globeRadiusPixels);
13637		const matrix = clone$1(this._globeViewProjMatrixF64Inverted);
13638		scale(matrix, matrix, [
13639			1,
13640			1,
13641			-1
13642		]);
13643		this._cachedFrustum = Frustum.fromInvProjectionMatrix(matrix, 1, 0, this._cachedClippingPlane, true);
13644	}
13645	calculateFogMatrix(_unwrappedTileID) {
13646		warnOnce("calculateFogMatrix is not supported on globe projection.");
13647		const m = createMat4f64();
13648		identity(m);
13649		return m;
13650	}
13651	getVisibleUnwrappedCoordinates(tileID) {
13652		return [new UnwrappedTileID(0, tileID)];
13653	}
13654	getCameraFrustum() {
13655		return this._cachedFrustum;
13656	}
13657	getClippingPlane() {
13658		return this._cachedClippingPlane;
13659	}
13660	getCoveringTilesDetailsProvider() {
13661		return this._coveringTilesDetailsProvider;
13662	}
13663	recalculateZoomAndCenter(terrain) {
13664		if (terrain) {
13665			warnOnce("terrain is not fully supported on vertical perspective projection.");
13666			return;
13667		}
13668		this._helper.recalculateZoomAndCenter(0);
13669	}
13670	maxPitchScaleFactor() {
13671		return 1;
13672	}
13673	getCameraPoint() {
13674		return this._helper.getCameraPoint();
13675	}
13676	/**
13677	* The altitude of the rendered camera above sea level. The sphere keeps the center point at sea level whatever its
13678	* elevation (`_calcMatrices` does not apply it), so unlike on mercator the center elevation does not lift the camera.
13679	* {@link calculateCameraOptionsFromTo} is the inverse.
13680	*/
13681	getCameraAltitude() {
13682		return (length(this._cameraPosition) - 1) * earthRadius;
13683	}
13684	getCameraLngLat() {
13685		const surface = createVec3f64();
13686		normalize(surface, this._cameraPosition);
13687		return sphereSurfacePointToCoordinates(surface);
13688	}
13689	populateCache(_coords) {}
13690	getBounds() {
13691		const xMid = this.width * .5;
13692		const yMid = this.height * .5;
13693		const testPoints = [
13694			new Point(0, 0),
13695			new Point(xMid, 0),
13696			new Point(this.width, 0),
13697			new Point(this.width, yMid),
13698			new Point(this.width, this.height),
13699			new Point(xMid, this.height),
13700			new Point(0, this.height),
13701			new Point(0, yMid)
13702		];
13703		const projectedPoints = [];
13704		for (const p of testPoints) projectedPoints.push(this.unprojectScreenPoint(p));
13705		let mostEast = 0, mostWest = 0, mostNorth = 0, mostSouth = 0;
13706		const center = this.center;
13707		for (const p of projectedPoints) {
13708			const dLng = differenceOfAnglesDegrees(center.lng, p.lng);
13709			const dLat = differenceOfAnglesDegrees(center.lat, p.lat);
13710			if (dLng < mostWest) mostWest = dLng;
13711			if (dLng > mostEast) mostEast = dLng;
13712			if (dLat < mostSouth) mostSouth = dLat;
13713			if (dLat > mostNorth) mostNorth = dLat;
13714		}
13715		const boundsArray = [
13716			center.lng + mostWest,
13717			center.lat + mostSouth,
13718			center.lng + mostEast,
13719			center.lat + mostNorth
13720		];
13721		if (this.isSurfacePointOnScreen([
13722			0,
13723			1,
13724			0
13725		])) {
13726			boundsArray[3] = 90;
13727			boundsArray[0] = -180;
13728			boundsArray[2] = 180;
13729		}
13730		if (this.isSurfacePointOnScreen([
13731			0,
13732			-1,
13733			0
13734		])) {
13735			boundsArray[1] = -90;
13736			boundsArray[0] = -180;
13737			boundsArray[2] = 180;
13738		}
13739		return new LngLatBounds(boundsArray);
13740	}
13741	calculateCenterFromCameraLngLatAlt(lngLat, alt, bearing, pitch) {
13742		return this._helper.calculateCenterFromCameraLngLatAlt(lngLat, alt, bearing, pitch);
13743	}
13744	/**
13745	* Inverts the camera placement of `_calcMatrices` in unit-globe coordinates: the camera sits at radius
13746	* `1 + altitudeFrom / earthRadius` and looks at the center on the sea-level sphere, the target altitude only becoming
13747	* the center elevation (the inverse of {@link getCameraAltitude}). Pitch and bearing are read in the center's local
13748	* frame, +z up, +y north, +x east. A camera straight above the center keeps the transform's bearing.
13749	*/
13750	calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo) {
13751		const center = LngLat.convert(to);
13752		const camera = angularCoordinatesToSurfaceVector(LngLat.convert(from));
13753		scale$1(camera, camera, 1 + altitudeFrom / earthRadius);
13754		const target = angularCoordinatesToSurfaceVector(center);
13755		const targetAtAltitude = scale$1(createVec3f64(), target, 1 + altitudeTo / earthRadius);
13756		const toCamera = subtract(createVec3f64(), camera, target);
13757		const distance$1 = length(toCamera);
13758		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");
13759		const zero = createVec3f64();
13760		rotateY(toCamera, toCamera, zero, -degreesToRadians(center.lng));
13761		rotateX$1(toCamera, toCamera, zero, degreesToRadians(center.lat));
13762		const pitch = radiansToDegrees(Math.acos(clamp(toCamera[2] / distance$1, -1, 1)));
13763		const bearing = Math.hypot(toCamera[0], toCamera[1]) < distance$1 * STRAIGHT_ABOVE_RATIO ? this.bearing : radiansToDegrees(Math.atan2(-toCamera[0], -toCamera[1]));
13764		return {
13765			center,
13766			elevation: altitudeTo,
13767			zoom: scaleZoom(this.cameraToCenterDistance / distance$1 / getGlobeRadiusPixels(this.tileSize, center.lat)),
13768			pitch,
13769			bearing
13770		};
13771	}
13772	/**
13773	* Note: automatically adjusts zoom to keep planet size consistent
13774	* (same size before and after a {@link setLocationAtPoint} call).
13775	*/
13776	setLocationAtPoint(lnglat, point, _elevation) {
13777		const vecToPixelCurrent = angularCoordinatesToSurfaceVector(this.unprojectScreenPoint(point));
13778		const vecToTarget = angularCoordinatesToSurfaceVector(lnglat);
13779		const zero = createVec3f64();
13780		zero$1(zero);
13781		const rotatedPixelVector = createVec3f64();
13782		rotateY(rotatedPixelVector, vecToPixelCurrent, zero, -this.center.lng * Math.PI / 180);
13783		rotateX$1(rotatedPixelVector, rotatedPixelVector, zero, this.center.lat * Math.PI / 180);
13784		const vecToTargetXZLengthSquared = vecToTarget[0] * vecToTarget[0] + vecToTarget[2] * vecToTarget[2];
13785		const targetXSquared = rotatedPixelVector[0] * rotatedPixelVector[0];
13786		if (vecToTargetXZLengthSquared < targetXSquared) return;
13787		const intersectionA = Math.sqrt(vecToTargetXZLengthSquared - targetXSquared);
13788		const intersectionB = -intersectionA;
13789		const lngA = angleToRotateBetweenVectors2D(vecToTarget[0], vecToTarget[2], rotatedPixelVector[0], intersectionA);
13790		const lngB = angleToRotateBetweenVectors2D(vecToTarget[0], vecToTarget[2], rotatedPixelVector[0], intersectionB);
13791		const vecToTargetLngA = createVec3f64();
13792		rotateY(vecToTargetLngA, vecToTarget, zero, -lngA);
13793		const latA = angleToRotateBetweenVectors2D(vecToTargetLngA[1], vecToTargetLngA[2], rotatedPixelVector[1], rotatedPixelVector[2]);
13794		const vecToTargetLngB = createVec3f64();
13795		rotateY(vecToTargetLngB, vecToTarget, zero, -lngB);
13796		const latB = angleToRotateBetweenVectors2D(vecToTargetLngB[1], vecToTargetLngB[2], rotatedPixelVector[1], rotatedPixelVector[2]);
13797		const limit = Math.PI * .5;
13798		const isValidA = latA >= -limit && latA <= limit;
13799		const isValidB = latB >= -limit && latB <= limit;
13800		let validLng;
13801		let validLat;
13802		if (isValidA && isValidB) {
13803			const centerLngRadians = this.center.lng * Math.PI / 180;
13804			const centerLatRadians = this.center.lat * Math.PI / 180;
13805			const lngDistA = distanceOfAnglesRadians(lngA, centerLngRadians);
13806			const latDistA = distanceOfAnglesRadians(latA, centerLatRadians);
13807			const lngDistB = distanceOfAnglesRadians(lngB, centerLngRadians);
13808			const latDistB = distanceOfAnglesRadians(latB, centerLatRadians);
13809			if (lngDistA + latDistA < lngDistB + latDistB) {
13810				validLng = lngA;
13811				validLat = latA;
13812			} else {
13813				validLng = lngB;
13814				validLat = latB;
13815			}
13816		} else if (isValidA) {
13817			validLng = lngA;
13818			validLat = latA;
13819		} else if (isValidB) {
13820			validLng = lngB;
13821			validLat = latB;
13822		} else return;
13823		const newLng = validLng / Math.PI * 180;
13824		const newLat = validLat / Math.PI * 180;
13825		const oldLat = this.center.lat;
13826		this.setCenter(new LngLat(newLng, clamp(newLat, -90, 90)));
13827		this.setZoom(this.zoom + getZoomAdjustment(oldLat, this.center.lat));
13828	}
13829	locationToScreenPoint(lnglat, terrain) {
13830		const pos = angularCoordinatesToSurfaceVector(lnglat);
13831		if (terrain) {
13832			const elevation = terrain.getElevationForLngLatZoom(lnglat, this._helper._tileZoom);
13833			scale$1(pos, pos, 1 + elevation / earthRadius);
13834		}
13835		return this._projectSurfacePointToScreen(pos);
13836	}
13837	/**
13838	* Projects a given vector on the surface of a unit sphere (or possible above the surface)
13839	* and returns its coordinates on screen in pixels.
13840	*/
13841	_projectSurfacePointToScreen(pos) {
13842		const projected = createVec4f64();
13843		transformMat4(projected, [...pos, 1], this._globeViewProjMatrixF64);
13844		projected[0] /= projected[3];
13845		projected[1] /= projected[3];
13846		return new Point((projected[0] * .5 + .5) * this.width, (-projected[1] * .5 + .5) * this.height);
13847	}
13848	screenPointToMercatorCoordinate(p, terrain) {
13849		if (terrain) {
13850			const coordinate = this.screenTerrainPointToMercatorCoordinate(p, terrain);
13851			if (coordinate) return coordinate;
13852		}
13853		return MercatorCoordinate.fromLngLat(this.unprojectScreenPoint(p));
13854	}
13855	/** {@inheritDoc ITransform.screenTerrainPointToMercatorCoordinate} */
13856	screenTerrainPointToMercatorCoordinate(p, terrain) {
13857		const index = terrain.getCoverageIndex();
13858		if (!index) return null;
13859		const origin = this.cameraPosition;
13860		const direction = this.getRayDirectionFromPixel(p);
13861		const outer = raySphereIntersection(origin, direction, 1 + index.maxElevation / earthRadius);
13862		if (!outer) return null;
13863		const inner = raySphereIntersection(origin, direction, 1 + index.minElevation / earthRadius);
13864		const tStart = Math.max(outer.tMin, 0);
13865		const tEnd = inner ? inner.tMin : outer.tMax;
13866		if (tEnd <= tStart) return null;
13867		const ray = {
13868			index,
13869			exaggeration: terrain.exaggeration,
13870			origin,
13871			direction
13872		};
13873		let previousT = 0;
13874		for (let i = 0; i <= GLOBE_SAMPLES; i++) {
13875			const t = tStart + (tEnd - tStart) * i / GLOBE_SAMPLES;
13876			if (globeIsBelowTerrain(ray, t)) {
13877				const { hi } = bisect(ray, globeIsBelowTerrain, previousT, t, GLOBE_BISECT_EPSILON_T);
13878				const { sample, mercator } = globeSampleAt(ray, hi);
13879				return new MercatorCoordinate(mercator.x, mercator.y, sample.elevation);
13880			}
13881			previousT = t;
13882		}
13883		return null;
13884	}
13885	screenPointToLocation(p, terrain) {
13886		return this.screenPointToMercatorCoordinate(p, terrain)?.toLngLat();
13887	}
13888	screenPointToLocationAtElevation(p, _elevation) {
13889		return this.screenPointToLocation(p);
13890	}
13891	isPointOnMapSurface(p, _terrain) {
13892		const rayOrigin = this._cameraPosition;
13893		return !!raySphereIntersection(rayOrigin, this.getRayDirectionFromPixel(p));
13894	}
13895	/**
13896	* Computes normalized direction of a ray from the camera to the given screen pixel.
13897	*/
13898	getRayDirectionFromPixel(p) {
13899		const pos = createVec4f64();
13900		pos[0] = p.x / this.width * 2 - 1;
13901		pos[1] = (p.y / this.height * 2 - 1) * -1;
13902		pos[2] = 1;
13903		pos[3] = 1;
13904		transformMat4(pos, pos, this._globeViewProjMatrixF64Inverted);
13905		pos[0] /= pos[3];
13906		pos[1] /= pos[3];
13907		pos[2] /= pos[3];
13908		const ray = createVec3f64();
13909		ray[0] = pos[0] - this._cameraPosition[0];
13910		ray[1] = pos[1] - this._cameraPosition[1];
13911		ray[2] = pos[2] - this._cameraPosition[2];
13912		const rayNormalized = createVec3f64();
13913		normalize(rayNormalized, ray);
13914		return rayNormalized;
13915	}
13916	/**
13917	* For a given point on the unit sphere of the planet, or raised above it, returns whether it lies on the camera's
13918	* side of the horizon plane, the plane the globe shaders clip with (not taking into account camera rotation at all).
13919	*/
13920	isSurfacePointVisible(p) {
13921		const plane = this._cachedClippingPlane;
13922		return plane[0] * p[0] + plane[1] * p[1] + plane[2] * p[2] + plane[3] >= 0;
13923	}
13924	/**
13925	* Returns whether surface point is visible on screen.
13926	* It must both project to a pixel in screen bounds and not be occluded by the planet.
13927	*/
13928	isSurfacePointOnScreen(vec) {
13929		if (!this.isSurfacePointVisible(vec)) return false;
13930		const projected = createVec4f64();
13931		transformMat4(projected, [...vec, 1], this._globeViewProjMatrixF64);
13932		projected[0] /= projected[3];
13933		projected[1] /= projected[3];
13934		projected[2] /= projected[3];
13935		return projected[0] > -1 && projected[0] < 1 && projected[1] > -1 && projected[1] < 1 && projected[2] > -1 && projected[2] < 1;
13936	}
13937	/**
13938	* @internal
13939	* Returns a {@link LngLat} representing geographical coordinates that correspond to the specified pixel coordinates.
13940	* Note: if the point does not lie on the globe, returns a location on the visible globe horizon (edge) that is
13941	* as close to the point as possible.
13942	* @param p - Screen point in pixels to unproject.
13943	* @param terrain - Optional terrain.
13944	*/
13945	unprojectScreenPoint(p) {
13946		const rayOrigin = this._cameraPosition;
13947		const rayDirection = this.getRayDirectionFromPixel(p);
13948		const intersection = raySphereIntersection(rayOrigin, rayDirection);
13949		if (intersection) {
13950			const intersectionPoint = createVec3f64();
13951			add(intersectionPoint, rayOrigin, [
13952				rayDirection[0] * intersection.tMin,
13953				rayDirection[1] * intersection.tMin,
13954				rayDirection[2] * intersection.tMin
13955			]);
13956			const sphereSurface = createVec3f64();
13957			normalize(sphereSurface, intersectionPoint);
13958			return sphereSurfacePointToCoordinates(sphereSurface);
13959		}
13960		const horizonPlane = this._cachedClippingPlane;
13961		const directionDotPlaneXyz = horizonPlane[0] * rayDirection[0] + horizonPlane[1] * rayDirection[1] + horizonPlane[2] * rayDirection[2];
13962		const distanceToIntersection = -pointPlaneSignedDistance(horizonPlane, rayOrigin) / directionDotPlaneXyz;
13963		const maxRayLength = 2;
13964		const planeIntersection = createVec3f64();
13965		if (distanceToIntersection > 0) add(planeIntersection, rayOrigin, [
13966			rayDirection[0] * distanceToIntersection,
13967			rayDirection[1] * distanceToIntersection,
13968			rayDirection[2] * distanceToIntersection
13969		]);
13970		else {
13971			const distantPoint = createVec3f64();
13972			add(distantPoint, rayOrigin, [
13973				rayDirection[0] * maxRayLength,
13974				rayDirection[1] * maxRayLength,
13975				rayDirection[2] * maxRayLength
13976			]);
13977			const distanceFromPlane = pointPlaneSignedDistance(this._cachedClippingPlane, distantPoint);
13978			sub(planeIntersection, distantPoint, [
13979				this._cachedClippingPlane[0] * distanceFromPlane,
13980				this._cachedClippingPlane[1] * distanceFromPlane,
13981				this._cachedClippingPlane[2] * distanceFromPlane
13982			]);
13983		}
13984		const horizonDisk = horizonPlaneToCenterAndRadius(horizonPlane);
13985		return sphereSurfacePointToCoordinates(clampToSphere(horizonDisk.center, horizonDisk.radius, planeIntersection));
13986	}
13987	getProjectionDataForCustomLayer(applyGlobeMatrix = true) {
13988		const globeData = this.getProjectionData({
13989			overscaledTileID: new OverscaledTileID(0, 0, 0, 0, 0),
13990			applyGlobeMatrix
13991		});
13992		globeData.tileMercatorCoords = [
13993			0,
13994			0,
13995			1,
13996			1
13997		];
13998		return globeData;
13999	}
14000	getFastPathSimpleProjectionMatrix(_tileID) {}
14001};
14002function globeSampleAt(ray, t) {
14003	const position = createVec3f64();
14004	scaleAndAdd(position, ray.origin, ray.direction, t);
14005	const radius = length(position);
14006	const surface = createVec3f64();
14007	scale$1(surface, position, 1 / radius);
14008	const lngLat = sphereSurfacePointToCoordinates(surface);
14009	const projected = MercatorCoordinate.fromLngLat(lngLat);
14010	const mercator = new MercatorCoordinate(projected.x, clamp(projected.y, 0, MAX_MERCATOR_Y));
14011	const sample = sampleAt(ray.index, ray.exaggeration, mercator.x, mercator.y);
14012	const elevation = Math.abs(lngLat.lat) > 85.051129 ? 0 : sample.elevation;
14013	return {
14014		sample: {
14015			...sample,
14016			elevation
14017		},
14018		radius,
14019		mercator
14020	};
14021}
14022function globeIsBelowTerrain(ray, t) {
14023	const { sample, radius } = globeSampleAt(ray, t);
14024	return isBelowTerrainSample(sample, (radius - 1) * earthRadius);
14025}
14026/**
14027* The location as a vector from the globe's center in globe radii, raised `elevation` meters above the surface.
14028*/
14029function raisedSurfaceVector(lngLat, elevation) {
14030	const vector = angularCoordinatesToSurfaceVector(lngLat);
14031	return scale$1(vector, vector, 1 + elevation / earthRadius);
14032}
14033/**
14034* Where the point of the ray closest to `point` lies along it, in units of `direction` from `origin`.
14035*/
14036function rayParameter(origin, direction, point) {
14037	const offset = subtract(createVec3f64(), point, origin);
14038	return dot(offset, direction);
14039}
14040//#endregion
14041//#region src/geo/projection/globe_transform.ts
14042/**
14043* Globe transform is a transform that moves between vertical perspective and mercator projections.
14044*
14045* The two child transforms share this transform's {@link TransformHelper}, so the camera exists exactly once:
14046* a child that moves itself - as {@link setLocationAtPoint} has it do, since only the child knows its own
14047* projection's geometry - has moved this transform too, with no copying back. The children own only what they
14048* derive from that shared camera, such as their matrices, and this transform drives their `_calcMatrices`
14049* because a helper has a single `calcMatrices` callback.
14050*/
14051var GlobeTransform = class GlobeTransform {
14052	get pixelsToClipSpaceMatrix() {
14053		return this._helper.pixelsToClipSpaceMatrix;
14054	}
14055	get clipSpaceToPixelsMatrix() {
14056		return this._helper.clipSpaceToPixelsMatrix;
14057	}
14058	get pixelsToGLUnits() {
14059		return this._helper.pixelsToGLUnits;
14060	}
14061	get centerOffset() {
14062		return this._helper.centerOffset;
14063	}
14064	get size() {
14065		return this._helper.size;
14066	}
14067	get rotationMatrix() {
14068		return this._helper.rotationMatrix;
14069	}
14070	get centerPoint() {
14071		return this._helper.centerPoint;
14072	}
14073	get pixelsPerMeter() {
14074		return this._helper.pixelsPerMeter;
14075	}
14076	setMinZoom(zoom) {
14077		this._helper.setMinZoom(zoom);
14078	}
14079	setMaxZoom(zoom) {
14080		this._helper.setMaxZoom(zoom);
14081	}
14082	setMinPitch(pitch) {
14083		this._helper.setMinPitch(pitch);
14084	}
14085	setMaxPitch(pitch) {
14086		this._helper.setMaxPitch(pitch);
14087	}
14088	setRenderWorldCopies(renderWorldCopies) {
14089		this._helper.setRenderWorldCopies(renderWorldCopies);
14090	}
14091	setBearing(bearing) {
14092		this._helper.setBearing(bearing);
14093	}
14094	setPitch(pitch) {
14095		this._helper.setPitch(pitch);
14096	}
14097	setRoll(roll) {
14098		this._helper.setRoll(roll);
14099	}
14100	setFov(fov) {
14101		this._helper.setFov(fov);
14102	}
14103	setZoom(zoom) {
14104		this._helper.setZoom(zoom);
14105	}
14106	setCenter(center) {
14107		this._helper.setCenter(center);
14108	}
14109	setElevation(elevation) {
14110		this._helper.setElevation(elevation);
14111	}
14112	setMinElevationForCurrentTile(elevation) {
14113		this._helper.setMinElevationForCurrentTile(elevation);
14114	}
14115	setPadding(padding) {
14116		this._helper.setPadding(padding);
14117	}
14118	interpolatePadding(start, target, t) {
14119		this._helper.interpolatePadding(start, target, t);
14120	}
14121	isPaddingEqual(padding) {
14122		return this._helper.isPaddingEqual(padding);
14123	}
14124	resize(width, height, constrainTransform = true) {
14125		this._helper.resize(width, height, constrainTransform);
14126	}
14127	getMaxBounds() {
14128		return this._helper.getMaxBounds();
14129	}
14130	setMaxBounds(bounds) {
14131		this._helper.setMaxBounds(bounds);
14132	}
14133	setConstrainOverride(constrain) {
14134		this._helper.setConstrainOverride(constrain);
14135	}
14136	overrideNearFarZ(nearZ, farZ) {
14137		this._helper.overrideNearFarZ(nearZ, farZ);
14138	}
14139	clearNearFarZOverride() {
14140		this._helper.clearNearFarZOverride();
14141	}
14142	getCameraQueryGeometry(queryGeometry) {
14143		return this._helper.getCameraQueryGeometry(this.getCameraPoint(), queryGeometry);
14144	}
14145	get tileSize() {
14146		return this._helper.tileSize;
14147	}
14148	get tileZoom() {
14149		return this._helper.tileZoom;
14150	}
14151	get scale() {
14152		return this._helper.scale;
14153	}
14154	get worldSize() {
14155		return this._helper.worldSize;
14156	}
14157	get width() {
14158		return this._helper.width;
14159	}
14160	get height() {
14161		return this._helper.height;
14162	}
14163	get lngRange() {
14164		return this._helper.lngRange;
14165	}
14166	get latRange() {
14167		return this._helper.latRange;
14168	}
14169	get minZoom() {
14170		return this._helper.minZoom;
14171	}
14172	get maxZoom() {
14173		return this._helper.maxZoom;
14174	}
14175	get zoom() {
14176		return this._helper.zoom;
14177	}
14178	get center() {
14179		return this._helper.center;
14180	}
14181	get minPitch() {
14182		return this._helper.minPitch;
14183	}
14184	get maxPitch() {
14185		return this._helper.maxPitch;
14186	}
14187	get pitch() {
14188		return this._helper.pitch;
14189	}
14190	get pitchInRadians() {
14191		return this._helper.pitchInRadians;
14192	}
14193	get roll() {
14194		return this._helper.roll;
14195	}
14196	get rollInRadians() {
14197		return this._helper.rollInRadians;
14198	}
14199	get bearing() {
14200		return this._helper.bearing;
14201	}
14202	get bearingInRadians() {
14203		return this._helper.bearingInRadians;
14204	}
14205	get fov() {
14206		return this._helper.fov;
14207	}
14208	get fovInRadians() {
14209		return this._helper.fovInRadians;
14210	}
14211	get elevation() {
14212		return this._helper.elevation;
14213	}
14214	get minElevationForCurrentTile() {
14215		return this._helper.minElevationForCurrentTile;
14216	}
14217	get padding() {
14218		return this._helper.padding;
14219	}
14220	get unmodified() {
14221		return this._helper.unmodified;
14222	}
14223	get renderWorldCopies() {
14224		return this._helper.renderWorldCopies;
14225	}
14226	get cameraToCenterDistance() {
14227		return this._helper.cameraToCenterDistance;
14228	}
14229	get constrainOverride() {
14230		return this._helper.constrainOverride;
14231	}
14232	get nearZ() {
14233		return this._helper.nearZ;
14234	}
14235	get farZ() {
14236		return this._helper.farZ;
14237	}
14238	get autoCalculateNearFarZ() {
14239		return this._helper.autoCalculateNearFarZ;
14240	}
14241	/**
14242	* True when globe render path should be used instead of the old but simpler mercator rendering.
14243	* Globe automatically transitions to mercator at high zoom levels, which causes a switch from
14244	* globe to mercator render path.
14245	*/
14246	get isGlobeRendering() {
14247		return this._globeness > 0;
14248	}
14249	setTransitionState(globeness) {
14250		this._globeness = globeness;
14251		this._calcMatrices();
14252		this._verticalPerspectiveTransform.getCoveringTilesDetailsProvider().prepareNextFrame();
14253		this._mercatorTransform.getCoveringTilesDetailsProvider().prepareNextFrame();
14254	}
14255	get currentTransform() {
14256		return this.isGlobeRendering ? this._verticalPerspectiveTransform : this._mercatorTransform;
14257	}
14258	constructor(options) {
14259		this._globeness = 1;
14260		this.defaultConstrain = (lngLat, zoom) => {
14261			return this.currentTransform.defaultConstrain(lngLat, zoom);
14262		};
14263		this.applyConstrain = (lngLat, zoom) => {
14264			return this._helper.applyConstrain(lngLat, zoom);
14265		};
14266		this._helper = new TransformHelper({
14267			calcMatrices: () => this._calcMatrices(),
14268			defaultConstrain: (center, zoom) => {
14269				return this.defaultConstrain(center, zoom);
14270			}
14271		}, options);
14272		this._globeness = 1;
14273		this._mercatorTransform = new MercatorTransform(void 0, this._helper);
14274		this._verticalPerspectiveTransform = new VerticalPerspectiveTransform(void 0, this._helper);
14275	}
14276	clone() {
14277		const clone = new GlobeTransform();
14278		clone._globeness = this._globeness;
14279		clone.apply(this, false);
14280		return clone;
14281	}
14282	apply(that, constrain) {
14283		this._helper.apply(that, constrain);
14284	}
14285	get projectionMatrix() {
14286		return this.currentTransform.projectionMatrix;
14287	}
14288	get modelViewProjectionMatrix() {
14289		return this.currentTransform.modelViewProjectionMatrix;
14290	}
14291	get inverseProjectionMatrix() {
14292		return this.currentTransform.inverseProjectionMatrix;
14293	}
14294	get cameraPosition() {
14295		return this.currentTransform.cameraPosition;
14296	}
14297	getProjectionData(params) {
14298		const mercatorProjectionData = this._mercatorTransform.getProjectionData(params);
14299		const verticalPerspectiveProjectionData = this._verticalPerspectiveTransform.getProjectionData(params);
14300		return {
14301			mainMatrix: this.isGlobeRendering ? verticalPerspectiveProjectionData.mainMatrix : mercatorProjectionData.mainMatrix,
14302			clippingPlane: verticalPerspectiveProjectionData.clippingPlane,
14303			tileMercatorCoords: verticalPerspectiveProjectionData.tileMercatorCoords,
14304			projectionTransition: params.applyGlobeMatrix ? this._globeness : 0,
14305			fallbackMatrix: mercatorProjectionData.fallbackMatrix,
14306			clipAntimeridian: verticalPerspectiveProjectionData.clipAntimeridian
14307		};
14308	}
14309	/** {@inheritDoc ITransform.isLocationOccluded} */
14310	isLocationOccluded(lngLat, terrain, elevation) {
14311		return this.currentTransform.isLocationOccluded(lngLat, terrain, elevation);
14312	}
14313	transformLightDirection(dir) {
14314		return this.currentTransform.transformLightDirection(dir);
14315	}
14316	getPixelScale() {
14317		return lerp(this._mercatorTransform.getPixelScale(), this._verticalPerspectiveTransform.getPixelScale(), this._globeness);
14318	}
14319	getCircleRadiusCorrection() {
14320		return lerp(this._mercatorTransform.getCircleRadiusCorrection(), this._verticalPerspectiveTransform.getCircleRadiusCorrection(), this._globeness);
14321	}
14322	getPitchedTextCorrection(textAnchorX, textAnchorY, tileID) {
14323		const mercatorCorrection = this._mercatorTransform.getPitchedTextCorrection(textAnchorX, textAnchorY, tileID);
14324		const verticalCorrection = this._verticalPerspectiveTransform.getPitchedTextCorrection(textAnchorX, textAnchorY, tileID);
14325		return lerp(mercatorCorrection, verticalCorrection, this._globeness);
14326	}
14327	projectTileCoordinates(x, y, unwrappedTileID, elevation) {
14328		return this.currentTransform.projectTileCoordinates(x, y, unwrappedTileID, elevation);
14329	}
14330	/**
14331	* Both children write their near/far Z into the shared helper, so the order here is what keeps the two render
14332	* paths at the same depth across the globe-to-mercator transition: vertical perspective computes the globe's Z
14333	* first, and while the globe is rendering mercator is made to reuse that result instead of computing its own.
14334	*/
14335	_calcMatrices() {
14336		if (!this._helper._width || !this._helper._height) return;
14337		this._verticalPerspectiveTransform._calcMatrices();
14338		this._mercatorTransform._calcMatrices(this.autoCalculateNearFarZ && !this.isGlobeRendering);
14339	}
14340	calculateFogMatrix(unwrappedTileID) {
14341		return this.currentTransform.calculateFogMatrix(unwrappedTileID);
14342	}
14343	getVisibleUnwrappedCoordinates(tileID) {
14344		return this.currentTransform.getVisibleUnwrappedCoordinates(tileID);
14345	}
14346	getCameraFrustum() {
14347		return this.currentTransform.getCameraFrustum();
14348	}
14349	getClippingPlane() {
14350		return this.currentTransform.getClippingPlane();
14351	}
14352	getCoveringTilesDetailsProvider() {
14353		return this.currentTransform.getCoveringTilesDetailsProvider();
14354	}
14355	recalculateZoomAndCenter(terrain) {
14356		this.currentTransform.recalculateZoomAndCenter(terrain);
14357	}
14358	maxPitchScaleFactor() {
14359		return this._mercatorTransform.maxPitchScaleFactor();
14360	}
14361	getCameraPoint() {
14362		return this._helper.getCameraPoint();
14363	}
14364	/** The camera of the child that renders the current frame. */
14365	getCameraAltitude() {
14366		return this.currentTransform.getCameraAltitude();
14367	}
14368	/** See {@link getCameraAltitude}. */
14369	getCameraLngLat() {
14370		return this.currentTransform.getCameraLngLat();
14371	}
14372	populateCache(coords) {
14373		this._mercatorTransform.populateCache(coords);
14374		this._verticalPerspectiveTransform.populateCache(coords);
14375	}
14376	getBounds() {
14377		return this.currentTransform.getBounds();
14378	}
14379	calculateCenterFromCameraLngLatAlt(lngLat, alt, bearing, pitch) {
14380		return this._helper.calculateCenterFromCameraLngLatAlt(lngLat, alt, bearing, pitch);
14381	}
14382	/** See {@link getCameraAltitude}. */
14383	calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo) {
14384		return this.currentTransform.calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo);
14385	}
14386	/**
14387	* Note: automatically adjusts zoom to keep planet size consistent
14388	* (same size before and after a {@link setLocationAtPoint} call).
14389	*/
14390	setLocationAtPoint(lnglat, point, elevation) {
14391		this.currentTransform.setLocationAtPoint(lnglat, point, elevation);
14392	}
14393	locationToScreenPoint(lnglat, terrain) {
14394		return this.currentTransform.locationToScreenPoint(lnglat, terrain);
14395	}
14396	screenPointToMercatorCoordinate(p, terrain) {
14397		return this.currentTransform.screenPointToMercatorCoordinate(p, terrain);
14398	}
14399	/** {@inheritDoc ITransform.screenTerrainPointToMercatorCoordinate} */
14400	screenTerrainPointToMercatorCoordinate(p, terrain) {
14401		return this.currentTransform.screenTerrainPointToMercatorCoordinate(p, terrain);
14402	}
14403	screenPointToLocation(p, terrain) {
14404		return this.currentTransform.screenPointToLocation(p, terrain);
14405	}
14406	screenPointToLocationAtElevation(p, elevation) {
14407		return this.currentTransform.screenPointToLocationAtElevation(p, elevation);
14408	}
14409	isPointOnMapSurface(p, terrain) {
14410		return this.currentTransform.isPointOnMapSurface(p, terrain);
14411	}
14412	/**
14413	* Computes normalized direction of a ray from the camera to the given screen pixel.
14414	*
14415	* Always answered by the vertical perspective child: the ray is in unit-sphere space, so it is only meaningful
14416	* to the globe camera controls that ask for it, and the mercator transform does not implement it at all.
14417	*/
14418	getRayDirectionFromPixel(p) {
14419		return this._verticalPerspectiveTransform.getRayDirectionFromPixel(p);
14420	}
14421	getProjectionDataForCustomLayer(applyGlobeMatrix = true) {
14422		const mercatorData = this._mercatorTransform.getProjectionDataForCustomLayer(applyGlobeMatrix);
14423		if (!this.isGlobeRendering) return mercatorData;
14424		const globeData = this._verticalPerspectiveTransform.getProjectionDataForCustomLayer(applyGlobeMatrix);
14425		globeData.fallbackMatrix = mercatorData.mainMatrix;
14426		globeData.projectionTransition = this._globeness;
14427		return globeData;
14428	}
14429	getFastPathSimpleProjectionMatrix(tileID) {
14430		return this.currentTransform.getFastPathSimpleProjectionMatrix(tileID);
14431	}
14432};
14433//#endregion
14434//#region src/geo/projection/vertical_perspective_camera_helper.ts
14435/**
14436* Zoom movement slowing starts when the mouse ray passes further than this above the planet surface,
14437* so a cursor off the globe does not move the map unnaturally. Globe radius is 1, so 0.3 is ~2000 km.
14438*/
14439const RAY_SURFACE_DISTANCE_FOR_SLOWING_START = .3;
14440/** How sharply zoom movement slows as the mouse ray rises above the planet surface. Lower is more gradual. */
14441const SLOWING_MULTIPLIER = .5;
14442/** Longitude difference between zoom location and map center at which the blend from exact to heuristic zooming starts, in degrees. */
14443const INTERPOLATE_TO_HEURISTIC_START_LNG = 45;
14444/** Longitude difference at which the blend to heuristic zooming is complete, in degrees. */
14445const INTERPOLATE_TO_HEURISTIC_END_LNG = 85;
14446/** Exponent applied to the blend factor: below 1, so the blend leans towards heuristic zooming and flattens as it completes. */
14447const INTERPOLATE_TO_HEURISTIC_EXPONENT = .25;
14448/**
14449* Distance of the mouse ray from the globe center at which the blend from exact to heuristic zooming
14450* starts. Globe radius is 1, so 1 is a ray grazing the horizon.
14451*/
14452const INTERPOLATE_TO_HEURISTIC_START_HORIZON = .95;
14453/** Ray distance at which the blend to heuristic zooming is complete. */
14454const INTERPOLATE_TO_HEURISTIC_END_HORIZON = .999;
14455/**
14456* Globe radius relative to the smaller viewport dimension below which zoom movement near the horizon
14457* starts being inhibited, avoiding unnatural movements when the map is zoomed out a lot.
14458*/
14459const SLOWING_RADIUS_START = .9;
14460/** Globe radius relative to the smaller viewport dimension at which that inhibition is at full strength. */
14461const SLOWING_RADIUS_STOP = .5;
14462/** Fraction of the zoom movement that remains once the globe has shrunk to `SLOWING_RADIUS_STOP`. */
14463const SLOWING_RADIUS_SLOW_FACTOR = .25;
14464/**
14465* @internal
14466*/
14467var VerticalPerspectiveCameraHelper = class VerticalPerspectiveCameraHelper {
14468	get useGlobeControls() {
14469		return true;
14470	}
14471	handlePanInertia(pan, transform) {
14472		const panCenter = computeGlobePanCenter(pan, transform);
14473		if (Math.abs(panCenter.lng - transform.center.lng) > 180) panCenter.lng = transform.center.lng + 179.5 * Math.sign(panCenter.lng - transform.center.lng);
14474		return {
14475			easingCenter: panCenter,
14476			easingOffset: new Point(0, 0)
14477		};
14478	}
14479	/**
14480	* Zooms around the pointer.
14481	*
14482	* `setLocationAtPoint` is exact but degenerates when called repeatedly for a
14483	* location whose longitude is far from the center's, or one near the horizon,
14484	* so those cases blend towards a heuristic.
14485	*/
14486	handleMapControlsRollPitchBearingZoom(deltas, tr) {
14487		const zoomPixel = deltas.around;
14488		const zoomLoc = tr.screenPointToLocation(zoomPixel);
14489		if (deltas.bearingDelta) tr.setBearing(tr.bearing + deltas.bearingDelta);
14490		if (deltas.pitchDelta) tr.setPitch(tr.pitch + deltas.pitchDelta);
14491		if (deltas.rollDelta) tr.setRoll(tr.roll + deltas.rollDelta);
14492		const oldZoomPreZoomDelta = tr.zoom;
14493		if (deltas.zoomDelta) tr.setZoom(tr.zoom + deltas.zoomDelta);
14494		const actualZoomDelta = tr.zoom - oldZoomPreZoomDelta;
14495		if (actualZoomDelta === 0) return;
14496		const dLngRaw = differenceOfAnglesDegrees(tr.center.lng, zoomLoc.lng);
14497		const dLng = dLngRaw / (Math.abs(dLngRaw / 180) + 1);
14498		const dLat = differenceOfAnglesDegrees(tr.center.lat, zoomLoc.lat);
14499		const rayDirection = tr.getRayDirectionFromPixel(zoomPixel);
14500		const rayOrigin = tr.cameraPosition;
14501		const distanceToClosestPoint = dot(rayOrigin, rayDirection) * -1;
14502		const closestPoint = createVec3f64();
14503		add(closestPoint, rayOrigin, [
14504			rayDirection[0] * distanceToClosestPoint,
14505			rayDirection[1] * distanceToClosestPoint,
14506			rayDirection[2] * distanceToClosestPoint
14507		]);
14508		const rayDistanceFromGlobeCenter = length(closestPoint);
14509		const distanceFromSurface = rayDistanceFromGlobeCenter - 1;
14510		const distanceFactor = Math.exp(-Math.max(distanceFromSurface - RAY_SURFACE_DISTANCE_FOR_SLOWING_START, 0) * SLOWING_MULTIPLIER);
14511		const interpolationFactorHorizon = remapSaturate(rayDistanceFromGlobeCenter, INTERPOLATE_TO_HEURISTIC_START_HORIZON, INTERPOLATE_TO_HEURISTIC_END_HORIZON, 0, 1);
14512		const radius = getGlobeRadiusPixels(tr.worldSize, tr.center.lat) / Math.min(tr.width, tr.height);
14513		const radiusFactor = remapSaturate(radius, SLOWING_RADIUS_START, SLOWING_RADIUS_STOP, 1, SLOWING_RADIUS_SLOW_FACTOR);
14514		const slowingFactor = Math.min(distanceFactor, lerp(1, radiusFactor, interpolationFactorHorizon));
14515		const factor = (1 - zoomScale(-actualZoomDelta)) * slowingFactor;
14516		const oldCenterLat = tr.center.lat;
14517		const oldZoom = tr.zoom;
14518		const heuristicCenter = new LngLat(tr.center.lng + dLng * factor, clamp(tr.center.lat + dLat * factor, -MAX_VALID_LATITUDE, MAX_VALID_LATITUDE));
14519		tr.setLocationAtPoint(zoomLoc, zoomPixel);
14520		const exactCenter = tr.center;
14521		const interpolationFactorLongitude = remapSaturate(Math.abs(dLngRaw), INTERPOLATE_TO_HEURISTIC_START_LNG, INTERPOLATE_TO_HEURISTIC_END_LNG, 0, 1);
14522		const heuristicFactor = Math.pow(Math.max(interpolationFactorLongitude, interpolationFactorHorizon), INTERPOLATE_TO_HEURISTIC_EXPONENT);
14523		const lngExactToHeuristic = differenceOfAnglesDegrees(exactCenter.lng, heuristicCenter.lng);
14524		const latExactToHeuristic = differenceOfAnglesDegrees(exactCenter.lat, heuristicCenter.lat);
14525		tr.setCenter(new LngLat(exactCenter.lng + lngExactToHeuristic * heuristicFactor, exactCenter.lat + latExactToHeuristic * heuristicFactor).wrap());
14526		tr.setZoom(oldZoom + getZoomAdjustment(oldCenterLat, tr.center.lat));
14527	}
14528	/**
14529	* Pans the globe by rotating it with a single quaternion that brings the grabbed location back
14530	* under the cursor. This stays consistent near and across the poles, where the previous
14531	* bearing-preserving mapping inverted and stalled (#5296).
14532	* @param deltas - The deltas accumulated for this frame.
14533	* @param tr - The transform to pan.
14534	* @param preZoomAroundLoc - The location that was under the cursor before this frame.
14535	*/
14536	handleMapControlsPan(deltas, tr, preZoomAroundLoc) {
14537		if (!deltas.panDelta) return;
14538		versorSetLocationAtPoint(tr, preZoomAroundLoc, tr.isPointOnMapSurface(deltas.around) ? deltas.around : tr.centerPoint, deltas.panDelta);
14539	}
14540	cameraForBoxAndBearing(options, padding, bounds, bearing, tr) {
14541		const result = cameraForBoxAndBearing(options, padding, bounds, bearing, tr);
14542		const xLeft = padding.left / tr.width * 2 - 1;
14543		const xRight = (tr.width - padding.right) / tr.width * 2 - 1;
14544		const yTop = padding.top / tr.height * -2 + 1;
14545		const yBottom = (tr.height - padding.bottom) / tr.height * -2 + 1;
14546		const flipEastWest = differenceOfAnglesDegrees(bounds.getWest(), bounds.getEast()) < 0;
14547		const lngWest = flipEastWest ? bounds.getEast() : bounds.getWest();
14548		const lngEast = flipEastWest ? bounds.getWest() : bounds.getEast();
14549		const latNorth = Math.max(bounds.getNorth(), bounds.getSouth());
14550		const latSouth = Math.min(bounds.getNorth(), bounds.getSouth());
14551		const lngMid = lngWest + differenceOfAnglesDegrees(lngWest, lngEast) * .5;
14552		const latMid = latNorth + differenceOfAnglesDegrees(latNorth, latSouth) * .5;
14553		const clonedTr = tr.clone();
14554		clonedTr.setCenter(result.center);
14555		clonedTr.setBearing(result.bearing);
14556		clonedTr.setPitch(0);
14557		clonedTr.setRoll(0);
14558		clonedTr.setZoom(result.zoom);
14559		const matrix = clonedTr.modelViewProjectionMatrix;
14560		const testVectors = [
14561			angularCoordinatesToSurfaceVector(bounds.getNorthWest()),
14562			angularCoordinatesToSurfaceVector(bounds.getNorthEast()),
14563			angularCoordinatesToSurfaceVector(bounds.getSouthWest()),
14564			angularCoordinatesToSurfaceVector(bounds.getSouthEast()),
14565			angularCoordinatesToSurfaceVector(new LngLat(lngEast, latMid)),
14566			angularCoordinatesToSurfaceVector(new LngLat(lngWest, latMid)),
14567			angularCoordinatesToSurfaceVector(new LngLat(lngMid, latNorth)),
14568			angularCoordinatesToSurfaceVector(new LngLat(lngMid, latSouth))
14569		];
14570		const vecToCenter = angularCoordinatesToSurfaceVector(result.center);
14571		let smallestNeededScale = Number.POSITIVE_INFINITY;
14572		for (const vec of testVectors) {
14573			if (xLeft < 0) smallestNeededScale = VerticalPerspectiveCameraHelper.getLesserNonNegativeNonNull(smallestNeededScale, VerticalPerspectiveCameraHelper.solveVectorScale(vec, vecToCenter, matrix, "x", xLeft));
14574			if (xRight > 0) smallestNeededScale = VerticalPerspectiveCameraHelper.getLesserNonNegativeNonNull(smallestNeededScale, VerticalPerspectiveCameraHelper.solveVectorScale(vec, vecToCenter, matrix, "x", xRight));
14575			if (yTop > 0) smallestNeededScale = VerticalPerspectiveCameraHelper.getLesserNonNegativeNonNull(smallestNeededScale, VerticalPerspectiveCameraHelper.solveVectorScale(vec, vecToCenter, matrix, "y", yTop));
14576			if (yBottom < 0) smallestNeededScale = VerticalPerspectiveCameraHelper.getLesserNonNegativeNonNull(smallestNeededScale, VerticalPerspectiveCameraHelper.solveVectorScale(vec, vecToCenter, matrix, "y", yBottom));
14577		}
14578		if (!Number.isFinite(smallestNeededScale) || smallestNeededScale === 0) {
14579			cameraBoundsWarning();
14580			return;
14581		}
14582		result.zoom = Math.min(clonedTr.zoom + scaleZoom(smallestNeededScale), options.maxZoom);
14583		return result;
14584	}
14585	/**
14586	* Handles the zoom and center change during camera jumpTo.
14587	*/
14588	handleJumpToCenterZoom(tr, options) {
14589		const startingLat = tr.center.lat;
14590		const constrainedCenter = tr.applyConstrain(options.center ? LngLat.convert(options.center) : tr.center, tr.zoom).center;
14591		tr.setCenter(constrainedCenter.wrap());
14592		const targetZoom = typeof options.zoom !== "undefined" ? +options.zoom : tr.zoom + getZoomAdjustment(startingLat, constrainedCenter.lat);
14593		if (tr.zoom !== targetZoom) tr.setZoom(targetZoom);
14594	}
14595	handleEaseTo(tr, options) {
14596		const startZoom = tr.zoom;
14597		const startCenter = tr.center;
14598		const startPadding = tr.padding;
14599		const startEulerAngles = {
14600			roll: tr.roll,
14601			pitch: tr.pitch,
14602			bearing: tr.bearing
14603		};
14604		const endEulerAngles = {
14605			roll: options.roll === void 0 ? tr.roll : options.roll,
14606			pitch: options.pitch === void 0 ? tr.pitch : options.pitch,
14607			bearing: options.bearing === void 0 ? tr.bearing : options.bearing
14608		};
14609		const optionsZoom = typeof options.zoom !== "undefined";
14610		const doPadding = !tr.isPaddingEqual(options.padding);
14611		let isZooming = false;
14612		const preConstrainCenter = options.center ? LngLat.convert(options.center) : startCenter;
14613		const constrainedCenter = tr.applyConstrain(preConstrainCenter, startZoom).center;
14614		normalizeCenter(tr, constrainedCenter);
14615		const clonedTr = tr.clone();
14616		clonedTr.setCenter(constrainedCenter);
14617		clonedTr.setZoom(optionsZoom ? +options.zoom : startZoom + getZoomAdjustment(startCenter.lat, preConstrainCenter.lat));
14618		clonedTr.setBearing(options.bearing);
14619		const clampedPoint = new Point(clamp(tr.centerPoint.x + options.offsetAsPoint.x, 0, tr.width), clamp(tr.centerPoint.y + options.offsetAsPoint.y, 0, tr.height));
14620		clonedTr.setLocationAtPoint(constrainedCenter, clampedPoint);
14621		const endCenterWithShift = (options.offset && options.offsetAsPoint.mag()) > 0 ? clonedTr.center : constrainedCenter;
14622		const endZoomWithShift = optionsZoom ? +options.zoom : startZoom + getZoomAdjustment(startCenter.lat, endCenterWithShift.lat);
14623		const normalizedStartZoom = startZoom + getZoomAdjustment(startCenter.lat, 0);
14624		const normalizedEndZoom = endZoomWithShift + getZoomAdjustment(endCenterWithShift.lat, 0);
14625		const deltaLng = differenceOfAnglesDegrees(startCenter.lng, endCenterWithShift.lng);
14626		const deltaLat = differenceOfAnglesDegrees(startCenter.lat, endCenterWithShift.lat);
14627		const finalScale = zoomScale(normalizedEndZoom - normalizedStartZoom);
14628		isZooming = endZoomWithShift !== startZoom;
14629		const easeFunc = (k) => {
14630			if (!rollPitchBearingEqual(startEulerAngles, endEulerAngles)) updateRotation({
14631				startEulerAngles,
14632				endEulerAngles,
14633				tr,
14634				k,
14635				useSlerp: startEulerAngles.roll != endEulerAngles.roll
14636			});
14637			if (doPadding) tr.interpolatePadding(startPadding, options.padding, k);
14638			if (options.around) {
14639				warnOnce("Easing around a point is not supported under globe projection.");
14640				tr.setLocationAtPoint(options.around, options.aroundPoint);
14641			} else {
14642				const factor = k * Math.pow(normalizedEndZoom > normalizedStartZoom ? Math.min(2, finalScale) : Math.max(.5, finalScale), 1 - k);
14643				const newCenter = interpolateLngLatForGlobe(startCenter, deltaLng, deltaLat, factor);
14644				tr.setCenter(newCenter.wrap());
14645			}
14646			if (isZooming) {
14647				const interpolatedZoom = interpolateFactory.number(normalizedStartZoom, normalizedEndZoom, k) + getZoomAdjustment(0, tr.center.lat);
14648				tr.setZoom(interpolatedZoom);
14649			}
14650		};
14651		return {
14652			easeFunc,
14653			isZooming,
14654			elevationCenter: endCenterWithShift
14655		};
14656	}
14657	handleFlyTo(tr, options) {
14658		const optionsZoom = typeof options.zoom !== "undefined";
14659		const startCenter = tr.center;
14660		const startZoom = tr.zoom;
14661		const startPadding = tr.padding;
14662		const doPadding = !tr.isPaddingEqual(options.padding);
14663		const constrainedCenter = tr.applyConstrain(LngLat.convert(options.center || options.locationAtOffset), startZoom).center;
14664		const targetZoom = optionsZoom ? +options.zoom : tr.zoom + getZoomAdjustment(tr.center.lat, constrainedCenter.lat);
14665		const clonedTr = tr.clone();
14666		clonedTr.setCenter(constrainedCenter);
14667		clonedTr.setZoom(targetZoom);
14668		clonedTr.setBearing(options.bearing);
14669		const clampedPoint = new Point(clamp(tr.centerPoint.x + options.offsetAsPoint.x, 0, tr.width), clamp(tr.centerPoint.y + options.offsetAsPoint.y, 0, tr.height));
14670		clonedTr.setLocationAtPoint(constrainedCenter, clampedPoint);
14671		const targetCenter = clonedTr.center;
14672		normalizeCenter(tr, targetCenter);
14673		const pixelPathLength = globeDistanceOfLocationsPixels(tr, startCenter, targetCenter);
14674		const normalizedStartZoom = startZoom + getZoomAdjustment(startCenter.lat, 0);
14675		const normalizedTargetZoom = targetZoom + getZoomAdjustment(targetCenter.lat, 0);
14676		const scaleOfZoom = zoomScale(normalizedTargetZoom - normalizedStartZoom);
14677		const requestedMinZoom = typeof options.minZoom === "number" ? +options.minZoom : tr.minZoom;
14678		const normalizedEffectiveMinZoom = Math.max(requestedMinZoom, tr.minZoom) + getZoomAdjustment(targetCenter.lat, 0);
14679		const minZoomPreConstrain = Math.min(normalizedEffectiveMinZoom, normalizedStartZoom, normalizedTargetZoom) + getZoomAdjustment(0, targetCenter.lat);
14680		const normalizedMinZoom = tr.applyConstrain(targetCenter, minZoomPreConstrain).zoom + getZoomAdjustment(targetCenter.lat, 0);
14681		const scaleOfMinZoom = zoomScale(normalizedMinZoom - normalizedStartZoom);
14682		const deltaLng = differenceOfAnglesDegrees(startCenter.lng, targetCenter.lng);
14683		const deltaLat = differenceOfAnglesDegrees(startCenter.lat, targetCenter.lat);
14684		const easeFunc = (k, scale, centerFactor, _pointAtOffset) => {
14685			const interpolatedCenter = interpolateLngLatForGlobe(startCenter, deltaLng, deltaLat, centerFactor);
14686			if (doPadding) tr.interpolatePadding(startPadding, options.padding, k);
14687			const newCenter = k === 1 ? targetCenter : interpolatedCenter;
14688			tr.setCenter(newCenter.wrap());
14689			const interpolatedZoom = normalizedStartZoom + scaleZoom(scale);
14690			tr.setZoom(k === 1 ? targetZoom : interpolatedZoom + getZoomAdjustment(0, newCenter.lat));
14691		};
14692		return {
14693			easeFunc,
14694			scaleOfZoom,
14695			targetCenter,
14696			scaleOfMinZoom,
14697			pixelPathLength
14698		};
14699	}
14700	/**
14701	* 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.
14702	* @param vector - Position of the queried location on the surface of the unit sphere globe.
14703	* @param toCenter - Position of current transform center on the surface of the unit sphere globe.
14704	* This is needed because zooming the globe not only changes its scale,
14705	* but also moves the camera closer or further away along this vector (pitch is disregarded).
14706	* @param projection - The globe projection matrix.
14707	* @param targetDimension - The dimension in which the scaled vector must match the target value in clip space.
14708	* @param targetValue - The target clip space value in the specified dimension to which the queried vector must project.
14709	* @returns How much to scale the globe.
14710	*/
14711	static solveVectorScale(vector, toCenter, projection, targetDimension, targetValue) {
14712		const k = targetValue;
14713		const columnXorY = targetDimension === "x" ? [
14714			projection[0],
14715			projection[4],
14716			projection[8],
14717			projection[12]
14718		] : [
14719			projection[1],
14720			projection[5],
14721			projection[9],
14722			projection[13]
14723		];
14724		const columnZ = [
14725			projection[3],
14726			projection[7],
14727			projection[11],
14728			projection[15]
14729		];
14730		const vecDotXY = vector[0] * columnXorY[0] + vector[1] * columnXorY[1] + vector[2] * columnXorY[2];
14731		const vecDotZ = vector[0] * columnZ[0] + vector[1] * columnZ[1] + vector[2] * columnZ[2];
14732		const toCenterDotXY = toCenter[0] * columnXorY[0] + toCenter[1] * columnXorY[1] + toCenter[2] * columnXorY[2];
14733		const toCenterDotZ = toCenter[0] * columnZ[0] + toCenter[1] * columnZ[1] + toCenter[2] * columnZ[2];
14734		const t = (toCenterDotXY + columnXorY[3] - k * toCenterDotZ - k * columnZ[3]) / (toCenterDotXY - vecDotXY - k * toCenterDotZ + k * vecDotZ);
14735		if (toCenterDotXY + k * vecDotZ === vecDotXY + k * toCenterDotZ || columnZ[3] * (vecDotXY - toCenterDotXY) + columnXorY[3] * (toCenterDotZ - vecDotZ) + vecDotXY * toCenterDotZ === toCenterDotXY * vecDotZ) return null;
14736		return t;
14737	}
14738	/**
14739	* Returns `newValue` if it is:
14740	*
14741	* - not null AND
14742	* - not negative AND
14743	* - smaller than `newValue`,
14744	*
14745	* ...otherwise returns `oldValue`.
14746	*/
14747	static getLesserNonNegativeNonNull(oldValue, newValue) {
14748		if (newValue !== null && newValue >= 0 && newValue < oldValue) return newValue;
14749		else return oldValue;
14750	}
14751};
14752//#endregion
14753//#region src/geo/projection/globe_camera_helper.ts
14754/**
14755* @internal
14756*/
14757var GlobeCameraHelper = class {
14758	constructor(globe) {
14759		this._globe = globe;
14760		this._mercatorCameraHelper = new MercatorCameraHelper();
14761		this._verticalPerspectiveCameraHelper = new VerticalPerspectiveCameraHelper();
14762	}
14763	get useGlobeControls() {
14764		return this._globe.useGlobeRendering;
14765	}
14766	get currentHelper() {
14767		return this.useGlobeControls ? this._verticalPerspectiveCameraHelper : this._mercatorCameraHelper;
14768	}
14769	handlePanInertia(pan, transform) {
14770		return this.currentHelper.handlePanInertia(pan, transform);
14771	}
14772	handleMapControlsRollPitchBearingZoom(deltas, tr) {
14773		this.currentHelper.handleMapControlsRollPitchBearingZoom(deltas, tr);
14774	}
14775	handleMapControlsPan(deltas, tr, preZoomAroundLoc) {
14776		this.currentHelper.handleMapControlsPan(deltas, tr, preZoomAroundLoc);
14777	}
14778	cameraForBoxAndBearing(options, padding, bounds, bearing, tr) {
14779		return this.currentHelper.cameraForBoxAndBearing(options, padding, bounds, bearing, tr);
14780	}
14781	/**
14782	* Handles the zoom and center change during camera jumpTo.
14783	*/
14784	handleJumpToCenterZoom(tr, options) {
14785		this.currentHelper.handleJumpToCenterZoom(tr, options);
14786	}
14787	handleEaseTo(tr, options) {
14788		return this.currentHelper.handleEaseTo(tr, options);
14789	}
14790	handleFlyTo(tr, options) {
14791		return this.currentHelper.handleFlyTo(tr, options);
14792	}
14793};
14794//#endregion
14795//#region src/geo/projection/projection_factory.ts
14796function createProjectionFromName(name, transformConstrain, globalState) {
14797	const transformOptions = { constrainOverride: transformConstrain };
14798	if (Array.isArray(name)) {
14799		const globeProjection = new GlobeProjection({ type: name }, globalState);
14800		return {
14801			projection: globeProjection,
14802			transform: new GlobeTransform(transformOptions),
14803			cameraHelper: new GlobeCameraHelper(globeProjection)
14804		};
14805	}
14806	switch (name) {
14807		case "mercator": return {
14808			projection: new MercatorProjection(),
14809			transform: new MercatorTransform(transformOptions),
14810			cameraHelper: new MercatorCameraHelper()
14811		};
14812		case "globe": {
14813			const globeProjection = new GlobeProjection({ type: [
14814				"interpolate",
14815				["linear"],
14816				["zoom"],
14817				11,
14818				"vertical-perspective",
14819				12,
14820				"mercator"
14821			] }, {});
14822			return {
14823				projection: globeProjection,
14824				transform: new GlobeTransform(transformOptions),
14825				cameraHelper: new GlobeCameraHelper(globeProjection)
14826			};
14827		}
14828		case "vertical-perspective": return {
14829			projection: new VerticalPerspectiveProjection(),
14830			transform: new VerticalPerspectiveTransform(transformOptions),
14831			cameraHelper: new VerticalPerspectiveCameraHelper()
14832		};
14833		default:
14834			warnOnce(`Unknown projection name: ${name}. Falling back to mercator projection.`);
14835			return {
14836				projection: new MercatorProjection(),
14837				transform: new MercatorTransform(transformOptions),
14838				cameraHelper: new MercatorCameraHelper()
14839			};
14840	}
14841}
14842//#endregion
14843//#region src/style/style.ts
14844const empty = emptyStyle();
14845/**
14846* The Style base class
14847*/
14848var Style = class extends Evented {
14849	constructor(map, options = {}) {
14850		super();
14851		this._rtlPluginLoaded = () => {
14852			for (const id in this.tileManagers) {
14853				const sourceType = this.tileManagers[id].getSource().type;
14854				if (sourceType === "vector" || sourceType === "geojson") this.tileManagers[id].reload();
14855			}
14856		};
14857		this.map = map;
14858		this.dispatcher = new Dispatcher(getGlobalWorkerPool(), map._getMapId()).setEventedParent(this);
14859		this.dispatcher.registerMessageHandler("GG", (mapId, params) => {
14860			return this.getGlyphs(mapId, params);
14861		});
14862		this.dispatcher.registerMessageHandler("GI", (mapId, params) => {
14863			return this.getImages(mapId, params);
14864		});
14865		this.dispatcher.registerMessageHandler("GDA", (mapId, params) => {
14866			return this.getDashes(mapId, params);
14867		});
14868		this.imageManager = new ImageManager();
14869		this.imageManager.setEventedParent(this);
14870		this.imageManager.setMissingImageResolver(map._missingStyleImageResolver);
14871		this.patternAtlas = new PatternAtlas(this.imageManager);
14872		const glyphLang = map._container?.lang || typeof document !== "undefined" && document.documentElement?.lang || void 0;
14873		this.glyphManager = new GlyphManager(map._requestManager, options.localIdeographFontFamily, glyphLang);
14874		this.lineAtlas = new LineAtlas(256, 512);
14875		this.crossTileSymbolIndex = new CrossTileSymbolIndex();
14876		this._setInitialValues();
14877		this._resetUpdates();
14878		this.dispatcher.broadcast("SR", getReferrer());
14879		rtlMainThreadPluginFactory().on(RTLPluginLoadedEventName, this._rtlPluginLoaded);
14880		this.on("data", (event) => {
14881			if (event.dataType !== "source" || event.sourceDataType !== "metadata") return;
14882			const tileManager = this.tileManagers[event.sourceId];
14883			if (!tileManager) return;
14884			const source = tileManager.getSource();
14885			if (!source?.vectorLayerIds) return;
14886			for (const layerId in this._layers) {
14887				const layer = this._layers[layerId];
14888				if (layer.source === source.id) this._validateLayer(layer);
14889			}
14890		});
14891	}
14892	_setInitialValues() {
14893		this._layers = {};
14894		this._order = [];
14895		this.tileManagers = {};
14896		this.zoomHistory = new ZoomHistory();
14897		this._imagesListDirty = false;
14898		this._globalState = {};
14899		this._serializedLayers = {};
14900		this.stylesheet = null;
14901		this.light = null;
14902		this.sky = null;
14903		if (this.projection) {
14904			this.projection.destroy();
14905			delete this.projection;
14906		}
14907		this._loaded = false;
14908		this._changed = false;
14909		this._updatedLayers = {};
14910		this._updatedSources = {};
14911		this._changedImages = {};
14912		this._glyphsDidChange = false;
14913		this._updatedPaintProps = {};
14914		this._layerOrderChanged = false;
14915		this._symbolPlacementTriggered = false;
14916		this._placedProjectionTransition = void 0;
14917		this.crossTileSymbolIndex = new ((this.crossTileSymbolIndex?.constructor) || Object)();
14918		this.pauseablePlacement = void 0;
14919		this.placement = void 0;
14920		this.z = 0;
14921	}
14922	setGlobalStateProperty(name, value) {
14923		this._checkLoaded();
14924		const newValue = value === null ? this.stylesheet.state?.[name]?.default ?? null : value;
14925		this._setGlobalStateValues({ [name]: newValue });
14926		return this;
14927	}
14928	getGlobalState() {
14929		return this._globalState;
14930	}
14931	setGlobalState(newStylesheetState) {
14932		this._checkLoaded();
14933		const values = {};
14934		for (const propertyName in newStylesheetState) values[propertyName] = newStylesheetState[propertyName].default;
14935		this._setGlobalStateValues(values);
14936	}
14937	/** Sets the keys in `values` that differ, keeping a copy of the state from before so readers transition from it. */
14938	_setGlobalStateValues(values) {
14939		const changedGlobalStateRefs = [];
14940		for (const ref in values) if (!deepEqual(values[ref], this._globalState[ref])) changedGlobalStateRefs.push(ref);
14941		if (changedGlobalStateRefs.length === 0) return;
14942		const priorGlobalState = { ...this._globalState };
14943		for (const ref of changedGlobalStateRefs) this._globalState[ref] = values[ref];
14944		this._applyGlobalStateChanges(changedGlobalStateRefs, priorGlobalState);
14945	}
14946	/**
14947	* @internal
14948	* Find all sources that are affected by the global state changes and reload them.
14949	* Find all paint, light and sky properties that are affected and transition them from `priorGlobalState`, the state before the change.
14950	* For example, if a layer filter uses global-state expression, this function will find the source id of that layer.
14951	*/
14952	_applyGlobalStateChanges(globalStateRefs, priorGlobalState) {
14953		const sourceIdsToReload = /* @__PURE__ */ new Set();
14954		const globalStateChange = {};
14955		for (const ref of globalStateRefs) globalStateChange[ref] = this._globalState[ref];
14956		for (const layerId in this._layers) {
14957			const layer = this._layers[layerId];
14958			layer.retainPriorGlobalState(globalStateRefs, priorGlobalState);
14959			const layoutAffectingGlobalStateRefs = layer.getLayoutAffectingGlobalStateRefs();
14960			const paintAffectingGlobalStateRefs = layer.getPaintAffectingGlobalStateRefs();
14961			const visibilityAffectingGlobalStateRefs = layer.getVisibilityAffectingGlobalStateRefs();
14962			for (const ref of globalStateRefs) {
14963				if (layoutAffectingGlobalStateRefs.has(ref)) sourceIdsToReload.add(layer.source);
14964				if (paintAffectingGlobalStateRefs.has(ref)) for (const { name, value } of paintAffectingGlobalStateRefs.get(ref)) this._updatePaintProperty(layer, name, value, { validate: false });
14965				if (visibilityAffectingGlobalStateRefs?.has(ref)) {
14966					layer.recalculateVisibility();
14967					this._updateLayer(layer);
14968				}
14969			}
14970		}
14971		const parameters = {
14972			now: now(),
14973			transition: this.getTransition()
14974		};
14975		this.light?.applyGlobalStateChange(globalStateRefs, priorGlobalState, parameters);
14976		this.sky?.applyGlobalStateChange(globalStateRefs, priorGlobalState, parameters);
14977		this.dispatcher.broadcast("UGS", globalStateChange);
14978		for (const id in this.tileManagers) if (sourceIdsToReload.has(id)) {
14979			this._reloadSource(id);
14980			this._changed = true;
14981		}
14982	}
14983	async loadURL(url, options = {}, previousStyle) {
14984		this.fire(new MapStyleDataEvent("dataloading"));
14985		options.validate = typeof options.validate === "boolean" ? options.validate : true;
14986		this._loadStyleRequest = new AbortController();
14987		const abortController = this._loadStyleRequest;
14988		try {
14989			const request = await this.map._requestManager.transformRequest(url, "Style");
14990			throwIfAborted(abortController.signal);
14991			const response = await getJSON(request, abortController);
14992			if (this._loadStyleRequest === abortController) this._loadStyleRequest = null;
14993			this._load(response.data, options, previousStyle);
14994		} catch (error) {
14995			if (this._loadStyleRequest === abortController) this._loadStyleRequest = null;
14996			if (error && !abortController.signal.aborted) this.fire(new ErrorEvent(ensureError(error)));
14997		}
14998	}
14999	loadJSON(json, options = {}, previousStyle) {
15000		this.fire(new MapStyleDataEvent("dataloading"));
15001		this._frameRequest = new AbortController();
15002		browser.frameAsync(this._frameRequest, this.map._ownerWindow).then(() => {
15003			this._frameRequest = null;
15004			options.validate = options.validate !== false;
15005			this._load(json, options, previousStyle);
15006		}).catch(() => {});
15007	}
15008	loadEmpty() {
15009		this.fire(new MapStyleDataEvent("dataloading"));
15010		this._load(empty, { validate: false });
15011	}
15012	_load(json, options, previousStyle) {
15013		let nextState = options.transformStyle ? options.transformStyle(previousStyle, json) : json;
15014		if (options.validate && validateStyleAndEmit(this, nextState)) return;
15015		nextState = { ...nextState };
15016		this._loaded = true;
15017		this.stylesheet = nextState;
15018		for (const id in nextState.sources) this.addSource(id, nextState.sources[id], { validate: false });
15019		if (nextState.sprite) this._loadSprite(nextState.sprite);
15020		else this.imageManager.setLoaded(true);
15021		this.glyphManager.setURL(nextState.glyphs);
15022		this.glyphManager.setFontFaces(nextState["font-faces"]);
15023		this._createLayers();
15024		this.light = new Light(this.stylesheet.light ?? {}, this._globalState);
15025		this._setProjectionInternal(this.stylesheet.projection?.type || "mercator");
15026		this.sky = new Sky(this.stylesheet.sky, this._globalState);
15027		this.sky.setEventedParent(this);
15028		this.map.setTerrain(this.stylesheet.terrain ?? null, { validate: false });
15029		this.fire(new MapStyleDataEvent("data"));
15030		this.fire(new MapStyleLoadEvent());
15031	}
15032	_createLayers() {
15033		const dereferencedLayers = derefLayers(this.stylesheet.layers);
15034		this.setGlobalState(this.stylesheet.state ?? null);
15035		this.dispatcher.broadcast("SL", dereferencedLayers);
15036		this._order = dereferencedLayers.map((layer) => layer.id);
15037		this._layers = {};
15038		this._serializedLayers = null;
15039		for (const layer of dereferencedLayers) {
15040			const styledLayer = createStyleLayer(layer, this._globalState);
15041			styledLayer.setEventedParent(this, { layer: { id: layer.id } });
15042			this._layers[layer.id] = styledLayer;
15043			if (isRasterStyleLayer(styledLayer) && this.tileManagers[styledLayer.source]) {
15044				const rasterFadeDuration = layer.paint?.["raster-fade-duration"] ?? styledLayer.paint.get("raster-fade-duration");
15045				this.tileManagers[styledLayer.source].setRasterFadeDuration(rasterFadeDuration);
15046			}
15047		}
15048	}
15049	async _loadSprite(sprite, isUpdate = false, completion = void 0) {
15050		this.imageManager.setLoaded(false);
15051		const abortController = new AbortController();
15052		this._spriteRequest = abortController;
15053		let err;
15054		try {
15055			const images = await loadSprite(sprite, this.map._requestManager, this.map.getPixelRatio(), abortController);
15056			if (!images) return;
15057			for (const spriteId in images) {
15058				const { loaded, removed } = this.imageManager.setSpriteImages(spriteId, images[spriteId]);
15059				this._markImagesChanged(removed);
15060				if (isUpdate) this._markImagesChanged(loaded);
15061			}
15062		} catch (error) {
15063			err = error;
15064			if (!abortController.signal.aborted) this.fire(new ErrorEvent(err));
15065		} finally {
15066			this._spriteRequest = null;
15067			this.imageManager.setLoaded(true);
15068			if (isUpdate) this._changed = true;
15069			this.dispatcher.broadcast("SI", this.imageManager.listImages());
15070			this.fire(new MapStyleDataEvent("data"));
15071			completion?.(err);
15072		}
15073	}
15074	_unloadSprite() {
15075		this._markImagesChanged(this.imageManager.removeAllSpriteImages());
15076		this._imagesListDirty = true;
15077		this._changed = true;
15078		this.fire(new MapStyleDataEvent("data"));
15079	}
15080	_validateLayer(layer) {
15081		const tileManager = this.tileManagers[layer.source];
15082		if (!tileManager) return;
15083		const sourceLayer = layer.sourceLayer;
15084		if (!sourceLayer) return;
15085		const source = tileManager.getSource();
15086		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}".`)));
15087	}
15088	loaded() {
15089		if (!this._loaded) return false;
15090		if (Object.keys(this._updatedSources).length) return false;
15091		for (const id in this.tileManagers) if (!this.tileManagers[id].loaded()) return false;
15092		return this.imageManager.isLoaded();
15093	}
15094	/**
15095	* @hidden
15096	* take an array of string IDs, and based on this._layers, generate an array of LayerSpecification
15097	* @param ids - an array of string IDs, for which serialized layers will be generated. If omitted, all serialized layers will be returned
15098	* @param returnClone - if true, return a clone of the layer object
15099	* @returns generated result
15100	*/
15101	_serializeByIds(ids, returnClone = false) {
15102		const serializedLayersDictionary = this._serializedAllLayers();
15103		if (!ids || ids.length === 0) return returnClone ? Object.values(clone$2(serializedLayersDictionary)) : Object.values(serializedLayersDictionary);
15104		const serializedLayers = [];
15105		for (const id of ids) if (serializedLayersDictionary[id]) {
15106			const toPush = returnClone ? clone$2(serializedLayersDictionary[id]) : serializedLayersDictionary[id];
15107			serializedLayers.push(toPush);
15108		}
15109		return serializedLayers;
15110	}
15111	/**
15112	* @hidden
15113	* Lazy initialization of this._serializedLayers dictionary and return it
15114	* @returns this._serializedLayers dictionary
15115	*/
15116	_serializedAllLayers() {
15117		let serializedLayers = this._serializedLayers;
15118		if (serializedLayers) return serializedLayers;
15119		serializedLayers = this._serializedLayers = {};
15120		const allLayerIds = Object.keys(this._layers);
15121		for (const layerId of allLayerIds) {
15122			const layer = this._layers[layerId];
15123			if (layer.type !== "custom") serializedLayers[layerId] = layer.serialize();
15124		}
15125		return serializedLayers;
15126	}
15127	hasTransitions() {
15128		if (this.light?.hasTransition()) return true;
15129		if (this.sky?.hasTransition()) return true;
15130		if (this.projection?.hasTransition()) return true;
15131		for (const id in this.tileManagers) if (this.tileManagers[id].hasTransition()) return true;
15132		for (const id in this._layers) if (this._layers[id].hasTransition()) return true;
15133		return false;
15134	}
15135	_checkLoaded() {
15136		if (!this._loaded) throw new Error("Style is not done loading.");
15137	}
15138	/**
15139	* @internal
15140	* Apply queued style updates in a batch and recalculate zoom-dependent paint properties.
15141	*/
15142	update(parameters) {
15143		if (!this._loaded) return;
15144		const changed = this._changed;
15145		if (changed) {
15146			if (this._imagesListDirty) {
15147				this.dispatcher.broadcast("SI", this.imageManager.listImages());
15148				this._imagesListDirty = false;
15149			}
15150			const updatedIds = Object.keys(this._updatedLayers);
15151			const removedIds = Object.keys(this._removedLayers);
15152			if (updatedIds.length || removedIds.length) this._updateWorkerLayers(updatedIds, removedIds);
15153			for (const id in this._updatedSources) {
15154				const action = this._updatedSources[id];
15155				if (action === "reload") this._reloadSource(id);
15156				else if (action === "clear") this._clearSource(id);
15157				else throw new Error(`Invalid action ${action}`);
15158			}
15159			this._updateTilesForChangedImages();
15160			this._updateTilesForChangedGlyphs();
15161			for (const id in this._updatedPaintProps) this._layers[id].updateTransitions(parameters);
15162			this._resetUpdates();
15163		}
15164		const managersUsedBefore = {};
15165		for (const id in this.tileManagers) {
15166			const tileManager = this.tileManagers[id];
15167			managersUsedBefore[id] = tileManager.used;
15168			tileManager.used = false;
15169		}
15170		const availableImages = this.imageManager.listImages();
15171		for (const layerId of this._order) {
15172			const layer = this._layers[layerId];
15173			layer.recalculate(parameters, availableImages);
15174			if (!layer.isHidden(parameters.zoom) && layer.source) this.tileManagers[layer.source].used = true;
15175		}
15176		for (const id in managersUsedBefore) {
15177			const tileManager = this.tileManagers[id];
15178			if (!!managersUsedBefore[id] !== !!tileManager.used) tileManager.fire(new MapSourceDataEvent("data", {
15179				sourceDataType: "visibility",
15180				sourceId: id
15181			}));
15182		}
15183		this.light.recalculate(parameters);
15184		this.sky.recalculate(parameters);
15185		this.projection.recalculate(parameters);
15186		this.z = parameters.zoom;
15187		if (changed) this.fire(new MapStyleDataEvent("data"));
15188	}
15189	_updateTilesForChangedImages() {
15190		const changedImages = Object.keys(this._changedImages);
15191		if (changedImages.length) {
15192			for (const name in this.tileManagers) this.tileManagers[name].reloadTilesForDependencies(["icons", "patterns"], changedImages);
15193			this._changedImages = {};
15194		}
15195	}
15196	_updateTilesForChangedGlyphs() {
15197		if (this._glyphsDidChange) {
15198			for (const name in this.tileManagers) this.tileManagers[name].reloadTilesForDependencies(["glyphs"], [""]);
15199			this._glyphsDidChange = false;
15200		}
15201	}
15202	_updateWorkerLayers(updatedIds, removedIds) {
15203		this.dispatcher.broadcast("UL", {
15204			layers: this._serializeByIds(updatedIds, false),
15205			removedIds
15206		});
15207	}
15208	_resetUpdates() {
15209		this._changed = false;
15210		this._updatedLayers = {};
15211		this._removedLayers = {};
15212		this._updatedSources = {};
15213		this._updatedPaintProps = {};
15214		this._changedImages = {};
15215		this._glyphsDidChange = false;
15216	}
15217	/**
15218	* Update this style's state to match the given style JSON, performing only
15219	* the necessary mutations.
15220	*
15221	* May throw an Error ('Unimplemented: METHOD') if the maplibre-gl-style-spec
15222	* diff algorithm produces an operation that is not supported.
15223	*
15224	* @returns true if any changes were made; false otherwise
15225	*/
15226	setState(nextState, options = {}) {
15227		this._checkLoaded();
15228		const serializedStyle = this.serialize();
15229		nextState = options.transformStyle ? options.transformStyle(serializedStyle, nextState) : nextState;
15230		if ((options.validate ?? true) && validateStyleAndEmit(this, nextState)) return false;
15231		nextState = clone$2(nextState);
15232		nextState.layers = derefLayers(nextState.layers);
15233		const changes = diff(serializedStyle, nextState);
15234		const operations = this._getOperationsToPerform(changes);
15235		if (operations.unimplemented.length > 0) throw new Error(`Unimplemented: ${operations.unimplemented.join(", ")}.`);
15236		if (operations.operations.length === 0) return false;
15237		for (const styleChangeOperation of operations.operations) styleChangeOperation();
15238		this.stylesheet = nextState;
15239		this._serializedLayers = null;
15240		this.fire(new MapStyleLoadEvent({ style: this }));
15241		return true;
15242	}
15243	/**
15244	* Translates a style diff into the calls that apply it to this style.
15245	* @param diff - the operations produced by the style-spec diff algorithm
15246	* @returns the operations to run, and the names of the commands that are not supported
15247	*/
15248	_getOperationsToPerform(diff) {
15249		const operations = [];
15250		const unimplemented = [];
15251		for (const op of diff) switch (op.command) {
15252			case "setCenter":
15253			case "setZoom":
15254			case "setBearing":
15255			case "setPitch":
15256			case "setRoll": continue;
15257			case "addLayer": {
15258				const [layer, before] = op.args;
15259				operations.push(() => this.addLayer(layer, before));
15260				break;
15261			}
15262			case "removeLayer": {
15263				const [layerId] = op.args;
15264				operations.push(() => this.removeLayer(layerId));
15265				break;
15266			}
15267			case "setPaintProperty": {
15268				const [layerId, name, value] = op.args;
15269				operations.push(() => this.setPaintProperty(layerId, name, value));
15270				break;
15271			}
15272			case "setLayoutProperty": {
15273				const [layerId, name, value] = op.args;
15274				operations.push(() => this.setLayoutProperty(layerId, name, value));
15275				break;
15276			}
15277			case "setFilter": {
15278				const [layerId, filter] = op.args;
15279				operations.push(() => this.setFilter(layerId, filter));
15280				break;
15281			}
15282			case "addSource": {
15283				const [id, source] = op.args;
15284				operations.push(() => this.addSource(id, source));
15285				break;
15286			}
15287			case "removeSource": {
15288				const [id] = op.args;
15289				operations.push(() => this.removeSource(id));
15290				break;
15291			}
15292			case "setLayerZoomRange": {
15293				const [layerId, minzoom, maxzoom] = op.args;
15294				operations.push(() => this.setLayerZoomRange(layerId, minzoom, maxzoom));
15295				break;
15296			}
15297			case "setLight": {
15298				const [light] = op.args;
15299				operations.push(() => this.setLight(light));
15300				break;
15301			}
15302			case "setGeoJSONSourceData": {
15303				const [id, data] = op.args;
15304				operations.push(() => this.setGeoJSONSourceData(id, data));
15305				break;
15306			}
15307			case "setGlyphs": {
15308				const [glyphsUrl] = op.args;
15309				operations.push(() => this.setGlyphs(glyphsUrl));
15310				break;
15311			}
15312			case "setFontFaces": {
15313				const [fontFaces] = op.args;
15314				operations.push(() => this.setFontFaces(fontFaces));
15315				break;
15316			}
15317			case "setSprite": {
15318				const [sprite] = op.args;
15319				operations.push(() => this.setSprite(sprite));
15320				break;
15321			}
15322			case "setTerrain": {
15323				const [terrain] = op.args;
15324				operations.push(() => this.map.setTerrain(terrain));
15325				break;
15326			}
15327			case "setSky": {
15328				const [sky] = op.args;
15329				operations.push(() => this.setSky(sky));
15330				break;
15331			}
15332			case "setProjection": {
15333				const [projection] = op.args;
15334				operations.push(() => this.setProjection(projection));
15335				break;
15336			}
15337			case "setGlobalState": {
15338				const [state] = op.args;
15339				operations.push(() => this.setGlobalState(state));
15340				break;
15341			}
15342			case "setTransition":
15343				operations.push(() => {});
15344				break;
15345			default: unimplemented.push(op.command);
15346		}
15347		return {
15348			operations,
15349			unimplemented
15350		};
15351	}
15352	addImage(id, image) {
15353		if (this.getImage(id)) {
15354			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`An image named "${id}" already exists.`)));
15355			return;
15356		}
15357		this.imageManager.addImage(id, image);
15358		this._afterImageUpdated(id);
15359	}
15360	updateImage(id, image) {
15361		this.imageManager.updateImage(id, image);
15362	}
15363	getImage(id) {
15364		return this.imageManager.getImage(id);
15365	}
15366	setMissingImageResolver(resolver) {
15367		this.imageManager.setMissingImageResolver(resolver);
15368	}
15369	removeImage(id) {
15370		if (!this.getImage(id)) {
15371			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`An image named "${id}" does not exist.`)));
15372			return;
15373		}
15374		this.imageManager.removeImage(id);
15375		this._afterImageUpdated(id);
15376	}
15377	/**
15378	* Queues the tiles that depend on these images to be reloaded on the next update, which is
15379	* needed whenever an image appears, disappears or changes size.
15380	*/
15381	_markImagesChanged(ids) {
15382		for (const id of ids) this._changedImages[id] = true;
15383	}
15384	_afterImageUpdated(id) {
15385		this._changedImages[id] = true;
15386		this._imagesListDirty = true;
15387		this._changed = true;
15388		this.fire(new MapStyleDataEvent("data"));
15389	}
15390	listImages() {
15391		this._checkLoaded();
15392		return this.imageManager.listImages();
15393	}
15394	addSource(id, source, options = {}) {
15395		this._checkLoaded();
15396		if (this.tileManagers[id] !== void 0) throw new Error(`Source "${id}" already exists.`);
15397		if (!source.type) throw new Error(`The type property must be defined, but only the following properties were given: ${Object.keys(source).join(", ")}.`);
15398		if (SPEC_SOURCE_TYPES.has(source.type) && this._validate(validateStyle.source, `sources.${id}`, source, null, options)) return;
15399		if (this.map?._collectResourceTiming) source.collectResourceTiming = true;
15400		const tileManager = this.tileManagers[id] = new TileManager(id, source, this.dispatcher);
15401		tileManager.style = this;
15402		tileManager.setEventedParent(this, () => ({
15403			isSourceLoaded: tileManager.loaded(),
15404			source: tileManager.serialize(),
15405			sourceId: id
15406		}));
15407		tileManager.onAdd(this.map);
15408		this._changed = true;
15409	}
15410	/**
15411	* Remove a source from this stylesheet, given its id.
15412	* @param id - id of the source to remove
15413	* @throws if no source is found with the given ID
15414	*/
15415	removeSource(id) {
15416		this._checkLoaded();
15417		if (this.tileManagers[id] === void 0) throw new Error(`There is no source with this ID=${id}`);
15418		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.`)));
15419		const tileManager = this.tileManagers[id];
15420		delete this.tileManagers[id];
15421		delete this._updatedSources[id];
15422		tileManager.fire(new MapSourceDataEvent("data", {
15423			sourceDataType: "metadata",
15424			sourceId: id
15425		}));
15426		tileManager.setEventedParent(null);
15427		tileManager.onRemove(this.map);
15428		this._changed = true;
15429	}
15430	/**
15431	* Set the data of a GeoJSON source, given its id.
15432	* @param id - id of the source
15433	* @param data - GeoJSON source
15434	*/
15435	setGeoJSONSourceData(id, data) {
15436		this._checkLoaded();
15437		if (this.tileManagers[id] === void 0) throw new Error(`There is no source with this ID=${id}`);
15438		const geojsonSource = this.tileManagers[id].getSource();
15439		if (geojsonSource.type !== "geojson") throw new Error(`geojsonSource.type is ${geojsonSource.type}, which is !== 'geojson`);
15440		geojsonSource.setData(data);
15441		this._changed = true;
15442	}
15443	/**
15444	* Get a source by ID.
15445	* @param id - ID of the desired source
15446	* @returns source
15447	*/
15448	getSource(id) {
15449		return this.tileManagers[id]?.getSource();
15450	}
15451	/**
15452	* Add a layer to the map style. The layer will be inserted before the layer with
15453	* ID `before`, or appended if `before` is omitted.
15454	* @param layerObject - The style layer to add.
15455	* @param before - ID of an existing layer to insert before
15456	* @param options - Style setter options.
15457	*/
15458	addLayer(layerObject, before, options = {}) {
15459		this._checkLoaded();
15460		const id = layerObject.id;
15461		if (this.getLayer(id)) {
15462			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Layer "${id}" already exists on this map.`)));
15463			return;
15464		}
15465		let layer;
15466		if (layerObject.type === "custom") {
15467			if (emitValidationErrors(this, validateCustomStyleLayer(layerObject))) return;
15468			layer = createStyleLayer(layerObject, this._globalState);
15469		} else {
15470			if ("source" in layerObject && typeof layerObject.source === "object") {
15471				this.addSource(id, layerObject.source);
15472				layerObject = clone$2(layerObject);
15473				layerObject = extend(layerObject, { source: id });
15474			}
15475			if (this._validate(validateStyle.layer, `layers.${id}`, layerObject, { arrayIndex: -1 }, options)) return;
15476			layer = createStyleLayer(layerObject, this._globalState);
15477			this._validateLayer(layer);
15478			layer.setEventedParent(this, { layer: { id } });
15479		}
15480		const index = before ? this._order.indexOf(before) : this._order.length;
15481		if (before && index === -1) {
15482			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot add layer "${id}" before non-existing layer "${before}".`)));
15483			return;
15484		}
15485		this._order.splice(index, 0, id);
15486		this._layerOrderChanged = true;
15487		this._layers[id] = layer;
15488		if (this._removedLayers[id] && layer.source && layer.type !== "custom") {
15489			const removed = this._removedLayers[id];
15490			delete this._removedLayers[id];
15491			if (removed.type !== layer.type) this._updatedSources[layer.source] = "clear";
15492			else {
15493				this._updatedSources[layer.source] = "reload";
15494				this.tileManagers[layer.source].pause();
15495			}
15496		}
15497		this._updateLayer(layer);
15498		if (layer.onAdd) layer.onAdd(this.map);
15499	}
15500	/**
15501	* Moves a layer to a different z-position. The layer will be inserted before the layer with
15502	* ID `before`, or appended if `before` is omitted.
15503	* @param id - ID of the layer to move
15504	* @param before - ID of an existing layer to insert before
15505	*/
15506	moveLayer(id, before) {
15507		this._checkLoaded();
15508		this._changed = true;
15509		if (!this._layers[id]) {
15510			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`The layer '${id}' does not exist in the map's style and cannot be moved.`)));
15511			return;
15512		}
15513		if (id === before) return;
15514		if (before && !this._order.includes(before)) {
15515			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot move layer "${id}" before non-existing layer "${before}".`)));
15516			return;
15517		}
15518		const index = this._order.indexOf(id);
15519		this._order.splice(index, 1);
15520		const newIndex = before ? this._order.indexOf(before) : this._order.length;
15521		this._order.splice(newIndex, 0, id);
15522		this._layerOrderChanged = true;
15523	}
15524	/**
15525	* Remove the layer with the given id from the style.
15526	* A {@link ErrorEvent} event will be fired if no such layer exists.
15527	*
15528	* @param id - id of the layer to remove
15529	*/
15530	removeLayer(id) {
15531		this._checkLoaded();
15532		const layer = this._layers[id];
15533		if (!layer) {
15534			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot remove non-existing layer "${id}".`)));
15535			return;
15536		}
15537		layer.setEventedParent(null);
15538		const index = this._order.indexOf(id);
15539		this._order.splice(index, 1);
15540		this._layerOrderChanged = true;
15541		this._changed = true;
15542		this._removedLayers[id] = layer;
15543		delete this._layers[id];
15544		if (this._serializedLayers) delete this._serializedLayers[id];
15545		delete this._updatedLayers[id];
15546		delete this._updatedPaintProps[id];
15547		if (layer.type === "symbol" && layer.source) this.tileManagers[layer.source]?.resetMaxContentElevation();
15548		if (layer.onRemove) layer.onRemove(this.map);
15549	}
15550	/**
15551	* Return the style layer object with the given `id`.
15552	*
15553	* @param id - id of the desired layer
15554	* @returns a layer, if one with the given `id` exists
15555	*/
15556	getLayer(id) {
15557		return this._layers[id];
15558	}
15559	/**
15560	* Return the ids of all layers currently in the style, including custom layers, in order.
15561	*
15562	* @returns ids of layers, in order
15563	*/
15564	getLayersOrder() {
15565		return [...this._order];
15566	}
15567	/**
15568	* Checks if a specific layer is present within the style.
15569	*
15570	* @param id - the id of the desired layer
15571	* @returns a boolean specifying if the given layer is present
15572	*/
15573	hasLayer(id) {
15574		return id in this._layers;
15575	}
15576	setLayerZoomRange(layerId, minzoom, maxzoom) {
15577		this._checkLoaded();
15578		const layer = this.getLayer(layerId);
15579		if (!layer) {
15580			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot set the zoom range of non-existing layer "${layerId}".`)));
15581			return;
15582		}
15583		if (layer.minzoom === minzoom && layer.maxzoom === maxzoom) return;
15584		if (minzoom != null) layer.minzoom = minzoom;
15585		if (maxzoom != null) layer.maxzoom = maxzoom;
15586		this._updateLayer(layer);
15587	}
15588	setFilter(layerId, filter, options = {}) {
15589		this._checkLoaded();
15590		const layer = this.getLayer(layerId);
15591		if (!layer) {
15592			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot filter non-existing layer "${layerId}".`)));
15593			return;
15594		}
15595		if (deepEqual(layer.filter, filter)) return;
15596		if (filter === null || filter === void 0) {
15597			layer.setFilter(void 0);
15598			this._updateLayer(layer);
15599			return;
15600		}
15601		if (this._validate(validateStyle.filter, `layers.${layer.id}.filter`, filter, null, options)) return;
15602		layer.setFilter(clone$2(filter));
15603		this._updateLayer(layer);
15604	}
15605	/**
15606	* Get a layer's filter object
15607	* @param layer - the layer to inspect
15608	* @returns the layer's filter, if any
15609	*/
15610	getFilter(layer) {
15611		return clone$2(this.getLayer(layer).filter);
15612	}
15613	setLayoutProperty(layerId, name, value, options = {}) {
15614		this._checkLoaded();
15615		const layer = this.getLayer(layerId);
15616		if (!layer) {
15617			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot style non-existing layer "${layerId}".`)));
15618			return;
15619		}
15620		if (deepEqual(layer.getLayoutProperty(name), value)) return;
15621		layer.setLayoutProperty(name, value, options);
15622		this._updateLayer(layer);
15623	}
15624	/**
15625	* Get a layout property's value from a given layer
15626	* @param layerId - the layer to inspect
15627	* @param name - the name of the layout property
15628	* @returns the property value
15629	*/
15630	getLayoutProperty(layerId, name) {
15631		const layer = this.getLayer(layerId);
15632		if (!layer) {
15633			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot get style of non-existing layer "${layerId}".`)));
15634			return;
15635		}
15636		return layer.getLayoutProperty(name);
15637	}
15638	setPaintProperty(layerId, name, value, options = {}) {
15639		this._checkLoaded();
15640		const layer = this.getLayer(layerId);
15641		if (!layer) {
15642			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Cannot style non-existing layer "${layerId}".`)));
15643			return;
15644		}
15645		if (deepEqual(layer.getPaintProperty(name), value)) return;
15646		this._updatePaintProperty(layer, name, value, options);
15647	}
15648	_updatePaintProperty(layer, name, value, options = {}) {
15649		if (layer.setPaintProperty(name, value, options)) this._updateLayer(layer);
15650		if (isRasterStyleLayer(layer) && name === "raster-fade-duration") this.tileManagers[layer.source].setRasterFadeDuration(value);
15651		this._changed = true;
15652		this._updatedPaintProps[layer.id] = true;
15653		if (layer.type === "symbol") this.triggerSymbolPlacement();
15654		this._serializedLayers = null;
15655	}
15656	getPaintProperty(layer, name) {
15657		return this.getLayer(layer).getPaintProperty(name);
15658	}
15659	setFeatureState(target, state) {
15660		this._checkLoaded();
15661		const sourceId = target.source;
15662		const sourceLayer = target.sourceLayer;
15663		const tileManager = this.tileManagers[sourceId];
15664		if (tileManager === void 0) {
15665			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`The source '${sourceId}' does not exist in the map's style.`)));
15666			return;
15667		}
15668		const sourceType = tileManager.getSource().type;
15669		if (sourceType === "geojson" && sourceLayer) {
15670			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("GeoJSON sources cannot have a sourceLayer parameter.")));
15671			return;
15672		}
15673		if (sourceType === "vector" && !sourceLayer) {
15674			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("The sourceLayer parameter must be provided for vector source types.")));
15675			return;
15676		}
15677		if (target.id === void 0) {
15678			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("The feature id parameter must be provided.")));
15679			return;
15680		}
15681		const forbiddenStateKeys = [
15682			"__proto__",
15683			"constructor",
15684			"prototype"
15685		];
15686		if (state && Object.keys(state).some((stateKey) => forbiddenStateKeys.includes(stateKey))) {
15687			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`The feature state should not include one of the following keys: ${forbiddenStateKeys}`)));
15688			return;
15689		}
15690		tileManager.setFeatureState(sourceLayer, target.id, state);
15691	}
15692	removeFeatureState(target, key) {
15693		this._checkLoaded();
15694		const sourceId = target.source;
15695		const tileManager = this.tileManagers[sourceId];
15696		if (tileManager === void 0) {
15697			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`The source '${sourceId}' does not exist in the map's style.`)));
15698			return;
15699		}
15700		const sourceType = tileManager.getSource().type;
15701		const sourceLayer = sourceType === "vector" ? target.sourceLayer : void 0;
15702		if (sourceType === "vector" && !sourceLayer) {
15703			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("The sourceLayer parameter must be provided for vector source types.")));
15704			return;
15705		}
15706		if (key && typeof target.id !== "string" && typeof target.id !== "number") {
15707			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("A feature id is required to remove its specific state property.")));
15708			return;
15709		}
15710		tileManager.removeFeatureState(sourceLayer, target.id, key);
15711	}
15712	getFeatureState(target) {
15713		this._checkLoaded();
15714		const sourceId = target.source;
15715		const sourceLayer = target.sourceLayer;
15716		const tileManager = this.tileManagers[sourceId];
15717		if (tileManager === void 0) {
15718			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`The source '${sourceId}' does not exist in the map's style.`)));
15719			return;
15720		}
15721		if (tileManager.getSource().type === "vector" && !sourceLayer) {
15722			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("The sourceLayer parameter must be provided for vector source types.")));
15723			return;
15724		}
15725		if (target.id === void 0) this.fire(new ErrorEvent(/* @__PURE__ */ new Error("The feature id parameter must be provided.")));
15726		return tileManager.getFeatureState(sourceLayer, target.id);
15727	}
15728	getTransition() {
15729		return extend({
15730			duration: 300,
15731			delay: 0
15732		}, this.stylesheet?.transition);
15733	}
15734	serialize() {
15735		if (!this._loaded) return;
15736		const sources = mapObject(this.tileManagers, (source) => source.serialize());
15737		const layers = this._serializeByIds(this._order, true);
15738		const terrain = this.map.getTerrain() || void 0;
15739		const myStyleSheet = this.stylesheet;
15740		return filterObject({
15741			version: myStyleSheet.version,
15742			name: myStyleSheet.name,
15743			metadata: myStyleSheet.metadata,
15744			light: myStyleSheet.light,
15745			sky: myStyleSheet.sky,
15746			center: myStyleSheet.center,
15747			zoom: myStyleSheet.zoom,
15748			bearing: myStyleSheet.bearing,
15749			pitch: myStyleSheet.pitch,
15750			sprite: myStyleSheet.sprite,
15751			glyphs: myStyleSheet.glyphs,
15752			"font-faces": myStyleSheet["font-faces"],
15753			transition: myStyleSheet.transition,
15754			projection: myStyleSheet.projection,
15755			state: myStyleSheet.state,
15756			sources,
15757			layers,
15758			terrain
15759		}, (value) => value !== void 0);
15760	}
15761	_updateLayer(layer) {
15762		this._updatedLayers[layer.id] = true;
15763		if (layer.source && !this._updatedSources[layer.source] && this.tileManagers[layer.source].getSource().type !== "raster") {
15764			this._updatedSources[layer.source] = "reload";
15765			this.tileManagers[layer.source].pause();
15766		}
15767		this._serializedLayers = null;
15768		this._changed = true;
15769	}
15770	_flattenAndSortRenderedFeatures(sourceResults) {
15771		const isLayer3D = (layerId) => this._layers[layerId].type === "fill-extrusion";
15772		const layerIndex = {};
15773		const features3D = [];
15774		for (let l = this._order.length - 1; l >= 0; l--) {
15775			const layerId = this._order[l];
15776			if (isLayer3D(layerId)) {
15777				layerIndex[layerId] = l;
15778				for (const sourceResult of sourceResults) {
15779					const layerFeatures = sourceResult[layerId];
15780					if (layerFeatures) for (const featureWrapper of layerFeatures) features3D.push(featureWrapper);
15781				}
15782			}
15783		}
15784		features3D.sort((a, b) => {
15785			return b.intersectionZ - a.intersectionZ;
15786		});
15787		const features = [];
15788		for (let l = this._order.length - 1; l >= 0; l--) {
15789			const layerId = this._order[l];
15790			if (isLayer3D(layerId)) for (let i = features3D.length - 1; i >= 0; i--) {
15791				const topmost3D = features3D[i].feature;
15792				if (layerIndex[topmost3D.layer.id] < l) break;
15793				features.push(topmost3D);
15794				features3D.pop();
15795			}
15796			else for (const sourceResult of sourceResults) {
15797				const layerFeatures = sourceResult[layerId];
15798				if (layerFeatures) for (const featureWrapper of layerFeatures) features.push(featureWrapper.feature);
15799			}
15800		}
15801		return features;
15802	}
15803	queryRenderedFeatures(queryGeometry, params, transform) {
15804		if (params?.filter) this._validate(validateStyle.filter, "queryRenderedFeatures.filter", params.filter, null, params);
15805		const includedSources = {};
15806		if (params?.layers) {
15807			if (!(Array.isArray(params.layers) || params.layers instanceof Set)) {
15808				this.fire(new ErrorEvent(/* @__PURE__ */ new Error("parameters.layers must be an Array or a Set of strings")));
15809				return [];
15810			}
15811			for (const layerId of params.layers) {
15812				const layer = this._layers[layerId];
15813				if (!layer) {
15814					this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`The layer '${layerId}' does not exist in the map's style and cannot be queried for features.`)));
15815					return [];
15816				}
15817				includedSources[layer.source] = true;
15818			}
15819		}
15820		const sourceResults = [];
15821		params.availableImages = this.imageManager.listImages();
15822		const serializedLayers = this._serializedAllLayers();
15823		const layersAsSet = params.layers instanceof Set ? params.layers : Array.isArray(params.layers) ? new Set(params.layers) : null;
15824		const paramsStrict = {
15825			...params,
15826			layers: layersAsSet,
15827			globalState: this._globalState
15828		};
15829		for (const id in this.tileManagers) {
15830			if (params.layers && !includedSources[id]) continue;
15831			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));
15832		}
15833		if (this.placement) sourceResults.push(queryRenderedSymbols(this._layers, serializedLayers, this.tileManagers, queryGeometry, paramsStrict, this.placement.collisionIndex, this.placement.retainedQueryData));
15834		return this._flattenAndSortRenderedFeatures(sourceResults);
15835	}
15836	querySourceFeatures(sourceID, params) {
15837		if (params?.filter) this._validate(validateStyle.filter, "querySourceFeatures.filter", params.filter, null, params);
15838		const tileManager = this.tileManagers[sourceID];
15839		return tileManager ? querySourceFeatures(tileManager, params ? {
15840			...params,
15841			globalState: this._globalState
15842		} : { globalState: this._globalState }) : [];
15843	}
15844	getLight() {
15845		return this.light.getLight();
15846	}
15847	setLight(lightOptions, options = {}) {
15848		this._checkLoaded();
15849		const light = this.light.getLight();
15850		let _update = false;
15851		for (const key in lightOptions) if (!deepEqual(lightOptions[key], light[key])) {
15852			_update = true;
15853			break;
15854		}
15855		if (!_update) return;
15856		const parameters = {
15857			now: now(),
15858			transition: extend({
15859				duration: 300,
15860				delay: 0
15861			}, this.stylesheet.transition)
15862		};
15863		this.light.setLight(lightOptions, options);
15864		this.light.updateTransitions(parameters);
15865	}
15866	getProjection() {
15867		return this.stylesheet?.projection;
15868	}
15869	setProjection(projection) {
15870		this._checkLoaded();
15871		const resolvedProjection = projection ?? { type: "mercator" };
15872		this.stylesheet.projection = projection;
15873		if (this.projection) {
15874			if (this.projection.name === resolvedProjection.type) return;
15875			this.projection.destroy();
15876			delete this.projection;
15877		}
15878		this._setProjectionInternal(resolvedProjection.type);
15879	}
15880	getSky() {
15881		return this.stylesheet?.sky;
15882	}
15883	setSky(skyOptions, options = {}) {
15884		this._checkLoaded();
15885		const sky = this.getSky();
15886		let update = false;
15887		if (!skyOptions && !sky) return;
15888		if (skyOptions && !sky) update = true;
15889		else if (!skyOptions && sky) update = true;
15890		else for (const key in skyOptions) if (!deepEqual(skyOptions[key], sky[key])) {
15891			update = true;
15892			break;
15893		}
15894		if (!update) return;
15895		const parameters = {
15896			now: now(),
15897			transition: extend({
15898				duration: 300,
15899				delay: 0
15900			}, this.stylesheet.transition)
15901		};
15902		if (!this.sky.setSky(skyOptions, options)) return;
15903		this.stylesheet.sky = skyOptions;
15904		this.sky.updateTransitions(parameters);
15905	}
15906	_setProjectionInternal(name) {
15907		const projectionObjects = createProjectionFromName(name, this.map._camera?.transform.constrainOverride, this._globalState);
15908		this.projection = projectionObjects.projection;
15909		this.map.migrateProjection(projectionObjects.transform, projectionObjects.cameraHelper);
15910		for (const key in this.tileManagers) this.tileManagers[key].reload();
15911	}
15912	_validate(validate, key, value, props, options = {}) {
15913		if (options.validate === false) return false;
15914		return validateAndEmit(this, validate, {
15915			key,
15916			style: this.serialize(),
15917			value,
15918			...props
15919		}, options);
15920	}
15921	_remove(mapRemoved = true) {
15922		if (this._frameRequest) {
15923			this._frameRequest.abort();
15924			this._frameRequest = null;
15925		}
15926		if (this._loadStyleRequest) {
15927			this._loadStyleRequest.abort();
15928			this._loadStyleRequest = null;
15929		}
15930		if (this._spriteRequest) {
15931			this._spriteRequest.abort();
15932			this._spriteRequest = null;
15933		}
15934		rtlMainThreadPluginFactory().off(RTLPluginLoadedEventName, this._rtlPluginLoaded);
15935		for (const layerId in this._layers) this._layers[layerId].setEventedParent(null);
15936		for (const id in this.tileManagers) {
15937			const tileManager = this.tileManagers[id];
15938			tileManager.setEventedParent(null);
15939			tileManager.onRemove(this.map);
15940		}
15941		this.imageManager.setEventedParent(null);
15942		this.setEventedParent(null);
15943		if (mapRemoved) this.dispatcher.broadcast("RM", void 0);
15944		this.dispatcher.remove(mapRemoved);
15945	}
15946	_clearSource(id) {
15947		this.tileManagers[id].clearTiles();
15948	}
15949	_reloadSource(id) {
15950		this.tileManagers[id].resume();
15951		this.tileManagers[id].reload();
15952	}
15953	_updateSources(transform) {
15954		for (const id in this.tileManagers) this.tileManagers[id].update(transform, this.map.terrain);
15955	}
15956	_generateCollisionBoxes() {
15957		for (const id in this.tileManagers) this._reloadSource(id);
15958	}
15959	/**
15960	* Re-places symbols on the next frame.
15961	* Placement is skipped while its inputs look unchanged, so call this when something
15962	* the map cannot see for itself has moved symbols.
15963	*/
15964	triggerSymbolPlacement() {
15965		this._symbolPlacementTriggered = true;
15966	}
15967	/**
15968	* Whether re-running symbol placement could produce a different result than the last run.
15969	* Every camera and viewport parameter placement can observe perturbs the model-view-projection
15970	* matrix, so comparing the matrix covers them all. Inputs the matrix cannot see arrive as
15971	* {@link triggerSymbolPlacement} calls.
15972	*
15973	* @internal
15974	*/
15975	_placementInputsChanged(transform, showCollisionBoxes, crossSourceCollisions) {
15976		const lastPlacement = this.pauseablePlacement;
15977		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);
15978	}
15979	_updatePlacement(transform, showCollisionBoxes, fadeDuration, crossSourceCollisions, forceFullPlacement = false) {
15980		let symbolBucketsChanged = false;
15981		let placementCommitted = false;
15982		const layerTiles = {};
15983		for (const layerID of this._order) {
15984			const styleLayer = this._layers[layerID];
15985			if (styleLayer.type !== "symbol") continue;
15986			if (!layerTiles[styleLayer.source]) {
15987				const tileManager = this.tileManagers[styleLayer.source];
15988				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));
15989			}
15990			const layerBucketsChanged = this.crossTileSymbolIndex.addLayer(styleLayer, layerTiles[styleLayer.source], transform.center.lng);
15991			symbolBucketsChanged ||= layerBucketsChanged;
15992		}
15993		this.crossTileSymbolIndex.pruneUnusedLayers(this._order);
15994		forceFullPlacement ||= this._layerOrderChanged || fadeDuration === 0;
15995		const placementInputsChanged = symbolBucketsChanged || this._placementInputsChanged(transform, showCollisionBoxes, crossSourceCollisions);
15996		const placementSettled = this.pauseablePlacement?.isDone() && !this.placement.stillRecent(now(), transform.zoom);
15997		if (forceFullPlacement || !this.pauseablePlacement || placementSettled && (placementInputsChanged || this.placement.stale)) {
15998			this._symbolPlacementTriggered = false;
15999			this._placedProjectionTransition = this.projection?.transitionState;
16000			this.pauseablePlacement = new PauseablePlacement(transform, this.map.terrain, this._order, forceFullPlacement, showCollisionBoxes, fadeDuration, crossSourceCollisions, this.placement);
16001			this._layerOrderChanged = false;
16002		}
16003		if (this.pauseablePlacement.isDone()) {
16004			if (placementInputsChanged) this.placement.setStale();
16005		} else {
16006			this.pauseablePlacement.continuePlacement(this._order, this._layers, layerTiles);
16007			if (this.pauseablePlacement.isDone()) {
16008				this.placement = this.pauseablePlacement.commit(now());
16009				placementCommitted = true;
16010			}
16011			if (symbolBucketsChanged) this.pauseablePlacement.placement.setStale();
16012		}
16013		if (placementCommitted || symbolBucketsChanged) for (const layerID of this._order) {
16014			const styleLayer = this._layers[layerID];
16015			if (styleLayer.type !== "symbol") continue;
16016			this.placement.updateLayerOpacities(styleLayer, layerTiles[styleLayer.source]);
16017		}
16018		return !this.pauseablePlacement.isDone() || this.placement.hasTransitions(now());
16019	}
16020	_releaseSymbolFadeTiles() {
16021		for (const id in this.tileManagers) this.tileManagers[id].releaseSymbolFadeTiles();
16022	}
16023	async getImages(mapId, params) {
16024		const images = await this.imageManager.getImages(params.icons);
16025		this._updateTilesForChangedImages();
16026		const tileManager = this.tileManagers[params.source];
16027		if (tileManager) tileManager.setDependencies(params.tileID.key, params.type, params.icons);
16028		return images;
16029	}
16030	async getGlyphs(mapId, params) {
16031		const glyphs = await this.glyphManager.getGlyphs(params.stacks);
16032		const tileManager = this.tileManagers[params.source];
16033		if (tileManager) tileManager.setDependencies(params.tileID.key, params.type, [""]);
16034		return glyphs;
16035	}
16036	getGlyphsUrl() {
16037		return this.stylesheet.glyphs || null;
16038	}
16039	setGlyphs(glyphsUrl, options = {}) {
16040		this._checkLoaded();
16041		if (glyphsUrl && this._validate(validateStyle.glyphs, "glyphs", glyphsUrl, null, options)) return;
16042		this._changed = true;
16043		this._glyphsDidChange = true;
16044		this.stylesheet.glyphs = glyphsUrl;
16045		this.glyphManager.entries = {};
16046		this.glyphManager.setURL(glyphsUrl);
16047	}
16048	getFontFaces() {
16049		return this.stylesheet["font-faces"] || null;
16050	}
16051	setFontFaces(fontFaces) {
16052		this._checkLoaded();
16053		this._changed = true;
16054		this._glyphsDidChange = true;
16055		this.stylesheet["font-faces"] = fontFaces;
16056		this.glyphManager.setFontFaces(fontFaces);
16057	}
16058	async getDashes(mapId, params) {
16059		const result = {};
16060		for (const [key, dash] of Object.entries(params.dashes)) result[key] = this.lineAtlas.getDash(dash.dasharray, dash.round);
16061		return result;
16062	}
16063	/**
16064	* Add a sprite.
16065	*
16066	* @param id - The id of the desired sprite
16067	* @param url - The url to load the desired sprite from
16068	* @param options - The style setter options
16069	* @param completion - The completion handler
16070	*/
16071	addSprite(id, url, options = {}, completion) {
16072		this._checkLoaded();
16073		const spriteToAdd = [{
16074			id,
16075			url
16076		}];
16077		const updatedSprite = [...coerceSpriteToArray(this.stylesheet.sprite), ...spriteToAdd];
16078		if (this._validate(validateStyle.sprite, "sprite", updatedSprite, null, options)) return;
16079		this.stylesheet.sprite = updatedSprite;
16080		this._loadSprite(spriteToAdd, true, completion);
16081	}
16082	/**
16083	* Remove a sprite by its id. When the last sprite is removed, the whole `this.stylesheet.sprite` object becomes
16084	* `undefined`. This falsy `undefined` value later prevents attempts to load the sprite when it's absent.
16085	*
16086	* @param id - the id of the sprite to remove
16087	*/
16088	removeSprite(id) {
16089		this._checkLoaded();
16090		const internalSpriteRepresentation = coerceSpriteToArray(this.stylesheet.sprite);
16091		if (!internalSpriteRepresentation.find((sprite) => sprite.id === id)) {
16092			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`Sprite "${id}" doesn't exists on this map.`)));
16093			return;
16094		}
16095		const changedImages = this.imageManager.removeSpriteImages(id);
16096		this._markImagesChanged(changedImages);
16097		internalSpriteRepresentation.splice(internalSpriteRepresentation.findIndex((sprite) => sprite.id === id), 1);
16098		this.stylesheet.sprite = internalSpriteRepresentation.length > 0 ? internalSpriteRepresentation : void 0;
16099		this._imagesListDirty = true;
16100		this._changed = true;
16101		this.fire(new MapStyleDataEvent("data"));
16102	}
16103	/**
16104	* Get the current sprite value.
16105	*
16106	* @returns empty array when no sprite is set; id-url pairs otherwise
16107	*/
16108	getSprite() {
16109		return coerceSpriteToArray(this.stylesheet.sprite);
16110	}
16111	/**
16112	* Set a new value for the style's sprite.
16113	*
16114	* @param sprite - new sprite value
16115	* @param options - style setter options
16116	* @param completion - the completion handler
16117	*/
16118	setSprite(sprite, options = {}, completion) {
16119		this._checkLoaded();
16120		if (sprite && this._validate(validateStyle.sprite, "sprite", sprite, null, options)) return;
16121		this.stylesheet.sprite = sprite;
16122		if (sprite) this._loadSprite(sprite, true, completion);
16123		else {
16124			this._unloadSprite();
16125			if (completion) completion(null);
16126		}
16127	}
16128	/**
16129	* Destroys all internal resources of the style (sources, images, layers, etc.)
16130	*/
16131	destroy() {
16132		if (this._frameRequest) {
16133			this._frameRequest.abort();
16134			this._frameRequest = null;
16135		}
16136		if (this._loadStyleRequest) {
16137			this._loadStyleRequest.abort();
16138			this._loadStyleRequest = null;
16139		}
16140		if (this._spriteRequest) {
16141			this._spriteRequest.abort();
16142			this._spriteRequest = null;
16143		}
16144		for (const id in this.tileManagers) {
16145			const tileManager = this.tileManagers[id];
16146			tileManager.setEventedParent(null);
16147			tileManager.onRemove(this.map);
16148		}
16149		this.tileManagers = {};
16150		if (this.imageManager) {
16151			this.imageManager.setEventedParent(null);
16152			this.imageManager.destroy();
16153			this.patternAtlas.destroy();
16154		}
16155		if (this.glyphManager) this.glyphManager.destroy();
16156		for (const layerId in this._layers) {
16157			const layer = this._layers[layerId];
16158			layer.setEventedParent(null);
16159			if (layer.onRemove) layer.onRemove(this.map);
16160		}
16161		this._setInitialValues();
16162		this.setEventedParent(null);
16163		this.dispatcher.unregisterMessageHandler("GG");
16164		this.dispatcher.unregisterMessageHandler("GI");
16165		this.dispatcher.unregisterMessageHandler("GDA");
16166		this.dispatcher.remove(true);
16167		this._listeners = {};
16168		this._oneTimeListeners = {};
16169	}
16170};
16171//#endregion
16172//#region src/data/raster_bounds_attributes.ts
16173const rasterBoundsAttributes = createLayout([{
16174	name: "a_pos",
16175	type: "Int16",
16176	components: 2
16177}, {
16178	name: "a_texture_pos",
16179	type: "Int16",
16180	components: 2
16181}]);
16182//#endregion
16183//#region src/webgl/vertex_array_object.ts
16184/**
16185* @internal
16186* A vertex array object used to pass data to the webgl code
16187*/
16188var VertexArrayObject = class {
16189	constructor() {
16190		this.boundProgram = null;
16191		this.boundLayoutVertexBuffer = null;
16192		this.boundPaintVertexBuffers = [];
16193		this.boundIndexBuffer = null;
16194		this.boundVertexOffset = null;
16195		this.boundDynamicVertexBuffer = null;
16196		this.vao = null;
16197	}
16198	bind(context, program, layoutVertexBuffer, paintVertexBuffers, indexBuffer, vertexOffset, dynamicVertexBuffer, dynamicVertexBuffer2, dynamicVertexBuffer3) {
16199		this.context = context;
16200		let paintBuffersDiffer = this.boundPaintVertexBuffers.length !== paintVertexBuffers.length;
16201		for (let i = 0; !paintBuffersDiffer && i < paintVertexBuffers.length; i++) if (this.boundPaintVertexBuffers[i] !== paintVertexBuffers[i]) paintBuffersDiffer = true;
16202		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);
16203		else context.bindVertexArray.set(this.vao);
16204	}
16205	freshBind(program, layoutVertexBuffer, paintVertexBuffers, indexBuffer, vertexOffset, dynamicVertexBuffer, dynamicVertexBuffer2, dynamicVertexBuffer3) {
16206		const numNextAttributes = program.numAttributes;
16207		const context = this.context;
16208		const gl = context.gl;
16209		if (this.vao) this.destroy();
16210		this.vao = context.createVertexArray();
16211		context.bindVertexArray.set(this.vao);
16212		this.boundProgram = program;
16213		this.boundLayoutVertexBuffer = layoutVertexBuffer;
16214		this.boundPaintVertexBuffers = paintVertexBuffers;
16215		this.boundIndexBuffer = indexBuffer;
16216		this.boundVertexOffset = vertexOffset;
16217		this.boundDynamicVertexBuffer = dynamicVertexBuffer;
16218		this.boundDynamicVertexBuffer2 = dynamicVertexBuffer2;
16219		this.boundDynamicVertexBuffer3 = dynamicVertexBuffer3;
16220		layoutVertexBuffer.enableAttributes(gl, program);
16221		for (const vertexBuffer of paintVertexBuffers) vertexBuffer.enableAttributes(gl, program);
16222		if (dynamicVertexBuffer) dynamicVertexBuffer.enableAttributes(gl, program);
16223		if (dynamicVertexBuffer2) dynamicVertexBuffer2.enableAttributes(gl, program);
16224		if (dynamicVertexBuffer3) dynamicVertexBuffer3.enableAttributes(gl, program);
16225		layoutVertexBuffer.bind();
16226		layoutVertexBuffer.setVertexAttribPointers(gl, program, vertexOffset);
16227		for (const vertexBuffer of paintVertexBuffers) {
16228			vertexBuffer.bind();
16229			vertexBuffer.setVertexAttribPointers(gl, program, vertexOffset);
16230		}
16231		if (dynamicVertexBuffer) {
16232			dynamicVertexBuffer.bind();
16233			dynamicVertexBuffer.setVertexAttribPointers(gl, program, vertexOffset);
16234		}
16235		if (indexBuffer) indexBuffer.bind();
16236		if (dynamicVertexBuffer2) {
16237			dynamicVertexBuffer2.bind();
16238			dynamicVertexBuffer2.setVertexAttribPointers(gl, program, vertexOffset);
16239		}
16240		if (dynamicVertexBuffer3) {
16241			dynamicVertexBuffer3.bind();
16242			dynamicVertexBuffer3.setVertexAttribPointers(gl, program, vertexOffset);
16243		}
16244		context.currentNumAttributes = numNextAttributes;
16245	}
16246	destroy() {
16247		if (this.vao) {
16248			this.context.deleteVertexArray(this.vao);
16249			this.vao = null;
16250		}
16251	}
16252};
16253//#endregion
16254//#region src/webgl/program/terrain_program.ts
16255const terrainPreludeUniforms = (context, locations) => ({
16256	"u_depth": new Uniform1i(context, locations.u_depth),
16257	"u_terrain": new Uniform1i(context, locations.u_terrain)
16258});
16259const terrainUniforms = (context, locations) => ({
16260	"u_texture": new Uniform1i(context, locations.u_texture),
16261	"u_ele_delta": new Uniform1f(context, locations.u_ele_delta),
16262	"u_fog_matrix": new UniformMatrix4f(context, locations.u_fog_matrix),
16263	"u_fog_color": new UniformColor(context, locations.u_fog_color),
16264	"u_fog_ground_blend": new Uniform1f(context, locations.u_fog_ground_blend),
16265	"u_fog_ground_blend_opacity": new Uniform1f(context, locations.u_fog_ground_blend_opacity),
16266	"u_horizon_color": new UniformColor(context, locations.u_horizon_color),
16267	"u_horizon_fog_blend": new Uniform1f(context, locations.u_horizon_fog_blend),
16268	"u_is_globe_mode": new Uniform1f(context, locations.u_is_globe_mode)
16269});
16270const terrainDepthUniforms = (context, locations) => ({ "u_ele_delta": new Uniform1f(context, locations.u_ele_delta) });
16271const terrainUniformValues = (eleDelta, fogMatrix, sky, pitch, isGlobeMode) => ({
16272	"u_texture": 0,
16273	"u_ele_delta": eleDelta,
16274	"u_fog_matrix": fogMatrix,
16275	"u_fog_color": sky ? sky.properties.get("fog-color") : Color.white,
16276	"u_fog_ground_blend": sky ? sky.properties.get("fog-ground-blend") : 1,
16277	"u_fog_ground_blend_opacity": isGlobeMode ? 0 : sky ? sky.calculateFogBlendOpacity(pitch) : 0,
16278	"u_horizon_color": sky ? sky.properties.get("horizon-color") : Color.white,
16279	"u_horizon_fog_blend": sky ? sky.properties.get("horizon-fog-blend") : 1,
16280	"u_is_globe_mode": isGlobeMode ? 1 : 0
16281});
16282const terrainDepthUniformValues = (eleDelta) => ({ "u_ele_delta": eleDelta });
16283//#endregion
16284//#region src/webgl/uniform_buffer.ts
16285const UBO_BINDINGS = {
16286	ProjectionUBO: 0,
16287	TerrainUBO: 1,
16288	FrameUBO: 2
16289};
16290function applyUBOBindings(gl, program) {
16291	for (const [name, binding] of Object.entries(UBO_BINDINGS)) {
16292		const index = gl.getUniformBlockIndex(program, name);
16293		if (index !== gl.INVALID_INDEX) gl.uniformBlockBinding(program, index, binding);
16294	}
16295}
16296const STD140_SIZE_AND_ALIGNMENT_WORDS = {
16297	float: [1, 1],
16298	int: [1, 1],
16299	vec2: [2, 2],
16300	vec4: [4, 4],
16301	mat4: [16, 4]
16302};
16303function std140Layout(members) {
16304	const offsets = {};
16305	let words = 0;
16306	for (const { name, type } of members) {
16307		const [size, alignment] = STD140_SIZE_AND_ALIGNMENT_WORDS[type];
16308		words = Math.ceil(words / alignment) * alignment;
16309		offsets[name] = words;
16310		words += size;
16311	}
16312	return {
16313		offsets,
16314		contentWords: words,
16315		sizeWords: Math.ceil(words / 4) * 4
16316	};
16317}
16318/**
16319* @internal
16320* The buffer behind one std140 uniform block, bound to a fixed binding point. Callers write members into
16321* `pending` at the layout's offsets and call `upload`, which skips the GPU write when nothing changed. Uploads
16322* use `bufferData` because Apple's OpenGL driver stalls a `bufferSubData` into a buffer a pending draw still reads (#8468).
16323*/
16324var UniformBuffer = class {
16325	constructor(context, binding, layout) {
16326		this.context = context;
16327		this.binding = binding;
16328		this.contentWords = layout.contentWords;
16329		const gl = context.gl;
16330		this.buffer = gl.createBuffer();
16331		gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
16332		gl.bufferData(gl.UNIFORM_BUFFER, layout.sizeWords * 4, gl.DYNAMIC_DRAW);
16333		gl.bindBufferBase(gl.UNIFORM_BUFFER, binding, this.buffer);
16334		this.uploaded = new Float32Array(layout.sizeWords);
16335		this.pending = new Float32Array(layout.sizeWords);
16336		this.uploadedWords = new Uint32Array(this.uploaded.buffer);
16337		this.pendingWords = new Uint32Array(this.pending.buffer);
16338		this.hasData = false;
16339		this.bindingDirty = false;
16340	}
16341	upload() {
16342		const gl = this.context.gl;
16343		let changed = !this.hasData;
16344		if (!changed) {
16345			const words = this.pendingWords;
16346			const uploadedWords = this.uploadedWords;
16347			for (let i = 0; i < this.contentWords; i++) if (words[i] !== uploadedWords[i]) {
16348				changed = true;
16349				break;
16350			}
16351		}
16352		this.bind();
16353		if (!changed) return;
16354		gl.bindBufferBase(gl.UNIFORM_BUFFER, this.binding, this.buffer);
16355		gl.bufferData(gl.UNIFORM_BUFFER, this.pending, gl.DYNAMIC_DRAW);
16356		this.uploaded.set(this.pending);
16357		this.hasData = true;
16358	}
16359	/** Restores the indexed binding after external rendering without uploading unchanged data. */
16360	bind() {
16361		if (!this.bindingDirty) return;
16362		const gl = this.context.gl;
16363		gl.bindBufferBase(gl.UNIFORM_BUFFER, this.binding, this.buffer);
16364		this.bindingDirty = false;
16365	}
16366	destroy() {
16367		const gl = this.context.gl;
16368		if (this.buffer) {
16369			gl.deleteBuffer(this.buffer);
16370			delete this.buffer;
16371		}
16372	}
16373};
16374//#endregion
16375//#region src/webgl/projection_uniform_buffer.ts
16376const layout$2 = std140Layout([
16377	{
16378		name: "u_projection_matrix",
16379		type: "mat4"
16380	},
16381	{
16382		name: "u_projection_fallback_matrix",
16383		type: "mat4"
16384	},
16385	{
16386		name: "u_projection_tile_mercator_coords",
16387		type: "vec4"
16388	},
16389	{
16390		name: "u_projection_clipping_plane",
16391		type: "vec4"
16392	},
16393	{
16394		name: "u_projection_transition",
16395		type: "float"
16396	},
16397	{
16398		name: "u_projection_clip_antimeridian",
16399		type: "int"
16400	}
16401]);
16402const offsets$2 = layout$2.offsets;
16403/**
16404* @internal
16405* The buffer behind the `ProjectionUBO` block in the shader preludes, written by `Program.draw` before each draw.
16406*/
16407function createProjectionUniformBuffer(context) {
16408	return new UniformBuffer(context, UBO_BINDINGS.ProjectionUBO, layout$2);
16409}
16410function updateProjectionUniformBuffer(buffer, projectionData) {
16411	const f32 = buffer.pending;
16412	f32.set(projectionData.mainMatrix, offsets$2.u_projection_matrix);
16413	f32.set(projectionData.fallbackMatrix, offsets$2.u_projection_fallback_matrix);
16414	f32.set(projectionData.tileMercatorCoords, offsets$2.u_projection_tile_mercator_coords);
16415	f32.set(projectionData.clippingPlane, offsets$2.u_projection_clipping_plane);
16416	f32[offsets$2.u_projection_transition] = projectionData.projectionTransition;
16417	buffer.pendingWords[offsets$2.u_projection_clip_antimeridian] = projectionData.clipAntimeridian ? 1 : 0;
16418	buffer.upload();
16419}
16420//#endregion
16421//#region src/webgl/terrain_uniform_buffer.ts
16422const layout$1 = std140Layout([
16423	{
16424		name: "u_terrain_matrix",
16425		type: "mat4"
16426	},
16427	{
16428		name: "u_terrain_unpack",
16429		type: "vec4"
16430	},
16431	{
16432		name: "u_terrain_dim",
16433		type: "float"
16434	},
16435	{
16436		name: "u_terrain_exaggeration",
16437		type: "float"
16438	}
16439]);
16440const offsets$1 = layout$1.offsets;
16441/**
16442* @internal
16443* The buffer behind the `TerrainUBO` block in the vertex prelude, written by `Program.draw` for every draw with terrain.
16444*/
16445function createTerrainUniformBuffer(context) {
16446	return new UniformBuffer(context, UBO_BINDINGS.TerrainUBO, layout$1);
16447}
16448function updateTerrainUniformBuffer(buffer, terrain) {
16449	const f32 = buffer.pending;
16450	f32.set(terrain.u_terrain_matrix, offsets$1.u_terrain_matrix);
16451	f32.set(terrain.u_terrain_unpack, offsets$1.u_terrain_unpack);
16452	f32[offsets$1.u_terrain_dim] = terrain.u_terrain_dim;
16453	f32[offsets$1.u_terrain_exaggeration] = terrain.u_terrain_exaggeration;
16454	buffer.upload();
16455}
16456//#endregion
16457//#region src/webgl/program.ts
16458function getTokenizedAttributesAndUniforms(array) {
16459	const result = [];
16460	for (const entry of array) {
16461		if (entry === null) continue;
16462		const token = entry.split(" ");
16463		result.push(token.pop());
16464	}
16465	return result;
16466}
16467function getIntegerAttributeNames(gl, program) {
16468	const integerTypes = /* @__PURE__ */ new Set([
16469		gl.INT,
16470		gl.INT_VEC2,
16471		gl.INT_VEC3,
16472		gl.INT_VEC4,
16473		gl.UNSIGNED_INT,
16474		gl.UNSIGNED_INT_VEC2,
16475		gl.UNSIGNED_INT_VEC3,
16476		gl.UNSIGNED_INT_VEC4
16477	]);
16478	const names = /* @__PURE__ */ new Set();
16479	const numActiveAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
16480	for (let i = 0; i < numActiveAttributes; i++) {
16481		const attribute = gl.getActiveAttrib(program, i);
16482		if (attribute && integerTypes.has(attribute.type)) names.add(attribute.name);
16483	}
16484	return names;
16485}
16486/**
16487* @internal
16488* A webgl program to execute in the GPU space
16489*/
16490var Program = class {
16491	constructor(context, source, configuration, fixedUniforms, showOverdrawInspector, useTerrain, projectionPrelude, projectionDefine, extraDefines = []) {
16492		const gl = context.gl;
16493		this.program = gl.createProgram();
16494		const staticAttrInfo = getTokenizedAttributesAndUniforms(source.staticAttributes);
16495		const dynamicAttrInfo = configuration ? configuration.getBinderAttributes() : [];
16496		const allAttrInfo = staticAttrInfo.concat(dynamicAttrInfo);
16497		const preludeUniformsInfo = shaders.prelude.staticUniforms ? getTokenizedAttributesAndUniforms(shaders.prelude.staticUniforms) : [];
16498		const staticUniformsInfo = source.staticUniforms ? getTokenizedAttributesAndUniforms(source.staticUniforms) : [];
16499		const dynamicUniformsInfo = configuration ? configuration.getBinderUniforms() : [];
16500		const uniformList = preludeUniformsInfo.concat(staticUniformsInfo).concat(dynamicUniformsInfo);
16501		const allUniformsInfo = [];
16502		for (const uniform of uniformList) if (!allUniformsInfo.includes(uniform)) allUniformsInfo.push(uniform);
16503		const defines = configuration ? configuration.defines() : [];
16504		defines.unshift("#version 300 es");
16505		if (showOverdrawInspector) defines.push("#define OVERDRAW_INSPECTOR;");
16506		if (useTerrain) defines.push("#define TERRAIN3D;");
16507		if (projectionDefine) defines.push(projectionDefine);
16508		if (extraDefines) defines.push(...extraDefines);
16509		const fragmentSource = defines.concat(shaders.prelude.fragmentSource, projectionPrelude.fragmentSource, source.fragmentSource).join("\n");
16510		const vertexSource = defines.concat(shaders.prelude.vertexSource, projectionPrelude.vertexSource, source.vertexSource).join("\n");
16511		const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
16512		if (gl.isContextLost()) {
16513			this.failedToCreate = true;
16514			return;
16515		}
16516		gl.shaderSource(fragmentShader, fragmentSource);
16517		gl.compileShader(fragmentShader);
16518		gl.attachShader(this.program, fragmentShader);
16519		const vertexShader = gl.createShader(gl.VERTEX_SHADER);
16520		if (gl.isContextLost()) {
16521			this.failedToCreate = true;
16522			return;
16523		}
16524		gl.shaderSource(vertexShader, vertexSource);
16525		gl.compileShader(vertexShader);
16526		gl.attachShader(this.program, vertexShader);
16527		this.attributes = {};
16528		const uniformLocations = {};
16529		this.numAttributes = allAttrInfo.length;
16530		gl.linkProgram(this.program);
16531		if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
16532			if (gl.isContextLost()) {
16533				this.failedToCreate = true;
16534				return;
16535			}
16536			if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) throw new Error(`Could not compile fragment shader: ${gl.getShaderInfoLog(fragmentShader)}`);
16537			if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) throw new Error(`Could not compile vertex shader: ${gl.getShaderInfoLog(vertexShader)}`);
16538			throw new Error(`Program failed to link: ${gl.getProgramInfoLog(this.program)}`);
16539		}
16540		applyUBOBindings(gl, this.program);
16541		const integerAttributeNames = getIntegerAttributeNames(gl, this.program);
16542		for (const name of allAttrInfo) {
16543			if (!name) continue;
16544			const location = gl.getAttribLocation(this.program, name);
16545			if (location >= 0) this.attributes[name] = {
16546				location,
16547				isInteger: integerAttributeNames.has(name)
16548			};
16549		}
16550		gl.deleteShader(vertexShader);
16551		gl.deleteShader(fragmentShader);
16552		for (const uniform of allUniformsInfo) if (uniform && !uniformLocations[uniform]) {
16553			const uniformLocation = gl.getUniformLocation(this.program, uniform);
16554			if (uniformLocation) uniformLocations[uniform] = uniformLocation;
16555		}
16556		this.fixedUniforms = fixedUniforms(context, uniformLocations);
16557		this.terrainUniforms = terrainPreludeUniforms(context, uniformLocations);
16558		this.binderUniforms = configuration ? configuration.getUniforms(context, uniformLocations) : [];
16559	}
16560	draw(context, drawMode, depthMode, stencilMode, colorMode, cullFaceMode, uniformValues, terrain, projectionData, layerID, layoutVertexBuffer, indexBuffer, segments, currentProperties, zoom, configuration, dynamicLayoutBuffer, dynamicLayoutBuffer2, dynamicLayoutBuffer3) {
16561		const gl = context.gl;
16562		if (this.failedToCreate) return;
16563		context.program.set(this.program);
16564		context.projectionUniformBuffer.bind();
16565		context.terrainUniformBuffer.bind();
16566		context.frameUniformBuffer.bind();
16567		context.setDepthMode(depthMode);
16568		context.setStencilMode(stencilMode);
16569		context.setColorMode(colorMode);
16570		context.setCullFace(cullFaceMode);
16571		if (terrain) {
16572			context.activeTexture.set(gl.TEXTURE2);
16573			gl.bindTexture(gl.TEXTURE_2D, terrain.depthTexture);
16574			context.activeTexture.set(gl.TEXTURE3);
16575			gl.bindTexture(gl.TEXTURE_2D, terrain.texture);
16576			for (const name in this.terrainUniforms) this.terrainUniforms[name].set(terrain[name]);
16577			updateTerrainUniformBuffer(context.terrainUniformBuffer, terrain);
16578		}
16579		if (projectionData) updateProjectionUniformBuffer(context.projectionUniformBuffer, projectionData);
16580		if (uniformValues) for (const name in this.fixedUniforms) this.fixedUniforms[name].set(uniformValues[name]);
16581		if (configuration) configuration.setUniforms(context, this.binderUniforms, currentProperties, { zoom });
16582		let primitiveSize = 0;
16583		switch (drawMode) {
16584			case gl.LINES:
16585				primitiveSize = 2;
16586				break;
16587			case gl.TRIANGLES:
16588				primitiveSize = 3;
16589				break;
16590			case gl.LINE_STRIP: primitiveSize = 1;
16591		}
16592		for (const segment of segments.get()) {
16593			segment.vaos ||= {};
16594			segment.vaos[layerID] ||= new VertexArrayObject();
16595			segment.vaos[layerID].bind(context, this, layoutVertexBuffer, configuration ? configuration.getPaintVertexBuffers() : [], indexBuffer, segment.vertexOffset, dynamicLayoutBuffer, dynamicLayoutBuffer2, dynamicLayoutBuffer3);
16596			gl.drawElements(drawMode, segment.primitiveLength * primitiveSize, gl.UNSIGNED_SHORT, segment.primitiveOffset * primitiveSize * 2);
16597		}
16598	}
16599};
16600//#endregion
16601//#region src/webgl/program/pattern.ts
16602function patternUniformValues(crossfade, painter, tile) {
16603	const tileRatio = 1 / pixelsToTileUnits(tile, 1, painter.transform.tileZoom);
16604	const numTiles = Math.pow(2, tile.tileID.overscaledZ);
16605	const tileSizeAtNearestZoom = tile.tileSize * Math.pow(2, painter.transform.tileZoom) / numTiles;
16606	const pixelX = tileSizeAtNearestZoom * (tile.tileID.canonical.x + tile.tileID.wrap * numTiles);
16607	const pixelY = tileSizeAtNearestZoom * tile.tileID.canonical.y;
16608	return {
16609		"u_image": 0,
16610		"u_texsize": tile.imageAtlasTexture.size,
16611		"u_scale": [
16612			tileRatio,
16613			crossfade.fromScale,
16614			crossfade.toScale
16615		],
16616		"u_fade": crossfade.t,
16617		"u_pixel_coord_upper": [pixelX >> 16, pixelY >> 16],
16618		"u_pixel_coord_lower": [pixelX & 65535, pixelY & 65535]
16619	};
16620}
16621function bgPatternUniformValues(image, crossfade, painter, tile) {
16622	const imagePosA = painter.patternAtlas.getPattern(image.from.toString());
16623	const imagePosB = painter.patternAtlas.getPattern(image.to.toString());
16624	const { width, height } = painter.patternAtlas.getPixelSize();
16625	const numTiles = Math.pow(2, tile.tileID.overscaledZ);
16626	const tileSizeAtNearestZoom = tile.tileSize * Math.pow(2, painter.transform.tileZoom) / numTiles;
16627	const pixelX = tileSizeAtNearestZoom * (tile.tileID.canonical.x + tile.tileID.wrap * numTiles);
16628	const pixelY = tileSizeAtNearestZoom * tile.tileID.canonical.y;
16629	return {
16630		"u_image": 0,
16631		"u_pattern_tl_a": imagePosA.tl,
16632		"u_pattern_br_a": imagePosA.br,
16633		"u_pattern_tl_b": imagePosB.tl,
16634		"u_pattern_br_b": imagePosB.br,
16635		"u_texsize": [width, height],
16636		"u_mix": crossfade.t,
16637		"u_pattern_size_a": imagePosA.displaySize,
16638		"u_pattern_size_b": imagePosB.displaySize,
16639		"u_scale_a": crossfade.fromScale,
16640		"u_scale_b": crossfade.toScale,
16641		"u_tile_units_to_pixels": 1 / pixelsToTileUnits(tile, 1, painter.transform.tileZoom),
16642		"u_pixel_coord_upper": [pixelX >> 16, pixelY >> 16],
16643		"u_pixel_coord_lower": [pixelX & 65535, pixelY & 65535]
16644	};
16645}
16646//#endregion
16647//#region src/webgl/program/fill_extrusion_program.ts
16648const fillExtrusionUniforms = (context, locations) => ({
16649	"u_lightpos": new Uniform3f(context, locations.u_lightpos),
16650	"u_lightpos_globe": new Uniform3f(context, locations.u_lightpos_globe),
16651	"u_lightintensity": new Uniform1f(context, locations.u_lightintensity),
16652	"u_lightcolor": new Uniform3f(context, locations.u_lightcolor),
16653	"u_vertical_gradient": new Uniform1f(context, locations.u_vertical_gradient),
16654	"u_opacity": new Uniform1f(context, locations.u_opacity),
16655	"u_fill_translate": new Uniform2f(context, locations.u_fill_translate)
16656});
16657const fillExtrusionPatternUniforms = (context, locations) => ({
16658	"u_lightpos": new Uniform3f(context, locations.u_lightpos),
16659	"u_lightpos_globe": new Uniform3f(context, locations.u_lightpos_globe),
16660	"u_lightintensity": new Uniform1f(context, locations.u_lightintensity),
16661	"u_lightcolor": new Uniform3f(context, locations.u_lightcolor),
16662	"u_vertical_gradient": new Uniform1f(context, locations.u_vertical_gradient),
16663	"u_height_factor": new Uniform1f(context, locations.u_height_factor),
16664	"u_opacity": new Uniform1f(context, locations.u_opacity),
16665	"u_fill_translate": new Uniform2f(context, locations.u_fill_translate),
16666	"u_image": new Uniform1i(context, locations.u_image),
16667	"u_texsize": new Uniform2f(context, locations.u_texsize),
16668	"u_pixel_coord_upper": new Uniform2f(context, locations.u_pixel_coord_upper),
16669	"u_pixel_coord_lower": new Uniform2f(context, locations.u_pixel_coord_lower),
16670	"u_scale": new Uniform3f(context, locations.u_scale),
16671	"u_fade": new Uniform1f(context, locations.u_fade)
16672});
16673const fillExtrusionUniformValues = (painter, shouldUseVerticalGradient, opacity, translate) => {
16674	const light = painter.style.light;
16675	const lightPos = light.getCartesianPosition();
16676	const lightMat = create$4();
16677	if (light.properties.get("anchor") === "viewport") fromRotation(lightMat, painter.transform.bearingInRadians);
16678	transformMat3(lightPos, lightPos, lightMat);
16679	const transformedLightPos = painter.transform.transformLightDirection(lightPos);
16680	const lightColor = light.properties.get("color");
16681	return {
16682		"u_lightpos": lightPos,
16683		"u_lightpos_globe": transformedLightPos,
16684		"u_lightintensity": light.properties.get("intensity"),
16685		"u_lightcolor": [
16686			lightColor.r,
16687			lightColor.g,
16688			lightColor.b
16689		],
16690		"u_vertical_gradient": +shouldUseVerticalGradient,
16691		"u_opacity": opacity,
16692		"u_fill_translate": translate
16693	};
16694};
16695const fillExtrusionPatternUniformValues = (painter, shouldUseVerticalGradient, opacity, translate, coord, crossfade, tile) => {
16696	return extend(fillExtrusionUniformValues(painter, shouldUseVerticalGradient, opacity, translate), patternUniformValues(crossfade, painter, tile), { "u_height_factor": -Math.pow(2, coord.overscaledZ) / tile.tileSize / 8 });
16697};
16698//#endregion
16699//#region src/webgl/program/fill_program.ts
16700const fillUniforms = (context, locations) => ({ "u_fill_translate": new Uniform2f(context, locations.u_fill_translate) });
16701const fillPatternUniforms = (context, locations) => ({
16702	"u_image": new Uniform1i(context, locations.u_image),
16703	"u_texsize": new Uniform2f(context, locations.u_texsize),
16704	"u_pixel_coord_upper": new Uniform2f(context, locations.u_pixel_coord_upper),
16705	"u_pixel_coord_lower": new Uniform2f(context, locations.u_pixel_coord_lower),
16706	"u_scale": new Uniform3f(context, locations.u_scale),
16707	"u_fade": new Uniform1f(context, locations.u_fade),
16708	"u_sdf_pattern": new Uniform1i(context, locations.u_sdf_pattern),
16709	"u_fill_translate": new Uniform2f(context, locations.u_fill_translate)
16710});
16711const fillOutlineUniforms = (context, locations) => ({ "u_fill_translate": new Uniform2f(context, locations.u_fill_translate) });
16712const fillOutlinePatternUniforms = (context, locations) => ({
16713	"u_image": new Uniform1i(context, locations.u_image),
16714	"u_texsize": new Uniform2f(context, locations.u_texsize),
16715	"u_pixel_coord_upper": new Uniform2f(context, locations.u_pixel_coord_upper),
16716	"u_pixel_coord_lower": new Uniform2f(context, locations.u_pixel_coord_lower),
16717	"u_scale": new Uniform3f(context, locations.u_scale),
16718	"u_fade": new Uniform1f(context, locations.u_fade),
16719	"u_sdf_pattern": new Uniform1i(context, locations.u_sdf_pattern),
16720	"u_fill_translate": new Uniform2f(context, locations.u_fill_translate)
16721});
16722const fillPatternUniformValues = (painter, crossfade, tile, translate, isSdfPattern) => extend(patternUniformValues(crossfade, painter, tile), {
16723	"u_fill_translate": translate,
16724	"u_sdf_pattern": isSdfPattern ? 1 : 0
16725});
16726const fillUniformValues = (translate) => ({ "u_fill_translate": translate });
16727const fillOutlineUniformValues = (translate) => ({ "u_fill_translate": translate });
16728const fillOutlinePatternUniformValues = (painter, crossfade, tile, translate, isSdfPattern) => fillPatternUniformValues(painter, crossfade, tile, translate, isSdfPattern);
16729//#endregion
16730//#region src/webgl/program/circle_program.ts
16731const circleUniforms = (context, locations) => ({
16732	"u_scale_with_map": new Uniform1i(context, locations.u_scale_with_map),
16733	"u_pitch_with_map": new Uniform1i(context, locations.u_pitch_with_map),
16734	"u_extrude_scale": new Uniform2f(context, locations.u_extrude_scale),
16735	"u_globe_extrude_scale": new Uniform1f(context, locations.u_globe_extrude_scale),
16736	"u_translate": new Uniform2f(context, locations.u_translate)
16737});
16738const circleUniformValues = (painter, tile, layer, translate, radiusCorrectionFactor) => {
16739	const transform = painter.transform;
16740	let pitchWithMap, extrudeScale;
16741	let globeExtrudeScale = 0;
16742	if (layer.paint.get("circle-pitch-alignment") === "map") {
16743		const pixelRatio = pixelsToTileUnits(tile, 1, transform.zoom);
16744		pitchWithMap = true;
16745		extrudeScale = [pixelRatio, pixelRatio];
16746		globeExtrudeScale = pixelRatio / (EXTENT * Math.pow(2, tile.tileID.overscaledZ)) * 2 * Math.PI * radiusCorrectionFactor;
16747	} else {
16748		pitchWithMap = false;
16749		extrudeScale = transform.pixelsToGLUnits;
16750	}
16751	return {
16752		"u_scale_with_map": +(layer.paint.get("circle-pitch-scale") === "map"),
16753		"u_pitch_with_map": +pitchWithMap,
16754		"u_extrude_scale": extrudeScale,
16755		"u_globe_extrude_scale": globeExtrudeScale,
16756		"u_translate": translate
16757	};
16758};
16759//#endregion
16760//#region src/webgl/program/debug_program.ts
16761const debugUniforms = (context, locations) => ({
16762	"u_color": new UniformColor(context, locations.u_color),
16763	"u_overlay": new Uniform1i(context, locations.u_overlay),
16764	"u_overlay_scale": new Uniform1f(context, locations.u_overlay_scale)
16765});
16766const debugUniformValues = (color, scaleRatio = 1) => ({
16767	"u_color": color,
16768	"u_overlay": 0,
16769	"u_overlay_scale": scaleRatio
16770});
16771//#endregion
16772//#region src/webgl/program/heatmap_program.ts
16773const heatmapUniforms = (context, locations) => ({
16774	"u_extrude_scale": new Uniform1f(context, locations.u_extrude_scale),
16775	"u_intensity": new Uniform1f(context, locations.u_intensity),
16776	"u_globe_extrude_scale": new Uniform1f(context, locations.u_globe_extrude_scale)
16777});
16778const heatmapTextureUniforms = (context, locations) => ({
16779	"u_matrix": new UniformMatrix4f(context, locations.u_matrix),
16780	"u_image": new Uniform1i(context, locations.u_image),
16781	"u_color_ramp": new Uniform1i(context, locations.u_color_ramp),
16782	"u_opacity": new Uniform1f(context, locations.u_opacity)
16783});
16784const heatmapUniformValues = (tile, zoom, intensity, radiusCorrectionFactor) => {
16785	const globeExtrudeScale = pixelsToTileUnits(tile, 1, zoom) / (EXTENT * Math.pow(2, tile.tileID.overscaledZ)) * 2 * Math.PI * radiusCorrectionFactor;
16786	return {
16787		"u_extrude_scale": pixelsToTileUnits(tile, 1, zoom),
16788		"u_intensity": intensity,
16789		"u_globe_extrude_scale": globeExtrudeScale
16790	};
16791};
16792const heatmapTextureUniformValues = (painter, layer, textureUnit, colorRampUnit) => {
16793	const matrix = create$1();
16794	ortho(matrix, 0, painter.width, painter.height, 0, 0, 1);
16795	return {
16796		"u_matrix": matrix,
16797		"u_image": textureUnit,
16798		"u_color_ramp": colorRampUnit,
16799		"u_opacity": layer.paint.get("heatmap-opacity")
16800	};
16801};
16802//#endregion
16803//#region src/webgl/program/hillshade_program.ts
16804const hillshadeUniforms = (context, locations) => ({
16805	"u_image": new Uniform1i(context, locations.u_image),
16806	"u_latrange": new Uniform2f(context, locations.u_latrange),
16807	"u_exaggeration": new Uniform1f(context, locations.u_exaggeration),
16808	"u_altitudes": new UniformFloatArray(context, locations.u_altitudes),
16809	"u_azimuths": new UniformFloatArray(context, locations.u_azimuths),
16810	"u_accent": new UniformColor(context, locations.u_accent),
16811	"u_method": new Uniform1i(context, locations.u_method),
16812	"u_shadows": new UniformColorArray(context, locations.u_shadows),
16813	"u_highlights": new UniformColorArray(context, locations.u_highlights)
16814});
16815const hillshadePrepareUniforms = (context, locations) => ({
16816	"u_matrix": new UniformMatrix4f(context, locations.u_matrix),
16817	"u_image": new Uniform1i(context, locations.u_image),
16818	"u_dimension": new Uniform2f(context, locations.u_dimension),
16819	"u_zoom": new Uniform1f(context, locations.u_zoom),
16820	"u_unpack": new Uniform4f(context, locations.u_unpack)
16821});
16822const hillshadeUniformValues = (painter, tile, layer) => {
16823	const accent = layer.paint.get("hillshade-accent-color");
16824	let method;
16825	switch (layer.paint.get("hillshade-method")) {
16826		case "basic":
16827			method = 4;
16828			break;
16829		case "combined":
16830			method = 1;
16831			break;
16832		case "igor":
16833			method = 2;
16834			break;
16835		case "multidirectional":
16836			method = 3;
16837			break;
16838		default: method = 0;
16839	}
16840	const illumination = layer.getIlluminationProperties();
16841	for (let i = 0; i < illumination.directionRadians.length; i++) if (layer.paint.get("hillshade-illumination-anchor") === "viewport") illumination.directionRadians[i] += painter.transform.bearingInRadians;
16842	return {
16843		"u_image": 0,
16844		"u_latrange": getTileLatRange(painter, tile.tileID),
16845		"u_exaggeration": layer.paint.get("hillshade-exaggeration"),
16846		"u_altitudes": illumination.altitudeRadians,
16847		"u_azimuths": illumination.directionRadians,
16848		"u_accent": accent,
16849		"u_method": method,
16850		"u_highlights": illumination.highlightColor,
16851		"u_shadows": illumination.shadowColor
16852	};
16853};
16854const hillshadeUniformPrepareValues = (tileID, dem) => {
16855	const stride = dem.stride;
16856	const matrix = create$1();
16857	ortho(matrix, 0, EXTENT, -EXTENT, 0, 0, 1);
16858	translate(matrix, matrix, [
16859		0,
16860		-EXTENT,
16861		0
16862	]);
16863	return {
16864		"u_matrix": matrix,
16865		"u_image": 1,
16866		"u_dimension": [stride, stride],
16867		"u_zoom": tileID.overscaledZ,
16868		"u_unpack": dem.getUnpackVector()
16869	};
16870};
16871function getTileLatRange(painter, tileID) {
16872	const tilesAtZoom = Math.pow(2, tileID.canonical.z);
16873	const y = tileID.canonical.y;
16874	return [new MercatorCoordinate(0, y / tilesAtZoom).toLngLat().lat, new MercatorCoordinate(0, (y + 1) / tilesAtZoom).toLngLat().lat];
16875}
16876//#endregion
16877//#region src/webgl/program/color_relief_program.ts
16878const colorReliefUniforms = (context, locations) => ({
16879	"u_image": new Uniform1i(context, locations.u_image),
16880	"u_unpack": new Uniform4f(context, locations.u_unpack),
16881	"u_dimension": new Uniform2f(context, locations.u_dimension),
16882	"u_elevation_stops": new Uniform1i(context, locations.u_elevation_stops),
16883	"u_color_stops": new Uniform1i(context, locations.u_color_stops),
16884	"u_color_ramp_size": new Uniform1i(context, locations.u_color_ramp_size),
16885	"u_opacity": new Uniform1f(context, locations.u_opacity)
16886});
16887const colorReliefUniformValues = (layer, dem, colorRampSize = 0) => {
16888	return {
16889		"u_image": 0,
16890		"u_unpack": dem.getUnpackVector(),
16891		"u_dimension": [dem.stride, dem.stride],
16892		"u_elevation_stops": 1,
16893		"u_color_stops": 4,
16894		"u_color_ramp_size": colorRampSize,
16895		"u_opacity": layer.paint.get("color-relief-opacity")
16896	};
16897};
16898//#endregion
16899//#region src/webgl/program/line_program.ts
16900const lineUniforms = (context, locations) => ({
16901	"u_translation": new Uniform2f(context, locations.u_translation),
16902	"u_ratio": new Uniform1f(context, locations.u_ratio)
16903});
16904const lineGradientUniforms = (context, locations) => ({
16905	"u_translation": new Uniform2f(context, locations.u_translation),
16906	"u_ratio": new Uniform1f(context, locations.u_ratio),
16907	"u_image": new Uniform1i(context, locations.u_image),
16908	"u_image_height": new Uniform1f(context, locations.u_image_height)
16909});
16910const linePatternUniforms = (context, locations) => ({
16911	"u_translation": new Uniform2f(context, locations.u_translation),
16912	"u_texsize": new Uniform2f(context, locations.u_texsize),
16913	"u_ratio": new Uniform1f(context, locations.u_ratio),
16914	"u_image": new Uniform1i(context, locations.u_image),
16915	"u_scale": new Uniform3f(context, locations.u_scale),
16916	"u_fade": new Uniform1f(context, locations.u_fade)
16917});
16918const lineSDFUniforms = (context, locations) => ({
16919	"u_translation": new Uniform2f(context, locations.u_translation),
16920	"u_ratio": new Uniform1f(context, locations.u_ratio),
16921	"u_image": new Uniform1i(context, locations.u_image),
16922	"u_mix": new Uniform1f(context, locations.u_mix),
16923	"u_tileratio": new Uniform1f(context, locations.u_tileratio),
16924	"u_crossfade_from": new Uniform1f(context, locations.u_crossfade_from),
16925	"u_crossfade_to": new Uniform1f(context, locations.u_crossfade_to),
16926	"u_lineatlas_width": new Uniform1f(context, locations.u_lineatlas_width),
16927	"u_lineatlas_height": new Uniform1f(context, locations.u_lineatlas_height)
16928});
16929const lineGradientSDFUniforms = (context, locations) => ({
16930	"u_translation": new Uniform2f(context, locations.u_translation),
16931	"u_ratio": new Uniform1f(context, locations.u_ratio),
16932	"u_image": new Uniform1i(context, locations.u_image),
16933	"u_image_height": new Uniform1f(context, locations.u_image_height),
16934	"u_tileratio": new Uniform1f(context, locations.u_tileratio),
16935	"u_crossfade_from": new Uniform1f(context, locations.u_crossfade_from),
16936	"u_crossfade_to": new Uniform1f(context, locations.u_crossfade_to),
16937	"u_image_dash": new Uniform1i(context, locations.u_image_dash),
16938	"u_mix": new Uniform1f(context, locations.u_mix),
16939	"u_lineatlas_width": new Uniform1f(context, locations.u_lineatlas_width),
16940	"u_lineatlas_height": new Uniform1f(context, locations.u_lineatlas_height)
16941});
16942const lineUniformValues = (painter, tile, layer, ratioScale) => {
16943	const transform = painter.transform;
16944	return {
16945		"u_translation": calculateTranslation(painter, tile, layer),
16946		"u_ratio": ratioScale / pixelsToTileUnits(tile, 1, transform.zoom)
16947	};
16948};
16949const lineGradientUniformValues = (painter, tile, layer, ratioScale, imageHeight) => {
16950	return extend(lineUniformValues(painter, tile, layer, ratioScale), {
16951		"u_image": 0,
16952		"u_image_height": imageHeight
16953	});
16954};
16955const linePatternUniformValues = (painter, tile, layer, ratioScale, crossfade) => {
16956	const transform = painter.transform;
16957	const tileZoomRatio = calculateTileRatio(tile, transform);
16958	return {
16959		"u_translation": calculateTranslation(painter, tile, layer),
16960		"u_texsize": tile.imageAtlasTexture.size,
16961		"u_ratio": ratioScale / pixelsToTileUnits(tile, 1, transform.zoom),
16962		"u_image": 0,
16963		"u_scale": [
16964			tileZoomRatio,
16965			crossfade.fromScale,
16966			crossfade.toScale
16967		],
16968		"u_fade": crossfade.t
16969	};
16970};
16971const lineSDFUniformValues = (painter, tile, layer, ratioScale, crossfade) => {
16972	const transform = painter.transform;
16973	const tileRatio = calculateTileRatio(tile, transform);
16974	return extend(lineUniformValues(painter, tile, layer, ratioScale), {
16975		"u_tileratio": tileRatio,
16976		"u_crossfade_from": crossfade.fromScale,
16977		"u_crossfade_to": crossfade.toScale,
16978		"u_image": 0,
16979		"u_mix": crossfade.t,
16980		"u_lineatlas_width": painter.lineAtlas.width,
16981		"u_lineatlas_height": painter.lineAtlas.height
16982	});
16983};
16984const lineGradientSDFUniformValues = (painter, tile, layer, ratioScale, crossfade, imageHeight) => {
16985	const transform = painter.transform;
16986	const tileRatio = calculateTileRatio(tile, transform);
16987	return extend(lineUniformValues(painter, tile, layer, ratioScale), {
16988		"u_image": 0,
16989		"u_image_height": imageHeight,
16990		"u_tileratio": tileRatio,
16991		"u_crossfade_from": crossfade.fromScale,
16992		"u_crossfade_to": crossfade.toScale,
16993		"u_image_dash": 1,
16994		"u_mix": crossfade.t,
16995		"u_lineatlas_width": painter.lineAtlas.width,
16996		"u_lineatlas_height": painter.lineAtlas.height
16997	});
16998};
16999function calculateTileRatio(tile, transform) {
17000	return 1 / pixelsToTileUnits(tile, 1, transform.tileZoom);
17001}
17002function calculateTranslation(painter, tile, layer) {
17003	return translatePosition(painter.transform, tile, layer.paint.get("line-translate"), layer.paint.get("line-translate-anchor"));
17004}
17005//#endregion
17006//#region src/webgl/program/layer_opacity_program.ts
17007const layerOpacityUniforms = (context, locations) => ({
17008	"u_image": new Uniform1i(context, locations.u_image),
17009	"u_opacity": new Uniform1f(context, locations.u_opacity)
17010});
17011const layerOpacityUniformValues = (opacity, textureUnit) => ({
17012	"u_image": textureUnit,
17013	"u_opacity": opacity
17014});
17015//#endregion
17016//#region src/webgl/program/symbol_program.ts
17017const symbolIconUniforms = (context, locations) => ({
17018	"u_is_size_zoom_constant": new Uniform1i(context, locations.u_is_size_zoom_constant),
17019	"u_is_size_feature_constant": new Uniform1i(context, locations.u_is_size_feature_constant),
17020	"u_size_t": new Uniform1f(context, locations.u_size_t),
17021	"u_size": new Uniform1f(context, locations.u_size),
17022	"u_rotate_symbol": new Uniform1i(context, locations.u_rotate_symbol),
17023	"u_label_plane_matrix": new UniformMatrix4f(context, locations.u_label_plane_matrix),
17024	"u_coord_matrix": new UniformMatrix4f(context, locations.u_coord_matrix),
17025	"u_is_text": new Uniform1i(context, locations.u_is_text),
17026	"u_pitch_with_map": new Uniform1i(context, locations.u_pitch_with_map),
17027	"u_is_along_line": new Uniform1i(context, locations.u_is_along_line),
17028	"u_is_variable_anchor": new Uniform1i(context, locations.u_is_variable_anchor),
17029	"u_texsize": new Uniform2f(context, locations.u_texsize),
17030	"u_texture": new Uniform1i(context, locations.u_texture),
17031	"u_translation": new Uniform2f(context, locations.u_translation),
17032	"u_pitched_scale": new Uniform1f(context, locations.u_pitched_scale),
17033	"u_is_offset": new Uniform1i(context, locations.u_is_offset),
17034	"u_height_anchor_ground": new Uniform1i(context, locations.u_height_anchor_ground)
17035});
17036const symbolSDFUniforms = (context, locations) => ({
17037	"u_is_size_zoom_constant": new Uniform1i(context, locations.u_is_size_zoom_constant),
17038	"u_is_size_feature_constant": new Uniform1i(context, locations.u_is_size_feature_constant),
17039	"u_size_t": new Uniform1f(context, locations.u_size_t),
17040	"u_size": new Uniform1f(context, locations.u_size),
17041	"u_rotate_symbol": new Uniform1i(context, locations.u_rotate_symbol),
17042	"u_label_plane_matrix": new UniformMatrix4f(context, locations.u_label_plane_matrix),
17043	"u_coord_matrix": new UniformMatrix4f(context, locations.u_coord_matrix),
17044	"u_is_text": new Uniform1i(context, locations.u_is_text),
17045	"u_pitch_with_map": new Uniform1i(context, locations.u_pitch_with_map),
17046	"u_is_along_line": new Uniform1i(context, locations.u_is_along_line),
17047	"u_is_variable_anchor": new Uniform1i(context, locations.u_is_variable_anchor),
17048	"u_texsize": new Uniform2f(context, locations.u_texsize),
17049	"u_texture": new Uniform1i(context, locations.u_texture),
17050	"u_gamma_scale": new Uniform1f(context, locations.u_gamma_scale),
17051	"u_is_halo": new Uniform1i(context, locations.u_is_halo),
17052	"u_is_plain": new Uniform1i(context, locations.u_is_plain),
17053	"u_translation": new Uniform2f(context, locations.u_translation),
17054	"u_pitched_scale": new Uniform1f(context, locations.u_pitched_scale),
17055	"u_is_offset": new Uniform1i(context, locations.u_is_offset),
17056	"u_height_anchor_ground": new Uniform1i(context, locations.u_height_anchor_ground)
17057});
17058const symbolTextAndIconUniforms = (context, locations) => ({
17059	"u_is_size_zoom_constant": new Uniform1i(context, locations.u_is_size_zoom_constant),
17060	"u_is_size_feature_constant": new Uniform1i(context, locations.u_is_size_feature_constant),
17061	"u_size_t": new Uniform1f(context, locations.u_size_t),
17062	"u_size": new Uniform1f(context, locations.u_size),
17063	"u_rotate_symbol": new Uniform1i(context, locations.u_rotate_symbol),
17064	"u_label_plane_matrix": new UniformMatrix4f(context, locations.u_label_plane_matrix),
17065	"u_coord_matrix": new UniformMatrix4f(context, locations.u_coord_matrix),
17066	"u_is_text": new Uniform1i(context, locations.u_is_text),
17067	"u_pitch_with_map": new Uniform1i(context, locations.u_pitch_with_map),
17068	"u_is_along_line": new Uniform1i(context, locations.u_is_along_line),
17069	"u_is_variable_anchor": new Uniform1i(context, locations.u_is_variable_anchor),
17070	"u_texsize": new Uniform2f(context, locations.u_texsize),
17071	"u_texsize_icon": new Uniform2f(context, locations.u_texsize_icon),
17072	"u_texture": new Uniform1i(context, locations.u_texture),
17073	"u_texture_icon": new Uniform1i(context, locations.u_texture_icon),
17074	"u_gamma_scale": new Uniform1f(context, locations.u_gamma_scale),
17075	"u_is_halo": new Uniform1i(context, locations.u_is_halo),
17076	"u_translation": new Uniform2f(context, locations.u_translation),
17077	"u_pitched_scale": new Uniform1f(context, locations.u_pitched_scale),
17078	"u_is_offset": new Uniform1i(context, locations.u_is_offset),
17079	"u_height_anchor_ground": new Uniform1i(context, locations.u_height_anchor_ground)
17080});
17081const symbolIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, pitchedScale, isOffset, heightAnchorGround) => {
17082	return {
17083		"u_is_size_zoom_constant": +(functionType === "constant" || functionType === "source"),
17084		"u_is_size_feature_constant": +(functionType === "constant" || functionType === "camera"),
17085		"u_size_t": size ? size.uSizeT : 0,
17086		"u_size": size ? size.uSize : 0,
17087		"u_rotate_symbol": +rotateInShader,
17088		"u_label_plane_matrix": labelPlaneMatrix,
17089		"u_coord_matrix": glCoordMatrix,
17090		"u_is_text": +isText,
17091		"u_pitch_with_map": +pitchWithMap,
17092		"u_is_along_line": isAlongLine,
17093		"u_is_variable_anchor": isVariableAnchor,
17094		"u_texsize": texSize,
17095		"u_texture": 0,
17096		"u_translation": translation,
17097		"u_pitched_scale": pitchedScale,
17098		"u_is_offset": isOffset,
17099		"u_height_anchor_ground": +heightAnchorGround
17100	};
17101};
17102const symbolSDFUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, isHalo, pitchedScale, isOffset, heightAnchorGround) => {
17103	const transform = painter.transform;
17104	return extend(symbolIconUniformValues(functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, isText, texSize, pitchedScale, isOffset, heightAnchorGround), {
17105		"u_gamma_scale": pitchWithMap ? Math.cos(transform.pitch * Math.PI / 180) * transform.cameraToCenterDistance : 1,
17106		"u_is_halo": isHalo ? 1 : 0,
17107		"u_is_plain": 1
17108	});
17109};
17110const symbolTextAndIconUniformValues = (functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, texSizeSDF, texSizeIcon, pitchedScale, isOffset, heightAnchorGround) => {
17111	return extend(symbolSDFUniformValues(functionType, size, rotateInShader, pitchWithMap, isAlongLine, isVariableAnchor, painter, labelPlaneMatrix, glCoordMatrix, translation, true, texSizeSDF, true, pitchedScale, isOffset, heightAnchorGround), {
17112		"u_texsize_icon": texSizeIcon,
17113		"u_texture_icon": 1
17114	});
17115};
17116//#endregion
17117//#region src/webgl/program/background_program.ts
17118const backgroundUniforms = (context, locations) => ({
17119	"u_opacity": new Uniform1f(context, locations.u_opacity),
17120	"u_color": new UniformColor(context, locations.u_color)
17121});
17122const backgroundPatternUniforms = (context, locations) => ({
17123	"u_opacity": new Uniform1f(context, locations.u_opacity),
17124	"u_image": new Uniform1i(context, locations.u_image),
17125	"u_pattern_tl_a": new Uniform2f(context, locations.u_pattern_tl_a),
17126	"u_pattern_br_a": new Uniform2f(context, locations.u_pattern_br_a),
17127	"u_pattern_tl_b": new Uniform2f(context, locations.u_pattern_tl_b),
17128	"u_pattern_br_b": new Uniform2f(context, locations.u_pattern_br_b),
17129	"u_texsize": new Uniform2f(context, locations.u_texsize),
17130	"u_mix": new Uniform1f(context, locations.u_mix),
17131	"u_pattern_size_a": new Uniform2f(context, locations.u_pattern_size_a),
17132	"u_pattern_size_b": new Uniform2f(context, locations.u_pattern_size_b),
17133	"u_scale_a": new Uniform1f(context, locations.u_scale_a),
17134	"u_scale_b": new Uniform1f(context, locations.u_scale_b),
17135	"u_pixel_coord_upper": new Uniform2f(context, locations.u_pixel_coord_upper),
17136	"u_pixel_coord_lower": new Uniform2f(context, locations.u_pixel_coord_lower),
17137	"u_tile_units_to_pixels": new Uniform1f(context, locations.u_tile_units_to_pixels)
17138});
17139const backgroundUniformValues = (opacity, color) => ({
17140	"u_opacity": opacity,
17141	"u_color": color
17142});
17143const backgroundPatternUniformValues = (opacity, painter, image, tile, crossfade) => extend(bgPatternUniformValues(image, crossfade, painter, tile), { "u_opacity": opacity });
17144//#endregion
17145//#region src/webgl/program/atmosphere_program.ts
17146const atmosphereUniforms = (context, locations) => ({
17147	"u_sun_pos": new Uniform3f(context, locations.u_sun_pos),
17148	"u_atmosphere_blend": new Uniform1f(context, locations.u_atmosphere_blend),
17149	"u_globe_position": new Uniform3f(context, locations.u_globe_position),
17150	"u_globe_radius": new Uniform1f(context, locations.u_globe_radius),
17151	"u_inv_proj_matrix": new UniformMatrix4f(context, locations.u_inv_proj_matrix)
17152});
17153const atmosphereUniformValues = (sunPos, atmosphereBlend, globePosition, globeRadius, invProjMatrix) => ({
17154	"u_sun_pos": sunPos,
17155	"u_atmosphere_blend": atmosphereBlend,
17156	"u_globe_position": globePosition,
17157	"u_globe_radius": globeRadius,
17158	"u_inv_proj_matrix": invProjMatrix
17159});
17160//#endregion
17161//#region src/webgl/program/sky_program.ts
17162const skyUniforms = (context, locations) => ({
17163	"u_sky_color": new UniformColor(context, locations.u_sky_color),
17164	"u_horizon_color": new UniformColor(context, locations.u_horizon_color),
17165	"u_horizon": new Uniform2f(context, locations.u_horizon),
17166	"u_horizon_normal": new Uniform2f(context, locations.u_horizon_normal),
17167	"u_sky_horizon_blend": new Uniform1f(context, locations.u_sky_horizon_blend),
17168	"u_sky_blend": new Uniform1f(context, locations.u_sky_blend),
17169	"u_inv_proj_matrix": new UniformMatrix4f(context, locations.u_inv_proj_matrix),
17170	"u_globe_position": new Uniform3f(context, locations.u_globe_position),
17171	"u_globe_radius": new Uniform1f(context, locations.u_globe_radius),
17172	"u_atmosphere_blend": new Uniform1f(context, locations.u_atmosphere_blend)
17173});
17174const skyUniformValues = (sky, transform, pixelRatio) => {
17175	const cosRoll = Math.cos(transform.rollInRadians);
17176	const sinRoll = Math.sin(transform.rollInRadians);
17177	const mercatorHorizon = getMercatorHorizon(transform);
17178	const skyBlend = transform.getProjectionData({
17179		overscaledTileID: null,
17180		applyGlobeMatrix: true,
17181		applyTerrainMatrix: true
17182	}).projectionTransition;
17183	const globePosition = getGlobeCenterInViewSpace(transform);
17184	const globeRadius = getGlobeRadiusPixels(transform.worldSize, transform.center.lat);
17185	return {
17186		"u_sky_color": sky.properties.get("sky-color"),
17187		"u_horizon_color": sky.properties.get("horizon-color"),
17188		"u_horizon": [(transform.width / 2 - mercatorHorizon * sinRoll) * pixelRatio, (transform.height / 2 + mercatorHorizon * cosRoll) * pixelRatio],
17189		"u_horizon_normal": [-sinRoll, cosRoll],
17190		"u_sky_horizon_blend": sky.properties.get("sky-horizon-blend") * transform.height / 2 * pixelRatio,
17191		"u_sky_blend": skyBlend,
17192		"u_inv_proj_matrix": transform.inverseProjectionMatrix,
17193		"u_globe_position": globePosition,
17194		"u_globe_radius": globeRadius,
17195		"u_atmosphere_blend": sky.properties.get("atmosphere-blend") * getAtmosphereAltitudeBlend(length(globePosition) - globeRadius, globeRadius)
17196	};
17197};
17198//#endregion
17199//#region src/webgl/program/program_uniforms.ts
17200const emptyUniforms = (_, __) => ({});
17201const programUniforms = {
17202	fillExtrusion: fillExtrusionUniforms,
17203	fillExtrusionPattern: fillExtrusionPatternUniforms,
17204	fill: fillUniforms,
17205	fillPattern: fillPatternUniforms,
17206	fillOutline: fillOutlineUniforms,
17207	fillOutlinePattern: fillOutlinePatternUniforms,
17208	circle: circleUniforms,
17209	collisionBox: emptyUniforms,
17210	collisionCircle: emptyUniforms,
17211	debug: debugUniforms,
17212	depth: emptyUniforms,
17213	clippingMask: emptyUniforms,
17214	heatmap: heatmapUniforms,
17215	heatmapTexture: heatmapTextureUniforms,
17216	hillshade: hillshadeUniforms,
17217	hillshadePrepare: hillshadePrepareUniforms,
17218	colorRelief: colorReliefUniforms,
17219	line: lineUniforms,
17220	lineGradient: lineGradientUniforms,
17221	linePattern: linePatternUniforms,
17222	lineSDF: lineSDFUniforms,
17223	lineGradientSDF: lineGradientSDFUniforms,
17224	layerOpacity: layerOpacityUniforms,
17225	raster: rasterUniforms,
17226	symbolIcon: symbolIconUniforms,
17227	symbolSDF: symbolSDFUniforms,
17228	symbolTextAndIcon: symbolTextAndIconUniforms,
17229	background: backgroundUniforms,
17230	backgroundPattern: backgroundPatternUniforms,
17231	terrain: terrainUniforms,
17232	terrainDepth: terrainDepthUniforms,
17233	atmosphere: atmosphereUniforms,
17234	sky: skyUniforms
17235};
17236//#endregion
17237//#region src/webgl/index_buffer.ts
17238/**
17239* @internal
17240* an index buffer class
17241*/
17242var IndexBuffer = class {
17243	constructor(context, array, dynamicDraw) {
17244		this.context = context;
17245		const gl = context.gl;
17246		this.buffer = gl.createBuffer();
17247		this.dynamicDraw = Boolean(dynamicDraw);
17248		this.context.unbindVAO();
17249		context.bindElementBuffer.set(this.buffer);
17250		gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, array.arrayBuffer, this.dynamicDraw ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW);
17251		if (!this.dynamicDraw) array.freeBufferAfterUpload();
17252	}
17253	bind() {
17254		this.context.bindElementBuffer.set(this.buffer);
17255	}
17256	updateData(array) {
17257		const gl = this.context.gl;
17258		if (!this.dynamicDraw) throw new Error("Attempted to update data while not in dynamic mode.");
17259		this.context.unbindVAO();
17260		this.bind();
17261		gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, array.arrayBuffer);
17262	}
17263	destroy() {
17264		const gl = this.context.gl;
17265		if (this.buffer) {
17266			gl.deleteBuffer(this.buffer);
17267			delete this.buffer;
17268		}
17269	}
17270};
17271//#endregion
17272//#region src/webgl/vertex_buffer.ts
17273/**
17274* An Enum for AttributeType
17275*/
17276const AttributeType = {
17277	Int8: "BYTE",
17278	Uint8: "UNSIGNED_BYTE",
17279	Int16: "SHORT",
17280	Uint16: "UNSIGNED_SHORT",
17281	Int32: "INT",
17282	Uint32: "UNSIGNED_INT",
17283	Float32: "FLOAT"
17284};
17285/**
17286* @internal
17287* The `VertexBuffer` class turns a `StructArray` into a WebGL buffer. Each member of the StructArray's
17288* Struct type is converted to a WebGL attribute.
17289*/
17290var VertexBuffer = class {
17291	/**
17292	* @param dynamicDraw - Whether this buffer will be repeatedly updated.
17293	*/
17294	constructor(context, array, attributes, dynamicDraw) {
17295		this.length = array.length;
17296		this.attributes = attributes;
17297		this.itemSize = array.bytesPerElement;
17298		this.dynamicDraw = dynamicDraw;
17299		this.context = context;
17300		const gl = context.gl;
17301		this.buffer = gl.createBuffer();
17302		context.bindVertexBuffer.set(this.buffer);
17303		gl.bufferData(gl.ARRAY_BUFFER, array.arrayBuffer, this.dynamicDraw ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW);
17304		if (!this.dynamicDraw) array.freeBufferAfterUpload();
17305	}
17306	bind() {
17307		this.context.bindVertexBuffer.set(this.buffer);
17308	}
17309	updateData(array) {
17310		if (array.length !== this.length) throw new Error(`Length of new data is ${array.length}, which doesn't match current length of ${this.length}`);
17311		const gl = this.context.gl;
17312		this.bind();
17313		gl.bufferSubData(gl.ARRAY_BUFFER, 0, array.arrayBuffer);
17314	}
17315	enableAttributes(gl, program) {
17316		for (const member of this.attributes) {
17317			const attribute = program.attributes[member.name];
17318			if (attribute !== void 0) gl.enableVertexAttribArray(attribute.location);
17319		}
17320	}
17321	/**
17322	* Set the attribute pointers in a WebGL context
17323	* @param gl - The WebGL context
17324	* @param program - The active WebGL program
17325	* @param vertexOffset - Index of the starting vertex of the segment
17326	*/
17327	setVertexAttribPointers(gl, program, vertexOffset) {
17328		for (const member of this.attributes) {
17329			const attribute = program.attributes[member.name];
17330			if (attribute !== void 0) {
17331				const offset = member.offset + this.itemSize * (vertexOffset || 0);
17332				if (attribute.isInteger) gl.vertexAttribIPointer(attribute.location, member.components, gl[AttributeType[member.type]], this.itemSize, offset);
17333				else gl.vertexAttribPointer(attribute.location, member.components, gl[AttributeType[member.type]], false, this.itemSize, offset);
17334			}
17335		}
17336	}
17337	/**
17338	* Destroy the GL buffer bound to the given WebGL context
17339	*/
17340	destroy() {
17341		const gl = this.context.gl;
17342		if (this.buffer) {
17343			gl.deleteBuffer(this.buffer);
17344			delete this.buffer;
17345		}
17346	}
17347};
17348//#endregion
17349//#region src/webgl/value.ts
17350var BaseValue = class {
17351	constructor(context) {
17352		this.gl = context.gl;
17353		this.default = this.getDefault();
17354		this.current = this.default;
17355		this.dirty = false;
17356	}
17357	get() {
17358		return this.current;
17359	}
17360	set(value) {}
17361	getDefault() {
17362		return this.default;
17363	}
17364	setDefault() {
17365		this.set(this.default);
17366	}
17367};
17368var ClearColor = class extends BaseValue {
17369	getDefault() {
17370		return Color.transparent;
17371	}
17372	set(v) {
17373		const c = this.current;
17374		if (v.r === c.r && v.g === c.g && v.b === c.b && v.a === c.a && !this.dirty) return;
17375		this.gl.clearColor(v.r, v.g, v.b, v.a);
17376		this.current = v;
17377		this.dirty = false;
17378	}
17379};
17380var ClearDepth = class extends BaseValue {
17381	getDefault() {
17382		return 1;
17383	}
17384	set(v) {
17385		if (v === this.current && !this.dirty) return;
17386		this.gl.clearDepth(v);
17387		this.current = v;
17388		this.dirty = false;
17389	}
17390};
17391var ClearStencil = class extends BaseValue {
17392	getDefault() {
17393		return 0;
17394	}
17395	set(v) {
17396		if (v === this.current && !this.dirty) return;
17397		this.gl.clearStencil(v);
17398		this.current = v;
17399		this.dirty = false;
17400	}
17401};
17402var ColorMask = class extends BaseValue {
17403	getDefault() {
17404		return [
17405			true,
17406			true,
17407			true,
17408			true
17409		];
17410	}
17411	set(v) {
17412		const c = this.current;
17413		if (v[0] === c[0] && v[1] === c[1] && v[2] === c[2] && v[3] === c[3] && !this.dirty) return;
17414		this.gl.colorMask(v[0], v[1], v[2], v[3]);
17415		this.current = v;
17416		this.dirty = false;
17417	}
17418};
17419var DepthMask = class extends BaseValue {
17420	getDefault() {
17421		return true;
17422	}
17423	set(v) {
17424		if (v === this.current && !this.dirty) return;
17425		this.gl.depthMask(v);
17426		this.current = v;
17427		this.dirty = false;
17428	}
17429};
17430var StencilMask = class extends BaseValue {
17431	getDefault() {
17432		return 255;
17433	}
17434	set(v) {
17435		if (v === this.current && !this.dirty) return;
17436		this.gl.stencilMask(v);
17437		this.current = v;
17438		this.dirty = false;
17439	}
17440};
17441var StencilFunc = class extends BaseValue {
17442	getDefault() {
17443		return {
17444			func: this.gl.ALWAYS,
17445			ref: 0,
17446			mask: 255
17447		};
17448	}
17449	set(v) {
17450		const c = this.current;
17451		if (v.func === c.func && v.ref === c.ref && v.mask === c.mask && !this.dirty) return;
17452		this.gl.stencilFunc(v.func, v.ref, v.mask);
17453		this.current = v;
17454		this.dirty = false;
17455	}
17456};
17457var StencilOp = class extends BaseValue {
17458	getDefault() {
17459		const gl = this.gl;
17460		return [
17461			gl.KEEP,
17462			gl.KEEP,
17463			gl.KEEP
17464		];
17465	}
17466	set(v) {
17467		const c = this.current;
17468		if (v[0] === c[0] && v[1] === c[1] && v[2] === c[2] && !this.dirty) return;
17469		this.gl.stencilOp(v[0], v[1], v[2]);
17470		this.current = v;
17471		this.dirty = false;
17472	}
17473};
17474var StencilTest = class extends BaseValue {
17475	getDefault() {
17476		return false;
17477	}
17478	set(v) {
17479		if (v === this.current && !this.dirty) return;
17480		const gl = this.gl;
17481		if (v) gl.enable(gl.STENCIL_TEST);
17482		else gl.disable(gl.STENCIL_TEST);
17483		this.current = v;
17484		this.dirty = false;
17485	}
17486};
17487var DepthRange = class extends BaseValue {
17488	getDefault() {
17489		return [0, 1];
17490	}
17491	set(v) {
17492		const c = this.current;
17493		if (v[0] === c[0] && v[1] === c[1] && !this.dirty) return;
17494		this.gl.depthRange(v[0], v[1]);
17495		this.current = v;
17496		this.dirty = false;
17497	}
17498};
17499var DepthTest = class extends BaseValue {
17500	getDefault() {
17501		return false;
17502	}
17503	set(v) {
17504		if (v === this.current && !this.dirty) return;
17505		const gl = this.gl;
17506		if (v) gl.enable(gl.DEPTH_TEST);
17507		else gl.disable(gl.DEPTH_TEST);
17508		this.current = v;
17509		this.dirty = false;
17510	}
17511};
17512var DepthFunc = class extends BaseValue {
17513	getDefault() {
17514		return this.gl.LESS;
17515	}
17516	set(v) {
17517		if (v === this.current && !this.dirty) return;
17518		this.gl.depthFunc(v);
17519		this.current = v;
17520		this.dirty = false;
17521	}
17522};
17523var Blend = class extends BaseValue {
17524	getDefault() {
17525		return false;
17526	}
17527	set(v) {
17528		if (v === this.current && !this.dirty) return;
17529		const gl = this.gl;
17530		if (v) gl.enable(gl.BLEND);
17531		else gl.disable(gl.BLEND);
17532		this.current = v;
17533		this.dirty = false;
17534	}
17535};
17536var BlendFunc = class extends BaseValue {
17537	getDefault() {
17538		const gl = this.gl;
17539		return [gl.ONE, gl.ZERO];
17540	}
17541	set(v) {
17542		const c = this.current;
17543		if (v[0] === c[0] && v[1] === c[1] && !this.dirty) return;
17544		this.gl.blendFunc(v[0], v[1]);
17545		this.current = v;
17546		this.dirty = false;
17547	}
17548};
17549var BlendColor = class extends BaseValue {
17550	getDefault() {
17551		return Color.transparent;
17552	}
17553	set(v) {
17554		const c = this.current;
17555		if (v.r === c.r && v.g === c.g && v.b === c.b && v.a === c.a && !this.dirty) return;
17556		this.gl.blendColor(v.r, v.g, v.b, v.a);
17557		this.current = v;
17558		this.dirty = false;
17559	}
17560};
17561var BlendEquation = class extends BaseValue {
17562	getDefault() {
17563		return this.gl.FUNC_ADD;
17564	}
17565	set(v) {
17566		if (v === this.current && !this.dirty) return;
17567		this.gl.blendEquation(v);
17568		this.current = v;
17569		this.dirty = false;
17570	}
17571};
17572var CullFace = class extends BaseValue {
17573	getDefault() {
17574		return false;
17575	}
17576	set(v) {
17577		if (v === this.current && !this.dirty) return;
17578		const gl = this.gl;
17579		if (v) gl.enable(gl.CULL_FACE);
17580		else gl.disable(gl.CULL_FACE);
17581		this.current = v;
17582		this.dirty = false;
17583	}
17584};
17585var CullFaceSide = class extends BaseValue {
17586	getDefault() {
17587		return this.gl.BACK;
17588	}
17589	set(v) {
17590		if (v === this.current && !this.dirty) return;
17591		this.gl.cullFace(v);
17592		this.current = v;
17593		this.dirty = false;
17594	}
17595};
17596var FrontFace = class extends BaseValue {
17597	getDefault() {
17598		return this.gl.CCW;
17599	}
17600	set(v) {
17601		if (v === this.current && !this.dirty) return;
17602		this.gl.frontFace(v);
17603		this.current = v;
17604		this.dirty = false;
17605	}
17606};
17607var ProgramValue = class extends BaseValue {
17608	getDefault() {
17609		return null;
17610	}
17611	set(v) {
17612		if (v === this.current && !this.dirty) return;
17613		this.gl.useProgram(v);
17614		this.current = v;
17615		this.dirty = false;
17616	}
17617};
17618var ActiveTextureUnit = class extends BaseValue {
17619	getDefault() {
17620		return this.gl.TEXTURE0;
17621	}
17622	set(v) {
17623		if (v === this.current && !this.dirty) return;
17624		this.gl.activeTexture(v);
17625		this.current = v;
17626		this.dirty = false;
17627	}
17628};
17629var Viewport = class extends BaseValue {
17630	getDefault() {
17631		const gl = this.gl;
17632		return [
17633			0,
17634			0,
17635			gl.drawingBufferWidth,
17636			gl.drawingBufferHeight
17637		];
17638	}
17639	set(v) {
17640		const c = this.current;
17641		if (v[0] === c[0] && v[1] === c[1] && v[2] === c[2] && v[3] === c[3] && !this.dirty) return;
17642		this.gl.viewport(v[0], v[1], v[2], v[3]);
17643		this.current = v;
17644		this.dirty = false;
17645	}
17646};
17647var BindFramebuffer = class extends BaseValue {
17648	getDefault() {
17649		return null;
17650	}
17651	set(v) {
17652		if (v === this.current && !this.dirty) return;
17653		const gl = this.gl;
17654		gl.bindFramebuffer(gl.FRAMEBUFFER, v);
17655		this.current = v;
17656		this.dirty = false;
17657	}
17658};
17659var BindRenderbuffer = class extends BaseValue {
17660	getDefault() {
17661		return null;
17662	}
17663	set(v) {
17664		if (v === this.current && !this.dirty) return;
17665		const gl = this.gl;
17666		gl.bindRenderbuffer(gl.RENDERBUFFER, v);
17667		this.current = v;
17668		this.dirty = false;
17669	}
17670};
17671var BindTexture = class extends BaseValue {
17672	getDefault() {
17673		return null;
17674	}
17675	set(v) {
17676		if (v === this.current && !this.dirty) return;
17677		const gl = this.gl;
17678		gl.bindTexture(gl.TEXTURE_2D, v);
17679		this.current = v;
17680		this.dirty = false;
17681	}
17682};
17683var BindVertexBuffer = class extends BaseValue {
17684	getDefault() {
17685		return null;
17686	}
17687	set(v) {
17688		if (v === this.current && !this.dirty) return;
17689		const gl = this.gl;
17690		gl.bindBuffer(gl.ARRAY_BUFFER, v);
17691		this.current = v;
17692		this.dirty = false;
17693	}
17694};
17695var BindElementBuffer = class extends BaseValue {
17696	getDefault() {
17697		return null;
17698	}
17699	set(v) {
17700		const gl = this.gl;
17701		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, v);
17702		this.current = v;
17703		this.dirty = false;
17704	}
17705};
17706var BindVertexArray = class extends BaseValue {
17707	getDefault() {
17708		return null;
17709	}
17710	set(v) {
17711		if (v === this.current && !this.dirty) return;
17712		this.gl.bindVertexArray(v);
17713		this.current = v;
17714		this.dirty = false;
17715	}
17716};
17717var PixelStoreUnpack = class extends BaseValue {
17718	getDefault() {
17719		return 4;
17720	}
17721	set(v) {
17722		if (v === this.current && !this.dirty) return;
17723		const gl = this.gl;
17724		gl.pixelStorei(gl.UNPACK_ALIGNMENT, v);
17725		this.current = v;
17726		this.dirty = false;
17727	}
17728};
17729var PixelStoreUnpackPremultiplyAlpha = class extends BaseValue {
17730	getDefault() {
17731		return false;
17732	}
17733	set(v) {
17734		if (v === this.current && !this.dirty) return;
17735		const gl = this.gl;
17736		gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, v);
17737		this.current = v;
17738		this.dirty = false;
17739	}
17740};
17741var PixelStoreUnpackFlipY = class extends BaseValue {
17742	getDefault() {
17743		return false;
17744	}
17745	set(v) {
17746		if (v === this.current && !this.dirty) return;
17747		const gl = this.gl;
17748		gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, v);
17749		this.current = v;
17750		this.dirty = false;
17751	}
17752};
17753var FramebufferAttachment = class extends BaseValue {
17754	constructor(context, parent) {
17755		super(context);
17756		this.context = context;
17757		this.parent = parent;
17758	}
17759	getDefault() {
17760		return null;
17761	}
17762};
17763var ColorAttachment = class extends FramebufferAttachment {
17764	setDirty() {
17765		this.dirty = true;
17766	}
17767	set(v) {
17768		if (v === this.current && !this.dirty) return;
17769		this.context.bindFramebuffer.set(this.parent);
17770		const gl = this.gl;
17771		gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, v, 0);
17772		this.current = v;
17773		this.dirty = false;
17774	}
17775};
17776var DepthAttachment = class extends FramebufferAttachment {
17777	set(v) {
17778		if (v === this.current && !this.dirty) return;
17779		this.context.bindFramebuffer.set(this.parent);
17780		const gl = this.gl;
17781		gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, v);
17782		this.current = v;
17783		this.dirty = false;
17784	}
17785};
17786var DepthStencilAttachment = class extends FramebufferAttachment {
17787	set(v) {
17788		if (v === this.current && !this.dirty) return;
17789		this.context.bindFramebuffer.set(this.parent);
17790		const gl = this.gl;
17791		gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, v);
17792		this.current = v;
17793		this.dirty = false;
17794	}
17795};
17796//#endregion
17797//#region src/webgl/framebuffer.ts
17798/**
17799* @internal
17800* A framebuffer holder object
17801*/
17802var Framebuffer = class {
17803	constructor(context, width, height, hasDepth, hasStencil) {
17804		this.context = context;
17805		this.width = width;
17806		this.height = height;
17807		const gl = context.gl;
17808		const fbo = this.framebuffer = gl.createFramebuffer();
17809		this.colorAttachment = new ColorAttachment(context, fbo);
17810		if (hasDepth) this.depthAttachment = hasStencil ? new DepthStencilAttachment(context, fbo) : new DepthAttachment(context, fbo);
17811		else if (hasStencil) throw new Error("Stencil cannot be set without depth");
17812	}
17813	destroy() {
17814		const gl = this.context.gl;
17815		const texture = this.colorAttachment.get();
17816		if (texture) gl.deleteTexture(texture);
17817		if (this.depthAttachment) {
17818			const renderbuffer = this.depthAttachment.get();
17819			if (renderbuffer) gl.deleteRenderbuffer(renderbuffer);
17820		}
17821		gl.deleteFramebuffer(this.framebuffer);
17822	}
17823};
17824//#endregion
17825//#region src/webgl/frame_uniform_buffer.ts
17826const layout = std140Layout([
17827	{
17828		name: "u_units_to_pixels",
17829		type: "vec2"
17830	},
17831	{
17832		name: "u_world_size",
17833		type: "vec2"
17834	},
17835	{
17836		name: "u_camera_to_center_distance",
17837		type: "float"
17838	},
17839	{
17840		name: "u_symbol_fade_change",
17841		type: "float"
17842	},
17843	{
17844		name: "u_aspect_ratio",
17845		type: "float"
17846	},
17847	{
17848		name: "u_device_pixel_ratio",
17849		type: "float"
17850	},
17851	{
17852		name: "u_viewport_size",
17853		type: "vec2"
17854	},
17855	{
17856		name: "u_pixel_extrude_scale",
17857		type: "vec2"
17858	},
17859	{
17860		name: "u_pitch",
17861		type: "float"
17862	}
17863]);
17864const offsets = layout.offsets;
17865/**
17866* @internal
17867* The buffer behind the `FrameUBO` block in the shader preludes, written once per frame by `Painter.render`.
17868*/
17869function createFrameUniformBuffer(context) {
17870	return new UniformBuffer(context, UBO_BINDINGS.FrameUBO, layout);
17871}
17872function updateFrameUniformBuffer(buffer, painter) {
17873	const f32 = buffer.pending;
17874	const { transform } = painter;
17875	const gl = painter.context.gl;
17876	f32[offsets.u_units_to_pixels] = 1 / transform.pixelsToGLUnits[0];
17877	f32[offsets.u_units_to_pixels + 1] = 1 / transform.pixelsToGLUnits[1];
17878	f32[offsets.u_world_size] = gl.drawingBufferWidth;
17879	f32[offsets.u_world_size + 1] = gl.drawingBufferHeight;
17880	f32[offsets.u_camera_to_center_distance] = transform.cameraToCenterDistance;
17881	f32[offsets.u_symbol_fade_change] = painter.options.fadeDuration ? painter.symbolFadeChange : 1;
17882	f32[offsets.u_aspect_ratio] = transform.width / transform.height;
17883	f32[offsets.u_device_pixel_ratio] = painter.pixelRatio;
17884	f32[offsets.u_viewport_size] = transform.width;
17885	f32[offsets.u_viewport_size + 1] = transform.height;
17886	f32[offsets.u_pixel_extrude_scale] = 1 / transform.width;
17887	f32[offsets.u_pixel_extrude_scale + 1] = 1 / transform.height;
17888	f32[offsets.u_pitch] = transform.pitch / 360 * 2 * Math.PI;
17889	buffer.upload();
17890}
17891//#endregion
17892//#region src/webgl/color_mode.ts
17893const ZERO = 0;
17894const ONE = 1;
17895const ONE_MINUS_SRC_ALPHA = 771;
17896var ColorMode = class {
17897	constructor(blendFunction, blendColor, mask) {
17898		this.blendFunction = blendFunction;
17899		this.blendColor = blendColor;
17900		this.mask = mask;
17901	}
17902};
17903ColorMode.Replace = [ONE, ZERO];
17904ColorMode.disabled = new ColorMode(ColorMode.Replace, Color.transparent, [
17905	false,
17906	false,
17907	false,
17908	false
17909]);
17910ColorMode.unblended = new ColorMode(ColorMode.Replace, Color.transparent, [
17911	true,
17912	true,
17913	true,
17914	true
17915]);
17916ColorMode.alphaBlended = new ColorMode([ONE, ONE_MINUS_SRC_ALPHA], Color.transparent, [
17917	true,
17918	true,
17919	true,
17920	true
17921]);
17922//#endregion
17923//#region src/webgl/context.ts
17924/**
17925* @internal
17926* A webgl wrapper class to allow injection, mocking and abstraction
17927*/
17928var Context = class {
17929	constructor(gl) {
17930		this.gl = gl;
17931		this.clearColor = new ClearColor(this);
17932		this.clearDepth = new ClearDepth(this);
17933		this.clearStencil = new ClearStencil(this);
17934		this.colorMask = new ColorMask(this);
17935		this.depthMask = new DepthMask(this);
17936		this.stencilMask = new StencilMask(this);
17937		this.stencilFunc = new StencilFunc(this);
17938		this.stencilOp = new StencilOp(this);
17939		this.stencilTest = new StencilTest(this);
17940		this.depthRange = new DepthRange(this);
17941		this.depthTest = new DepthTest(this);
17942		this.depthFunc = new DepthFunc(this);
17943		this.blend = new Blend(this);
17944		this.blendFunc = new BlendFunc(this);
17945		this.blendColor = new BlendColor(this);
17946		this.blendEquation = new BlendEquation(this);
17947		this.cullFace = new CullFace(this);
17948		this.cullFaceSide = new CullFaceSide(this);
17949		this.frontFace = new FrontFace(this);
17950		this.program = new ProgramValue(this);
17951		this.activeTexture = new ActiveTextureUnit(this);
17952		this.viewport = new Viewport(this);
17953		this.bindFramebuffer = new BindFramebuffer(this);
17954		this.bindRenderbuffer = new BindRenderbuffer(this);
17955		this.bindTexture = new BindTexture(this);
17956		this.bindVertexBuffer = new BindVertexBuffer(this);
17957		this.bindElementBuffer = new BindElementBuffer(this);
17958		this.bindVertexArray = new BindVertexArray(this);
17959		this.pixelStoreUnpack = new PixelStoreUnpack(this);
17960		this.pixelStoreUnpackPremultiplyAlpha = new PixelStoreUnpackPremultiplyAlpha(this);
17961		this.pixelStoreUnpackFlipY = new PixelStoreUnpackFlipY(this);
17962		this.extTextureFilterAnisotropic = gl.getExtension("EXT_texture_filter_anisotropic");
17963		if (this.extTextureFilterAnisotropic) this.extTextureFilterAnisotropicMax = gl.getParameter(this.extTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
17964		this.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
17965		gl.getExtension("EXT_color_buffer_half_float");
17966		gl.getExtension("EXT_color_buffer_float");
17967		this.projectionUniformBuffer = createProjectionUniformBuffer(this);
17968		this.terrainUniformBuffer = createTerrainUniformBuffer(this);
17969		this.frameUniformBuffer = createFrameUniformBuffer(this);
17970	}
17971	setDefault() {
17972		this.unbindVAO();
17973		this.clearColor.setDefault();
17974		this.clearDepth.setDefault();
17975		this.clearStencil.setDefault();
17976		this.colorMask.setDefault();
17977		this.depthMask.setDefault();
17978		this.stencilMask.setDefault();
17979		this.stencilFunc.setDefault();
17980		this.stencilOp.setDefault();
17981		this.stencilTest.setDefault();
17982		this.depthRange.setDefault();
17983		this.depthTest.setDefault();
17984		this.depthFunc.setDefault();
17985		this.blend.setDefault();
17986		this.blendFunc.setDefault();
17987		this.blendColor.setDefault();
17988		this.blendEquation.setDefault();
17989		this.cullFace.setDefault();
17990		this.cullFaceSide.setDefault();
17991		this.frontFace.setDefault();
17992		this.program.setDefault();
17993		this.activeTexture.setDefault();
17994		this.bindFramebuffer.setDefault();
17995		this.pixelStoreUnpack.setDefault();
17996		this.pixelStoreUnpackPremultiplyAlpha.setDefault();
17997		this.pixelStoreUnpackFlipY.setDefault();
17998	}
17999	setDirty() {
18000		this.clearColor.dirty = true;
18001		this.clearDepth.dirty = true;
18002		this.clearStencil.dirty = true;
18003		this.colorMask.dirty = true;
18004		this.depthMask.dirty = true;
18005		this.stencilMask.dirty = true;
18006		this.stencilFunc.dirty = true;
18007		this.stencilOp.dirty = true;
18008		this.stencilTest.dirty = true;
18009		this.depthRange.dirty = true;
18010		this.depthTest.dirty = true;
18011		this.depthFunc.dirty = true;
18012		this.blend.dirty = true;
18013		this.blendFunc.dirty = true;
18014		this.blendColor.dirty = true;
18015		this.blendEquation.dirty = true;
18016		this.cullFace.dirty = true;
18017		this.cullFaceSide.dirty = true;
18018		this.frontFace.dirty = true;
18019		this.program.dirty = true;
18020		this.activeTexture.dirty = true;
18021		this.viewport.dirty = true;
18022		this.bindFramebuffer.dirty = true;
18023		this.bindRenderbuffer.dirty = true;
18024		this.bindTexture.dirty = true;
18025		this.bindVertexBuffer.dirty = true;
18026		this.bindElementBuffer.dirty = true;
18027		this.bindVertexArray.dirty = true;
18028		this.pixelStoreUnpack.dirty = true;
18029		this.pixelStoreUnpackPremultiplyAlpha.dirty = true;
18030		this.pixelStoreUnpackFlipY.dirty = true;
18031		this.projectionUniformBuffer.bindingDirty = true;
18032		this.terrainUniformBuffer.bindingDirty = true;
18033		this.frameUniformBuffer.bindingDirty = true;
18034	}
18035	/**
18036	* Reset some GL state to default values before handing users the raw context, as we do for
18037	* custom layers and WebGL style images, to avoid hard-to-debug bugs in their code.
18038	*
18039	* MapLibre restores all of its own state afterwards, so the only state worth resetting first
18040	* is state users would be surprised to find dirty: `CULL_FACE`, `TEXTURE0` and the three
18041	* `UNPACK_` settings, whose defaults are meaningful enough that most code assumes them.
18042	* The vertex array is unbound rather than reset, so that MapLibre never has to track it and
18043	* a user's `vertexAttribPointer` calls cannot land on one of ours.
18044	*/
18045	setCustomLayerDefaults() {
18046		this.unbindVAO();
18047		this.cullFace.setDefault();
18048		this.activeTexture.setDefault();
18049		this.pixelStoreUnpack.setDefault();
18050		this.pixelStoreUnpackPremultiplyAlpha.setDefault();
18051		this.pixelStoreUnpackFlipY.setDefault();
18052	}
18053	createIndexBuffer(array, dynamicDraw) {
18054		return new IndexBuffer(this, array, dynamicDraw);
18055	}
18056	createVertexBuffer(array, attributes, dynamicDraw) {
18057		return new VertexBuffer(this, array, attributes, dynamicDraw);
18058	}
18059	createRenderbuffer(storageFormat, width, height) {
18060		const gl = this.gl;
18061		const rbo = gl.createRenderbuffer();
18062		this.bindRenderbuffer.set(rbo);
18063		gl.renderbufferStorage(gl.RENDERBUFFER, storageFormat, width, height);
18064		this.bindRenderbuffer.set(null);
18065		return rbo;
18066	}
18067	createFramebuffer(width, height, hasDepth, hasStencil) {
18068		return new Framebuffer(this, width, height, hasDepth, hasStencil);
18069	}
18070	clear({ color, depth, stencil }) {
18071		const gl = this.gl;
18072		let mask = 0;
18073		if (color) {
18074			mask |= gl.COLOR_BUFFER_BIT;
18075			this.clearColor.set(color);
18076			this.colorMask.set([
18077				true,
18078				true,
18079				true,
18080				true
18081			]);
18082		}
18083		if (typeof depth !== "undefined") {
18084			mask |= gl.DEPTH_BUFFER_BIT;
18085			this.depthRange.set([0, 1]);
18086			this.clearDepth.set(depth);
18087			this.depthMask.set(true);
18088		}
18089		if (typeof stencil !== "undefined") {
18090			mask |= gl.STENCIL_BUFFER_BIT;
18091			this.clearStencil.set(stencil);
18092			this.stencilMask.set(255);
18093		}
18094		gl.clear(mask);
18095	}
18096	setCullFace(cullFaceMode) {
18097		if (cullFaceMode.enable === false) this.cullFace.set(false);
18098		else {
18099			this.cullFace.set(true);
18100			this.cullFaceSide.set(cullFaceMode.mode);
18101			this.frontFace.set(cullFaceMode.frontFace);
18102		}
18103	}
18104	setDepthMode(depthMode) {
18105		if (depthMode.func === this.gl.ALWAYS && !depthMode.mask) this.depthTest.set(false);
18106		else {
18107			this.depthTest.set(true);
18108			this.depthFunc.set(depthMode.func);
18109			this.depthMask.set(depthMode.mask);
18110			this.depthRange.set(depthMode.range);
18111		}
18112	}
18113	setStencilMode(stencilMode) {
18114		if (stencilMode.test.func === this.gl.ALWAYS && !stencilMode.mask) this.stencilTest.set(false);
18115		else {
18116			this.stencilTest.set(true);
18117			this.stencilMask.set(stencilMode.mask);
18118			this.stencilOp.set([
18119				stencilMode.fail,
18120				stencilMode.depthFail,
18121				stencilMode.pass
18122			]);
18123			this.stencilFunc.set({
18124				func: stencilMode.test.func,
18125				ref: stencilMode.ref,
18126				mask: stencilMode.test.mask
18127			});
18128		}
18129	}
18130	setColorMode(colorMode) {
18131		if (deepEqual(colorMode.blendFunction, ColorMode.Replace)) this.blend.set(false);
18132		else {
18133			this.blend.set(true);
18134			this.blendFunc.set(colorMode.blendFunction);
18135			this.blendColor.set(colorMode.blendColor);
18136		}
18137		this.colorMask.set(colorMode.mask);
18138	}
18139	createVertexArray() {
18140		return this.gl.createVertexArray();
18141	}
18142	deleteVertexArray(x) {
18143		this.gl.deleteVertexArray(x);
18144	}
18145	unbindVAO() {
18146		this.bindVertexArray.set(null);
18147	}
18148};
18149//#endregion
18150//#region src/webgl/depth_mode.ts
18151const ALWAYS$1 = 519;
18152var DepthMode = class {
18153	constructor(depthFunc, depthMask, depthRange) {
18154		this.func = depthFunc;
18155		this.mask = depthMask;
18156		this.range = depthRange;
18157	}
18158};
18159DepthMode.ReadOnly = false;
18160DepthMode.ReadWrite = true;
18161DepthMode.disabled = new DepthMode(ALWAYS$1, DepthMode.ReadOnly, [0, 1]);
18162//#endregion
18163//#region src/webgl/stencil_mode.ts
18164const ALWAYS = 519;
18165const KEEP = 7680;
18166var StencilMode = class {
18167	constructor(test, ref, mask, fail, depthFail, pass) {
18168		this.test = test;
18169		this.ref = ref;
18170		this.mask = mask;
18171		this.fail = fail;
18172		this.depthFail = depthFail;
18173		this.pass = pass;
18174	}
18175};
18176StencilMode.disabled = new StencilMode({
18177	func: ALWAYS,
18178	mask: 0
18179}, 0, 0, KEEP, KEEP, KEEP);
18180//#endregion
18181//#region src/webgl/cull_face_mode.ts
18182const FRONT = 1028;
18183const BACK = 1029;
18184const CCW = 2305;
18185var CullFaceMode = class {
18186	constructor(enable, mode, frontFace) {
18187		this.enable = enable;
18188		this.mode = mode;
18189		this.frontFace = frontFace;
18190	}
18191};
18192CullFaceMode.disabled = new CullFaceMode(false, BACK, CCW);
18193CullFaceMode.backCCW = new CullFaceMode(true, BACK, CCW);
18194CullFaceMode.frontCCW = new CullFaceMode(true, FRONT, CCW);
18195//#endregion
18196//#region src/render/render_context.ts
18197function createRenderContext(transform, projection, terrain) {
18198	const projectionTransition = projection?.transitionState ?? 0;
18199	return {
18200		currentPass: "offscreen",
18201		currentLayer: 0,
18202		opaquePassCutoff: Infinity,
18203		depthRangeFor3D: [0, 1],
18204		isRenderingToTexture: false,
18205		transform,
18206		terrain,
18207		projectionTransition,
18208		isRenderingGlobe: projectionTransition > 0
18209	};
18210}
18211function getProjectionDataForTile(renderContext, tileID, options = {}) {
18212	return renderContext.transform.getProjectionData({
18213		overscaledTileID: tileID,
18214		aligned: options.aligned,
18215		applyGlobeMatrix: !renderContext.isRenderingToTexture,
18216		applyTerrainMatrix: options.applyTerrainMatrix ?? true
18217	});
18218}
18219/**
18220* Returns terrain data for a tile.
18221* Returns null if terrain is not configured or tiles are being rendered to a texture.
18222*/
18223function getTerrainDataForTile(renderContext, tileID) {
18224	if (renderContext.isRenderingToTexture) return null;
18225	return renderContext.terrain?.getTerrainData(tileID) ?? null;
18226}
18227//#endregion
18228//#region src/webgl/draw/draw_collision_debug.ts
18229let quadTriangles;
18230function drawCollisionDebug(painter, tileManager, layer, coords, isText, renderContext) {
18231	const context = painter.context;
18232	const gl = context.gl;
18233	const program = painter.useProgram("collisionBox");
18234	const tileBatches = [];
18235	let circleCount = 0;
18236	let circleOffset = 0;
18237	for (const coord of coords) {
18238		const bucket = tileManager.getTile(coord).getBucket(layer);
18239		if (!bucket) continue;
18240		const buffers = isText ? bucket.textCollisionBox : bucket.iconCollisionBox;
18241		const circleArray = bucket.collisionCircleArray;
18242		if (circleArray.length > 0) {
18243			tileBatches.push({
18244				circleArray,
18245				circleOffset,
18246				coord
18247			});
18248			circleCount += circleArray.length / 4;
18249			circleOffset = circleCount;
18250		}
18251		if (!buffers) continue;
18252		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);
18253	}
18254	if (!isText || !tileBatches.length) return;
18255	const circleProgram = painter.useProgram("collisionCircle");
18256	const vertexData = new CollisionCircleLayoutArray();
18257	vertexData.resize(circleCount * 4);
18258	vertexData._trim();
18259	let vertexOffset = 0;
18260	for (const batch of tileBatches) for (let i = 0; i < batch.circleArray.length / 4; i++) {
18261		const circleIdx = i * 4;
18262		const x = batch.circleArray[circleIdx + 0];
18263		const y = batch.circleArray[circleIdx + 1];
18264		const radius = batch.circleArray[circleIdx + 2];
18265		const collision = batch.circleArray[circleIdx + 3];
18266		vertexData.emplace(vertexOffset++, x, y, radius, collision, 0);
18267		vertexData.emplace(vertexOffset++, x, y, radius, collision, 1);
18268		vertexData.emplace(vertexOffset++, x, y, radius, collision, 2);
18269		vertexData.emplace(vertexOffset++, x, y, radius, collision, 3);
18270	}
18271	if (!quadTriangles || quadTriangles.length < circleCount * 2) quadTriangles = createQuadTriangles(circleCount);
18272	const indexBuffer = context.createIndexBuffer(quadTriangles, true);
18273	const vertexBuffer = context.createVertexBuffer(vertexData, collisionCircleLayout.members, true);
18274	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);
18275	vertexBuffer.destroy();
18276	indexBuffer.destroy();
18277}
18278function createQuadTriangles(quadCount) {
18279	const triCount = quadCount * 2;
18280	const array = new QuadTriangleArray();
18281	array.resize(triCount);
18282	array._trim();
18283	for (let i = 0; i < triCount; i++) {
18284		const idx = i * 6;
18285		array.uint16[idx + 0] = i * 4 + 0;
18286		array.uint16[idx + 1] = i * 4 + 1;
18287		array.uint16[idx + 2] = i * 4 + 2;
18288		array.uint16[idx + 3] = i * 4 + 2;
18289		array.uint16[idx + 4] = i * 4 + 3;
18290		array.uint16[idx + 5] = i * 4 + 0;
18291	}
18292	return array;
18293}
18294//#endregion
18295//#region src/webgl/draw/draw_symbol.ts
18296const identityMat4 = identity(/* @__PURE__ */ new Float32Array(16));
18297function drawSymbols(painter, tileManager, layer, coords, variableOffsets, renderContext) {
18298	if (renderContext.currentPass !== "translucent") return;
18299	const stencilMode = StencilMode.disabled;
18300	const colorMode = painter.colorModeForRenderPass();
18301	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);
18302	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);
18303	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);
18304	if (tileManager.map.showCollisionBoxes) {
18305		drawCollisionDebug(painter, tileManager, layer, coords, true, renderContext);
18306		drawCollisionDebug(painter, tileManager, layer, coords, false, renderContext);
18307	}
18308}
18309function calculateVariableRenderShift(anchor, width, height, textOffset, textBoxScale, renderTextSize) {
18310	const { horizontalAlign, verticalAlign } = getAnchorAlignment(anchor);
18311	const shiftX = -(horizontalAlign - .5) * width;
18312	const shiftY = -(verticalAlign - .5) * height;
18313	return new Point((shiftX / textBoxScale + textOffset[0]) * renderTextSize, (shiftY / textBoxScale + textOffset[1]) * renderTextSize);
18314}
18315function updateVariableAnchors(coords, painter, layer, tileManager, rotationAlignment, pitchAlignment, translate, translateAnchor, variableOffsets) {
18316	const transform = painter.transform;
18317	const terrain = painter.style.map.terrain;
18318	const rotateWithMap = rotationAlignment === "map";
18319	const pitchWithMap = pitchAlignment === "map";
18320	for (const coord of coords) {
18321		const tile = tileManager.getTile(coord);
18322		const bucket = tile.getBucket(layer);
18323		if (!bucket?.text?.segments.get().length) continue;
18324		const sizeData = bucket.textSizeData;
18325		const size = evaluateSizeForZoom(sizeData, transform.zoom);
18326		const pixelToTileScale = pixelsToTileUnits(tile, 1, painter.transform.zoom);
18327		const pitchedLabelPlaneMatrix = getPitchedLabelPlaneMatrix(rotateWithMap, painter.transform, pixelToTileScale);
18328		const updateTextFitIcon = layer.layout.get("icon-text-fit") !== "none" && bucket.hasIconData();
18329		if (size) {
18330			const tileScale = Math.pow(2, transform.zoom - tile.tileID.overscaledZ);
18331			const getElevation = terrain ? (x, y) => terrain.getElevation(coord, x, y) : void 0;
18332			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");
18333		}
18334	}
18335}
18336function getShiftedAnchor(projectedAnchorPoint, projectionContext, rotateWithMap, shift, transformAngle, pitchedTextShiftCorrection) {
18337	const translatedAnchor = projectionContext.tileAnchorPoint.add(new Point(projectionContext.translation[0], projectionContext.translation[1]));
18338	if (projectionContext.pitchWithMap) {
18339		let adjustedShift = shift.mult(pitchedTextShiftCorrection);
18340		if (!rotateWithMap) adjustedShift = adjustedShift.rotate(-transformAngle);
18341		const tileAnchorShifted = translatedAnchor.add(adjustedShift);
18342		return projectWithMatrix(tileAnchorShifted.x, tileAnchorShifted.y, projectionContext.pitchedLabelPlaneMatrix, elevationAt(projectionContext, tileAnchorShifted.x, tileAnchorShifted.y)).point;
18343	} else if (rotateWithMap) {
18344		const east = projectTileCoordinatesToLabelPlane(projectionContext.tileAnchorPoint.x + 1, projectionContext.tileAnchorPoint.y, projectionContext).point.sub(projectedAnchorPoint);
18345		const angle = Math.atan(east.y / east.x) + (east.x < 0 ? Math.PI : 0);
18346		return projectedAnchorPoint.add(shift.rotate(angle));
18347	} else return projectedAnchorPoint.add(shift);
18348}
18349function updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, variableOffsets, transform, pitchedLabelPlaneMatrix, tileScale, size, updateTextFitIcon, translation, unwrappedTileID, getElevation, heightAnchorGround) {
18350	const placedSymbols = bucket.text.placedSymbolArray;
18351	const dynamicTextLayoutVertexArray = bucket.text.dynamicLayoutVertexArray;
18352	const dynamicIconLayoutVertexArray = bucket.icon.dynamicLayoutVertexArray;
18353	const placedTextShifts = {};
18354	dynamicTextLayoutVertexArray.clear();
18355	for (let s = 0; s < placedSymbols.length; s++) {
18356		const symbol = placedSymbols.get(s);
18357		const skipOrientation = bucket.allowVerticalPlacement && !symbol.placedOrientation;
18358		const variableOffset = !symbol.hidden && symbol.crossTileID && !skipOrientation ? variableOffsets[symbol.crossTileID] : null;
18359		if (!variableOffset) hideGlyphs(symbol.numGlyphs, dynamicTextLayoutVertexArray);
18360		else {
18361			const tileAnchor = new Point(symbol.anchorX, symbol.anchorY);
18362			const projectionContext = {
18363				getElevation,
18364				heightOffset: symbol.heightOffset,
18365				heightAnchorGround,
18366				width: transform.width,
18367				height: transform.height,
18368				pitchedLabelPlaneMatrix,
18369				lineVertexArray: null,
18370				pitchWithMap,
18371				transform,
18372				projectionCache: null,
18373				tileAnchorPoint: tileAnchor,
18374				translation,
18375				unwrappedTileID
18376			};
18377			const projectedAnchor = pitchWithMap ? projectTileCoordinatesToClipSpace(tileAnchor.x, tileAnchor.y, projectionContext) : projectTileCoordinatesToLabelPlane(tileAnchor.x, tileAnchor.y, projectionContext);
18378			const perspectiveRatio = getPerspectiveRatio(transform.cameraToCenterDistance, projectedAnchor.signedDistanceFromCamera);
18379			let renderTextSize = evaluateSizeForFeature(bucket.textSizeData, size, symbol) * perspectiveRatio / 24;
18380			if (pitchWithMap) renderTextSize *= bucket.tilePixelRatio / tileScale;
18381			const { width, height, anchor, textOffset, textBoxScale } = variableOffset;
18382			const shift = calculateVariableRenderShift(anchor, width, height, textOffset, textBoxScale, renderTextSize);
18383			const pitchedTextCorrection = transform.getPitchedTextCorrection(tileAnchor.x + translation[0], tileAnchor.y + translation[1], unwrappedTileID);
18384			const shiftedAnchor = getShiftedAnchor(projectedAnchor.point, projectionContext, rotateWithMap, shift, -transform.bearingInRadians, pitchedTextCorrection);
18385			const angle = bucket.allowVerticalPlacement && symbol.placedOrientation === 2 ? Math.PI / 2 : 0;
18386			for (let g = 0; g < symbol.numGlyphs; g++) addDynamicAttributes(dynamicTextLayoutVertexArray, shiftedAnchor, angle);
18387			if (updateTextFitIcon && symbol.associatedIconIndex >= 0) placedTextShifts[symbol.associatedIconIndex] = {
18388				shiftedAnchor,
18389				angle
18390			};
18391		}
18392	}
18393	if (updateTextFitIcon) {
18394		dynamicIconLayoutVertexArray.clear();
18395		const placedIcons = bucket.icon.placedSymbolArray;
18396		for (let i = 0; i < placedIcons.length; i++) {
18397			const placedIcon = placedIcons.get(i);
18398			if (placedIcon.hidden) hideGlyphs(placedIcon.numGlyphs, dynamicIconLayoutVertexArray);
18399			else {
18400				const shift = placedTextShifts[i];
18401				if (!shift) hideGlyphs(placedIcon.numGlyphs, dynamicIconLayoutVertexArray);
18402				else for (let g = 0; g < placedIcon.numGlyphs; g++) addDynamicAttributes(dynamicIconLayoutVertexArray, shift.shiftedAnchor, shift.angle);
18403			}
18404		}
18405		bucket.icon.dynamicLayoutVertexBuffer.updateData(dynamicIconLayoutVertexArray);
18406	}
18407	bucket.text.dynamicLayoutVertexBuffer.updateData(dynamicTextLayoutVertexArray);
18408}
18409function getSymbolProgramName(isSDF, isText, bucket) {
18410	if (bucket.iconsInText && isText) return "symbolTextAndIcon";
18411	else if (isSDF) return "symbolSDF";
18412	else return "symbolIcon";
18413}
18414function drawLayerSymbols(painter, tileManager, layer, coords, isText, translate, translateAnchor, rotationAlignment, pitchAlignment, keepUpright, stencilMode, colorMode, renderContext) {
18415	const context = painter.context;
18416	const gl = context.gl;
18417	const transform = painter.transform;
18418	const rotateWithMap = rotationAlignment === "map";
18419	const pitchWithMap = pitchAlignment === "map";
18420	const alongLine = rotationAlignment !== "viewport" && layer.layout.get("symbol-placement") !== "point";
18421	const rotateInShader = rotateWithMap && !pitchWithMap && !alongLine;
18422	const hasSortKey = !layer.layout.get("symbol-sort-key").isConstant();
18423	let sortFeaturesByKey = false;
18424	const depthMode = painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
18425	const hasVariablePlacement = layer._unevaluatedLayout.hasValue("text-variable-anchor") || layer._unevaluatedLayout.hasValue("text-variable-anchor-offset");
18426	const tileRenderState = [];
18427	const pitchedTextRescaling = transform.getCircleRadiusCorrection();
18428	for (const coord of coords) {
18429		const tile = tileManager.getTile(coord);
18430		const bucket = tile.getBucket(layer);
18431		if (!bucket) continue;
18432		const buffers = isText ? bucket.text : bucket.icon;
18433		if (!buffers?.segments.get().length || !buffers.hasVisibleVertices) continue;
18434		const programConfiguration = buffers.programConfigurations.get(layer.id);
18435		const isSDF = isText || bucket.sdfIcons;
18436		const sizeData = isText ? bucket.textSizeData : bucket.iconSizeData;
18437		const transformed = pitchWithMap || transform.pitch !== 0;
18438		const program = painter.useProgram(getSymbolProgramName(isSDF, isText, bucket), programConfiguration);
18439		const size = evaluateSizeForZoom(sizeData, transform.zoom);
18440		const terrainData = getTerrainDataForTile(renderContext, coord);
18441		let texSize;
18442		let texSizeIcon = [0, 0];
18443		let atlasTexture;
18444		let atlasInterpolation;
18445		let atlasTextureIcon = null;
18446		let atlasInterpolationIcon;
18447		if (isText) {
18448			atlasTexture = tile.glyphAtlasTexture;
18449			atlasInterpolation = gl.LINEAR;
18450			texSize = tile.glyphAtlasTexture.size;
18451			if (bucket.iconsInText) {
18452				texSizeIcon = tile.imageAtlasTexture.size;
18453				atlasTextureIcon = tile.imageAtlasTexture;
18454				const zoomDependentSize = sizeData.kind === "composite" || sizeData.kind === "camera";
18455				atlasInterpolationIcon = transformed || painter.options.rotating || painter.options.zooming || zoomDependentSize ? gl.LINEAR : gl.NEAREST;
18456			}
18457		} else {
18458			const iconScaled = layer.layout.get("icon-size").constantOr(0) !== 1 || bucket.iconsNeedLinear;
18459			atlasTexture = tile.imageAtlasTexture;
18460			atlasInterpolation = isSDF || painter.options.rotating || painter.options.zooming || iconScaled || transformed ? gl.LINEAR : gl.NEAREST;
18461			texSize = tile.imageAtlasTexture.size;
18462		}
18463		const s = pixelsToTileUnits(tile, 1, painter.transform.zoom);
18464		const pitchedLabelPlaneMatrix = getPitchedLabelPlaneMatrix(rotateWithMap, painter.transform, s);
18465		const glCoordMatrixForShader = getGlCoordMatrix(pitchWithMap, rotateWithMap, painter.transform, s);
18466		const translation = translatePosition(transform, tile, translate, translateAnchor);
18467		const projectionData = getProjectionDataForTile(renderContext, coord);
18468		const hasVariableAnchors = hasVariablePlacement && bucket.hasTextData();
18469		const updateTextFitIcon = layer.layout.get("icon-text-fit") !== "none" && hasVariableAnchors && bucket.hasIconData();
18470		const isOffset = layer._unevaluatedLayout.hasValue("icon-offset");
18471		const heightAnchorGround = layer.layout.get("symbol-height-anchor") === "ground";
18472		if (alongLine) {
18473			const pitchedLabelPlaneMatrixInverse = create$1();
18474			fastInvertTransformMat4(pitchedLabelPlaneMatrixInverse, pitchedLabelPlaneMatrix);
18475			const getElevation = painter.style.map.terrain ? (x, y) => painter.style.map.terrain.getElevation(coord, x, y) : void 0;
18476			updateLineLabels(bucket, painter, isText, pitchedLabelPlaneMatrix, pitchedLabelPlaneMatrixInverse, pitchWithMap, keepUpright, layer.layout.get("text-rotation-alignment") === "map", coord.toUnwrapped(), transform.width, transform.height, translation, getElevation);
18477		}
18478		const shaderVariableAnchor = isText && hasVariablePlacement || updateTextFitIcon;
18479		const combinedLabelPlaneMatrix = pitchWithMap ? pitchedLabelPlaneMatrix : painter.transform.clipSpaceToPixelsMatrix;
18480		const uLabelPlaneMatrix = alongLine || shaderVariableAnchor ? identityMat4 : combinedLabelPlaneMatrix;
18481		const hasHalo = isSDF && layer.paint.get(isText ? "text-halo-width" : "icon-halo-width").constantOr(1) !== 0;
18482		let uniformValues;
18483		if (isSDF) {
18484			if (!bucket.iconsInText) uniformValues = symbolSDFUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, isText, texSize, hasHalo, pitchedTextRescaling, isOffset, heightAnchorGround);
18485			else uniformValues = symbolTextAndIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, texSize, texSizeIcon, pitchedTextRescaling, isOffset, heightAnchorGround);
18486		} else uniformValues = symbolIconUniformValues(sizeData.kind, size, rotateInShader, pitchWithMap, alongLine, shaderVariableAnchor, painter, uLabelPlaneMatrix, glCoordMatrixForShader, translation, isText, texSize, pitchedTextRescaling, isOffset, heightAnchorGround);
18487		const state = {
18488			program,
18489			buffers,
18490			uniformValues,
18491			projectionData,
18492			atlasTexture,
18493			atlasTextureIcon,
18494			atlasInterpolation,
18495			atlasInterpolationIcon,
18496			isSDF,
18497			hasHalo
18498		};
18499		if (hasSortKey && bucket.canOverlap) {
18500			sortFeaturesByKey = true;
18501			const oldSegments = buffers.segments.get();
18502			for (const segment of oldSegments) tileRenderState.push({
18503				segments: new SegmentVector([segment]),
18504				sortKey: segment.sortKey,
18505				state,
18506				terrainData
18507			});
18508		} else tileRenderState.push({
18509			segments: buffers.segments,
18510			sortKey: 0,
18511			state,
18512			terrainData
18513		});
18514	}
18515	if (sortFeaturesByKey) tileRenderState.sort((a, b) => a.sortKey - b.sortKey);
18516	const haloWidth = layer.paint.get(isText ? "text-halo-width" : "icon-halo-width").constantOr(null) ?? Infinity;
18517	const isGlyphOverlap = layer.layout.get("text-letter-spacing").constantOr(0) * 24 < 0 || haloWidth > 1;
18518	for (const segmentState of tileRenderState) {
18519		const state = segmentState.state;
18520		context.activeTexture.set(gl.TEXTURE0);
18521		state.atlasTexture.bind(state.atlasInterpolation, gl.CLAMP_TO_EDGE);
18522		if (state.atlasTextureIcon) {
18523			context.activeTexture.set(gl.TEXTURE1);
18524			if (state.atlasTextureIcon) state.atlasTextureIcon.bind(state.atlasInterpolationIcon, gl.CLAMP_TO_EDGE);
18525		}
18526		const isHalo = state.isSDF && state.hasHalo;
18527		if (isHalo) {
18528			const uniformValues = state.uniformValues;
18529			uniformValues["u_is_halo"] = 1;
18530			if (isGlyphOverlap) {
18531				uniformValues["u_is_plain"] = 0;
18532				drawSymbolElements(state.buffers, segmentState.segments, layer, painter, state.program, depthMode, stencilMode, colorMode, uniformValues, state.projectionData, segmentState.terrainData);
18533				uniformValues["u_is_halo"] = 0;
18534				uniformValues["u_is_plain"] = 1;
18535			}
18536		}
18537		drawSymbolElements(state.buffers, segmentState.segments, layer, painter, state.program, depthMode, stencilMode, colorMode, state.uniformValues, state.projectionData, segmentState.terrainData);
18538		if (isHalo && !isGlyphOverlap) state.uniformValues["u_is_halo"] = 0;
18539	}
18540}
18541function drawSymbolElements(buffers, segments, layer, painter, program, depthMode, stencilMode, colorMode, uniformValues, projectionData, terrainData) {
18542	const context = painter.context;
18543	const gl = context.gl;
18544	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);
18545}
18546//#endregion
18547//#region src/webgl/draw/draw_circle.ts
18548function drawCircles(painter, tileManager, layer, coords, renderContext) {
18549	if (renderContext.currentPass !== "translucent") return;
18550	const opacity = layer.paint.get("circle-opacity");
18551	const strokeWidth = layer.paint.get("circle-stroke-width");
18552	const strokeOpacity = layer.paint.get("circle-stroke-opacity");
18553	const sortFeaturesByKey = !layer.layout.get("circle-sort-key").isConstant();
18554	if (opacity.constantOr(1) === 0 && (strokeWidth.constantOr(1) === 0 || strokeOpacity.constantOr(1) === 0)) return;
18555	const context = painter.context;
18556	const gl = context.gl;
18557	const transform = painter.transform;
18558	const depthMode = painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
18559	const stencilMode = StencilMode.disabled;
18560	const colorMode = painter.colorModeForRenderPass();
18561	const segmentsRenderStates = [];
18562	const radiusCorrectionFactor = transform.getCircleRadiusCorrection();
18563	for (const coord of coords) {
18564		const tile = tileManager.getTile(coord);
18565		const bucket = tile.getBucket(layer);
18566		if (!bucket) continue;
18567		const styleTranslate = layer.paint.get("circle-translate");
18568		const styleTranslateAnchor = layer.paint.get("circle-translate-anchor");
18569		const translateForUniforms = translatePosition(transform, tile, styleTranslate, styleTranslateAnchor);
18570		const programConfiguration = bucket.programConfigurations.get(layer.id);
18571		const program = painter.useProgram("circle", programConfiguration);
18572		const layoutVertexBuffer = bucket.layoutVertexBuffer;
18573		const indexBuffer = bucket.indexBuffer;
18574		const terrainData = getTerrainDataForTile(renderContext, coord);
18575		const state = {
18576			programConfiguration,
18577			program,
18578			layoutVertexBuffer,
18579			indexBuffer,
18580			uniformValues: circleUniformValues(painter, tile, layer, translateForUniforms, radiusCorrectionFactor),
18581			terrainData,
18582			projectionData: getProjectionDataForTile(renderContext, coord)
18583		};
18584		if (sortFeaturesByKey) {
18585			const oldSegments = bucket.segments.get();
18586			for (const segment of oldSegments) segmentsRenderStates.push({
18587				segments: new SegmentVector([segment]),
18588				sortKey: segment.sortKey,
18589				state
18590			});
18591		} else segmentsRenderStates.push({
18592			segments: bucket.segments,
18593			sortKey: 0,
18594			state
18595		});
18596	}
18597	if (sortFeaturesByKey) segmentsRenderStates.sort((a, b) => a.sortKey - b.sortKey);
18598	for (const segmentsState of segmentsRenderStates) {
18599		const { programConfiguration, program, layoutVertexBuffer, indexBuffer, uniformValues, terrainData, projectionData } = segmentsState.state;
18600		const segments = segmentsState.segments;
18601		program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, layer.id, layoutVertexBuffer, indexBuffer, segments, layer.paint, painter.transform.zoom, programConfiguration);
18602	}
18603}
18604//#endregion
18605//#region src/webgl/draw/draw_heatmap.ts
18606function drawHeatmap(painter, tileManager, layer, tileIDs, renderContext) {
18607	if (layer.paint.get("heatmap-opacity") === 0) return;
18608	const context = painter.context;
18609	if (painter.style.map.terrain) {
18610		for (const coord of tileIDs) {
18611			const tile = tileManager.getTile(coord);
18612			if (tileManager.hasRenderableParent(coord)) continue;
18613			if (renderContext.currentPass === "offscreen") prepareHeatmapTerrain(painter, tile, layer, coord, renderContext);
18614			else if (renderContext.currentPass === "translucent") renderHeatmapTerrain(painter, layer, coord, renderContext);
18615		}
18616		context.viewport.set([
18617			0,
18618			0,
18619			painter.width,
18620			painter.height
18621		]);
18622	} else if (renderContext.currentPass === "offscreen") prepareHeatmapFlat(painter, tileManager, layer, tileIDs, renderContext);
18623	else if (renderContext.currentPass === "translucent") renderHeatmapFlat(painter, layer);
18624}
18625function prepareHeatmapFlat(painter, tileManager, layer, coords, renderContext) {
18626	const context = painter.context;
18627	const gl = context.gl;
18628	const transform = painter.transform;
18629	const stencilMode = StencilMode.disabled;
18630	const colorMode = new ColorMode([gl.ONE, gl.ONE], Color.transparent, [
18631		true,
18632		true,
18633		true,
18634		true
18635	]);
18636	bindFramebuffer(context, painter, layer);
18637	context.clear({ color: Color.transparent });
18638	for (const coord of coords) {
18639		if (tileManager.hasRenderableParent(coord)) continue;
18640		const tile = tileManager.getTile(coord);
18641		const bucket = tile.getBucket(layer);
18642		if (!bucket) continue;
18643		const programConfiguration = bucket.programConfigurations.get(layer.id);
18644		const program = painter.useProgram("heatmap", programConfiguration);
18645		const projectionData = getProjectionDataForTile(renderContext, coord, { applyTerrainMatrix: false });
18646		const radiusCorrectionFactor = transform.getCircleRadiusCorrection();
18647		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);
18648	}
18649	context.viewport.set([
18650		0,
18651		0,
18652		painter.width,
18653		painter.height
18654	]);
18655}
18656function renderHeatmapFlat(painter, layer) {
18657	const context = painter.context;
18658	const gl = context.gl;
18659	context.setColorMode(painter.colorModeForRenderPass());
18660	const fbo = layer.heatmapFbos.get(HEATMAP_FULL_RENDER_FBO_KEY);
18661	if (!fbo) return;
18662	context.activeTexture.set(gl.TEXTURE0);
18663	gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
18664	context.activeTexture.set(gl.TEXTURE1);
18665	getColorRampTexture(context, layer).bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
18666	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);
18667}
18668function prepareHeatmapTerrain(painter, tile, layer, coord, renderContext) {
18669	const context = painter.context;
18670	const gl = context.gl;
18671	const stencilMode = StencilMode.disabled;
18672	const colorMode = new ColorMode([gl.ONE, gl.ONE], Color.transparent, [
18673		true,
18674		true,
18675		true,
18676		true
18677	]);
18678	const bucket = tile.getBucket(layer);
18679	if (!bucket) return;
18680	const tileKey = coord.key;
18681	let fbo = layer.heatmapFbos.get(tileKey);
18682	if (!fbo) {
18683		fbo = createHeatmapFbo(context, tile.tileSize, tile.tileSize);
18684		layer.heatmapFbos.set(tileKey, fbo);
18685	}
18686	context.bindFramebuffer.set(fbo.framebuffer);
18687	context.viewport.set([
18688		0,
18689		0,
18690		tile.tileSize,
18691		tile.tileSize
18692	]);
18693	context.clear({ color: Color.transparent });
18694	const programConfiguration = bucket.programConfigurations.get(layer.id);
18695	const program = painter.useProgram("heatmap", programConfiguration, !renderContext.isRenderingGlobe);
18696	const projectionData = getProjectionDataForTile(renderContext, tile.tileID);
18697	const terrainData = getTerrainDataForTile(renderContext, coord);
18698	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);
18699}
18700function renderHeatmapTerrain(painter, layer, coord, renderContext) {
18701	const context = painter.context;
18702	const gl = context.gl;
18703	const transform = painter.transform;
18704	context.setColorMode(painter.colorModeForRenderPass());
18705	const colorRampTexture = getColorRampTexture(context, layer);
18706	const tileKey = coord.key;
18707	const fbo = layer.heatmapFbos.get(tileKey);
18708	if (!fbo) return;
18709	context.activeTexture.set(gl.TEXTURE0);
18710	gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
18711	context.activeTexture.set(gl.TEXTURE1);
18712	colorRampTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
18713	const projectionData = getProjectionDataForTile(renderContext, coord, { applyTerrainMatrix: renderContext.isRenderingGlobe });
18714	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);
18715	fbo.destroy();
18716	layer.heatmapFbos.delete(tileKey);
18717}
18718function bindFramebuffer(context, painter, layer) {
18719	const gl = context.gl;
18720	context.activeTexture.set(gl.TEXTURE1);
18721	context.viewport.set([
18722		0,
18723		0,
18724		painter.width / 4,
18725		painter.height / 4
18726	]);
18727	let fbo = layer.heatmapFbos.get(HEATMAP_FULL_RENDER_FBO_KEY);
18728	if (!fbo) {
18729		fbo = createHeatmapFbo(context, painter.width / 4, painter.height / 4);
18730		layer.heatmapFbos.set(HEATMAP_FULL_RENDER_FBO_KEY, fbo);
18731	} else {
18732		gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
18733		context.bindFramebuffer.set(fbo.framebuffer);
18734	}
18735}
18736function createHeatmapFbo(context, width, height) {
18737	const gl = context.gl;
18738	const texture = gl.createTexture();
18739	gl.bindTexture(gl.TEXTURE_2D, texture);
18740	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
18741	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
18742	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
18743	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
18744	gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA16F, width, height);
18745	const fbo = context.createFramebuffer(width, height, false, false);
18746	fbo.colorAttachment.set(texture);
18747	return fbo;
18748}
18749function getColorRampTexture(context, layer) {
18750	layer.colorRampTexture ||= new Texture(context, layer.colorRamp, context.gl.RGBA);
18751	return layer.colorRampTexture;
18752}
18753//#endregion
18754//#region src/webgl/draw/draw_layer_opacity.ts
18755/**
18756* Partial line-layer-opacity
18757* render the whole layer to a scratch FBO, then composite with `layerOpacity`.
18758* Applies opacity uniformly to the layer instead of accumulating alpha across overlapping segments.
18759*/
18760function prepareDrawLayerOpacity(painter, layer, coords) {
18761	const context = painter.context;
18762	const compositeTarget = context.bindFramebuffer.get();
18763	const compositeViewport = context.viewport.get();
18764	const [, , width, height] = compositeViewport;
18765	bindLayerOpacity(painter, width, height);
18766	context.viewport.set([
18767		0,
18768		0,
18769		width,
18770		height
18771	]);
18772	context.clear({
18773		color: Color.transparent,
18774		depth: 1,
18775		stencil: 0
18776	});
18777	painter.currentStencilSource = void 0;
18778	painter.renderTileClippingMasks(layer, coords);
18779	return {
18780		compositeTarget,
18781		compositeViewport
18782	};
18783}
18784function bindLayerOpacity(painter, width, height) {
18785	const gl = painter.context.gl;
18786	if (!painter.layerOpacityFbo) {
18787		const fbo = painter.context.createFramebuffer(width, height, true, true);
18788		const texture = gl.createTexture();
18789		gl.bindTexture(gl.TEXTURE_2D, texture);
18790		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
18791		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
18792		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
18793		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
18794		gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
18795		fbo.colorAttachment.set(texture);
18796		fbo.depthAttachment.set(painter.context.createRenderbuffer(gl.DEPTH_STENCIL, width, height));
18797		painter.layerOpacityFbo = fbo;
18798		painter.context.bindFramebuffer.set(painter.layerOpacityFbo.framebuffer);
18799		return;
18800	}
18801	if (painter.layerOpacityFbo.width === width && painter.layerOpacityFbo.height === height) {
18802		painter.context.bindFramebuffer.set(painter.layerOpacityFbo.framebuffer);
18803		return;
18804	}
18805	const fbo = painter.layerOpacityFbo;
18806	gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
18807	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
18808	painter.context.bindRenderbuffer.set(fbo.depthAttachment.get());
18809	gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
18810	painter.context.bindRenderbuffer.set(null);
18811	fbo.width = width;
18812	fbo.height = height;
18813	painter.context.bindFramebuffer.set(fbo.framebuffer);
18814}
18815function drawLayerOpacity(painter, opacity, prepareDrawLayerOpacityResult, layer) {
18816	const context = painter.context;
18817	const gl = context.gl;
18818	context.bindFramebuffer.set(prepareDrawLayerOpacityResult.compositeTarget);
18819	context.viewport.set(prepareDrawLayerOpacityResult.compositeViewport);
18820	context.activeTexture.set(gl.TEXTURE0);
18821	gl.bindTexture(gl.TEXTURE_2D, painter.layerOpacityFbo.colorAttachment.get());
18822	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);
18823	painter.currentStencilSource = void 0;
18824}
18825//#endregion
18826//#region src/webgl/draw/draw_line.ts
18827function updateGradientTexture(painter, tileManager, context, gl, layer, bucket, coord, layerGradient) {
18828	let textureResolution = 256;
18829	if (layer.stepInterpolant) {
18830		const sourceMaxZoom = tileManager.getSource().maxzoom;
18831		const potentialOverzoom = coord.canonical.z === sourceMaxZoom ? Math.ceil(1 << painter.transform.maxZoom - coord.canonical.z) : 1;
18832		const maxTextureCoverage = bucket.maxLineLength / EXTENT * 1024 * potentialOverzoom;
18833		textureResolution = clamp(nextPowerOfTwo(maxTextureCoverage), 256, context.maxTextureSize);
18834	}
18835	layerGradient.gradient = renderColorRamp({
18836		expression: layer.gradientExpression(),
18837		evaluationKey: "lineProgress",
18838		resolution: textureResolution,
18839		image: layerGradient.gradient || void 0,
18840		clips: bucket.lineClipsArray
18841	});
18842	if (layerGradient.texture) layerGradient.texture.update(layerGradient.gradient);
18843	else layerGradient.texture = new Texture(context, layerGradient.gradient, gl.RGBA);
18844	layerGradient.version = layer.gradientVersion;
18845	return layerGradient.texture;
18846}
18847function bindImagePatternTextures(context, gl, tile, programConfiguration, crossfade) {
18848	context.activeTexture.set(gl.TEXTURE0);
18849	tile.imageAtlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
18850	programConfiguration.updatePaintBuffers(crossfade);
18851}
18852function bindDasharrayTextures(painter, context, gl, programConfiguration, programChanged, crossfade) {
18853	if (programChanged || painter.lineAtlas.dirty) {
18854		context.activeTexture.set(gl.TEXTURE0);
18855		painter.lineAtlas.bind(context);
18856	}
18857	programConfiguration.updatePaintBuffers(crossfade);
18858}
18859function bindGradientTextures(painter, tileManager, context, gl, layer, bucket, coord) {
18860	const layerGradient = bucket.gradients[layer.id];
18861	let gradientTexture = layerGradient.texture;
18862	if (layer.gradientVersion !== layerGradient.version) gradientTexture = updateGradientTexture(painter, tileManager, context, gl, layer, bucket, coord, layerGradient);
18863	context.activeTexture.set(gl.TEXTURE0);
18864	gradientTexture.bind(layer.stepInterpolant ? gl.NEAREST : gl.LINEAR, gl.CLAMP_TO_EDGE);
18865}
18866function bindGradientAndDashTextures(painter, tileManager, context, gl, layer, bucket, coord, programConfiguration, crossfade) {
18867	const layerGradient = bucket.gradients[layer.id];
18868	let gradientTexture = layerGradient.texture;
18869	if (layer.gradientVersion !== layerGradient.version) gradientTexture = updateGradientTexture(painter, tileManager, context, gl, layer, bucket, coord, layerGradient);
18870	context.activeTexture.set(gl.TEXTURE0);
18871	gradientTexture.bind(layer.stepInterpolant ? gl.NEAREST : gl.LINEAR, gl.CLAMP_TO_EDGE);
18872	context.activeTexture.set(gl.TEXTURE1);
18873	painter.lineAtlas.bind(context);
18874	programConfiguration.updatePaintBuffers(crossfade);
18875}
18876function drawLine(painter, tileManager, layer, coords, renderContext) {
18877	if (renderContext.currentPass !== "translucent") return;
18878	const opacity = layer.paint.get("line-opacity");
18879	const width = layer.paint.get("line-width");
18880	const layerOpacity = layer.paint.get("line-layer-opacity");
18881	if (opacity.constantOr(1) === 0 || width.constantOr(1) === 0 || layerOpacity === 0) return;
18882	if (layerOpacity < 1) {
18883		const results = prepareDrawLayerOpacity(painter, layer, coords);
18884		drawLineTiles(painter, tileManager, layer, coords, renderContext);
18885		drawLayerOpacity(painter, layerOpacity, results, layer);
18886		return;
18887	}
18888	drawLineTiles(painter, tileManager, layer, coords, renderContext);
18889}
18890function drawLineTiles(painter, tileManager, layer, coords, renderContext) {
18891	const depthMode = painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
18892	const colorMode = painter.colorModeForRenderPass();
18893	const dasharrayProperty = layer.paint.get("line-dasharray");
18894	const dasharray = dasharrayProperty.constantOr(1);
18895	const patternProperty = layer.paint.get("line-pattern");
18896	const image = patternProperty.constantOr(1);
18897	const gradient = layer.paint.get("line-gradient");
18898	const crossfade = layer.getCrossfadeParameters();
18899	let programId;
18900	if (image) programId = "linePattern";
18901	else if (dasharray && gradient) programId = "lineGradientSDF";
18902	else if (dasharray) programId = "lineSDF";
18903	else if (gradient) programId = "lineGradient";
18904	else programId = "line";
18905	const context = painter.context;
18906	const gl = context.gl;
18907	const transform = painter.transform;
18908	let firstTile = true;
18909	for (const coord of coords) {
18910		const tile = tileManager.getTile(coord);
18911		if (image && !tile.patternsLoaded()) continue;
18912		const bucket = tile.getBucket(layer);
18913		if (!bucket) continue;
18914		const programConfiguration = bucket.programConfigurations.get(layer.id);
18915		const prevProgram = painter.context.program.get();
18916		const program = painter.useProgram(programId, programConfiguration);
18917		const programChanged = firstTile || program.program !== prevProgram;
18918		const terrainData = getTerrainDataForTile(renderContext, coord);
18919		const constantPattern = patternProperty.constantOr(null);
18920		const constantDasharray = dasharrayProperty?.constantOr(null);
18921		if (constantPattern && tile.imageAtlas) {
18922			const atlas = tile.imageAtlas;
18923			const posTo = atlas.patternPositions[constantPattern.to.toString()];
18924			const posFrom = atlas.patternPositions[constantPattern.from.toString()];
18925			if (posTo && posFrom) programConfiguration.setConstantPatternPositions(posTo, posFrom);
18926		} else if (constantDasharray) {
18927			const round = layer.layout.get("line-cap").constantOr(null) === "round";
18928			const dashTo = painter.lineAtlas.getDash(constantDasharray.to, round);
18929			const dashFrom = painter.lineAtlas.getDash(constantDasharray.from, round);
18930			programConfiguration.setConstantDashPositions(dashTo, dashFrom);
18931		}
18932		const projectionData = getProjectionDataForTile(renderContext, coord);
18933		const pixelRatio = transform.getPixelScale();
18934		let uniformValues;
18935		if (image) {
18936			uniformValues = linePatternUniformValues(painter, tile, layer, pixelRatio, crossfade);
18937			bindImagePatternTextures(context, gl, tile, programConfiguration, crossfade);
18938		} else if (dasharray && gradient) {
18939			uniformValues = lineGradientSDFUniformValues(painter, tile, layer, pixelRatio, crossfade, bucket.lineClipsArray.length);
18940			bindGradientAndDashTextures(painter, tileManager, context, gl, layer, bucket, coord, programConfiguration, crossfade);
18941		} else if (dasharray) {
18942			uniformValues = lineSDFUniformValues(painter, tile, layer, pixelRatio, crossfade);
18943			bindDasharrayTextures(painter, context, gl, programConfiguration, programChanged, crossfade);
18944		} else if (gradient) {
18945			uniformValues = lineGradientUniformValues(painter, tile, layer, pixelRatio, bucket.lineClipsArray.length);
18946			bindGradientTextures(painter, tileManager, context, gl, layer, bucket, coord);
18947		} else uniformValues = lineUniformValues(painter, tile, layer, pixelRatio);
18948		const stencil = painter.stencilModeForClipping(coord);
18949		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);
18950		firstTile = false;
18951	}
18952}
18953//#endregion
18954//#region src/render/update_pattern_positions_in_program.ts
18955/**
18956* A simple helper shared by draw_fill and draw_fill_extrusions to find the correct pattern positions AND update program.
18957* For transitionable properties, especially 'fill-pattern' and 'fill-extrusion-pattern', while rendering certain frames
18958* tile.imageAtlas has been updated by worker to hold the new pattern only, but rendering code is still looking for the previous image.
18959* The mismatch was causing setConstantPatternPositions method not being called and pixelRatio was always the
18960* default of 1, instead of actual values set by original map.addImage.
18961*
18962* @param programConfiguration - to be used to set pattern position and device pixel ratio.
18963* @param propertyName - 'fill-pattern' or 'fill-extrusion-pattern' property key
18964* @param constantPattern - either 'fill-pattern' or 'fill-extrusion-pattern' property value
18965* @param tile - current tile being drawn
18966* @param layer - current layer being rendered
18967*/
18968function updatePatternPositionsInProgram(programConfiguration, propertyName, constantPattern, tile, layer) {
18969	if (!constantPattern || !tile?.imageAtlas) return;
18970	const patternPositions = tile.imageAtlas.patternPositions;
18971	let posTo = patternPositions[constantPattern.to.toString()];
18972	let posFrom = patternPositions[constantPattern.from.toString()];
18973	if (!posTo && posFrom) posTo = posFrom;
18974	if (!posFrom && posTo) posFrom = posTo;
18975	if (!posTo || !posFrom) {
18976		const transitioned = layer.getPaintProperty(propertyName);
18977		posTo = patternPositions[transitioned];
18978		posFrom = patternPositions[transitioned];
18979	}
18980	if (posTo && posFrom) programConfiguration.setConstantPatternPositions(posTo, posFrom);
18981}
18982//#endregion
18983//#region src/webgl/draw/draw_fill.ts
18984function drawFill(painter, tileManager, layer, coords, renderContext) {
18985	const color = layer.paint.get("fill-color");
18986	const opacity = layer.paint.get("fill-opacity");
18987	const layerOpacity = layer.paint.get("fill-layer-opacity");
18988	if (opacity.constantOr(1) === 0 || layerOpacity === 0) return;
18989	if (layerOpacity < 1) {
18990		if (renderContext.currentPass !== "translucent") return;
18991		const results = prepareDrawLayerOpacity(painter, layer, coords);
18992		drawFillAndOutline(painter, tileManager, layer, coords, renderContext);
18993		drawLayerOpacity(painter, layerOpacity, results, layer);
18994		return;
18995	}
18996	const pattern = layer.paint.get("fill-pattern");
18997	const fillEligibleForOpaque = painter.opaquePassEnabledForLayer() && !pattern.constantOr(1) && color.constantOr(Color.transparent).a === 1 && opacity.constantOr(0) === 1;
18998	if (fillEligibleForOpaque && renderContext.currentPass === "opaque") {
18999		const colorMode = painter.colorModeForRenderPass();
19000		drawFillTiles(painter, tileManager, layer, coords, painter.getDepthModeForSublayer(1, DepthMode.ReadWrite), colorMode, false, renderContext);
19001		return;
19002	}
19003	if (fillEligibleForOpaque && renderContext.currentPass === "translucent") {
19004		drawOutline(painter, tileManager, layer, coords, renderContext);
19005		return;
19006	}
19007	if (renderContext.currentPass === "translucent") drawFillAndOutline(painter, tileManager, layer, coords, renderContext);
19008}
19009/**
19010* Draw fill + outline in a single translucent pass with ReadOnly depth.
19011* Shared by the layer-opacity subpass (always) and the normal translucent path
19012* (when the fill is not opaque-pass-eligible).
19013*/
19014function drawFillAndOutline(painter, tileManager, layer, coords, renderContext) {
19015	const colorMode = painter.colorModeForRenderPass();
19016	drawFillTiles(painter, tileManager, layer, coords, painter.getDepthModeForSublayer(1, DepthMode.ReadOnly), colorMode, false, renderContext);
19017	drawOutline(painter, tileManager, layer, coords, renderContext);
19018}
19019function drawOutline(painter, tileManager, layer, coords, renderContext) {
19020	if (!layer.paint.get("fill-antialias")) return;
19021	const colorMode = painter.colorModeForRenderPass();
19022	drawFillTiles(painter, tileManager, layer, coords, painter.getDepthModeForSublayer(layer.getPaintProperty("fill-outline-color") ? 2 : 0, DepthMode.ReadOnly), colorMode, true, renderContext);
19023}
19024function drawFillTiles(painter, tileManager, layer, coords, depthMode, colorMode, isOutline, renderContext) {
19025	const gl = painter.context.gl;
19026	const fillPropertyName = "fill-pattern";
19027	const patternProperty = layer.paint.get(fillPropertyName);
19028	const image = patternProperty?.constantOr(1);
19029	const crossfade = layer.getCrossfadeParameters();
19030	let drawMode, programName, uniformValues, indexBuffer, segments;
19031	const transform = painter.transform;
19032	const propertyFillTranslate = layer.paint.get("fill-translate");
19033	const propertyFillTranslateAnchor = layer.paint.get("fill-translate-anchor");
19034	if (!isOutline) {
19035		programName = image ? "fillPattern" : "fill";
19036		drawMode = gl.TRIANGLES;
19037	} else {
19038		programName = image && !layer.getPaintProperty("fill-outline-color") ? "fillOutlinePattern" : "fillOutline";
19039		drawMode = gl.LINES;
19040	}
19041	const constantPattern = patternProperty.constantOr(null);
19042	for (const coord of coords) {
19043		const tile = tileManager.getTile(coord);
19044		if (image && !tile.patternsLoaded()) continue;
19045		const bucket = tile.getBucket(layer);
19046		if (!bucket) continue;
19047		const isSdfPattern = bucket.sdfPatterns[layer.id] ?? false;
19048		const programConfiguration = bucket.programConfigurations.get(layer.id);
19049		const program = painter.useProgram(programName, programConfiguration);
19050		const terrainData = getTerrainDataForTile(renderContext, coord);
19051		if (image) {
19052			painter.context.activeTexture.set(gl.TEXTURE0);
19053			tile.imageAtlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
19054			programConfiguration.updatePaintBuffers(crossfade);
19055		}
19056		updatePatternPositionsInProgram(programConfiguration, fillPropertyName, constantPattern, tile, layer);
19057		const projectionData = getProjectionDataForTile(renderContext, coord);
19058		const translateForUniforms = translatePosition(transform, tile, propertyFillTranslate, propertyFillTranslateAnchor);
19059		if (!isOutline) {
19060			indexBuffer = bucket.indexBuffer;
19061			segments = bucket.segments;
19062			uniformValues = image ? fillPatternUniformValues(painter, crossfade, tile, translateForUniforms, isSdfPattern) : fillUniformValues(translateForUniforms);
19063		} else {
19064			indexBuffer = bucket.indexBuffer2;
19065			segments = bucket.segments2;
19066			uniformValues = programName === "fillOutlinePattern" && image ? fillOutlinePatternUniformValues(painter, crossfade, tile, translateForUniforms, isSdfPattern) : fillOutlineUniformValues(translateForUniforms);
19067		}
19068		const stencil = painter.stencilModeForClipping(coord);
19069		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);
19070	}
19071}
19072//#endregion
19073//#region src/webgl/draw/draw_fill_extrusion.ts
19074function drawFillExtrusion(painter, tileManager, layer, coords, renderContext) {
19075	const opacity = layer.paint.get("fill-extrusion-opacity");
19076	if (opacity === 0) return;
19077	if (renderContext.currentPass === "translucent") {
19078		const depthMode = new DepthMode(painter.context.gl.LEQUAL, DepthMode.ReadWrite, renderContext.depthRangeFor3D);
19079		if (opacity === 1 && !layer.paint.get("fill-extrusion-pattern").constantOr(1)) {
19080			const colorMode = painter.colorModeForRenderPass();
19081			drawExtrusionTiles(painter, tileManager, layer, coords, depthMode, StencilMode.disabled, colorMode, renderContext);
19082		} else {
19083			drawExtrusionTiles(painter, tileManager, layer, coords, depthMode, StencilMode.disabled, ColorMode.disabled, renderContext);
19084			drawExtrusionTiles(painter, tileManager, layer, coords, depthMode, painter.stencilModeFor3D(), painter.colorModeForRenderPass(), renderContext);
19085		}
19086	}
19087}
19088function drawExtrusionTiles(painter, tileManager, layer, coords, depthMode, stencilMode, colorMode, renderContext) {
19089	const context = painter.context;
19090	const gl = context.gl;
19091	const fillPropertyName = "fill-extrusion-pattern";
19092	const patternProperty = layer.paint.get(fillPropertyName);
19093	const image = patternProperty.constantOr(1);
19094	const crossfade = layer.getCrossfadeParameters();
19095	const opacity = layer.paint.get("fill-extrusion-opacity");
19096	const constantPattern = patternProperty.constantOr(null);
19097	const transform = painter.transform;
19098	for (const coord of coords) {
19099		const tile = tileManager.getTile(coord);
19100		const bucket = tile.getBucket(layer);
19101		if (!bucket) continue;
19102		const terrainData = getTerrainDataForTile(renderContext, coord);
19103		const programConfiguration = bucket.programConfigurations.get(layer.id);
19104		const program = painter.useProgram(image ? "fillExtrusionPattern" : "fillExtrusion", programConfiguration);
19105		if (image) {
19106			painter.context.activeTexture.set(gl.TEXTURE0);
19107			tile.imageAtlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
19108			programConfiguration.updatePaintBuffers(crossfade);
19109		}
19110		const projectionData = getProjectionDataForTile(renderContext, coord);
19111		updatePatternPositionsInProgram(programConfiguration, fillPropertyName, constantPattern, tile, layer);
19112		const translate = translatePosition(transform, tile, layer.paint.get("fill-extrusion-translate"), layer.paint.get("fill-extrusion-translate-anchor"));
19113		const shouldUseVerticalGradient = layer.paint.get("fill-extrusion-vertical-gradient");
19114		const uniformValues = image ? fillExtrusionPatternUniformValues(painter, shouldUseVerticalGradient, opacity, translate, coord, crossfade, tile) : fillExtrusionUniformValues(painter, shouldUseVerticalGradient, opacity, translate);
19115		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);
19116	}
19117}
19118//#endregion
19119//#region src/webgl/draw/draw_hillshade.ts
19120function drawHillshade(painter, tileManager, layer, tileIDs, renderContext) {
19121	if (renderContext.currentPass !== "offscreen" && renderContext.currentPass !== "translucent") return;
19122	const context = painter.context;
19123	const useSubdivision = painter.style.projection.useSubdivision;
19124	const depthMode = painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
19125	const colorMode = painter.colorModeForRenderPass();
19126	if (renderContext.currentPass === "offscreen") {
19127		prepareHillshade(painter, tileManager, tileIDs, layer, depthMode, StencilMode.disabled, colorMode);
19128		context.viewport.set([
19129			0,
19130			0,
19131			painter.width,
19132			painter.height
19133		]);
19134	} else if (renderContext.currentPass === "translucent") {
19135		if (useSubdivision) {
19136			const [stencilBorderless, stencilBorders, coords] = painter.stencilConfigForOverlapTwoPass(tileIDs);
19137			renderHillshade(painter, tileManager, layer, coords, stencilBorderless, depthMode, colorMode, false, renderContext);
19138			renderHillshade(painter, tileManager, layer, coords, stencilBorders, depthMode, colorMode, true, renderContext);
19139		} else {
19140			const [stencil, coords] = painter.getStencilConfigForOverlapAndUpdateStencilID(tileIDs);
19141			renderHillshade(painter, tileManager, layer, coords, stencil, depthMode, colorMode, false, renderContext);
19142		}
19143	}
19144}
19145function renderHillshade(painter, tileManager, layer, coords, stencilModes, depthMode, colorMode, useBorder, renderContext) {
19146	const projection = painter.style.projection;
19147	const context = painter.context;
19148	const gl = context.gl;
19149	const defines = [`#define NUM_ILLUMINATION_SOURCES ${layer.paint.get("hillshade-highlight-color").values.length}`];
19150	const program = painter.useProgram("hillshade", null, false, defines);
19151	const align = !painter.options.moving;
19152	for (const coord of coords) {
19153		const tile = tileManager.getTile(coord);
19154		const fbo = tile.fbo;
19155		if (!fbo) continue;
19156		const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder, true, "raster");
19157		const terrainData = getTerrainDataForTile(renderContext, coord);
19158		context.activeTexture.set(gl.TEXTURE0);
19159		gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get());
19160		const projectionData = getProjectionDataForTile(renderContext, coord, { aligned: align });
19161		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);
19162	}
19163}
19164function prepareHillshade(painter, tileManager, tileIDs, layer, depthMode, stencilMode, colorMode) {
19165	const context = painter.context;
19166	const gl = context.gl;
19167	const textureFilter = layer.paint.get("resampling") === "nearest" ? gl.NEAREST : gl.LINEAR;
19168	for (const coord of tileIDs) {
19169		const tile = tileManager.getTile(coord);
19170		const dem = tile.dem;
19171		if (!dem?.data) continue;
19172		if (!tile.needsHillshadePrepare) continue;
19173		const hillshadeTextureSize = dem.dim + 2;
19174		const textureStride = dem.stride;
19175		const pixelData = dem.getPixels();
19176		context.activeTexture.set(gl.TEXTURE1);
19177		context.pixelStoreUnpackPremultiplyAlpha.set(false);
19178		tile.demTexture ||= painter.getTileTexture(textureStride);
19179		if (tile.demTexture) {
19180			const demTexture = tile.demTexture;
19181			demTexture.update(pixelData, { premultiply: false });
19182			demTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);
19183		} else {
19184			tile.demTexture = new Texture(context, pixelData, gl.RGBA, { premultiply: false });
19185			tile.demTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);
19186		}
19187		context.activeTexture.set(gl.TEXTURE0);
19188		let fbo = tile.fbo;
19189		if (!fbo) {
19190			const renderTexture = new Texture(context, {
19191				width: hillshadeTextureSize,
19192				height: hillshadeTextureSize,
19193				data: null
19194			}, gl.RGBA);
19195			renderTexture.bind(textureFilter, gl.CLAMP_TO_EDGE);
19196			fbo = tile.fbo = context.createFramebuffer(hillshadeTextureSize, hillshadeTextureSize, true, false);
19197			fbo.colorAttachment.set(renderTexture.texture);
19198		}
19199		context.bindFramebuffer.set(fbo.framebuffer);
19200		context.viewport.set([
19201			0,
19202			0,
19203			hillshadeTextureSize,
19204			hillshadeTextureSize
19205		]);
19206		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);
19207		tile.needsHillshadePrepare = false;
19208	}
19209}
19210//#endregion
19211//#region src/webgl/draw/draw_color_relief.ts
19212function drawColorRelief(painter, tileManager, layer, tileIDs, renderContext) {
19213	if (renderContext.currentPass !== "translucent") return;
19214	if (!tileIDs.length) return;
19215	const useSubdivision = painter.style.projection.useSubdivision;
19216	const depthMode = painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
19217	const colorMode = painter.colorModeForRenderPass();
19218	if (useSubdivision) {
19219		const [stencilBorderless, stencilBorders, coords] = painter.stencilConfigForOverlapTwoPass(tileIDs);
19220		renderColorRelief(painter, tileManager, layer, coords, stencilBorderless, depthMode, colorMode, false, renderContext);
19221		renderColorRelief(painter, tileManager, layer, coords, stencilBorders, depthMode, colorMode, true, renderContext);
19222	} else {
19223		const [stencil, coords] = painter.getStencilConfigForOverlapAndUpdateStencilID(tileIDs);
19224		renderColorRelief(painter, tileManager, layer, coords, stencil, depthMode, colorMode, false, renderContext);
19225	}
19226}
19227let textureMaxSize = 0;
19228/**
19229* Draws the color-relief tiles of one pass.
19230*
19231* A loaded raster-DEM tile can carry no DEM at all (e.g. an empty 204 response);
19232* such tiles are skipped before the first-tile setup reads from them.
19233*/
19234function renderColorRelief(painter, tileManager, layer, coords, stencilModes, depthMode, colorMode, useBorder, renderContext) {
19235	const projection = painter.style.projection;
19236	const context = painter.context;
19237	const gl = context.gl;
19238	const program = painter.useProgram("colorRelief");
19239	const align = !painter.options.moving;
19240	const textureFilter = layer.paint.get("resampling") === "nearest" ? gl.NEAREST : gl.LINEAR;
19241	let firstTile = true;
19242	let colorRampSize = 0;
19243	for (const coord of coords) {
19244		const tile = tileManager.getTile(coord);
19245		const dem = tile.dem;
19246		if (!dem?.data) continue;
19247		if (firstTile) {
19248			textureMaxSize ||= gl.getParameter(gl.MAX_TEXTURE_SIZE);
19249			const maxLength = textureMaxSize;
19250			const { elevationTexture, colorTexture } = layer.getColorRampTextures(context, maxLength, dem.getUnpackVector());
19251			context.activeTexture.set(gl.TEXTURE1);
19252			elevationTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE);
19253			context.activeTexture.set(gl.TEXTURE4);
19254			colorTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
19255			firstTile = false;
19256			colorRampSize = elevationTexture.size[0];
19257		}
19258		const textureStride = dem.stride;
19259		context.activeTexture.set(gl.TEXTURE0);
19260		if (!tile.demTexture || tile.needsColorReliefPrepare) {
19261			context.pixelStoreUnpackPremultiplyAlpha.set(false);
19262			tile.demTexture ||= painter.getTileTexture(textureStride) ?? new Texture(context, {
19263				width: textureStride,
19264				height: textureStride,
19265				data: null
19266			}, gl.RGBA);
19267			tile.demTexture.update(dem.getPixels(), { premultiply: false });
19268			tile.needsColorReliefPrepare = false;
19269		}
19270		tile.demTexture.bind(textureFilter, gl.CLAMP_TO_EDGE);
19271		const mesh = projection.getMeshFromTileID(context, coord.canonical, useBorder, true, "raster");
19272		const terrainData = getTerrainDataForTile(renderContext, coord);
19273		const projectionData = getProjectionDataForTile(renderContext, coord, { aligned: align });
19274		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);
19275	}
19276}
19277//#endregion
19278//#region src/webgl/draw/draw_raster.ts
19279const cornerCoords = [
19280	new Point(0, 0),
19281	new Point(EXTENT, 0),
19282	new Point(EXTENT, EXTENT),
19283	new Point(0, EXTENT)
19284];
19285function drawRaster(painter, tileManager, layer, tileIDs, renderContext) {
19286	if (renderContext.currentPass !== "translucent") return;
19287	if (layer.paint.get("raster-opacity") === 0) return;
19288	if (!tileIDs.length) return;
19289	const source = tileManager.getSource();
19290	const useSubdivision = painter.style.projection.useSubdivision;
19291	if (source instanceof ImageSource) drawTiles(painter, tileManager, layer, tileIDs, null, false, false, source.tileCoords, source.imageWarp, source.flippedWindingOrder, renderContext, source.getMesh(painter.context, useSubdivision));
19292	else if (useSubdivision) {
19293		const [stencilBorderless, stencilBorders, coords] = painter.stencilConfigForOverlapTwoPass(tileIDs);
19294		drawTiles(painter, tileManager, layer, coords, stencilBorderless, false, true, cornerCoords, bilinearImageWarp, false, renderContext);
19295		drawTiles(painter, tileManager, layer, coords, stencilBorders, true, true, cornerCoords, bilinearImageWarp, false, renderContext);
19296	} else {
19297		const [stencil, coords] = painter.getStencilConfigForOverlapAndUpdateStencilID(tileIDs);
19298		drawTiles(painter, tileManager, layer, coords, stencil, false, true, cornerCoords, bilinearImageWarp, false, renderContext);
19299	}
19300}
19301function drawTiles(painter, tileManager, layer, coords, stencilModes, useBorder, allowPoles, corners, imageWarp, flipCullfaceMode = false, renderContext, sourceMesh = null) {
19302	const minTileZ = coords[coords.length - 1].overscaledZ;
19303	const context = painter.context;
19304	const gl = context.gl;
19305	const program = painter.useProgram("raster");
19306	const projection = painter.style.projection;
19307	const colorMode = painter.colorModeForRenderPass();
19308	const align = !painter.options.moving;
19309	const rasterOpacity = layer.paint.get("raster-opacity");
19310	const textureFilter = layer.paint.get("resampling") === "nearest" || layer.paint.get("raster-resampling") === "nearest" ? gl.NEAREST : gl.LINEAR;
19311	const fadeDuration = layer.paint.get("raster-fade-duration");
19312	const isTerrain = !!painter.style.map.terrain;
19313	for (const coord of coords) {
19314		const depthMode = painter.getDepthModeForSublayer(coord.overscaledZ - minTileZ, rasterOpacity === 1 ? DepthMode.ReadWrite : DepthMode.ReadOnly, gl.LESS);
19315		const tile = tileManager.getTile(coord);
19316		context.activeTexture.set(gl.TEXTURE0);
19317		tile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
19318		context.activeTexture.set(gl.TEXTURE1);
19319		const { parentTile, parentScaleBy, parentTopLeft, fadeValues } = getFadeProperties(tile, tileManager, fadeDuration, isTerrain);
19320		tile.fadeOpacity = fadeValues.tileOpacity;
19321		if (parentTile) {
19322			parentTile.fadeOpacity = fadeValues.parentTileOpacity;
19323			parentTile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
19324		} else tile.texture.bind(textureFilter, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
19325		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);
19326		const terrainData = getTerrainDataForTile(renderContext, coord);
19327		const projectionData = getProjectionDataForTile(renderContext, coord, { aligned: align });
19328		const uniformValues = rasterUniformValues(parentTopLeft, parentScaleBy, fadeValues.fadeMix, layer, corners, imageWarp);
19329		const mesh = sourceMesh ?? projection.getMeshFromTileID(context, coord.canonical, useBorder, allowPoles, "raster");
19330		const stencilMode = stencilModes ? stencilModes[coord.overscaledZ] : StencilMode.disabled;
19331		program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, flipCullfaceMode ? CullFaceMode.frontCCW : CullFaceMode.backCCW, uniformValues, terrainData, projectionData, layer.id, mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19332	}
19333}
19334/**
19335* Get fade properties for current tile - either cross-fading or self-fading properties.
19336*/
19337function getFadeProperties(tile, tileManager, fadeDuration, isTerrain) {
19338	const defaults = {
19339		parentTile: null,
19340		parentScaleBy: 1,
19341		parentTopLeft: [0, 0],
19342		fadeValues: {
19343			tileOpacity: 1,
19344			parentTileOpacity: 1,
19345			fadeMix: {
19346				opacity: 1,
19347				mix: 0
19348			}
19349		}
19350	};
19351	if (fadeDuration === 0 || isTerrain) return defaults;
19352	if (tile.fadingParentID) {
19353		const parentTile = tileManager.getLoadedTile(tile.fadingParentID);
19354		if (!parentTile) return defaults;
19355		const parentScaleBy = Math.pow(2, parentTile.tileID.overscaledZ - tile.tileID.overscaledZ);
19356		return {
19357			parentTile,
19358			parentScaleBy,
19359			parentTopLeft: [tile.tileID.canonical.x * parentScaleBy % 1, tile.tileID.canonical.y * parentScaleBy % 1],
19360			fadeValues: getCrossFadeValues(tile, parentTile, fadeDuration)
19361		};
19362	}
19363	if (tile.selfFading) return {
19364		parentTile: null,
19365		parentScaleBy: 1,
19366		parentTopLeft: [0, 0],
19367		fadeValues: getSelfFadeValues(tile, fadeDuration)
19368	};
19369	return defaults;
19370}
19371/**
19372* Cross-fade values for a base tile with a parent tile (for zooming in/out)
19373*/
19374function getCrossFadeValues(tile, parentTile, fadeDuration) {
19375	const currentTime = now();
19376	const timeSinceTile = (currentTime - tile.timeAdded) / fadeDuration;
19377	const timeSinceParent = (currentTime - parentTile.timeAdded) / fadeDuration;
19378	const doFadeIn = tile.fadingDirection === 1;
19379	const opacity1 = clamp(timeSinceTile, 0, 1);
19380	const opacity2 = clamp(1 - timeSinceParent, 0, 1);
19381	const tileOpacity = doFadeIn ? opacity1 : opacity2;
19382	return {
19383		tileOpacity,
19384		parentTileOpacity: doFadeIn ? opacity2 : opacity1,
19385		fadeMix: {
19386			opacity: 1,
19387			mix: 1 - tileOpacity
19388		}
19389	};
19390}
19391/**
19392* Simple fade-in values for tile without a parent (i.e. edge tiles)
19393*/
19394function getSelfFadeValues(tile, fadeDuration) {
19395	const timeSinceTile = (now() - tile.timeAdded) / fadeDuration;
19396	const tileOpacity = clamp(timeSinceTile, 0, 1);
19397	return {
19398		tileOpacity,
19399		fadeMix: {
19400			opacity: tileOpacity,
19401			mix: 0
19402		}
19403	};
19404}
19405//#endregion
19406//#region src/webgl/draw/draw_background.ts
19407function drawBackground(painter, tileManager, layer, coords, renderContext) {
19408	const color = layer.paint.get("background-color");
19409	const opacity = layer.paint.get("background-opacity");
19410	if (opacity === 0) return;
19411	const context = painter.context;
19412	const gl = context.gl;
19413	const projection = painter.style.projection;
19414	const transform = painter.transform;
19415	const tileSize = transform.tileSize;
19416	const image = layer.paint.get("background-pattern");
19417	if (painter.isPatternMissing(image)) return;
19418	const pass = !image && color.a === 1 && opacity === 1 && painter.opaquePassEnabledForLayer() ? "opaque" : "translucent";
19419	if (renderContext.currentPass !== pass) return;
19420	const stencilMode = StencilMode.disabled;
19421	const depthMode = painter.getDepthModeForSublayer(0, pass === "opaque" ? DepthMode.ReadWrite : DepthMode.ReadOnly);
19422	const colorMode = painter.colorModeForRenderPass();
19423	const program = painter.useProgram(image ? "backgroundPattern" : "background");
19424	const tileIDs = coords ? coords : coveringTiles(transform, {
19425		tileSize,
19426		terrain: painter.style.map.terrain
19427	});
19428	if (image) {
19429		context.activeTexture.set(gl.TEXTURE0);
19430		painter.patternAtlas.bind(painter.context);
19431	}
19432	const crossfade = layer.getCrossfadeParameters();
19433	for (const tileID of tileIDs) {
19434		const projectionData = getProjectionDataForTile(renderContext, tileID);
19435		const uniformValues = image ? backgroundPatternUniformValues(opacity, painter, image, {
19436			tileID,
19437			tileSize
19438		}, crossfade) : backgroundUniformValues(opacity, color);
19439		const terrainData = getTerrainDataForTile(renderContext, tileID);
19440		const mesh = projection.getMeshFromTileID(context, tileID.canonical, false, true, "raster");
19441		program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, layer.id, mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19442	}
19443}
19444//#endregion
19445//#region src/webgl/draw/draw_debug.ts
19446const topColor = new Color(1, 0, 0, 1);
19447const btmColor = new Color(0, 1, 0, 1);
19448const leftColor = new Color(0, 0, 1, 1);
19449const rightColor = new Color(1, 0, 1, 1);
19450const centerColor = new Color(0, 1, 1, 1);
19451function drawDebugPadding(painter) {
19452	const padding = painter.transform.padding;
19453	const lineWidth = 3;
19454	drawHorizontalLine(painter, painter.transform.height - (padding.top || 0), lineWidth, topColor);
19455	drawHorizontalLine(painter, padding.bottom || 0, lineWidth, btmColor);
19456	drawVerticalLine(painter, padding.left || 0, lineWidth, leftColor);
19457	drawVerticalLine(painter, painter.transform.width - (padding.right || 0), lineWidth, rightColor);
19458	const center = painter.transform.centerPoint;
19459	drawCrosshair(painter, center.x, painter.transform.height - center.y, centerColor);
19460}
19461function drawCrosshair(painter, x, y, color) {
19462	const size = 20;
19463	const lineWidth = 2;
19464	drawDebugSSRect(painter, x - lineWidth / 2, y - size / 2, lineWidth, size, color);
19465	drawDebugSSRect(painter, x - size / 2, y - lineWidth / 2, size, lineWidth, color);
19466}
19467function drawHorizontalLine(painter, y, lineWidth, color) {
19468	drawDebugSSRect(painter, 0, y + lineWidth / 2, painter.transform.width, lineWidth, color);
19469}
19470function drawVerticalLine(painter, x, lineWidth, color) {
19471	drawDebugSSRect(painter, x - lineWidth / 2, 0, lineWidth, painter.transform.height, color);
19472}
19473function drawDebugSSRect(painter, x, y, width, height, color) {
19474	const context = painter.context;
19475	const gl = context.gl;
19476	gl.enable(gl.SCISSOR_TEST);
19477	gl.scissor(x * painter.pixelRatio, y * painter.pixelRatio, width * painter.pixelRatio, height * painter.pixelRatio);
19478	context.clear({ color });
19479	gl.disable(gl.SCISSOR_TEST);
19480}
19481function drawDebug(painter, tileManager, coords, renderContext) {
19482	for (const coord of coords) drawDebugTile(painter, tileManager, coord, renderContext);
19483}
19484function drawDebugTile(painter, tileManager, coord, renderContext) {
19485	const context = painter.context;
19486	const gl = context.gl;
19487	const program = painter.useProgram("debug");
19488	const depthMode = DepthMode.disabled;
19489	const stencilMode = StencilMode.disabled;
19490	const colorMode = painter.colorModeForRenderPass();
19491	const id = "$debug";
19492	const terrainData = getTerrainDataForTile(renderContext, coord);
19493	context.activeTexture.set(gl.TEXTURE0);
19494	const tileByteLength = tileManager.getTileByID(coord.key).latestRawTileData?.byteLength || 0;
19495	const tileSizeKb = Math.floor(tileByteLength / 1024);
19496	const tileSize = tileManager.getTile(coord).tileSize;
19497	const scaleRatio = 512 / Math.min(tileSize, 512) * (coord.overscaledZ / painter.transform.zoom) * .5;
19498	let tileIdText = coord.canonical.toString();
19499	if (coord.overscaledZ !== coord.canonical.z) tileIdText += ` => ${coord.overscaledZ}`;
19500	drawTextToOverlay(painter, `${tileIdText} ${tileSizeKb}kB`);
19501	const projectionData = getProjectionDataForTile(renderContext, coord);
19502	program.draw(context, gl.TRIANGLES, depthMode, stencilMode, ColorMode.alphaBlended, CullFaceMode.disabled, debugUniformValues(Color.transparent, scaleRatio), null, projectionData, id, painter.debugBuffer, painter.quadTriangleIndexBuffer, painter.debugSegments);
19503	program.draw(context, gl.LINE_STRIP, depthMode, stencilMode, colorMode, CullFaceMode.disabled, debugUniformValues(Color.red), terrainData, projectionData, id, painter.debugBuffer, painter.tileBorderIndexBuffer, painter.debugSegments);
19504}
19505function drawTextToOverlay(painter, text) {
19506	painter.initDebugOverlayCanvas();
19507	const canvas = painter.debugOverlayCanvas;
19508	const gl = painter.context.gl;
19509	const ctx2d = painter.debugOverlayCanvas.getContext("2d");
19510	ctx2d.clearRect(0, 0, canvas.width, canvas.height);
19511	ctx2d.shadowColor = "white";
19512	ctx2d.shadowBlur = 2;
19513	ctx2d.lineWidth = 1.5;
19514	ctx2d.strokeStyle = "white";
19515	ctx2d.textBaseline = "top";
19516	ctx2d.font = `bold 36px Open Sans, sans-serif`;
19517	ctx2d.fillText(text, 5, 5);
19518	ctx2d.strokeText(text, 5, 5);
19519	painter.debugOverlayTexture.update(canvas);
19520	painter.debugOverlayTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
19521}
19522function selectDebugSource(style, zoom) {
19523	let selectedSource = null;
19524	const sources = Object.values(style._layers).flatMap((layer) => {
19525		if (layer.source && !layer.isHidden(zoom)) return [style.tileManagers[layer.source]];
19526		else return [];
19527	});
19528	const vectorSources = sources.filter((source) => source.getSource().type === "vector");
19529	const otherSources = sources.filter((source) => source.getSource().type !== "vector");
19530	const considerSource = (source) => {
19531		if (!selectedSource || selectedSource.getSource().maxzoom < source.getSource().maxzoom) selectedSource = source;
19532	};
19533	for (const source of vectorSources) considerSource(source);
19534	if (!selectedSource) for (const source of otherSources) considerSource(source);
19535	return selectedSource;
19536}
19537//#endregion
19538//#region src/webgl/draw/draw_custom.ts
19539function drawCustom(painter, tileManager, layer, renderContext) {
19540	const { isRenderingGlobe } = renderContext;
19541	const context = painter.context;
19542	const implementation = layer.implementation;
19543	const projection = painter.style.projection;
19544	const transform = painter.transform;
19545	const projectionData = transform.getProjectionDataForCustomLayer(isRenderingGlobe);
19546	const customLayerArgs = {
19547		farZ: transform.farZ,
19548		nearZ: transform.nearZ,
19549		fov: transform.fov * Math.PI / 180,
19550		modelViewProjectionMatrix: transform.modelViewProjectionMatrix,
19551		projectionMatrix: transform.projectionMatrix,
19552		shaderData: {
19553			variantName: projection.shaderVariantName,
19554			vertexShaderPrelude: `const float PI = 3.141592653589793;\nuniform mat4 u_projection_matrix;\n${projection.shaderPreludeCode.vertexSource}`,
19555			define: projection.shaderDefine
19556		},
19557		defaultProjectionData: projectionData,
19558		getProjectionData: (params) => {
19559			return transform.getProjectionData({
19560				overscaledTileID: new OverscaledTileID(params.tileID.canonical.z, params.tileID.wrap ?? 0, params.tileID.canonical.z, params.tileID.canonical.x, params.tileID.canonical.y),
19561				aligned: params.aligned,
19562				applyGlobeMatrix: params.applyGlobeMatrix,
19563				applyTerrainMatrix: params.applyTerrainMatrix
19564			});
19565		}
19566	};
19567	const renderingMode = implementation.renderingMode ? implementation.renderingMode : "2d";
19568	if (renderContext.currentPass === "offscreen") {
19569		const prerender = implementation.prerender;
19570		if (prerender) {
19571			painter.setCustomLayerDefaults();
19572			context.setColorMode(painter.colorModeForRenderPass());
19573			prerender.call(implementation, context.gl, customLayerArgs);
19574			context.setDirty();
19575			painter.setBaseState();
19576		}
19577	} else if (renderContext.currentPass === "translucent") {
19578		painter.setCustomLayerDefaults();
19579		context.setColorMode(painter.colorModeForRenderPass());
19580		context.setStencilMode(StencilMode.disabled);
19581		const depthMode = renderingMode === "3d" ? painter.getDepthModeFor3D() : painter.getDepthModeForSublayer(0, DepthMode.ReadOnly);
19582		context.setDepthMode(depthMode);
19583		implementation.render(context.gl, customLayerArgs);
19584		context.setDirty();
19585		painter.setBaseState();
19586		context.bindFramebuffer.set(null);
19587	}
19588}
19589//#endregion
19590//#region src/webgl/draw/draw_terrain.ts
19591/**
19592* Redraw the Depth Framebuffer
19593* @param painter - the painter
19594* @param terrain - the terrain
19595*/
19596function drawDepth(painter, terrain) {
19597	const context = painter.context;
19598	const gl = context.gl;
19599	const tr = painter.transform;
19600	const colorMode = ColorMode.unblended;
19601	const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, [0, 1]);
19602	const tiles = terrain.tileManager.getRenderableTiles();
19603	const program = painter.useProgram("terrainDepth");
19604	context.bindFramebuffer.set(terrain.getFramebuffer().framebuffer);
19605	context.viewport.set([
19606		0,
19607		0,
19608		painter.width / devicePixelRatio,
19609		painter.height / devicePixelRatio
19610	]);
19611	context.clear({
19612		color: Color.white,
19613		depth: 1
19614	});
19615	for (const tile of tiles) {
19616		const mesh = terrain.getTerrainMesh(tile.tileID);
19617		const terrainData = terrain.getTerrainData(tile.tileID);
19618		const projectionData = tr.getProjectionData({
19619			overscaledTileID: tile.tileID,
19620			applyTerrainMatrix: false,
19621			applyGlobeMatrix: true
19622		});
19623		const uniformValues = terrainDepthUniformValues(terrain.getSkirtLength(tr.zoom));
19624		program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, "terrain", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19625	}
19626	context.bindFramebuffer.set(null);
19627	context.viewport.set([
19628		0,
19629		0,
19630		painter.width,
19631		painter.height
19632	]);
19633}
19634function drawTerrain(painter, terrain, tiles, renderContext) {
19635	const { isRenderingGlobe } = renderContext;
19636	const context = painter.context;
19637	const gl = context.gl;
19638	const tr = painter.transform;
19639	const colorMode = painter.colorModeForRenderPass();
19640	const depthMode = painter.getDepthModeFor3D();
19641	const program = painter.useProgram("terrain");
19642	context.bindFramebuffer.set(null);
19643	context.viewport.set([
19644		0,
19645		0,
19646		painter.width,
19647		painter.height
19648	]);
19649	for (const tile of tiles) {
19650		const mesh = terrain.getTerrainMesh(tile.tileID);
19651		const texture = painter.renderToTexture.getTexture(tile);
19652		const terrainData = terrain.getTerrainData(tile.tileID);
19653		context.activeTexture.set(gl.TEXTURE0);
19654		texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_LINEAR);
19655		const eleDelta = terrain.getSkirtLength(tr.zoom);
19656		const fogMatrix = tr.calculateFogMatrix(tile.tileID.toUnwrapped());
19657		const uniformValues = terrainUniformValues(eleDelta, fogMatrix, painter.style.sky, tr.pitch, isRenderingGlobe);
19658		const projectionData = getProjectionDataForTile(renderContext, tile.tileID, { applyTerrainMatrix: false });
19659		program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCCW, uniformValues, terrainData, projectionData, "terrain", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19660	}
19661}
19662//#endregion
19663//#region src/webgl/draw/draw_sky.ts
19664function getMesh(context, sky) {
19665	if (!sky.mesh) {
19666		const vertexArray = new PosArray();
19667		vertexArray.emplaceBack(-1, -1);
19668		vertexArray.emplaceBack(1, -1);
19669		vertexArray.emplaceBack(1, 1);
19670		vertexArray.emplaceBack(-1, 1);
19671		const indexArray = new TriangleIndexArray();
19672		indexArray.emplaceBack(0, 1, 2);
19673		indexArray.emplaceBack(0, 2, 3);
19674		sky.mesh = new Mesh(context.createVertexBuffer(vertexArray, posAttributes.members), context.createIndexBuffer(indexArray), SegmentVector.simpleSegment(0, 0, vertexArray.length, indexArray.length));
19675	}
19676	return sky.mesh;
19677}
19678function drawSky(painter, sky) {
19679	const context = painter.context;
19680	const gl = context.gl;
19681	const skyUniforms = skyUniformValues(sky, painter.transform, painter.pixelRatio);
19682	const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadWrite, [0, 1]);
19683	const stencilMode = StencilMode.disabled;
19684	const colorMode = painter.colorModeForRenderPass();
19685	const program = painter.useProgram("sky");
19686	const mesh = getMesh(context, sky);
19687	program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled, skyUniforms, null, void 0, "sky", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19688}
19689function getSunPos(light, transform) {
19690	const lightPos = light.getCartesianPosition();
19691	negate(lightPos, lightPos);
19692	const lightMat = identity(/* @__PURE__ */ new Float64Array(16));
19693	if (light.properties.get("anchor") === "map") {
19694		rotateZ(lightMat, lightMat, transform.rollInRadians);
19695		rotateX(lightMat, lightMat, -transform.pitchInRadians);
19696		rotateZ(lightMat, lightMat, transform.bearingInRadians);
19697		rotateX(lightMat, lightMat, transform.center.lat * Math.PI / 180);
19698		rotateY$1(lightMat, lightMat, -transform.center.lng * Math.PI / 180);
19699	}
19700	transformMat4$1(lightPos, lightPos, lightMat);
19701	return lightPos;
19702}
19703function drawAtmosphere(painter, sky, light) {
19704	const context = painter.context;
19705	const gl = context.gl;
19706	const program = painter.useProgram("atmosphere");
19707	const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadOnly, [0, 1]);
19708	const transform = painter.transform;
19709	const sunPos = getSunPos(light, painter.transform);
19710	const projectionData = transform.getProjectionData({
19711		overscaledTileID: null,
19712		applyGlobeMatrix: true,
19713		applyTerrainMatrix: true
19714	});
19715	const globeRadius = getGlobeRadiusPixels(transform.worldSize, transform.center.lat);
19716	const globePosition = getGlobeCenterInViewSpace(transform);
19717	const altitudeBlend = getAtmosphereAltitudeBlend(length(globePosition) - globeRadius, globeRadius);
19718	const atmosphereBlend = sky.properties.get("atmosphere-blend") * projectionData.projectionTransition * altitudeBlend;
19719	if (atmosphereBlend === 0) return;
19720	const uniformValues = atmosphereUniformValues(sunPos, atmosphereBlend, globePosition, globeRadius, transform.inverseProjectionMatrix);
19721	const mesh = getMesh(context, sky);
19722	program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, ColorMode.alphaBlended, CullFaceMode.disabled, uniformValues, null, null, "atmosphere", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19723}
19724//#endregion
19725//#region src/webgl/draw/index.ts
19726const webglDrawFunctions = {
19727	symbol: drawSymbols,
19728	circle: drawCircles,
19729	heatmap: drawHeatmap,
19730	line: drawLine,
19731	fill: drawFill,
19732	fillExtrusion: drawFillExtrusion,
19733	hillshade: drawHillshade,
19734	colorRelief: drawColorRelief,
19735	raster: drawRaster,
19736	background: drawBackground,
19737	sky: drawSky,
19738	atmosphere: drawAtmosphere,
19739	custom: drawCustom,
19740	debug: drawDebug,
19741	debugPadding: drawDebugPadding,
19742	terrainDepth: drawDepth
19743};
19744//#endregion
19745//#region src/render/painter.ts
19746/**
19747* @internal
19748* Initialize a new painter object.
19749*/
19750var Painter = class Painter {
19751	constructor(gl, transform) {
19752		this.drawFunctions = webglDrawFunctions;
19753		this.context = new Context(gl);
19754		this.transform = transform;
19755		this.layerOpacityFbo = null;
19756		this._tileTextures = {};
19757		this._rttObjectRecyclePool = [];
19758		this._rttSharedFbo = null;
19759		this.terrainFacilitator = {
19760			depthDirty: true,
19761			matrix: identity(/* @__PURE__ */ new Float64Array(16)),
19762			renderTime: 0
19763		};
19764		this.setup();
19765		this.numSublayers = TileManager.maxOverzooming + TileManager.maxUnderzooming + 1;
19766		this.depthEpsilon = 1 / Math.pow(2, 16);
19767		this.crossTileSymbolIndex = new CrossTileSymbolIndex();
19768	}
19769	resize(width, height, pixelRatio) {
19770		this.width = Math.floor(width * pixelRatio);
19771		this.height = Math.floor(height * pixelRatio);
19772		this.pixelRatio = pixelRatio;
19773		this.context.viewport.set([
19774			0,
19775			0,
19776			this.width,
19777			this.height
19778		]);
19779		if (this.style) for (const layerId of this.style._order) this.style._layers[layerId].resize();
19780	}
19781	setup() {
19782		const context = this.context;
19783		const tileExtentArray = new PosArray();
19784		tileExtentArray.emplaceBack(0, 0);
19785		tileExtentArray.emplaceBack(EXTENT, 0);
19786		tileExtentArray.emplaceBack(0, EXTENT);
19787		tileExtentArray.emplaceBack(EXTENT, EXTENT);
19788		this.tileExtentBuffer = context.createVertexBuffer(tileExtentArray, posAttributes.members);
19789		this.tileExtentSegments = SegmentVector.simpleSegment(0, 0, 4, 2);
19790		const debugArray = new PosArray();
19791		debugArray.emplaceBack(0, 0);
19792		debugArray.emplaceBack(EXTENT, 0);
19793		debugArray.emplaceBack(0, EXTENT);
19794		debugArray.emplaceBack(EXTENT, EXTENT);
19795		this.debugBuffer = context.createVertexBuffer(debugArray, posAttributes.members);
19796		this.debugSegments = SegmentVector.simpleSegment(0, 0, 4, 5);
19797		const rasterBoundsArray = new RasterBoundsArray();
19798		rasterBoundsArray.emplaceBack(0, 0, 0, 0);
19799		rasterBoundsArray.emplaceBack(EXTENT, 0, EXTENT, 0);
19800		rasterBoundsArray.emplaceBack(0, EXTENT, 0, EXTENT);
19801		rasterBoundsArray.emplaceBack(EXTENT, EXTENT, EXTENT, EXTENT);
19802		this.rasterBoundsBuffer = context.createVertexBuffer(rasterBoundsArray, rasterBoundsAttributes.members);
19803		this.rasterBoundsSegments = SegmentVector.simpleSegment(0, 0, 4, 2);
19804		const rasterBoundsArrayPosOnly = new PosArray();
19805		rasterBoundsArrayPosOnly.emplaceBack(0, 0);
19806		rasterBoundsArrayPosOnly.emplaceBack(EXTENT, 0);
19807		rasterBoundsArrayPosOnly.emplaceBack(0, EXTENT);
19808		rasterBoundsArrayPosOnly.emplaceBack(EXTENT, EXTENT);
19809		this.rasterBoundsBufferPosOnly = context.createVertexBuffer(rasterBoundsArrayPosOnly, posAttributes.members);
19810		this.rasterBoundsSegmentsPosOnly = SegmentVector.simpleSegment(0, 0, 4, 5);
19811		const viewportArray = new PosArray();
19812		viewportArray.emplaceBack(0, 0);
19813		viewportArray.emplaceBack(1, 0);
19814		viewportArray.emplaceBack(0, 1);
19815		viewportArray.emplaceBack(1, 1);
19816		this.viewportBuffer = context.createVertexBuffer(viewportArray, posAttributes.members);
19817		this.viewportSegments = SegmentVector.simpleSegment(0, 0, 4, 2);
19818		const tileLineStripIndices = new LineStripIndexArray();
19819		tileLineStripIndices.emplaceBack(0);
19820		tileLineStripIndices.emplaceBack(1);
19821		tileLineStripIndices.emplaceBack(3);
19822		tileLineStripIndices.emplaceBack(2);
19823		tileLineStripIndices.emplaceBack(0);
19824		this.tileBorderIndexBuffer = context.createIndexBuffer(tileLineStripIndices);
19825		const quadTriangleIndices = new TriangleIndexArray();
19826		quadTriangleIndices.emplaceBack(1, 0, 2);
19827		quadTriangleIndices.emplaceBack(1, 2, 3);
19828		this.quadTriangleIndexBuffer = context.createIndexBuffer(quadTriangleIndices);
19829		const gl = this.context.gl;
19830		this.stencilClearMode = new StencilMode({
19831			func: gl.ALWAYS,
19832			mask: 0
19833		}, 0, 255, gl.ZERO, gl.ZERO, gl.ZERO);
19834		this.tileExtentMesh = new Mesh(this.tileExtentBuffer, this.quadTriangleIndexBuffer, this.tileExtentSegments);
19835	}
19836	clearStencil() {
19837		const context = this.context;
19838		const gl = context.gl;
19839		this.nextStencilID = 1;
19840		this.currentStencilSource = void 0;
19841		const matrix = create$1();
19842		ortho(matrix, 0, this.width, this.height, 0, 0, 1);
19843		scale(matrix, matrix, [
19844			gl.drawingBufferWidth,
19845			gl.drawingBufferHeight,
19846			0
19847		]);
19848		const projectionData = {
19849			mainMatrix: matrix,
19850			tileMercatorCoords: [
19851				0,
19852				0,
19853				1,
19854				1
19855			],
19856			clippingPlane: [
19857				0,
19858				0,
19859				0,
19860				0
19861			],
19862			projectionTransition: 0,
19863			fallbackMatrix: matrix,
19864			clipAntimeridian: false
19865		};
19866		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);
19867	}
19868	renderTileClippingMasks(layer, tileIDs) {
19869		if (this.currentStencilSource === layer.source || !layer.isTileClipped() || !tileIDs?.length) return;
19870		this.currentStencilSource = layer.source;
19871		if (this.nextStencilID + tileIDs.length > 256) this.clearStencil();
19872		const context = this.context;
19873		context.setColorMode(ColorMode.disabled);
19874		context.setDepthMode(DepthMode.disabled);
19875		const stencilRefs = {};
19876		for (const tileID of tileIDs) stencilRefs[tileID.key] = this.nextStencilID++;
19877		if (this.style.projection.useSubdivision) this._renderTileMasks(stencilRefs, tileIDs, true);
19878		this._renderTileMasks(stencilRefs, tileIDs, false);
19879		this._tileClippingMaskIDs = stencilRefs;
19880	}
19881	_renderTileMasks(tileStencilRefs, tileIDs, useBorders) {
19882		const context = this.context;
19883		const gl = context.gl;
19884		const projection = this.style.projection;
19885		const renderContext = this.renderContext;
19886		const program = this.useProgram("clippingMask");
19887		for (const tileID of tileIDs) {
19888			const stencilRef = tileStencilRefs[tileID.key];
19889			const terrainData = getTerrainDataForTile(renderContext, tileID);
19890			const mesh = projection.getMeshFromTileID(this.context, tileID.canonical, useBorders, true, "stencil");
19891			const projectionData = getProjectionDataForTile(renderContext, tileID);
19892			program.draw(context, gl.TRIANGLES, DepthMode.disabled, new StencilMode({
19893				func: gl.ALWAYS,
19894				mask: 0
19895			}, stencilRef, 255, gl.KEEP, gl.KEEP, gl.REPLACE), ColorMode.disabled, renderContext.isRenderingToTexture ? CullFaceMode.disabled : CullFaceMode.backCCW, null, terrainData, projectionData, "$clipping", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19896		}
19897	}
19898	/**
19899	* Fills the depth buffer with the geometry of all supplied tiles.
19900	* Does not change the color buffer or the stencil buffer.
19901	*/
19902	_renderTilesDepthBuffer() {
19903		const context = this.context;
19904		const gl = context.gl;
19905		const projection = this.style.projection;
19906		const transform = this.transform;
19907		const program = this.useProgram("depth");
19908		const depthMode = this.getDepthModeFor3D();
19909		const tileIDs = coveringTiles(transform, { tileSize: transform.tileSize });
19910		for (const tileID of tileIDs) {
19911			const terrainData = getTerrainDataForTile(this.renderContext, tileID);
19912			const mesh = projection.getMeshFromTileID(this.context, tileID.canonical, true, true, "raster");
19913			const projectionData = getProjectionDataForTile(this.renderContext, tileID);
19914			program.draw(context, gl.TRIANGLES, depthMode, StencilMode.disabled, ColorMode.disabled, CullFaceMode.backCCW, null, terrainData, projectionData, "$clipping", mesh.vertexBuffer, mesh.indexBuffer, mesh.segments);
19915		}
19916	}
19917	stencilModeFor3D() {
19918		this.currentStencilSource = void 0;
19919		if (this.nextStencilID + 1 > 256) this.clearStencil();
19920		const id = this.nextStencilID++;
19921		const gl = this.context.gl;
19922		return new StencilMode({
19923			func: gl.NOTEQUAL,
19924			mask: 255
19925		}, id, 255, gl.KEEP, gl.KEEP, gl.REPLACE);
19926	}
19927	stencilModeForClipping(tileID) {
19928		const gl = this.context.gl;
19929		return new StencilMode({
19930			func: gl.EQUAL,
19931			mask: 255
19932		}, this._tileClippingMaskIDs[tileID.key], 0, gl.KEEP, gl.KEEP, gl.REPLACE);
19933	}
19934	getStencilConfigForOverlapAndUpdateStencilID(tileIDs) {
19935		const gl = this.context.gl;
19936		const coords = tileIDs.sort((a, b) => b.overscaledZ - a.overscaledZ);
19937		const minTileZ = coords[coords.length - 1].overscaledZ;
19938		const stencilValues = coords[0].overscaledZ - minTileZ + 1;
19939		if (stencilValues > 1) {
19940			this.currentStencilSource = void 0;
19941			if (this.nextStencilID + stencilValues > 256) this.clearStencil();
19942			const zToStencilMode = {};
19943			for (let i = 0; i < stencilValues; i++) zToStencilMode[i + minTileZ] = new StencilMode({
19944				func: gl.GEQUAL,
19945				mask: 255
19946			}, i + this.nextStencilID, 255, gl.KEEP, gl.KEEP, gl.REPLACE);
19947			this.nextStencilID += stencilValues;
19948			return [zToStencilMode, coords];
19949		}
19950		return [{ [minTileZ]: StencilMode.disabled }, coords];
19951	}
19952	stencilConfigForOverlapTwoPass(tileIDs) {
19953		const gl = this.context.gl;
19954		const coords = tileIDs.sort((a, b) => b.overscaledZ - a.overscaledZ);
19955		const minTileZ = coords[coords.length - 1].overscaledZ;
19956		const stencilValues = coords[0].overscaledZ - minTileZ + 1;
19957		this.clearStencil();
19958		if (stencilValues > 1) {
19959			const zToStencilModeHigh = {};
19960			const zToStencilModeLow = {};
19961			for (let i = 0; i < stencilValues; i++) {
19962				zToStencilModeHigh[i + minTileZ] = new StencilMode({
19963					func: gl.GREATER,
19964					mask: 255
19965				}, stencilValues + 1 + i, 255, gl.KEEP, gl.KEEP, gl.REPLACE);
19966				zToStencilModeLow[i + minTileZ] = new StencilMode({
19967					func: gl.GREATER,
19968					mask: 255
19969				}, 1 + i, 255, gl.KEEP, gl.KEEP, gl.REPLACE);
19970			}
19971			this.nextStencilID = stencilValues * 2 + 1;
19972			return [
19973				zToStencilModeHigh,
19974				zToStencilModeLow,
19975				coords
19976			];
19977		} else {
19978			this.nextStencilID = 3;
19979			return [
19980				{ [minTileZ]: new StencilMode({
19981					func: gl.GREATER,
19982					mask: 255
19983				}, 2, 255, gl.KEEP, gl.KEEP, gl.REPLACE) },
19984				{ [minTileZ]: new StencilMode({
19985					func: gl.GREATER,
19986					mask: 255
19987				}, 1, 255, gl.KEEP, gl.KEEP, gl.REPLACE) },
19988				coords
19989			];
19990		}
19991	}
19992	colorModeForRenderPass() {
19993		const gl = this.context.gl;
19994		if (this._showOverdrawInspector) {
19995			const a = 1 / 8;
19996			return new ColorMode([gl.CONSTANT_COLOR, gl.ONE], new Color(a, a, a, 0), [
19997				true,
19998				true,
19999				true,
20000				true
20001			]);
20002		} else if (this.renderContext.currentPass === "opaque") return ColorMode.unblended;
20003		else return ColorMode.alphaBlended;
20004	}
20005	getDepthModeForSublayer(n, mask, func) {
20006		if (!this.opaquePassEnabledForLayer()) return DepthMode.disabled;
20007		const depth = 1 - ((1 + this.renderContext.currentLayer) * this.numSublayers + n) * this.depthEpsilon;
20008		return new DepthMode(func || this.context.gl.LEQUAL, mask, [depth, depth]);
20009	}
20010	getDepthModeFor3D() {
20011		return new DepthMode(this.context.gl.LEQUAL, DepthMode.ReadWrite, this.renderContext.depthRangeFor3D);
20012	}
20013	opaquePassEnabledForLayer() {
20014		return this.renderContext.currentLayer < this.renderContext.opaquePassCutoff;
20015	}
20016	render(style, options) {
20017		this.style = style;
20018		this.options = options;
20019		const renderContext = this.renderContext = createRenderContext(this.transform, style.projection, style.map.terrain ?? null);
20020		this.lineAtlas = style.lineAtlas;
20021		this.imageManager = style.imageManager;
20022		this.patternAtlas = style.patternAtlas;
20023		this.glyphManager = style.glyphManager;
20024		this.symbolFadeChange = style.placement.symbolFadeChange(now());
20025		updateFrameUniformBuffer(this.context.frameUniformBuffer, this);
20026		this.imageManager.beginFrame();
20027		const layerIds = this.style._order;
20028		const tileManagers = this.style.tileManagers;
20029		const coordsAscending = {};
20030		const coordsDescending = {};
20031		const coordsDescendingSymbol = {};
20032		for (const id in tileManagers) {
20033			const tileManager = tileManagers[id];
20034			if (tileManager.used) tileManager.prepare(this.context);
20035			coordsAscending[id] = tileManager.getVisibleCoordinates(false);
20036			coordsDescending[id] = coordsAscending[id].slice().reverse();
20037			coordsDescendingSymbol[id] = tileManager.getVisibleCoordinates(true).reverse();
20038		}
20039		renderContext.opaquePassCutoff = Infinity;
20040		for (let i = 0; i < layerIds.length; i++) {
20041			const layerId = layerIds[i];
20042			if (this.style._layers[layerId].is3D()) {
20043				renderContext.opaquePassCutoff = i;
20044				break;
20045			}
20046		}
20047		this.maybeDrawDepth();
20048		if (this.renderToTexture) {
20049			this.renderToTexture.prepareForRender(this.style, this.transform.zoom);
20050			renderContext.opaquePassCutoff = 0;
20051		}
20052		renderContext.currentPass = "offscreen";
20053		for (const layerId of layerIds) {
20054			const layer = this.style._layers[layerId];
20055			if (!layer.hasOffscreenPass() || layer.isHidden(this.transform.zoom)) continue;
20056			const coords = coordsDescending[layer.source];
20057			if (layer.type !== "custom" && !coords.length) continue;
20058			this.renderLayer(this, tileManagers[layer.source], layer, coords, renderContext);
20059		}
20060		this.context.viewport.set([
20061			0,
20062			0,
20063			this.width,
20064			this.height
20065		]);
20066		this.context.bindFramebuffer.set(null);
20067		this.context.clear({
20068			color: options.showOverdrawInspector ? Color.black : Color.transparent,
20069			depth: 1
20070		});
20071		this.clearStencil();
20072		if (this.style.sky) this.drawFunctions.sky(this, this.style.sky);
20073		this._showOverdrawInspector = options.showOverdrawInspector;
20074		renderContext.depthRangeFor3D = [0, 1 - (style._order.length + 2) * this.numSublayers * this.depthEpsilon];
20075		if (!this.renderToTexture) {
20076			renderContext.currentPass = "opaque";
20077			for (renderContext.currentLayer = layerIds.length - 1; renderContext.currentLayer >= 0; renderContext.currentLayer--) {
20078				const layer = this.style._layers[layerIds[renderContext.currentLayer]];
20079				if (layer.isHidden(this.transform.zoom)) continue;
20080				const tileManager = tileManagers[layer.source];
20081				const coords = coordsAscending[layer.source];
20082				this.renderTileClippingMasks(layer, coords);
20083				this.renderLayer(this, tileManager, layer, coords, renderContext);
20084			}
20085		}
20086		renderContext.currentPass = "translucent";
20087		let globeDepthRendered = false;
20088		for (renderContext.currentLayer = 0; renderContext.currentLayer < layerIds.length; renderContext.currentLayer++) {
20089			const layer = this.style._layers[layerIds[renderContext.currentLayer]];
20090			if (layer.isHidden(this.transform.zoom)) continue;
20091			const tileManager = tileManagers[layer.source];
20092			if (this.renderToTexture?.renderLayer(layer, renderContext)) continue;
20093			if (!this.opaquePassEnabledForLayer() && !globeDepthRendered) {
20094				globeDepthRendered = true;
20095				if (renderContext.isRenderingGlobe && !this.style.map.terrain) this._renderTilesDepthBuffer();
20096			}
20097			const coords = (layer.type === "symbol" ? coordsDescendingSymbol : coordsDescending)[layer.source];
20098			this.renderTileClippingMasks(layer, coordsAscending[layer.source]);
20099			this.renderLayer(this, tileManager, layer, coords, renderContext);
20100		}
20101		if (renderContext.isRenderingGlobe) this.drawFunctions.atmosphere(this, this.style.sky, this.style.light);
20102		if (this.options.showTileBoundaries) {
20103			const selectedSource = selectDebugSource(this.style, this.transform.zoom);
20104			if (selectedSource) this.drawFunctions.debug(this, selectedSource, selectedSource.getVisibleCoordinates(), renderContext);
20105		}
20106		if (this.options.showPadding) this.drawFunctions.debugPadding(this);
20107		if (this.renderToTexture && !this.options.moving) this.clearRTTPool();
20108		this.context.setDefault();
20109	}
20110	/**
20111	* Invalidates cached terrain depth so the next eligible depth pass redraws it.
20112	* DEM data can change the rendered surface while the camera and terrain tile set stay unchanged.
20113	* Repeated calls coalesce without rendering or scheduling a frame, even if terrain is not yet present.
20114	*/
20115	markTerrainDepthDirty() {
20116		this.terrainFacilitator.depthDirty = true;
20117	}
20118	/**
20119	* Updates the depth framebuffer after explicit invalidation, camera movement, or tile reloading.
20120	*/
20121	maybeDrawDepth() {
20122		if (!this.style?.projection || !this.style.map?.terrain) return;
20123		const prevMatrix = this.terrainFacilitator.matrix;
20124		const currMatrix = this.transform.modelViewProjectionMatrix;
20125		let doUpdate = this.terrainFacilitator.depthDirty;
20126		doUpdate ||= !equals(prevMatrix, currMatrix);
20127		doUpdate ||= this.style.map.terrain.tileManager.anyTilesAfterTime(this.terrainFacilitator.renderTime);
20128		if (!doUpdate) return;
20129		copy(prevMatrix, currMatrix);
20130		this.terrainFacilitator.renderTime = now();
20131		this.terrainFacilitator.depthDirty = false;
20132		this.drawFunctions.terrainDepth(this, this.style.map.terrain);
20133	}
20134	renderLayer(painter, tileManager, layer, coords, renderContext) {
20135		if (layer.isHidden(this.transform.zoom)) return;
20136		if (layer.type !== "background" && layer.type !== "custom" && !(coords || []).length) return;
20137		this.id = layer.id;
20138		const draw = this.drawFunctions;
20139		if (isSymbolStyleLayer(layer)) draw.symbol(painter, tileManager, layer, coords, this.style.placement.variableOffsets, renderContext);
20140		else if (isCircleStyleLayer(layer)) draw.circle(painter, tileManager, layer, coords, renderContext);
20141		else if (isHeatmapStyleLayer(layer)) draw.heatmap(painter, tileManager, layer, coords, renderContext);
20142		else if (isLineStyleLayer(layer)) draw.line(painter, tileManager, layer, coords, renderContext);
20143		else if (isFillStyleLayer(layer)) draw.fill(painter, tileManager, layer, coords, renderContext);
20144		else if (isFillExtrusionStyleLayer(layer)) draw.fillExtrusion(painter, tileManager, layer, coords, renderContext);
20145		else if (isHillshadeStyleLayer(layer)) draw.hillshade(painter, tileManager, layer, coords, renderContext);
20146		else if (isColorReliefStyleLayer(layer)) draw.colorRelief(painter, tileManager, layer, coords, renderContext);
20147		else if (isRasterStyleLayer(layer)) draw.raster(painter, tileManager, layer, coords, renderContext);
20148		else if (isBackgroundStyleLayer(layer)) draw.background(painter, tileManager, layer, coords, renderContext);
20149		else if (isCustomStyleLayer(layer)) draw.custom(painter, tileManager, layer, renderContext);
20150	}
20151	static {
20152		this.MAX_TEXTURE_POOL_SIZE_PER_BUCKET = 50;
20153	}
20154	saveTileTexture(texture) {
20155		const textures = this._tileTextures[texture.size[0]];
20156		if (!textures) this._tileTextures[texture.size[0]] = [texture];
20157		else if (textures.length < Painter.MAX_TEXTURE_POOL_SIZE_PER_BUCKET) textures.push(texture);
20158		else texture.destroy();
20159	}
20160	getTileTexture(size) {
20161		const textures = this._tileTextures[size];
20162		return textures && textures.length > 0 ? textures.pop() : null;
20163	}
20164	acquireRTT(size) {
20165		const gl = this.context.gl;
20166		const obj = this._rttObjectRecyclePool.pop();
20167		if (obj) {
20168			if (obj.size !== size) {
20169				obj.texture.update({
20170					width: size,
20171					height: size,
20172					data: null
20173				}, {
20174					premultiply: false,
20175					useMipmap: true
20176				});
20177				obj.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_LINEAR);
20178				if (this.context.extTextureFilterAnisotropic) gl.texParameterf(gl.TEXTURE_2D, this.context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, this.context.extTextureFilterAnisotropicMax);
20179				obj.size = size;
20180			}
20181			return obj;
20182		}
20183		const texture = new Texture(this.context, {
20184			width: size,
20185			height: size,
20186			data: null
20187		}, gl.RGBA, {
20188			premultiply: false,
20189			useMipmap: true
20190		});
20191		texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_LINEAR);
20192		if (this.context.extTextureFilterAnisotropic) gl.texParameterf(gl.TEXTURE_2D, this.context.extTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, this.context.extTextureFilterAnisotropicMax);
20193		return {
20194			texture,
20195			size
20196		};
20197	}
20198	/**
20199	* Binds the shared RTT FBO with the given RTTObject's texture attached
20200	* as color target. Creates / resizes the shared FBO and depth-stencil
20201	* renderbuffer lazily.
20202	*/
20203	bindRTT(obj) {
20204		const gl = this.context.gl;
20205		const size = obj.size;
20206		if (!this._rttSharedFbo) {
20207			const fbo = this.context.createFramebuffer(size, size, true, true);
20208			const depthRenderbuffer = this.context.createRenderbuffer(gl.DEPTH_STENCIL, size, size);
20209			fbo.depthAttachment.set(depthRenderbuffer);
20210			this._rttSharedFbo = {
20211				fbo,
20212				depthRenderbuffer,
20213				size
20214			};
20215		}
20216		if (this._rttSharedFbo.size !== size) {
20217			this.context.bindRenderbuffer.set(this._rttSharedFbo.depthRenderbuffer);
20218			gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, size, size);
20219			this.context.bindRenderbuffer.set(null);
20220			this._rttSharedFbo.fbo.width = size;
20221			this._rttSharedFbo.fbo.height = size;
20222			this._rttSharedFbo.size = size;
20223		}
20224		this._rttSharedFbo.fbo.colorAttachment.set(obj.texture.texture);
20225		this.context.bindFramebuffer.set(this._rttSharedFbo.fbo.framebuffer);
20226	}
20227	releaseRTT(obj) {
20228		this._rttObjectRecyclePool.push(obj);
20229	}
20230	/**
20231	* Destroys the pooled {@link RTTObject}s, the drapes no tile holds. Called at the end of a frame at rest,
20232	* when they would otherwise stay resident until the next gesture reuses them.
20233	*/
20234	clearRTTPool() {
20235		for (const obj of this._rttObjectRecyclePool) obj.texture.destroy();
20236		this._rttObjectRecyclePool.length = 0;
20237	}
20238	/**
20239	* Frees the pooled drapes and the shared render-to-texture FBO. Called when terrain is removed, after its tiles
20240	* released their drapes.
20241	*/
20242	destroyRTTResources() {
20243		this.clearRTTPool();
20244		if (!this._rttSharedFbo) return;
20245		this._rttSharedFbo.fbo.colorAttachment.set(null);
20246		this._rttSharedFbo.fbo.depthAttachment.set(null);
20247		const gl = this.context.gl;
20248		gl.deleteRenderbuffer(this._rttSharedFbo.depthRenderbuffer);
20249		gl.deleteFramebuffer(this._rttSharedFbo.fbo.framebuffer);
20250		this._rttSharedFbo = null;
20251	}
20252	/**
20253	* Checks whether a pattern image is needed, and if it is, whether it is not loaded.
20254	*
20255	* @returns true if a needed image is missing and rendering needs to be skipped.
20256	*/
20257	isPatternMissing(image) {
20258		if (!image) return false;
20259		if (!image.from || !image.to) return true;
20260		const imagePosA = this.patternAtlas.getPattern(image.from.toString());
20261		const imagePosB = this.patternAtlas.getPattern(image.to.toString());
20262		return !imagePosA || !imagePosB;
20263	}
20264	/**
20265	* Finds the required shader and its variant (base/terrain/globe, etc.) and binds it, compiling a new shader if required.
20266	* @param name - Name of the desired shader.
20267	* @param programConfiguration - Configuration of shader's inputs.
20268	* @param forceSimpleProjection - Whether to force the use of a shader variant with simple mercator projection vertex shader.
20269	* @param defines - Additional macros to be injected at the beginning of the shader. Expected format is `['#define XYZ']`, etc.
20270	* False by default. Use true when drawing with a simple projection matrix is desired, eg. when drawing a fullscreen quad.
20271	* @returns
20272	*/
20273	useProgram(name, programConfiguration, forceSimpleProjection = false, defines = []) {
20274		this.cache ||= {};
20275		const useTerrain = !!this.style.map.terrain;
20276		const projection = this.style.projection;
20277		const projectionPrelude = forceSimpleProjection ? shaders.projectionMercator : projection.shaderPreludeCode;
20278		const projectionDefine = forceSimpleProjection ? MercatorShaderDefine : projection.shaderDefine;
20279		const projectionKey = `/${forceSimpleProjection ? MercatorShaderVariantKey : projection.shaderVariantName}`;
20280		const configurationKey = programConfiguration ? programConfiguration.cacheKey : "";
20281		const overdrawKey = this._showOverdrawInspector ? "/overdraw" : "";
20282		const terrainKey = useTerrain ? "/terrain" : "";
20283		const definesKey = defines ? `/${defines.join("/")}` : "";
20284		const key = name + configurationKey + projectionKey + overdrawKey + terrainKey + definesKey;
20285		this.cache[key] ||= new Program(this.context, shaders[name], programConfiguration, programUniforms[name], this._showOverdrawInspector, useTerrain, projectionPrelude, projectionDefine, defines);
20286		return this.cache[key];
20287	}
20288	setCustomLayerDefaults() {
20289		this.context.setCustomLayerDefaults();
20290	}
20291	setBaseState() {
20292		const gl = this.context.gl;
20293		this.context.cullFace.set(false);
20294		this.context.viewport.set([
20295			0,
20296			0,
20297			this.width,
20298			this.height
20299		]);
20300		this.context.blendEquation.set(gl.FUNC_ADD);
20301	}
20302	initDebugOverlayCanvas() {
20303		if (this.debugOverlayCanvas == null) {
20304			this.debugOverlayCanvas = document.createElement("canvas");
20305			this.debugOverlayCanvas.width = 512;
20306			this.debugOverlayCanvas.height = 512;
20307			const gl = this.context.gl;
20308			this.debugOverlayTexture = new Texture(this.context, this.debugOverlayCanvas, gl.RGBA);
20309		}
20310	}
20311	destroy() {
20312		if (this._tileTextures) {
20313			for (const size in this._tileTextures) {
20314				const textures = this._tileTextures[size];
20315				if (textures) for (const texture of textures) texture.destroy();
20316			}
20317			this._tileTextures = {};
20318		}
20319		this.destroyRTTResources();
20320		this.layerOpacityFbo?.destroy();
20321		this.layerOpacityFbo = null;
20322		if (this.tileExtentBuffer) this.tileExtentBuffer.destroy();
20323		if (this.debugBuffer) this.debugBuffer.destroy();
20324		if (this.rasterBoundsBuffer) this.rasterBoundsBuffer.destroy();
20325		if (this.rasterBoundsBufferPosOnly) this.rasterBoundsBufferPosOnly.destroy();
20326		if (this.viewportBuffer) this.viewportBuffer.destroy();
20327		if (this.tileBorderIndexBuffer) this.tileBorderIndexBuffer.destroy();
20328		if (this.quadTriangleIndexBuffer) this.quadTriangleIndexBuffer.destroy();
20329		if (this.tileExtentMesh) this.tileExtentMesh.vertexBuffer?.destroy();
20330		if (this.tileExtentMesh) this.tileExtentMesh.indexBuffer?.destroy();
20331		if (this.debugOverlayTexture) this.debugOverlayTexture.destroy();
20332		this.context.projectionUniformBuffer.destroy();
20333		this.context.terrainUniformBuffer.destroy();
20334		this.context.frameUniformBuffer.destroy();
20335		if (this.cache) {
20336			for (const key in this.cache) {
20337				const program = this.cache[key];
20338				if (program?.program) this.context.gl.deleteProgram(program.program);
20339			}
20340			this.cache = {};
20341		}
20342		if (this.context) this.context.setDefault();
20343	}
20344	overLimit() {
20345		const { drawingBufferWidth, drawingBufferHeight } = this.context.gl;
20346		return this.width !== drawingBufferWidth || this.height !== drawingBufferHeight;
20347	}
20348};
20349//#endregion
20350//#region src/util/gpu_initialization_error.ts
20351/**
20352* Thrown by the `Map` constructor when a GPU rendering context cannot be created,
20353* or fired via the map's `error` event when recreating the context after a `webglcontextrestored` fails.
20354*
20355* Carries the canvas attributes that were requested and, when the browser provided one,
20356* the originating `webglcontextcreationerror` `statusMessage`.
20357* Consumers can branch on `instanceof GPUInitializationError` and inspect the cause programmatically.
20358* @see https://wiki.openstreetmap.org/wiki/This_map_requires_WebGL
20359*/
20360var GPUInitializationError = class extends Error {
20361	constructor(requestedAttributes, creationEvent) {
20362		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");
20363		this.name = "GPUInitializationError";
20364		this.requestedAttributes = requestedAttributes;
20365		this.statusMessage = creationEvent?.statusMessage ?? null;
20366	}
20367};
20368//#endregion
20369//#region src/util/throttle.ts
20370/**
20371* Throttle the given function to run at most every `period` milliseconds.
20372*/
20373function throttle(fn, time) {
20374	let pending = false;
20375	let timerId = null;
20376	let lastCallArgs;
20377	const later = () => {
20378		timerId = null;
20379		if (pending) {
20380			fn(...lastCallArgs);
20381			timerId = setTimeout(later, time);
20382			pending = false;
20383		}
20384	};
20385	return (...args) => {
20386		pending = true;
20387		lastCallArgs = args;
20388		if (!timerId) later();
20389		return timerId;
20390	};
20391}
20392//#endregion
20393//#region src/ui/hash.ts
20394/**
20395* Adds the map's position to its page's location hash.
20396* Passed as an option to the map object.
20397*
20398* @group Markers and Controls
20399*/
20400var Hash = class {
20401	constructor(hashName) {
20402		this._getHashParams = () => {
20403			return new URLSearchParams(window.location.hash.replace("#", ""));
20404		};
20405		this._getCurrentHash = () => {
20406			const params = this._getHashParams();
20407			if (this._hashName) return (params.get(this._hashName) || "").split("/");
20408			return ([...params.keys()][0] ?? "").split("/");
20409		};
20410		this._onHashChange = () => {
20411			const hash = this._getCurrentHash();
20412			if (!this._isValidHash(hash)) return false;
20413			const bearing = this._map.dragRotate.isEnabled() && this._map.touchZoomRotate.isEnabled() ? +(hash[3] || 0) : this._map.getBearing();
20414			this._map.jumpTo({
20415				center: [+hash[2], +hash[1]],
20416				zoom: +hash[0],
20417				bearing,
20418				pitch: +(hash[4] || 0)
20419			});
20420			return true;
20421		};
20422		this._updateHashUnthrottled = () => {
20423			const location = window.location.href.replace(/(#.*)?$/, this.getHashString());
20424			window.history.replaceState(window.history.state, null, location);
20425		};
20426		this._removeHash = () => {
20427			const params = this._getHashParams();
20428			if (this._hashName) params.delete(this._hashName);
20429			else {
20430				const keys = Array.from(params.keys());
20431				if (keys.length > 0) params.delete(keys[0]);
20432			}
20433			const newHash = decodeURIComponent(params.toString()).replace(/=&/g, "&").replace(/=$/g, "");
20434			const replaceString = newHash ? `#${newHash}` : "";
20435			let location = window.location.href.replace(/(#.+)?$/, replaceString);
20436			location = location.replace("&&", "&");
20437			window.history.replaceState(window.history.state, null, location);
20438		};
20439		this._updateHash = throttle(this._updateHashUnthrottled, 300);
20440		this._hashName = hashName && encodeURIComponent(hashName);
20441	}
20442	/**
20443	* Map element to listen for coordinate changes
20444	*
20445	* @param map - The map object
20446	*/
20447	addTo(map) {
20448		this._map = map;
20449		addEventListener("hashchange", this._onHashChange, false);
20450		this._map.on("moveend", this._updateHash);
20451		return this;
20452	}
20453	/**
20454	* Removes hash
20455	*/
20456	remove() {
20457		removeEventListener("hashchange", this._onHashChange, false);
20458		this._map.off("moveend", this._updateHash);
20459		clearTimeout(this._updateHash());
20460		this._removeHash();
20461		delete this._map;
20462		return this;
20463	}
20464	getHashString(mapFeedback) {
20465		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();
20466		let hash = "";
20467		if (mapFeedback) hash += `/${lng}/${lat}/${zoom}`;
20468		else hash += `${zoom}/${lat}/${lng}`;
20469		if (bearing || pitch) hash += `/${Math.round(bearing * 10) / 10}`;
20470		if (pitch) hash += `/${Math.round(pitch)}`;
20471		if (this._hashName) {
20472			const params = this._getHashParams();
20473			params.set(this._hashName, hash);
20474			return `#${decodeURIComponent(params.toString()).replace(/=&/g, "&").replace(/=$/g, "")}`;
20475		}
20476		return `#${hash}`;
20477	}
20478	_isValidHash(hash) {
20479		if (hash.length < 3 || hash.some((h) => isNaN(+h))) return false;
20480		try {
20481			new LngLat(+hash[2], +hash[1]);
20482		} catch {
20483			return false;
20484		}
20485		const zoom = +hash[0];
20486		const bearing = +(hash[3] || 0);
20487		const pitch = +(hash[4] || 0);
20488		return zoom >= this._map.getMinZoom() && zoom <= this._map.getMaxZoom() && bearing >= -180 && bearing <= 180 && pitch >= this._map.getMinPitch() && pitch <= this._map.getMaxPitch();
20489	}
20490};
20491//#endregion
20492//#region src/ui/handler_inertia.ts
20493const defaultInertiaOptions = {
20494	linearity: .3,
20495	easing: bezier(0, 0, .3, 1)
20496};
20497const defaultPanInertiaOptions = extend({
20498	deceleration: 2500,
20499	maxSpeed: 1400
20500}, defaultInertiaOptions);
20501const defaultZoomInertiaOptions = extend({
20502	deceleration: 20,
20503	maxSpeed: 1400
20504}, defaultInertiaOptions);
20505const defaultBearingInertiaOptions = extend({
20506	deceleration: 1e3,
20507	maxSpeed: 360
20508}, defaultInertiaOptions);
20509const defaultPitchInertiaOptions = extend({
20510	deceleration: 1e3,
20511	maxSpeed: 90
20512}, defaultInertiaOptions);
20513const defaultRollInertiaOptions = extend({
20514	deceleration: 1e3,
20515	maxSpeed: 360
20516}, defaultInertiaOptions);
20517/**
20518* The maximum age of a recorded gesture update, in milliseconds.
20519*/
20520const BUFFER_CUTOFF = 160;
20521/**
20522* The time window the gesture velocity is measured over, in milliseconds.
20523*/
20524const VELOCITY_WINDOW = 60;
20525var HandlerInertia = class {
20526	constructor(map) {
20527		this._map = map;
20528		this.clear();
20529	}
20530	clear() {
20531		this._inertiaBuffer = [];
20532	}
20533	record(settings) {
20534		this._drainInertiaBuffer();
20535		this._inertiaBuffer.push({
20536			time: now(),
20537			settings
20538		});
20539	}
20540	_drainInertiaBuffer() {
20541		const inertia = this._inertiaBuffer, currentTime = now();
20542		while (inertia.length > 0 && currentTime - inertia[0].time > BUFFER_CUTOFF) inertia.shift();
20543	}
20544	/**
20545	* Returns the entries the velocity is measured from: everything recorded within
20546	* {@link VELOCITY_WINDOW} before now, but never fewer than the last two, so that
20547	* inertia also works below two recorded updates per window.
20548	*/
20549	_getVelocityEntries() {
20550		const inertia = this._inertiaBuffer;
20551		const windowStart = now() - VELOCITY_WINDOW;
20552		let firstIndex = Math.max(0, inertia.length - 2);
20553		while (firstIndex > 0 && inertia[firstIndex - 1].time >= windowStart) firstIndex--;
20554		return inertia.slice(firstIndex);
20555	}
20556	/**
20557	* Returns the ease which continues the gesture that has just ended.
20558	*
20559	* The velocity is measured over the time up to the release and not up to the last
20560	* recorded movement, so that a gesture held still before being released has a lower
20561	* velocity, down to no inertia at all. The delta of an entry happened before it was
20562	* recorded, so the first entry only marks the start of the measured interval; anchors
20563	* are the exception, as they describe the state at the moment of their entry.
20564	*
20565	* @param panInertiaOptions - overrides for the pan inertia defaults
20566	* @returns the ease options, or `undefined` when the measured interval holds no
20567	* movement, which terrain gestures produce by recording updates without any delta
20568	*/
20569	_onMoveEnd(panInertiaOptions) {
20570		this._drainInertiaBuffer();
20571		const entries = this._getVelocityEntries();
20572		if (entries.length < 2) {
20573			this.clear();
20574			return;
20575		}
20576		const deltas = {
20577			zoom: 0,
20578			bearing: 0,
20579			pitch: 0,
20580			roll: 0,
20581			pan: new Point(0, 0),
20582			pinchAround: void 0,
20583			around: void 0
20584		};
20585		for (const { settings } of entries) {
20586			if (settings.around) deltas.around = settings.around;
20587			if (settings.pinchAround) deltas.pinchAround = settings.pinchAround;
20588		}
20589		for (const { settings } of entries.slice(1)) {
20590			deltas.zoom += settings.zoomDelta || 0;
20591			deltas.bearing += settings.bearingDelta || 0;
20592			deltas.pitch += settings.pitchDelta || 0;
20593			deltas.roll += settings.rollDelta || 0;
20594			if (settings.panDelta) deltas.pan._add(settings.panDelta);
20595		}
20596		if (!deltas.pan.mag() && !deltas.zoom && !deltas.bearing && !deltas.pitch && !deltas.roll) {
20597			this.clear();
20598			return;
20599		}
20600		const duration = now() - entries[0].time;
20601		const easeOptions = {};
20602		if (deltas.pan.mag()) {
20603			const result = calculateEasing(deltas.pan.mag(), duration, extend({}, defaultPanInertiaOptions, panInertiaOptions || {}));
20604			const finalPan = deltas.pan.mult(result.amount / deltas.pan.mag());
20605			const computedEaseOptions = this._map._camera.cameraHelper.handlePanInertia(finalPan, this._map._camera.transform);
20606			easeOptions.center = computedEaseOptions.easingCenter;
20607			easeOptions.offset = computedEaseOptions.easingOffset;
20608			extendDuration(easeOptions, result);
20609		}
20610		if (deltas.zoom) {
20611			const result = calculateEasing(deltas.zoom, duration, defaultZoomInertiaOptions);
20612			easeOptions.zoom = evaluateZoomSnap(this._map.getZoom() + result.amount, this._map.getZoomSnap(), result.amount);
20613			extendDuration(easeOptions, result);
20614		}
20615		if (deltas.bearing) {
20616			const result = calculateEasing(deltas.bearing, duration, defaultBearingInertiaOptions);
20617			easeOptions.bearing = this._map.getBearing() + clamp(result.amount, -179, 179);
20618			extendDuration(easeOptions, result);
20619		}
20620		if (deltas.pitch) {
20621			const result = calculateEasing(deltas.pitch, duration, defaultPitchInertiaOptions);
20622			easeOptions.pitch = this._map.getPitch() + result.amount;
20623			extendDuration(easeOptions, result);
20624		}
20625		if (deltas.roll) {
20626			const result = calculateEasing(deltas.roll, duration, defaultRollInertiaOptions);
20627			easeOptions.roll = this._map.getRoll() + clamp(result.amount, -179, 179);
20628			extendDuration(easeOptions, result);
20629		}
20630		if (easeOptions.zoom || easeOptions.bearing) {
20631			const last = deltas.pinchAround === void 0 ? deltas.around : deltas.pinchAround;
20632			easeOptions.around = last ? this._map.unproject(last) : this._map.getCenter();
20633		}
20634		this.clear();
20635		return extend(easeOptions, { noMoveStart: true });
20636	}
20637};
20638function extendDuration(easeOptions, result) {
20639	if (!easeOptions.duration || easeOptions.duration < result.duration) {
20640		easeOptions.duration = result.duration;
20641		easeOptions.easing = result.easing;
20642	}
20643}
20644function calculateEasing(amount, inertiaDuration, inertiaOptions) {
20645	const { maxSpeed, linearity, deceleration } = inertiaOptions;
20646	const speed = clamp(amount * linearity / (inertiaDuration / 1e3), -maxSpeed, maxSpeed);
20647	const duration = Math.abs(speed) / (deceleration * linearity);
20648	return {
20649		easing: inertiaOptions.easing,
20650		duration: duration * 1e3,
20651		amount: speed * (duration / 2)
20652	};
20653}
20654//#endregion
20655//#region src/ui/handler/map_event.ts
20656var MapEventHandler = class {
20657	constructor(map, options) {
20658		this._map = map;
20659		this._clickTolerance = options.clickTolerance;
20660	}
20661	reset() {
20662		delete this._mousedownPos;
20663	}
20664	wheel(e) {
20665		return this._firePreventable(new MapWheelEvent(this._map, e));
20666	}
20667	mousedown(e, point) {
20668		this._mousedownPos = point;
20669		return this._firePreventable(new MapMouseEvent(e.type, this._map, e));
20670	}
20671	mouseup(e) {
20672		this._map.fire(new MapMouseEvent(e.type, this._map, e));
20673	}
20674	click(e, point) {
20675		if (this._mousedownPos && this._mousedownPos.dist(point) >= this._clickTolerance) return;
20676		this._map.fire(new MapMouseEvent(e.type, this._map, e));
20677	}
20678	dblclick(e) {
20679		return this._firePreventable(new MapMouseEvent(e.type, this._map, e));
20680	}
20681	mouseover(e) {
20682		this._map.fire(new MapMouseEvent(e.type, this._map, e));
20683	}
20684	mouseout(e) {
20685		this._map.fire(new MapMouseEvent(e.type, this._map, e));
20686	}
20687	touchstart(e) {
20688		return this._firePreventable(new MapTouchEvent(e.type, this._map, e));
20689	}
20690	touchmove(e) {
20691		this._map.fire(new MapTouchEvent(e.type, this._map, e));
20692	}
20693	touchend(e) {
20694		this._map.fire(new MapTouchEvent(e.type, this._map, e));
20695	}
20696	touchcancel(e) {
20697		this._map.fire(new MapTouchEvent(e.type, this._map, e));
20698	}
20699	_firePreventable(mapEvent) {
20700		this._map.fire(mapEvent);
20701		if (mapEvent.defaultPrevented) return {};
20702	}
20703	isEnabled() {
20704		return true;
20705	}
20706	isActive() {
20707		return false;
20708	}
20709	enable() {}
20710	disable() {}
20711};
20712/**
20713* A single finger held in place this long fires a long-press contextmenu.
20714*/
20715const LONG_PRESS_DELAY = 500;
20716var BlockableMapEventHandler = class {
20717	constructor(map, options) {
20718		this._map = map;
20719		this._clickTolerance = options.clickTolerance;
20720	}
20721	reset() {
20722		this._delayContextMenu = false;
20723		this._ignoreContextMenu = true;
20724		delete this._contextMenuEvent;
20725		this._clearLongPress();
20726		this._touchActive = false;
20727	}
20728	mousemove(e) {
20729		this._map.fire(new MapMouseEvent(e.type, this._map, e));
20730	}
20731	mousedown() {
20732		this._delayContextMenu = true;
20733		this._ignoreContextMenu = false;
20734	}
20735	mouseup() {
20736		this._delayContextMenu = false;
20737		if (this._contextMenuEvent) {
20738			this._map.fire(new MapMouseEvent("contextmenu", this._map, this._contextMenuEvent));
20739			delete this._contextMenuEvent;
20740		}
20741	}
20742	/**
20743	* Starts the touch long press. Touch devices have no right click, so a long press opens
20744	* the context menu. It is detected with a timer rather than by relying on a native
20745	* contextmenu event, because Android Chrome fires one on a long press but iOS
20746	* Safari does not. A single finger held past the delay without moving fires a
20747	* contextmenu at that point; a pan or a lift cancels it.
20748	* @param e - the touch event
20749	* @param points - the touch points, in map coordinates
20750	* @param mapTouches - the touches that are on the map
20751	*/
20752	touchstart(e, points, mapTouches) {
20753		this._clearLongPress();
20754		this._touchActive = mapTouches.length > 0;
20755		if (mapTouches.length !== 1) return;
20756		this._longPressStart = points[0];
20757		const touch = mapTouches[0];
20758		this._longPressTimer = setTimeout(() => {
20759			this._longPressTimer = void 0;
20760			const originalEvent = new MouseEvent("contextmenu", {
20761				clientX: touch.clientX,
20762				clientY: touch.clientY,
20763				button: 2,
20764				bubbles: true,
20765				cancelable: true
20766			});
20767			this._map.fire(new MapMouseEvent("contextmenu", this._map, originalEvent));
20768		}, LONG_PRESS_DELAY);
20769	}
20770	/**
20771	* Cancels a pending long press once the finger has moved further than the map's
20772	* click tolerance, or once a second finger lands.
20773	* @param e - the touch event
20774	* @param points - the touch points, in map coordinates
20775	* @param mapTouches - the touches that are on the map
20776	*/
20777	touchmove(e, points, mapTouches) {
20778		if (!this._longPressTimer) return;
20779		if (mapTouches.length !== 1 || !this._longPressStart || points[0].dist(this._longPressStart) > this._clickTolerance) this._clearLongPress();
20780	}
20781	touchend() {
20782		this._touchActive = false;
20783		this._clearLongPress();
20784	}
20785	touchcancel() {
20786		this._touchActive = false;
20787		this._clearLongPress();
20788	}
20789	_clearLongPress() {
20790		if (this._longPressTimer) {
20791			clearTimeout(this._longPressTimer);
20792			this._longPressTimer = void 0;
20793		}
20794		this._longPressStart = void 0;
20795	}
20796	/**
20797	* Handles a contextmenu event. A native one during a touch is suppressed, because the
20798	* long press timer already fires the event and the browser menu would double up.
20799	* @param e - the mouse event
20800	*/
20801	contextmenu(e) {
20802		if (this._touchActive) {
20803			e.preventDefault();
20804			return;
20805		}
20806		if (this._delayContextMenu) this._contextMenuEvent = e;
20807		else if (!this._ignoreContextMenu) this._map.fire(new MapMouseEvent(e.type, this._map, e));
20808		if (this._map.listens("contextmenu")) e.preventDefault();
20809	}
20810	isEnabled() {
20811		return true;
20812	}
20813	isActive() {
20814		return false;
20815	}
20816	enable() {}
20817	disable() {}
20818};
20819//#endregion
20820//#region src/ui/handler/box_zoom.ts
20821/**
20822* The `BoxZoomHandler` allows the user to zoom the map to fit within a bounding box.
20823* The bounding box is defined by clicking and holding `shift` while dragging the cursor.
20824*
20825* @group Handlers
20826*/
20827var BoxZoomHandler = class {
20828	/** @internal */
20829	constructor(map, options, transformProvider) {
20830		this._map = map;
20831		this._tr = transformProvider;
20832		this._el = map.getCanvasContainer();
20833		this._container = map.getContainer();
20834		this._clickTolerance = options.clickTolerance || 1;
20835		if (options.boxZoom && typeof options.boxZoom === "object") this._boxZoomEnd = options.boxZoom.boxZoomEnd;
20836	}
20837	/**
20838	* Returns a Boolean indicating whether the "box zoom" interaction is enabled.
20839	*
20840	* @returns `true` if the "box zoom" interaction is enabled.
20841	*/
20842	isEnabled() {
20843		return !!this._enabled;
20844	}
20845	/**
20846	* Returns a Boolean indicating whether the "box zoom" interaction is active, i.e. currently being used.
20847	*
20848	* @returns `true` if the "box zoom" interaction is active.
20849	*/
20850	isActive() {
20851		return !!this._active;
20852	}
20853	/**
20854	* Enables the "box zoom" interaction.
20855	*
20856	* @example
20857	* ```ts
20858	* map.boxZoom.enable();
20859	* ```
20860	*/
20861	enable() {
20862		if (this.isEnabled()) return;
20863		this._enabled = true;
20864	}
20865	/**
20866	* Disables the "box zoom" interaction.
20867	*
20868	* @example
20869	* ```ts
20870	* map.boxZoom.disable();
20871	* ```
20872	*/
20873	disable() {
20874		if (!this.isEnabled()) return;
20875		this._enabled = false;
20876	}
20877	mousedown(e, point) {
20878		if (!this.isEnabled()) return;
20879		if (!(e.shiftKey && e.button === 0)) return;
20880		DOM.disableDrag();
20881		this._startPos = this._lastPos = point;
20882		this._active = true;
20883	}
20884	mousemoveWindow(e, point) {
20885		if (!this._active) return;
20886		const pos = point;
20887		if (this._lastPos.equals(pos) || !this._box && pos.dist(this._startPos) < this._clickTolerance) return;
20888		const p0 = this._startPos;
20889		this._lastPos = pos;
20890		if (!this._box) {
20891			this._box = DOM.create("div", "maplibregl-boxzoom", this._container);
20892			this._container.classList.add("maplibregl-crosshair");
20893			this._fireEvent("boxzoomstart", e);
20894		}
20895		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);
20896		this._box.style.transform = `translate(${minX}px,${minY}px)`;
20897		this._box.style.width = `${maxX - minX}px`;
20898		this._box.style.height = `${maxY - minY}px`;
20899	}
20900	mouseupWindow(e, point) {
20901		if (!this._active) return;
20902		if (e.button !== 0) return;
20903		const p0 = this._startPos, p1 = point;
20904		this.reset();
20905		DOM.suppressClick();
20906		if (p0.x === p1.x && p0.y === p1.y) this._fireEvent("boxzoomcancel", e);
20907		else {
20908			this._map.fire(new MapBoxZoomEvent("boxzoomend", { originalEvent: e }));
20909			if (this._boxZoomEnd) {
20910				this._boxZoomEnd(this._map, p0, p1, e);
20911				return;
20912			}
20913			return { cameraAnimation: (map) => map.fitScreenCoordinates(p0, p1, this._tr.bearing, { linear: true }) };
20914		}
20915	}
20916	keydown(e) {
20917		if (!this._active) return;
20918		if (e.keyCode === 27) {
20919			this.reset();
20920			this._fireEvent("boxzoomcancel", e);
20921		}
20922	}
20923	reset() {
20924		this._active = false;
20925		this._container.classList.remove("maplibregl-crosshair");
20926		if (this._box) {
20927			this._box.remove();
20928			this._box = null;
20929		}
20930		DOM.enableDrag();
20931		delete this._startPos;
20932		delete this._lastPos;
20933	}
20934	_fireEvent(type, e) {
20935		return this._map.fire(new MapBoxZoomEvent(type, { originalEvent: e }));
20936	}
20937};
20938//#endregion
20939//#region src/ui/handler/handler_util.ts
20940function indexTouches(touches, points) {
20941	if (touches.length !== points.length) throw new Error(`The number of touches and points are not equal - touches ${touches.length}, points ${points.length}`);
20942	const obj = {};
20943	for (let i = 0; i < touches.length; i++) obj[touches[i].identifier] = points[i];
20944	return obj;
20945}
20946//#endregion
20947//#region src/ui/handler/tap_recognizer.ts
20948function getCentroid(points) {
20949	const sum = new Point(0, 0);
20950	for (const point of points) sum._add(point);
20951	return sum.div(points.length);
20952}
20953const MAX_TOUCH_TIME = 500;
20954var SingleTapRecognizer = class {
20955	constructor(options) {
20956		this.reset();
20957		this.numTouches = options.numTouches;
20958	}
20959	reset() {
20960		delete this.centroid;
20961		delete this.startTime;
20962		delete this.touches;
20963		this.aborted = false;
20964	}
20965	touchstart(e, points, mapTouches) {
20966		if (this.centroid || mapTouches.length > this.numTouches) this.aborted = true;
20967		if (this.aborted) return;
20968		if (this.startTime === void 0) this.startTime = e.timeStamp;
20969		if (mapTouches.length === this.numTouches) {
20970			this.centroid = getCentroid(points);
20971			this.touches = indexTouches(mapTouches, points);
20972		}
20973	}
20974	touchmove(e, points, mapTouches) {
20975		if (this.aborted || !this.centroid) return;
20976		const newTouches = indexTouches(mapTouches, points);
20977		for (const id in this.touches) {
20978			const prevPos = this.touches[id];
20979			const pos = newTouches[id];
20980			if (!pos || pos.dist(prevPos) > 30) this.aborted = true;
20981		}
20982	}
20983	touchend(e, points, mapTouches) {
20984		if (!this.centroid || e.timeStamp - this.startTime > MAX_TOUCH_TIME) this.aborted = true;
20985		if (mapTouches.length === 0) {
20986			const centroid = !this.aborted && this.centroid;
20987			this.reset();
20988			if (centroid) return centroid;
20989		}
20990	}
20991};
20992var TapRecognizer = class {
20993	constructor(options) {
20994		this.singleTap = new SingleTapRecognizer(options);
20995		this.numTaps = options.numTaps;
20996		this.reset();
20997	}
20998	reset() {
20999		this.lastTime = Infinity;
21000		delete this.lastTap;
21001		this.count = 0;
21002		this.singleTap.reset();
21003	}
21004	touchstart(e, points, mapTouches) {
21005		this.singleTap.touchstart(e, points, mapTouches);
21006	}
21007	touchmove(e, points, mapTouches) {
21008		this.singleTap.touchmove(e, points, mapTouches);
21009	}
21010	touchend(e, points, mapTouches) {
21011		const tap = this.singleTap.touchend(e, points, mapTouches);
21012		if (tap) {
21013			const soonEnough = e.timeStamp - this.lastTime < 500;
21014			const closeEnough = !this.lastTap || this.lastTap.dist(tap) < 30;
21015			if (!soonEnough || !closeEnough) this.reset();
21016			this.count++;
21017			this.lastTime = e.timeStamp;
21018			this.lastTap = tap;
21019			if (this.count === this.numTaps) {
21020				this.reset();
21021				return tap;
21022			}
21023		}
21024	}
21025};
21026//#endregion
21027//#region src/ui/handler/tap_zoom.ts
21028/**
21029* A `TapZoomHandler` allows the user to zoom the map at a point by double tapping
21030*/
21031var TapZoomHandler = class {
21032	constructor(map, transformProvider) {
21033		this._tr = transformProvider;
21034		this._zoomIn = new TapRecognizer({
21035			numTouches: 1,
21036			numTaps: 2
21037		});
21038		this._zoomOut = new TapRecognizer({
21039			numTouches: 2,
21040			numTaps: 1
21041		});
21042		this.reset();
21043	}
21044	reset() {
21045		this._active = false;
21046		this._zoomIn.reset();
21047		this._zoomOut.reset();
21048	}
21049	touchstart(e, points, mapTouches) {
21050		this._zoomIn.touchstart(e, points, mapTouches);
21051		this._zoomOut.touchstart(e, points, mapTouches);
21052	}
21053	touchmove(e, points, mapTouches) {
21054		this._zoomIn.touchmove(e, points, mapTouches);
21055		this._zoomOut.touchmove(e, points, mapTouches);
21056	}
21057	touchend(e, points, mapTouches) {
21058		const zoomInPoint = this._zoomIn.touchend(e, points, mapTouches);
21059		const zoomOutPoint = this._zoomOut.touchend(e, points, mapTouches);
21060		const tr = this._tr;
21061		if (zoomInPoint) {
21062			this._active = true;
21063			e.preventDefault();
21064			setTimeout(() => this.reset(), 0);
21065			return { cameraAnimation: (map) => map.easeTo({
21066				duration: 300,
21067				zoom: evaluateZoomSnap(tr.zoom + 1, map.getZoomSnap()),
21068				around: tr.unproject(zoomInPoint)
21069			}, { originalEvent: e }) };
21070		} else if (zoomOutPoint) {
21071			this._active = true;
21072			e.preventDefault();
21073			setTimeout(() => this.reset(), 0);
21074			return { cameraAnimation: (map) => map.easeTo({
21075				duration: 300,
21076				zoom: evaluateZoomSnap(tr.zoom - 1, map.getZoomSnap()),
21077				around: tr.unproject(zoomOutPoint)
21078			}, { originalEvent: e }) };
21079		}
21080	}
21081	touchcancel() {
21082		this.reset();
21083	}
21084	enable() {
21085		this._enabled = true;
21086	}
21087	disable() {
21088		this._enabled = false;
21089		this.reset();
21090	}
21091	isEnabled() {
21092		return this._enabled;
21093	}
21094	isActive() {
21095		return this._active;
21096	}
21097};
21098//#endregion
21099//#region src/ui/handler/drag_handler.ts
21100/**
21101* A generic class to create handlers for drag events, from both mouse and touch events.
21102*/
21103var DragHandler = class {
21104	constructor(options) {
21105		this._enabled = !!options.enable;
21106		this._moveStateManager = options.moveStateManager;
21107		this._clickTolerance = options.clickTolerance || 1;
21108		this._moveFunction = options.move;
21109		this._activateOnStart = !!options.activateOnStart;
21110		options.assignEvents(this);
21111		this.reset();
21112	}
21113	reset(e) {
21114		this._active = false;
21115		this._moved = false;
21116		delete this._lastPoint;
21117		this._moveStateManager.endMove(e);
21118	}
21119	_move(...params) {
21120		const move = this._moveFunction(...params);
21121		if (move.bearingDelta || move.pitchDelta || move.rollDelta || move.around || move.panDelta) {
21122			this._active = true;
21123			return move;
21124		}
21125	}
21126	dragStart(e, point) {
21127		if (!this.isEnabled() || this._lastPoint) return;
21128		if (!this._moveStateManager.isValidStartEvent(e)) return;
21129		this._moveStateManager.startMove(e);
21130		this._lastPoint = Array.isArray(point) ? point[0] : point;
21131		if (this._activateOnStart && this._lastPoint) this._active = true;
21132	}
21133	dragMove(e, point) {
21134		if (!this.isEnabled()) return;
21135		const lastPoint = this._lastPoint;
21136		if (!lastPoint) return;
21137		e.preventDefault();
21138		if (!this._moveStateManager.isValidMoveEvent(e)) {
21139			this.reset(e);
21140			return;
21141		}
21142		const movePoint = Array.isArray(point) ? point[0] : point;
21143		if (!this._moved && movePoint.dist(lastPoint) < this._clickTolerance) return;
21144		this._moved = true;
21145		this._lastPoint = movePoint;
21146		return this._move(lastPoint, movePoint);
21147	}
21148	dragEnd(e) {
21149		if (!this.isEnabled() || !this._lastPoint) return;
21150		if (!this._moveStateManager.isValidEndEvent(e)) return;
21151		if (this._moved) DOM.suppressClick();
21152		this.reset(e);
21153	}
21154	enable() {
21155		this._enabled = true;
21156	}
21157	disable() {
21158		this._enabled = false;
21159		this.reset();
21160	}
21161	isEnabled() {
21162		return this._enabled;
21163	}
21164	isActive() {
21165		return this._active;
21166	}
21167	getClickTolerance() {
21168		return this._clickTolerance;
21169	}
21170};
21171//#endregion
21172//#region src/ui/handler/drag_move_state_manager.ts
21173const LEFT_BUTTON$1 = 0;
21174const RIGHT_BUTTON$1 = 2;
21175const BUTTONS_FLAGS = {
21176	[LEFT_BUTTON$1]: 1,
21177	[RIGHT_BUTTON$1]: 2
21178};
21179function buttonNoLongerPressed(e, button) {
21180	const flag = BUTTONS_FLAGS[button];
21181	return e.buttons === void 0 || (e.buttons & flag) !== flag;
21182}
21183var MouseMoveStateManager = class {
21184	constructor(options) {
21185		this._correctEvent = options.checkCorrectEvent;
21186	}
21187	startMove(e) {
21188		this._eventButton = e.button;
21189	}
21190	endMove(_e) {
21191		delete this._eventButton;
21192	}
21193	isValidStartEvent(e) {
21194		return this._correctEvent(e);
21195	}
21196	isValidMoveEvent(e) {
21197		return !buttonNoLongerPressed(e, this._eventButton);
21198	}
21199	isValidEndEvent(e) {
21200		return e.button === this._eventButton;
21201	}
21202};
21203var OneFingerTouchMoveStateManager = class {
21204	constructor() {
21205		this._firstTouch = void 0;
21206	}
21207	_isOneFingerTouch(e) {
21208		return e.targetTouches.length === 1;
21209	}
21210	_isSameTouchEvent(e) {
21211		return e.targetTouches[0].identifier === this._firstTouch;
21212	}
21213	startMove(e) {
21214		this._firstTouch = e.targetTouches[0].identifier;
21215	}
21216	endMove(_e) {
21217		delete this._firstTouch;
21218	}
21219	isValidStartEvent(e) {
21220		return this._isOneFingerTouch(e);
21221	}
21222	isValidMoveEvent(e) {
21223		return this._isOneFingerTouch(e) && this._isSameTouchEvent(e);
21224	}
21225	isValidEndEvent(e) {
21226		return this._isOneFingerTouch(e) && this._isSameTouchEvent(e);
21227	}
21228};
21229var MouseOrTouchMoveStateManager = class {
21230	constructor(mouseMoveStateManager = new MouseMoveStateManager({ checkCorrectEvent: () => true }), oneFingerTouchMoveStateManager = new OneFingerTouchMoveStateManager()) {
21231		this.mouseMoveStateManager = mouseMoveStateManager;
21232		this.oneFingerTouchMoveStateManager = oneFingerTouchMoveStateManager;
21233	}
21234	_executeRelevantHandler(e, onMouseEvent, onTouchEvent) {
21235		if (e instanceof MouseEvent) return onMouseEvent(e);
21236		if (typeof TouchEvent !== "undefined" && e instanceof TouchEvent) return onTouchEvent(e);
21237	}
21238	startMove(e) {
21239		this._executeRelevantHandler(e, (e) => {
21240			this.mouseMoveStateManager.startMove(e);
21241		}, (e) => {
21242			this.oneFingerTouchMoveStateManager.startMove(e);
21243		});
21244	}
21245	endMove(e) {
21246		this._executeRelevantHandler(e, (e) => {
21247			this.mouseMoveStateManager.endMove(e);
21248		}, (e) => {
21249			this.oneFingerTouchMoveStateManager.endMove(e);
21250		});
21251	}
21252	isValidStartEvent(e) {
21253		return !!this._executeRelevantHandler(e, (e) => this.mouseMoveStateManager.isValidStartEvent(e), (e) => this.oneFingerTouchMoveStateManager.isValidStartEvent(e));
21254	}
21255	isValidMoveEvent(e) {
21256		return !!this._executeRelevantHandler(e, (e) => this.mouseMoveStateManager.isValidMoveEvent(e), (e) => this.oneFingerTouchMoveStateManager.isValidMoveEvent(e));
21257	}
21258	isValidEndEvent(e) {
21259		return !!this._executeRelevantHandler(e, (e) => this.mouseMoveStateManager.isValidEndEvent(e), (e) => this.oneFingerTouchMoveStateManager.isValidEndEvent(e));
21260	}
21261};
21262//#endregion
21263//#region src/ui/handler/mouse.ts
21264const LEFT_BUTTON = 0;
21265const RIGHT_BUTTON = 2;
21266const assignEvents = (handler) => {
21267	handler.mousedown = handler.dragStart;
21268	handler.mousemoveWindow = handler.dragMove;
21269	handler.mouseup = handler.dragEnd;
21270	handler.contextmenu = (e) => {
21271		e.preventDefault();
21272	};
21273};
21274function generateMousePanHandler({ enable, clickTolerance }) {
21275	return new DragHandler({
21276		clickTolerance,
21277		move: (lastPoint, point) => ({
21278			around: point,
21279			panDelta: point.sub(lastPoint)
21280		}),
21281		activateOnStart: true,
21282		moveStateManager: new MouseMoveStateManager({ checkCorrectEvent: (e) => e.button === LEFT_BUTTON && !e.ctrlKey }),
21283		enable,
21284		assignEvents
21285	});
21286}
21287function generateMouseRotationHandler({ enable, clickTolerance, aroundCenter = true, minPixelCenterThreshold = 100, rotateSpeed = .8 }, getCenter) {
21288	return new DragHandler({
21289		clickTolerance,
21290		move: (lastPoint, currentPoint) => {
21291			const center = getCenter();
21292			if (aroundCenter && Math.abs(center.y - lastPoint.y) > minPixelCenterThreshold) return { bearingDelta: getAngleDelta(new Point(lastPoint.x, currentPoint.y), currentPoint, center) };
21293			let bearingDelta = (currentPoint.x - lastPoint.x) * rotateSpeed;
21294			if (aroundCenter && currentPoint.y < center.y) bearingDelta = -bearingDelta;
21295			return { bearingDelta };
21296		},
21297		moveStateManager: new MouseMoveStateManager({ checkCorrectEvent: (e) => e.button === LEFT_BUTTON && e.ctrlKey || e.button === RIGHT_BUTTON && !e.ctrlKey }),
21298		enable,
21299		assignEvents
21300	});
21301}
21302function generateMousePitchHandler({ enable, clickTolerance, pitchSpeed = -.5 }) {
21303	return new DragHandler({
21304		clickTolerance,
21305		move: (lastPoint, point) => ({ pitchDelta: (point.y - lastPoint.y) * pitchSpeed }),
21306		moveStateManager: new MouseMoveStateManager({ checkCorrectEvent: (e) => e.button === LEFT_BUTTON && e.ctrlKey || e.button === RIGHT_BUTTON }),
21307		enable,
21308		assignEvents
21309	});
21310}
21311function generateMouseRollHandler({ enable, clickTolerance, rollDegreesPerPixelMoved = .3 }, getCenter) {
21312	return new DragHandler({
21313		clickTolerance,
21314		move: (lastPoint, currentPoint) => {
21315			const center = getCenter();
21316			let rollDelta = (currentPoint.x - lastPoint.x) * rollDegreesPerPixelMoved;
21317			if (currentPoint.y < center.y) rollDelta = -rollDelta;
21318			return { rollDelta };
21319		},
21320		moveStateManager: new MouseMoveStateManager({ checkCorrectEvent: (e) => e.button === RIGHT_BUTTON && e.ctrlKey }),
21321		enable,
21322		assignEvents
21323	});
21324}
21325//#endregion
21326//#region src/ui/handler/touch_pan.ts
21327/**
21328* A `TouchPanHandler` allows the user to pan the map using touch gestures.
21329*/
21330var TouchPanHandler = class {
21331	constructor(options, map) {
21332		this._clickTolerance = options.clickTolerance || 1;
21333		this._map = map;
21334		this.reset();
21335	}
21336	reset() {
21337		this._active = false;
21338		this._touches = {};
21339		this._sum = new Point(0, 0);
21340	}
21341	_shouldBePrevented(touchesCount) {
21342		return touchesCount < (this._map.cooperativeGestures.isEnabled() ? 2 : 1);
21343	}
21344	touchstart(e, points, mapTouches) {
21345		return this._calculateTransform(e, points, mapTouches);
21346	}
21347	touchmove(e, points, mapTouches) {
21348		if (!this._active) return;
21349		if (this._shouldBePrevented(mapTouches.length)) {
21350			this._map.cooperativeGestures.notifyGestureBlocked("touch_pan", e);
21351			return;
21352		}
21353		e.preventDefault();
21354		return this._calculateTransform(e, points, mapTouches);
21355	}
21356	touchend(e, points, mapTouches) {
21357		this._calculateTransform(e, points, mapTouches);
21358		if (this._active && this._shouldBePrevented(mapTouches.length)) this.reset();
21359	}
21360	touchcancel() {
21361		this.reset();
21362	}
21363	_calculateTransform(e, points, mapTouches) {
21364		if (mapTouches.length > 0) this._active = true;
21365		const touches = indexTouches(mapTouches, points);
21366		const touchPointSum = new Point(0, 0);
21367		const touchDeltaSum = new Point(0, 0);
21368		let touchDeltaCount = 0;
21369		for (const identifier in touches) {
21370			const point = touches[identifier];
21371			const prevPoint = this._touches[identifier];
21372			if (prevPoint) {
21373				touchPointSum._add(point);
21374				touchDeltaSum._add(point.sub(prevPoint));
21375				touchDeltaCount++;
21376				touches[identifier] = point;
21377			}
21378		}
21379		this._touches = touches;
21380		if (this._shouldBePrevented(touchDeltaCount) || !touchDeltaSum.mag()) return;
21381		const panDelta = touchDeltaSum.div(touchDeltaCount);
21382		this._sum._add(panDelta);
21383		if (this._sum.mag() < this._clickTolerance) return;
21384		return {
21385			around: touchPointSum.div(touchDeltaCount),
21386			panDelta
21387		};
21388	}
21389	enable() {
21390		this._enabled = true;
21391	}
21392	disable() {
21393		this._enabled = false;
21394		this.reset();
21395	}
21396	isEnabled() {
21397		return this._enabled;
21398	}
21399	isActive() {
21400		return this._active;
21401	}
21402};
21403//#endregion
21404//#region src/ui/handler/two_fingers_touch.ts
21405/**
21406* The `TwoFingersTouchHandler`s allows the user to zoom, pitch and rotate the map using two fingers
21407*
21408*/
21409var TwoFingersTouchHandler = class {
21410	/** @internal */
21411	constructor() {
21412		this.reset();
21413	}
21414	reset() {
21415		this._active = false;
21416		delete this._firstTwoTouches;
21417	}
21418	touchstart(e, points, mapTouches) {
21419		if (this._firstTwoTouches || mapTouches.length < 2) return;
21420		this._firstTwoTouches = [mapTouches[0].identifier, mapTouches[1].identifier];
21421		this._start([points[0], points[1]]);
21422	}
21423	touchmove(e, points, mapTouches) {
21424		if (!this._firstTwoTouches) return;
21425		e.preventDefault();
21426		const [idA, idB] = this._firstTwoTouches;
21427		const a = getTouchById(mapTouches, points, idA);
21428		const b = getTouchById(mapTouches, points, idB);
21429		if (!a || !b) return;
21430		const pinchAround = this._aroundCenter ? null : a.add(b).div(2);
21431		return this._move([a, b], pinchAround, e);
21432	}
21433	touchend(e, points, mapTouches) {
21434		if (!this._firstTwoTouches) return;
21435		const [idA, idB] = this._firstTwoTouches;
21436		const a = getTouchById(mapTouches, points, idA);
21437		const b = getTouchById(mapTouches, points, idB);
21438		if (a && b) return;
21439		if (this._active) DOM.suppressClick();
21440		this.reset();
21441	}
21442	touchcancel() {
21443		this.reset();
21444	}
21445	/**
21446	* Enables the "drag to pitch" interaction.
21447	*
21448	* @example
21449	* ```ts
21450	* map.touchPitch.enable();
21451	* ```
21452	*/
21453	enable(options) {
21454		this._enabled = true;
21455		this._aroundCenter = !!options && options.around === "center";
21456	}
21457	/**
21458	* Disables the "drag to pitch" interaction.
21459	*
21460	* @example
21461	* ```ts
21462	* map.touchPitch.disable();
21463	* ```
21464	*/
21465	disable() {
21466		this._enabled = false;
21467		this.reset();
21468	}
21469	/**
21470	* Returns a Boolean indicating whether the "drag to pitch" interaction is enabled.
21471	*
21472	* @returns  `true` if the "drag to pitch" interaction is enabled.
21473	*/
21474	isEnabled() {
21475		return !!this._enabled;
21476	}
21477	/**
21478	* Returns a Boolean indicating whether the "drag to pitch" interaction is active, i.e. currently being used.
21479	*
21480	* @returns `true` if the "drag to pitch" interaction is active.
21481	*/
21482	isActive() {
21483		return !!this._active;
21484	}
21485};
21486function getTouchById(mapTouches, points, identifier) {
21487	for (let i = 0; i < mapTouches.length; i++) if (mapTouches[i].identifier === identifier) return points[i];
21488}
21489const defaultZoomRate$2 = 1;
21490const defaultZoomThreshold = .1;
21491function getZoomDelta(distance, lastDistance) {
21492	return Math.log(distance / lastDistance) / Math.LN2;
21493}
21494/**
21495* The `TwoFingersTouchHandler`s allows the user to zoom the map two fingers
21496*
21497* @group Handlers
21498*/
21499var TwoFingersTouchZoomHandler = class extends TwoFingersTouchHandler {
21500	constructor() {
21501		super();
21502		this._zoomRate = defaultZoomRate$2;
21503		this._zoomThreshold = defaultZoomThreshold;
21504	}
21505	/**
21506	* Sets the zoom rate of touch gestures.
21507	* @param zoomRate - 1 The rate used to scale touch movement to a zoom value. Set to `undefined` to restore the default.
21508	* @example
21509	* Slow down touch zoom
21510	* ```ts
21511	* map.touchZoomRotate.setZoomRate(0.5);
21512	* ```
21513	*/
21514	setZoomRate(zoomRate) {
21515		this._zoomRate = zoomRate ?? defaultZoomRate$2;
21516	}
21517	/**
21518	* Sets the threshold before a pinch gesture starts zooming.
21519	* @param zoomThreshold - 0.1 The minimum zoom delta before the pinch gesture becomes active. Set to `undefined` to restore the default.
21520	* @example
21521	* Make pinch zoom less sensitive
21522	* ```ts
21523	* map.touchZoomRotate.setZoomThreshold(0.3);
21524	* ```
21525	*/
21526	setZoomThreshold(zoomThreshold) {
21527		this._zoomThreshold = zoomThreshold ?? defaultZoomThreshold;
21528	}
21529	reset() {
21530		super.reset();
21531		delete this._distance;
21532		delete this._startDistance;
21533	}
21534	_start(points) {
21535		this._startDistance = this._distance = points[0].dist(points[1]);
21536	}
21537	_move(points, pinchAround) {
21538		const lastDistance = this._distance;
21539		this._distance = points[0].dist(points[1]);
21540		if (!this._active && Math.abs(getZoomDelta(this._distance, this._startDistance)) < this._zoomThreshold) return;
21541		this._active = true;
21542		return {
21543			zoomDelta: getZoomDelta(this._distance, lastDistance) * this._zoomRate,
21544			pinchAround
21545		};
21546	}
21547};
21548const ROTATION_THRESHOLD = 25;
21549function getBearingDelta(a, b) {
21550	return a.angleWith(b) * 180 / Math.PI;
21551}
21552/**
21553* The `TwoFingersTouchHandler`s allows the user to rotate the map two fingers
21554*
21555* @group Handlers
21556*/
21557var TwoFingersTouchRotateHandler = class extends TwoFingersTouchHandler {
21558	reset() {
21559		super.reset();
21560		delete this._minDiameter;
21561		delete this._startVector;
21562		delete this._vector;
21563	}
21564	_start(points) {
21565		this._startVector = this._vector = points[0].sub(points[1]);
21566		this._minDiameter = points[0].dist(points[1]);
21567	}
21568	_move(points, pinchAround, _e) {
21569		const lastVector = this._vector;
21570		this._vector = points[0].sub(points[1]);
21571		if (!this._active && this._isBelowThreshold(this._vector)) return;
21572		this._active = true;
21573		return {
21574			bearingDelta: getBearingDelta(this._vector, lastVector),
21575			pinchAround
21576		};
21577	}
21578	_isBelowThreshold(vector) {
21579		this._minDiameter = Math.min(this._minDiameter, vector.mag());
21580		const circumference = Math.PI * this._minDiameter;
21581		const threshold = ROTATION_THRESHOLD / circumference * 360;
21582		const bearingDeltaSinceStart = getBearingDelta(vector, this._startVector);
21583		return Math.abs(bearingDeltaSinceStart) < threshold;
21584	}
21585};
21586function isVertical(vector) {
21587	return Math.abs(vector.y) > Math.abs(vector.x);
21588}
21589const ALLOWED_SINGLE_TOUCH_TIME = 100;
21590/**
21591* The `TwoFingersTouchPitchHandler` allows the user to pitch the map by dragging up and down with two fingers.
21592*
21593* @group Handlers
21594*/
21595var TwoFingersTouchPitchHandler = class extends TwoFingersTouchHandler {
21596	constructor(map) {
21597		super();
21598		this._currentTouchCount = 0;
21599		this._map = map;
21600	}
21601	reset() {
21602		super.reset();
21603		this._valid = void 0;
21604		delete this._firstMove;
21605		delete this._lastPoints;
21606	}
21607	touchstart(e, points, mapTouches) {
21608		super.touchstart(e, points, mapTouches);
21609		this._currentTouchCount = mapTouches.length;
21610	}
21611	_start(points) {
21612		this._lastPoints = points;
21613		if (isVertical(points[0].sub(points[1]))) this._valid = false;
21614	}
21615	_move(points, center, e) {
21616		if (this._map.cooperativeGestures.isEnabled() && this._currentTouchCount < 3) return;
21617		const vectorA = points[0].sub(this._lastPoints[0]);
21618		const vectorB = points[1].sub(this._lastPoints[1]);
21619		this._valid = this.gestureBeginsVertically(vectorA, vectorB, e.timeStamp);
21620		if (!this._valid) return;
21621		this._lastPoints = points;
21622		this._active = true;
21623		return { pitchDelta: (vectorA.y + vectorB.y) / 2 * -.5 };
21624	}
21625	gestureBeginsVertically(vectorA, vectorB, timeStamp) {
21626		if (this._valid !== void 0) return this._valid;
21627		const threshold = 2;
21628		const movedA = vectorA.mag() >= threshold;
21629		const movedB = vectorB.mag() >= threshold;
21630		if (!movedA && !movedB) return;
21631		if (!movedA || !movedB) {
21632			if (this._firstMove === void 0) this._firstMove = timeStamp;
21633			if (timeStamp - this._firstMove < ALLOWED_SINGLE_TOUCH_TIME) return;
21634			else return false;
21635		}
21636		const isSameDirection = vectorA.y > 0 === vectorB.y > 0;
21637		return isVertical(vectorA) && isVertical(vectorB) && isSameDirection;
21638	}
21639};
21640//#endregion
21641//#region src/ui/handler/keyboard.ts
21642const defaultOptions$5 = {
21643	panStep: 100,
21644	bearingStep: 15,
21645	pitchStep: 10
21646};
21647/**
21648* The `KeyboardHandler` allows the user to zoom, rotate, and pan the map using
21649* the following keyboard shortcuts:
21650*
21651* - `=` / `+`: Increase the zoom level by 1.
21652* - `Shift-=` / `Shift-+`: Increase the zoom level by 2.
21653* - `-`: Decrease the zoom level by 1.
21654* - `Shift--`: Decrease the zoom level by 2.
21655* - Arrow keys: Pan by 100 pixels.
21656* - `Shift+⇢`: Increase the rotation by 15 degrees.
21657* - `Shift+⇠`: Decrease the rotation by 15 degrees.
21658* - `Shift+⇡`: Increase the pitch by 10 degrees.
21659* - `Shift+⇣`: Decrease the pitch by 10 degrees.
21660*
21661* @group Handlers
21662*/
21663var KeyboardHandler = class {
21664	/** @internal */
21665	constructor(map, transformProvider) {
21666		this._tr = transformProvider;
21667		const stepOptions = defaultOptions$5;
21668		this._panStep = stepOptions.panStep;
21669		this._bearingStep = stepOptions.bearingStep;
21670		this._pitchStep = stepOptions.pitchStep;
21671		this._rotationDisabled = false;
21672	}
21673	reset() {
21674		this._active = false;
21675	}
21676	keydown(e) {
21677		if (e.altKey || e.ctrlKey || e.metaKey) return;
21678		let zoomDir = 0;
21679		let bearingDir = 0;
21680		let pitchDir = 0;
21681		let xDir = 0;
21682		let yDir = 0;
21683		switch (e.keyCode) {
21684			case 61:
21685			case 107:
21686			case 171:
21687			case 187:
21688				zoomDir = 1;
21689				break;
21690			case 189:
21691			case 109:
21692			case 173:
21693				zoomDir = -1;
21694				break;
21695			case 37:
21696				if (e.shiftKey) bearingDir = -1;
21697				else {
21698					e.preventDefault();
21699					xDir = -1;
21700				}
21701				break;
21702			case 39:
21703				if (e.shiftKey) bearingDir = 1;
21704				else {
21705					e.preventDefault();
21706					xDir = 1;
21707				}
21708				break;
21709			case 38:
21710				if (e.shiftKey) pitchDir = 1;
21711				else {
21712					e.preventDefault();
21713					yDir = -1;
21714				}
21715				break;
21716			case 40:
21717				if (e.shiftKey) pitchDir = -1;
21718				else {
21719					e.preventDefault();
21720					yDir = 1;
21721				}
21722				break;
21723			default: return;
21724		}
21725		if (this._rotationDisabled) {
21726			bearingDir = 0;
21727			pitchDir = 0;
21728		}
21729		return { cameraAnimation: (map) => {
21730			const tr = this._tr;
21731			map.easeTo({
21732				duration: 300,
21733				easeId: "keyboardHandler",
21734				easing: easeOut,
21735				zoom: zoomDir ? evaluateZoomSnap(tr.zoom + zoomDir * (e.shiftKey ? 2 : 1), map.getZoomSnap()) : tr.zoom,
21736				bearing: tr.bearing + bearingDir * this._bearingStep,
21737				pitch: tr.pitch + pitchDir * this._pitchStep,
21738				offset: [-xDir * this._panStep, -yDir * this._panStep],
21739				center: tr.center
21740			}, { originalEvent: e });
21741		} };
21742	}
21743	/**
21744	* Enables the "keyboard rotate and zoom" interaction.
21745	*
21746	* @example
21747	* ```ts
21748	* map.keyboard.enable();
21749	* ```
21750	*/
21751	enable() {
21752		this._enabled = true;
21753	}
21754	/**
21755	* Disables the "keyboard rotate and zoom" interaction.
21756	*
21757	* @example
21758	* ```ts
21759	* map.keyboard.disable();
21760	* ```
21761	*/
21762	disable() {
21763		this._enabled = false;
21764		this.reset();
21765	}
21766	/**
21767	* Returns a Boolean indicating whether the "keyboard rotate and zoom"
21768	* interaction is enabled.
21769	*
21770	* @returns `true` if the "keyboard rotate and zoom"
21771	* interaction is enabled.
21772	*/
21773	isEnabled() {
21774		return this._enabled;
21775	}
21776	/**
21777	* Returns true if the handler is enabled and has detected the start of a
21778	* zoom/rotate gesture.
21779	*
21780	* @returns `true` if the handler is enabled and has detected the
21781	* start of a zoom/rotate gesture.
21782	*/
21783	isActive() {
21784		return this._active;
21785	}
21786	/**
21787	* Disables the "keyboard pan/rotate" interaction, leaving the
21788	* "keyboard zoom" interaction enabled.
21789	*
21790	* @example
21791	* ```ts
21792	* map.keyboard.disableRotation();
21793	* ```
21794	*/
21795	disableRotation() {
21796		this._rotationDisabled = true;
21797	}
21798	/**
21799	* Enables the "keyboard pan/rotate" interaction.
21800	*
21801	* @example
21802	* ```ts
21803	* map.keyboard.enable();
21804	* map.keyboard.enableRotation();
21805	* ```
21806	*/
21807	enableRotation() {
21808		this._rotationDisabled = false;
21809	}
21810};
21811function easeOut(t) {
21812	return t * (2 - t);
21813}
21814//#endregion
21815//#region src/ui/handler/scroll_zoom.ts
21816const wheelZoomDelta = 4.000244140625;
21817const defaultZoomRate$1 = 1 / 100;
21818const wheelZoomRate = 1 / 450;
21819const maxScalePerFrame = 2;
21820const wheelEventTimeDiffAdjustment = 5;
21821/**
21822* The `ScrollZoomHandler` allows the user to zoom the map by scrolling.
21823*
21824* @group Handlers
21825*/
21826var ScrollZoomHandler = class {
21827	/** @internal */
21828	constructor(map, triggerRenderFrame, transformProvider) {
21829		this._onTimeout = (initialEvent) => {
21830			this._type = "wheel";
21831			this._delta -= this._lastValue;
21832			if (!this._active) this._start(initialEvent);
21833		};
21834		this._map = map;
21835		this._tr = transformProvider;
21836		this._triggerRenderFrame = triggerRenderFrame;
21837		this._delta = 0;
21838		this._defaultZoomRate = defaultZoomRate$1;
21839		this._wheelZoomRate = wheelZoomRate;
21840	}
21841	/**
21842	* Set the zoom rate of a trackpad
21843	* @param zoomRate - 1/100 The rate used to scale trackpad movement to a zoom value.
21844	* @example
21845	* Speed up trackpad zoom
21846	* ```ts
21847	* map.scrollZoom.setZoomRate(1/25);
21848	* ```
21849	*/
21850	setZoomRate(zoomRate) {
21851		this._defaultZoomRate = zoomRate;
21852	}
21853	/**
21854	* Set the zoom rate of a mouse wheel
21855	* @param wheelZoomRate - 1/450 The rate used to scale mouse wheel movement to a zoom value.
21856	* @example
21857	* Slow down zoom of mouse wheel
21858	* ```ts
21859	* map.scrollZoom.setWheelZoomRate(1/600);
21860	* ```
21861	*/
21862	setWheelZoomRate(wheelZoomRate) {
21863		this._wheelZoomRate = wheelZoomRate;
21864	}
21865	/**
21866	* Returns a Boolean indicating whether the "scroll to zoom" interaction is enabled.
21867	* @returns `true` if the "scroll to zoom" interaction is enabled.
21868	*/
21869	isEnabled() {
21870		return !!this._enabled;
21871	}
21872	isActive() {
21873		return !!this._active || this._finishTimeout !== void 0;
21874	}
21875	isZooming() {
21876		return !!this._zooming;
21877	}
21878	/**
21879	* Enables the "scroll to zoom" interaction.
21880	*
21881	* @param options - Options object.
21882	* @example
21883	* ```ts
21884	* map.scrollZoom.enable();
21885	* map.scrollZoom.enable({ around: 'center' })
21886	* ```
21887	*/
21888	enable(options) {
21889		if (this.isEnabled()) return;
21890		this._enabled = true;
21891		this._aroundCenter = !!options && options.around === "center";
21892	}
21893	/**
21894	* Disables the "scroll to zoom" interaction.
21895	*
21896	* @example
21897	* ```ts
21898	* map.scrollZoom.disable();
21899	* ```
21900	*/
21901	disable() {
21902		if (!this.isEnabled()) return;
21903		this._enabled = false;
21904	}
21905	/**
21906	* Determines whether or not the gesture is blocked due to cooperativeGestures.
21907	*/
21908	_shouldBePrevented(e) {
21909		if (!this._map.cooperativeGestures.isEnabled()) return false;
21910		return !(e.ctrlKey || this._map.cooperativeGestures.isBypassed(e));
21911	}
21912	wheel(e) {
21913		if (!this.isEnabled()) return;
21914		if (this._shouldBePrevented(e)) {
21915			this._map.cooperativeGestures.notifyGestureBlocked("wheel_zoom", e);
21916			return;
21917		}
21918		let value = e.deltaMode === WheelEvent.DOM_DELTA_LINE ? e.deltaY * 40 : e.deltaY;
21919		const currentTime = now(), timeDelta = currentTime - (this._lastWheelEventTime || 0);
21920		this._lastWheelEventTime = currentTime;
21921		if (value !== 0 && value % wheelZoomDelta === 0) this._type = "wheel";
21922		else if (value !== 0 && Math.abs(value) < 4) this._type = "trackpad";
21923		else if (timeDelta > 400) {
21924			this._type = null;
21925			this._lastValue = value;
21926			this._timeout = setTimeout(this._onTimeout, 40, e);
21927		} else if (!this._type) {
21928			this._type = Math.abs(timeDelta * value) < 200 ? "trackpad" : "wheel";
21929			if (this._timeout) {
21930				clearTimeout(this._timeout);
21931				this._timeout = null;
21932				value += this._lastValue;
21933			}
21934		}
21935		if (e.shiftKey && value) value = value / 4;
21936		if (this._type) {
21937			this._lastWheelEvent = e;
21938			this._delta -= value;
21939			if (!this._active) this._start(e);
21940		}
21941		e.preventDefault();
21942	}
21943	_start(e) {
21944		if (!this._delta) return;
21945		this._needsRerender = false;
21946		this._active = true;
21947		if (!this.isZooming()) this._zooming = true;
21948		if (this._finishTimeout) {
21949			clearTimeout(this._finishTimeout);
21950			delete this._finishTimeout;
21951		}
21952		const pos = DOM.mousePos(this._map.getCanvas(), e);
21953		const tr = this._tr;
21954		if (this._aroundCenter) this._aroundPoint = tr.transform.locationToScreenPoint(LngLat.convert(tr.center));
21955		else this._aroundPoint = pos;
21956		if (!this._needsRerender) {
21957			this._needsRerender = true;
21958			this._triggerRenderFrame();
21959		}
21960	}
21961	renderFrame() {
21962		if (!this._needsRerender) return;
21963		this._needsRerender = false;
21964		if (!this.isActive()) return;
21965		const tr = this._tr.transform;
21966		if (typeof this._lastExpectedZoom === "number") {
21967			const externalZoomChange = tr.zoom - this._lastExpectedZoom;
21968			if (typeof this._startZoom === "number") this._startZoom += externalZoomChange;
21969			if (typeof this._targetZoom === "number") this._targetZoom += externalZoomChange;
21970		}
21971		if (this._delta !== 0) {
21972			const zoomRate = this._type === "wheel" && Math.abs(this._delta) > wheelZoomDelta ? this._wheelZoomRate : this._defaultZoomRate;
21973			let scale = maxScalePerFrame / (1 + Math.exp(-Math.abs(this._delta * zoomRate)));
21974			if (this._delta < 0 && scale !== 0) scale = 1 / scale;
21975			const fromScale = typeof this._targetZoom !== "number" ? tr.scale : zoomScale(this._targetZoom);
21976			const target = tr.applyConstrain(tr.getCameraLngLat(), scaleZoom(fromScale * scale)).zoom;
21977			const zoomSnap = this._map.getZoomSnap();
21978			if (this._type === "wheel" && zoomSnap > 0) {
21979				const currentSnapped = evaluateZoomSnap(tr.zoom, zoomSnap);
21980				this._targetZoom = evaluateZoomSnap(target, zoomSnap, target - currentSnapped);
21981			} else this._targetZoom = target;
21982			if (this._type === "wheel") {
21983				this._startZoom = tr.zoom;
21984				this._easing = this._smoothOutEasing(200);
21985			}
21986			this._delta = 0;
21987		}
21988		const targetZoom = typeof this._targetZoom !== "number" ? tr.zoom : this._targetZoom;
21989		const startZoom = this._startZoom;
21990		const easing = this._easing;
21991		let finished = false;
21992		let zoom;
21993		if (this._type === "wheel" && startZoom && easing) {
21994			const lastWheelEventTimeDiff = now() - this._lastWheelEventTime;
21995			const t = Math.min((lastWheelEventTimeDiff + wheelEventTimeDiffAdjustment) / 200, 1);
21996			const k = easing(t);
21997			zoom = interpolateFactory.number(startZoom, targetZoom, k);
21998			if (t < 1) this._needsRerender = true;
21999			else finished = true;
22000		} else {
22001			zoom = targetZoom;
22002			finished = true;
22003		}
22004		this._active = true;
22005		if (finished) {
22006			this._active = false;
22007			this._finishTimeout = setTimeout(() => {
22008				this._zooming = false;
22009				this._triggerRenderFrame();
22010				delete this._targetZoom;
22011				delete this._lastExpectedZoom;
22012				delete this._finishTimeout;
22013			}, 200);
22014		}
22015		this._lastExpectedZoom = zoom;
22016		return {
22017			noInertia: true,
22018			needsRenderFrame: !finished,
22019			zoomDelta: zoom - tr.zoom,
22020			around: this._aroundPoint,
22021			originalEvent: this._lastWheelEvent
22022		};
22023	}
22024	_smoothOutEasing(duration) {
22025		let easing = defaultEasing;
22026		if (this._prevEase) {
22027			const currentEase = this._prevEase;
22028			const t = (now() - currentEase.start) / currentEase.duration;
22029			const speed = currentEase.easing(t + .01) - currentEase.easing(t);
22030			const x = .27 / Math.sqrt(speed * speed + 1e-4) * .01;
22031			const y = Math.sqrt(.0729 - x * x);
22032			easing = bezier(x, y, .25, 1);
22033		}
22034		this._prevEase = {
22035			start: now(),
22036			duration,
22037			easing
22038		};
22039		return easing;
22040	}
22041	reset() {
22042		this._active = false;
22043		this._zooming = false;
22044		delete this._targetZoom;
22045		delete this._lastExpectedZoom;
22046		if (this._finishTimeout) {
22047			clearTimeout(this._finishTimeout);
22048			delete this._finishTimeout;
22049		}
22050	}
22051};
22052//#endregion
22053//#region src/ui/handler/shim/dblclick_zoom.ts
22054/**
22055* The `DoubleClickZoomHandler` allows the user to zoom the map at a point by
22056* double clicking or double tapping.
22057*
22058* @group Handlers
22059*/
22060var DoubleClickZoomHandler = class {
22061	/** @internal */
22062	constructor(clickZoom, TapZoom) {
22063		this._clickZoom = clickZoom;
22064		this._tapZoom = TapZoom;
22065	}
22066	/**
22067	* Enables the "double click to zoom" interaction.
22068	*
22069	* @example
22070	* ```ts
22071	* map.doubleClickZoom.enable();
22072	* ```
22073	*/
22074	enable() {
22075		this._clickZoom.enable();
22076		this._tapZoom.enable();
22077	}
22078	/**
22079	* Disables the "double click to zoom" interaction.
22080	*
22081	* @example
22082	* ```ts
22083	* map.doubleClickZoom.disable();
22084	* ```
22085	*/
22086	disable() {
22087		this._clickZoom.disable();
22088		this._tapZoom.disable();
22089	}
22090	/**
22091	* Returns a Boolean indicating whether the "double click to zoom" interaction is enabled.
22092	*
22093	* @returns `true` if the "double click to zoom" interaction is enabled.
22094	*/
22095	isEnabled() {
22096		return this._clickZoom.isEnabled() && this._tapZoom.isEnabled();
22097	}
22098	/**
22099	* Returns a Boolean indicating whether the "double click to zoom" interaction is active, i.e. currently being used.
22100	*
22101	* @returns `true` if the "double click to zoom" interaction is active.
22102	*/
22103	isActive() {
22104		return this._clickZoom.isActive() || this._tapZoom.isActive();
22105	}
22106};
22107//#endregion
22108//#region src/ui/handler/click_zoom.ts
22109/**
22110* The `ClickZoomHandler` allows the user to zoom the map at a point by double clicking
22111* It is used by other handlers
22112*/
22113var ClickZoomHandler = class {
22114	/** @internal */
22115	constructor(map, transformProvider) {
22116		this._tr = transformProvider;
22117		this.reset();
22118	}
22119	reset() {
22120		this._active = false;
22121	}
22122	dblclick(e, point) {
22123		e.preventDefault();
22124		return { cameraAnimation: (map) => {
22125			map.easeTo({
22126				duration: 300,
22127				zoom: evaluateZoomSnap(this._tr.zoom + (e.shiftKey ? -1 : 1), map.getZoomSnap()),
22128				around: this._tr.unproject(point)
22129			}, { originalEvent: e });
22130		} };
22131	}
22132	enable() {
22133		this._enabled = true;
22134	}
22135	disable() {
22136		this._enabled = false;
22137		this.reset();
22138	}
22139	isEnabled() {
22140		return this._enabled;
22141	}
22142	isActive() {
22143		return this._active;
22144	}
22145};
22146//#endregion
22147//#region src/ui/handler/tap_drag_zoom.ts
22148const defaultZoomRate = 1;
22149/**
22150* 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.
22151*/
22152var TapDragZoomHandler = class {
22153	constructor() {
22154		this._tap = new TapRecognizer({
22155			numTouches: 1,
22156			numTaps: 1
22157		});
22158		this._zoomRate = defaultZoomRate;
22159		this.reset();
22160	}
22161	setZoomRate(zoomRate) {
22162		this._zoomRate = zoomRate ?? defaultZoomRate;
22163	}
22164	reset() {
22165		this._active = false;
22166		delete this._swipePoint;
22167		delete this._swipeTouch;
22168		delete this._tapTime;
22169		delete this._tapPoint;
22170		this._tap.reset();
22171	}
22172	touchstart(e, points, mapTouches) {
22173		if (this._swipePoint) return;
22174		if (!this._tapTime) this._tap.touchstart(e, points, mapTouches);
22175		else {
22176			const swipePoint = points[0];
22177			const soonEnough = e.timeStamp - this._tapTime < 500;
22178			const closeEnough = this._tapPoint.dist(swipePoint) < 30;
22179			if (!soonEnough || !closeEnough) this.reset();
22180			else if (mapTouches.length > 0) {
22181				this._swipePoint = swipePoint;
22182				this._swipeTouch = mapTouches[0].identifier;
22183			}
22184		}
22185	}
22186	touchmove(e, points, mapTouches) {
22187		if (!this._tapTime) this._tap.touchmove(e, points, mapTouches);
22188		else if (this._swipePoint) {
22189			if (mapTouches[0].identifier !== this._swipeTouch) return;
22190			const newSwipePoint = points[0];
22191			const dist = newSwipePoint.y - this._swipePoint.y;
22192			this._swipePoint = newSwipePoint;
22193			e.preventDefault();
22194			this._active = true;
22195			return { zoomDelta: dist / 128 * this._zoomRate };
22196		}
22197	}
22198	touchend(e, points, mapTouches) {
22199		if (!this._tapTime) {
22200			const point = this._tap.touchend(e, points, mapTouches);
22201			if (point) {
22202				this._tapTime = e.timeStamp;
22203				this._tapPoint = point;
22204			}
22205		} else if (this._swipePoint) {
22206			if (mapTouches.length === 0) this.reset();
22207		}
22208	}
22209	touchcancel() {
22210		this.reset();
22211	}
22212	enable() {
22213		this._enabled = true;
22214	}
22215	disable() {
22216		this._enabled = false;
22217		this.reset();
22218	}
22219	isEnabled() {
22220		return this._enabled;
22221	}
22222	isActive() {
22223		return this._active;
22224	}
22225};
22226//#endregion
22227//#region src/ui/handler/shim/drag_pan.ts
22228/**
22229* The `DragPanHandler` allows the user to pan the map by clicking and dragging
22230* the cursor.
22231*
22232* @group Handlers
22233*/
22234var DragPanHandler = class {
22235	/** @internal */
22236	constructor(el, mousePan, touchPan) {
22237		this._el = el;
22238		this._mousePan = mousePan;
22239		this._touchPan = touchPan;
22240	}
22241	/**
22242	* Enables the "drag to pan" interaction.
22243	*
22244	* @param options - Options object
22245	* @example
22246	* ```ts
22247	*   map.dragPan.enable();
22248	*   map.dragPan.enable({
22249	*      linearity: 0.3,
22250	*      easing: bezier(0, 0, 0.3, 1),
22251	*      maxSpeed: 1400,
22252	*      deceleration: 2500,
22253	*   });
22254	* ```
22255	*/
22256	enable(options) {
22257		this._inertiaOptions = options || {};
22258		this._mousePan.enable();
22259		this._touchPan.enable();
22260		this._el.classList.add("maplibregl-touch-drag-pan");
22261	}
22262	/**
22263	* Disables the "drag to pan" interaction.
22264	*
22265	* @example
22266	* ```ts
22267	* map.dragPan.disable();
22268	* ```
22269	*/
22270	disable() {
22271		this._mousePan.disable();
22272		this._touchPan.disable();
22273		this._el.classList.remove("maplibregl-touch-drag-pan");
22274	}
22275	/**
22276	* Returns a Boolean indicating whether the "drag to pan" interaction is enabled.
22277	*
22278	* @returns `true` if the "drag to pan" interaction is enabled.
22279	*/
22280	isEnabled() {
22281		return this._mousePan.isEnabled() && this._touchPan.isEnabled();
22282	}
22283	/**
22284	* Returns a Boolean indicating whether the "drag to pan" interaction is active, i.e. currently being used.
22285	*
22286	* @returns `true` if the "drag to pan" interaction is active.
22287	*/
22288	isActive() {
22289		return this._mousePan.isActive() || this._touchPan.isActive();
22290	}
22291};
22292//#endregion
22293//#region src/ui/handler/shim/drag_rotate.ts
22294/**
22295* The `DragRotateHandler` allows the user to rotate the map by clicking and
22296* dragging the cursor while holding the right mouse button or `ctrl` key.
22297*
22298* @group Handlers
22299*/
22300var DragRotateHandler = class {
22301	/** @internal */
22302	constructor(options, mouseRotate, mousePitch, mouseRoll) {
22303		this._pitchWithRotate = options.pitchWithRotate;
22304		this._rollEnabled = options.rollEnabled;
22305		this._mouseRotate = mouseRotate;
22306		this._mousePitch = mousePitch;
22307		this._mouseRoll = mouseRoll;
22308	}
22309	/**
22310	* Enables the "drag to rotate" interaction.
22311	*
22312	* @example
22313	* ```ts
22314	* map.dragRotate.enable();
22315	* ```
22316	*/
22317	enable() {
22318		this._mouseRotate.enable();
22319		if (this._pitchWithRotate) this._mousePitch.enable();
22320		if (this._rollEnabled) this._mouseRoll.enable();
22321	}
22322	/**
22323	* Disables the "drag to rotate" interaction.
22324	*
22325	* @example
22326	* ```ts
22327	* map.dragRotate.disable();
22328	* ```
22329	*/
22330	disable() {
22331		this._mouseRotate.disable();
22332		this._mousePitch.disable();
22333		this._mouseRoll.disable();
22334	}
22335	/**
22336	* Returns a Boolean indicating whether the "drag to rotate" interaction is enabled.
22337	*
22338	* @returns `true` if the "drag to rotate" interaction is enabled.
22339	*/
22340	isEnabled() {
22341		return this._mouseRotate.isEnabled() && (!this._pitchWithRotate || this._mousePitch.isEnabled()) && (!this._rollEnabled || this._mouseRoll.isEnabled());
22342	}
22343	/**
22344	* Returns a Boolean indicating whether the "drag to rotate" interaction is active, i.e. currently being used.
22345	*
22346	* @returns `true` if the "drag to rotate" interaction is active.
22347	*/
22348	isActive() {
22349		return this._mouseRotate.isActive() || this._mousePitch.isActive() || this._mouseRoll.isActive();
22350	}
22351};
22352//#endregion
22353//#region src/ui/handler/shim/two_fingers_touch.ts
22354/**
22355* The `TwoFingersTouchZoomRotateHandler` allows the user to zoom and rotate the map by
22356* pinching on a touchscreen.
22357*
22358* They can zoom with one finger by double tapping and dragging. On the second tap,
22359* hold the finger down and drag up or down to zoom in or out.
22360*
22361* @group Handlers
22362*/
22363var TwoFingersTouchZoomRotateHandler = class {
22364	/** @internal */
22365	constructor(el, touchZoom, touchRotate, tapDragZoom) {
22366		this._el = el;
22367		this._touchZoom = touchZoom;
22368		this._touchRotate = touchRotate;
22369		this._tapDragZoom = tapDragZoom;
22370		this._rotationDisabled = false;
22371		this._enabled = true;
22372	}
22373	/**
22374	* Enables the "pinch to rotate and zoom" interaction.
22375	*
22376	* @param options - Options object.
22377	*
22378	* @example
22379	* ```ts
22380	* map.touchZoomRotate.enable();
22381	* map.touchZoomRotate.enable({ around: 'center' });
22382	* ```
22383	*/
22384	enable(options) {
22385		this._touchZoom.enable(options);
22386		if (!this._rotationDisabled) this._touchRotate.enable(options);
22387		this._tapDragZoom.enable();
22388		this._el.classList.add("maplibregl-touch-zoom-rotate");
22389	}
22390	/**
22391	* Disables the "pinch to rotate and zoom" interaction.
22392	*
22393	* @example
22394	* ```ts
22395	* map.touchZoomRotate.disable();
22396	* ```
22397	*/
22398	disable() {
22399		this._touchZoom.disable();
22400		this._touchRotate.disable();
22401		this._tapDragZoom.disable();
22402		this._el.classList.remove("maplibregl-touch-zoom-rotate");
22403	}
22404	/**
22405	* Returns a Boolean indicating whether the "pinch to rotate and zoom" interaction is enabled.
22406	*
22407	* @returns `true` if the "pinch to rotate and zoom" interaction is enabled.
22408	*/
22409	isEnabled() {
22410		return this._touchZoom.isEnabled() && (this._rotationDisabled || this._touchRotate.isEnabled()) && this._tapDragZoom.isEnabled();
22411	}
22412	/**
22413	* Returns true if the handler is enabled and has detected the start of a zoom/rotate gesture.
22414	*
22415	* @returns `true` if the handler is active, `false` otherwise
22416	*/
22417	isActive() {
22418		return this._touchZoom.isActive() || this._touchRotate.isActive() || this._tapDragZoom.isActive();
22419	}
22420	/**
22421	* Sets the zoom rate of touch gestures.
22422	* @param zoomRate - 1 The rate used to scale touch movement to a zoom value. Set to `undefined` to restore the default.
22423	* @example
22424	* Slow down touch zoom
22425	* ```ts
22426	* map.touchZoomRotate.setZoomRate(0.5);
22427	* ```
22428	*/
22429	setZoomRate(zoomRate) {
22430		this._touchZoom.setZoomRate(zoomRate);
22431		this._tapDragZoom.setZoomRate(zoomRate);
22432	}
22433	/**
22434	* Sets the threshold before a pinch gesture starts zooming.
22435	* @param zoomThreshold - 0.1 The minimum zoom delta before the pinch gesture becomes active. Set to `undefined` to restore the default.
22436	* @example
22437	* Make pinch zoom less sensitive
22438	* ```ts
22439	* map.touchZoomRotate.setZoomThreshold(0.3);
22440	* ```
22441	*/
22442	setZoomThreshold(zoomThreshold) {
22443		this._touchZoom.setZoomThreshold(zoomThreshold);
22444	}
22445	/**
22446	* Disables the "pinch to rotate" interaction, leaving the "pinch to zoom"
22447	* interaction enabled.
22448	*
22449	* @example
22450	* ```ts
22451	* map.touchZoomRotate.disableRotation();
22452	* ```
22453	*/
22454	disableRotation() {
22455		this._rotationDisabled = true;
22456		this._touchRotate.disable();
22457	}
22458	/**
22459	* Enables the "pinch to rotate" interaction.
22460	*
22461	* @example
22462	* ```ts
22463	* map.touchZoomRotate.enable();
22464	* map.touchZoomRotate.enableRotation();
22465	* ```
22466	*/
22467	enableRotation() {
22468		this._rotationDisabled = false;
22469		if (this._touchZoom.isEnabled()) this._touchRotate.enable();
22470	}
22471};
22472//#endregion
22473//#region src/ui/handler/cooperative_gestures.ts
22474/**
22475* A `CooperativeGestureHandler` is a control that adds cooperative gesture info when user tries to zoom in/out.
22476*
22477* When the CooperativeGestureHandler blocks a gesture, it will emit a `cooperativegestureprevented` event.
22478*
22479* @group Handlers
22480*
22481* @example
22482* ```ts
22483* const map = new Map({
22484*   cooperativeGestures: true
22485* });
22486* ```
22487* @see [Example: cooperative gestures](https://maplibre.org/maplibre-gl-js/docs/examples/cooperative-gestures/)
22488**/
22489var CooperativeGesturesHandler = class {
22490	constructor(map, options) {
22491		this._bypassKey = navigator.userAgent.includes("Mac") ? "metaKey" : "ctrlKey";
22492		this._map = map;
22493		this._options = options;
22494		this._enabled = false;
22495	}
22496	isActive() {
22497		return false;
22498	}
22499	reset() {}
22500	_setupUI() {
22501		if (this._container) return;
22502		const mapCanvasContainer = this._map.getCanvasContainer();
22503		mapCanvasContainer.classList.add("maplibregl-cooperative-gestures");
22504		this._container = DOM.create("div", "maplibregl-cooperative-gesture-screen", mapCanvasContainer);
22505		let desktopMessage = this._map._getUIString("CooperativeGesturesHandler.WindowsHelpText");
22506		if (this._bypassKey === "metaKey") desktopMessage = this._map._getUIString("CooperativeGesturesHandler.MacHelpText");
22507		const mobileMessage = this._map._getUIString("CooperativeGesturesHandler.MobileHelpText");
22508		const desktopDiv = document.createElement("div");
22509		desktopDiv.className = "maplibregl-desktop-message";
22510		desktopDiv.textContent = desktopMessage;
22511		this._container.appendChild(desktopDiv);
22512		const mobileDiv = document.createElement("div");
22513		mobileDiv.className = "maplibregl-mobile-message";
22514		mobileDiv.textContent = mobileMessage;
22515		this._container.appendChild(mobileDiv);
22516		this._container.setAttribute("aria-hidden", "true");
22517	}
22518	_destroyUI() {
22519		if (this._container) {
22520			this._container.remove();
22521			this._map.getCanvasContainer().classList.remove("maplibregl-cooperative-gestures");
22522		}
22523		delete this._container;
22524	}
22525	enable() {
22526		this._setupUI();
22527		this._enabled = true;
22528	}
22529	disable() {
22530		this._enabled = false;
22531		this._destroyUI();
22532	}
22533	isEnabled() {
22534		return this._enabled;
22535	}
22536	isBypassed(event) {
22537		return event[this._bypassKey];
22538	}
22539	notifyGestureBlocked(gestureType, originalEvent) {
22540		if (!this._enabled) return;
22541		this._map.fire(new MapLibreEvent("cooperativegestureprevented", {
22542			gestureType,
22543			originalEvent
22544		}));
22545		this._container.classList.add("maplibregl-show");
22546		setTimeout(() => {
22547			this._container.classList.remove("maplibregl-show");
22548		}, 100);
22549	}
22550};
22551//#endregion
22552//#region src/ui/handler/transform-provider.ts
22553/**
22554* @internal
22555* Shared utilities for the Handler classes to access the correct camera state.
22556* If Camera.transformCameraUpdate is specified or terrain is enabled, the
22557* "desired state" of camera may differ from the state used for rendering. The
22558* handlers need the "desired state" to track accumulated changes.
22559*/
22560var TransformProvider = class {
22561	constructor(camera) {
22562		this._camera = camera;
22563	}
22564	get transform() {
22565		return this._camera._requestedCameraState || this._camera.transform;
22566	}
22567	get center() {
22568		return {
22569			lng: this.transform.center.lng,
22570			lat: this.transform.center.lat
22571		};
22572	}
22573	get zoom() {
22574		return this.transform.zoom;
22575	}
22576	get pitch() {
22577		return this.transform.pitch;
22578	}
22579	get bearing() {
22580		return this.transform.bearing;
22581	}
22582	unproject(point) {
22583		return this.transform.screenPointToLocation(Point.convert(point), this._camera.terrain);
22584	}
22585};
22586//#endregion
22587//#region src/ui/handler_manager.ts
22588const isMoving = (p) => p.zoom || p.drag || p.roll || p.pitch || p.rotate;
22589/**
22590* A terrain gesture's anchor plane must stay below this fraction of the camera's
22591* altitude over the center-elevation plane, or the ray-plane solve degenerates.
22592*/
22593const TERRAIN_ANCHOR_MAX_CAMERA_ALTITUDE_FRACTION = .9;
22594var RenderFrameEvent = class extends Event {};
22595function hasChange(result) {
22596	return result.panDelta?.mag() || result.zoomDelta || result.bearingDelta || result.pitchDelta || result.rollDelta;
22597}
22598var HandlerManager = class {
22599	/**
22600	* @internal
22601	* The document that contains the map container element, for cross-window support.
22602	*/
22603	get _ownerDocument() {
22604		return this._el?.ownerDocument || document;
22605	}
22606	/**
22607	* @internal
22608	* The window that contains the map container element, for cross-window support.
22609	*/
22610	get _ownerWindow() {
22611		return this._el?.ownerDocument?.defaultView || window;
22612	}
22613	constructor(map, camera, options) {
22614		this._terrainGesture = {
22615			inFlight: false,
22616			anchorElevation: null
22617		};
22618		this.handleWindowEvent = (e) => {
22619			this.handleEvent(e, `${e.type}Window`);
22620		};
22621		this.handleEvent = (e, eventName) => {
22622			if (e.type === "blur") {
22623				this.stop(true);
22624				return;
22625			}
22626			this._updatingCamera = true;
22627			const inputEvent = e.type === "renderFrame" ? void 0 : e;
22628			const mergedHandlerResult = { needsRenderFrame: false };
22629			const eventsInProgress = {};
22630			const activeHandlers = {};
22631			for (const { handlerName, handler, allowed } of this._handlers) {
22632				if (!handler.isEnabled()) continue;
22633				let data;
22634				if (this._blockedByActive(activeHandlers, allowed, handlerName)) handler.reset();
22635				else if (handler[eventName || e.type]) {
22636					if (isPointableEvent(e, eventName || e.type)) {
22637						const point = DOM.mousePos(this._map.getCanvas(), e);
22638						data = handler[eventName || e.type](e, point);
22639					} else if (isTouchableEvent(e, eventName || e.type)) {
22640						const eventTouches = e.touches;
22641						const mapTouches = this._getMapTouches(eventTouches);
22642						const points = DOM.touchPos(this._map.getCanvas(), mapTouches);
22643						data = handler[eventName || e.type](e, points, mapTouches);
22644					} else if (!isTouchableOrPointableType(eventName || e.type)) data = handler[eventName || e.type](e);
22645					this.mergeHandlerResult(mergedHandlerResult, eventsInProgress, data, handlerName, inputEvent);
22646					if (data?.needsRenderFrame) this._triggerRenderFrame();
22647				}
22648				if (data || handler.isActive()) activeHandlers[handlerName] = handler;
22649			}
22650			const deactivatedHandlers = {};
22651			for (const name in this._previousActiveHandlers) if (!activeHandlers[name]) deactivatedHandlers[name] = inputEvent;
22652			this._previousActiveHandlers = activeHandlers;
22653			if (Object.keys(deactivatedHandlers).length || hasChange(mergedHandlerResult)) {
22654				this._changes.push([
22655					mergedHandlerResult,
22656					eventsInProgress,
22657					deactivatedHandlers
22658				]);
22659				this._triggerRenderFrame();
22660			}
22661			if (Object.keys(activeHandlers).length || hasChange(mergedHandlerResult)) this._camera.stop(true);
22662			this._updatingCamera = false;
22663			const { cameraAnimation } = mergedHandlerResult;
22664			if (cameraAnimation) {
22665				this._inertia.clear();
22666				this._fireEvents({}, {}, true);
22667				this._changes = [];
22668				cameraAnimation(this._map);
22669			}
22670		};
22671		this._map = map;
22672		this._camera = camera;
22673		this._transformProvider = new TransformProvider(this._camera);
22674		this._el = this._map.getCanvasContainer();
22675		this._handlers = [];
22676		this._handlersById = {};
22677		this._changes = [];
22678		this._inertia = new HandlerInertia(map);
22679		this._bearingSnap = options.bearingSnap;
22680		this._previousActiveHandlers = {};
22681		this._eventsInProgress = {};
22682		this._addDefaultHandlers(options);
22683		const el = this._el;
22684		this._listeners = [
22685			[
22686				el,
22687				"touchstart",
22688				{ passive: true }
22689			],
22690			[
22691				el,
22692				"touchmove",
22693				{ passive: false }
22694			],
22695			[
22696				el,
22697				"touchend",
22698				void 0
22699			],
22700			[
22701				el,
22702				"touchcancel",
22703				void 0
22704			],
22705			[
22706				el,
22707				"mousedown",
22708				void 0
22709			],
22710			[
22711				el,
22712				"mousemove",
22713				void 0
22714			],
22715			[
22716				el,
22717				"mouseup",
22718				void 0
22719			],
22720			[
22721				this._ownerDocument,
22722				"mousemove",
22723				{ capture: true }
22724			],
22725			[
22726				this._ownerDocument,
22727				"mouseup",
22728				void 0
22729			],
22730			[
22731				el,
22732				"mouseover",
22733				void 0
22734			],
22735			[
22736				el,
22737				"mouseout",
22738				void 0
22739			],
22740			[
22741				el,
22742				"dblclick",
22743				void 0
22744			],
22745			[
22746				el,
22747				"click",
22748				void 0
22749			],
22750			[
22751				el,
22752				"keydown",
22753				{ capture: false }
22754			],
22755			[
22756				el,
22757				"keyup",
22758				void 0
22759			],
22760			[
22761				el,
22762				"wheel",
22763				{ passive: false }
22764			],
22765			[
22766				el,
22767				"contextmenu",
22768				void 0
22769			],
22770			[
22771				this._ownerWindow,
22772				"blur",
22773				void 0
22774			]
22775		];
22776		for (const [target, type, listenerOptions] of this._listeners) target.addEventListener(type, target === this._ownerDocument ? this.handleWindowEvent : this.handleEvent, listenerOptions);
22777	}
22778	destroy() {
22779		for (const [target, type, listenerOptions] of this._listeners) target.removeEventListener(type, target === this._ownerDocument ? this.handleWindowEvent : this.handleEvent, listenerOptions);
22780	}
22781	_addDefaultHandlers(options) {
22782		const map = this._map;
22783		const el = map.getCanvasContainer();
22784		this._add("mapEvent", new MapEventHandler(map, options));
22785		const boxZoom = map.boxZoom = new BoxZoomHandler(map, options, this._transformProvider);
22786		this._add("boxZoom", boxZoom);
22787		if (options.interactive && options.boxZoom) boxZoom.enable();
22788		const cooperativeGestures = map.cooperativeGestures = new CooperativeGesturesHandler(map, options.cooperativeGestures);
22789		this._add("cooperativeGestures", cooperativeGestures);
22790		if (options.cooperativeGestures) cooperativeGestures.enable();
22791		const tapZoom = new TapZoomHandler(map, this._transformProvider);
22792		const clickZoom = new ClickZoomHandler(map, this._transformProvider);
22793		map.doubleClickZoom = new DoubleClickZoomHandler(clickZoom, tapZoom);
22794		this._add("tapZoom", tapZoom);
22795		this._add("clickZoom", clickZoom);
22796		if (options.interactive && options.doubleClickZoom) map.doubleClickZoom.enable();
22797		const tapDragZoom = new TapDragZoomHandler();
22798		this._add("tapDragZoom", tapDragZoom);
22799		const touchPitch = map.touchPitch = new TwoFingersTouchPitchHandler(map);
22800		this._add("touchPitch", touchPitch);
22801		if (options.interactive && options.touchPitch) map.touchPitch.enable(options.touchPitch);
22802		const getCenter = () => this._camera.transform.centerPoint;
22803		const mouseRotate = generateMouseRotationHandler(options, getCenter);
22804		const mousePitch = generateMousePitchHandler(options);
22805		const mouseRoll = generateMouseRollHandler(options, getCenter);
22806		map.dragRotate = new DragRotateHandler(options, mouseRotate, mousePitch, mouseRoll);
22807		this._add("mouseRotate", mouseRotate, ["mousePitch"]);
22808		this._add("mousePitch", mousePitch, ["mouseRotate", "mouseRoll"]);
22809		this._add("mouseRoll", mouseRoll, ["mousePitch"]);
22810		if (options.interactive && options.dragRotate) map.dragRotate.enable();
22811		const mousePan = generateMousePanHandler(options);
22812		const touchPan = new TouchPanHandler(options, map);
22813		map.dragPan = new DragPanHandler(el, mousePan, touchPan);
22814		this._add("mousePan", mousePan);
22815		this._add("touchPan", touchPan, ["touchZoom", "touchRotate"]);
22816		if (options.interactive && options.dragPan) map.dragPan.enable(options.dragPan);
22817		const touchRotate = new TwoFingersTouchRotateHandler();
22818		const touchZoom = new TwoFingersTouchZoomHandler();
22819		map.touchZoomRotate = new TwoFingersTouchZoomRotateHandler(el, touchZoom, touchRotate, tapDragZoom);
22820		this._add("touchRotate", touchRotate, ["touchPan", "touchZoom"]);
22821		this._add("touchZoom", touchZoom, ["touchPan", "touchRotate"]);
22822		if (options.interactive && options.touchZoomRotate) map.touchZoomRotate.enable(options.touchZoomRotate);
22823		this._add("blockableMapEvent", new BlockableMapEventHandler(map, options), ["touchPan"]);
22824		const scrollZoom = map.scrollZoom = new ScrollZoomHandler(map, () => this._triggerRenderFrame(), this._transformProvider);
22825		this._add("scrollZoom", scrollZoom, ["mousePan"]);
22826		if (options.interactive && options.scrollZoom) map.scrollZoom.enable(options.scrollZoom);
22827		const keyboard = map.keyboard = new KeyboardHandler(map, this._transformProvider);
22828		this._add("keyboard", keyboard);
22829		if (options.interactive && options.keyboard) map.keyboard.enable();
22830	}
22831	_add(handlerName, handler, allowed) {
22832		this._handlers.push({
22833			handlerName,
22834			handler,
22835			allowed
22836		});
22837		this._handlersById[handlerName] = handler;
22838	}
22839	stop(allowEndAnimation) {
22840		if (this._updatingCamera) return;
22841		for (const { handler } of this._handlers) handler.reset();
22842		this._inertia.clear();
22843		this._fireEvents({}, {}, allowEndAnimation);
22844		this._changes = [];
22845	}
22846	isActive() {
22847		for (const { handler } of this._handlers) if (handler.isActive()) return true;
22848		return false;
22849	}
22850	isZooming() {
22851		return !!this._eventsInProgress.zoom || this._map.scrollZoom.isZooming();
22852	}
22853	isRotating() {
22854		return !!this._eventsInProgress.rotate;
22855	}
22856	isMoving() {
22857		return Boolean(isMoving(this._eventsInProgress)) || this.isZooming();
22858	}
22859	_blockedByActive(activeHandlers, allowed, myName) {
22860		for (const name in activeHandlers) {
22861			if (name === myName) continue;
22862			if (!allowed?.includes(name)) return true;
22863		}
22864		return false;
22865	}
22866	_getMapTouches(touches) {
22867		const mapTouches = [];
22868		for (const t of touches) {
22869			const target = t.target;
22870			if (this._el.contains(target)) mapTouches.push(t);
22871		}
22872		return mapTouches;
22873	}
22874	mergeHandlerResult(mergedHandlerResult, eventsInProgress, handlerResult, name, e) {
22875		if (!handlerResult) return;
22876		extend(mergedHandlerResult, handlerResult);
22877		const eventData = {
22878			handlerName: name,
22879			originalEvent: handlerResult.originalEvent || e
22880		};
22881		if (handlerResult.zoomDelta !== void 0) eventsInProgress.zoom = eventData;
22882		if (handlerResult.panDelta !== void 0) eventsInProgress.drag = eventData;
22883		if (handlerResult.rollDelta !== void 0) eventsInProgress.roll = eventData;
22884		if (handlerResult.pitchDelta !== void 0) eventsInProgress.pitch = eventData;
22885		if (handlerResult.bearingDelta !== void 0) eventsInProgress.rotate = eventData;
22886	}
22887	_applyChanges() {
22888		const combined = {};
22889		const combinedEventsInProgress = {};
22890		const combinedDeactivatedHandlers = {};
22891		for (const [change, eventsInProgress, deactivatedHandlers] of this._changes) {
22892			if (change.panDelta) combined.panDelta = (combined.panDelta || new Point(0, 0))._add(change.panDelta);
22893			if (change.zoomDelta) combined.zoomDelta = (combined.zoomDelta || 0) + change.zoomDelta;
22894			if (change.bearingDelta) combined.bearingDelta = (combined.bearingDelta || 0) + change.bearingDelta;
22895			if (change.pitchDelta) combined.pitchDelta = (combined.pitchDelta || 0) + change.pitchDelta;
22896			if (change.rollDelta) combined.rollDelta = (combined.rollDelta || 0) + change.rollDelta;
22897			if (change.around !== void 0) combined.around = change.around;
22898			if (change.pinchAround !== void 0) combined.pinchAround = change.pinchAround;
22899			if (change.noInertia) combined.noInertia = change.noInertia;
22900			extend(combinedEventsInProgress, eventsInProgress);
22901			extend(combinedDeactivatedHandlers, deactivatedHandlers);
22902		}
22903		this._updateMapTransform(combined, combinedEventsInProgress, combinedDeactivatedHandlers);
22904		this._changes = [];
22905	}
22906	/**
22907	* Applies a frame's combined handler deltas to the requested camera state and fires the movement
22908	* events. A frame without a change only fires the events and takes no requested camera state, so a
22909	* trailing frame that merely ends a handler leaves none behind for the next gesture to start from.
22910	*/
22911	_updateMapTransform(combinedResult, combinedEventsInProgress, deactivatedHandlers) {
22912		const terrain = this._map.terrain;
22913		if (!hasChange(combinedResult)) {
22914			this._fireEvents(combinedEventsInProgress, deactivatedHandlers, true);
22915			return;
22916		}
22917		const tr = this._camera.getTransformForUpdate();
22918		this._camera.stop(true);
22919		const { panDelta, zoomDelta, bearingDelta, pitchDelta, rollDelta } = combinedResult;
22920		let { around, aroundOnSurface } = this._resolveAround(combinedResult, terrain, tr);
22921		const aroundElevation = terrain ? this._terrainGestureElevation(terrain, around, aroundOnSurface, tr, combinedEventsInProgress) : void 0;
22922		const deltasForHelper = {
22923			panDelta,
22924			zoomDelta,
22925			rollDelta,
22926			pitchDelta,
22927			bearingDelta,
22928			around,
22929			aroundElevation
22930		};
22931		if (this._camera.cameraHelper.useGlobeControls && !tr.isPointOnMapSurface(around)) around = tr.centerPoint;
22932		const preZoomAroundLoc = this._computePreZoomAroundLoc(tr, around, panDelta, aroundElevation);
22933		this._handleMapControls({
22934			terrain,
22935			tr,
22936			deltasForHelper,
22937			preZoomAroundLoc,
22938			combinedEventsInProgress,
22939			panDelta
22940		});
22941		this._camera.applyUpdatedTransform(tr);
22942		this._map._update();
22943		if (!combinedResult.noInertia) this._inertia.record(combinedResult);
22944		this._fireEvents(combinedEventsInProgress, deactivatedHandlers, true);
22945	}
22946	/**
22947	* The gesture's anchor point: the pinch midpoint when pinching, otherwise the
22948	* pan's anchor, clamped to the center point when it does not lie on the map.
22949	*/
22950	_resolveAround(combinedResult, terrain, tr) {
22951		let around = combinedResult.pinchAround !== void 0 ? combinedResult.pinchAround : combinedResult.around;
22952		around ||= this._camera.transform.centerPoint;
22953		if (terrain && !tr.isPointOnMapSurface(around)) return {
22954			around: tr.centerPoint,
22955			aroundOnSurface: false
22956		};
22957		return {
22958			around,
22959			aroundOnSurface: true
22960		};
22961	}
22962	/**
22963	* The elevation of the plane a terrain gesture is solved on: the elevation of the
22964	* terrain point grabbed at gesture start, captured here on the gesture's first
22965	* frame. Undefined means the gesture is solved at the center's elevation instead —
22966	* when the grabbed terrain is not loaded, the anchor is the center point (a
22967	* center-point anchor is skipped by the camera helper and would lose the pan), or
22968	* the anchor plane is too close to the camera for a stable ray-plane solve.
22969	*/
22970	_terrainGestureElevation(terrain, around, aroundOnSurface, tr, combinedEventsInProgress) {
22971		if (!aroundOnSurface) return;
22972		if (this._terrainGesture.anchorElevation === null && (combinedEventsInProgress.drag || combinedEventsInProgress.zoom)) {
22973			const anchor = tr.screenTerrainPointToMercatorCoordinate(around, terrain);
22974			this._terrainGesture.anchorElevation = anchor ? anchor.z : void 0;
22975		}
22976		const elevation = this._terrainGesture.anchorElevation;
22977		if (elevation === null || elevation === void 0) return;
22978		if (around.distSqr(tr.centerPoint) < .01) return;
22979		if (elevation - tr.elevation >= TERRAIN_ANCHOR_MAX_CAMERA_ALTITUDE_FRACTION * (tr.getCameraAltitude() - tr.elevation)) return;
22980		return elevation;
22981	}
22982	/**
22983	* The location that was under `around` before this frame's deltas — the point the
22984	* camera helper re-anchors after zooming — solved at `aroundElevation` when a
22985	* terrain gesture provides one. When rotating about the center point, the
22986	* transform's center is used directly to avoid numerical issues near the horizon.
22987	*/
22988	_computePreZoomAroundLoc(tr, around, panDelta, aroundElevation) {
22989		if (around.distSqr(tr.centerPoint) < .01) return tr.center;
22990		const aroundPreviousPoint = panDelta ? around.sub(panDelta) : around;
22991		if (aroundElevation !== void 0) return tr.screenPointToLocationAtElevation(aroundPreviousPoint, aroundElevation);
22992		return tr.screenPointToLocation(aroundPreviousPoint);
22993	}
22994	_handleMapControls({ terrain, tr, deltasForHelper, preZoomAroundLoc, combinedEventsInProgress, panDelta }) {
22995		const cameraHelper = this._camera.cameraHelper;
22996		cameraHelper.handleMapControlsRollPitchBearingZoom(deltasForHelper, tr);
22997		if (!terrain) {
22998			cameraHelper.handleMapControlsPan(deltasForHelper, tr, preZoomAroundLoc);
22999			return;
23000		}
23001		if (!this._terrainGesture.inFlight) {
23002			this._terrainGesture.inFlight = true;
23003			this._camera.elevationFreeze = true;
23004			cameraHelper.handleMapControlsPan(deltasForHelper, tr, preZoomAroundLoc);
23005			return;
23006		}
23007		if (!cameraHelper.useGlobeControls && deltasForHelper.aroundElevation === void 0 && combinedEventsInProgress.drag && panDelta) {
23008			tr.setCenter(tr.screenPointToLocation(tr.centerPoint.sub(panDelta)));
23009			return;
23010		}
23011		cameraHelper.handleMapControlsPan(deltasForHelper, tr, preZoomAroundLoc);
23012	}
23013	_fireEvents(newEventsInProgress, deactivatedHandlers, allowEndAnimation) {
23014		const wasMoving = isMoving(this._eventsInProgress);
23015		const nowMoving = isMoving(newEventsInProgress);
23016		const startEvents = {};
23017		for (const eventName in newEventsInProgress) {
23018			const { originalEvent } = newEventsInProgress[eventName];
23019			if (!this._eventsInProgress[eventName]) startEvents[`${eventName}start`] = originalEvent;
23020			this._eventsInProgress[eventName] = newEventsInProgress[eventName];
23021		}
23022		if (!wasMoving && nowMoving) this._fireEvent("movestart", nowMoving.originalEvent);
23023		for (const name in startEvents) this._fireEvent(name, startEvents[name]);
23024		if (nowMoving) this._fireEvent("move", nowMoving.originalEvent);
23025		for (const eventName in newEventsInProgress) {
23026			const { originalEvent } = newEventsInProgress[eventName];
23027			this._fireEvent(eventName, originalEvent);
23028		}
23029		const endEvents = {};
23030		let originalEndEvent;
23031		for (const eventName in this._eventsInProgress) {
23032			const { handlerName, originalEvent } = this._eventsInProgress[eventName];
23033			if (!this._handlersById[handlerName].isActive()) {
23034				delete this._eventsInProgress[eventName];
23035				originalEndEvent = deactivatedHandlers[handlerName] || originalEvent;
23036				endEvents[`${eventName}end`] = originalEndEvent;
23037			}
23038		}
23039		for (const name in endEvents) this._fireEvent(name, endEvents[name]);
23040		const stillMoving = isMoving(this._eventsInProgress);
23041		const finishedMoving = (wasMoving || nowMoving) && !stillMoving;
23042		if (finishedMoving && this._terrainGesture.inFlight) {
23043			this._camera.elevationFreeze = false;
23044			this._terrainGesture = {
23045				inFlight: false,
23046				anchorElevation: null
23047			};
23048			const tr = this._camera.getTransformForUpdate();
23049			if (this._map.getCenterClampedToGround()) tr.recalculateZoomAndCenter(this._map.terrain);
23050			this._camera.applyUpdatedTransform(tr);
23051		}
23052		if (allowEndAnimation && finishedMoving) {
23053			this._updatingCamera = true;
23054			const inertialEase = this._inertia._onMoveEnd(this._map.dragPan._inertiaOptions);
23055			const shouldSnapToNorth = (bearing) => bearing !== 0 && -this._bearingSnap < bearing && bearing < this._bearingSnap;
23056			if (inertialEase && (inertialEase.essential || !browser.prefersReducedMotion)) {
23057				if (shouldSnapToNorth(inertialEase.bearing || this._map.getBearing())) inertialEase.bearing = 0;
23058				inertialEase.freezeElevation = true;
23059				this._map.easeTo(inertialEase, { originalEvent: originalEndEvent });
23060			} else {
23061				this._fireEvent("moveend", originalEndEvent);
23062				if (shouldSnapToNorth(this._map.getBearing())) this._map.resetNorth();
23063			}
23064			this._updatingCamera = false;
23065		}
23066	}
23067	/**
23068	* Fires a movement event through the camera, whose evented parent is the map: the map's listeners
23069	* see the event as before, and the camera's own `moveend` listener drops the requested camera state,
23070	* so the next gesture starts from the rendered transform instead of the state this one ended in.
23071	*/
23072	_fireEvent(type, e) {
23073		this._camera.fire(new MapMovementEvent(type, e ? { originalEvent: e } : {}));
23074	}
23075	_requestFrame() {
23076		this._map.triggerRepaint();
23077		return this._map._renderTaskQueue.add((timeStamp) => {
23078			delete this._frameId;
23079			this.handleEvent(new RenderFrameEvent("renderFrame", { timeStamp }));
23080			this._applyChanges();
23081		});
23082	}
23083	_triggerRenderFrame() {
23084		if (this._frameId === void 0) this._frameId = this._requestFrame();
23085	}
23086};
23087//#endregion
23088//#region src/ui/camera.ts
23089var Camera = class extends Evented {
23090	constructor(options) {
23091		super();
23092		this._renderFrameCallback = () => {
23093			const t = Math.min((now() - this._easeStart) / this._easeOptions.duration, 1);
23094			this._onEaseFrame(this._easeOptions.easing(t));
23095			if (t < 1 && this._easeFrameId) this._easeFrameId = this._requestRenderFrame(this._renderFrameCallback);
23096			else this.stop();
23097		};
23098		this.transform = new MercatorTransform();
23099		this.cameraHelper = new MercatorCameraHelper();
23100		if (options.minZoom !== void 0) this.transform.setMinZoom(options.minZoom);
23101		if (options.maxZoom !== void 0) this.transform.setMaxZoom(options.maxZoom);
23102		if (options.minPitch !== void 0) this.transform.setMinPitch(options.minPitch);
23103		if (options.maxPitch !== void 0) this.transform.setMaxPitch(options.maxPitch);
23104		if (options.renderWorldCopies !== void 0) this.transform.setRenderWorldCopies(options.renderWorldCopies);
23105		if (options.transformConstrain !== null) this.transform.setConstrainOverride(options.transformConstrain);
23106		this._moving = false;
23107		this._zooming = false;
23108		this._bearingSnap = options.bearingSnap;
23109		this._zoomSnap = options.zoomSnap;
23110		this._requestRenderFrame = options.requestRenderFrame;
23111		this._cancelRenderFrame = options.cancelRenderFrame;
23112		this.terrain = options.terrain;
23113		this._centerClampedToGround = options.centerClampedToGround ?? true;
23114		this.transformCameraUpdate = options.transformCameraUpdate ?? null;
23115		this._stopHandlers = options.stopHandlers ?? (() => {});
23116		this.on("moveend", () => {
23117			delete this._requestedCameraState;
23118		});
23119	}
23120	/**
23121	* @internal
23122	* Hands the camera the map's terrain, or null when the map has none, and brings the center
23123	* elevation up to date with it, see {@link Camera.applyTerrainChange}.
23124	*/
23125	setTerrain(terrain) {
23126		this.terrain = terrain;
23127		this.applyTerrainChange();
23128	}
23129	migrateProjection(newTransform, newCameraHelper) {
23130		newTransform.apply(this.transform, true);
23131		this.transform = newTransform;
23132		this.cameraHelper = newCameraHelper;
23133		if (this._requestedCameraState) {
23134			const requestedCameraState = newTransform.clone();
23135			requestedCameraState.apply(this._requestedCameraState, true);
23136			this._requestedCameraState = requestedCameraState;
23137		}
23138	}
23139	getCenter() {
23140		return new LngLat(this.transform.center.lng, this.transform.center.lat);
23141	}
23142	setCenter(center, eventData) {
23143		return this.jumpTo({ center }, eventData);
23144	}
23145	getCenterElevation() {
23146		return this.transform.elevation;
23147	}
23148	setCenterElevation(elevation, eventData) {
23149		this.jumpTo({ elevation }, eventData);
23150		return this;
23151	}
23152	getCenterClampedToGround() {
23153		return this._centerClampedToGround;
23154	}
23155	setCenterClampedToGround(centerClampedToGround) {
23156		this._centerClampedToGround = centerClampedToGround;
23157	}
23158	panBy(offset, options, eventData) {
23159		offset = Point.convert(offset).mult(-1);
23160		return this.panTo(this.transform.center, extend({ offset }, options), eventData);
23161	}
23162	panTo(lnglat, options, eventData) {
23163		return this.easeTo(extend({ center: lnglat }, options), eventData);
23164	}
23165	getZoom() {
23166		return this.transform.zoom;
23167	}
23168	setZoom(zoom, eventData) {
23169		this.jumpTo({ zoom }, eventData);
23170		return this;
23171	}
23172	zoomTo(zoom, options, eventData) {
23173		return this.easeTo(extend({ zoom }, options), eventData);
23174	}
23175	zoomIn(options, eventData) {
23176		this.zoomTo(evaluateZoomSnap(this.getZoom() + 1, this._zoomSnap), options, eventData);
23177		return this;
23178	}
23179	zoomOut(options, eventData) {
23180		this.zoomTo(evaluateZoomSnap(this.getZoom() - 1, this._zoomSnap), options, eventData);
23181		return this;
23182	}
23183	getVerticalFieldOfView() {
23184		return this.transform.fov;
23185	}
23186	setVerticalFieldOfView(fov, eventData) {
23187		if (fov != this.transform.fov) {
23188			this.transform.setFov(fov);
23189			this.fire(new MapMovementEvent("movestart", eventData)).fire(new MapMovementEvent("move", eventData)).fire(new MapMovementEvent("moveend", eventData));
23190		}
23191		return this;
23192	}
23193	getBearing() {
23194		return this.transform.bearing;
23195	}
23196	setZoomSnap(snap) {
23197		this._zoomSnap = snap;
23198		return this;
23199	}
23200	getZoomSnap() {
23201		return this._zoomSnap;
23202	}
23203	setBearing(bearing, eventData) {
23204		this.jumpTo({ bearing }, eventData);
23205		return this;
23206	}
23207	getPadding() {
23208		return this.transform.padding;
23209	}
23210	setPadding(padding, eventData) {
23211		this.jumpTo({ padding }, eventData);
23212		return this;
23213	}
23214	rotateTo(bearing, options, eventData) {
23215		return this.easeTo(extend({ bearing }, options), eventData);
23216	}
23217	resetNorth(options, eventData) {
23218		this.rotateTo(0, extend({ duration: 1e3 }, options), eventData);
23219		return this;
23220	}
23221	resetNorthPitch(options, eventData) {
23222		this.easeTo(extend({
23223			bearing: 0,
23224			pitch: 0,
23225			roll: 0,
23226			duration: 1e3
23227		}, options), eventData);
23228		return this;
23229	}
23230	snapToNorth(options, eventData) {
23231		if (Math.abs(this.getBearing()) < this._bearingSnap) return this.resetNorth(options, eventData);
23232		return this;
23233	}
23234	getPitch() {
23235		return this.transform.pitch;
23236	}
23237	setPitch(pitch, eventData) {
23238		this.jumpTo({ pitch }, eventData);
23239		return this;
23240	}
23241	getRoll() {
23242		return this.transform.roll;
23243	}
23244	setRoll(roll, eventData) {
23245		this.jumpTo({ roll }, eventData);
23246		return this;
23247	}
23248	cameraForBounds(bounds, options) {
23249		bounds = LngLatBounds.convert(bounds).adjustAntiMeridian();
23250		const bearing = options?.bearing || 0;
23251		return this._cameraForBoxAndBearing(bounds.getNorthWest(), bounds.getSouthEast(), bearing, options);
23252	}
23253	/**
23254	* @internal
23255	* Calculate the center of these two points in the viewport and use
23256	* the highest zoom level up to and including {@link Map.getMaxZoom} that fits
23257	* the AABB defined by these points in the viewport at the specified bearing.
23258	* @param p0 - First point
23259	* @param p1 - Second point
23260	* @param bearing - Desired map bearing at end of animation, in degrees
23261	* @param options - the camera options
23262	* @returns If map is able to fit to provided bounds, returns `center`, `zoom`, and `bearing`.
23263	*      If map is unable to fit, method will warn and return undefined.
23264	* @example
23265	* ```ts
23266	* let p0 = [-79, 43];
23267	* let p1 = [-73, 45];
23268	* let bearing = 90;
23269	* let newCameraTransform = map._cameraForBoxAndBearing(p0, p1, bearing, {
23270	*   padding: {top: 10, bottom:25, left: 15, right: 5}
23271	* });
23272	* ```
23273	*/
23274	_cameraForBoxAndBearing(p0, p1, bearing, options) {
23275		const defaultPadding = {
23276			top: 0,
23277			bottom: 0,
23278			right: 0,
23279			left: 0
23280		};
23281		options = extend({
23282			padding: defaultPadding,
23283			offset: [0, 0],
23284			maxZoom: this.transform.maxZoom
23285		}, options);
23286		if (typeof options.padding === "number") {
23287			const p = options.padding;
23288			options.padding = {
23289				top: p,
23290				bottom: p,
23291				right: p,
23292				left: p
23293			};
23294		}
23295		const padding = extend(defaultPadding, options.padding);
23296		options.padding = padding;
23297		const tr = this.transform;
23298		const bounds = new LngLatBounds(p0, p1);
23299		const result = this.cameraHelper.cameraForBoxAndBearing(options, padding, bounds, bearing, tr);
23300		if (result && this._zoomSnap) result.zoom = evaluateZoomSnap(result.zoom, this._zoomSnap, -1);
23301		return result;
23302	}
23303	fitBounds(bounds, options, eventData) {
23304		return this._fitInternal(this.cameraForBounds(bounds, options), options, eventData);
23305	}
23306	fitScreenCoordinates(p0, p1, bearing, options, eventData) {
23307		return this._fitInternal(this._cameraForBoxAndBearing(this.transform.screenPointToLocation(Point.convert(p0)), this.transform.screenPointToLocation(Point.convert(p1)), bearing, options), options, eventData);
23308	}
23309	_fitInternal(calculatedOptions, options, eventData) {
23310		if (!calculatedOptions) return this;
23311		options = extend(calculatedOptions, options);
23312		delete options.padding;
23313		return options.linear ? this.easeTo(options, eventData) : this.flyTo(options, eventData);
23314	}
23315	jumpTo(options, eventData) {
23316		this.stop();
23317		if (options.zoom !== void 0 && this._zoomSnap) options.zoom = evaluateZoomSnap(options.zoom, this._zoomSnap);
23318		const tr = this.getTransformForUpdate();
23319		let bearingChanged = false, pitchChanged = false;
23320		let rollChanged = false;
23321		const oldZoom = tr.zoom;
23322		if (this.terrain) tr.setElevation(this.terrain.getElevationForLngLat(options.center ? LngLat.convert(options.center) : tr.center, tr));
23323		this.cameraHelper.handleJumpToCenterZoom(tr, options);
23324		const zoomChanged = tr.zoom !== oldZoom;
23325		if (options.elevation !== void 0 && tr.elevation !== +options.elevation) tr.setElevation(+options.elevation);
23326		if (options.bearing !== void 0 && tr.bearing !== +options.bearing) {
23327			bearingChanged = true;
23328			tr.setBearing(+options.bearing);
23329		}
23330		if (options.pitch !== void 0 && tr.pitch !== +options.pitch) {
23331			pitchChanged = true;
23332			tr.setPitch(+options.pitch);
23333		}
23334		if (options.roll !== void 0 && tr.roll !== +options.roll) {
23335			rollChanged = true;
23336			tr.setRoll(+options.roll);
23337		}
23338		if (options.padding != null && !tr.isPaddingEqual(options.padding)) tr.setPadding(options.padding);
23339		this.applyUpdatedTransform(tr);
23340		this.fire(new MapMovementEvent("movestart", eventData)).fire(new MapMovementEvent("move", eventData));
23341		if (zoomChanged) this.fire(new MapMovementEvent("zoomstart", eventData)).fire(new MapMovementEvent("zoom", eventData)).fire(new MapMovementEvent("zoomend", eventData));
23342		if (bearingChanged) this.fire(new MapMovementEvent("rotatestart", eventData)).fire(new MapMovementEvent("rotate", eventData)).fire(new MapMovementEvent("rotateend", eventData));
23343		if (pitchChanged) this.fire(new MapMovementEvent("pitchstart", eventData)).fire(new MapMovementEvent("pitch", eventData)).fire(new MapMovementEvent("pitchend", eventData));
23344		if (rollChanged) this.fire(new MapMovementEvent("rollstart", eventData)).fire(new MapMovementEvent("roll", eventData)).fire(new MapMovementEvent("rollend", eventData));
23345		return this.fire(new MapMovementEvent("moveend", eventData));
23346	}
23347	/**
23348	* Calculates camera options for moving a geographic anchor to a screen point without
23349	* changing this camera.
23350	*/
23351	calculateAnchoredCameraOptions(options) {
23352		const tr = this.transform.clone();
23353		const anchor = LngLat.convert(options.anchorLocation);
23354		const target = Point.convert(options.anchorScreenPoint);
23355		const deltas = {
23356			panDelta: target.sub(this.transform.locationToScreenPoint(anchor, this.terrain)),
23357			zoomDelta: options.zoom === void 0 ? 0 : options.zoom - tr.zoom,
23358			bearingDelta: 0,
23359			pitchDelta: 0,
23360			rollDelta: 0,
23361			around: target,
23362			aroundElevation: this.terrain?.getElevationForLngLat(anchor, tr)
23363		};
23364		this.cameraHelper.handleMapControlsRollPitchBearingZoom(deltas, tr);
23365		this.cameraHelper.handleMapControlsPan(deltas, tr, anchor);
23366		return {
23367			center: tr.center,
23368			zoom: tr.zoom
23369		};
23370	}
23371	calculateCameraOptionsFromCameraLngLatAltRotation(cameraLngLat, cameraAlt, bearing, pitch, roll) {
23372		const centerInfo = this.transform.calculateCenterFromCameraLngLatAlt(cameraLngLat, cameraAlt, bearing, pitch);
23373		return {
23374			center: centerInfo.center,
23375			elevation: centerInfo.elevation,
23376			zoom: centerInfo.zoom,
23377			bearing,
23378			pitch,
23379			roll
23380		};
23381	}
23382	easeTo(options, eventData) {
23383		this._stop(false, options.easeId);
23384		options = extend({
23385			offset: [0, 0],
23386			duration: 500,
23387			easing: defaultEasing
23388		}, options);
23389		if (options.zoom !== void 0 && this._zoomSnap) options.zoom = evaluateZoomSnap(options.zoom, this._zoomSnap);
23390		if (options.animate === false || !options.essential && browser.prefersReducedMotion) options.duration = 0;
23391		const tr = this.getTransformForUpdate();
23392		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;
23393		const offsetAsPoint = Point.convert(options.offset);
23394		let around, aroundPoint;
23395		if (options.around) {
23396			around = LngLat.convert(options.around);
23397			aroundPoint = tr.locationToScreenPoint(around);
23398		}
23399		const currently = {
23400			moving: this._moving,
23401			zooming: this._zooming,
23402			rotating: this._rotating,
23403			pitching: this._pitching,
23404			rolling: this._rolling
23405		};
23406		const easeHandler = this.cameraHelper.handleEaseTo(tr, {
23407			bearing,
23408			pitch,
23409			roll,
23410			padding,
23411			around,
23412			aroundPoint,
23413			offsetAsPoint,
23414			offset: options.offset,
23415			zoom: options.zoom,
23416			center: options.center
23417		});
23418		this._rotating ||= startBearing !== bearing;
23419		this._pitching ||= pitch !== startPitch;
23420		this._rolling ||= roll !== startRoll;
23421		this._padding = !tr.isPaddingEqual(padding);
23422		this._zooming ||= easeHandler.isZooming;
23423		this._easeId = options.easeId;
23424		this._prepareEase(eventData, options.noMoveStart, currently);
23425		if (this.terrain) this._prepareElevation(easeHandler.elevationCenter);
23426		this._ease((k) => {
23427			easeHandler.easeFunc(k);
23428			if (this.terrain && !options.freezeElevation) this._updateElevation(k);
23429			this.applyUpdatedTransform(tr);
23430			this._fireMoveEvents(eventData);
23431		}, (interruptingEaseId) => {
23432			if (this.terrain && options.freezeElevation) this._finalizeElevation();
23433			else this.elevationFreeze = false;
23434			this._afterEase(eventData, interruptingEaseId);
23435		}, options);
23436		return this;
23437	}
23438	_prepareEase(eventData, noMoveStart, currently = {}) {
23439		this._moving = true;
23440		if (!noMoveStart && !currently.moving) this.fire(new MapMovementEvent("movestart", eventData));
23441		if (this._zooming && !currently.zooming) this.fire(new MapMovementEvent("zoomstart", eventData));
23442		if (this._rotating && !currently.rotating) this.fire(new MapMovementEvent("rotatestart", eventData));
23443		if (this._pitching && !currently.pitching) this.fire(new MapMovementEvent("pitchstart", eventData));
23444		if (this._rolling && !currently.rolling) this.fire(new MapMovementEvent("rollstart", eventData));
23445	}
23446	_prepareElevation(center) {
23447		this._elevationCenter = center;
23448		this._elevationStart = this.transform.elevation;
23449		this._elevationTarget = this.terrain.getElevationForLngLat(center, this.transform);
23450		this.elevationFreeze = true;
23451	}
23452	_updateElevation(k) {
23453		if (this._elevationStart === void 0 || this._elevationCenter === void 0) this._prepareElevation(this.transform.center);
23454		this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._elevationCenter, this.transform.tileZoom));
23455		const elevation = this.terrain.getElevationForLngLat(this._elevationCenter, this.transform);
23456		if (k < 1 && elevation !== this._elevationTarget) {
23457			const pitch1 = this._elevationTarget - this._elevationStart;
23458			const pitch2 = (elevation - (pitch1 * k + this._elevationStart)) / (1 - k);
23459			this._elevationStart += k * (pitch1 - pitch2);
23460			this._elevationTarget = elevation;
23461		}
23462		this.transform.setElevation(interpolateFactory.number(this._elevationStart, this._elevationTarget, k));
23463	}
23464	_finalizeElevation() {
23465		this.elevationFreeze = false;
23466		if (this.getCenterClampedToGround()) this.transform.recalculateZoomAndCenter(this.terrain);
23467	}
23468	/**
23469	* @internal
23470	* Applies a change of the terrain under the center to the transform: the terrain was set or
23471	* removed, or a DEM tile landed. The center keeps its place and the camera moves with the
23472	* center's elevation, as it does on every rendered frame while nothing holds the elevation.
23473	* While a gesture or an ease holds it this does nothing: the camera stays where the user put
23474	* it and the hold's end re-solves zoom and center onto the new terrain without moving it.
23475	* Nothing is in flight when this writes, so it writes the rendered transform, like the
23476	* per-frame clamp; a requested camera state created here would outlive the call and the
23477	* next gesture would start from it.
23478	*/
23479	applyTerrainChange() {
23480		if (this.elevationFreeze) return;
23481		const tr = this.transform;
23482		tr.setMinElevationForCurrentTile(this.terrain ? this.terrain.getMinTileElevationForLngLatZoom(tr.center, tr.tileZoom) : 0);
23483		if (this.getCenterClampedToGround()) tr.setElevation(this.terrain ? this.terrain.getElevationForLngLat(tr.center, tr) : 0);
23484	}
23485	/**
23486	* @internal
23487	* Called when the camera is about to be manipulated.
23488	* If `transformCameraUpdate` is specified or terrain is enabled, a copy of
23489	* the current transform is created to track the accumulated changes.
23490	* This underlying transform represents the "desired state" proposed by input handlers / animations / UI controls.
23491	* It may differ from the state used for rendering (`this.transform`).
23492	* @returns Transform to apply changes to
23493	*/
23494	getTransformForUpdate() {
23495		if (!this.transformCameraUpdate && !this.terrain) return this.transform;
23496		this._requestedCameraState ||= this.transform.clone();
23497		return this._requestedCameraState;
23498	}
23499	/**
23500	* @internal
23501	* Checks the given transform for the camera being below terrain surface and
23502	* returns new pitch and zoom to fix that.
23503	*
23504	* With the new pitch and zoom, the camera will be at the same ground
23505	* position but at higher altitude. It will still point to the same spot on
23506	* the map.
23507	*
23508	* @param tr - The transform to check.
23509	*/
23510	_elevateCameraIfInsideTerrain(tr) {
23511		if (!this.terrain && tr.elevation >= 0 && tr.pitch <= 90) return {};
23512		const cameraLngLat = tr.getCameraLngLat();
23513		const cameraAltitude = tr.getCameraAltitude();
23514		const minAltitude = this.terrain ? this.terrain.getElevationForLngLatZoom(cameraLngLat, tr.zoom) : 0;
23515		if (cameraAltitude < minAltitude) {
23516			const newCamera = tr.calculateCameraOptionsFromTo(cameraLngLat, minAltitude, tr.center, tr.elevation);
23517			return {
23518				pitch: newCamera.pitch,
23519				zoom: newCamera.zoom
23520			};
23521		}
23522		return {};
23523	}
23524	/**
23525	* @internal
23526	* Called after the camera is done being manipulated.
23527	* @param tr - the requested camera end state
23528	* If the camera is inside terrain, it gets elevated.
23529	* Call `transformCameraUpdate` if present, and then apply the "approved" changes.
23530	*/
23531	applyUpdatedTransform(tr) {
23532		const modifiers = [];
23533		modifiers.push((tr) => this._elevateCameraIfInsideTerrain(tr));
23534		if (this.transformCameraUpdate) modifiers.push((tr) => this.transformCameraUpdate(tr));
23535		if (!modifiers.length) return;
23536		const finalTransform = tr.clone();
23537		for (const modifier of modifiers) {
23538			const nextTransform = finalTransform.clone();
23539			const { center, zoom, roll, pitch, bearing, elevation } = modifier(nextTransform);
23540			if (center) nextTransform.setCenter(center);
23541			if (elevation !== void 0) nextTransform.setElevation(elevation);
23542			if (zoom !== void 0) nextTransform.setZoom(zoom);
23543			if (roll !== void 0) nextTransform.setRoll(roll);
23544			if (pitch !== void 0) nextTransform.setPitch(pitch);
23545			if (bearing !== void 0) nextTransform.setBearing(bearing);
23546			finalTransform.apply(nextTransform, false);
23547		}
23548		this.transform.apply(finalTransform, false);
23549	}
23550	_fireMoveEvents(eventData) {
23551		this.fire(new MapMovementEvent("move", eventData));
23552		if (this._zooming) this.fire(new MapMovementEvent("zoom", eventData));
23553		if (this._rotating) this.fire(new MapMovementEvent("rotate", eventData));
23554		if (this._pitching) this.fire(new MapMovementEvent("pitch", eventData));
23555		if (this._rolling) this.fire(new MapMovementEvent("roll", eventData));
23556	}
23557	_afterEase(eventData, easeId) {
23558		if (this._easeId && easeId && this._easeId === easeId) return;
23559		delete this._easeId;
23560		const wasZooming = this._zooming;
23561		const wasRotating = this._rotating;
23562		const wasPitching = this._pitching;
23563		const wasRolling = this._rolling;
23564		this._moving = false;
23565		this._zooming = false;
23566		this._rotating = false;
23567		this._pitching = false;
23568		this._rolling = false;
23569		this._padding = false;
23570		if (wasZooming) this.fire(new MapMovementEvent("zoomend", eventData));
23571		if (wasRotating) this.fire(new MapMovementEvent("rotateend", eventData));
23572		if (wasPitching) this.fire(new MapMovementEvent("pitchend", eventData));
23573		if (wasRolling) this.fire(new MapMovementEvent("rollend", eventData));
23574		this.fire(new MapMovementEvent("moveend", eventData));
23575	}
23576	flyTo(options, eventData) {
23577		if (!options.essential && browser.prefersReducedMotion) {
23578			const coercedOptions = pick(options, [
23579				"center",
23580				"zoom",
23581				"bearing",
23582				"pitch",
23583				"roll",
23584				"elevation",
23585				"padding"
23586			]);
23587			return this.jumpTo(coercedOptions, eventData);
23588		}
23589		this.stop();
23590		options = extend({
23591			offset: [0, 0],
23592			speed: 1.2,
23593			curve: 1.42,
23594			easing: defaultEasing
23595		}, options);
23596		if (options.zoom !== void 0 && this._zoomSnap) options.zoom = evaluateZoomSnap(options.zoom, this._zoomSnap);
23597		const tr = this.getTransformForUpdate(), startBearing = tr.bearing, startPitch = tr.pitch, startRoll = tr.roll, startPadding = tr.padding;
23598		const bearing = options.bearing !== void 0 ? this._normalizeBearing(options.bearing, startBearing) : startBearing;
23599		const pitch = options.pitch !== void 0 ? +options.pitch : startPitch;
23600		const roll = options.roll !== void 0 ? this._normalizeBearing(options.roll, startRoll) : startRoll;
23601		const padding = options.padding !== void 0 ? options.padding : tr.padding;
23602		const offsetAsPoint = Point.convert(options.offset);
23603		let pointAtOffset = tr.centerPoint.add(offsetAsPoint);
23604		const locationAtOffset = tr.screenPointToLocation(pointAtOffset);
23605		const flyToHandler = this.cameraHelper.handleFlyTo(tr, {
23606			bearing,
23607			pitch,
23608			roll,
23609			padding,
23610			locationAtOffset,
23611			offsetAsPoint,
23612			center: options.center,
23613			minZoom: options.minZoom,
23614			zoom: options.zoom
23615		});
23616		let rho = options.curve;
23617		const w0 = Math.max(tr.width, tr.height);
23618		const w1 = w0 / flyToHandler.scaleOfZoom;
23619		const u1 = flyToHandler.pixelPathLength;
23620		const wMax = w0 / flyToHandler.scaleOfMinZoom;
23621		rho = Math.min(rho, Math.sqrt(wMax / u1 * 2));
23622		const rho2 = rho * rho;
23623		/**
23624		* ráµ¢: Returns the zoom-out factor at one end of the animation.
23625		*
23626		* @param descent - `true` for the descent, `false` for the ascent
23627		*/
23628		function zoomOutFactor(descent) {
23629			const b = (w1 * w1 - w0 * w0 + (descent ? -1 : 1) * rho2 * rho2 * u1 * u1) / (2 * (descent ? w1 : w0) * rho2 * u1);
23630			return Math.log(Math.sqrt(b * b + 1) - b);
23631		}
23632		function sinh(n) {
23633			return (Math.exp(n) - Math.exp(-n)) / 2;
23634		}
23635		function cosh(n) {
23636			return (Math.exp(n) + Math.exp(-n)) / 2;
23637		}
23638		function tanh(n) {
23639			return sinh(n) / cosh(n);
23640		}
23641		const r0 = zoomOutFactor(false);
23642		let w = function(s) {
23643			return cosh(r0) / cosh(r0 + rho * s);
23644		};
23645		let u = function(s) {
23646			return w0 * ((cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2) / u1;
23647		};
23648		let S = (zoomOutFactor(true) - r0) / rho;
23649		if (Math.abs(u1) < 2e-6 || !isFinite(S)) {
23650			if (Math.abs(w0 - w1) < 1e-6) return this.easeTo(options, eventData);
23651			const k = w1 < w0 ? -1 : 1;
23652			S = Math.abs(Math.log(w1 / w0)) / rho;
23653			u = () => 0;
23654			w = (s) => Math.exp(k * rho * s);
23655		}
23656		if (options.duration !== void 0) options.duration = +options.duration;
23657		else {
23658			const V = options.screenSpeed !== void 0 ? +options.screenSpeed / rho : +options.speed;
23659			options.duration = 1e3 * S / V;
23660		}
23661		if (options.maxDuration && options.duration > options.maxDuration) options.duration = 0;
23662		this._zooming = true;
23663		this._rotating = startBearing !== bearing;
23664		this._pitching = pitch !== startPitch;
23665		this._rolling = roll !== startRoll;
23666		this._padding = !tr.isPaddingEqual(padding);
23667		this._prepareEase(eventData, false);
23668		if (this.terrain) this._prepareElevation(flyToHandler.targetCenter);
23669		this._ease((k) => {
23670			const s = k * S;
23671			const scale = 1 / w(s);
23672			const centerFactor = u(s);
23673			if (this._rotating) tr.setBearing(interpolateFactory.number(startBearing, bearing, k));
23674			if (this._pitching) tr.setPitch(interpolateFactory.number(startPitch, pitch, k));
23675			if (this._rolling) tr.setRoll(interpolateFactory.number(startRoll, roll, k));
23676			if (this._padding) {
23677				tr.interpolatePadding(startPadding, padding, k);
23678				pointAtOffset = tr.centerPoint.add(offsetAsPoint);
23679			}
23680			flyToHandler.easeFunc(k, scale, centerFactor, pointAtOffset);
23681			if (this.terrain && !options.freezeElevation) this._updateElevation(k);
23682			this.applyUpdatedTransform(tr);
23683			this._fireMoveEvents(eventData);
23684		}, () => {
23685			if (this.terrain && options.freezeElevation) this._finalizeElevation();
23686			else this.elevationFreeze = false;
23687			this._afterEase(eventData);
23688		}, options);
23689		return this;
23690	}
23691	isEasing() {
23692		return !!this._easeFrameId;
23693	}
23694	stop(allowGestures) {
23695		return this._stop(allowGestures);
23696	}
23697	_stop(allowGestures, easeId) {
23698		if (this._easeFrameId) {
23699			this._cancelRenderFrame(this._easeFrameId);
23700			delete this._easeFrameId;
23701			delete this._onEaseFrame;
23702		}
23703		if (this._onEaseEnd) {
23704			const onEaseEnd = this._onEaseEnd;
23705			delete this._onEaseEnd;
23706			onEaseEnd.call(this, easeId);
23707		}
23708		if (!allowGestures) this._stopHandlers();
23709		return this;
23710	}
23711	_ease(frame, finish, options) {
23712		if (options.animate === false || options.duration === 0) {
23713			frame(1);
23714			finish();
23715		} else {
23716			this._easeStart = now();
23717			this._easeOptions = options;
23718			this._onEaseFrame = frame;
23719			this._onEaseEnd = finish;
23720			this._easeFrameId = this._requestRenderFrame(this._renderFrameCallback);
23721		}
23722	}
23723	_normalizeBearing(bearing, currentBearing) {
23724		bearing = wrap(bearing, -180, 180);
23725		const diff = Math.abs(bearing - currentBearing);
23726		if (Math.abs(bearing - 360 - currentBearing) < diff) bearing -= 360;
23727		if (Math.abs(bearing + 360 - currentBearing) < diff) bearing += 360;
23728		return bearing;
23729	}
23730	isMoving() {
23731		return this._moving;
23732	}
23733	isZooming() {
23734		return this._zooming;
23735	}
23736	isRotating() {
23737		return this._rotating;
23738	}
23739};
23740//#endregion
23741//#region src/ui/control/attribution_control.ts
23742const defaultAttributionControlOptions = {
23743	compact: true,
23744	customAttribution: "<a href=\"https://maplibre.org/\" target=\"_blank\">MapLibre</a>"
23745};
23746/**
23747* An `AttributionControl` control presents the map's attribution information. By default, the attribution control is expanded (regardless of map width).
23748* @group Markers and Controls
23749* @example
23750* ```ts
23751* let map = new Map({attributionControl: false})
23752*     .addControl(new AttributionControl({
23753*         compact: true
23754*     }));
23755* ```
23756* @see [Change the default position for attribution](https://maplibre.org/maplibre-gl-js/docs/examples/change-the-default-position-for-attribution/)
23757*/
23758var AttributionControl = class {
23759	/**
23760	* @param options - the attribution options
23761	*/
23762	constructor(options = defaultAttributionControlOptions) {
23763		this._toggleAttribution = () => {
23764			if (this._container.classList.contains("maplibregl-compact")) {
23765				if (this._container.classList.contains("maplibregl-compact-show")) {
23766					this._container.setAttribute("open", "");
23767					this._container.classList.remove("maplibregl-compact-show");
23768				} else {
23769					this._container.classList.add("maplibregl-compact-show");
23770					this._container.removeAttribute("open");
23771				}
23772			}
23773		};
23774		this._updateData = (e) => {
23775			if (e && (e.type === "terrain" || e.dataType === "style" || e.dataType === "source" && (e.sourceDataType === "metadata" || e.sourceDataType === "visibility"))) this._updateAttributions();
23776		};
23777		this._updateCompact = () => {
23778			if (this._map.getCanvasContainer().offsetWidth <= 640 || this._compact) {
23779				if (this._compact === false) this._container.setAttribute("open", "");
23780				else if (!this._container.classList.contains("maplibregl-compact") && !this._container.classList.contains("maplibregl-attrib-empty")) {
23781					this._container.setAttribute("open", "");
23782					this._container.classList.add("maplibregl-compact", "maplibregl-compact-show");
23783				}
23784			} else {
23785				this._container.setAttribute("open", "");
23786				if (this._container.classList.contains("maplibregl-compact")) this._container.classList.remove("maplibregl-compact", "maplibregl-compact-show");
23787			}
23788		};
23789		this._updateCompactMinimize = () => {
23790			if (this._container.classList.contains("maplibregl-compact")) {
23791				if (this._container.classList.contains("maplibregl-compact-show")) this._container.classList.remove("maplibregl-compact-show");
23792			}
23793		};
23794		this.options = options;
23795	}
23796	getDefaultPosition() {
23797		return "bottom-right";
23798	}
23799	/** {@inheritDoc IControl.onAdd} */
23800	onAdd(map) {
23801		this._map = map;
23802		this._compact = this.options.compact;
23803		this._container = DOM.create("details", "maplibregl-ctrl maplibregl-ctrl-attrib");
23804		this._compactButton = DOM.create("summary", "maplibregl-ctrl-attrib-button", this._container);
23805		this._compactButton.addEventListener("click", this._toggleAttribution);
23806		this._setElementTitle(this._compactButton, "ToggleAttribution");
23807		this._innerContainer = DOM.create("div", "maplibregl-ctrl-attrib-inner", this._container);
23808		this._updateAttributions();
23809		this._updateCompact();
23810		this._map.on("styledata", this._updateData);
23811		this._map.on("sourcedata", this._updateData);
23812		this._map.on("terrain", this._updateData);
23813		this._map.on("resize", this._updateCompact);
23814		this._map.on("drag", this._updateCompactMinimize);
23815		return this._container;
23816	}
23817	/** {@inheritDoc IControl.onRemove} */
23818	onRemove() {
23819		this._container.remove();
23820		this._map.off("styledata", this._updateData);
23821		this._map.off("sourcedata", this._updateData);
23822		this._map.off("terrain", this._updateData);
23823		this._map.off("resize", this._updateCompact);
23824		this._map.off("drag", this._updateCompactMinimize);
23825		this._map = void 0;
23826		this._compact = void 0;
23827		this._attribHTML = void 0;
23828	}
23829	_setElementTitle(element, title) {
23830		const str = this._map._getUIString(`AttributionControl.${title}`);
23831		element.title = str;
23832		element.setAttribute("aria-label", str);
23833	}
23834	_updateAttributions() {
23835		if (!this._map.style) return;
23836		let attributions = [];
23837		if (this.options.customAttribution) {
23838			if (Array.isArray(this.options.customAttribution)) attributions = attributions.concat(this.options.customAttribution.map((attribution) => {
23839				if (typeof attribution !== "string") return "";
23840				return attribution;
23841			}));
23842			else if (typeof this.options.customAttribution === "string") attributions.push(this.options.customAttribution);
23843		}
23844		if (this._map.style.stylesheet) {
23845			const stylesheet = this._map.style.stylesheet;
23846			this.styleOwner = stylesheet.owner;
23847			this.styleId = stylesheet.id;
23848		}
23849		const tileManagers = this._map.style.tileManagers;
23850		for (const id in tileManagers) {
23851			const tileManager = tileManagers[id];
23852			if (tileManager.used || tileManager.usedForTerrain) {
23853				const source = tileManager.getSource();
23854				if (source.attribution && !attributions.includes(source.attribution)) attributions.push(source.attribution);
23855			}
23856		}
23857		attributions = attributions.filter((e) => String(e).trim());
23858		attributions.sort((a, b) => a.length - b.length);
23859		attributions = attributions.filter((attrib, i) => {
23860			for (let j = i + 1; j < attributions.length; j++) if (attributions[j].includes(attrib)) return false;
23861			return true;
23862		});
23863		const attribHTML = attributions.join(" | ");
23864		if (attribHTML === this._attribHTML) return;
23865		this._attribHTML = attribHTML;
23866		if (attributions.length) {
23867			this._innerContainer.replaceChildren(DOM.sanitize(attribHTML));
23868			this._container.classList.remove("maplibregl-attrib-empty");
23869		} else this._container.classList.add("maplibregl-attrib-empty");
23870		this._updateCompact();
23871		this._editLink = null;
23872	}
23873};
23874//#endregion
23875//#region src/ui/control/logo_control.ts
23876/**
23877* A `LogoControl` is a control that adds the watermark.
23878*
23879* @group Markers and Controls
23880*
23881* @example
23882* ```ts
23883* map.addControl(new LogoControl({compact: false}));
23884* ```
23885**/
23886var LogoControl = class {
23887	/**
23888	* @param options - the control's options
23889	*/
23890	constructor(options = {}) {
23891		this._updateCompact = () => {
23892			const containerChildren = this._container.children;
23893			if (containerChildren.length) {
23894				const anchor = containerChildren[0];
23895				if (this._map.getCanvasContainer().offsetWidth <= 640 || this._compact) {
23896					if (this._compact !== false) anchor.classList.add("maplibregl-compact");
23897				} else anchor.classList.remove("maplibregl-compact");
23898			}
23899		};
23900		this.options = options;
23901	}
23902	getDefaultPosition() {
23903		return "bottom-left";
23904	}
23905	/** {@inheritDoc IControl.onAdd} */
23906	onAdd(map) {
23907		this._map = map;
23908		this._compact = this.options?.compact;
23909		this._container = DOM.create("div", "maplibregl-ctrl");
23910		const anchor = DOM.create("a", "maplibregl-ctrl-logo");
23911		anchor.target = "_blank";
23912		anchor.rel = "noopener nofollow";
23913		anchor.href = "https://maplibre.org/";
23914		anchor.setAttribute("aria-label", this._map._getUIString("LogoControl.Title"));
23915		anchor.setAttribute("rel", "noopener nofollow");
23916		this._container.appendChild(anchor);
23917		this._container.style.display = "block";
23918		this._map.on("resize", this._updateCompact);
23919		this._updateCompact();
23920		return this._container;
23921	}
23922	/** {@inheritDoc IControl.onRemove} */
23923	onRemove() {
23924		this._container.remove();
23925		this._map.off("resize", this._updateCompact);
23926		this._map = void 0;
23927		this._compact = void 0;
23928	}
23929};
23930//#endregion
23931//#region src/util/task_queue.ts
23932var TaskQueue = class {
23933	constructor() {
23934		this._queue = [];
23935		this._id = 0;
23936		this._cleared = false;
23937		this._currentlyRunning = false;
23938	}
23939	add(callback) {
23940		const id = ++this._id;
23941		this._queue.push({
23942			callback,
23943			id,
23944			cancelled: false
23945		});
23946		return id;
23947	}
23948	remove(id) {
23949		const running = this._currentlyRunning;
23950		const queue = running ? this._queue.concat(running) : this._queue;
23951		for (const task of queue) if (task.id === id) {
23952			task.cancelled = true;
23953			return;
23954		}
23955	}
23956	run(timeStamp = 0) {
23957		if (this._currentlyRunning) throw new Error("Attempting to run(), but is already running.");
23958		const queue = this._currentlyRunning = this._queue;
23959		this._queue = [];
23960		let failed = false;
23961		let firstError;
23962		for (const task of queue) {
23963			if (task.cancelled) continue;
23964			try {
23965				task.callback(timeStamp);
23966			} catch (error) {
23967				if (!failed) {
23968					failed = true;
23969					firstError = error;
23970				}
23971			}
23972			if (this._cleared) break;
23973		}
23974		this._cleared = false;
23975		this._currentlyRunning = false;
23976		if (failed) throw firstError;
23977	}
23978	clear() {
23979		if (this._currentlyRunning) this._cleared = true;
23980		this._queue = [];
23981	}
23982};
23983//#endregion
23984//#region src/webgl/rtt_fingerprint.ts
23985/**
23986* What a render-to-texture tile's textures differ in from the state this frame would render them from,
23987* least to most severe: `zoom` (the same layers and source tiles, evaluated at another zoom), `sourceTiles`
23988* (other source tiles under the terrain tile), `visibleLayers` (a layer entered or left its zoom range) and
23989* `revision` (the source data changed, or the textures were rendered without this source).
23990*
23991* @internal
23992*/
23993const RTT_DIFFERENCES = [
23994	"none",
23995	"zoom",
23996	"sourceTiles",
23997	"visibleLayers",
23998	"revision"
23999];
24000/**
24001* Immutable value describing the state a render-to-texture tile's textures were rendered from: the source
24002* tiles drawn into them, the source data revision, the map zoom (zoom-dependent style properties are
24003* evaluated then) and the ids of the layers visible at that zoom.
24004*
24005* @internal
24006*/
24007var RTTFingerprint = class {
24008	constructor(coords, revision, zoom, visibleLayerIds) {
24009		this._tileKeys = coords.map((c) => c.key).sort().join();
24010		this._revision = revision;
24011		this._zoom = zoom;
24012		this._visibleLayerIds = visibleLayerIds;
24013	}
24014	/**
24015	* The most severe difference between this fingerprint and the one the textures were rendered from,
24016	* `none` when they match. A missing fingerprint counts as a revision difference.
24017	*/
24018	difference(other) {
24019		if (this._revision !== other?._revision) return "revision";
24020		if (this._visibleLayerIds !== other._visibleLayerIds) return "visibleLayers";
24021		if (this._tileKeys !== other._tileKeys) return "sourceTiles";
24022		return this._zoom === other._zoom ? "none" : "zoom";
24023	}
24024};
24025//#endregion
24026//#region src/webgl/render_to_texture.ts
24027/**
24028* lookup table which layers should rendered to texture
24029*/
24030const LAYERS_TO_TEXTURES = {
24031	background: true,
24032	fill: true,
24033	line: true,
24034	raster: true,
24035	hillshade: true,
24036	"color-relief": true
24037};
24038/**
24039* @internal
24040* Renders RTT-eligible layers into per-tile cached textures, then drapes
24041* them onto the terrain mesh. Slots live on each Tile so their lifetime
24042* tracks the tile itself; the underlying FBO+texture handles are recycled
24043* via the painter's RTT pool.
24044*/
24045var RenderToTexture = class {
24046	constructor(painter, terrain) {
24047		this.needsFollowUpFrame = false;
24048		this.painter = painter;
24049		this.terrain = terrain;
24050		this.rttSize = terrain.tileManager.tileSize * terrain.qualityFactor;
24051	}
24052	getTexture(tile) {
24053		return tile.getRTT(this._stacks.length - 1).texture;
24054	}
24055	/**
24056	* Collects the frame's tiles, layers and source fingerprints and releases the textures that need
24057	* re-rendering. Textures that differ only by zoom are kept while the zoom is changing or the map is moving
24058	* and re-rendered one tile per frame once it has stopped, nearest to the camera first; textures with other
24059	* source tiles under them are re-rendered one per frame too, `needsFollowUpFrame` bringing in the rest.
24060	* Textures whose visible layer set changed (a layer entered or left its zoom range) are kept while the zoom
24061	* is changing and then all re-rendered in the same frame, since a tile-by-tile change would show; a source
24062	* data change re-renders immediately.
24063	*/
24064	prepareForRender(style, zoom) {
24065		const zoomChanged = zoom !== this._lastPrepareZoom;
24066		this._lastPrepareZoom = zoom;
24067		this._stacks = [];
24068		this._prevType = null;
24069		this._rttTiles = [];
24070		this._renderableTiles = this.terrain.tileManager.getRenderableTiles();
24071		this._renderableLayerIds = style._order.filter((id) => !style._layers[id].isHidden(zoom));
24072		const visibleLayerIds = this._renderableLayerIds.join();
24073		const rttSourceIds = /* @__PURE__ */ new Set();
24074		for (const layerId of this._renderableLayerIds) {
24075			const layer = style._layers[layerId];
24076			const source = layer.source;
24077			if (source && LAYERS_TO_TEXTURES[layer.type]) rttSourceIds.add(source);
24078		}
24079		this._coordsAscending = {};
24080		this._rttFingerprints = {};
24081		for (const sourceId of rttSourceIds) {
24082			const tileManager = style.tileManagers[sourceId];
24083			if (!tileManager) continue;
24084			this._coordsAscending[sourceId] = {};
24085			const coordsAscending = this._coordsAscending[sourceId];
24086			const source = tileManager.getSource();
24087			const terrainTileRanges = source instanceof ImageSource ? source.terrainTileRanges : null;
24088			for (const tileID of tileManager.getVisibleCoordinates()) {
24089				const keys = this.terrain.tileManager.getTerrainCoords(tileID, terrainTileRanges);
24090				for (const key in keys) {
24091					coordsAscending[key] ||= [];
24092					coordsAscending[key].push(keys[key]);
24093				}
24094			}
24095			this._rttFingerprints[sourceId] = {};
24096			const fingerprints = this._rttFingerprints[sourceId];
24097			const revision = tileManager.getState().revision;
24098			for (const key in coordsAscending) fingerprints[key] = new RTTFingerprint(coordsAscending[key], revision, zoom, visibleLayerIds);
24099		}
24100		this.needsFollowUpFrame = false;
24101		const moving = zoomChanged || this.painter.options.moving;
24102		let staleTileReleased = false;
24103		for (const tile of this._renderableTiles) {
24104			const difference = this._textureDifference(tile);
24105			if (difference === "none") continue;
24106			if (difference === "zoom" && moving || difference === "visibleLayers" && zoomChanged) this.needsFollowUpFrame = true;
24107			else if (difference === "zoom" || difference === "sourceTiles") {
24108				if (staleTileReleased) this.needsFollowUpFrame = true;
24109				else tile.releaseRTT(this.painter);
24110				staleTileReleased = true;
24111			} else tile.releaseRTT(this.painter);
24112		}
24113	}
24114	/**
24115	* The most severe difference, over the sources rendered to texture, between the tile's cached textures
24116	* and what this frame would render into them.
24117	*/
24118	_textureDifference(tile) {
24119		let worst = "none";
24120		for (const source in this._rttFingerprints) {
24121			const frameFingerprint = this._rttFingerprints[source][tile.tileID.key];
24122			if (!frameFingerprint) continue;
24123			const difference = frameFingerprint.difference(tile.rttFingerprint[source]);
24124			if (RTT_DIFFERENCES.indexOf(difference) > RTT_DIFFERENCES.indexOf(worst)) worst = difference;
24125		}
24126		return worst;
24127	}
24128	/**
24129	* due that switching textures is relatively slow, the render
24130	* layer-by-layer context is not practicable. To bypass this problem
24131	* this lines of code stack all layers and later render all at once.
24132	* Because of the stylesheet possibility to mixing render-to-texture layers
24133	* and 'live'-layers (f.e. symbols) it is necessary to create more stacks. For example
24134	* a symbol-layer is in between of fill-layers.
24135	* @param layer - the layer to render
24136	* @param renderContext - shared state for the current render
24137	* @returns if true layer is rendered to texture, otherwise false
24138	*/
24139	renderLayer(layer, renderContext) {
24140		if (layer.isHidden(this.painter.transform.zoom)) return false;
24141		const type = layer.type;
24142		const painter = this.painter;
24143		const isLastLayer = this._renderableLayerIds[this._renderableLayerIds.length - 1] === layer.id;
24144		if (LAYERS_TO_TEXTURES[type]) {
24145			if (!this._prevType || !LAYERS_TO_TEXTURES[this._prevType]) this._stacks.push([]);
24146			this._prevType = type;
24147			this._stacks[this._stacks.length - 1].push(layer.id);
24148			if (!isLastLayer) return true;
24149		}
24150		if (LAYERS_TO_TEXTURES[this._prevType] || LAYERS_TO_TEXTURES[type] && isLastLayer) {
24151			this._prevType = type;
24152			const stack = this._stacks.length - 1, layers = this._stacks[stack] || [];
24153			renderContext.isRenderingToTexture = true;
24154			for (const tile of this._renderableTiles) {
24155				this._rttTiles.push(tile);
24156				if (tile.getRTT(stack)) continue;
24157				const obj = tile.acquireRTT(painter, stack, this.rttSize);
24158				painter.bindRTT(obj);
24159				painter.context.clear({
24160					color: Color.transparent,
24161					stencil: 0
24162				});
24163				painter.currentStencilSource = void 0;
24164				for (const layerId of layers) {
24165					const layer = painter.style._layers[layerId];
24166					const coords = layer.source ? this._coordsAscending[layer.source][tile.tileID.key] : [tile.tileID];
24167					painter.context.viewport.set([
24168						0,
24169						0,
24170						this.rttSize,
24171						this.rttSize
24172					]);
24173					painter.renderTileClippingMasks(layer, coords);
24174					painter.renderLayer(painter, painter.style.tileManagers[layer.source], layer, coords, renderContext);
24175					if (layer.source) tile.rttFingerprint[layer.source] = this._rttFingerprints[layer.source][tile.tileID.key];
24176				}
24177				obj.texture.generateMipmap();
24178			}
24179			renderContext.isRenderingToTexture = false;
24180			drawTerrain(this.painter, this.terrain, this._rttTiles, renderContext);
24181			this._rttTiles = [];
24182			return LAYERS_TO_TEXTURES[type];
24183		}
24184		return false;
24185	}
24186};
24187//#endregion
24188//#region src/ui/default_locale.ts
24189const defaultLocale = {
24190	"AttributionControl.ToggleAttribution": "Toggle attribution",
24191	"AttributionControl.MapFeedback": "Map feedback",
24192	"FullscreenControl.Enter": "Enter fullscreen",
24193	"FullscreenControl.Exit": "Exit fullscreen",
24194	"GeolocateControl.FindMyLocation": "Find my location",
24195	"GeolocateControl.LocationNotAvailable": "Location not available",
24196	"LogoControl.Title": "MapLibre logo",
24197	"Map.Title": "Map",
24198	"Marker.Title": "Map marker",
24199	"NavigationControl.ResetBearing": "Drag to rotate map, click to reset north",
24200	"NavigationControl.ZoomIn": "Zoom in",
24201	"NavigationControl.ZoomOut": "Zoom out",
24202	"Popup.Close": "Close popup",
24203	"ScaleControl.Feet": "ft",
24204	"ScaleControl.Meters": "m",
24205	"ScaleControl.Kilometers": "km",
24206	"ScaleControl.Miles": "mi",
24207	"ScaleControl.NauticalMiles": "nm",
24208	"GlobeControl.Enable": "Enable globe",
24209	"GlobeControl.Disable": "Disable globe",
24210	"TerrainControl.Enable": "Enable terrain",
24211	"TerrainControl.Disable": "Disable terrain",
24212	"CooperativeGesturesHandler.WindowsHelpText": "Use Ctrl + scroll to zoom the map",
24213	"CooperativeGesturesHandler.MacHelpText": "Use ⌘ + scroll to zoom the map",
24214	"CooperativeGesturesHandler.MobileHelpText": "Use two fingers to move the map"
24215};
24216//#endregion
24217//#region src/ui/map.ts
24218const version$1 = version$2;
24219const defaultMinZoom = -2;
24220const defaultMaxZoom = 22;
24221const defaultMinPitch = 0;
24222const defaultMaxPitch = 60;
24223const defaultAnisotropicFilterPitch = 20;
24224const maxPitchThreshold = 180;
24225const defaultOptions$4 = {
24226	hash: false,
24227	interactive: true,
24228	bearingSnap: 7,
24229	zoomSnap: 0,
24230	attributionControl: defaultAttributionControlOptions,
24231	maplibreLogo: false,
24232	refreshExpiredTiles: true,
24233	canvasContextAttributes: {
24234		antialias: false,
24235		preserveDrawingBuffer: false,
24236		powerPreference: "high-performance",
24237		failIfMajorPerformanceCaveat: false,
24238		desynchronized: false,
24239		contextType: void 0
24240	},
24241	scrollZoom: true,
24242	minZoom: defaultMinZoom,
24243	maxZoom: defaultMaxZoom,
24244	minPitch: defaultMinPitch,
24245	maxPitch: defaultMaxPitch,
24246	boxZoom: true,
24247	dragRotate: true,
24248	dragPan: true,
24249	keyboard: true,
24250	doubleClickZoom: true,
24251	touchZoomRotate: true,
24252	touchPitch: true,
24253	cooperativeGestures: false,
24254	trackResize: true,
24255	center: [0, 0],
24256	elevation: 0,
24257	zoom: 0,
24258	bearing: 0,
24259	pitch: 0,
24260	roll: 0,
24261	renderWorldCopies: true,
24262	maxTileCacheSize: null,
24263	maxTileCacheZoomLevels: config.MAX_TILE_CACHE_ZOOM_LEVELS,
24264	transformRequest: null,
24265	transformCameraUpdate: null,
24266	transformConstrain: null,
24267	fadeDuration: 300,
24268	crossSourceCollisions: true,
24269	clickTolerance: 3,
24270	localIdeographFontFamily: "sans-serif",
24271	pitchWithRotate: true,
24272	rollEnabled: false,
24273	rotateSpeed: .8,
24274	pitchSpeed: -.5,
24275	reduceMotion: void 0,
24276	validateStyle: true,
24277	/**Because GL MAX_TEXTURE_SIZE is usually at least 4096px. */
24278	maxCanvasSize: [4096, 4096],
24279	cancelPendingTileRequestsWhileZooming: true,
24280	centerClampedToGround: true,
24281	terrainSkirtLength: "auto",
24282	zoomLevelsToOverscale: 4,
24283	anisotropicFilterPitch: defaultAnisotropicFilterPitch
24284};
24285/**
24286* The `Map` object represents the map on your page. It exposes methods
24287* and properties that enable you to programmatically change the map,
24288* and fires events as users interact with it.
24289*
24290* You create a `Map` by specifying a `container` and other options, see {@link MapOptions} for the full list.
24291* Then MapLibre GL JS initializes the map on the page and returns your `Map` object.
24292*
24293* @group Main
24294*
24295* @example
24296* ```ts
24297* let map = new Map({
24298*   container: 'map',
24299*   center: [-122.420679, 37.772537],
24300*   zoom: 13,
24301*   style: style_object,
24302*   hash: true,
24303*   transformRequest: (url, resourceType)=> {
24304*     if(resourceType === 'Source' && url.startsWith('http://myHost')) {
24305*       return {
24306*        url: url.replace('http', 'https'),
24307*        headers: { 'my-custom-header': true},
24308*        credentials: 'include'  // Include cookies for cross-origin requests
24309*      }
24310*     }
24311*   }
24312* });
24313* ```
24314* @see [Display a map](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-map/)
24315*/
24316var Map$1 = class extends Evented {
24317	/**
24318	* @internal
24319	* The window that owns the map container element, for cross-window support.
24320	* Returns `typeof window` to include global constructors like ResizeObserver.
24321	*/
24322	get _ownerWindow() {
24323		return this._container?.ownerDocument?.defaultView || window;
24324	}
24325	constructor(options) {
24326		super();
24327		this._idleTriggered = false;
24328		this._crossFadingFactor = 1;
24329		this._renderTaskQueue = new TaskQueue();
24330		this._controls = [];
24331		this._mapId = uniqueId();
24332		this._styleUrl = null;
24333		this._missingStyleImageResolver = null;
24334		this._lostContextStyle = {
24335			style: null,
24336			images: null
24337		};
24338		this._contextLost = (event) => {
24339			event.preventDefault();
24340			if (this._frameRequest) {
24341				this._frameRequest.abort();
24342				this._frameRequest = null;
24343			}
24344			this.painter.destroy();
24345			this._lostContextStyle = this._getStyleAndImages();
24346			if (!this.style) {
24347				this.fire(new MapContextEvent("webglcontextlost", { originalEvent: event }));
24348				return;
24349			}
24350			for (const layer of Object.values(this.style._layers)) {
24351				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.`);
24352				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.`);
24353			}
24354			this.style.destroy();
24355			this.style = null;
24356			this.fire(new MapContextEvent("webglcontextlost", { originalEvent: event }));
24357		};
24358		this._contextRestored = (event) => {
24359			if (this._lostContextStyle.style) this.setStyle(this._lostContextStyle.style, { diff: false });
24360			if (this._lostContextStyle.images && this.style) {
24361				this.style.imageManager.images = this._lostContextStyle.images;
24362				for (const id in this._lostContextStyle.images) {
24363					const image = this._lostContextStyle.images[id];
24364					if (image.isWebGLImage) this.style.imageManager.updateImage(id, image, false);
24365				}
24366			}
24367			this._lostContextStyle = {
24368				style: null,
24369				images: null
24370			};
24371			try {
24372				this._setupPainter();
24373			} catch (error) {
24374				this.fire(new ErrorEvent(error));
24375				return;
24376			}
24377			this.resize();
24378			this._update();
24379			this._resizeInternal();
24380			this.fire(new MapContextEvent("webglcontextrestored", { originalEvent: event }));
24381		};
24382		this._onMapScroll = (event) => {
24383			if (event.target !== this._container) return;
24384			this._container.scrollTop = 0;
24385			this._container.scrollLeft = 0;
24386			return false;
24387		};
24388		this._onWindowOnline = () => {
24389			this._update();
24390		};
24391		const resolvedOptions = {
24392			...defaultOptions$4,
24393			...options,
24394			canvasContextAttributes: {
24395				...defaultOptions$4.canvasContextAttributes,
24396				...options.canvasContextAttributes
24397			}
24398		};
24399		if (resolvedOptions.minZoom != null && resolvedOptions.maxZoom != null && resolvedOptions.minZoom > resolvedOptions.maxZoom) throw new Error("maxZoom must be greater than or equal to minZoom");
24400		if (resolvedOptions.minPitch != null && resolvedOptions.maxPitch != null && resolvedOptions.minPitch > resolvedOptions.maxPitch) throw new Error("maxPitch must be greater than or equal to minPitch");
24401		if (resolvedOptions.minPitch != null && resolvedOptions.minPitch < defaultMinPitch) throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`);
24402		if (resolvedOptions.maxPitch != null && resolvedOptions.maxPitch > maxPitchThreshold) throw new Error(`maxPitch must be less than or equal to ${maxPitchThreshold}`);
24403		this._camera = new Camera({
24404			minZoom: resolvedOptions.minZoom,
24405			maxZoom: resolvedOptions.maxZoom,
24406			minPitch: resolvedOptions.minPitch,
24407			maxPitch: resolvedOptions.maxPitch,
24408			bearingSnap: resolvedOptions.bearingSnap,
24409			zoomSnap: resolvedOptions.zoomSnap,
24410			renderWorldCopies: resolvedOptions.renderWorldCopies,
24411			centerClampedToGround: resolvedOptions.centerClampedToGround,
24412			terrain: this.terrain,
24413			transformConstrain: resolvedOptions.transformConstrain,
24414			requestRenderFrame: (callback) => this._requestRenderFrame(callback),
24415			cancelRenderFrame: (id) => this._cancelRenderFrame(id),
24416			transformCameraUpdate: resolvedOptions.transformCameraUpdate,
24417			stopHandlers: () => this._handlers?.stop(false)
24418		});
24419		this._camera.setEventedParent(this);
24420		this._interactive = resolvedOptions.interactive;
24421		this._maxTileCacheSize = resolvedOptions.maxTileCacheSize;
24422		this._maxTileCacheZoomLevels = resolvedOptions.maxTileCacheZoomLevels;
24423		this._canvasContextAttributes = { ...resolvedOptions.canvasContextAttributes };
24424		this._trackResize = resolvedOptions.trackResize === true;
24425		this._terrainSkirtLength = resolvedOptions.terrainSkirtLength;
24426		this._refreshExpiredTiles = resolvedOptions.refreshExpiredTiles === true;
24427		this._fadeDuration = resolvedOptions.fadeDuration;
24428		this._crossSourceCollisions = resolvedOptions.crossSourceCollisions === true;
24429		this._collectResourceTiming = resolvedOptions.collectResourceTiming === true;
24430		this._locale = {
24431			...defaultLocale,
24432			...resolvedOptions.locale
24433		};
24434		this._clickTolerance = resolvedOptions.clickTolerance;
24435		this._overridePixelRatio = resolvedOptions.pixelRatio;
24436		this._maxCanvasSize = resolvedOptions.maxCanvasSize;
24437		this._zoomLevelsToOverscale = resolvedOptions.zoomLevelsToOverscale;
24438		this.cancelPendingTileRequestsWhileZooming = resolvedOptions.cancelPendingTileRequestsWhileZooming === true;
24439		this.setAnisotropicFilterPitch(resolvedOptions.anisotropicFilterPitch);
24440		if (resolvedOptions.reduceMotion !== void 0) browser.prefersReducedMotion = resolvedOptions.reduceMotion;
24441		this._requestManager = new RequestManager(resolvedOptions.transformRequest);
24442		this._container = this._resolveContainer(resolvedOptions.container);
24443		if (resolvedOptions.maxBounds) this.setMaxBounds(resolvedOptions.maxBounds);
24444		this._setupContainer();
24445		try {
24446			this._setupPainter();
24447		} catch (error) {
24448			this._cleanupContainer();
24449			throw error;
24450		}
24451		this._imageQueueHandle = ImageRequest.addThrottleControl(() => this.isMoving());
24452		this.on("move", () => this._update(false));
24453		this.on("moveend", () => this._update(false));
24454		this.on("zoom", () => this._update(true));
24455		this.on("terrain", () => {
24456			this.painter.markTerrainDepthDirty();
24457			this._update(true);
24458		});
24459		this.once("idle", () => this._idleTriggered = true);
24460		this._handlers = new HandlerManager(this, this._camera, resolvedOptions);
24461		if (typeof window !== "undefined") {
24462			this._ownerWindow.addEventListener("online", this._onWindowOnline, false);
24463			this._setupResizeObserver();
24464		}
24465		const hashName = typeof resolvedOptions.hash === "string" && resolvedOptions.hash || void 0;
24466		this._hash = resolvedOptions.hash ? new Hash(hashName).addTo(this) : void 0;
24467		if (!this._hash?._onHashChange()) {
24468			this.jumpTo({
24469				center: resolvedOptions.center,
24470				elevation: resolvedOptions.elevation,
24471				zoom: resolvedOptions.zoom,
24472				bearing: resolvedOptions.bearing,
24473				pitch: resolvedOptions.pitch,
24474				roll: resolvedOptions.roll
24475			});
24476			if (resolvedOptions.bounds) {
24477				this.resize();
24478				this.fitBounds(resolvedOptions.bounds, extend({}, resolvedOptions.fitBoundsOptions, { duration: 0 }));
24479			}
24480		}
24481		const shouldConstrainUsingMercatorTransform = typeof resolvedOptions.style === "string" || !(resolvedOptions.style?.projection?.type === "globe");
24482		this.resize(null, shouldConstrainUsingMercatorTransform);
24483		this._localIdeographFontFamily = resolvedOptions.localIdeographFontFamily;
24484		this._validateStyle = resolvedOptions.validateStyle;
24485		if (resolvedOptions.style) this.setStyle(resolvedOptions.style, { localIdeographFontFamily: resolvedOptions.localIdeographFontFamily });
24486		if (resolvedOptions.attributionControl) this.addControl(new AttributionControl(typeof resolvedOptions.attributionControl === "boolean" ? void 0 : resolvedOptions.attributionControl));
24487		if (resolvedOptions.maplibreLogo) this.addControl(new LogoControl(), resolvedOptions.logoPosition);
24488		this.on("style.load", () => {
24489			if (!shouldConstrainUsingMercatorTransform) this._resizeTransform();
24490			if (this._camera.transform.unmodified) {
24491				const coercedOptions = pick(this.style.stylesheet, [
24492					"center",
24493					"zoom",
24494					"bearing",
24495					"pitch",
24496					"roll"
24497				]);
24498				this.jumpTo(coercedOptions);
24499			}
24500		});
24501		this.on("data", (event) => {
24502			this._update(event.dataType === "style");
24503			this.fire(event.dataType === "style" ? new MapStyleDataEvent("styledata", event) : new MapSourceDataEvent("sourcedata", event));
24504		});
24505		this.on("dataloading", (event) => {
24506			this.fire(event.dataType === "style" ? new MapStyleDataEvent("styledataloading", event) : new MapSourceDataEvent("sourcedataloading", event));
24507		});
24508		this.on("dataabort", (event) => {
24509			this.fire(new MapSourceDataEvent("sourcedataabort", event));
24510		});
24511	}
24512	/**
24513	* @internal
24514	* Returns a unique number for this map instance which is used for the MapLoadEvent
24515	* to make sure we only fire one event per instantiated map object.
24516	* @returns the uniq map ID
24517	*/
24518	_getMapId() {
24519		return this._mapId;
24520	}
24521	/**
24522	* Sets a global state property that can be retrieved with the [`global-state` expression](https://maplibre.org/maplibre-style-spec/expressions/#global-state).
24523	* 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).
24524	*
24525	* @param propertyName - The name of the state property to set.
24526	* @param value - The value of the state property to set.
24527	*/
24528	setGlobalStateProperty(propertyName, value) {
24529		this.style.setGlobalStateProperty(propertyName, value);
24530		return this._update(true);
24531	}
24532	/**
24533	* Returns the global map state
24534	*
24535	* @returns The map state object.
24536	*/
24537	getGlobalState() {
24538		return this.style.getGlobalState();
24539	}
24540	/**
24541	* Adds an {@link IControl} to the map, calling `control.onAdd(this)`.
24542	*
24543	* An {@link ErrorEvent} will be fired if the control is invalid.
24544	*
24545	* @param control - The {@link IControl} to add.
24546	* @param position - position on the map to which the control will be added.
24547	* Valid values are `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. Defaults to `'top-right'`.
24548	* @example
24549	* Add zoom and rotation controls to the map.
24550	* ```ts
24551	* map.addControl(new NavigationControl());
24552	* ```
24553	* @see [Display map navigation controls](https://maplibre.org/maplibre-gl-js/docs/examples/display-map-navigation-controls/)
24554	*/
24555	addControl(control, position) {
24556		if (position === void 0) {
24557			if (control.getDefaultPosition) position = control.getDefaultPosition();
24558			else position = "top-right";
24559		}
24560		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.")));
24561		const controlElement = control.onAdd(this);
24562		this._controls.push(control);
24563		const positionContainer = this._controlPositions[position];
24564		if (position.includes("bottom")) positionContainer.insertBefore(controlElement, positionContainer.firstChild);
24565		else positionContainer.appendChild(controlElement);
24566		return this;
24567	}
24568	/**
24569	* Removes the control from the map.
24570	*
24571	* An {@link ErrorEvent} will be fired if the control is invalid.
24572	*
24573	* @param control - The {@link IControl} to remove.
24574	* @example
24575	* ```ts
24576	* // Define a new navigation control.
24577	* let navigation = new NavigationControl();
24578	* // Add zoom and rotation controls to the map.
24579	* map.addControl(navigation);
24580	* // Remove zoom and rotation controls from the map.
24581	* map.removeControl(navigation);
24582	* ```
24583	*/
24584	removeControl(control) {
24585		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.")));
24586		const ci = this._controls.indexOf(control);
24587		if (ci > -1) this._controls.splice(ci, 1);
24588		control.onRemove(this);
24589		return this;
24590	}
24591	/**
24592	* Checks if a control exists on the map.
24593	*
24594	* @param control - The {@link IControl} to check.
24595	* @returns true if map contains control.
24596	* @example
24597	* ```ts
24598	* // Define a new navigation control.
24599	* let navigation = new NavigationControl();
24600	* // Add zoom and rotation controls to the map.
24601	* map.addControl(navigation);
24602	* // Check that the navigation control exists on the map.
24603	* map.hasControl(navigation);
24604	* ```
24605	*/
24606	hasControl(control) {
24607		return this._controls.includes(control);
24608	}
24609	/**
24610	* Returns an array of `OverscaledTileID` objects that cover the current viewport for a given tile size.
24611	* This method is useful for determining which tiles are visible in the current viewport.
24612	*
24613	* @param options - Options for calculating the covering tiles.
24614	* @returns An array of `OverscaledTileID` objects.
24615	* @example
24616	* ```ts
24617	* // Get the tiles to cover the view for a 512x512px tile source
24618	* const tiles = map.coveringTiles({tileSize: 512});
24619	* ```
24620	*/
24621	coveringTiles(options) {
24622		return coveringTiles(this._camera.transform, options);
24623	}
24624	/**
24625	* Sets the callback used to defer camera updates or apply arbitrary constraints.
24626	* If specified, this Camera instance can be used as a stateless component in React etc.
24627	*/
24628	setTransformCameraUpdate(value) {
24629		this._camera.transformCameraUpdate = value;
24630	}
24631	/**
24632	* Returns the map's geographical centerpoint.
24633	*
24634	* @returns The map's geographical centerpoint.
24635	* @example
24636	* Return a LngLat object such as `{lng: 0, lat: 0}`
24637	* ```ts
24638	* let center = map.getCenter();
24639	* // access longitude and latitude values directly
24640	* let {lng, lat} = map.getCenter();
24641	* ```
24642	*/
24643	getCenter() {
24644		return new LngLat(this._camera.transform.center.lng, this._camera.transform.center.lat);
24645	}
24646	/**
24647	* Sets the map's geographical centerpoint. Equivalent to `jumpTo({center: center})`.
24648	*
24649	* Triggers the following events: `movestart` and `moveend`.
24650	*
24651	* @param center - The centerpoint to set.
24652	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24653	* @example
24654	* ```ts
24655	* map.setCenter([-74, 38]);
24656	* ```
24657	*/
24658	setCenter(center, eventData) {
24659		this._camera.setCenter(center, eventData);
24660		return this;
24661	}
24662	/**
24663	* Returns the elevation of the map's center point.
24664	*
24665	* @returns The elevation of the map's center point, in meters above sea level.
24666	*/
24667	getCenterElevation() {
24668		return this._camera.transform.elevation;
24669	}
24670	/**
24671	* Sets the elevation of the map's center point, in meters above sea level. Equivalent to `jumpTo({elevation: elevation})`.
24672	*
24673	* Triggers the following events: `movestart` and `moveend`.
24674	*
24675	* @param elevation - The elevation to set, in meters above sea level.
24676	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24677	*/
24678	setCenterElevation(elevation, eventData) {
24679		this._camera.setCenterElevation(elevation, eventData);
24680		return this;
24681	}
24682	/**
24683	* Sets the value of `centerClampedToGround`.
24684	*
24685	* If true, the elevation of the center point will automatically be set to the terrain elevation
24686	* (or zero if terrain is not enabled). If false, the elevation of the center point will default
24687	* to sea level and will not automatically update. Defaults to true. Needs to be set to false to
24688	* keep the camera above ground when pitch \> 90 degrees.
24689	*/
24690	setCenterClampedToGround(centerClampedToGround) {
24691		this._camera.setCenterClampedToGround(centerClampedToGround);
24692	}
24693	/**
24694	* Pans the map by the specified offset.
24695	*
24696	* Triggers the following events: `movestart` and `moveend`.
24697	*
24698	* @param offset - `x` and `y` coordinates by which to pan the map.
24699	* @param options - Options object
24700	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24701	* @see [Navigate the map with game-like controls](https://maplibre.org/maplibre-gl-js/docs/examples/navigate-the-map-with-game-like-controls/)
24702	*/
24703	panBy(offset, options, eventData) {
24704		this._camera.panBy(offset, options, eventData);
24705		return this;
24706	}
24707	/**
24708	* Pans the map to the specified location with an animated transition.
24709	*
24710	* Triggers the following events: `movestart` and `moveend`.
24711	*
24712	* @param lnglat - The location to pan the map to.
24713	* @param options - Options describing the destination and animation of the transition.
24714	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24715	* @example
24716	* ```ts
24717	* map.panTo([-74, 38]);
24718	* // Specify that the panTo animation should last 5000 milliseconds.
24719	* map.panTo([-74, 38], {duration: 5000});
24720	* ```
24721	* @see [Update a feature in realtime](https://maplibre.org/maplibre-gl-js/docs/examples/update-a-feature-in-realtime/)
24722	*/
24723	panTo(lnglat, options, eventData) {
24724		this._camera.panTo(lnglat, options, eventData);
24725		return this;
24726	}
24727	/**
24728	* Returns the map's current zoom level.
24729	*
24730	* @returns The map's current zoom level.
24731	* @example
24732	* ```ts
24733	* map.getZoom();
24734	* ```
24735	*/
24736	getZoom() {
24737		return this._camera.transform.zoom;
24738	}
24739	/**
24740	* Sets the map's zoom level. Equivalent to `jumpTo({zoom: zoom})`.
24741	*
24742	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`.
24743	*
24744	* @param zoom - The zoom level to set (0-20).
24745	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24746	* @example
24747	* Zoom to the zoom level 5 without an animated transition
24748	* ```ts
24749	* map.setZoom(5);
24750	* ```
24751	*/
24752	setZoom(zoom, eventData) {
24753		this._camera.setZoom(zoom, eventData);
24754		return this;
24755	}
24756	/**
24757	* Zooms the map to the specified zoom level, with an animated transition.
24758	*
24759	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`.
24760	*
24761	* @param zoom - The zoom level to transition to.
24762	* @param options - Options object
24763	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24764	* @example
24765	* ```ts
24766	* // Zoom to the zoom level 5 without an animated transition
24767	* map.zoomTo(5);
24768	* // Zoom to the zoom level 8 with an animated transition
24769	* map.zoomTo(8, {
24770	*   duration: 2000,
24771	*   offset: [100, 50]
24772	* });
24773	* ```
24774	*/
24775	zoomTo(zoom, options, eventData) {
24776		this._camera.zoomTo(zoom, options, eventData);
24777		return this;
24778	}
24779	/**
24780	* Incrementally increases the map's zoom level by 1, first snapping to the nearest `zoomSnap` increment.
24781	*
24782	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`.
24783	*
24784	* @param options - Options object
24785	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24786	* @example
24787	* Zoom the map in one level with a custom animation duration
24788	* ```ts
24789	* map.zoomIn({duration: 1000});
24790	* ```
24791	*/
24792	zoomIn(options, eventData) {
24793		this._camera.zoomIn(options, eventData);
24794		return this;
24795	}
24796	/**
24797	* Decreases the map's zoom level by 1, first snapping to the nearest `zoomSnap` increment.
24798	*
24799	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`.
24800	*
24801	* @param options - Options object
24802	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24803	* @example
24804	* Zoom the map out one level with a custom animation offset
24805	* ```ts
24806	* map.zoomOut({offset: [80, 60]});
24807	* ```
24808	*/
24809	zoomOut(options, eventData) {
24810		this._camera.zoomOut(options, eventData);
24811		return this;
24812	}
24813	/**
24814	* Returns the map's current vertical field of view, in degrees.
24815	*
24816	* @returns The map's current vertical field of view.
24817	* @defaultValue 36.87
24818	* @example
24819	* ```ts
24820	* const verticalFieldOfView = map.getVerticalFieldOfView();
24821	* ```
24822	*/
24823	getVerticalFieldOfView() {
24824		return this._camera.transform.fov;
24825	}
24826	/**
24827	* Sets the map's vertical field of view, in degrees.
24828	*
24829	* Triggers the following events: `movestart`, `move`, and `moveend`.
24830	*
24831	* @param fov - The vertical field of view to set, in degrees (0-180).
24832	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24833	* @defaultValue 36.87
24834	* @example
24835	* Change vertical field of view to 30 degrees
24836	* ```ts
24837	* map.setVerticalFieldOfView(30);
24838	* ```
24839	*/
24840	setVerticalFieldOfView(fov, eventData) {
24841		this._camera.setVerticalFieldOfView(fov, eventData);
24842		return this;
24843	}
24844	/**
24845	* Returns the map's current bearing. The bearing is the compass direction that is "up"; for example, a bearing
24846	* of 90° orients the map so that east is up.
24847	*
24848	* @returns The map's current bearing.
24849	* @see [Navigate the map with game-like controls](https://maplibre.org/maplibre-gl-js/docs/examples/navigate-the-map-with-game-like-controls/)
24850	*/
24851	getBearing() {
24852		return this._camera.transform.bearing;
24853	}
24854	/**
24855	* Sets the map's bearing (rotation). The bearing is the compass direction that is "up"; for example, a bearing
24856	* of 90° orients the map so that east is up.
24857	*
24858	* Equivalent to `jumpTo({bearing: bearing})`.
24859	*
24860	* Triggers the following events: `movestart`, `moveend`, and `rotate`.
24861	*
24862	* @param bearing - The desired bearing.
24863	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24864	* @example
24865	* Rotate the map to 90 degrees
24866	* ```ts
24867	* map.setBearing(90);
24868	* ```
24869	*/
24870	setBearing(bearing, eventData) {
24871		this._camera.setBearing(bearing, eventData);
24872		return this;
24873	}
24874	/**
24875	* Returns the map's current zoom snap level.
24876	*
24877	* @returns The map's current zoom snap level.
24878	*/
24879	getZoomSnap() {
24880		return this._camera.getZoomSnap();
24881	}
24882	/**
24883	* Sets the map's zoom snap level.
24884	*
24885	* @param snap - The zoom snap level to set.
24886	*/
24887	setZoomSnap(snap) {
24888		this._camera.setZoomSnap(snap);
24889		return this;
24890	}
24891	/**
24892	* Returns the current padding applied around the map viewport.
24893	*
24894	* @returns The current padding around the map viewport.
24895	*/
24896	getPadding() {
24897		return this._camera.transform.padding;
24898	}
24899	/**
24900	* Sets the padding in pixels around the viewport.
24901	*
24902	* Equivalent to `jumpTo({padding: padding})`.
24903	*
24904	* Triggers the following events: `movestart` and `moveend`.
24905	*
24906	* @param padding - The desired padding.
24907	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24908	* @example
24909	* Sets a left padding of 300px, and a top padding of 50px
24910	* ```ts
24911	* map.setPadding({ left: 300, top: 50 });
24912	* ```
24913	*/
24914	setPadding(padding, eventData) {
24915		this._camera.setPadding(padding, eventData);
24916		return this;
24917	}
24918	/**
24919	* Rotates the map to the specified bearing, with an animated transition. The bearing is the compass direction
24920	* that is "up"; for example, a bearing of 90° orients the map so that east is up.
24921	*
24922	* Triggers the following events: `movestart`, `moveend`, and `rotate`.
24923	*
24924	* @param bearing - The desired bearing.
24925	* @param options - Options object
24926	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24927	*/
24928	rotateTo(bearing, options, eventData) {
24929		this._camera.rotateTo(bearing, options, eventData);
24930		return this;
24931	}
24932	/**
24933	* Rotates the map so that north is up (0° bearing), with an animated transition.
24934	*
24935	* Triggers the following events: `movestart`, `moveend`, and `rotate`.
24936	*
24937	* @param options - Options object
24938	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24939	*/
24940	resetNorth(options, eventData) {
24941		this._camera.resetNorth(options, eventData);
24942		return this;
24943	}
24944	/**
24945	* Rotates and pitches the map so that north is up (0° bearing) and pitch and roll are 0°, with an animated transition.
24946	*
24947	* Triggers the following events: `movestart`, `move`, `moveend`, `pitchstart`, `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`.
24948	*
24949	* @param options - Options object
24950	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24951	*/
24952	resetNorthPitch(options, eventData) {
24953		this._camera.resetNorthPitch(options, eventData);
24954		return this;
24955	}
24956	/**
24957	* Snaps the map so that north is up (0° bearing), if the current bearing is close enough to it (i.e. within the
24958	* `bearingSnap` threshold).
24959	*
24960	* Triggers the following events: `movestart`, `moveend`, and `rotate`.
24961	*
24962	* @param options - Options object
24963	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24964	*/
24965	snapToNorth(options, eventData) {
24966		this._camera.snapToNorth(options, eventData);
24967		return this;
24968	}
24969	/**
24970	* Returns the map's current pitch (tilt).
24971	*
24972	* @returns The map's current pitch, measured in degrees away from the plane of the screen.
24973	*/
24974	getPitch() {
24975		return this._camera.transform.pitch;
24976	}
24977	/**
24978	* Sets the map's pitch (tilt). Equivalent to `jumpTo({pitch: pitch})`.
24979	*
24980	* Triggers the following events: `movestart`, `moveend`, `pitchstart`, and `pitchend`.
24981	*
24982	* @param pitch - The pitch to set, measured in degrees away from the plane of the screen (0-60).
24983	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
24984	*/
24985	setPitch(pitch, eventData) {
24986		this._camera.setPitch(pitch, eventData);
24987		return this;
24988	}
24989	/**
24990	* Returns the map's current roll angle.
24991	*
24992	* @returns The map's current roll, measured in degrees about the camera boresight.
24993	*/
24994	getRoll() {
24995		return this._camera.transform.roll;
24996	}
24997	/**
24998	* Sets the map's roll angle. Equivalent to `jumpTo({roll: roll})`.
24999	*
25000	* Triggers the following events: `movestart`, `moveend`, `rollstart`, and `rollend`.
25001	*
25002	* @param roll - The roll to set, measured in degrees about the camera boresight
25003	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
25004	*/
25005	setRoll(roll, eventData) {
25006		this._camera.setRoll(roll, eventData);
25007		return this;
25008	}
25009	/**
25010	* @param bounds - Calculate the center for these bounds in the viewport and use
25011	* the highest zoom level up to and including {@link Map.getMaxZoom} that fits
25012	* in the viewport. LngLatBounds represent a box that is always axis-aligned with bearing 0.
25013	* Bounds will be taken in `[sw, ne]` order. Southwest point will always be to the left of the northeast point.
25014	* @param options - Options object
25015	* @returns If map is able to fit to provided bounds, returns `center`, `zoom`, and `bearing`.
25016	* If map is unable to fit, method will warn and return undefined.
25017	* @example
25018	* ```ts
25019	* let bbox = [[-79, 43], [-73, 45]];
25020	* let newCameraTransform = map.cameraForBounds(bbox, {
25021	*   padding: {top: 10, bottom:25, left: 15, right: 5}
25022	* });
25023	* ```
25024	*/
25025	cameraForBounds(bounds, options) {
25026		return this._camera.cameraForBounds(bounds, options);
25027	}
25028	/**
25029	* Pans and zooms the map to contain its visible area within the specified geographical bounds.
25030	* This function will also reset the map's bearing to 0 if bearing is nonzero.
25031	*
25032	* Triggers the following events: `movestart` and `moveend`.
25033	*
25034	* @param bounds - Center these bounds in the viewport and use the highest
25035	* zoom level up to and including {@link Map.getMaxZoom} that fits them in the viewport.
25036	* Bounds will be taken in `[sw, ne]` order. Southwest point will always be to the left of the northeast point.
25037	* @param options - Options supports all properties from {@link AnimationOptions} and {@link CameraOptions} in addition to the fields below.
25038	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
25039	* @example
25040	* ```ts
25041	* let bbox = [[-79, 43], [-73, 45]];
25042	* map.fitBounds(bbox, {
25043	*   padding: {top: 10, bottom:25, left: 15, right: 5}
25044	* });
25045	* ```
25046	* @see [Fit a map to a bounding box](https://maplibre.org/maplibre-gl-js/docs/examples/fit-a-map-to-a-bounding-box/)
25047	*/
25048	fitBounds(bounds, options, eventData) {
25049		this._camera.fitBounds(bounds, options, eventData);
25050		return this;
25051	}
25052	/**
25053	* Pans, rotates and zooms the map to to fit the box made by points p0 and p1
25054	* once the map is rotated to the specified bearing. To zoom without rotating,
25055	* pass in the current map bearing.
25056	*
25057	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend` and `rotate`.
25058	*
25059	* @param p0 - First point on screen, in pixel coordinates
25060	* @param p1 - Second point on screen, in pixel coordinates
25061	* @param bearing - Desired map bearing at end of animation, in degrees
25062	* @param options - Options object
25063	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
25064	* @example
25065	* ```ts
25066	* let p0 = [220, 400];
25067	* let p1 = [500, 900];
25068	* map.fitScreenCoordinates(p0, p1, map.getBearing(), {
25069	*   padding: {top: 10, bottom:25, left: 15, right: 5}
25070	* });
25071	* ```
25072	* @see Used by {@link BoxZoomHandler}
25073	*/
25074	fitScreenCoordinates(p0, p1, bearing, options, eventData) {
25075		this._camera.fitScreenCoordinates(p0, p1, bearing, options, eventData);
25076		return this;
25077	}
25078	/**
25079	* Changes any combination of center, zoom, bearing, pitch, and roll, without
25080	* an animated transition. The map will retain its current values for any
25081	* details not specified in `options`.
25082	*
25083	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`,
25084	* `pitch`, `pitchend`, `rollstart`, `roll`, `rollend` and `rotate`.
25085	*
25086	* @param options - Options object
25087	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
25088	* @example
25089	* ```ts
25090	* // jump to coordinates at current zoom
25091	* map.jumpTo({center: [0, 0]});
25092	* // jump with zoom, pitch, and bearing options
25093	* map.jumpTo({
25094	*   center: [0, 0],
25095	*   zoom: 8,
25096	*   pitch: 45,
25097	*   bearing: 90
25098	* });
25099	* ```
25100	* @see [Jump to a series of locations](https://maplibre.org/maplibre-gl-js/docs/examples/jump-to-a-series-of-locations/)
25101	* @see [Update a feature in realtime](https://maplibre.org/maplibre-gl-js/docs/examples/update-a-feature-in-realtime/)
25102	*/
25103	jumpTo(options, eventData) {
25104		this._camera.jumpTo(options, eventData);
25105		return this;
25106	}
25107	/**
25108	* Calculates constrained camera options that place a geographic anchor at a screen point
25109	* without changing the map, using the same logic as MapLibre's interaction handlers.
25110	*
25111	* @param options - Anchor and optional zoom.
25112	* @returns Camera options that can be passed to {@link Map.jumpTo}.
25113	* @example
25114	* ```ts
25115	* const cameraOptions = map.calculateAnchoredCameraOptions({
25116	*   anchorLocation: map.unproject(pointerDownPosition),
25117	*   anchorScreenPoint: currentPointerPosition,
25118	*   zoom: map.getZoom() + 1,
25119	* });
25120	* map.jumpTo(cameraOptions);
25121	* ```
25122	*/
25123	calculateAnchoredCameraOptions(options) {
25124		return this._camera.calculateAnchoredCameraOptions(options);
25125	}
25126	/**
25127	* Given a camera position and rotation, calculates zoom and center point and returns them as {@link CameraOptions}.
25128	* @param cameraLngLat - The lng, lat of the camera to look from
25129	* @param cameraAlt - The altitude of the camera to look from, in meters above sea level
25130	* @param bearing - Bearing of the camera, in degrees
25131	* @param pitch - Pitch of the camera, in degrees
25132	* @param roll - Roll of the camera, in degrees
25133	* @returns the calculated camera options
25134	* @example
25135	* ```ts
25136	* // Calculate options to look from camera position(1°, 0°, 1000m) with bearing = 90°, pitch = 30°, and roll = 45°
25137	* const cameraLngLat = new LngLat(1, 0);
25138	* const cameraAltitude = 1000;
25139	* const bearing = 90;
25140	* const pitch = 30;
25141	* const roll = 45;
25142	* const cameraOptions = map.calculateCameraOptionsFromCameraLngLatAltRotation(cameraLngLat, cameraAltitude, bearing, pitch, roll);
25143	* // Apply calculated options
25144	* map.jumpTo(cameraOptions);
25145	* ```
25146	*/
25147	calculateCameraOptionsFromCameraLngLatAltRotation(cameraLngLat, cameraAlt, bearing, pitch, roll) {
25148		return this._camera.calculateCameraOptionsFromCameraLngLatAltRotation(cameraLngLat, cameraAlt, bearing, pitch, roll);
25149	}
25150	/**
25151	* Changes any combination of `center`, `zoom`, `bearing`, `pitch`, `roll`, and `padding` with an animated transition
25152	* between old and new values. The map will retain its current values for any
25153	* details not specified in `options`.
25154	*
25155	* !!! note "Reduced Motion"
25156	*     The transition will happen instantly if the user has enabled
25157	*     the `reduced motion` accessibility feature enabled in their operating system,
25158	*     unless `options` includes `essential: true`.
25159	*
25160	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`,
25161	* `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`.
25162	*
25163	* @param options - Options describing the destination and animation of the transition.
25164	* Accepts {@link CameraOptions} and {@link AnimationOptions}.
25165	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
25166	* @see [Navigate the map with game-like controls](https://maplibre.org/maplibre-gl-js/docs/examples/navigate-the-map-with-game-like-controls/)
25167	*/
25168	easeTo(options, eventData) {
25169		this._camera.easeTo(options, eventData);
25170		return this;
25171	}
25172	/**
25173	* Changes any combination of center, zoom, bearing, pitch, and roll, animating the transition along a curve that
25174	* evokes flight. The animation seamlessly incorporates zooming and panning to help
25175	* the user maintain her bearings even after traversing a great distance.
25176	*
25177	* !!! note "Reduced Motion"
25178	*     The animation will be skipped, and this will behave equivalently to `jumpTo`
25179	*     if the user has the `reduced motion` accessibility feature enabled in their operating system,
25180	*     unless 'options' includes `essential: true`.
25181	*
25182	* Triggers the following events: `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, `zoomend`, `pitchstart`,
25183	* `pitch`, `pitchend`, `rollstart`, `roll`, `rollend`, and `rotate`.
25184	*
25185	* @param options - Options describing the destination and animation of the transition.
25186	* Accepts {@link CameraOptions}, {@link AnimationOptions},
25187	* and the following additional options.
25188	* @param eventData - Additional properties to be added to event objects of events triggered by this method.
25189	* @example
25190	* ```ts
25191	* // fly with default options to null island
25192	* map.flyTo({center: [0, 0], zoom: 9});
25193	* // using flyTo options
25194	* map.flyTo({
25195	*   center: [0, 0],
25196	*   zoom: 9,
25197	*   speed: 0.2,
25198	*   curve: 1,
25199	*   easing(t) {
25200	*     return t;
25201	*   }
25202	* });
25203	* ```
25204	* @see [Fly to a location](https://maplibre.org/maplibre-gl-js/docs/examples/fly-to-a-location/)
25205	* @see [Slowly fly to a location](https://maplibre.org/maplibre-gl-js/docs/examples/slowly-fly-to-a-location/)
25206	* @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/)
25207	*/
25208	flyTo(options, eventData) {
25209		this._camera.flyTo(options, eventData);
25210		return this;
25211	}
25212	/**
25213	* Stops any animated transition underway.
25214	*/
25215	stop() {
25216		this._camera.stop();
25217		return this;
25218	}
25219	/**
25220	* Gets the elevation at a given location, in meters above sea level.
25221	* Returns null if terrain is not enabled.
25222	* If terrain is enabled with some exaggeration value, the value returned here will be reflective of (multiplied by) that exaggeration value.
25223	* 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/)
25224	* @param lngLatLike - `[x, y]` or LngLat coordinates of the location
25225	* @returns elevation in meters
25226	*/
25227	queryTerrainElevation(lngLatLike) {
25228		if (!this.terrain) return null;
25229		return this.terrain.getElevationForLngLat(LngLat.convert(lngLatLike), this._camera.transform);
25230	}
25231	/**
25232	* Returns the value of `centerClampedToGround`.
25233	*
25234	* If true, the elevation of the center point will automatically be set to the terrain elevation
25235	* (or zero if terrain is not enabled). If false, the elevation of the center point will default
25236	* to sea level and will not automatically update. Defaults to true. Needs to be set to false to
25237	* keep the camera above ground when pitch \> 90 degrees.
25238	*/
25239	getCenterClampedToGround() {
25240		return this._camera.getCenterClampedToGround();
25241	}
25242	/**
25243	* Given a camera 'from' position and a position to look at (`to`), calculates zoom and camera rotation and returns them as {@link CameraOptions}.
25244	* 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.
25245	* @param from - The camera to look from
25246	* @param altitudeFrom - The altitude of the camera to look from
25247	* @param to - The center to look at
25248	* @param altitudeTo - Optional altitude of the center to look at. If none given the ground height will be used.
25249	* @returns the calculated camera options
25250	* @example
25251	* ```ts
25252	* // Calculate options to look from (1°, 0°, 1000m) to (1°, 1°, 0m)
25253	* const cameraLngLat = new LngLat(1, 0);
25254	* const cameraAltitude = 1000;
25255	* const targetLngLat = new LngLat(1, 1);
25256	* const targetAltitude = 0;
25257	* const cameraOptions = map.calculateCameraOptionsFromTo(cameraLngLat, cameraAltitude, targetLngLat, targetAltitude);
25258	* // Apply calculated options
25259	* map.jumpTo(cameraOptions);
25260	* ```
25261	*/
25262	calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo) {
25263		if (altitudeTo == null && this.terrain) altitudeTo = this.terrain.getElevationForLngLat(to, this._camera.transform);
25264		return this._camera.transform.calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo ?? 0);
25265	}
25266	/**
25267	* Resizes the map according to the dimensions of its
25268	* `container` element.
25269	*
25270	* Checks if the map container size changed and updates the map if it has changed.
25271	* With the default `trackResize: true`, container size changes are picked up automatically,
25272	* including a container that becomes visible after being hidden with CSS. Call this method
25273	* explicitly when `trackResize` is `false`, or when the map's size changes in a way the
25274	* container's `ResizeObserver` cannot observe.
25275	*
25276	* Triggers the following events: `movestart`, `move`, `moveend`, and `resize`.
25277	*
25278	* @param eventData - Additional properties to be passed to `movestart`, `move`, `resize`, and `moveend`
25279	* events that get triggered as a result of resize. This can be useful for differentiating the
25280	* source of an event (for example, user-initiated or programmatically-triggered events).
25281	* @example
25282	* Resize a map with `trackResize` disabled when its container is shown after being hidden with CSS.
25283	* ```ts
25284	* let mapDiv = document.getElementById('map');
25285	* if (mapDiv.style.visibility === 'visible') map.resize();
25286	* ```
25287	*/
25288	resize(eventData, constrainTransform = true) {
25289		if (this._lostContextStyle.style !== null) return this;
25290		this._resizeInternal(constrainTransform);
25291		const fireMoving = !this._camera._moving;
25292		if (fireMoving) {
25293			this.stop();
25294			this.fire(new MapMovementEvent("movestart", eventData)).fire(new MapMovementEvent("move", eventData));
25295		}
25296		this.fire(new MapLibreEvent("resize", eventData));
25297		if (fireMoving) this.fire(new MapMovementEvent("moveend", eventData));
25298		return this;
25299	}
25300	/**
25301	* Resizes the map according to the dimensions of its
25302	* `container` element.
25303	*
25304	* It does not trigger any events, and does not check for context loss.
25305	*
25306	* It is used internally and by {@link Map.resize}.
25307	*
25308	* @internal
25309	*
25310	* @param constrainTransform - whether to constrain the transform after resizing.
25311	*/
25312	_resizeInternal(constrainTransform = true) {
25313		const [width, height] = this._containerDimensions();
25314		const clampedPixelRatio = this._getClampedPixelRatio(width, height);
25315		this._resizeCanvas(width, height, clampedPixelRatio);
25316		this.painter.resize(width, height, clampedPixelRatio);
25317		if (this.painter.overLimit()) {
25318			const gl = this.painter.context.gl;
25319			this._maxCanvasSize = [gl.drawingBufferWidth, gl.drawingBufferHeight];
25320			const clampedPixelRatio = this._getClampedPixelRatio(width, height);
25321			this._resizeCanvas(width, height, clampedPixelRatio);
25322			this.painter.resize(width, height, clampedPixelRatio);
25323		}
25324		this._resizeTransform(constrainTransform);
25325	}
25326	_resizeTransform(constrainTransform = true) {
25327		const [width, height] = this._containerDimensions();
25328		this._camera.transform.resize(width, height, constrainTransform);
25329		this._camera._requestedCameraState?.resize(width, height, constrainTransform);
25330	}
25331	/**
25332	* @internal
25333	* Return the map's pixel ratio eventually scaled down to respect maxCanvasSize.
25334	* Internally you should use this and not getPixelRatio().
25335	* Warns once when the ratio is scaled down. The message carries no sizes: this runs on every
25336	* resize and `warnOnce` de-duplicates by message, so sizes would warn on every drag frame.
25337	*/
25338	_getClampedPixelRatio(width, height) {
25339		const { 0: maxCanvasWidth, 1: maxCanvasHeight } = this._maxCanvasSize;
25340		const pixelRatio = this.getPixelRatio();
25341		const canvasWidth = width * pixelRatio;
25342		const canvasHeight = height * pixelRatio;
25343		const widthScaleFactor = canvasWidth > maxCanvasWidth ? maxCanvasWidth / canvasWidth : 1;
25344		const heightScaleFactor = canvasHeight > maxCanvasHeight ? maxCanvasHeight / canvasHeight : 1;
25345		const scaleFactor = Math.min(widthScaleFactor, heightScaleFactor);
25346		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.");
25347		return scaleFactor * pixelRatio;
25348	}
25349	/**
25350	* Returns the map's pixel ratio.
25351	* Note that the pixel ratio actually applied may be lower to respect maxCanvasSize.
25352	* @returns The pixel ratio.
25353	*/
25354	getPixelRatio() {
25355		return this._overridePixelRatio ?? devicePixelRatio;
25356	}
25357	/**
25358	* Sets the map's pixel ratio. This allows to override `devicePixelRatio`.
25359	* After this call, the canvas' `width` attribute will be `container.clientWidth * pixelRatio`
25360	* and its height attribute will be `container.clientHeight * pixelRatio`.
25361	* Set this to null to disable `devicePixelRatio` override.
25362	* Note that the pixel ratio actually applied may be lower to respect maxCanvasSize.
25363	* @param pixelRatio - The pixel ratio.
25364	*/
25365	setPixelRatio(pixelRatio) {
25366		this._overridePixelRatio = pixelRatio;
25367		this.resize();
25368	}
25369	/**
25370	* Returns the map's geographical bounds. When the bearing or pitch is non-zero, the visible region is not
25371	* an axis-aligned rectangle, and the result is the smallest bounds that encompasses the visible region.
25372	* @returns The geographical bounds of the map as {@link LngLatBounds}.
25373	* @example
25374	* ```ts
25375	* let bounds = map.getBounds();
25376	* ```
25377	*/
25378	getBounds() {
25379		return this._camera.transform.getBounds();
25380	}
25381	/**
25382	* Returns the maximum geographical bounds the map is constrained to, or `null` if none set.
25383	* @returns The map object.
25384	* @example
25385	* ```ts
25386	* let maxBounds = map.getMaxBounds();
25387	* ```
25388	*/
25389	getMaxBounds() {
25390		return this._camera.transform.getMaxBounds();
25391	}
25392	/**
25393	* Sets or clears the map's geographical bounds.
25394	*
25395	* Pan and zoom operations are constrained within these bounds.
25396	* If a pan or zoom is performed that would
25397	* display regions outside these bounds, the map will
25398	* instead display a position and zoom level
25399	* as close as possible to the operation's request while still
25400	* remaining within the bounds.
25401	*
25402	* @param bounds - The maximum bounds to set. If `null` or `undefined` is provided, the function removes the map's maximum bounds.
25403	* @example
25404	* Define bounds that conform to the `LngLatBoundsLike` object as set the max bounds.
25405	* ```ts
25406	* let bounds = [
25407	*   [-74.04728, 40.68392], // [west, south]
25408	*   [-73.91058, 40.87764]  // [east, north]
25409	* ];
25410	* map.setMaxBounds(bounds);
25411	* ```
25412	*/
25413	setMaxBounds(bounds) {
25414		this._camera.transform.setMaxBounds(LngLatBounds.convert(bounds));
25415		return this._update();
25416	}
25417	/**
25418	* Sets or clears the map's minimum zoom level.
25419	* If the map's current zoom level is lower than the new minimum,
25420	* the map will zoom to the new minimum and trigger the following events:
25421	* `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`.
25422	*
25423	* It is not always possible to zoom out and reach the set `minZoom`.
25424	* Other factors such as map height may restrict zooming. For example,
25425	* if the map is 512px tall it will not be possible to zoom below zoom 0
25426	* no matter what the `minZoom` is set to.
25427	*
25428	* A {@link ErrorEvent} event will be fired if minZoom is out of bounds.
25429	*
25430	* @param minZoom - The minimum zoom level to set (-2 - 24).
25431	* If `null` or `undefined` is provided, the function removes the current minimum zoom (i.e. sets it to -2).
25432	* @example
25433	* ```ts
25434	* map.setMinZoom(12.25);
25435	* ```
25436	*/
25437	setMinZoom(minZoom) {
25438		minZoom = minZoom === null || minZoom === void 0 ? defaultMinZoom : minZoom;
25439		if (minZoom >= defaultMinZoom && minZoom <= this._camera.transform.maxZoom) {
25440			const zoomBefore = this._camera.transform.zoom;
25441			const tr = this._camera.getTransformForUpdate();
25442			tr.setMinZoom(minZoom);
25443			this._camera.applyUpdatedTransform(tr);
25444			this._update();
25445			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"));
25446			return this;
25447		} else throw new Error(`minZoom must be between ${defaultMinZoom} and the current maxZoom, inclusive`);
25448	}
25449	/**
25450	* Returns the map's minimum allowable zoom level.
25451	*
25452	* @param constrained - If `true`, returns the effective minimum zoom after applying the map's viewport constraints.
25453	* If `false` or omitted, returns the configured minimum zoom.
25454	* @returns minZoom
25455	* @example
25456	* ```ts
25457	* let minZoom = map.getMinZoom();
25458	* ```
25459	*/
25460	getMinZoom(constrained = false) {
25461		const transform = this._camera.transform;
25462		return constrained ? transform.applyConstrain(transform.center, transform.minZoom).zoom : transform.minZoom;
25463	}
25464	/**
25465	* Sets or clears the map's maximum zoom level.
25466	* If the map's current zoom level is higher than the new maximum,
25467	* the map will zoom to the new maximum and trigger the following events:
25468	* `movestart`, `move`, `moveend`, `zoomstart`, `zoom`, and `zoomend`.
25469	*
25470	* A {@link ErrorEvent} event will be fired if minZoom is out of bounds.
25471	*
25472	* @param maxZoom - The maximum zoom level to set.
25473	* If `null` or `undefined` is provided, the function removes the current maximum zoom (sets it to 22).
25474	* @example
25475	* ```ts
25476	* map.setMaxZoom(18.75);
25477	* ```
25478	*/
25479	setMaxZoom(maxZoom) {
25480		maxZoom = maxZoom === null || maxZoom === void 0 ? defaultMaxZoom : maxZoom;
25481		if (maxZoom >= this._camera.transform.minZoom) {
25482			const zoomBefore = this._camera.transform.zoom;
25483			const tr = this._camera.getTransformForUpdate();
25484			tr.setMaxZoom(maxZoom);
25485			this._camera.applyUpdatedTransform(tr);
25486			this._update();
25487			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"));
25488			return this;
25489		} else throw new Error("maxZoom must be greater than the current minZoom");
25490	}
25491	/**
25492	* Returns the map's maximum allowable zoom level.
25493	*
25494	* @returns The maxZoom
25495	* @example
25496	* ```ts
25497	* let maxZoom = map.getMaxZoom();
25498	* ```
25499	*/
25500	getMaxZoom() {
25501		return this._camera.transform.maxZoom;
25502	}
25503	/**
25504	* Sets or clears the map's minimum pitch.
25505	* If the map's current pitch is lower than the new minimum,
25506	* the map will pitch to the new minimum and trigger the following events:
25507	* `movestart`, `move`, `moveend`, `pitchstart`, `pitch`, and `pitchend`.
25508	*
25509	* A {@link ErrorEvent} event will be fired if minPitch is out of bounds.
25510	*
25511	* @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.
25512	* If `null` or `undefined` is provided, the function removes the current minimum pitch (i.e. sets it to 0).
25513	*/
25514	setMinPitch(minPitch) {
25515		minPitch = minPitch === null || minPitch === void 0 ? defaultMinPitch : minPitch;
25516		if (minPitch < defaultMinPitch) throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`);
25517		if (minPitch >= defaultMinPitch && minPitch <= this._camera.transform.maxPitch) {
25518			const pitchBefore = this._camera.transform.pitch;
25519			const tr = this._camera.getTransformForUpdate();
25520			tr.setMinPitch(minPitch);
25521			this._camera.applyUpdatedTransform(tr);
25522			this._update();
25523			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"));
25524			return this;
25525		} else throw new Error(`minPitch must be between ${defaultMinPitch} and the current maxPitch, inclusive`);
25526	}
25527	/**
25528	* Returns the map's minimum allowable pitch.
25529	*
25530	* @returns The minPitch
25531	*/
25532	getMinPitch() {
25533		return this._camera.transform.minPitch;
25534	}
25535	/**
25536	* Sets or clears the map's maximum pitch.
25537	* If the map's current pitch is higher than the new maximum,
25538	* the map will pitch to the new maximum and trigger the following events:
25539	* `movestart`, `move`, `moveend`, `pitchstart`, `pitch`, and `pitchend`.
25540	*
25541	* A {@link ErrorEvent} event will be fired if maxPitch is out of bounds.
25542	*
25543	* @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.
25544	* If `null` or `undefined` is provided, the function removes the current maximum pitch (sets it to 60).
25545	*/
25546	setMaxPitch(maxPitch) {
25547		maxPitch = maxPitch === null || maxPitch === void 0 ? defaultMaxPitch : maxPitch;
25548		if (maxPitch > maxPitchThreshold) throw new Error(`maxPitch must be less than or equal to ${maxPitchThreshold}`);
25549		if (maxPitch >= this._camera.transform.minPitch) {
25550			const pitchBefore = this._camera.transform.pitch;
25551			const tr = this._camera.getTransformForUpdate();
25552			tr.setMaxPitch(maxPitch);
25553			this._camera.applyUpdatedTransform(tr);
25554			this._update();
25555			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"));
25556			return this;
25557		} else throw new Error("maxPitch must be greater than the current minPitch");
25558	}
25559	/**
25560	* Returns the map's maximum allowable pitch.
25561	*
25562	* @returns The maxPitch
25563	*/
25564	getMaxPitch() {
25565		return this._camera.transform.maxPitch;
25566	}
25567	/**
25568	* Returns the map's anisotropic filter pitch.
25569	* If the map is pitched beyond this threshold, anisotropic filtering will be applied to all raster layers.
25570	*
25571	* @returns The anisotropicFilterPitch
25572	* @example
25573	* ```ts
25574	* let anisotropicFilterPitch = map.getAnisotropicFilterPitch();
25575	* ```
25576	*/
25577	getAnisotropicFilterPitch() {
25578		return this._anisotropicFilterPitch;
25579	}
25580	/**
25581	* Sets the map's anisotropic filter pitch or reverts it to its default.
25582	*
25583	* A {@link ErrorEvent} event will be fired if anisotropicFilterPitch is out of bounds.
25584	*
25585	* @param anisotropicFilterPitch - The pitch above which to apply anisotropic filtering to the map's raster layers (0-180).
25586	* If `null` or `undefined` is provided, the function reverts to the default pitch threshold (20).
25587	*
25588	*
25589	* @example
25590	* ```ts
25591	* map.setAnisotropicFilterPitch(85);
25592	* ```
25593	*/
25594	setAnisotropicFilterPitch(anisotropicFilterPitch) {
25595		anisotropicFilterPitch = anisotropicFilterPitch === null || anisotropicFilterPitch === void 0 ? defaultAnisotropicFilterPitch : anisotropicFilterPitch;
25596		if (anisotropicFilterPitch > maxPitchThreshold) throw new Error(`anisotropicFilterPitch must be less than or equal to ${maxPitchThreshold}`);
25597		if (anisotropicFilterPitch < defaultMinPitch) throw new Error(`anisotropicFilterPitch must be greater than or equal to ${defaultMinPitch}`);
25598		this._anisotropicFilterPitch = anisotropicFilterPitch;
25599		return this._update();
25600	}
25601	/**
25602	* 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`:
25603	*
25604	* - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire
25605	* container, there will be blank space beyond 180 and -180 degrees longitude.
25606	* - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the
25607	* map and the other on the left edge of the map) at every zoom level.
25608	* @returns The renderWorldCopies
25609	* @example
25610	* ```ts
25611	* let worldCopiesRendered = map.getRenderWorldCopies();
25612	* ```
25613	* @see [Render world copies](https://maplibre.org/maplibre-gl-js/docs/examples/render-world-copies/)
25614	*/
25615	getRenderWorldCopies() {
25616		return this._camera.transform.renderWorldCopies;
25617	}
25618	/**
25619	* Sets the state of `renderWorldCopies`.
25620	*
25621	* @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`:
25622	*
25623	* - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire
25624	* container, there will be blank space beyond 180 and -180 degrees longitude.
25625	* - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the
25626	* map and the other on the left edge of the map) at every zoom level.
25627	*
25628	* `undefined` is treated as `true`, `null` is treated as `false`.
25629	* @example
25630	* ```ts
25631	* map.setRenderWorldCopies(true);
25632	* ```
25633	* @see [Render world copies](https://maplibre.org/maplibre-gl-js/docs/examples/render-world-copies/)
25634	*/
25635	setRenderWorldCopies(renderWorldCopies) {
25636		this._camera.transform.setRenderWorldCopies(renderWorldCopies);
25637		return this._update();
25638	}
25639	/** Sets or clears the callback overriding how the map constrains the viewport's lnglat and zoom to respect the longitude and latitude bounds.
25640	*
25641	* @param constrain - A {@link TransformConstrainFunction} callback defining how the viewport should respect the bounds.
25642	*
25643	* `null` clears the callback and reverts the constrain to the map transform's default constrain function.
25644	* @example
25645	* ```ts
25646	* function customTransformConstrain(lngLat, zoom) {
25647	*   return {center: lngLat, zoom: zoom ?? 0};
25648	* };
25649	* map.setTransformConstrain(customTransformConstrain);
25650	* ```
25651	* @see [Customize the map transform constrain](https://maplibre.org/maplibre-gl-js/docs/examples/customize-the-map-transform-constrain/)
25652	*/
25653	setTransformConstrain(constrain) {
25654		this._camera.transform.setConstrainOverride(constrain);
25655		return this._update();
25656	}
25657	/**
25658	* Returns a [Point](https://github.com/mapbox/point-geometry) representing pixel coordinates, relative to the map's `container`,
25659	* that correspond to the specified geographical location.
25660	*
25661	* A location behind the camera has no corresponding pixel. For such a location the
25662	* returned point is outside the viewport, on the side through which the location left
25663	* the screen, one viewport width or height away from the edge.
25664	*
25665	* @param lnglat - The geographical location to project.
25666	* @returns The [Point](https://github.com/mapbox/point-geometry) corresponding to `lnglat`, relative to the map's `container`.
25667	* @example
25668	* ```ts
25669	* let coordinate = [-122.420679, 37.772537];
25670	* let point = map.project(coordinate);
25671	* ```
25672	*/
25673	project(lnglat) {
25674		return this._camera.transform.locationToScreenPoint(LngLat.convert(lnglat), this.style && this.terrain);
25675	}
25676	/**
25677	* Returns a {@link LngLat} representing geographical coordinates that correspond
25678	* to the specified pixel coordinates.
25679	*
25680	* @param point - The pixel coordinates to unproject.
25681	* @returns The {@link LngLat} corresponding to `point`.
25682	* @example
25683	* ```ts
25684	* map.on('click', (e) => {
25685	*   // When the map is clicked, get the geographic coordinate.
25686	*   let coordinate = map.unproject(e.point);
25687	* });
25688	* ```
25689	*/
25690	unproject(point) {
25691		return this._camera.transform.screenPointToLocation(Point.convert(point), this.terrain);
25692	}
25693	/**
25694	* Returns true if the map is panning, zooming, rotating, or pitching due to a camera animation or user gesture.
25695	* @returns true if the map is moving.
25696	* @example
25697	* ```ts
25698	* let isMoving = map.isMoving();
25699	* ```
25700	*/
25701	isMoving() {
25702		return this._camera.isMoving() || this._handlers?.isMoving() || false;
25703	}
25704	/**
25705	* Returns true if the map is zooming due to a camera animation or user gesture.
25706	* @returns true if the map is zooming.
25707	* @example
25708	* ```ts
25709	* let isZooming = map.isZooming();
25710	* ```
25711	*/
25712	isZooming() {
25713		return this._camera.isZooming() || this._handlers?.isZooming() || false;
25714	}
25715	/**
25716	* Returns true if the map is rotating due to a camera animation or user gesture.
25717	* @returns true if the map is rotating.
25718	* @example
25719	* ```ts
25720	* map.isRotating();
25721	* ```
25722	*/
25723	isRotating() {
25724		return this._camera.isRotating() || this._handlers?.isRotating() || false;
25725	}
25726	_createDelegatedListener(type, layerIds, listener) {
25727		if (type === "mouseenter" || type === "mouseover") {
25728			let mousein = false;
25729			const mousemove = (e) => {
25730				const existingLayers = layerIds.filter((layerId) => this.getLayer(layerId));
25731				const features = existingLayers.length !== 0 ? this.queryRenderedFeatures(e.point, { layers: existingLayers }) : [];
25732				if (!features.length) mousein = false;
25733				else if (!mousein) {
25734					mousein = true;
25735					listener.call(this, new MapMouseEvent(type, this, e.originalEvent, { features }));
25736				}
25737			};
25738			const mouseout = () => {
25739				mousein = false;
25740			};
25741			return {
25742				layers: layerIds,
25743				listener,
25744				delegates: {
25745					mousemove,
25746					mouseout
25747				}
25748			};
25749		} else if (type === "mouseleave" || type === "mouseout") {
25750			let mousein = false;
25751			const mousemove = (e) => {
25752				const existingLayers = layerIds.filter((layerId) => this.getLayer(layerId));
25753				if ((existingLayers.length !== 0 ? this.queryRenderedFeatures(e.point, { layers: existingLayers }) : []).length) mousein = true;
25754				else if (mousein) {
25755					mousein = false;
25756					listener.call(this, new MapMouseEvent(type, this, e.originalEvent));
25757				}
25758			};
25759			const mouseout = (e) => {
25760				if (mousein) {
25761					mousein = false;
25762					listener.call(this, new MapMouseEvent(type, this, e.originalEvent));
25763				}
25764			};
25765			return {
25766				layers: layerIds,
25767				listener,
25768				delegates: {
25769					mousemove,
25770					mouseout
25771				}
25772			};
25773		} else {
25774			const delegate = (e) => {
25775				const existingLayers = layerIds.filter((layerId) => this.getLayer(layerId));
25776				const features = existingLayers.length !== 0 ? this.queryRenderedFeatures(e.point, { layers: existingLayers }) : [];
25777				if (features.length) {
25778					e.features = features;
25779					listener.call(this, e);
25780					delete e.features;
25781				}
25782			};
25783			return {
25784				layers: layerIds,
25785				listener,
25786				delegates: { [type]: delegate }
25787			};
25788		}
25789	}
25790	_saveDelegatedListener(type, delegatedListener) {
25791		this._delegatedListeners ||= {};
25792		this._delegatedListeners[type] ||= [];
25793		this._delegatedListeners[type].push(delegatedListener);
25794	}
25795	_removeDelegatedListener(type, layerIds, listener) {
25796		if (!this._delegatedListeners?.[type]) return;
25797		const listeners = this._delegatedListeners[type];
25798		for (let i = 0; i < listeners.length; i++) {
25799			const delegatedListener = listeners[i];
25800			if (delegatedListener.listener === listener && delegatedListener.layers.length === layerIds.length && delegatedListener.layers.every((layerId) => layerIds.includes(layerId))) {
25801				for (const event in delegatedListener.delegates) this.off(event, delegatedListener.delegates[event]);
25802				listeners.splice(i, 1);
25803				return;
25804			}
25805		}
25806	}
25807	on(type, layerIdsOrListener, listener) {
25808		if (listener === void 0) return super.on(type, layerIdsOrListener);
25809		const layerIds = typeof layerIdsOrListener === "string" ? [layerIdsOrListener] : layerIdsOrListener;
25810		const delegatedListener = this._createDelegatedListener(type, layerIds, listener);
25811		this._saveDelegatedListener(type, delegatedListener);
25812		for (const event in delegatedListener.delegates) this.on(event, delegatedListener.delegates[event]);
25813		return { unsubscribe: () => {
25814			this._removeDelegatedListener(type, layerIds, listener);
25815		} };
25816	}
25817	once(type, layerIdsOrListener, listener) {
25818		if (listener === void 0) return super.once(type, layerIdsOrListener);
25819		const layerIds = typeof layerIdsOrListener === "string" ? [layerIdsOrListener] : layerIdsOrListener;
25820		const delegatedListener = this._createDelegatedListener(type, layerIds, listener);
25821		for (const key in delegatedListener.delegates) {
25822			const delegate = delegatedListener.delegates[key];
25823			delegatedListener.delegates[key] = (...args) => {
25824				this._removeDelegatedListener(type, layerIds, listener);
25825				delegate(...args);
25826			};
25827		}
25828		this._saveDelegatedListener(type, delegatedListener);
25829		for (const event in delegatedListener.delegates) this.once(event, delegatedListener.delegates[event]);
25830		return this;
25831	}
25832	off(type, layerIdsOrListener, listener) {
25833		if (listener === void 0) return super.off(type, layerIdsOrListener);
25834		const layerIds = typeof layerIdsOrListener === "string" ? [layerIdsOrListener] : layerIdsOrListener;
25835		this._removeDelegatedListener(type, layerIds, listener);
25836		return this;
25837	}
25838	/**
25839	* Returns an array of MapGeoJSONFeature objects
25840	* representing visible features that satisfy the query parameters.
25841	*
25842	* @param geometryOrOptions - (optional) The geometry of the query region in pixel points within the map viewport:
25843	* either a single pixel point or a pair of top-left and bottom-right pixel points describing a bounding box.
25844	* The origin of the pixel points is at the top-left of the map viewport.
25845	* Omitting this parameter (i.e. calling {@link Map.queryRenderedFeatures} with zero arguments,
25846	* or with only a `options` argument) is equivalent to passing a bounding box encompassing the entire
25847	* map viewport.
25848	* The geometryOrOptions can receive a {@link QueryRenderedFeaturesOptions} only to support a situation where the function receives only one parameter which is the options parameter.
25849	* @param options - (optional) Options object.
25850	*
25851	* @returns An array of MapGeoJSONFeature objects.
25852	*
25853	* The `properties` value of each returned feature object contains the properties of its source feature. For GeoJSON sources, only
25854	* string and numeric property values are supported (i.e. `null`, `Array`, and `Object` values are not supported).
25855	*
25856	* Each feature includes top-level `layer`, `source`, and `sourceLayer` properties. The `layer` property is an object
25857	* representing the style layer to  which the feature belongs. Layout and paint properties in this object contain values
25858	* which are fully evaluated for the given zoom level and feature.
25859	*
25860	* Only features that are currently rendered are included. Some features will **not** be included, like:
25861	*
25862	* - Features from layers whose `visibility` property is `"none"`.
25863	* - Features from layers whose zoom range excludes the current zoom level.
25864	* - Symbol features that have been hidden due to text or icon collision.
25865	*
25866	* Features from all other layers are included, including features that may have no visible
25867	* contribution to the rendered result; for example, because the layer's opacity or color alpha component is set to
25868	* 0.
25869	*
25870	* The topmost rendered feature appears first in the returned array, and subsequent features are sorted by
25871	* descending z-order. Features that are rendered multiple times (due to wrapping across the antemeridian at low
25872	* zoom levels) are returned only once (though subject to the following caveat).
25873	*
25874	* Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature
25875	* geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple
25876	* times in query results. For example, suppose there is a highway running through the bounding rectangle of a query.
25877	* The results of the query will be those parts of the highway that lie within the map tiles covering the bounding
25878	* rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile
25879	* will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple
25880	* tiles due to tile buffering.
25881	*
25882	* @example
25883	* Find all features at a point
25884	* ```ts
25885	* let features = map.queryRenderedFeatures(
25886	*   [20, 35],
25887	*   { layers: ['my-layer-name'] }
25888	* );
25889	* ```
25890	*
25891	* @example
25892	* Find all features within a static bounding box
25893	* ```ts
25894	* let features = map.queryRenderedFeatures(
25895	*   [[10, 20], [30, 50]],
25896	*   { layers: ['my-layer-name'] }
25897	* );
25898	* ```
25899	*
25900	* @example
25901	* Find all features within a bounding box around a point
25902	* ```ts
25903	* let width = 10;
25904	* let height = 20;
25905	* let features = map.queryRenderedFeatures([
25906	*   [point.x - width / 2, point.y - height / 2],
25907	*   [point.x + width / 2, point.y + height / 2]
25908	* ], { layers: ['my-layer-name'] });
25909	* ```
25910	*
25911	* @example
25912	* Query all rendered features from a single layer
25913	* ```ts
25914	* let features = map.queryRenderedFeatures({ layers: ['my-layer-name'] });
25915	* ```
25916	* @see [Get features under the mouse pointer](https://maplibre.org/maplibre-gl-js/docs/examples/get-features-under-the-mouse-pointer/)
25917	*/
25918	queryRenderedFeatures(geometryOrOptions, options) {
25919		if (!this.style) return [];
25920		let queryGeometry;
25921		const isGeometry = geometryOrOptions instanceof Point || Array.isArray(geometryOrOptions);
25922		const geometry = isGeometry ? geometryOrOptions : [[0, 0], [this._camera.transform.width, this._camera.transform.height]];
25923		options ||= (isGeometry ? {} : geometryOrOptions) || {};
25924		if (geometry instanceof Point || typeof geometry[0] === "number") queryGeometry = [Point.convert(geometry)];
25925		else {
25926			const tl = Point.convert(geometry[0]);
25927			const br = Point.convert(geometry[1]);
25928			queryGeometry = [
25929				tl,
25930				new Point(br.x, tl.y),
25931				br,
25932				new Point(tl.x, br.y),
25933				tl
25934			];
25935		}
25936		return this.style.queryRenderedFeatures(queryGeometry, options, this._camera.transform);
25937	}
25938	/**
25939	* Returns an array of MapGeoJSONFeature objects
25940	* representing features within the specified vector tile or GeoJSON source that satisfy the query parameters.
25941	*
25942	* @param sourceId - The ID of the vector tile or GeoJSON source to query.
25943	* @param parameters - The options object.
25944	* @returns An array of MapGeoJSONFeature objects.
25945	*
25946	* In contrast to {@link Map.queryRenderedFeatures}, this function returns all features matching the query parameters,
25947	* whether or not they are rendered by the current style (i.e. visible). The domain of the query includes all currently-loaded
25948	* vector tiles and GeoJSON source tiles: this function does not check tiles outside the currently
25949	* visible viewport.
25950	*
25951	* Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature
25952	* geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple
25953	* times in query results. For example, suppose there is a highway running through the bounding rectangle of a query.
25954	* The results of the query will be those parts of the highway that lie within the map tiles covering the bounding
25955	* rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile
25956	* will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple
25957	* tiles due to tile buffering.
25958	*
25959	* @example
25960	* Find all features in one source layer in a vector source
25961	* ```ts
25962	* let features = map.querySourceFeatures('your-source-id', {
25963	*   sourceLayer: 'your-source-layer'
25964	* });
25965	* ```
25966	*
25967	*/
25968	querySourceFeatures(sourceId, parameters) {
25969		return this.style.querySourceFeatures(sourceId, parameters);
25970	}
25971	/**
25972	* Updates the map's MapLibre style object with a new value.
25973	*
25974	* 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
25975	* against the map's current state and perform only the changes necessary to make the map style match the desired state. Changes in sprites
25976	* (images used for icons and patterns) and glyphs (fonts for label text) **cannot** be diffed. If the sprites or fonts used in the current
25977	* style and the given style are different in any way, the map renderer will force a full update, removing the current style and building
25978	* the given one from scratch.
25979	*
25980	*
25981	* @param style - A JSON object conforming to the schema described in the
25982	* [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/), or a URL to such JSON.
25983	* @param options - The options object.
25984	*
25985	* @example
25986	* ```ts
25987	* map.setStyle("https://demotiles.maplibre.org/style.json");
25988	*
25989	* map.setStyle('https://demotiles.maplibre.org/style.json', {
25990	*   transformStyle: (previousStyle, nextStyle) => ({
25991	*       ...nextStyle,
25992	*       sources: {
25993	*           ...nextStyle.sources,
25994	*           // copy a source from previous style
25995	*           'osm': previousStyle.sources.osm
25996	*       },
25997	*       layers: [
25998	*           // background layer
25999	*           nextStyle.layers[0],
26000	*           // copy a layer from previous style
26001	*           previousStyle.layers[0],
26002	*           // other layers from the next style
26003	*           ...nextStyle.layers.slice(1).map(layer => {
26004	*               // hide the layers we don't need from demotiles style
26005	*               if (layer.id.startsWith('geolines')) {
26006	*                   layer.layout = {...layer.layout || {}, visibility: 'none'};
26007	*               // filter out US polygons
26008	*               } else if (layer.id.startsWith('coastline') || layer.id.startsWith('countries')) {
26009	*                   layer.filter = ['!=', ['get', 'ADM0_A3'], 'USA'];
26010	*               }
26011	*               return layer;
26012	*           })
26013	*       ]
26014	*   })
26015	* });
26016	* ```
26017	*/
26018	setStyle(style, options) {
26019		options = extend({}, {
26020			localIdeographFontFamily: this._localIdeographFontFamily,
26021			validate: this._validateStyle
26022		}, options);
26023		this._styleUrl = typeof style === "string" ? style : null;
26024		if (options.diff !== false && options.localIdeographFontFamily === this._localIdeographFontFamily && this.style && style) {
26025			this._diffStyle(style, options);
26026			return this;
26027		} else {
26028			this._localIdeographFontFamily = options.localIdeographFontFamily;
26029			return this._updateStyle(style, options);
26030		}
26031	}
26032	/**
26033	*  Updates the requestManager's transform request with a new function
26034	*
26035	* @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.
26036	* Expected to return an object with a `url` property and optionally `headers` and `credentials` properties
26037	*
26038	* @example
26039	* ```ts
26040	* map.setTransformRequest((url: string, resourceType: string) => {});
26041	* ```
26042	*/
26043	setTransformRequest(transformRequest) {
26044		this._requestManager.setTransformRequest(transformRequest);
26045		return this;
26046	}
26047	_getUIString(key) {
26048		const str = this._locale[key];
26049		if (str == null) throw new Error(`Missing UI string '${key}'`);
26050		return str;
26051	}
26052	_updateStyle(style, options) {
26053		this._diffStyleRequest?.abort();
26054		this._diffStyleRequest = null;
26055		if (options.transformStyle && this.style && !this.style._loaded) {
26056			this.style.once("style.load", () => this._updateStyle(style, options));
26057			return;
26058		}
26059		const previousStyle = this.style && options.transformStyle ? this.style.serialize() : void 0;
26060		if (this.style) {
26061			this.style.setEventedParent(null);
26062			this.style._remove(!style);
26063		}
26064		if (!style) {
26065			if (this._frameRequest) {
26066				this._frameRequest.abort();
26067				this._frameRequest = null;
26068			}
26069			this.style?.projection?.destroy();
26070			delete this.style;
26071			return this;
26072		} else this.style = new Style(this, options || {});
26073		this.style.setEventedParent(this, { style: this.style });
26074		if (typeof style === "string") this.style.loadURL(style, options, previousStyle);
26075		else this.style.loadJSON(style, options, previousStyle);
26076		return this;
26077	}
26078	_lazyInitEmptyStyle() {
26079		if (!this.style) {
26080			this.style = new Style(this, {});
26081			this.style.setEventedParent(this, { style: this.style });
26082			this.style.loadEmpty();
26083		}
26084	}
26085	async _diffStyle(style, options) {
26086		this._diffStyleRequest?.abort();
26087		if (typeof style === "string") {
26088			const url = style;
26089			this._diffStyleRequest = new AbortController();
26090			const abortController = this._diffStyleRequest;
26091			try {
26092				const request = await this._requestManager.transformRequest(url, "Style");
26093				if (abortController.signal.aborted) {
26094					this._diffStyleRequest = null;
26095					return;
26096				}
26097				const response = await getJSON(request, abortController);
26098				this._diffStyleRequest = null;
26099				this._updateDiff(response.data, options);
26100			} catch (error) {
26101				this._diffStyleRequest = null;
26102				if (!isAbortError(error)) this.fire(new ErrorEvent(ensureError(error)));
26103			}
26104		} else if (typeof style === "object") {
26105			this._diffStyleRequest = null;
26106			this._updateDiff(style, options);
26107		}
26108	}
26109	_updateDiff(style, options) {
26110		try {
26111			if (this.style.setState(style, options)) this._update(true);
26112		} catch (e) {
26113			warnOnce(`Unable to perform style diff: ${ensureError(e).message}.  Rebuilding the style from scratch.`);
26114			this._updateStyle(style, options);
26115		}
26116	}
26117	/**
26118	* Returns the map's MapLibre style object, a JSON object which can be used to recreate the map's style.
26119	*
26120	* @returns The map's style JSON object.
26121	*
26122	* @example
26123	* ```ts
26124	* let styleJson = map.getStyle();
26125	* ```
26126	*
26127	*/
26128	getStyle() {
26129		if (this.style) return this.style.serialize();
26130	}
26131	/**
26132	* Returns the URL the map's style was loaded from.
26133	*
26134	* @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.
26135	*
26136	* @example
26137	* ```ts
26138	* const styleUrl = map.getStyleUrl();
26139	* ```
26140	*/
26141	getStyleUrl() {
26142		return this._styleUrl;
26143	}
26144	/**
26145	* @internal
26146	* Returns the map's style and cloned images to restore context.
26147	* @returns An object containing the style and images.
26148	*/
26149	_getStyleAndImages() {
26150		if (this.style) return {
26151			style: this.style.serialize(),
26152			images: this.style.imageManager.cloneImages()
26153		};
26154		return {
26155			style: null,
26156			images: {}
26157		};
26158	}
26159	/**
26160	* Returns a Boolean indicating whether the map's style is fully loaded.
26161	*
26162	* @returns A Boolean indicating whether the style is fully loaded.
26163	*
26164	* @example
26165	* ```ts
26166	* let styleLoadStatus = map.isStyleLoaded();
26167	* ```
26168	*/
26169	isStyleLoaded() {
26170		if (!this.style) {
26171			warnOnce("There is no style added to the map.");
26172			return;
26173		}
26174		return this.style.loaded();
26175	}
26176	/**
26177	* Adds a source to the map's style.
26178	*
26179	* Events triggered:
26180	*
26181	* Triggers the `source.add` event.
26182	*
26183	* @param id - The ID of the source to add. Must not conflict with existing sources.
26184	* @param source - The source object, conforming to the
26185	* MapLibre Style Specification's [source definition](https://maplibre.org/maplibre-style-spec/sources) or
26186	* {@link CanvasSourceSpecification}.
26187	* @example
26188	* ```ts
26189	* map.addSource('my-data', {
26190	*   type: 'vector',
26191	*   url: 'https://demotiles.maplibre.org/tiles/tiles.json'
26192	* });
26193	* ```
26194	* @example
26195	* ```ts
26196	* map.addSource('my-data', {
26197	*   "type": "geojson",
26198	*   "data": {
26199	*     "type": "Feature",
26200	*     "geometry": {
26201	*       "type": "Point",
26202	*       "coordinates": [-77.0396, 38.8891]
26203	*     },
26204	*     "properties": {
26205	*       "title": "Washington DC",
26206	*       "marker-symbol": "monument"
26207	*     }
26208	*   }
26209	* });
26210	* ```
26211	* @see GeoJSON source: [Add live realtime data](https://maplibre.org/maplibre-gl-js/docs/examples/add-live-realtime-data/)
26212	*/
26213	addSource(id, source) {
26214		this._lazyInitEmptyStyle();
26215		this.style.addSource(id, source);
26216		return this._update(true);
26217	}
26218	/**
26219	* Returns a Boolean indicating whether the source is loaded. Returns `true` if the source with
26220	* the given ID in the map's style has no outstanding network requests, otherwise `false`.
26221	*
26222	* A {@link ErrorEvent} event will be fired if there is no source with the specified ID.
26223	*
26224	* @param id - The ID of the source to be checked.
26225	* @returns A Boolean indicating whether the source is loaded.
26226	* @example
26227	* ```ts
26228	* let sourceLoaded = map.isSourceLoaded('bathymetry-data');
26229	* ```
26230	*/
26231	isSourceLoaded(id) {
26232		const tileManager = this.style?.tileManagers[id];
26233		if (tileManager === void 0) {
26234			this.fire(new ErrorEvent(/* @__PURE__ */ new Error(`There is no tile manager with ID '${id}'`)));
26235			return;
26236		}
26237		return tileManager.loaded();
26238	}
26239	/**
26240	* Loads a 3D terrain mesh, based on a "raster-dem" source.
26241	*
26242	* Triggers the `terrain` event.
26243	*
26244	* @param options - Options object.
26245	* @example
26246	* ```ts
26247	* map.setTerrain({ source: 'terrain' });
26248	* ```
26249	*/
26250	setTerrain(options, styleOptions = {}) {
26251		this.style._checkLoaded();
26252		if (options && validateAndEmit(this, validateStyle.terrain, { value: options }, styleOptions)) return this;
26253		if (this._terrainDataCallback) this.style.off("data", this._terrainDataCallback);
26254		if (!options) {
26255			if (this.terrain) this.terrain.destroy();
26256			this.terrain = null;
26257			this.painter.renderToTexture = null;
26258			this.painter.destroyRTTResources();
26259			this._camera.setTerrain(null);
26260		} else {
26261			const tileManager = this.style.tileManagers[options.source];
26262			if (!tileManager) throw new Error(`cannot load terrain, because there exists no source with ID: ${options.source}`);
26263			if (this.terrain === null) tileManager.reload();
26264			for (const index in this.style._layers) {
26265				const thisLayer = this.style._layers[index];
26266				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.");
26267				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.");
26268			}
26269			if (this.terrain) this.terrain.destroy();
26270			this.terrain = new Terrain(this.painter, tileManager, options, this._terrainSkirtLength);
26271			this.painter.renderToTexture = new RenderToTexture(this.painter, this.terrain);
26272			this._camera.setTerrain(this.terrain);
26273			this._terrainDataCallback = (e) => this._handleTerrainDataEvent(e, options.source);
26274			this.style.on("data", this._terrainDataCallback);
26275		}
26276		this.style.triggerSymbolPlacement();
26277		this.fire(new MapTerrainEvent({ terrain: options }));
26278		return this;
26279	}
26280	_handleTerrainDataEvent(event, terrainSourceId) {
26281		if (event.dataType === "style") {
26282			this.terrain.tileManager.releaseAllRTT();
26283			return;
26284		}
26285		const isTerrainSourceEvent = event.sourceId === terrainSourceId;
26286		if (isTerrainSourceEvent) {
26287			this.terrain.resetElevationCache();
26288			this.style.triggerSymbolPlacement();
26289		}
26290		if (isTerrainSourceEvent && event.tile) {
26291			this.painter.markTerrainDepthDirty();
26292			this._camera.applyTerrainChange();
26293		}
26294		if (!event.tile) return;
26295		if (event.source?.type === "image") {
26296			this.terrain.tileManager.releaseAllRTT();
26297			return;
26298		}
26299		this.terrain.tileManager.releaseRTT(event.tile.tileID);
26300	}
26301	/**
26302	* Get the terrain-options if terrain is loaded
26303	* @returns the TerrainSpecification passed to setTerrain
26304	* @example
26305	* ```ts
26306	* map.getTerrain(); // { source: 'terrain' };
26307	* ```
26308	*/
26309	getTerrain() {
26310		return this.terrain?.options ?? null;
26311	}
26312	/**
26313	* Returns a Boolean indicating whether all tiles in the viewport from all sources on
26314	* the style are loaded.
26315	*
26316	* @returns A Boolean indicating whether all tiles are loaded.
26317	* @example
26318	* ```ts
26319	* let tilesLoaded = map.areTilesLoaded();
26320	* ```
26321	*/
26322	areTilesLoaded() {
26323		const tileManagers = this.style?.tileManagers;
26324		for (const tileManager of Object.values(tileManagers)) if (!tileManager.areTilesLoaded()) return false;
26325		return true;
26326	}
26327	/**
26328	* Removes a source from the map's style.
26329	*
26330	* @param id - The ID of the source to remove.
26331	* @example
26332	* ```ts
26333	* map.removeSource('bathymetry-data');
26334	* ```
26335	*/
26336	removeSource(id) {
26337		this.style.removeSource(id);
26338		return this._update(true);
26339	}
26340	/**
26341	* Returns the source with the specified ID in the map's style.
26342	*
26343	* This method is often used to update a source using the instance members for the relevant
26344	* source type as defined in classes that derive from {@link Source}.
26345	* For example, setting the `data` for a GeoJSON source or updating the `url` and `coordinates`
26346	* of an image source.
26347	*
26348	* @param id - The ID of the source to get.
26349	* @returns The style source with the specified ID or `undefined` if the ID
26350	* corresponds to no existing sources.
26351	* The shape of the object varies by source type.
26352	* A list of options for each source type is available on the MapLibre Style Specification's
26353	* [Sources](https://maplibre.org/maplibre-style-spec/sources/) page.
26354	* @example
26355	* ```ts
26356	* let sourceObject = map.getSource('points');
26357	* ```
26358	* @see [Create a draggable point](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-draggable-point/)
26359	* @see [Animate a point](https://maplibre.org/maplibre-gl-js/docs/examples/animate-a-point/)
26360	* @see [Add live realtime data](https://maplibre.org/maplibre-gl-js/docs/examples/add-live-realtime-data/)
26361	*/
26362	getSource(id) {
26363		return this.style?.getSource(id);
26364	}
26365	/**
26366	* Change the tile Level of Detail behavior of the specified source. These parameters have no effect when
26367	* pitch == 0, and the largest effect when the horizon is visible on screen.
26368	*
26369	* @param maxZoomLevelsOnScreen - The maximum number of distinct zoom levels allowed on screen at a time.
26370	* There will generally be fewer zoom levels on the screen, the maximum can only be reached when the horizon
26371	* is at the top of the screen. Increasing the maximum number of zoom levels causes the zoom level to decay
26372	* faster toward the horizon.
26373	* @param tileCountMaxMinRatio - The ratio of the maximum number of tiles loaded (at high pitch) to the minimum
26374	* number of tiles loaded. Increasing this ratio allows more tiles to be loaded at high pitch angles. If the ratio
26375	* would otherwise be exceeded, the zoom level is reduced uniformly to keep the number of tiles within the limit.
26376	* @param sourceId - The ID of the source to set tile LOD parameters for. All sources will be updated if unspecified.
26377	* If `sourceId` is specified but a corresponding source does not exist, an error is thrown.
26378	* @example
26379	* ```ts
26380	* map.setSourceTileLodParams(4.0, 3.0, 'terrain');
26381	* ```
26382	* @see [Modify Level of Detail behavior](https://maplibre.org/maplibre-gl-js/docs/examples/level-of-detail-control/)
26383	
26384	*/
26385	setSourceTileLodParams(maxZoomLevelsOnScreen, tileCountMaxMinRatio, sourceId) {
26386		if (sourceId) {
26387			const source = this.getSource(sourceId);
26388			if (!source) throw new Error(`There is no source with ID "${sourceId}", cannot set LOD parameters`);
26389			source.calculateTileZoom = createCalculateTileZoomFunction(Math.max(1, maxZoomLevelsOnScreen), Math.max(1, tileCountMaxMinRatio));
26390		} else for (const id in this.style.tileManagers) this.style.tileManagers[id].getSource().calculateTileZoom = createCalculateTileZoomFunction(Math.max(1, maxZoomLevelsOnScreen), Math.max(1, tileCountMaxMinRatio));
26391		this._update(true);
26392		return this;
26393	}
26394	/**
26395	* Triggers a reload of the selected tiles
26396	*
26397	* @param sourceId - The ID of the source
26398	* @param tileIds - An array of tile IDs to be reloaded. If not defined, all tiles will be reloaded.
26399	* @example
26400	* ```ts
26401	* map.refreshTiles('satellite', [{x:1024, y: 1023, z: 11}, {x:1023, y: 1023, z: 11}]);
26402	* ```
26403	*/
26404	refreshTiles(sourceId, tileIds) {
26405		const tileManager = this.style.tileManagers[sourceId];
26406		if (!tileManager) throw new Error(`There is no tile manager with ID "${sourceId}", cannot refresh tile`);
26407		if (tileIds === void 0) tileManager.reload(true);
26408		else tileManager.refreshTiles(tileIds.map((tileId) => {
26409			return new CanonicalTileID(tileId.z, tileId.x, tileId.y);
26410		}));
26411	}
26412	/**
26413	* Add an image to the style. This image can be displayed on the map like any other icon in the style's
26414	* sprite using the image's ID with
26415	* [`icon-image`](https://maplibre.org/maplibre-style-spec/layers/#layout-symbol-icon-image),
26416	* [`background-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-background-background-pattern),
26417	* [`fill-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-fill-fill-pattern),
26418	* or [`line-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-line-line-pattern).
26419	*
26420	* 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.
26421	*
26422	* @param id - The ID of the image.
26423	* @param image - The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data`
26424	* properties with the same format as `ImageData`.
26425	* @param options - Options object.
26426	* @example
26427	* ```ts
26428	* // If the style's sprite does not already contain an image with ID 'cat',
26429	* // add the image 'cat-icon.png' to the style's sprite with the ID 'cat'.
26430	* const image = await map.loadImage('https://upload.wikimedia.org/wikipedia/commons/thumb/6/60/Cat_silhouette.svg/400px-Cat_silhouette.svg.png');
26431	* if (!map.hasImage('cat')) map.addImage('cat', image.data);
26432	*
26433	* // Add a stretchable image that can be used with `icon-text-fit`
26434	* // In this example, the image is 600px wide by 400px high.
26435	* const image = await map.loadImage('https://upload.wikimedia.org/wikipedia/commons/8/89/Black_and_White_Boxed_%28bordered%29.png');
26436	* if (map.hasImage('border-image')) return;
26437	* map.addImage('border-image', image.data, {
26438	*     content: [16, 16, 300, 384], // place text over left half of image, avoiding the 16px border
26439	*     stretchX: [[16, 584]], // stretch everything horizontally except the 16px border
26440	*     stretchY: [[16, 384]], // stretch everything vertically except the 16px border
26441	* });
26442	* ```
26443	* @see Use `HTMLImageElement`: [Add an icon to the map](https://maplibre.org/maplibre-gl-js/docs/examples/add-an-icon-to-the-map/)
26444	* @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/)
26445	*/
26446	addImage(id, image, options = {}) {
26447		this._lazyInitEmptyStyle();
26448		const styleImage = this._createStyleImage(image, options);
26449		if (!styleImage) return this;
26450		this.style.addImage(id, styleImage);
26451		if (styleImage.userImage?.onAdd) styleImage.userImage.onAdd(this, id);
26452		return this;
26453	}
26454	/**
26455	* Sets a callback that is invoked when an icon or pattern needed by the style is missing.
26456	*
26457	* The resolver typically loads or generates the image and registers it with {@link Map.addImage}.
26458	* MapLibre awaits the returned promise before treating the image as missing, so async work is
26459	* supported. If the image is still missing afterwards, the `styleimagemissing` event is fired.
26460	*
26461	* @param resolver - Callback used to resolve missing images, or `null` to remove the resolver.
26462	* @example
26463	* ```ts
26464	* map.setMissingStyleImageResolver(async (id) => {
26465	*     const response = await fetch(`/icons/${id}.png`);
26466	*     const image = await createImageBitmap(await response.blob());
26467	*     map.addImage(id, image, {pixelRatio: 2});
26468	* });
26469	* ```
26470	*/
26471	setMissingStyleImageResolver(resolver) {
26472		this._missingStyleImageResolver = resolver;
26473		this.style?.setMissingImageResolver(resolver);
26474		return this;
26475	}
26476	_createStyleImage(image, options = {}) {
26477		const { pixelRatio = 1, sdf = false, stretchX, stretchY, content, textFitWidth, textFitHeight } = options;
26478		const version = 0;
26479		if (image instanceof HTMLImageElement || isImageBitmap(image)) {
26480			const { width, height, data } = browser.getImageData(image);
26481			return {
26482				data: new RGBAImage({
26483					width,
26484					height
26485				}, data),
26486				pixelRatio,
26487				stretchX,
26488				stretchY,
26489				content,
26490				textFitWidth,
26491				textFitHeight,
26492				sdf,
26493				version
26494			};
26495		} else if (image.width === void 0 || image.height === void 0) {
26496			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`")));
26497			return null;
26498		} else {
26499			const { width, height, data } = image;
26500			const userImage = image;
26501			const isWebGLImage = isStyleImageWebGLData(userImage.data);
26502			return {
26503				data: isWebGLImage ? new RGBAImage({
26504					width,
26505					height
26506				}) : new RGBAImage({
26507					width,
26508					height
26509				}, new Uint8Array(data)),
26510				pixelRatio,
26511				stretchX,
26512				stretchY,
26513				content,
26514				textFitWidth,
26515				textFitHeight,
26516				sdf,
26517				version,
26518				isWebGLImage,
26519				userImage
26520			};
26521		}
26522	}
26523	/**
26524	* Update an existing image in a style. This image can be displayed on the map like any other icon in the style's
26525	* sprite using the image's ID with
26526	* [`icon-image`](https://maplibre.org/maplibre-style-spec/layers/#layout-symbol-icon-image),
26527	* [`background-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-background-background-pattern),
26528	* [`fill-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-fill-fill-pattern),
26529	* or [`line-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-line-line-pattern).
26530	*
26531	* An {@link ErrorEvent} will be fired if the image parameter is invalid.
26532	*
26533	* @param id - The ID of the image.
26534	* @param image - The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data`
26535	* properties with the same format as `ImageData`.
26536	* @example
26537	* ```ts
26538	* // If an image with the ID 'cat' already exists in the style's sprite,
26539	* // replace that image with a new image, 'other-cat-icon.png'.
26540	* if (map.hasImage('cat')) map.updateImage('cat', './other-cat-icon.png');
26541	* ```
26542	*/
26543	updateImage(id, image) {
26544		const existingImage = this.style.getImage(id);
26545		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.")));
26546		const { width, height, data } = image instanceof HTMLImageElement || isImageBitmap(image) ? browser.getImageData(image) : image;
26547		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`")));
26548		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")));
26549		existingImage.isWebGLImage = isStyleImageWebGLData(data);
26550		if (existingImage.isWebGLImage) existingImage.userImage = image;
26551		else {
26552			const copy = !(image instanceof HTMLImageElement || isImageBitmap(image));
26553			existingImage.data.replace(data, copy);
26554		}
26555		this.style.updateImage(id, existingImage);
26556		return this;
26557	}
26558	/**
26559	* Returns an image, specified by ID, currently available in the map.
26560	* This includes both images from the style's original sprite
26561	* and any images that have been added at runtime using {@link Map.addImage}.
26562	*
26563	* @param id - The ID of the image.
26564	* @returns An image in the map with the specified ID.
26565	*
26566	* @example
26567	* ```ts
26568	* let coffeeShopIcon = map.getImage("coffee_cup");
26569	* ```
26570	*/
26571	getImage(id) {
26572		return this.style.getImage(id);
26573	}
26574	/**
26575	* Check whether or not an image with a specific ID exists in the style. This checks both images
26576	* in the style's original sprite and any images
26577	* that have been added at runtime using {@link Map.addImage}.
26578	*
26579	* An {@link ErrorEvent} will be fired if the image ID is missing.
26580	*
26581	* @param id - The ID of the image.
26582	*
26583	* @returns A Boolean indicating whether the image exists.
26584	* @example
26585	* Check if an image with the ID 'cat' exists in the style's sprite.
26586	* ```ts
26587	* let catIconExists = map.hasImage('cat');
26588	* ```
26589	*/
26590	hasImage(id) {
26591		if (!id) {
26592			this.fire(new ErrorEvent(/* @__PURE__ */ new Error("Missing required image id")));
26593			return false;
26594		}
26595		return !!this.style.getImage(id);
26596	}
26597	/**
26598	* Remove an image from a style. This can be an image from the style's original
26599	* sprite or any images
26600	* that have been added at runtime using {@link Map.addImage}.
26601	*
26602	* @param id - The ID of the image.
26603	*
26604	* @example
26605	* ```ts
26606	* // If an image with the ID 'cat' exists in
26607	* // the style's sprite, remove it.
26608	* if (map.hasImage('cat')) map.removeImage('cat');
26609	* ```
26610	*/
26611	removeImage(id) {
26612		this.style.removeImage(id);
26613	}
26614	/**
26615	* Load an image from an external URL to be used with {@link Map.addImage}. External
26616	* domains must support [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS).
26617	*
26618	* @param url - The URL of the image file. Image file must be in png, webp, or jpg format.
26619	* @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)
26620	*
26621	* @example
26622	* Load an image from an external URL.
26623	* ```ts
26624	* const response = await map.loadImage('https://picsum.photos/50/50');
26625	* // Add the loaded image to the style's sprite with the ID 'photo'.
26626	* map.addImage('photo', response.data);
26627	* ```
26628	* @see [Add an icon to the map](https://maplibre.org/maplibre-gl-js/docs/examples/add-an-icon-to-the-map/)
26629	*/
26630	async loadImage(url) {
26631		const response = await ImageRequest.getImage(await this._requestManager.transformRequest(url, "Image"), new AbortController());
26632		if (!response.data) throw new Error(`Could not load image ${url}: the response is empty`);
26633		return response;
26634	}
26635	/**
26636	* Returns an Array of strings containing the IDs of all images currently available in the map.
26637	* This includes both images from the style's original sprite
26638	* and any images that have been added at runtime using {@link Map.addImage}.
26639	*
26640	* @returns An Array of strings containing the names of all sprites/images currently available in the map.
26641	*
26642	* @example
26643	* ```ts
26644	* let allImages = map.listImages();
26645	* ```
26646	*/
26647	listImages() {
26648		return this.style?.listImages() ?? [];
26649	}
26650	/**
26651	* Adds a [MapLibre style layer](https://maplibre.org/maplibre-style-spec/layers)
26652	* to the map's style.
26653	*
26654	* A layer defines how data from a specified source will be styled. Read more about layer types
26655	* and available paint and layout properties in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/layers).
26656	*
26657	* @param layer - The layer to add,
26658	* conforming to either the MapLibre Style Specification's [layer definition](https://maplibre.org/maplibre-style-spec/layers) or,
26659	* less commonly, the {@link CustomLayerInterface} specification. Can also be a layer definition with an embedded source definition.
26660	* The MapLibre Style Specification's layer definition is appropriate for most layers.
26661	*
26662	* @param beforeId - The ID of an existing layer to insert the new layer before,
26663	* resulting in the new layer appearing visually beneath the existing layer.
26664	* If this argument is not specified, the layer will be appended to the end of the layers array
26665	* and appear visually above all other layers.
26666	*
26667	* @example
26668	* Add a circle layer with a vector source
26669	* ```ts
26670	* map.addLayer({
26671	*   id: 'points-of-interest',
26672	*   source: {
26673	*     type: 'vector',
26674	*     url: 'https://demotiles.maplibre.org/tiles/tiles.json'
26675	*   },
26676	*   'source-layer': 'poi_label',
26677	*   type: 'circle',
26678	*   paint: {
26679	*     // MapLibre Style Specification paint properties
26680	*   },
26681	*   layout: {
26682	*     // MapLibre Style Specification layout properties
26683	*   }
26684	* });
26685	* ```
26686	*
26687	* @example
26688	* Define a source before using it to create a new layer
26689	* ```ts
26690	* map.addSource('state-data', {
26691	*   type: 'geojson',
26692	*   data: 'path/to/data.geojson'
26693	* });
26694	*
26695	* map.addLayer({
26696	*   id: 'states',
26697	*   // References the GeoJSON source defined above
26698	*   // and does not require a `source-layer`
26699	*   source: 'state-data',
26700	*   type: 'symbol',
26701	*   layout: {
26702	*     // Set the label content to the
26703	*     // feature's `name` property
26704	*     text-field: ['get', 'name']
26705	*   }
26706	* });
26707	* ```
26708	*
26709	* @example
26710	* Add a new symbol layer before an existing layer
26711	* ```ts
26712	* map.addLayer({
26713	*   id: 'states',
26714	*   // References a source that's already been defined
26715	*   source: 'state-data',
26716	*   type: 'symbol',
26717	*   layout: {
26718	*     // Set the label content to the
26719	*     // feature's `name` property
26720	*     text-field: ['get', 'name']
26721	*   }
26722	* // Add the layer before the existing `cities` layer
26723	* }, 'cities');
26724	* ```
26725	* @see [Create and style clusters](https://maplibre.org/maplibre-gl-js/docs/examples/create-and-style-clusters/)
26726	* @see [Add a vector tile source](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-vector-tile-source/)
26727	* @see [Add a WMS source](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-wms-source/)
26728	*/
26729	addLayer(layer, beforeId) {
26730		this._lazyInitEmptyStyle();
26731		this.style.addLayer(layer, beforeId);
26732		return this._update(true);
26733	}
26734	/**
26735	* Moves a layer to a different z-position.
26736	*
26737	* @param id - The ID of the layer to move.
26738	* @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.
26739	*
26740	* @example
26741	* 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.
26742	* ```ts
26743	* map.moveLayer('polygon', 'country-label');
26744	* ```
26745	*/
26746	moveLayer(id, beforeId) {
26747		this.style.moveLayer(id, beforeId);
26748		return this._update(true);
26749	}
26750	/**
26751	* Removes the layer with the given ID from the map's style.
26752	*
26753	* An {@link ErrorEvent} will be fired if no such layer exists.
26754	*
26755	* @param id - The ID of the layer to remove
26756	*
26757	* @example
26758	* If a layer with ID 'state-data' exists, remove it.
26759	* ```ts
26760	* if (map.getLayer('state-data')) map.removeLayer('state-data');
26761	* ```
26762	*/
26763	removeLayer(id) {
26764		this.style.removeLayer(id);
26765		return this._update(true);
26766	}
26767	/**
26768	* Returns the layer with the specified ID in the map's style.
26769	*
26770	* @param id - The ID of the layer to get.
26771	* @returns The layer with the specified ID, or `undefined`
26772	* if the ID corresponds to no existing layers.
26773	*
26774	* @example
26775	* ```ts
26776	* let stateDataLayer = map.getLayer('state-data');
26777	* ```
26778	* @see [Filter symbols by toggling a list](https://maplibre.org/maplibre-gl-js/docs/examples/filter-symbols-by-toggling-a-list/)
26779	* @see [Filter symbols by text input](https://maplibre.org/maplibre-gl-js/docs/examples/filter-symbols-by-text-input/)
26780	*/
26781	getLayer(id) {
26782		return this.style?.getLayer(id);
26783	}
26784	/**
26785	* Return the ids of all layers currently in the style, including custom layers, in order.
26786	*
26787	* @returns ids of layers, in order
26788	*
26789	* @example
26790	* ```ts
26791	* const orderedLayerIds = map.getLayersOrder();
26792	* ```
26793	*/
26794	getLayersOrder() {
26795		return this.style?.getLayersOrder() ?? [];
26796	}
26797	/**
26798	* Sets the zoom extent for the specified style layer. The zoom extent includes the
26799	* [minimum zoom level](https://maplibre.org/maplibre-style-spec/layers/#minzoom)
26800	* and [maximum zoom level](https://maplibre.org/maplibre-style-spec/layers/#maxzoom))
26801	* at which the layer will be rendered.
26802	*
26803	* !!! note
26804	*     For style layers using vector sources, style layers cannot be rendered at zoom levels lower than the
26805	*     minimum zoom level of the _source layer_ because the data does not exist at those zoom levels. If the minimum
26806	*     zoom level of the source layer is higher than the minimum zoom level defined in the style layer, the style
26807	*     layer will not be rendered at all zoom levels in the zoom range.
26808	*
26809	* @param layerId - The ID of the layer to which the zoom extent will be applied.
26810	* @param minzoom - The minimum zoom to set (0-24).
26811	* @param maxzoom - The maximum zoom to set (0-24).
26812	*
26813	* @example
26814	* ```ts
26815	* map.setLayerZoomRange('my-layer', 2, 5);
26816	* ```
26817	*/
26818	setLayerZoomRange(layerId, minzoom, maxzoom) {
26819		this.style.setLayerZoomRange(layerId, minzoom, maxzoom);
26820		return this._update(true);
26821	}
26822	/**
26823	* Sets the filter for the specified style layer.
26824	*
26825	* Filters control which features a style layer renders from its source.
26826	* Any feature for which the filter expression evaluates to `true` will be
26827	* rendered on the map. Those that are false will be hidden.
26828	*
26829	* Use `setFilter` to show a subset of your source data.
26830	*
26831	* To clear the filter, pass `null` or `undefined` as the second parameter.
26832	*
26833	* @param layerId - The ID of the layer to which the filter will be applied.
26834	* @param filter - The filter, conforming to the MapLibre Style Specification's
26835	* [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.
26836	* @param options - Options object.
26837	*
26838	* @example
26839	* Display only features with the 'name' property 'USA'
26840	* ```ts
26841	* map.setFilter('my-layer', ['==', ['get', 'name'], 'USA']);
26842	* ```
26843	* @example
26844	* Display only features with five or more 'available-spots'
26845	* ```ts
26846	* map.setFilter('bike-docks', ['>=', ['get', 'available-spots'], 5]);
26847	* ```
26848	* @example
26849	* Remove the filter for the 'bike-docks' style layer
26850	* ```ts
26851	* map.setFilter('bike-docks', null);
26852	* ```
26853	* @see [Create a timeline animation](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-time-slider/)
26854	*/
26855	setFilter(layerId, filter, options = {}) {
26856		this.style?.setFilter(layerId, filter, options);
26857		return this._update(true);
26858	}
26859	/**
26860	* Returns the filter applied to the specified style layer.
26861	*
26862	* @param layerId - The ID of the style layer whose filter to get.
26863	* @returns The layer's filter.
26864	*/
26865	getFilter(layerId) {
26866		return this.style.getFilter(layerId);
26867	}
26868	/**
26869	* Sets the value of a paint property in the specified style layer.
26870	*
26871	* @param layerId - The ID of the layer to set the paint property in.
26872	* @param name - The name of the paint property to set.
26873	* @param value - The value of the paint property to set.
26874	* Must be of a type appropriate for the property, as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/).
26875	* Pass `null` to unset the existing value.
26876	* @param options - Options object.
26877	* @example
26878	* ```ts
26879	* map.setPaintProperty('my-layer', 'fill-color', '#faafee');
26880	* ```
26881	* @see [Change a layer's color with buttons](https://maplibre.org/maplibre-gl-js/docs/examples/change-a-layers-color-with-buttons/)
26882	* @see [Create a draggable point](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-draggable-point/)
26883	*/
26884	setPaintProperty(layerId, name, value, options = {}) {
26885		this.style?.setPaintProperty(layerId, name, value, options);
26886		return this._update(true);
26887	}
26888	/**
26889	* Returns the value of a paint property in the specified style layer.
26890	*
26891	* @param layerId - The ID of the layer to get the paint property from.
26892	* @param name - The name of a paint property to get.
26893	* @returns The value of the specified paint property.
26894	*/
26895	getPaintProperty(layerId, name) {
26896		return this.style.getPaintProperty(layerId, name);
26897	}
26898	/**
26899	* Sets the value of a layout property in the specified style layer.
26900	*
26901	* @param layerId - The ID of the layer to set the layout property in.
26902	* @param name - The name of the layout property to set.
26903	* @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/).
26904	* @param options - The options object.
26905	* @example
26906	* ```ts
26907	* map.setLayoutProperty('my-layer', 'visibility', 'none');
26908	* ```
26909	*/
26910	setLayoutProperty(layerId, name, value, options = {}) {
26911		this.style.setLayoutProperty(layerId, name, value, options);
26912		return this._update(true);
26913	}
26914	/**
26915	* Returns the value of a layout property in the specified style layer.
26916	*
26917	* @param layerId - The ID of the layer to get the layout property from.
26918	* @param name - The name of the layout property to get.
26919	* @returns The value of the specified layout property.
26920	*/
26921	getLayoutProperty(layerId, name) {
26922		return this.style.getLayoutProperty(layerId, name);
26923	}
26924	/**
26925	* Sets the value of the style's glyphs property. Pass a falsy value (null or undefined)
26926	* to unset glyphs.
26927	*
26928	* @param glyphsUrl - Glyph URL to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/glyphs/).
26929	* @param options - Options object.
26930	* @example
26931	* ```ts
26932	* map.setGlyphs('https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf');
26933	* ```
26934	*/
26935	setGlyphs(glyphsUrl, options = {}) {
26936		this._lazyInitEmptyStyle();
26937		this.style.setGlyphs(glyphsUrl, options);
26938		return this._update(true);
26939	}
26940	/**
26941	* Returns the value of the style's glyphs URL
26942	*
26943	* @returns glyphs Style's glyphs url, or `null` if glyphs are unset.
26944	*/
26945	getGlyphs() {
26946		return this.style.getGlyphsUrl();
26947	}
26948	/**
26949	* Sets the value of the style's `font-faces` property, which points at the font files used to
26950	* draw text that the style's `glyphs` URL does not cover. Pass a falsy value (null or undefined)
26951	* to unset it.
26952	*
26953	* The files are handed to the browser's CSS Font Loading API, so any format the browser can
26954	* render text with may be used, and requests for them go through `transformRequest` as glyph
26955	* requests do. Text is drawn a grapheme cluster at a time, so a letter and the marks written on
26956	* it are handed to the browser's text engine together and come back as the one shape they are
26957	* written as -- which is what a `glyphs` URL, serving one codepoint at a time, cannot do.
26958	*
26959	* @param fontFaces - The font faces to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/root/#font-faces).
26960	* A declaration this cannot make sense of is skipped with a warning, as is a font file that
26961	* fails to load, so the text it would have drawn falls back to the `glyphs` URL.
26962	* @example
26963	* ```ts
26964	* map.setFontFaces({
26965	*     'Noto Sans Regular': [
26966	*         {url: 'https://example.com/NotoSansKhmer-Regular.ttf', 'unicode-range': ['U+1780-17FF']}
26967	*     ]
26968	* });
26969	* ```
26970	*/
26971	setFontFaces(fontFaces) {
26972		this._lazyInitEmptyStyle();
26973		this.style.setFontFaces(fontFaces);
26974		return this._update(true);
26975	}
26976	/**
26977	* Returns the value of the style's `font-faces` property.
26978	*
26979	* @returns The style's font faces, or `null` if it declares none.
26980	*/
26981	getFontFaces() {
26982		return this.style.getFontFaces();
26983	}
26984	/**
26985	* Adds a sprite to the map's style. Fires the `style` event.
26986	*
26987	* @param id - The ID of the sprite to add. Must not conflict with existing sprites.
26988	* @param url - The URL to load the sprite from
26989	* @param options - Options object.
26990	* @example
26991	* ```ts
26992	* map.addSprite('sprite-two', 'http://example.com/sprite-two');
26993	* ```
26994	*/
26995	addSprite(id, url, options = {}) {
26996		this._lazyInitEmptyStyle();
26997		this.style.addSprite(id, url, options, (err) => {
26998			if (!err) this._update(true);
26999		});
27000		return this;
27001	}
27002	/**
27003	* Removes the sprite from the map's style. Fires the `style` event.
27004	*
27005	* @param id - The ID of the sprite to remove. If the sprite is declared as a single URL, the ID must be "default".
27006	* @example
27007	* ```ts
27008	* map.removeSprite('sprite-two');
27009	* map.removeSprite('default');
27010	* ```
27011	*/
27012	removeSprite(id) {
27013		this._lazyInitEmptyStyle();
27014		this.style.removeSprite(id);
27015		return this._update(true);
27016	}
27017	/**
27018	* Returns the as-is value of the style's sprite.
27019	*
27020	* @returns style's sprite list of id-url pairs
27021	*/
27022	getSprite() {
27023		return this.style.getSprite();
27024	}
27025	/**
27026	* Sets the value of the style's sprite property.
27027	*
27028	* @param spriteUrl - Sprite URL to set.
27029	* @param options - Options object.
27030	* @example
27031	* ```ts
27032	* map.setSprite('YOUR_SPRITE_URL');
27033	* ```
27034	*/
27035	setSprite(spriteUrl, options = {}) {
27036		this._lazyInitEmptyStyle();
27037		this.style.setSprite(spriteUrl, options, (err) => {
27038			if (!err) this._update(true);
27039		});
27040		return this;
27041	}
27042	/**
27043	* Sets the any combination of light values.
27044	*
27045	* @param light - Light properties to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/light).
27046	* @param options - Options object.
27047	*
27048	* @example
27049	* ```ts
27050	* let layerVisibility = map.getLayoutProperty('my-layer', 'visibility');
27051	* ```
27052	*/
27053	setLight(light, options = {}) {
27054		this._lazyInitEmptyStyle();
27055		this.style.setLight(light, options);
27056		return this._update(true);
27057	}
27058	/**
27059	* Returns the value of the light object.
27060	*
27061	* @returns light Light properties of the style.
27062	*/
27063	getLight() {
27064		return this.style.getLight();
27065	}
27066	/**
27067	* Sets the value of style's sky properties.
27068	*
27069	* @param sky - Sky properties to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/sky/).
27070	* @param options - Options object.
27071	*
27072	* @example
27073	* ```ts
27074	* map.setSky({'atmosphere-blend': 1.0});
27075	* ```
27076	*/
27077	setSky(sky, options = {}) {
27078		this._lazyInitEmptyStyle();
27079		this.style.setSky(sky, options);
27080		return this._update(true);
27081	}
27082	/**
27083	* Returns the value of the style's sky.
27084	*
27085	* @returns the sky properties of the style.
27086	* @example
27087	* ```ts
27088	* map.getSky();
27089	* ```
27090	*/
27091	getSky() {
27092		return this.style.getSky();
27093	}
27094	/**
27095	* Sets the `state` of a feature.
27096	* A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime.
27097	* When using this method, the `state` object is merged with any existing key-value pairs in the feature's state.
27098	* Features are identified by their `feature.id` attribute, which can be any number or string.
27099	*
27100	* This method can only be used with sources that have a `feature.id` attribute. The `feature.id` attribute can be defined in three ways:
27101	*
27102	* - For vector or GeoJSON sources, including an `id` attribute in the original data file.
27103	* - For vector or GeoJSON sources, using the [`promoteId`](https://maplibre.org/maplibre-style-spec/sources/#promoteid) option at the time the source is defined.
27104	* - 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.
27105	*
27106	* !!! note
27107	*     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.
27108	*
27109	* @param feature - Feature identifier. Feature objects returned from
27110	* {@link Map.queryRenderedFeatures} or event handlers can be used as feature identifiers.
27111	* @param state - A set of key-value pairs. The values should be valid JSON types.
27112	*
27113	* @example
27114	* ```ts
27115	* // When the mouse moves over the `my-layer` layer, update
27116	* // the feature state for the feature under the mouse
27117	* map.on('mousemove', 'my-layer', (e) => {
27118	*   if (e.features.length > 0) {
27119	*     map.setFeatureState({
27120	*       source: 'my-source',
27121	*       sourceLayer: 'my-source-layer',
27122	*       id: e.features[0].id,
27123	*     }, {
27124	*       hover: true
27125	*     });
27126	*   }
27127	* });
27128	* ```
27129	* @see [Create a hover effect](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-hover-effect/)
27130	*/
27131	setFeatureState(feature, state) {
27132		this.style.setFeatureState(feature, state);
27133		return this._update();
27134	}
27135	/**
27136	* Removes the `state` of a feature, setting it back to the default behavior.
27137	* If only a `target.source` is specified, it will remove the state for all features from that source.
27138	* If `target.id` is also specified, it will remove all keys for that feature's state.
27139	* If `key` is also specified, it removes only that key from that feature's state.
27140	* Features are identified by their `feature.id` attribute, which can be any number or string.
27141	*
27142	* @param target - Identifier of where to remove state. It can be a source, a feature, or a specific key of feature.
27143	* Feature objects returned from {@link Map.queryRenderedFeatures} or event handlers can be used as feature identifiers.
27144	* @param key - (optional) The key in the feature state to reset.
27145	* @example
27146	* Reset the entire state object for all features in the `my-source` source
27147	* ```ts
27148	* map.removeFeatureState({
27149	*   source: 'my-source'
27150	* });
27151	* ```
27152	*
27153	* @example
27154	* When the mouse leaves the `my-layer` layer,
27155	* reset the entire state object for the
27156	* feature under the mouse
27157	* ```ts
27158	* map.on('mouseleave', 'my-layer', (e) => {
27159	*   map.removeFeatureState({
27160	*     source: 'my-source',
27161	*     sourceLayer: 'my-source-layer',
27162	*     id: e.features[0].id
27163	*   });
27164	* });
27165	* ```
27166	*
27167	* @example
27168	* When the mouse leaves the `my-layer` layer,
27169	* reset only the `hover` key-value pair in the
27170	* state for the feature under the mouse
27171	* ```ts
27172	* map.on('mouseleave', 'my-layer', (e) => {
27173	*   map.removeFeatureState({
27174	*     source: 'my-source',
27175	*     sourceLayer: 'my-source-layer',
27176	*     id: e.features[0].id
27177	*   }, 'hover');
27178	* });
27179	* ```
27180	*/
27181	removeFeatureState(target, key) {
27182		this.style.removeFeatureState(target, key);
27183		return this._update();
27184	}
27185	/**
27186	* Gets the `state` of a feature.
27187	* A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime.
27188	* Features are identified by their `feature.id` attribute, which can be any number or string.
27189	*
27190	* !!! note
27191	*     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).
27192	*
27193	* @param feature - Feature identifier. Feature objects returned from
27194	* {@link Map.queryRenderedFeatures} or event handlers can be used as feature identifiers.
27195	* @returns The state of the feature: a set of key-value pairs that was assigned to the feature at runtime.
27196	*
27197	* @example
27198	* When the mouse moves over the `my-layer` layer,
27199	* get the feature state for the feature under the mouse
27200	* ```ts
27201	* map.on('mousemove', 'my-layer', (e) => {
27202	*   if (e.features.length > 0) {
27203	*     map.getFeatureState({
27204	*       source: 'my-source',
27205	*       sourceLayer: 'my-source-layer',
27206	*       id: e.features[0].id
27207	*     });
27208	*   }
27209	* });
27210	* ```
27211	*/
27212	getFeatureState(feature) {
27213		return this.style.getFeatureState(feature);
27214	}
27215	/**
27216	* Returns the map's containing HTML element.
27217	*
27218	* @returns The map's container.
27219	*/
27220	getContainer() {
27221		return this._container;
27222	}
27223	/**
27224	* Returns the HTML element containing the map's `<canvas>` element.
27225	*
27226	* If you want to add non-GL overlays to the map, you should append them to this element.
27227	*
27228	* This is the element to which event bindings for map interactivity (such as panning and zooming) are
27229	* attached. It will receive bubbled events from child elements such as the `<canvas>`, but not from
27230	* map controls.
27231	*
27232	* @returns The container of the map's `<canvas>`.
27233	* @see [Create a draggable point](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-draggable-point/)
27234	*/
27235	getCanvasContainer() {
27236		return this._canvasContainer;
27237	}
27238	/**
27239	* Returns the map's `<canvas>` element.
27240	*
27241	* @returns The map's `<canvas>` element.
27242	* @see [Measure distances](https://maplibre.org/maplibre-gl-js/docs/examples/measure-distances/)
27243	* @see [Display a popup on hover](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-hover/)
27244	* @see [Center the map on a clicked symbol](https://maplibre.org/maplibre-gl-js/docs/examples/center-the-map-on-a-clicked-symbol/)
27245	*/
27246	getCanvas() {
27247		return this._canvas;
27248	}
27249	_containerDimensions() {
27250		let width = 0;
27251		let height = 0;
27252		if (this._container) {
27253			width = this._container.clientWidth || 400;
27254			height = this._container.clientHeight || 300;
27255		}
27256		return [width, height];
27257	}
27258	/**
27259	* Determines if the initial resize event should be handled based on the container's dimensions.
27260	*
27261	* @returns `true` if the initial resize event should be handled, `false` otherwise.
27262	*/
27263	_shouldHandleInitialResize() {
27264		if (!this._container?.clientWidth || !this._container.clientHeight) return false;
27265		const [width, height] = this._containerDimensions();
27266		return width !== this._camera.transform.width || height !== this._camera.transform.height;
27267	}
27268	/**
27269	* @internal
27270	* Sets up the ResizeObserver to track container size changes.
27271	* Uses the owning window's ResizeObserver for cross-window support.
27272	*/
27273	_setupResizeObserver() {
27274		let initialResizeEventCaptured = false;
27275		const throttledResizeCallback = throttle((entries) => {
27276			if (this._trackResize && !this._removed) {
27277				this.resize(entries);
27278				this.redraw();
27279			}
27280		}, 50);
27281		const ResizeObserverClass = this._ownerWindow.ResizeObserver ?? ResizeObserver;
27282		this._resizeObserver = new ResizeObserverClass((entries) => {
27283			if (!initialResizeEventCaptured) {
27284				initialResizeEventCaptured = true;
27285				if (!this._shouldHandleInitialResize()) return;
27286			}
27287			throttledResizeCallback(entries);
27288		});
27289		this._resizeObserver.observe(this._container);
27290	}
27291	/**
27292	* @internal
27293	* Resolves the container option to an HTMLElement.
27294	* Supports string ID, HTMLElement, or cross-window elements (using nodeType check).
27295	*/
27296	_resolveContainer(container) {
27297		if (typeof container === "string") {
27298			const element = document.getElementById(container);
27299			if (!element) throw new Error(`Container '${container}' not found.`);
27300			return element;
27301		}
27302		if (container instanceof HTMLElement) return container;
27303		if (container && typeof container === "object" && container.nodeType === 1) return container;
27304		throw new Error("Invalid type: 'container' must be a String or HTMLElement.");
27305	}
27306	_setupContainer() {
27307		const dimensions = this._containerDimensions();
27308		const clampedPixelRatio = this._getClampedPixelRatio(dimensions[0], dimensions[1]);
27309		const container = this._container;
27310		container.classList.add("maplibregl-map");
27311		const canvasContainer = this._canvasContainer = DOM.create("div", "maplibregl-canvas-container", container);
27312		if (this._interactive) canvasContainer.classList.add("maplibregl-interactive");
27313		this._canvas = DOM.create("canvas", "maplibregl-canvas", canvasContainer);
27314		this._canvas.addEventListener("webglcontextlost", this._contextLost, false);
27315		this._canvas.addEventListener("webglcontextrestored", this._contextRestored, false);
27316		this._canvas.setAttribute("tabindex", this._interactive ? "0" : "-1");
27317		this._canvas.setAttribute("aria-label", this._getUIString("Map.Title"));
27318		this._canvas.setAttribute("role", "region");
27319		this._resizeCanvas(dimensions[0], dimensions[1], clampedPixelRatio);
27320		const controlContainer = this._controlContainer = DOM.create("div", "maplibregl-control-container", container);
27321		const positions = this._controlPositions = {};
27322		for (const positionName of [
27323			"top-left",
27324			"top-right",
27325			"bottom-left",
27326			"bottom-right"
27327		]) positions[positionName] = DOM.create("div", `maplibregl-ctrl-${positionName} `, controlContainer);
27328		this._container.addEventListener("scroll", this._onMapScroll, false);
27329	}
27330	/**
27331	* @internal
27332	* Reverses the DOM mutations of {@link Map._setupContainer}, returning the container element to its pre-construction state.
27333	*/
27334	_cleanupContainer() {
27335		this._canvas.removeEventListener("webglcontextrestored", this._contextRestored, false);
27336		this._canvas.removeEventListener("webglcontextlost", this._contextLost, false);
27337		this._canvasContainer.remove();
27338		this._controlContainer.remove();
27339		this._container.removeEventListener("scroll", this._onMapScroll, false);
27340		this._container.classList.remove("maplibregl-map");
27341	}
27342	_resizeCanvas(width, height, pixelRatio) {
27343		this._canvas.width = Math.floor(pixelRatio * width);
27344		this._canvas.height = Math.floor(pixelRatio * height);
27345		this._canvas.style.width = `${width}px`;
27346		this._canvas.style.height = `${height}px`;
27347	}
27348	/**
27349	* @internal
27350	* Creates the WebGL2 context and the painter. Throws {@link GPUInitializationError} when the context cannot be created.
27351	*/
27352	_setupPainter() {
27353		const attributes = {
27354			...this._canvasContextAttributes,
27355			alpha: true,
27356			depth: true,
27357			stencil: true,
27358			premultipliedAlpha: true
27359		};
27360		let creationEvent = null;
27361		this._canvas.addEventListener("webglcontextcreationerror", (event) => {
27362			creationEvent = event;
27363		}, { once: true });
27364		const gl = this._canvas.getContext("webgl2", attributes);
27365		if (!gl) throw new GPUInitializationError(attributes, creationEvent);
27366		this.painter = new Painter(gl, this._camera.transform);
27367	}
27368	/**
27369	* @internal
27370	* Creates a new specialized transform instance from a projection instance and migrates
27371	* to this new transform, carrying over all the properties of the old transform (center, pitch, etc.).
27372	* When the style's projection is changed (or first set), this function should be called.
27373	*/
27374	migrateProjection(newTransform, newCameraHelper) {
27375		this._camera.migrateProjection(newTransform, newCameraHelper);
27376		this.painter.transform = newTransform;
27377		this.fire(new MapProjectionEvent({ newProjection: this.style.projection.name }));
27378	}
27379	/**
27380	* Returns a Boolean indicating whether the map is fully loaded.
27381	*
27382	* Returns `false` if the style is not yet fully loaded,
27383	* or if there has been a change to the sources or style that
27384	* has not yet fully loaded.
27385	*
27386	* @returns A Boolean indicating whether the map is fully loaded.
27387	*/
27388	loaded() {
27389		return !this._styleDirty && !this._sourcesDirty && !!this.style && this.style.loaded();
27390	}
27391	/**
27392	* @internal
27393	* Update this map's style and sources, and re-render the map.
27394	*
27395	* @param updateStyle - mark the map's style for reprocessing as
27396	* well as its sources
27397	*/
27398	_update(updateStyle) {
27399		if (!this.style?._loaded) return this;
27400		this._styleDirty ||= updateStyle;
27401		this._sourcesDirty = true;
27402		this.triggerRepaint();
27403		return this;
27404	}
27405	/**
27406	* @internal
27407	* Request that the given callback be executed during the next render
27408	* frame.  Schedule a render frame if one is not already scheduled.
27409	*
27410	* @returns An id that can be used to cancel the callback
27411	*/
27412	_requestRenderFrame(callback) {
27413		this._update();
27414		return this._renderTaskQueue.add(callback);
27415	}
27416	_cancelRenderFrame(id) {
27417		this._renderTaskQueue.remove(id);
27418	}
27419	/**
27420	* @internal
27421	* Call when a (re-)render of the map is required:
27422	*
27423	* - The style has changed (`setPaintProperty()`, etc.)
27424	* - Source data has changed (e.g. tiles have finished loading)
27425	* - The map has is moving (or just finished moving)
27426	* - A transition is in progress
27427	*
27428	* @param paintStartTimeStamp - The time when the animation frame began executing.
27429	*/
27430	_render(paintStartTimeStamp) {
27431		const fadeDuration = this._idleTriggered ? this._fadeDuration : 0;
27432		const isGlobeRendering = this.style.projection?.transitionState > 0;
27433		this.painter.context.setDirty();
27434		this.painter.setBaseState();
27435		this._renderTaskQueue.run(paintStartTimeStamp);
27436		if (this._removed) return;
27437		let crossFading = false;
27438		if (this.style && this._styleDirty) {
27439			this._styleDirty = false;
27440			const zoom = this._camera.transform.zoom;
27441			const currentTime = now();
27442			this.style.zoomHistory.update(zoom, currentTime);
27443			const parameters = new EvaluationParameters(zoom, {
27444				now: currentTime,
27445				fadeDuration,
27446				zoomHistory: this.style.zoomHistory,
27447				transition: this.style.getTransition()
27448			});
27449			const factor = parameters.crossFadingFactor();
27450			if (factor !== 1 || factor !== this._crossFadingFactor) {
27451				crossFading = true;
27452				this._crossFadingFactor = factor;
27453			}
27454			this.style.update(parameters);
27455		}
27456		const globeRenderingChanged = this.style.projection?.transitionState > 0 !== isGlobeRendering;
27457		this._camera.transform.setTransitionState(this.style.projection?.transitionState);
27458		if (this.style && (this._sourcesDirty || globeRenderingChanged)) {
27459			this._sourcesDirty = false;
27460			this.style._updateSources(this._camera.transform);
27461		}
27462		if (this.terrain) {
27463			if (this.terrain.tileManager.update(this._camera.transform, this.terrain)) this.terrain.resetElevationCache();
27464			this._camera.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._camera.transform.center, this._camera.transform.tileZoom));
27465			if (!this._camera.elevationFreeze && this.getCenterClampedToGround()) this._camera.transform.setElevation(this.terrain.getElevationForLngLat(this._camera.transform.center, this._camera.transform));
27466		} else {
27467			this._camera.transform.setMinElevationForCurrentTile(0);
27468			if (this.getCenterClampedToGround()) this._camera.transform.setElevation(0);
27469		}
27470		this._placementDirty = this.style?._updatePlacement(this._camera.transform, this.showCollisionBoxes, fadeDuration, this._crossSourceCollisions, globeRenderingChanged);
27471		this.painter.render(this.style, {
27472			showTileBoundaries: this.showTileBoundaries,
27473			showOverdrawInspector: this._showOverdrawInspector,
27474			rotating: this.isRotating(),
27475			zooming: this.isZooming(),
27476			moving: this.isMoving(),
27477			fadeDuration,
27478			showPadding: this.showPadding,
27479			anisotropicFilterPitch: this.getAnisotropicFilterPitch()
27480		});
27481		this.fire(new MapLibreEvent("render"));
27482		if (this.loaded() && !this._loaded) {
27483			this._loaded = true;
27484			this.fire(new MapLibreEvent("load"));
27485		}
27486		if (this.style && (this.style.hasTransitions() || crossFading)) this._styleDirty = true;
27487		if (this.style && !this._placementDirty) this.style._releaseSymbolFadeTiles();
27488		const somethingDirty = this._sourcesDirty || this._styleDirty || this._placementDirty || this.painter.renderToTexture?.needsFollowUpFrame;
27489		if (somethingDirty || this._repaint) this.triggerRepaint();
27490		else if (!this.isMoving() && this.loaded()) this.fire(new MapLibreEvent("idle"));
27491		if (this._loaded && !this._fullyLoaded && !somethingDirty) this._fullyLoaded = true;
27492		return this;
27493	}
27494	/**
27495	* Force a synchronous redraw of the map.
27496	* @example
27497	* ```ts
27498	* map.redraw();
27499	* ```
27500	*/
27501	redraw() {
27502		if (this.style) {
27503			if (this._frameRequest) {
27504				this._frameRequest.abort();
27505				this._frameRequest = null;
27506			}
27507			this._render(0);
27508		}
27509		return this;
27510	}
27511	/**
27512	* Clean up and release all internal resources associated with this map.
27513	*
27514	* This includes DOM elements, event bindings, web workers, and WebGL resources.
27515	*
27516	* Use this method when you are done using the map and wish to ensure that it no
27517	* longer consumes browser resources. Afterwards, you must not call any other
27518	* methods on the map.
27519	*/
27520	remove() {
27521		if (this._hash) this._hash.remove();
27522		for (const control of this._controls) control.onRemove(this);
27523		this._controls = [];
27524		if (this._frameRequest) {
27525			this._frameRequest.abort();
27526			this._frameRequest = null;
27527		}
27528		this._renderTaskQueue.clear();
27529		this._diffStyleRequest?.abort();
27530		this.painter.destroy();
27531		this._handlers.destroy();
27532		this.setStyle(null);
27533		if (typeof window !== "undefined") this._ownerWindow.removeEventListener("online", this._onWindowOnline, false);
27534		ImageRequest.removeThrottleControl(this._imageQueueHandle);
27535		this._resizeObserver?.disconnect();
27536		const extension = this.painter.context.gl.getExtension("WEBGL_lose_context");
27537		if (extension?.loseContext) extension.loseContext();
27538		this._cleanupContainer();
27539		this._removed = true;
27540		this.fire(new MapLibreEvent("remove"));
27541	}
27542	/**
27543	* Trigger the rendering of a single frame. Use this method with custom layers to
27544	* repaint the map when the layer changes. Calling this multiple times before the
27545	* next frame is rendered will still result in only a single frame being rendered.
27546	* @example
27547	* ```ts
27548	* map.triggerRepaint();
27549	* ```
27550	* @see [Add a 3D model](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-3d-model-using-threejs/)
27551	* @see [Add an animated icon to the map](https://maplibre.org/maplibre-gl-js/docs/examples/add-an-animated-icon-to-the-map/)
27552	*/
27553	triggerRepaint() {
27554		if (this.style && !this._frameRequest) {
27555			this._frameRequest = new AbortController();
27556			browser.frame(this._frameRequest, (paintStartTimeStamp) => {
27557				this._frameRequest = null;
27558				try {
27559					this._render(paintStartTimeStamp);
27560				} catch (error) {
27561					if (!isAbortError(error)) throw error;
27562				}
27563			}, () => {}, this._ownerWindow);
27564		}
27565	}
27566	/**
27567	* Gets and sets a Boolean indicating whether the map will render an outline
27568	* around each tile and the tile ID. These tile boundaries are useful for
27569	* debugging.
27570	*
27571	* The uncompressed file size of the first vector source is drawn in the top left
27572	* corner of each tile, next to the tile ID.
27573	*
27574	* @example
27575	* ```ts
27576	* map.showTileBoundaries = true;
27577	* ```
27578	*/
27579	get showTileBoundaries() {
27580		return !!this._showTileBoundaries;
27581	}
27582	set showTileBoundaries(value) {
27583		if (this._showTileBoundaries === value) return;
27584		this._showTileBoundaries = value;
27585		this._update();
27586	}
27587	/**
27588	* Gets and sets a Boolean indicating whether the map will visualize
27589	* the padding offsets.
27590	*/
27591	get showPadding() {
27592		return !!this._showPadding;
27593	}
27594	set showPadding(value) {
27595		if (this._showPadding === value) return;
27596		this._showPadding = value;
27597		this._update();
27598	}
27599	/**
27600	* Gets and sets a Boolean indicating whether the map will render boxes
27601	* around all symbols in the data source, revealing which symbols
27602	* were rendered or which were hidden due to collisions.
27603	* This information is useful for debugging.
27604	*/
27605	get showCollisionBoxes() {
27606		return !!this._showCollisionBoxes;
27607	}
27608	set showCollisionBoxes(value) {
27609		if (this._showCollisionBoxes === value) return;
27610		this._showCollisionBoxes = value;
27611		if (value) this.style._generateCollisionBoxes();
27612		else this._update();
27613	}
27614	/**
27615	* Gets and sets a Boolean indicating whether the map should color-code
27616	* each fragment to show how many times it has been shaded.
27617	* White fragments have been shaded 8 or more times.
27618	* Black fragments have been shaded 0 times.
27619	* This information is useful for debugging.
27620	*/
27621	get showOverdrawInspector() {
27622		return !!this._showOverdrawInspector;
27623	}
27624	set showOverdrawInspector(value) {
27625		if (this._showOverdrawInspector === value) return;
27626		this._showOverdrawInspector = value;
27627		this._update();
27628	}
27629	/**
27630	* Gets and sets a Boolean indicating whether the map will
27631	* continuously repaint. This information is useful for analyzing performance.
27632	*/
27633	get repaint() {
27634		return !!this._repaint;
27635	}
27636	set repaint(value) {
27637		if (this._repaint !== value) {
27638			this._repaint = value;
27639			this.triggerRepaint();
27640		}
27641	}
27642	get vertices() {
27643		return !!this._vertices;
27644	}
27645	set vertices(value) {
27646		this._vertices = value;
27647		this._update();
27648	}
27649	/**
27650	* Returns the package version of the library
27651	* @returns Package version of the library
27652	*/
27653	get version() {
27654		return version$1;
27655	}
27656	/**
27657	* Returns the elevation for the point where the camera is looking.
27658	* This value corresponds to:
27659	* "meters above sea level" * "exaggeration"
27660	* @returns The elevation.
27661	*/
27662	getCameraTargetElevation() {
27663		return this._camera.transform.elevation;
27664	}
27665	/**
27666	* Gets the {@link ProjectionSpecification}.
27667	* @returns the projection specification.
27668	* @example
27669	* ```ts
27670	* let projection = map.getProjection();
27671	* ```
27672	*/
27673	getProjection() {
27674		return this.style.getProjection();
27675	}
27676	/**
27677	* Sets the {@link ProjectionSpecification}.
27678	* @param projection - the projection specification to set
27679	* @returns
27680	*/
27681	setProjection(projection) {
27682		this._lazyInitEmptyStyle();
27683		this.style.setProjection(projection);
27684		return this._update(true);
27685	}
27686};
27687//#endregion
27688//#region src/ui/control/navigation_control.ts
27689const defaultOptions$3 = {
27690	showCompass: true,
27691	showZoom: true,
27692	visualizePitch: false,
27693	visualizeRoll: true
27694};
27695/**
27696* A `NavigationControl` control contains zoom buttons and a compass.
27697*
27698* @group Markers and Controls
27699*
27700* @example
27701* ```ts
27702* let nav = new NavigationControl();
27703* map.addControl(nav, 'top-left');
27704* ```
27705* @see [Display map navigation controls](https://maplibre.org/maplibre-gl-js/docs/examples/display-map-navigation-controls/)
27706*/
27707var NavigationControl = class {
27708	/**
27709	* @param options - the control's options
27710	*/
27711	constructor(options) {
27712		this._updateZoomButtons = () => {
27713			const zoom = this._map.getZoom();
27714			const isMax = zoom === this._map.getMaxZoom();
27715			const isMin = zoom === this._map.getMinZoom(true);
27716			this._zoomInButton.disabled = isMax;
27717			this._zoomOutButton.disabled = isMin;
27718			this._zoomInButton.setAttribute("aria-disabled", isMax.toString());
27719			this._zoomOutButton.setAttribute("aria-disabled", isMin.toString());
27720		};
27721		this._rotateCompassArrow = () => {
27722			const pitch = this._map.getPitch();
27723			const roll = this._map.getRoll();
27724			const bearing = this._map.getBearing();
27725			const pitchScale = 1 / Math.pow(Math.cos(degreesToRadians(pitch)), .5);
27726			if (this.options.visualizePitch && this.options.visualizeRoll) {
27727				this._compassIcon.style.transform = `scale(${pitchScale}) rotateZ(${-roll}deg) rotateX(${pitch}deg) rotateZ(${-bearing}deg)`;
27728				return;
27729			}
27730			if (this.options.visualizePitch) {
27731				this._compassIcon.style.transform = `scale(${pitchScale}) rotateX(${pitch}deg) rotateZ(${-bearing}deg)`;
27732				return;
27733			}
27734			if (this.options.visualizeRoll) {
27735				this._compassIcon.style.transform = `rotate(${-bearing - roll}deg)`;
27736				return;
27737			}
27738			this._compassIcon.style.transform = `rotate(${-bearing}deg)`;
27739		};
27740		this._setButtonTitle = (button, title) => {
27741			const str = this._map._getUIString(`NavigationControl.${title}`);
27742			button.title = str;
27743			button.setAttribute("aria-label", str);
27744		};
27745		this.options = extend({}, defaultOptions$3, options);
27746		this._container = DOM.create("div", "maplibregl-ctrl maplibregl-ctrl-group");
27747		this._container.addEventListener("contextmenu", (e) => e.preventDefault());
27748		if (this.options.showZoom) {
27749			this._zoomInButton = this._createButton("maplibregl-ctrl-zoom-in", (e) => this._map.zoomIn({}, { originalEvent: e }));
27750			DOM.create("span", "maplibregl-ctrl-icon", this._zoomInButton).setAttribute("aria-hidden", "true");
27751			this._zoomOutButton = this._createButton("maplibregl-ctrl-zoom-out", (e) => this._map.zoomOut({}, { originalEvent: e }));
27752			DOM.create("span", "maplibregl-ctrl-icon", this._zoomOutButton).setAttribute("aria-hidden", "true");
27753		}
27754		if (this.options.showCompass) {
27755			this._compass = this._createButton("maplibregl-ctrl-compass", (e) => {
27756				if (this.options.visualizePitch) this._map.resetNorthPitch({}, { originalEvent: e });
27757				else this._map.resetNorth({}, { originalEvent: e });
27758			});
27759			this._compassIcon = DOM.create("span", "maplibregl-ctrl-icon", this._compass);
27760			this._compassIcon.setAttribute("aria-hidden", "true");
27761		}
27762	}
27763	/** {@inheritDoc IControl.onAdd} */
27764	onAdd(map) {
27765		this._map = map;
27766		if (this.options.showZoom) {
27767			this._setButtonTitle(this._zoomInButton, "ZoomIn");
27768			this._setButtonTitle(this._zoomOutButton, "ZoomOut");
27769			this._map.on("move", this._updateZoomButtons);
27770			this._updateZoomButtons();
27771		}
27772		if (this.options.showCompass) {
27773			this._setButtonTitle(this._compass, "ResetBearing");
27774			if (this.options.visualizePitch) this._map.on("pitch", this._rotateCompassArrow);
27775			if (this.options.visualizeRoll) this._map.on("roll", this._rotateCompassArrow);
27776			this._map.on("rotate", this._rotateCompassArrow);
27777			this._rotateCompassArrow();
27778			this._handler = new MouseRotateWrapper(this._map, this._compass, this.options.visualizePitch);
27779		}
27780		return this._container;
27781	}
27782	/** {@inheritDoc IControl.onRemove} */
27783	onRemove() {
27784		this._container.remove();
27785		if (this.options.showZoom) this._map.off("move", this._updateZoomButtons);
27786		if (this.options.showCompass) {
27787			if (this.options.visualizePitch) this._map.off("pitch", this._rotateCompassArrow);
27788			if (this.options.visualizeRoll) this._map.off("roll", this._rotateCompassArrow);
27789			this._map.off("rotate", this._rotateCompassArrow);
27790			this._handler.off();
27791			delete this._handler;
27792		}
27793		delete this._map;
27794	}
27795	_createButton(className, fn) {
27796		const a = DOM.create("button", className, this._container);
27797		a.type = "button";
27798		a.addEventListener("click", fn);
27799		return a;
27800	}
27801};
27802var MouseRotateWrapper = class {
27803	constructor(map, element, pitch = false) {
27804		this.mousedown = (e) => {
27805			this.startMove(e, DOM.mousePos(this.element, e));
27806			window.addEventListener("mousemove", this.mousemove);
27807			window.addEventListener("mouseup", this.mouseup);
27808		};
27809		this.mousemove = (e) => {
27810			this.move(e, DOM.mousePos(this.element, e));
27811		};
27812		this.mouseup = (e) => {
27813			this._rotatePitchHandler.dragEnd(e);
27814			this.offTemp();
27815		};
27816		this.touchstart = (e) => {
27817			if (e.targetTouches.length !== 1) this.reset();
27818			else {
27819				this._startPos = this._lastPos = DOM.touchPos(this.element, e.targetTouches)[0];
27820				this.startMove(e, this._startPos);
27821				window.addEventListener("touchmove", this.touchmove, { passive: false });
27822				window.addEventListener("touchend", this.touchend);
27823			}
27824		};
27825		this.touchmove = (e) => {
27826			if (e.targetTouches.length !== 1) this.reset();
27827			else {
27828				this._lastPos = DOM.touchPos(this.element, e.targetTouches)[0];
27829				this.move(e, this._lastPos);
27830			}
27831		};
27832		this.touchend = (e) => {
27833			if (e.targetTouches.length === 0 && this._startPos && this._lastPos && this._startPos.dist(this._lastPos) < this._clickTolerance) this.element.click();
27834			delete this._startPos;
27835			delete this._lastPos;
27836			this.offTemp();
27837		};
27838		this.reset = () => {
27839			this._rotatePitchHandler.reset();
27840			delete this._startPos;
27841			delete this._lastPos;
27842			this.offTemp();
27843		};
27844		this._clickTolerance = 10;
27845		this.element = element;
27846		const moveStateManager = new MouseOrTouchMoveStateManager();
27847		this._rotatePitchHandler = new DragHandler({
27848			clickTolerance: 3,
27849			move: (lastPoint, currentPoint) => {
27850				const rect = element.getBoundingClientRect();
27851				const center = new Point((rect.bottom - rect.top) / 2, (rect.right - rect.left) / 2);
27852				return {
27853					bearingDelta: getAngleDelta(new Point(lastPoint.x, currentPoint.y), currentPoint, center),
27854					pitchDelta: pitch ? (currentPoint.y - lastPoint.y) * -.5 : void 0
27855				};
27856			},
27857			moveStateManager,
27858			enable: true,
27859			assignEvents: () => {}
27860		});
27861		this.map = map;
27862		element.addEventListener("mousedown", this.mousedown);
27863		element.addEventListener("touchstart", this.touchstart, { passive: false });
27864		element.addEventListener("touchcancel", this.reset);
27865	}
27866	startMove(e, point) {
27867		this._rotatePitchHandler.dragStart(e, point);
27868		DOM.disableDrag();
27869	}
27870	move(e, point) {
27871		const map = this.map;
27872		const { bearingDelta, pitchDelta } = this._rotatePitchHandler.dragMove(e, point) || {};
27873		if (bearingDelta) map.setBearing(map.getBearing() + bearingDelta);
27874		if (pitchDelta) map.setPitch(map.getPitch() + pitchDelta);
27875	}
27876	off() {
27877		const element = this.element;
27878		element.removeEventListener("mousedown", this.mousedown);
27879		element.removeEventListener("touchstart", this.touchstart);
27880		window.removeEventListener("touchmove", this.touchmove);
27881		window.removeEventListener("touchend", this.touchend);
27882		element.removeEventListener("touchcancel", this.reset);
27883		this.offTemp();
27884	}
27885	offTemp() {
27886		DOM.enableDrag();
27887		window.removeEventListener("mousemove", this.mousemove);
27888		window.removeEventListener("mouseup", this.mouseup);
27889		window.removeEventListener("touchmove", this.touchmove);
27890		window.removeEventListener("touchend", this.touchend);
27891	}
27892};
27893//#endregion
27894//#region src/util/geolocation_support.ts
27895let supportsGeolocation;
27896async function checkGeolocationSupport(forceRecalculation = false) {
27897	if (supportsGeolocation !== void 0 && !forceRecalculation) return supportsGeolocation;
27898	if (window.navigator.permissions === void 0) {
27899		supportsGeolocation = !!window.navigator.geolocation;
27900		return supportsGeolocation;
27901	}
27902	try {
27903		supportsGeolocation = (await window.navigator.permissions.query({ name: "geolocation" })).state !== "denied";
27904	} catch {
27905		supportsGeolocation = !!window.navigator.geolocation;
27906	}
27907	return supportsGeolocation;
27908}
27909//#endregion
27910//#region src/util/smart_wrap.ts
27911/**
27912* Given a LngLat, prior projected position, and a transform, return a new LngLat shifted
27913* n × 360° east or west for some n ≥ 0 such that:
27914*
27915* * the projected location of the result is on screen, if possible, and secondarily:
27916* * the difference between the projected location of the result and the prior position
27917*   is minimized.
27918*
27919* The object is to preserve perceived object constancy for Popups and Markers as much as
27920* possible; they should avoid shifting large distances across the screen, even when the
27921* map center changes by ±360° due to automatic wrapping, and when about to go off screen,
27922* should wrap just enough to avoid doing so.
27923*/
27924function smartWrap(lngLat, priorPos, transform, useNormalWrap = false) {
27925	if (useNormalWrap || !transform.getCoveringTilesDetailsProvider().allowWorldCopies()) return lngLat?.wrap();
27926	const originalLngLat = new LngLat(lngLat.lng, lngLat.lat);
27927	lngLat = new LngLat(lngLat.lng, lngLat.lat);
27928	if (priorPos) {
27929		const left = new LngLat(lngLat.lng - 360, lngLat.lat);
27930		const right = new LngLat(lngLat.lng + 360, lngLat.lat);
27931		const delta = transform.locationToScreenPoint(lngLat).distSqr(priorPos);
27932		if (transform.locationToScreenPoint(left).distSqr(priorPos) < delta) lngLat = left;
27933		else if (transform.locationToScreenPoint(right).distSqr(priorPos) < delta) lngLat = right;
27934	}
27935	while (Math.abs(lngLat.lng - transform.center.lng) > 180) {
27936		const pos = transform.locationToScreenPoint(lngLat);
27937		if (pos.x >= 0 && pos.y >= 0 && pos.x <= transform.width && pos.y <= transform.height) break;
27938		if (lngLat.lng > transform.center.lng) lngLat.lng -= 360;
27939		else lngLat.lng += 360;
27940	}
27941	if (lngLat.lng !== originalLngLat.lng && transform.isPointOnMapSurface(transform.locationToScreenPoint(lngLat))) return lngLat;
27942	return originalLngLat;
27943}
27944//#endregion
27945//#region src/ui/anchor.ts
27946const anchorTranslate = {
27947	"center": "translate(-50%,-50%)",
27948	"top": "translate(-50%,0)",
27949	"top-left": "translate(0,0)",
27950	"top-right": "translate(-100%,0)",
27951	"bottom": "translate(-50%,-100%)",
27952	"bottom-left": "translate(0,-100%)",
27953	"bottom-right": "translate(-100%,-100%)",
27954	"left": "translate(0,-50%)",
27955	"right": "translate(-100%,-50%)"
27956};
27957function applyAnchorClass(element, anchor, prefix) {
27958	const classList = element.classList;
27959	for (const key in anchorTranslate) classList.remove(`maplibregl-${prefix}-anchor-${key}`);
27960	classList.add(`maplibregl-${prefix}-anchor-${anchor}`);
27961}
27962//#endregion
27963//#region src/ui/marker.ts
27964/**
27965* Screen-pixel deltas applied when a focused draggable marker is moved with the arrow keys.
27966*/
27967const ARROW_KEY_DELTAS = {
27968	ArrowLeft: [-1, 0],
27969	ArrowRight: [1, 0],
27970	ArrowUp: [0, -1],
27971	ArrowDown: [0, 1]
27972};
27973const KEYBOARD_DRAG_SMALL_STEP = 1;
27974const KEYBOARD_DRAG_LARGE_STEP = 10;
27975/** Fill color of the default marker when {@link MarkerOptions.color} is not given. */
27976const DEFAULT_MARKER_COLOR = "#3FB1CE";
27977/** Height in CSS pixels of the default marker SVG at scale 1. */
27978const DEFAULT_MARKER_HEIGHT = 41;
27979/** Width in CSS pixels of the default marker SVG at scale 1. */
27980const DEFAULT_MARKER_WIDTH = 27;
27981const SVG_NS = "http://www.w3.org/2000/svg";
27982/** Creates an SVG element with the given attributes, set in the order given, and children. */
27983function svgElement(tag, attributes, children = []) {
27984	const element = DOM.createNS(SVG_NS, tag);
27985	for (const name in attributes) element.setAttributeNS(null, name, attributes[name]);
27986	for (const child of children) element.appendChild(child);
27987	return element;
27988}
27989/** The default marker SVG at scale 1 in the default color, built on first use and cloned per marker. */
27990let defaultMarkerTemplate;
27991/**
27992* Returns the shared default marker SVG, building it the first time it is asked for. Assembling
27993* the pin takes about fifty DOM calls, so each default {@link Marker} deep-clones this template
27994* and overrides only its size and its fill, see {@link defaultMarkerFillGroup}.
27995*/
27996function getDefaultMarkerTemplate() {
27997	if (defaultMarkerTemplate) return defaultMarkerTemplate;
27998	const shadow = svgElement("g", {
27999		transform: "translate(3.0, 29.0)",
28000		fill: "#000000"
28001	}, [
28002		["10.5", "5.25002273"],
28003		["10.5", "5.25002273"],
28004		["9.5", "4.77275007"],
28005		["8.5", "4.29549936"],
28006		["7.5", "3.81822308"],
28007		["6.5", "3.34094679"],
28008		["5.5", "2.86367051"],
28009		["4.5", "2.38636864"]
28010	].map(([rx, ry]) => svgElement("ellipse", {
28011		opacity: "0.04",
28012		cx: "10.5",
28013		cy: "5.80029008",
28014		rx,
28015		ry
28016	})));
28017	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" })]);
28018	const border = svgElement("g", {
28019		opacity: "0.25",
28020		fill: "#000000"
28021	}, [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" })]);
28022	const maki = svgElement("g", {
28023		transform: "translate(6.0, 7.0)",
28024		fill: "#FFFFFF"
28025	});
28026	const circles = svgElement("g", { transform: "translate(8.0, 8.0)" }, [svgElement("circle", {
28027		fill: "#000000",
28028		opacity: "0.25",
28029		cx: "5.5",
28030		cy: "5.5",
28031		r: "5.4999962"
28032	}), svgElement("circle", {
28033		fill: "#FFFFFF",
28034		cx: "5.5",
28035		cy: "5.5",
28036		r: "5.4999962"
28037	})]);
28038	defaultMarkerTemplate = svgElement("svg", {
28039		display: "block",
28040		height: `${DEFAULT_MARKER_HEIGHT}px`,
28041		width: `${DEFAULT_MARKER_WIDTH}px`,
28042		viewBox: `0 0 ${DEFAULT_MARKER_WIDTH} ${DEFAULT_MARKER_HEIGHT}`
28043	}, [svgElement("g", { "fill-rule": "nonzero" }, [
28044		shadow,
28045		background,
28046		border,
28047		maki,
28048		circles
28049	])]);
28050	return defaultMarkerTemplate;
28051}
28052/** The group of a default marker SVG that carries the pin's fill color: the second child of its single top-level group. */
28053function defaultMarkerFillGroup(svg) {
28054	return svg.firstElementChild.children[1];
28055}
28056/**
28057* The event class for marker drag events (`dragstart`, `drag` and `dragend`).
28058*
28059* @group Event Related
28060*/
28061var MarkerDragEvent = class extends Event {};
28062/**
28063* The event class for the marker `click` event.
28064*
28065* @group Event Related
28066*/
28067var MarkerClickEvent = class extends Event {};
28068/**
28069* Creates a marker component
28070*
28071* @group Markers and Controls
28072*
28073* @example
28074* ```ts
28075* let marker = new Marker()
28076*   .setLngLat([30.5, 50.5])
28077*   .addTo(map);
28078* ```
28079*
28080* @example
28081* Set options
28082* ```ts
28083* let marker = new Marker({
28084*     color: "#FFFFFF",
28085*     draggable: true
28086*   }).setLngLat([30.5, 50.5])
28087*   .addTo(map);
28088* ```
28089* @see [Add a default marker](https://maplibre.org/maplibre-gl-js/docs/examples/add-a-default-marker/)
28090* @see [Add custom icons with Markers](https://maplibre.org/maplibre-gl-js/docs/examples/add-custom-icons-with-markers/)
28091* @see [Create a draggable Marker](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-draggable-marker/)
28092* @see [Animate a marker](https://maplibre.org/maplibre-gl-js/docs/examples/animate-a-marker/)
28093* @see [Attach a popup to a marker instance](https://maplibre.org/maplibre-gl-js/docs/examples/attach-a-popup-to-a-marker-instance/)
28094*
28095* ## Events
28096*
28097* **Event** `dragstart` of type {@link MarkerDragEvent} will be fired when dragging starts.
28098*
28099* **Event** `drag` of type {@link MarkerDragEvent} will be fired while dragging.
28100*
28101* **Event** `dragend` of type {@link MarkerDragEvent} will be fired when the marker is finished being dragged.
28102*
28103* **Event** `click` of type {@link MarkerClickEvent} will be fired when the marker is clicked.
28104*
28105* ## CSS Classes
28106*
28107* **CSS class** `maplibregl-marker-covered` is toggled on the marker element when the marker
28108* is hidden behind 3D terrain or on the back of a globe.
28109* Use this class to apply custom styles to covered markers.
28110*
28111* @example
28112* ```css
28113* .maplibregl-marker-covered {
28114*     pointer-events: none;
28115*     cursor: default;
28116* }
28117* ```
28118*/
28119var Marker = class extends Evented {
28120	/**
28121	* @param options - the options
28122	*/
28123	constructor(options) {
28124		super();
28125		this._onClick = (e) => {
28126			this.fire(new MarkerClickEvent("click", { originalEvent: e }));
28127		};
28128		this._onKeyPress = (e) => {
28129			if (e.code === "Space" || e.code === "Enter") this.togglePopup();
28130		};
28131		this._onKeyDown = (e) => {
28132			if (!this._defaultMarker || !this._draggable || !this._map || !this._lngLat) return;
28133			if (e.composedPath()[0] !== this._element) return;
28134			if (e.altKey || e.ctrlKey || e.metaKey) return;
28135			const delta = ARROW_KEY_DELTAS[e.key];
28136			if (!delta) return;
28137			e.preventDefault();
28138			e.stopPropagation();
28139			const step = e.shiftKey ? KEYBOARD_DRAG_LARGE_STEP : KEYBOARD_DRAG_SMALL_STEP;
28140			const pos = this._map.project(this._lngLat);
28141			this.setLngLat(this._map.unproject(new Point(pos.x + delta[0] * step, pos.y + delta[1] * step)));
28142			if (!this._keyboardDragActive) {
28143				this._keyboardDragActive = true;
28144				this.fire(new MarkerDragEvent("dragstart"));
28145			}
28146			this.fire(new MarkerDragEvent("drag"));
28147		};
28148		this._onKeyUp = (e) => {
28149			if (!ARROW_KEY_DELTAS[e.key]) return;
28150			this._endKeyboardDrag();
28151		};
28152		this._onBlur = () => {
28153			this._endKeyboardDrag();
28154		};
28155		this._onMapClick = (e) => {
28156			const targetElement = e.originalEvent.target;
28157			const element = this._element;
28158			if (this._popup && (targetElement === element || element.contains(targetElement))) this.togglePopup();
28159		};
28160		this._updateCoveredUnthrottled = () => {
28161			const terrain = this._map?.terrain;
28162			if (!terrain || !this._isInViewport()) return;
28163			const covered = this._isCovered(terrain);
28164			if (covered && this._popup?.isOpen()) this._popup.remove();
28165			this._element.style.opacity = covered ? this._opacityWhenCovered : this._opacity;
28166			this._element.classList.toggle("maplibregl-marker-covered", covered);
28167		};
28168		this._updateCovered = throttle(this._updateCoveredUnthrottled, 100);
28169		this._update = (e) => {
28170			if (!this._map) return;
28171			this._lngLat = smartWrap(this._lngLat, this._flatPos, this._map._camera.transform);
28172			this._flatPos = this._pos = this._map.project(this._lngLat)._add(this._offset);
28173			if (this._map.terrain) this._flatPos = this._map._camera.transform.locationToScreenPoint(this._lngLat)._add(this._offset);
28174			let rotation = "";
28175			if (this._rotationAlignment === "viewport" || this._rotationAlignment === "auto") rotation = `rotateZ(${this._rotation}deg)`;
28176			else if (this._rotationAlignment === "map") rotation = `rotateZ(${this._rotation - this._map.getBearing()}deg)`;
28177			let pitch = "";
28178			if (this._pitchAlignment === "viewport" || this._pitchAlignment === "auto") pitch = "rotateX(0deg)";
28179			else if (this._pitchAlignment === "map") pitch = `rotateX(${this._map.getPitch()}deg)`;
28180			if (!this._subpixelPositioning && e?.type !== "move") this._pos = this._pos.round();
28181			this._element.style.transform = `${anchorTranslate[this._anchor]} translate(${this._pos.x}px, ${this._pos.y}px) ${pitch} ${rotation}`;
28182			this._updateOpacity();
28183		};
28184		this._onMove = (e) => {
28185			if (!this._isDragging) {
28186				const clickTolerance = this._clickTolerance || this._map._clickTolerance;
28187				this._isDragging = e.point.dist(this._pointerdownPos) >= clickTolerance;
28188			}
28189			if (!this._isDragging) return;
28190			this._pos = e.point.sub(this._positionDelta);
28191			this._lngLat = this._map.unproject(this._pos);
28192			this.setLngLat(this._lngLat);
28193			this._element.style.pointerEvents = "none";
28194			if (this._state === "pending") {
28195				this._state = "active";
28196				this.fire(new MarkerDragEvent("dragstart"));
28197			}
28198			this.fire(new MarkerDragEvent("drag"));
28199		};
28200		this._onUp = () => {
28201			this._element.style.pointerEvents = "auto";
28202			this._positionDelta = null;
28203			this._pointerdownPos = null;
28204			this._isDragging = false;
28205			this._map.off("mousemove", this._onMove);
28206			this._map.off("touchmove", this._onMove);
28207			if (this._state === "active") this.fire(new MarkerDragEvent("dragend"));
28208			this._state = "inactive";
28209		};
28210		this._addDragHandler = (e) => {
28211			if (this._element.contains(e.originalEvent.target)) {
28212				e.preventDefault();
28213				this._positionDelta = e.point.sub(this._pos).add(this._offset);
28214				this._pointerdownPos = e.point;
28215				this._state = "pending";
28216				this._map.on("mousemove", this._onMove);
28217				this._map.on("touchmove", this._onMove);
28218				this._map.once("mouseup", this._onUp);
28219				this._map.once("touchend", this._onUp);
28220			}
28221		};
28222		this._anchor = options?.anchor || "center";
28223		this._color = options?.color || DEFAULT_MARKER_COLOR;
28224		this._scale = options?.scale || 1;
28225		this._draggable = options?.draggable || false;
28226		this._clickTolerance = options?.clickTolerance || 0;
28227		this._subpixelPositioning = options?.subpixelPositioning || false;
28228		this._isDragging = false;
28229		this._roleManaged = false;
28230		this._tabIndexManaged = false;
28231		this._keyboardDragActive = false;
28232		this._state = "inactive";
28233		this._rotation = options?.rotation || 0;
28234		this._rotationAlignment = options?.rotationAlignment || "auto";
28235		this._pitchAlignment = options?.pitchAlignment && options.pitchAlignment !== "auto" ? options.pitchAlignment : this._rotationAlignment;
28236		this.setOpacity(options?.opacity, options?.opacityWhenCovered);
28237		if (!options?.element) {
28238			this._defaultMarker = true;
28239			this._element = DOM.create("div");
28240			const svg = getDefaultMarkerTemplate().cloneNode(true);
28241			svg.setAttributeNS(null, "height", `${DEFAULT_MARKER_HEIGHT * this._scale}px`);
28242			svg.setAttributeNS(null, "width", `${DEFAULT_MARKER_WIDTH * this._scale}px`);
28243			defaultMarkerFillGroup(svg).setAttributeNS(null, "fill", this._color);
28244			this._element.appendChild(svg);
28245			this._offset = Point.convert(options?.offset || [0, -14]);
28246		} else {
28247			this._element = options.element;
28248			this._offset = Point.convert(options?.offset || [0, 0]);
28249		}
28250		this._element.classList.add("maplibregl-marker");
28251		this._element.addEventListener("dragstart", (e) => {
28252			e.preventDefault();
28253		});
28254		this._element.addEventListener("mousedown", (e) => {
28255			e.preventDefault();
28256		});
28257		applyAnchorClass(this._element, this._anchor, "marker");
28258		if (options?.className) for (const name of options.className.split(" ")) this._element.classList.add(name);
28259		this._popup = null;
28260	}
28261	/**
28262	* Attaches the `Marker` to a `Map` object.
28263	* @param map - The MapLibre GL JS map to add the marker to.
28264	* @example
28265	* ```ts
28266	* let marker = new Marker()
28267	*   .setLngLat([30.5, 50.5])
28268	*   .addTo(map); // add the marker to the map
28269	* ```
28270	*/
28271	addTo(map) {
28272		this.remove();
28273		this._map = map;
28274		if (this._defaultMarker && !this._element.hasAttribute("aria-label")) this._element.setAttribute("aria-label", map._getUIString("Marker.Title"));
28275		this._updateAccessibilityRole();
28276		map.getCanvasContainer().appendChild(this._element);
28277		map.on("move", this._update);
28278		map.on("moveend", this._update);
28279		map.on("terrain", this._update);
28280		map.on("projectiontransition", this._update);
28281		map.on("idle", this._update);
28282		this._element.addEventListener("click", this._onClick);
28283		this.setDraggable(this._draggable);
28284		this._update();
28285		this._map.on("click", this._onMapClick);
28286		return this;
28287	}
28288	/**
28289	* Removes the marker from a map
28290	* @example
28291	* ```ts
28292	* let marker = new Marker().addTo(map);
28293	* marker.remove();
28294	* ```
28295	*/
28296	remove() {
28297		if (this._map) {
28298			this._map.off("click", this._onMapClick);
28299			this._map.off("move", this._update);
28300			this._map.off("moveend", this._update);
28301			this._map.off("terrain", this._update);
28302			this._map.off("projectiontransition", this._update);
28303			this._map.off("idle", this._update);
28304			this._map.off("mousedown", this._addDragHandler);
28305			this._map.off("touchstart", this._addDragHandler);
28306			this._map.off("mouseup", this._onUp);
28307			this._map.off("touchend", this._onUp);
28308			this._map.off("mousemove", this._onMove);
28309			this._map.off("touchmove", this._onMove);
28310			delete this._map;
28311		}
28312		this._element.removeEventListener("click", this._onClick);
28313		this._element.removeEventListener("keydown", this._onKeyDown);
28314		this._element.removeEventListener("keyup", this._onKeyUp);
28315		this._element.removeEventListener("blur", this._onBlur);
28316		this._element.removeEventListener("keypress", this._onKeyPress);
28317		this._keyboardDragActive = false;
28318		this._element.remove();
28319		if (this._popup) this._popup.remove();
28320		return this;
28321	}
28322	/**
28323	* Get the marker's geographical location.
28324	*
28325	* The longitude of the result may differ by a multiple of 360 degrees from the longitude previously
28326	* set by `setLngLat` because `Marker` wraps the anchor longitude across copies of the world to keep
28327	* the marker on screen.
28328	*
28329	* @returns A {@link LngLat} describing the marker's location.
28330	* @example
28331	* ```ts
28332	* // Store the marker's longitude and latitude coordinates in a variable
28333	* let lngLat = marker.getLngLat();
28334	* // Print the marker's longitude and latitude values in the console
28335	* console.log('Longitude: ' + lngLat.lng + ', Latitude: ' + lngLat.lat )
28336	* ```
28337	* @see [Create a draggable Marker](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-draggable-marker/)
28338	*/
28339	getLngLat() {
28340		return this._lngLat;
28341	}
28342	/**
28343	* Set the marker's geographical position and move it.
28344	* @param lnglat - A {@link LngLat} describing where the marker should be located.
28345	* @example
28346	* Create a new marker, set the longitude and latitude, and add it to the map
28347	* ```ts
28348	* new Marker()
28349	*   .setLngLat([-65.017, -16.457])
28350	*   .addTo(map);
28351	* ```
28352	* @see [Add custom icons with Markers](https://maplibre.org/maplibre-gl-js/docs/examples/add-custom-icons-with-markers/)
28353	* @see [Create a draggable Marker](https://maplibre.org/maplibre-gl-js/docs/examples/create-a-draggable-marker/)
28354	*/
28355	setLngLat(lnglat) {
28356		this._lngLat = LngLat.convert(lnglat);
28357		this._pos = null;
28358		this._update();
28359		if (this._popup) this._popup.setLngLat(this._lngLat);
28360		return this;
28361	}
28362	/**
28363	* Returns the `Marker`'s HTML element.
28364	* @returns element
28365	*/
28366	getElement() {
28367		return this._element;
28368	}
28369	/**
28370	* Binds a {@link Popup} to the {@link Marker}.
28371	* @param popup - An instance of the {@link Popup} class. If undefined or null, any popup
28372	* set on this {@link Marker} instance is unset.
28373	* @example
28374	* ```ts
28375	* let marker = new Marker()
28376	*  .setLngLat([0, 0])
28377	*  .setPopup(new Popup().setHTML("<h1>Hello World!</h1>")) // add popup
28378	*  .addTo(map);
28379	* ```
28380	* @see [Attach a popup to a marker instance](https://maplibre.org/maplibre-gl-js/docs/examples/attach-a-popup-to-a-marker-instance/)
28381	*/
28382	setPopup(popup) {
28383		if (this._popup) {
28384			this._popup.remove();
28385			this._popup = null;
28386			this._element.removeEventListener("keypress", this._onKeyPress);
28387		}
28388		if (popup) {
28389			if (!("offset" in popup.options)) {
28390				const markerRadius = 13.5;
28391				const linearOffset = Math.abs(markerRadius) / Math.SQRT2;
28392				popup.options.offset = this._defaultMarker ? {
28393					"top": [0, 0],
28394					"top-left": [0, 0],
28395					"top-right": [0, 0],
28396					"bottom": [0, -38.1],
28397					"bottom-left": [linearOffset, (24.6 + linearOffset) * -1],
28398					"bottom-right": [-linearOffset, (24.6 + linearOffset) * -1],
28399					"left": [markerRadius, -24.6],
28400					"right": [-13.5, -24.6]
28401				} : this._offset;
28402			}
28403			this._popup = popup;
28404			this._element.addEventListener("keypress", this._onKeyPress);
28405		}
28406		this._updateTabIndex();
28407		this._updateAccessibilityRole();
28408		return this;
28409	}
28410	/**
28411	* Set the option to allow subpixel positioning of the marker by passing a boolean
28412	*
28413	* @param value - when set to `true`, subpixel positioning is enabled for the marker.
28414	*
28415	* @example
28416	* ```ts
28417	* let marker = new Marker()
28418	* marker.setSubpixelPositioning(true);
28419	* ```
28420	*/
28421	setSubpixelPositioning(value) {
28422		this._subpixelPositioning = value;
28423		return this;
28424	}
28425	_endKeyboardDrag() {
28426		if (this._keyboardDragActive) {
28427			this._keyboardDragActive = false;
28428			this.fire(new MarkerDragEvent("dragend"));
28429		}
28430	}
28431	/**
28432	* Returns the {@link Popup} instance that is bound to the {@link Marker}.
28433	* @returns popup
28434	* @example
28435	* ```ts
28436	* let marker = new Marker()
28437	*  .setLngLat([0, 0])
28438	*  .setPopup(new Popup().setHTML("<h1>Hello World!</h1>"))
28439	*  .addTo(map);
28440	*
28441	* console.log(marker.getPopup()); // return the popup instance
28442	* ```
28443	*/
28444	getPopup() {
28445		return this._popup;
28446	}
28447	/**
28448	* Opens or closes the {@link Popup} instance that is bound to the {@link Marker}, depending on the current state of the {@link Popup}.
28449	* @example
28450	* ```ts
28451	* let marker = new Marker()
28452	*  .setLngLat([0, 0])
28453	*  .setPopup(new Popup().setHTML("<h1>Hello World!</h1>"))
28454	*  .addTo(map);
28455	*
28456	* marker.togglePopup(); // toggle popup open or closed
28457	* ```
28458	*/
28459	togglePopup() {
28460		const popup = this._popup;
28461		if (this._element.style.opacity === this._opacityWhenCovered) return this;
28462		if (!popup) return this;
28463		else if (popup.isOpen()) popup.remove();
28464		else {
28465			popup.setLngLat(this._lngLat);
28466			popup.addTo(this._map);
28467		}
28468		return this;
28469	}
28470	_updateOpacity() {
28471		if (!this._isInViewport()) return;
28472		const terrain = this._map?.terrain;
28473		const occluded = this._map._camera.transform.isLocationOccluded(this._lngLat);
28474		if (!terrain || occluded) {
28475			const targetOpacity = occluded ? this._opacityWhenCovered : this._opacity;
28476			if (this._element.style.opacity !== targetOpacity) {
28477				this._element.style.opacity = targetOpacity;
28478				this._element.classList.toggle("maplibregl-marker-covered", occluded);
28479			}
28480			return;
28481		}
28482		this._updateCovered();
28483	}
28484	/**
28485	* @internal
28486	* Whether the viewport shows the marker's position. There is nothing to compute for one it does not.
28487	*/
28488	_isInViewport() {
28489		const { width, height } = this._map._camera.transform;
28490		return !!this._pos && this._pos.x >= 0 && this._pos.y >= 0 && this._pos.x <= width && this._pos.y <= height;
28491	}
28492	/**
28493	* @internal
28494	* Whether the terrain covers the marker's center: the location raised by the height `offset` lifts the element
28495	* above it on screen, or the location itself when the offset lowers the element.
28496	*/
28497	_isCovered(terrain) {
28498		const transform = this._map._camera.transform;
28499		const elevation = terrain.getElevationForLngLat(this._lngLat, transform);
28500		const metersToCenter = Math.max(0, -this._offset.y) / transform.pixelsPerMeter;
28501		const elevationToCenter = Math.sin(this._map.getPitch() * Math.PI / 180) * metersToCenter;
28502		return transform.isLocationOccluded(this._lngLat, terrain, elevation + elevationToCenter);
28503	}
28504	/**
28505	* Get the marker's offset.
28506	* @returns The marker's screen coordinates in pixels.
28507	*/
28508	getOffset() {
28509		return this._offset;
28510	}
28511	/**
28512	* Sets the offset of the marker
28513	* @param offset - The offset in pixels as a {@link PointLike} object to apply relative to the element's center. Negatives indicate left and up.
28514	*/
28515	setOffset(offset) {
28516		this._offset = Point.convert(offset);
28517		this._update();
28518		return this;
28519	}
28520	/**
28521	* Adds a CSS class to the marker element.
28522	*
28523	* @param className - on-empty string with CSS class name to add to marker element
28524	*
28525	* @example
28526	* ```
28527	* let marker = new Marker()
28528	* marker.addClassName('some-class')
28529	* ```
28530	*/
28531	addClassName(className) {
28532		this._element.classList.add(className);
28533	}
28534	/**
28535	* Removes a CSS class from the marker element.
28536	*
28537	* @param className - Non-empty string with CSS class name to remove from marker element
28538	*
28539	* @example
28540	* ```ts
28541	* let marker = new Marker()
28542	* marker.removeClassName('some-class')
28543	* ```
28544	*/
28545	removeClassName(className) {
28546		this._element.classList.remove(className);
28547	}
28548	/**
28549	* Add or remove the given CSS class on the marker element, depending on whether the element currently has that class.
28550	*
28551	* @param className - Non-empty string with CSS class name to add/remove
28552	*
28553	* @returns if the class was removed return false, if class was added, then return true
28554	*
28555	* @example
28556	* ```ts
28557	* let marker = new Marker()
28558	* marker.toggleClassName('toggleClass')
28559	* ```
28560	*/
28561	toggleClassName(className) {
28562		return this._element.classList.toggle(className);
28563	}
28564	/**
28565	* Sets the `draggable` property and functionality of the marker.
28566	* A draggable default marker is also keyboard focusable and movable with the
28567	* arrow keys (see {@link MarkerOptions.draggable}); custom marker elements keep
28568	* their focusability and keyboard behavior application-owned.
28569	* @param shouldBeDraggable - Turns drag functionality on/off
28570	*/
28571	setDraggable(shouldBeDraggable) {
28572		this._draggable = !!shouldBeDraggable;
28573		this._element.classList.toggle("maplibregl-marker-draggable", this._draggable);
28574		if (this._map) {
28575			if (shouldBeDraggable) {
28576				this._map.on("mousedown", this._addDragHandler);
28577				this._map.on("touchstart", this._addDragHandler);
28578			} else {
28579				this._map.off("mousedown", this._addDragHandler);
28580				this._map.off("touchstart", this._addDragHandler);
28581			}
28582		}
28583		if (this._defaultMarker) {
28584			if (this._draggable) {
28585				this._element.addEventListener("keydown", this._onKeyDown);
28586				this._element.addEventListener("keyup", this._onKeyUp);
28587				this._element.addEventListener("blur", this._onBlur);
28588			} else {
28589				this._element.removeEventListener("keydown", this._onKeyDown);
28590				this._element.removeEventListener("keyup", this._onKeyUp);
28591				this._element.removeEventListener("blur", this._onBlur);
28592				this._endKeyboardDrag();
28593			}
28594		}
28595		this._updateTabIndex();
28596		this._updateAccessibilityRole();
28597		return this;
28598	}
28599	/**
28600	* Returns true if the marker can be dragged
28601	* @returns True if the marker is draggable.
28602	*/
28603	isDraggable() {
28604		return this._draggable;
28605	}
28606	/**
28607	* Keep the marker element focusable while it has built-in keyboard behavior.
28608	* A popup makes any marker element interactive; dragging only manages
28609	* focusability for the default marker so custom elements stay
28610	* application-owned (#7790). A tabindex supplied by the application is
28611	* never added, changed, or removed here.
28612	*/
28613	_updateTabIndex() {
28614		if (!!this._popup || this._defaultMarker && this._draggable) {
28615			if (!this._element.hasAttribute("tabindex")) {
28616				this._element.setAttribute("tabindex", "0");
28617				this._tabIndexManaged = true;
28618			}
28619		} else if (this._tabIndexManaged) {
28620			if (this._element.getAttribute("tabindex") === "0") this._element.removeAttribute("tabindex");
28621			this._tabIndexManaged = false;
28622		}
28623	}
28624	/**
28625	* Keep the default marker role aligned with interactivity.
28626	* Default markers need a role because `aria-label` is set in {@link Marker.addTo}.
28627	* Non-interactive markers use `role=img`; interactive ones (draggable or with a popup) use `role=button`.
28628	* Click listeners are application-owned and do not automatically change the role.
28629	* Custom marker elements are left alone so applications own their a11y tree.
28630	* Explicit roles set by the application are preserved.
28631	*/
28632	_updateAccessibilityRole() {
28633		if (!this._defaultMarker) return;
28634		if (this._element.hasAttribute("role") && !this._roleManaged) return;
28635		const role = this._draggable || !!this._popup ? "button" : "img";
28636		this._element.setAttribute("role", role);
28637		this._roleManaged = true;
28638	}
28639	/**
28640	* Sets the `rotation` property of the marker.
28641	* @param rotation - The rotation angle of the marker (clockwise, in degrees), relative to its respective {@link Marker.setRotationAlignment} setting.
28642	*/
28643	setRotation(rotation) {
28644		this._rotation = rotation || 0;
28645		this._update();
28646		return this;
28647	}
28648	/**
28649	* Returns the current rotation angle of the marker (in degrees).
28650	* @returns The current rotation angle of the marker.
28651	*/
28652	getRotation() {
28653		return this._rotation;
28654	}
28655	/**
28656	* Sets the `rotationAlignment` property of the marker.
28657	* @param alignment - Sets the `rotationAlignment` property of the marker. defaults to 'auto'
28658	*/
28659	setRotationAlignment(alignment) {
28660		this._rotationAlignment = alignment || "auto";
28661		this._update();
28662		return this;
28663	}
28664	/**
28665	* Returns the current `rotationAlignment` property of the marker.
28666	* @returns The current rotational alignment of the marker.
28667	*/
28668	getRotationAlignment() {
28669		return this._rotationAlignment;
28670	}
28671	/**
28672	* Sets the `pitchAlignment` property of the marker.
28673	* @param alignment - Sets the `pitchAlignment` property of the marker. If alignment is 'auto', it will automatically match `rotationAlignment`.
28674	*/
28675	setPitchAlignment(alignment) {
28676		this._pitchAlignment = alignment && alignment !== "auto" ? alignment : this._rotationAlignment;
28677		this._update();
28678		return this;
28679	}
28680	/**
28681	* Returns the current `pitchAlignment` property of the marker.
28682	* @returns The current pitch alignment of the marker in degrees.
28683	*/
28684	getPitchAlignment() {
28685		return this._pitchAlignment;
28686	}
28687	/**
28688	* Sets the `opacity` and `opacityWhenCovered` properties of the marker.
28689	* When called without arguments, resets opacity and opacityWhenCovered to defaults
28690	* @param opacity - Sets the `opacity` property of the marker.
28691	* @param opacityWhenCovered - Sets the `opacityWhenCovered` property of the marker.
28692	*/
28693	setOpacity(opacity, opacityWhenCovered) {
28694		if (this._opacity === void 0 || opacity === void 0 && opacityWhenCovered === void 0) {
28695			this._opacity = "1";
28696			this._opacityWhenCovered = "0.2";
28697		}
28698		if (opacity !== void 0) this._opacity = String(opacity);
28699		if (opacityWhenCovered !== void 0) this._opacityWhenCovered = String(opacityWhenCovered);
28700		if (this._map) this._updateOpacity();
28701		return this;
28702	}
28703};
28704//#endregion
28705//#region src/ui/control/geolocate_control.ts
28706const defaultOptions$2 = {
28707	positionOptions: {
28708		enableHighAccuracy: false,
28709		maximumAge: 0,
28710		timeout: 6e3
28711	},
28712	fitBoundsOptions: { maxZoom: 15 },
28713	trackUserLocation: false,
28714	showAccuracyCircle: true,
28715	showUserLocation: true
28716};
28717let numberOfWatches = 0;
28718let noTimeout = false;
28719/**
28720* The event class for geolocate control state events
28721* (`trackuserlocationstart`, `trackuserlocationend`, `userlocationfocus` and `userlocationlostfocus`).
28722*
28723* @group Event Related
28724*/
28725var GeolocateEvent = class extends Event {};
28726/**
28727* The event class for the geolocate control `geolocate` and `outofmaxbounds` events.
28728* Carries the [Position](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPosition) returned by the Geolocation API.
28729*
28730* @group Event Related
28731*/
28732var GeolocatePositionEvent = class extends Event {};
28733/**
28734* The event class for the geolocate control `error` event.
28735* Carries the [PositionError](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError) returned by the Geolocation API.
28736*
28737* @group Event Related
28738*/
28739var GeolocateErrorEvent = class extends Event {};
28740/**
28741* A `GeolocateControl` control provides a button that uses the browser's geolocation
28742* API to locate the user on the map.
28743*
28744* Not all browsers support geolocation,
28745* and some users may disable the feature. Geolocation support for modern
28746* browsers including Chrome requires sites to be served over HTTPS. If
28747* geolocation support is not available, the `GeolocateControl` will show
28748* as disabled.
28749*
28750* The zoom level applied will depend on the accuracy of the geolocation provided by the device.
28751*
28752* 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:
28753* * 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.
28754* * passive - the user's location dot automatically updates, but the map's camera does not. Occurs upon the user initiating a map movement.
28755* * disabled - occurs if Geolocation is not available, disabled or denied.
28756*
28757* These interaction states can't be controlled programmatically, rather they are set based on user interactions.
28758*
28759* ## State Diagram
28760* ![GeolocateControl state diagram](https://github.com/maplibre/maplibre-gl-js/assets/3269297/78e720e5-d781-4da8-9803-a7a0e6aaaa9f)
28761*
28762* @group Markers and Controls
28763*
28764* @example
28765* ```ts
28766* map.addControl(new GeolocateControl({
28767*     positionOptions: {
28768*         enableHighAccuracy: true
28769*     },
28770*     trackUserLocation: true
28771* }));
28772* ```
28773* @see [Locate the user](https://maplibre.org/maplibre-gl-js/docs/examples/locate-the-user/)
28774*
28775* ## Events
28776*
28777* **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.
28778*
28779* **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).
28780*
28781* **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.
28782*
28783* **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.
28784*
28785* **Event** `geolocate` of type {@link Event} will be fired on each Geolocation API position update which returned as success.
28786* `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).
28787*
28788* **Event** `error` of type {@link Event} will be fired on each Geolocation API position update which returned as an error.
28789* `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).
28790*
28791* **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`.
28792* `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).
28793*
28794* @example
28795* ```ts
28796* // Initialize the geolocate control.
28797* let geolocate = new GeolocateControl({
28798*   positionOptions: {
28799*       enableHighAccuracy: true
28800*   },
28801*   trackUserLocation: true
28802* });
28803* // Add the control to the map.
28804* map.addControl(geolocate);
28805* // Set an event listener that fires
28806* // when a trackuserlocationend event occurs.
28807* geolocate.on('trackuserlocationend', () => {
28808*   console.log('A trackuserlocationend event has occurred.')
28809* });
28810* ```
28811*
28812* @example
28813* ```ts
28814* // Initialize the geolocate control.
28815* let geolocate = new GeolocateControl({
28816*   positionOptions: {
28817*       enableHighAccuracy: true
28818*   },
28819*   trackUserLocation: true
28820* });
28821* // Add the control to the map.
28822* map.addControl(geolocate);
28823* // Set an event listener that fires
28824* // when a trackuserlocationstart event occurs.
28825* geolocate.on('trackuserlocationstart', () => {
28826*   console.log('A trackuserlocationstart event has occurred.')
28827* });
28828* ```
28829*
28830* @example
28831* ```ts
28832* // Initialize the geolocate control.
28833* let geolocate = new GeolocateControl({
28834*   positionOptions: {
28835*       enableHighAccuracy: true
28836*   },
28837*   trackUserLocation: true
28838* });
28839* // Add the control to the map.
28840* map.addControl(geolocate);
28841* // Set an event listener that fires
28842* // when an userlocationlostfocus event occurs.
28843* geolocate.on('userlocationlostfocus', function() {
28844*   console.log('An userlocationlostfocus event has occurred.')
28845* });
28846* ```
28847*
28848* @example
28849* ```ts
28850* // Initialize the geolocate control.
28851* let geolocate = new GeolocateControl({
28852*   positionOptions: {
28853*       enableHighAccuracy: true
28854*   },
28855*   trackUserLocation: true
28856* });
28857* // Add the control to the map.
28858* map.addControl(geolocate);
28859* // Set an event listener that fires
28860* // when an userlocationfocus event occurs.
28861* geolocate.on('userlocationfocus', function() {
28862*   console.log('An userlocationfocus event has occurred.')
28863* });
28864* ```
28865*
28866* @example
28867* ```ts
28868* // Initialize the geolocate control.
28869* let geolocate = new GeolocateControl({
28870*   positionOptions: {
28871*       enableHighAccuracy: true
28872*   },
28873*   trackUserLocation: true
28874* });
28875* // Add the control to the map.
28876* map.addControl(geolocate);
28877* // Set an event listener that fires
28878* // when a geolocate event occurs.
28879* geolocate.on('geolocate', () => {
28880*   console.log('A geolocate event has occurred.')
28881* });
28882* ```
28883*
28884* @example
28885* ```ts
28886* // Initialize the geolocate control.
28887* let geolocate = new GeolocateControl({
28888*   positionOptions: {
28889*       enableHighAccuracy: true
28890*   },
28891*   trackUserLocation: true
28892* });
28893* // Add the control to the map.
28894* map.addControl(geolocate);
28895* // Set an event listener that fires
28896* // when an error event occurs.
28897* geolocate.on('error', () => {
28898*   console.log('An error event has occurred.')
28899* });
28900* ```
28901*
28902* @example
28903* ```ts
28904* // Initialize the geolocate control.
28905* let geolocate = new GeolocateControl({
28906*   positionOptions: {
28907*       enableHighAccuracy: true
28908*   },
28909*   trackUserLocation: true
28910* });
28911* // Add the control to the map.
28912* map.addControl(geolocate);
28913* // Set an event listener that fires
28914* // when an outofmaxbounds event occurs.
28915* geolocate.on('outofmaxbounds', () => {
28916*   console.log('An outofmaxbounds event has occurred.')
28917* });
28918* ```
28919*/
28920var GeolocateControl = class extends Evented {
28921	/**
28922	* @param options - the control's options
28923	*/
28924	constructor(options) {
28925		super();
28926		this._onSuccess = (position) => {
28927			if (!this._map) return;
28928			if (this._isOutOfMapMaxBounds(position)) {
28929				this._setErrorState();
28930				this.fire(new GeolocatePositionEvent("outofmaxbounds", position));
28931				this._updateMarker();
28932				this._finish();
28933				return;
28934			}
28935			if (this.options.trackUserLocation) {
28936				this._lastKnownPosition = position;
28937				switch (this._watchState) {
28938					case "WAITING_ACTIVE":
28939					case "ACTIVE_LOCK":
28940					case "ACTIVE_ERROR":
28941						this._watchState = "ACTIVE_LOCK";
28942						this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting");
28943						this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active-error");
28944						this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active");
28945						break;
28946					case "BACKGROUND":
28947					case "BACKGROUND_ERROR":
28948						this._watchState = "BACKGROUND";
28949						this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting");
28950						this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background-error");
28951						this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-background");
28952						break;
28953					default: throw new Error(`Unexpected watchState ${this._watchState}`);
28954				}
28955			}
28956			if (this.options.showUserLocation && this._watchState !== "OFF") this._updateMarker(position);
28957			if (!this.options.trackUserLocation || this._watchState === "ACTIVE_LOCK") this._updateCamera(position);
28958			if (this.options.showUserLocation) this._dotElement.classList.remove("maplibregl-user-location-dot-stale");
28959			this.fire(new GeolocatePositionEvent("geolocate", position));
28960			this._finish();
28961		};
28962		this._updateCamera = (position) => {
28963			const center = new LngLat(position.coords.longitude, position.coords.latitude);
28964			const radius = position.coords.accuracy;
28965			const bearing = this._map.getBearing();
28966			const options = extend({ bearing }, this.options.fitBoundsOptions);
28967			const newBounds = LngLatBounds.fromLngLat(center, radius);
28968			this._map.fitBounds(newBounds, options, { geolocateSource: true });
28969		};
28970		this._updateMarker = (position) => {
28971			if (position) {
28972				const center = new LngLat(position.coords.longitude, position.coords.latitude);
28973				this._accuracyCircleMarker.setLngLat(center).addTo(this._map);
28974				this._userLocationDotMarker.setLngLat(center).addTo(this._map);
28975				this._accuracy = position.coords.accuracy;
28976				this._updateCircleRadiusIfNeeded();
28977			} else {
28978				this._userLocationDotMarker.remove();
28979				this._accuracyCircleMarker.remove();
28980			}
28981		};
28982		this._onUpdate = () => {
28983			this._updateCircleRadiusIfNeeded();
28984		};
28985		this._onError = (error) => {
28986			if (!this._map) return;
28987			if (error.code === 1) {
28988				this._watchState = "OFF";
28989				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting");
28990				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active");
28991				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active-error");
28992				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background");
28993				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background-error");
28994				this._geolocateButton.disabled = true;
28995				const title = this._map._getUIString("GeolocateControl.LocationNotAvailable");
28996				this._geolocateButton.title = title;
28997				this._geolocateButton.setAttribute("aria-label", title);
28998				if (this._geolocationWatchID !== void 0) this._clearWatch();
28999			} else if (error.code === 3 && noTimeout) return;
29000			else this._setErrorState();
29001			if (this._watchState !== "OFF" && this.options.showUserLocation) this._dotElement.classList.add("maplibregl-user-location-dot-stale");
29002			this.fire(new GeolocateErrorEvent("error", error));
29003			this._finish();
29004		};
29005		this._finish = () => {
29006			if (this._timeoutId) clearTimeout(this._timeoutId);
29007			this._timeoutId = void 0;
29008		};
29009		this._onMoveStart = (event) => {
29010			if (!this._map) return;
29011			const fromResize = event?.[0] instanceof ResizeObserverEntry;
29012			if (!event.geolocateSource && this._watchState === "ACTIVE_LOCK" && !fromResize && !this._map.isZooming()) {
29013				this._watchState = "BACKGROUND";
29014				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-background");
29015				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active");
29016				this.fire(new GeolocateEvent("trackuserlocationend"));
29017				this.fire(new GeolocateEvent("userlocationlostfocus"));
29018			}
29019		};
29020		this._setupUI = () => {
29021			if (!this._map) return;
29022			this._container.addEventListener("contextmenu", (e) => {
29023				e.preventDefault();
29024			});
29025			this._geolocateButton = DOM.create("button", "maplibregl-ctrl-geolocate", this._container);
29026			DOM.create("span", "maplibregl-ctrl-icon", this._geolocateButton).setAttribute("aria-hidden", "true");
29027			this._geolocateButton.type = "button";
29028			this._geolocateButton.disabled = true;
29029		};
29030		this._finishSetupUI = (supported) => {
29031			if (!this._map) return;
29032			if (supported === false) {
29033				warnOnce("Geolocation support is not available so the GeolocateControl will be disabled.");
29034				const title = this._map._getUIString("GeolocateControl.LocationNotAvailable");
29035				this._geolocateButton.disabled = true;
29036				this._geolocateButton.title = title;
29037				this._geolocateButton.setAttribute("aria-label", title);
29038			} else {
29039				const title = this._map._getUIString("GeolocateControl.FindMyLocation");
29040				this._geolocateButton.disabled = false;
29041				this._geolocateButton.title = title;
29042				this._geolocateButton.setAttribute("aria-label", title);
29043			}
29044			if (this.options.trackUserLocation) {
29045				this._geolocateButton.setAttribute("aria-pressed", "false");
29046				this._watchState = "OFF";
29047			}
29048			if (this.options.showUserLocation) {
29049				this._dotElement = DOM.create("div", "maplibregl-user-location-dot");
29050				this._userLocationDotMarker = new Marker({ element: this._dotElement });
29051				this._circleElement = DOM.create("div", "maplibregl-user-location-accuracy-circle");
29052				this._accuracyCircleMarker = new Marker({
29053					element: this._circleElement,
29054					pitchAlignment: "map"
29055				});
29056				if (this.options.trackUserLocation) this._watchState = "OFF";
29057				this._map.on("zoom", this._onUpdate);
29058				this._map.on("move", this._onUpdate);
29059				this._map.on("rotate", this._onUpdate);
29060				this._map.on("pitch", this._onUpdate);
29061			}
29062			this._geolocateButton.addEventListener("click", () => this.trigger());
29063			this._setup = true;
29064			if (this.options.trackUserLocation) this._map.on("movestart", this._onMoveStart);
29065		};
29066		this.options = extend({}, defaultOptions$2, options);
29067	}
29068	/** {@inheritDoc IControl.onAdd} */
29069	onAdd(map) {
29070		this._map = map;
29071		this._container = DOM.create("div", "maplibregl-ctrl maplibregl-ctrl-group");
29072		this._setupUI();
29073		checkGeolocationSupport().then((supported) => this._finishSetupUI(supported));
29074		return this._container;
29075	}
29076	/** {@inheritDoc IControl.onRemove} */
29077	onRemove() {
29078		if (this._geolocationWatchID !== void 0) {
29079			window.navigator.geolocation.clearWatch(this._geolocationWatchID);
29080			this._geolocationWatchID = void 0;
29081		}
29082		if (this.options.showUserLocation && this._userLocationDotMarker) this._userLocationDotMarker.remove();
29083		if (this.options.showAccuracyCircle && this._accuracyCircleMarker) this._accuracyCircleMarker.remove();
29084		this._container.remove();
29085		this._map.off("movestart", this._onMoveStart);
29086		this._map.off("zoom", this._onUpdate);
29087		this._map.off("move", this._onUpdate);
29088		this._map.off("rotate", this._onUpdate);
29089		this._map.off("pitch", this._onUpdate);
29090		this._map = void 0;
29091		numberOfWatches = 0;
29092		noTimeout = false;
29093	}
29094	/**
29095	* Check if the Geolocation API Position is outside the map's `maxBounds`.
29096	*
29097	* @param position - the Geolocation API Position
29098	* @returns `true` if position is outside the map's `maxBounds`, otherwise returns `false`.
29099	*/
29100	_isOutOfMapMaxBounds(position) {
29101		const bounds = this._map.getMaxBounds();
29102		const coordinates = position.coords;
29103		return bounds && (coordinates.longitude < bounds.getWest() || coordinates.longitude > bounds.getEast() || coordinates.latitude < bounds.getSouth() || coordinates.latitude > bounds.getNorth());
29104	}
29105	_setErrorState() {
29106		switch (this._watchState) {
29107			case "WAITING_ACTIVE":
29108				this._watchState = "ACTIVE_ERROR";
29109				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active");
29110				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active-error");
29111				break;
29112			case "ACTIVE_LOCK":
29113				this._watchState = "ACTIVE_ERROR";
29114				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active");
29115				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active-error");
29116				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting");
29117				break;
29118			case "BACKGROUND":
29119				this._watchState = "BACKGROUND_ERROR";
29120				this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background");
29121				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-background-error");
29122				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting");
29123				break;
29124			case "ACTIVE_ERROR":
29125			case "BACKGROUND_ERROR": break;
29126			case "OFF":
29127			case void 0: break;
29128			default: throw new Error(`Unexpected watchState ${this._watchState}`);
29129		}
29130	}
29131	_updateCircleRadiusIfNeeded() {
29132		const userLocation = this._userLocationDotMarker.getLngLat();
29133		if (!this.options.showUserLocation || !this.options.showAccuracyCircle || !this._accuracy || !userLocation) return;
29134		const screenPosition = this._map.project(userLocation);
29135		const userLocationWith100Px = this._map.unproject([screenPosition.x + 100, screenPosition.y]);
29136		const pixelsToMeters = userLocation.distanceTo(userLocationWith100Px) / 100;
29137		const circleDiameter = 2 * this._accuracy / pixelsToMeters;
29138		this._circleElement.style.width = `${circleDiameter.toFixed(2)}px`;
29139		this._circleElement.style.height = `${circleDiameter.toFixed(2)}px`;
29140	}
29141	/**
29142	* Programmatically request and move the map to the user's location.
29143	*
29144	* @returns `false` if called before control was added to a map, otherwise returns `true`.
29145	* @example
29146	* ```ts
29147	* // Initialize the geolocate control.
29148	* let geolocate = new GeolocateControl({
29149	*  positionOptions: {
29150	*    enableHighAccuracy: true
29151	*  },
29152	*  trackUserLocation: true
29153	* });
29154	* // Add the control to the map.
29155	* map.addControl(geolocate);
29156	* map.on('load', () => {
29157	*   geolocate.trigger();
29158	* });
29159	* ```
29160	*/
29161	trigger() {
29162		if (!this._setup) {
29163			warnOnce("Geolocate control triggered before added to a map");
29164			return false;
29165		}
29166		if (this.options.trackUserLocation) {
29167			switch (this._watchState) {
29168				case "OFF":
29169					this._watchState = "WAITING_ACTIVE";
29170					this.fire(new GeolocateEvent("trackuserlocationstart"));
29171					break;
29172				case "WAITING_ACTIVE":
29173				case "ACTIVE_LOCK":
29174				case "ACTIVE_ERROR":
29175				case "BACKGROUND_ERROR":
29176					numberOfWatches--;
29177					noTimeout = false;
29178					this._watchState = "OFF";
29179					this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting");
29180					this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active");
29181					this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-active-error");
29182					this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background");
29183					this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background-error");
29184					this.fire(new GeolocateEvent("trackuserlocationend"));
29185					break;
29186				case "BACKGROUND":
29187					this._watchState = "ACTIVE_LOCK";
29188					this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-background");
29189					if (this._lastKnownPosition) this._updateCamera(this._lastKnownPosition);
29190					this.fire(new GeolocateEvent("trackuserlocationstart"));
29191					this.fire(new GeolocateEvent("userlocationfocus"));
29192					break;
29193				default: throw new Error(`Unexpected watchState ${this._watchState}`);
29194			}
29195			switch (this._watchState) {
29196				case "WAITING_ACTIVE":
29197					this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting");
29198					this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active");
29199					break;
29200				case "ACTIVE_LOCK":
29201					this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-active");
29202					break;
29203				case "OFF": break;
29204				default: throw new Error(`Unexpected watchState ${this._watchState}`);
29205			}
29206			if (this._watchState === "OFF" && this._geolocationWatchID !== void 0) this._clearWatch();
29207			else if (this._geolocationWatchID === void 0) {
29208				this._geolocateButton.classList.add("maplibregl-ctrl-geolocate-waiting");
29209				this._geolocateButton.setAttribute("aria-pressed", "true");
29210				numberOfWatches++;
29211				let positionOptions;
29212				if (numberOfWatches > 1) {
29213					positionOptions = {
29214						maximumAge: 6e5,
29215						timeout: 0
29216					};
29217					noTimeout = true;
29218				} else {
29219					positionOptions = this.options.positionOptions;
29220					noTimeout = false;
29221				}
29222				this._geolocationWatchID = window.navigator.geolocation.watchPosition(this._onSuccess, this._onError, positionOptions);
29223			}
29224		} else {
29225			window.navigator.geolocation.getCurrentPosition(this._onSuccess, this._onError, this.options.positionOptions);
29226			this._timeoutId = setTimeout(this._finish, 1e4);
29227		}
29228		return true;
29229	}
29230	_clearWatch() {
29231		window.navigator.geolocation.clearWatch(this._geolocationWatchID);
29232		this._geolocationWatchID = void 0;
29233		this._geolocateButton.classList.remove("maplibregl-ctrl-geolocate-waiting");
29234		this._geolocateButton.setAttribute("aria-pressed", "false");
29235		if (this.options.showUserLocation) this._updateMarker(null);
29236	}
29237};
29238//#endregion
29239//#region src/ui/control/scale_control.ts
29240const defaultOptions$1 = {
29241	maxWidth: 100,
29242	unit: "metric"
29243};
29244/**
29245* A `ScaleControl` control displays the ratio of a distance on the map to the corresponding distance on the ground.
29246*
29247* @group Markers and Controls
29248*
29249* @example
29250* ```ts
29251* let scale = new ScaleControl({
29252*     maxWidth: 80,
29253*     unit: 'imperial'
29254* });
29255* map.addControl(scale);
29256*
29257* scale.setUnit('metric');
29258* ```
29259*/
29260var ScaleControl = class {
29261	/**
29262	* @param options - the control's options
29263	*/
29264	constructor(options) {
29265		this._onMove = () => {
29266			updateScale(this._map, this._container, this.options);
29267		};
29268		this.setUnit = (unit) => {
29269			this.options.unit = unit;
29270			updateScale(this._map, this._container, this.options);
29271		};
29272		this.options = {
29273			...defaultOptions$1,
29274			...options
29275		};
29276	}
29277	getDefaultPosition() {
29278		return "bottom-left";
29279	}
29280	/** {@inheritDoc IControl.onAdd} */
29281	onAdd(map) {
29282		this._map = map;
29283		this._container = DOM.create("div", "maplibregl-ctrl maplibregl-ctrl-scale", map.getContainer());
29284		this._map.on("move", this._onMove);
29285		this._onMove();
29286		return this._container;
29287	}
29288	/** {@inheritDoc IControl.onRemove} */
29289	onRemove() {
29290		this._container.remove();
29291		this._map.off("move", this._onMove);
29292		this._map = void 0;
29293	}
29294};
29295function updateScale(map, container, options) {
29296	const optWidth = options?.maxWidth || 100;
29297	const { width: containerWidth, height: containerHeight } = map._camera.transform;
29298	const y = containerHeight / 2;
29299	const x = containerWidth / 2;
29300	const left = map.unproject([x - optWidth / 2, y]);
29301	const right = map.unproject([x + optWidth / 2, y]);
29302	const globeWidth = Math.round(map.project(right).x - map.project(left).x);
29303	const maxWidth = Math.min(optWidth, globeWidth, containerWidth);
29304	const maxMeters = left.distanceTo(right);
29305	if (options?.unit === "imperial") {
29306		const maxFeet = 3.2808 * maxMeters;
29307		if (maxFeet > 5280) setScale(container, maxWidth, maxFeet / 5280, map._getUIString("ScaleControl.Miles"));
29308		else setScale(container, maxWidth, maxFeet, map._getUIString("ScaleControl.Feet"));
29309	} else if (options?.unit === "nautical") setScale(container, maxWidth, maxMeters / 1852, map._getUIString("ScaleControl.NauticalMiles"));
29310	else if (maxMeters >= 1e3) setScale(container, maxWidth, maxMeters / 1e3, map._getUIString("ScaleControl.Kilometers"));
29311	else setScale(container, maxWidth, maxMeters, map._getUIString("ScaleControl.Meters"));
29312}
29313function setScale(container, maxWidth, maxDistance, unit) {
29314	const distance = getRoundNum(maxDistance);
29315	const ratio = distance / maxDistance;
29316	container.style.width = `${maxWidth * ratio}px`;
29317	const label = `${distance}\u00a0${unit}`;
29318	if (container.textContent !== label) container.textContent = label;
29319}
29320function getDecimalRoundNum(d) {
29321	const multiplier = Math.pow(10, Math.ceil(-Math.log(d) / Math.LN10));
29322	return Math.round(d * multiplier) / multiplier;
29323}
29324function getRoundNum(num) {
29325	const pow10 = Math.pow(10, `${Math.floor(num)}`.length - 1);
29326	let d = num / pow10;
29327	d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : d >= 1 ? 1 : getDecimalRoundNum(d);
29328	return pow10 * d;
29329}
29330//#endregion
29331//#region src/ui/control/fullscreen_control.ts
29332/**
29333* The event class for fullscreen control events (`fullscreenstart` and `fullscreenend`).
29334*
29335* @group Event Related
29336*/
29337var FullscreenEvent = class extends Event {};
29338/**
29339* A `FullscreenControl` control contains a button for toggling the map in and out of fullscreen mode.
29340* When [requestFullscreen](https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullscreen) is not supported, fullscreen is handled via CSS properties.
29341* The map's `cooperativeGestures` option is temporarily disabled while the map
29342* is in fullscreen mode, and is restored when the map exist fullscreen mode.
29343*
29344* @group Markers and Controls
29345* @param options - the full screen control options
29346*
29347* @example
29348* ```ts
29349* map.addControl(new FullscreenControl({container: document.querySelector('body')}));
29350* ```
29351* @see [View a fullscreen map](https://maplibre.org/maplibre-gl-js/docs/examples/view-a-fullscreen-map/)
29352*
29353* ## Events
29354*
29355* **Event** `fullscreenstart` of type {@link FullscreenEvent} will be fired when fullscreen mode has started.
29356*
29357* **Event** `fullscreenend` of type {@link FullscreenEvent} will be fired when fullscreen mode has ended.
29358*/
29359var FullscreenControl = class extends Evented {
29360	/**
29361	* @param options - the control's options
29362	*/
29363	constructor(options = {}) {
29364		super();
29365		this._onFullscreenChange = () => {
29366			let fullscreenElement = window.document.fullscreenElement || window.document.webkitFullscreenElement;
29367			while (fullscreenElement?.shadowRoot?.fullscreenElement) fullscreenElement = fullscreenElement.shadowRoot.fullscreenElement;
29368			if (fullscreenElement === this._container !== this._fullscreen) this._handleFullscreenChange();
29369		};
29370		this._onClickFullscreen = () => {
29371			if (this._isFullscreen()) this._exitFullscreen();
29372			else this._requestFullscreen();
29373		};
29374		this._fullscreen = false;
29375		this._pseudo = options.pseudo ?? false;
29376		if (options?.container) {
29377			if (options.container instanceof HTMLElement) this._container = options.container;
29378			else warnOnce("Full screen control 'container' must be a DOM element.");
29379		}
29380		if ("onfullscreenchange" in document) this._fullscreenchange = "fullscreenchange";
29381		else if ("onmozfullscreenchange" in document) this._fullscreenchange = "mozfullscreenchange";
29382		else if ("onwebkitfullscreenchange" in document) this._fullscreenchange = "webkitfullscreenchange";
29383		else if ("onmsfullscreenchange" in document) this._fullscreenchange = "MSFullscreenChange";
29384	}
29385	/** {@inheritDoc IControl.onAdd} */
29386	onAdd(map) {
29387		this._map = map;
29388		this._container ||= this._map.getContainer();
29389		this._controlContainer = DOM.create("div", "maplibregl-ctrl maplibregl-ctrl-group");
29390		this._setupUI();
29391		return this._controlContainer;
29392	}
29393	/** {@inheritDoc IControl.onRemove} */
29394	onRemove() {
29395		this._controlContainer.remove();
29396		this._map = null;
29397		window.document.removeEventListener(this._fullscreenchange, this._onFullscreenChange);
29398	}
29399	_setupUI() {
29400		const button = this._fullscreenButton = DOM.create("button", "maplibregl-ctrl-fullscreen", this._controlContainer);
29401		DOM.create("span", "maplibregl-ctrl-icon", button).setAttribute("aria-hidden", "true");
29402		button.type = "button";
29403		this._updateTitle();
29404		this._fullscreenButton.addEventListener("click", this._onClickFullscreen);
29405		window.document.addEventListener(this._fullscreenchange, this._onFullscreenChange);
29406	}
29407	_updateTitle() {
29408		const title = this._getTitle();
29409		this._fullscreenButton.setAttribute("aria-label", title);
29410		this._fullscreenButton.title = title;
29411	}
29412	_getTitle() {
29413		return this._map._getUIString(this._isFullscreen() ? "FullscreenControl.Exit" : "FullscreenControl.Enter");
29414	}
29415	_isFullscreen() {
29416		return this._fullscreen;
29417	}
29418	_handleFullscreenChange() {
29419		this._fullscreen = !this._fullscreen;
29420		this._fullscreenButton.classList.toggle("maplibregl-ctrl-shrink");
29421		this._fullscreenButton.classList.toggle("maplibregl-ctrl-fullscreen");
29422		this._updateTitle();
29423		if (this._fullscreen) {
29424			this.fire(new FullscreenEvent("fullscreenstart"));
29425			this._prevCooperativeGesturesEnabled = this._map.cooperativeGestures.isEnabled();
29426			this._map.cooperativeGestures.disable();
29427		} else {
29428			this.fire(new FullscreenEvent("fullscreenend"));
29429			if (this._prevCooperativeGesturesEnabled) this._map.cooperativeGestures.enable();
29430		}
29431	}
29432	_exitFullscreen() {
29433		if (this._pseudo) this._togglePseudoFullScreen();
29434		else if (window.document.exitFullscreen) window.document.exitFullscreen();
29435		else if (window.document.webkitCancelFullScreen) window.document.webkitCancelFullScreen();
29436		else this._togglePseudoFullScreen();
29437	}
29438	_requestFullscreen() {
29439		if (this._pseudo) this._togglePseudoFullScreen();
29440		else if (this._container.requestFullscreen) this._container.requestFullscreen();
29441		else if (this._container.webkitRequestFullscreen) this._container.webkitRequestFullscreen();
29442		else this._togglePseudoFullScreen();
29443	}
29444	_togglePseudoFullScreen() {
29445		this._container.classList.toggle("maplibregl-pseudo-fullscreen");
29446		this._handleFullscreenChange();
29447		this._map.resize();
29448	}
29449};
29450//#endregion
29451//#region src/ui/control/terrain_control.ts
29452/**
29453* A `TerrainControl` control contains a button for turning the terrain on and off.
29454*
29455* @group Markers and Controls
29456*
29457* @example
29458* ```ts
29459* let map = new Map({TerrainControl: false})
29460*     .addControl(new TerrainControl({
29461*         source: "terrain"
29462*     }));
29463* ```
29464* @see [3D Terrain](https://maplibre.org/maplibre-gl-js/docs/examples/3d-terrain/)
29465* @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/)
29466* @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/)
29467* @see [Sky, Fog, Terrain](https://maplibre.org/maplibre-gl-js/docs/examples/sky-fog-terrain/)
29468*/
29469var TerrainControl = class {
29470	/**
29471	* @param options - the control's options
29472	*/
29473	constructor(options) {
29474		this._toggleTerrain = () => {
29475			if (this._map.getTerrain()) this._map.setTerrain(null);
29476			else this._map.setTerrain(this.options);
29477			this._updateTerrainIcon();
29478		};
29479		this._updateTerrainIcon = () => {
29480			this._terrainButton.classList.remove("maplibregl-ctrl-terrain");
29481			this._terrainButton.classList.remove("maplibregl-ctrl-terrain-enabled");
29482			if (this._map.terrain) {
29483				this._terrainButton.classList.add("maplibregl-ctrl-terrain-enabled");
29484				this._terrainButton.title = this._map._getUIString("TerrainControl.Disable");
29485			} else {
29486				this._terrainButton.classList.add("maplibregl-ctrl-terrain");
29487				this._terrainButton.title = this._map._getUIString("TerrainControl.Enable");
29488			}
29489		};
29490		this.options = options;
29491	}
29492	/** {@inheritDoc IControl.onAdd} */
29493	onAdd(map) {
29494		this._map = map;
29495		this._container = DOM.create("div", "maplibregl-ctrl maplibregl-ctrl-group");
29496		this._terrainButton = DOM.create("button", "maplibregl-ctrl-terrain", this._container);
29497		DOM.create("span", "maplibregl-ctrl-icon", this._terrainButton).setAttribute("aria-hidden", "true");
29498		this._terrainButton.type = "button";
29499		this._terrainButton.addEventListener("click", this._toggleTerrain);
29500		this._updateTerrainIcon();
29501		this._map.on("terrain", this._updateTerrainIcon);
29502		return this._container;
29503	}
29504	/** {@inheritDoc IControl.onRemove} */
29505	onRemove() {
29506		this._container.remove();
29507		this._map.off("terrain", this._updateTerrainIcon);
29508		this._map = void 0;
29509	}
29510};
29511//#endregion
29512//#region src/ui/control/globe_control.ts
29513/**
29514* A `GlobeControl` control contains a button for toggling the map projection between "mercator" and "globe".
29515*
29516* @group Markers and Controls
29517*
29518* @example
29519* ```ts
29520* let map = new Map()
29521*     .addControl(new GlobeControl());
29522* ```
29523*
29524* @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/)
29525* @see [Sky, Fog, Terrain](https://maplibre.org/maplibre-gl-js/docs/examples/sky-fog-terrain/)
29526*/
29527var GlobeControl = class {
29528	constructor() {
29529		this._toggleProjection = () => {
29530			const currentProjection = this._map.getProjection()?.type;
29531			if (currentProjection === "mercator" || !currentProjection) this._map.setProjection({ type: "globe" });
29532			else this._map.setProjection({ type: "mercator" });
29533			this._updateGlobeIcon();
29534		};
29535		this._updateGlobeIcon = () => {
29536			this._globeButton.classList.remove("maplibregl-ctrl-globe");
29537			this._globeButton.classList.remove("maplibregl-ctrl-globe-enabled");
29538			if (this._map.getProjection()?.type === "globe") {
29539				this._globeButton.classList.add("maplibregl-ctrl-globe-enabled");
29540				this._globeButton.title = this._map._getUIString("GlobeControl.Disable");
29541			} else {
29542				this._globeButton.classList.add("maplibregl-ctrl-globe");
29543				this._globeButton.title = this._map._getUIString("GlobeControl.Enable");
29544			}
29545		};
29546	}
29547	/** {@inheritDoc IControl.onAdd} */
29548	onAdd(map) {
29549		this._map = map;
29550		this._container = DOM.create("div", "maplibregl-ctrl maplibregl-ctrl-group");
29551		this._globeButton = DOM.create("button", "maplibregl-ctrl-globe", this._container);
29552		DOM.create("span", "maplibregl-ctrl-icon", this._globeButton).setAttribute("aria-hidden", "true");
29553		this._globeButton.type = "button";
29554		this._globeButton.addEventListener("click", this._toggleProjection);
29555		this._updateGlobeIcon();
29556		this._map.on("styledata", this._updateGlobeIcon);
29557		this._map.on("projectiontransition", this._updateGlobeIcon);
29558		return this._container;
29559	}
29560	/** {@inheritDoc IControl.onRemove} */
29561	onRemove() {
29562		this._container.remove();
29563		this._map.off("styledata", this._updateGlobeIcon);
29564		this._map.off("projectiontransition", this._updateGlobeIcon);
29565		this._globeButton.removeEventListener("click", this._toggleProjection);
29566		this._map = void 0;
29567	}
29568};
29569//#endregion
29570//#region src/ui/popup.ts
29571const defaultOptions = {
29572	closeButton: true,
29573	closeOnClick: true,
29574	focusAfterOpen: true,
29575	className: "",
29576	maxWidth: "240px",
29577	subpixelPositioning: false,
29578	locationOccludedOpacity: void 0,
29579	padding: void 0
29580};
29581const focusQuerySelector = [
29582	"a[href]",
29583	"[tabindex]:not([tabindex='-1'])",
29584	"[contenteditable]:not([contenteditable='false'])",
29585	"button:not([disabled])",
29586	"input:not([disabled])",
29587	"select:not([disabled])",
29588	"textarea:not([disabled])"
29589].join(", ");
29590/**
29591* The event class for popup events (`open` and `close`).
29592*
29593* @group Event Related
29594*/
29595var PopupEvent = class extends Event {};
29596/**
29597* A popup component.
29598*
29599* @group Markers and Controls
29600*
29601*
29602* @example
29603* Create a popup
29604* ```ts
29605* let popup = new Popup();
29606* // Set an event listener that will fire
29607* // any time the popup is opened
29608* popup.on('open', () => {
29609*   console.log('popup was opened');
29610* });
29611* ```
29612*
29613* @example
29614* Create a popup
29615* ```ts
29616* let popup = new Popup();
29617* // Set an event listener that will fire
29618* // any time the popup is closed
29619* popup.on('close', () => {
29620*   console.log('popup was closed');
29621* });
29622* ```
29623*
29624* @example
29625* ```ts
29626* let markerHeight = 50, markerRadius = 10, linearOffset = 25;
29627* let popupOffsets = {
29628*  'top': [0, 0],
29629*  'top-left': [0,0],
29630*  'top-right': [0,0],
29631*  'bottom': [0, -markerHeight],
29632*  'bottom-left': [linearOffset, (markerHeight - markerRadius + linearOffset) * -1],
29633*  'bottom-right': [-linearOffset, (markerHeight - markerRadius + linearOffset) * -1],
29634*  'left': [markerRadius, (markerHeight - markerRadius) * -1],
29635*  'right': [-markerRadius, (markerHeight - markerRadius) * -1]
29636*  };
29637* let popup = new Popup({offset: popupOffsets, className: 'my-class'})
29638*   .setLngLat(e.lngLat)
29639*   .setHTML("<h1>Hello World!</h1>")
29640*   .setMaxWidth("300px")
29641*   .addTo(map);
29642* ```
29643* @see [Display a popup](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup/)
29644* @see [Display a popup on hover](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-hover/)
29645* @see [Display a popup on click](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-click/)
29646* @see [Attach a popup to a marker instance](https://maplibre.org/maplibre-gl-js/docs/examples/attach-a-popup-to-a-marker-instance/)
29647* @see [Show polygon information on click](https://maplibre.org/maplibre-gl-js/docs/examples/show-polygon-information-on-click/)
29648*
29649* ## Events
29650*
29651* **Event** `open` of type {@link PopupEvent} will be fired when the popup is opened manually or programmatically.
29652*
29653* **Event** `close` of type {@link PopupEvent} will be fired when the popup is closed manually or programmatically.
29654*/
29655var Popup = class extends Evented {
29656	/**
29657	* @param options - the options
29658	*/
29659	constructor(options) {
29660		super();
29661		this._updateOpacity = () => {
29662			if (this.options.locationOccludedOpacity === void 0) return;
29663			if (this._map._camera.transform.isLocationOccluded(this.getLngLat())) this._container.style.opacity = `${this.options.locationOccludedOpacity}`;
29664			else this._container.style.opacity = "";
29665		};
29666		this.remove = () => {
29667			if (this._content) this._content.remove();
29668			if (this._container) {
29669				this._container.remove();
29670				delete this._container;
29671			}
29672			if (this._map) {
29673				this._map.off("move", this._update);
29674				this._map.off("move", this._onClose);
29675				this._map.off("click", this._onClose);
29676				this._map.off("remove", this.remove);
29677				this._map.off("terrain", this._update);
29678				this._map.off("projectiontransition", this._update);
29679				this._map.off("idle", this._update);
29680				this._map.off("mousemove", this._update);
29681				this._map.off("mouseup", this._update);
29682				this._map.off("drag", this._update);
29683				this._map._canvasContainer.classList.remove("maplibregl-track-pointer");
29684				delete this._map;
29685				this.fire(new PopupEvent("close"));
29686			}
29687			return this;
29688		};
29689		this._update = (event) => {
29690			const hasPosition = this._lngLat || this._trackPointer;
29691			if (!this._map || !hasPosition || !this._content) return;
29692			if (!this._container) {
29693				this._container = DOM.create("div", "maplibregl-popup", this._map.getContainer());
29694				this._tip = DOM.create("div", "maplibregl-popup-tip", this._container);
29695				this._container.appendChild(this._content);
29696				if (this.options.className) for (const name of this.options.className.split(" ")) this._container.classList.add(name);
29697				if (this._closeButton) this._closeButton.setAttribute("aria-label", this._map._getUIString("Popup.Close"));
29698				if (this._trackPointer) this._container.classList.add("maplibregl-popup-track-pointer");
29699			}
29700			if (this.options.maxWidth && this._container.style.maxWidth !== this.options.maxWidth) this._container.style.maxWidth = this.options.maxWidth;
29701			this._lngLat = smartWrap(this._lngLat, this._flatPos, this._map._camera.transform, this._trackPointer);
29702			let cursor;
29703			if (event && "point" in event && event.point) cursor = event.point;
29704			if (this._trackPointer && !cursor) return;
29705			const pos = this._flatPos = this._pos = this._trackPointer && cursor ? cursor : this._map.project(this._lngLat);
29706			if (this._map.terrain) this._flatPos = this._trackPointer && cursor ? cursor : this._map._camera.transform.locationToScreenPoint(this._lngLat);
29707			let anchor = this.options.anchor;
29708			const offset = normalizeOffset(this.options.offset);
29709			if (!anchor) {
29710				const width = this._container.offsetWidth;
29711				const height = this._container.offsetHeight;
29712				const padding = normalizePadding(this.options.padding);
29713				let anchorComponents;
29714				if (pos.y + offset.bottom.y < height + padding.top) anchorComponents = ["top"];
29715				else if (pos.y > this._map._camera.transform.height - height - padding.bottom) anchorComponents = ["bottom"];
29716				else anchorComponents = [];
29717				if (pos.x < width / 2 + padding.left) anchorComponents.push("left");
29718				else if (pos.x > this._map._camera.transform.width - width / 2 - padding.right) anchorComponents.push("right");
29719				if (anchorComponents.length === 0) anchor = "bottom";
29720				else anchor = anchorComponents.join("-");
29721			}
29722			let offsetedPos = pos.add(offset[anchor]);
29723			if (!this.options.subpixelPositioning) offsetedPos = offsetedPos.round();
29724			this._container.style.transform = `${anchorTranslate[anchor]} translate(${offsetedPos.x}px,${offsetedPos.y}px)`;
29725			applyAnchorClass(this._container, anchor, "popup");
29726			this._updateOpacity();
29727		};
29728		this._onClose = () => {
29729			this.remove();
29730		};
29731		this.options = extend(Object.create(defaultOptions), options);
29732	}
29733	/**
29734	* Adds the popup to a map.
29735	*
29736	* @param map - The MapLibre GL JS map to add the popup to.
29737	* @example
29738	* ```ts
29739	* new Popup()
29740	*   .setLngLat([0, 0])
29741	*   .setHTML("<h1>Null Island</h1>")
29742	*   .addTo(map);
29743	* ```
29744	* @see [Display a popup](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup/)
29745	* @see [Display a popup on hover](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-hover/)
29746	* @see [Display a popup on click](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-click/)
29747	* @see [Show polygon information on click](https://maplibre.org/maplibre-gl-js/docs/examples/show-polygon-information-on-click/)
29748	*/
29749	addTo(map) {
29750		if (this._map) this.remove();
29751		this._map = map;
29752		if (this.options.closeOnClick) this._map.on("click", this._onClose);
29753		if (this.options.closeOnMove) this._map.on("move", this._onClose);
29754		this._map.on("remove", this.remove);
29755		this._map.on("terrain", this._update);
29756		this._map.on("projectiontransition", this._update);
29757		this._map.on("idle", this._update);
29758		this._update();
29759		this._focusFirstElement();
29760		if (this._trackPointer) {
29761			this._map.on("mousemove", this._update);
29762			this._map.on("mouseup", this._update);
29763			if (this._container) this._container.classList.add("maplibregl-popup-track-pointer");
29764			this._map._canvasContainer.classList.add("maplibregl-track-pointer");
29765		} else this._map.on("move", this._update);
29766		this.fire(new PopupEvent("open"));
29767		return this;
29768	}
29769	/**
29770	* @returns `true` if the popup is open, `false` if it is closed.
29771	*/
29772	isOpen() {
29773		return !!this._map;
29774	}
29775	/**
29776	* Returns the geographical location of the popup's anchor.
29777	*
29778	* The longitude of the result may differ by a multiple of 360 degrees from the longitude previously
29779	* set by `setLngLat` because `Popup` wraps the anchor longitude across copies of the world to keep
29780	* the popup on screen.
29781	*
29782	* @returns The geographical location of the popup's anchor.
29783	*/
29784	getLngLat() {
29785		return this._lngLat;
29786	}
29787	/**
29788	* Sets the geographical location of the popup's anchor, and moves the popup to it. Replaces trackPointer() behavior.
29789	*
29790	* @param lnglat - The geographical location to set as the popup's anchor.
29791	*/
29792	setLngLat(lnglat) {
29793		this._lngLat = LngLat.convert(lnglat);
29794		this._pos = null;
29795		this._flatPos = null;
29796		this._trackPointer = false;
29797		this._update();
29798		if (this._map) {
29799			this._map.on("move", this._update);
29800			this._map.off("mousemove", this._update);
29801			if (this._container) this._container.classList.remove("maplibregl-popup-track-pointer");
29802			this._map._canvasContainer.classList.remove("maplibregl-track-pointer");
29803		}
29804		return this;
29805	}
29806	/**
29807	* Tracks the popup anchor to the cursor position on screens with a pointer device (it will be hidden on touchscreens). Replaces the `setLngLat` behavior.
29808	* For most use cases, set `closeOnClick` and `closeButton` to `false`.
29809	* @example
29810	* ```ts
29811	* let popup = new Popup({ closeOnClick: false, closeButton: false })
29812	*   .setHTML("<h1>Hello World!</h1>")
29813	*   .trackPointer()
29814	*   .addTo(map);
29815	* ```
29816	*/
29817	trackPointer() {
29818		this._trackPointer = true;
29819		this._pos = null;
29820		this._flatPos = null;
29821		this._update();
29822		if (this._map) {
29823			this._map.off("move", this._update);
29824			this._map.on("mousemove", this._update);
29825			this._map.on("drag", this._update);
29826			if (this._container) this._container.classList.add("maplibregl-popup-track-pointer");
29827			this._map._canvasContainer.classList.add("maplibregl-track-pointer");
29828		}
29829		return this;
29830	}
29831	/**
29832	* Returns the `Popup`'s HTML element.
29833	* @example
29834	* Change the `Popup` element's font size
29835	* ```ts
29836	* let popup = new Popup()
29837	*   .setLngLat([-96, 37.8])
29838	*   .setHTML("<p>Hello World!</p>")
29839	*   .addTo(map);
29840	* let popupElem = popup.getElement();
29841	* popupElem.style.fontSize = "25px";
29842	* ```
29843	* @returns element
29844	*/
29845	getElement() {
29846		return this._container;
29847	}
29848	/**
29849	* Sets the popup's content to a string of text.
29850	*
29851	* This function creates a [Text](https://developer.mozilla.org/en-US/docs/Web/API/Text) node in the DOM,
29852	* so it cannot insert raw HTML. Use this method for security against XSS
29853	* if the popup content is user-provided.
29854	*
29855	* @param text - Textual content for the popup.
29856	* @example
29857	* ```ts
29858	* let popup = new Popup()
29859	*   .setLngLat(e.lngLat)
29860	*   .setText('Hello, world!')
29861	*   .addTo(map);
29862	* ```
29863	*/
29864	setText(text) {
29865		return this.setDOMContent(document.createTextNode(text));
29866	}
29867	/**
29868	* Sets the popup's content to the HTML provided as a string.
29869	*
29870	* This method does not perform HTML filtering or sanitization, and must be
29871	* used only with trusted content. Consider {@link Popup.setText} if
29872	* the content is an untrusted text string.
29873	*
29874	* @param html - A string representing HTML content for the popup.
29875	* @example
29876	* ```ts
29877	* let popup = new Popup()
29878	*   .setLngLat(e.lngLat)
29879	*   .setHTML("<h1>Hello World!</h1>")
29880	*   .addTo(map);
29881	* ```
29882	* @see [Display a popup](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup/)
29883	* @see [Display a popup on hover](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-hover/)
29884	* @see [Display a popup on click](https://maplibre.org/maplibre-gl-js/docs/examples/display-a-popup-on-click/)
29885	* @see [Attach a popup to a marker instance](https://maplibre.org/maplibre-gl-js/docs/examples/attach-a-popup-to-a-marker-instance/)
29886	*/
29887	setHTML(html) {
29888		const frag = document.createDocumentFragment();
29889		const temp = document.createElement("body");
29890		let child;
29891		temp.innerHTML = html;
29892		while (true) {
29893			child = temp.firstChild;
29894			if (!child) break;
29895			frag.appendChild(child);
29896		}
29897		return this.setDOMContent(frag);
29898	}
29899	/**
29900	* Returns the popup's maximum width.
29901	*
29902	* @returns The maximum width of the popup.
29903	*/
29904	getMaxWidth() {
29905		return this._container?.style.maxWidth;
29906	}
29907	/**
29908	* Sets the popup's maximum width. This is setting the CSS property `max-width`.
29909	* Available values can be found here: https://developer.mozilla.org/en-US/docs/Web/CSS/max-width
29910	*
29911	* @param maxWidth - A string representing the value for the maximum width.
29912	*/
29913	setMaxWidth(maxWidth) {
29914		this.options.maxWidth = maxWidth;
29915		this._update();
29916		return this;
29917	}
29918	/**
29919	* Sets the popup's content to the element provided as a DOM node.
29920	*
29921	* @param htmlNode - A DOM node to be used as content for the popup.
29922	* @example
29923	* Create an element with the popup content
29924	* ```ts
29925	* let div = document.createElement('div');
29926	* div.innerHTML = 'Hello, world!';
29927	* let popup = new Popup()
29928	*   .setLngLat(e.lngLat)
29929	*   .setDOMContent(div)
29930	*   .addTo(map);
29931	* ```
29932	*/
29933	setDOMContent(htmlNode) {
29934		if (this._content) {
29935			while (this._content.hasChildNodes()) if (this._content.firstChild) this._content.removeChild(this._content.firstChild);
29936		} else this._content = DOM.create("div", "maplibregl-popup-content", this._container);
29937		this._content.appendChild(htmlNode);
29938		this._createCloseButton();
29939		this._update();
29940		this._focusFirstElement();
29941		return this;
29942	}
29943	/**
29944	* Adds a CSS class to the popup container element.
29945	*
29946	* @param className - Non-empty string with CSS class name to add to popup container
29947	*
29948	* @example
29949	* ```ts
29950	* let popup = new Popup()
29951	* popup.addClassName('some-class')
29952	* ```
29953	*/
29954	addClassName(className) {
29955		if (this._container) this._container.classList.add(className);
29956		return this;
29957	}
29958	/**
29959	* Removes a CSS class from the popup container element.
29960	*
29961	* @param className - Non-empty string with CSS class name to remove from popup container
29962	*
29963	* @example
29964	* ```ts
29965	* let popup = new Popup()
29966	* popup.removeClassName('some-class')
29967	* ```
29968	*/
29969	removeClassName(className) {
29970		if (this._container) this._container.classList.remove(className);
29971		return this;
29972	}
29973	/**
29974	* Sets the popup's offset.
29975	*
29976	* @param offset - Sets the popup's offset.
29977	*/
29978	setOffset(offset) {
29979		this.options.offset = offset;
29980		this._update();
29981		return this;
29982	}
29983	/**
29984	* Add or remove the given CSS class on the popup container, depending on whether the container currently has that class.
29985	*
29986	* @param className - Non-empty string with CSS class name to add/remove
29987	*
29988	* @returns if the class was removed return false, if class was added, then return true, undefined if there is no container
29989	*
29990	* @example
29991	* ```ts
29992	* let popup = new Popup()
29993	* popup.toggleClassName('toggleClass')
29994	* ```
29995	*/
29996	toggleClassName(className) {
29997		if (this._container) return this._container.classList.toggle(className);
29998	}
29999	/**
30000	* Set the option to allow subpixel positioning of the popup by passing a boolean
30001	*
30002	* @param value - When boolean is true, subpixel positioning is enabled for the popup.
30003	*
30004	* @example
30005	* ```ts
30006	* let popup = new Popup()
30007	* popup.setSubpixelPositioning(true);
30008	* ```
30009	*/
30010	setSubpixelPositioning(value) {
30011		this.options.subpixelPositioning = value;
30012	}
30013	/**
30014	* Sets the popup's padding constraints for positioning.
30015	*
30016	* @param padding - The padding to apply as a {@link PaddingOptions} object.
30017	* @example
30018	* ```ts
30019	* popup.setPadding({ top: 10, right: 20, bottom: 30, left: 40 });
30020	* ```
30021	*/
30022	setPadding(padding) {
30023		this.options.padding = padding;
30024		this._update();
30025	}
30026	_createCloseButton() {
30027		if (this.options.closeButton) {
30028			this._closeButton = DOM.create("button", "maplibregl-popup-close-button", this._content);
30029			this._closeButton.type = "button";
30030			this._closeButton.innerHTML = "&#215;";
30031			this._closeButton.addEventListener("click", this._onClose);
30032		}
30033	}
30034	_focusFirstElement() {
30035		if (!this.options.focusAfterOpen || !this._container) return;
30036		const firstFocusable = this._container.querySelector(focusQuerySelector);
30037		if (firstFocusable) firstFocusable.focus();
30038	}
30039};
30040function normalizeOffset(offset) {
30041	if (!offset) return normalizeOffset(new Point(0, 0));
30042	else if (typeof offset === "number") {
30043		const cornerOffset = Math.round(Math.abs(offset) / Math.SQRT2);
30044		return {
30045			"center": new Point(0, 0),
30046			"top": new Point(0, offset),
30047			"top-left": new Point(cornerOffset, cornerOffset),
30048			"top-right": new Point(-cornerOffset, cornerOffset),
30049			"bottom": new Point(0, -offset),
30050			"bottom-left": new Point(cornerOffset, -cornerOffset),
30051			"bottom-right": new Point(-cornerOffset, -cornerOffset),
30052			"left": new Point(offset, 0),
30053			"right": new Point(-offset, 0)
30054		};
30055	} else if (offset instanceof Point || Array.isArray(offset)) {
30056		const convertedOffset = Point.convert(offset);
30057		return {
30058			"center": convertedOffset,
30059			"top": convertedOffset,
30060			"top-left": convertedOffset,
30061			"top-right": convertedOffset,
30062			"bottom": convertedOffset,
30063			"bottom-left": convertedOffset,
30064			"bottom-right": convertedOffset,
30065			"left": convertedOffset,
30066			"right": convertedOffset
30067		};
30068	} else return {
30069		"center": Point.convert(offset["center"] || [0, 0]),
30070		"top": Point.convert(offset["top"] || [0, 0]),
30071		"top-left": Point.convert(offset["top-left"] || [0, 0]),
30072		"top-right": Point.convert(offset["top-right"] || [0, 0]),
30073		"bottom": Point.convert(offset["bottom"] || [0, 0]),
30074		"bottom-left": Point.convert(offset["bottom-left"] || [0, 0]),
30075		"bottom-right": Point.convert(offset["bottom-right"] || [0, 0]),
30076		"left": Point.convert(offset["left"] || [0, 0]),
30077		"right": Point.convert(offset["right"] || [0, 0])
30078	};
30079}
30080function normalizePadding(padding) {
30081	if (!padding) return {
30082		top: 0,
30083		right: 0,
30084		bottom: 0,
30085		left: 0
30086	};
30087	return {
30088		top: padding.top ?? 0,
30089		right: padding.right ?? 0,
30090		bottom: padding.bottom ?? 0,
30091		left: padding.left ?? 0
30092	};
30093}
30094//#endregion
30095//#region src/index.ts
30096const version = version$2;
30097/**
30098* Sets the map's [RTL text plugin](https://www.mapbox.com/mapbox-gl-js/plugins/#mapbox-gl-rtl-text).
30099*
30100* @deprecated MapLibre shapes Arabic and reorders bidirectional text itself, so nothing has to be
30101* loaded for right-to-left languages to be drawn correctly. A plugin set here still replaces the
30102* built-in implementation, but this will be removed in a future release.
30103* @param pluginURL - URL pointing to the Mapbox RTL text plugin source.
30104* @param lazy - If set to `true`, maplibre will defer loading the plugin until rtl text is encountered,
30105* rtl text will then be rendered only after the plugin finishes loading.
30106* @example
30107* ```ts
30108* setRTLTextPlugin('https://unpkg.com/@mapbox/[email protected]/dist/mapbox-gl-rtl-text.js', false);
30109* ```
30110* @see [Add support for right-to-left scripts](https://maplibre.org/maplibre-gl-js/docs/examples/add-support-for-right-to-left-scripts/)
30111* @see [Display and style rich text labels](https://maplibre.org/maplibre-gl-js/docs/examples/display-and-style-rich-text-labels/)
30112*/
30113function setRTLTextPlugin(pluginURL, lazy) {
30114	return rtlMainThreadPluginFactory().setRTLTextPlugin(pluginURL, lazy);
30115}
30116/**
30117* Gets the map's [RTL text plugin](https://www.mapbox.com/mapbox-gl-js/plugins/#mapbox-gl-rtl-text) status.
30118* The status can be `unavailable` (i.e. not requested or removed), `loading`, `loaded` or `error`.
30119* If the status is `loaded` and the plugin is requested again, an error will be thrown.
30120*
30121* @deprecated The status says nothing about whether right-to-left text can be drawn, which it always
30122* can be. It reports only on a plugin set through the deprecated {@link setRTLTextPlugin}.
30123* @example
30124* ```ts
30125* const pluginStatus = getRTLTextPluginStatus();
30126* ```
30127*/
30128function getRTLTextPluginStatus() {
30129	return rtlMainThreadPluginFactory().getRTLTextPluginStatus();
30130}
30131/**
30132* Returns the package version of the library
30133* @returns Package version of the library
30134*/
30135function getVersion() {
30136	return version;
30137}
30138/**
30139* Gets the number of web workers instantiated on a page with GL JS maps.
30140* By default, workerCount is 1 except for Safari browser where it is set to half the number of CPU cores (capped at 3).
30141* Make sure to set this property before creating any map instances for it to have effect.
30142*
30143* @returns Number of workers currently configured.
30144* @example
30145* ```ts
30146* const workerCount = getWorkerCount()
30147* ```
30148*/
30149function getWorkerCount() {
30150	return WorkerPool.workerCount;
30151}
30152/**
30153* Sets the number of web workers instantiated on a page with GL JS maps.
30154* By default, workerCount is 1 except for Safari browser where it is set to half the number of CPU cores (capped at 3).
30155* Make sure to set this property before creating any map instances for it to have effect.
30156*
30157* @example
30158* ```ts
30159* setWorkerCount(2);
30160* ```
30161*/
30162function setWorkerCount(count) {
30163	WorkerPool.workerCount = count;
30164}
30165/**
30166* Gets and sets the maximum number of images (raster tiles, sprites, icons) to load in parallel,
30167* which affects performance in raster-heavy maps. 16 by default.
30168*
30169* @returns Number of parallel requests currently configured.
30170* @example
30171* ```ts
30172* getMaxParallelImageRequests();
30173* ```
30174*/
30175function getMaxParallelImageRequests() {
30176	return config.MAX_PARALLEL_IMAGE_REQUESTS;
30177}
30178/**
30179* Sets the maximum number of images (raster tiles, sprites, icons) to load in parallel,
30180* which affects performance in raster-heavy maps. 16 by default.
30181*
30182* @example
30183* ```ts
30184* setMaxParallelImageRequests(10);
30185* ```
30186*/
30187function setMaxParallelImageRequests(numRequests) {
30188	config.MAX_PARALLEL_IMAGE_REQUESTS = numRequests;
30189}
30190/**
30191* Gets the worker url
30192* @returns The worker url
30193*/
30194function getWorkerUrl() {
30195	return config.WORKER_URL;
30196}
30197/**
30198* Sets the worker url
30199*/
30200function setWorkerUrl(value) {
30201	config.WORKER_URL = value;
30202}
30203/**
30204* Allows loading javascript code in the worker thread.
30205* *Note* that since this is using some very internal classes and flows it is considered experimental and can break at any point.
30206*
30207* It can be useful for the following examples:
30208* 1. Using `self.addProtocol` in the worker thread - note that you might need to also register the protocol on the main thread.
30209* 2. Using `self.registerWorkerSource(workerSource: WorkerSource)` to register a worker source, which should come with `addSourceType` usually.
30210* 3. using `self.actor.registerMessageHandler` to override some internal worker operations
30211* @param workerUrl - the worker url e.g. a url of a javascript file to load in the worker
30212* @returns
30213*
30214* @example
30215* ```ts
30216* // below is an example of sending a js file to the worker to load the method there
30217* // Note that you'll need to call the global function `addProtocol` in the worker to register the protocol there.
30218* // add-protocol-worker.js
30219* async function loadFn(params, abortController) {
30220*     const t = await fetch(`https://${params.url.split("://")[1]}`);
30221*     if (t.status == 200) {
30222*         const buffer = await t.arrayBuffer();
30223*         return {data: buffer}
30224*     } else {
30225*         throw new Error(`Tile fetch error: ${t.statusText}`);
30226*     }
30227* }
30228* self.addProtocol('custom', loadFn);
30229*
30230* // main.js
30231* importScriptInWorkers('add-protocol-worker.js');
30232* ```
30233*/
30234async function importScriptInWorkers(workerUrl) {
30235	await getGlobalDispatcher().broadcast("IS", workerUrl);
30236}
30237//#endregion
30238export { 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 };
30239
30240//# sourceMappingURL=maplibre-gl-dev.mjs.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.