UI: Map - improve MapLibre GL integration.

This commit is contained in:
Igor Kulikov 2025-03-12 18:51:02 +02:00
parent 0c2b6fb499
commit f04303954a
10 changed files with 8302 additions and 69 deletions

View File

@ -28,7 +28,6 @@
"@flowjs/ngx-flow": "18.0.1",
"@geoman-io/leaflet-geoman-free": "2.17.0",
"@iplab/ngx-color-picker": "^18.0.1",
"@maplibre/maplibre-gl-leaflet": "^0.0.22",
"@mat-datetimepicker/core": "~14.0.0",
"@mdi/svg": "^7.4.47",
"@messageformat/core": "^3.4.0",
@ -65,7 +64,7 @@
"leaflet.gridlayer.googlemutant": "0.14.1",
"leaflet.markercluster": "1.5.3",
"libphonenumber-js": "^1.11.15",
"maplibre-gl": "^4.7.1",
"maplibre-gl": "^5.2.0",
"marked": "~12.0.2",
"moment": "^2.30.1",
"moment-timezone": "^0.5.45",

View File

@ -17,7 +17,7 @@
import L, { TB } from 'leaflet';
import { guid, isNotEmptyStr } from '@core/utils';
import 'leaflet-providers';
import '@maplibre/maplibre-gl-leaflet';
import { Map as MapLibreGLMap, LngLat as MapLibreGLLngLat } from 'maplibre-gl';
import '@geoman-io/leaflet-geoman-free';
import 'leaflet.markercluster';
import { MatIconRegistry } from '@angular/material/icon';
@ -27,7 +27,7 @@ import { of } from 'rxjs';
L.MarkerCluster = L.MarkerCluster.mergeOptions({ pmIgnore: true });
class SidebarControl extends L.Control<TB.SidebarControlOptions> {
class SidebarControl extends L.Control<TB.SidebarControlOptions> implements L.TB.SidebarControl {
private readonly sidebar: JQuery<HTMLElement>;
@ -95,7 +95,7 @@ class SidebarControl extends L.Control<TB.SidebarControlOptions> {
}
}
class SidebarPaneControl<O extends TB.SidebarPaneControlOptions> extends L.Control<O> {
class SidebarPaneControl<O extends TB.SidebarPaneControlOptions> extends L.Control<O> implements L.TB.SidebarPaneControl<O> {
private button: JQuery<HTMLElement>;
private $ui: JQuery<HTMLElement>;
@ -155,7 +155,7 @@ class SidebarPaneControl<O extends TB.SidebarPaneControlOptions> extends L.Contr
}
}
class LayersControl extends SidebarPaneControl<TB.LayersControlOptions> {
class LayersControl extends SidebarPaneControl<TB.LayersControlOptions> implements L.TB.LayersControl {
constructor(options: TB.LayersControlOptions) {
super(options);
}
@ -215,9 +215,6 @@ class LayersControl extends SidebarPaneControl<TB.LayersControlOptions> {
if (!map.hasLayer(layerData.layer)) {
map.addLayer(layerData.layer);
map.attributionControl.setPrefix(layerData.attributionPrefix);
if (layerData.onAdd) {
layerData.onAdd();
}
layers.forEach((other) => {
if (other.layer !== layerData.layer) {
map.removeLayer(other.layer);
@ -238,12 +235,12 @@ class LayersControl extends SidebarPaneControl<TB.LayersControlOptions> {
}
}
class GroupsControl extends SidebarPaneControl<TB.GroupsControlOptions> {
class GroupsControl extends SidebarPaneControl<TB.GroupsControlOptions> implements L.TB.GroupsControl {
constructor(options: TB.GroupsControlOptions) {
super(options);
}
public onAddPane(map: L.Map, button: JQuery<HTMLElement>, $ui: JQuery<HTMLElement>, toggle: (e: JQuery.MouseEventBase) => void) {
public onAddPane(map: L.Map, _button: JQuery<HTMLElement>, $ui: JQuery<HTMLElement>, _toggle: (e: JQuery.MouseEventBase) => void) {
const paneId = guid();
const groups = this.options.groups;
const baseSection = $("<div>")
@ -285,7 +282,7 @@ class GroupsControl extends SidebarPaneControl<TB.GroupsControlOptions> {
}
}
class TopToolbarButton {
class TopToolbarButton implements L.TB.TopToolbarButton {
private readonly button: JQuery<HTMLElement>;
private active = false;
private disabled = false;
@ -393,7 +390,7 @@ class TopToolbarButton {
}
}
class ToolbarButton {
class ToolbarButton implements L.TB.ToolbarButton {
private readonly id: string;
private readonly button: JQuery<HTMLElement>;
private active = false;
@ -461,7 +458,7 @@ class ToolbarButton {
}
}
class TopToolbarControl {
class TopToolbarControl implements L.TB.TopToolbarControl {
private readonly toolbarElement: JQuery<HTMLElement>;
private buttons: Array<TopToolbarButton> = [];
@ -490,7 +487,7 @@ class TopToolbarControl {
}
}
class ToolbarControl extends L.Control<L.ControlOptions> {
class ToolbarControl extends L.Control<L.ControlOptions> implements L.TB.ToolbarControl {
private buttonContainer: JQuery<HTMLElement>;
@ -516,7 +513,7 @@ class ToolbarControl extends L.Control<L.ControlOptions> {
}
class BottomToolbarControl {
class BottomToolbarControl implements L.TB.BottomToolbarControl {
private readonly buttonContainer: JQuery<HTMLElement>;
private toolbarButtons: ToolbarButton[] = [];
@ -575,35 +572,35 @@ class BottomToolbarControl {
}
const sidebar = (options: TB.SidebarControlOptions): SidebarControl => {
const sidebar = (options: TB.SidebarControlOptions): L.TB.SidebarControl => {
return new SidebarControl(options);
}
const sidebarPane = <O extends TB.SidebarPaneControlOptions>(options: O): SidebarPaneControl<O> => {
const sidebarPane = <O extends TB.SidebarPaneControlOptions>(options: O): L.TB.SidebarPaneControl<O> => {
return new SidebarPaneControl(options);
}
const layers = (options: TB.LayersControlOptions): LayersControl => {
const layers = (options: TB.LayersControlOptions): L.TB.LayersControl => {
return new LayersControl(options);
}
const groups = (options: TB.GroupsControlOptions): GroupsControl => {
const groups = (options: TB.GroupsControlOptions): L.TB.GroupsControl => {
return new GroupsControl(options);
}
const topToolbar = (options: TB.TopToolbarControlOptions): TopToolbarControl => {
const topToolbar = (options: TB.TopToolbarControlOptions): L.TB.TopToolbarControl => {
return new TopToolbarControl(options);
}
const toolbar = (options: L.ControlOptions): ToolbarControl => {
const toolbar = (options: L.ControlOptions): L.TB.ToolbarControl => {
return new ToolbarControl(options);
}
const bottomToolbar = (options: TB.BottomToolbarControlOptions): BottomToolbarControl => {
const bottomToolbar = (options: TB.BottomToolbarControlOptions): L.TB.BottomToolbarControl => {
return new BottomToolbarControl(options);
}
class ChinaProvider extends L.TileLayer {
class ChinaProvider extends L.TileLayer implements L.TB.TileLayer.ChinaProvider {
static chinaProviders: L.TB.TileLayer.ChinaProvidersData = {
Tencent: {
@ -649,10 +646,299 @@ class ChinaProvider extends L.TileLayer {
}
}
const chinaProvider = (type: string, options?: L.TileLayerOptions): ChinaProvider => {
const chinaProvider = (type: string, options?: L.TileLayerOptions): L.TB.TileLayer.ChinaProvider => {
return new ChinaProvider(type, options);
}
class MapLibreGLLayer extends L.Layer implements TB.MapLibreGL.MapLibreGLLayer {
options: TB.MapLibreGL.LeafletMapLibreGLMapOptions;
private readonly _throttledUpdate: () => void;
private _container: HTMLDivElement;
private _glMap: MapLibreGLMap;
private _actualCanvas: HTMLCanvasElement;
private _offset: L.Point;
private _zooming: boolean;
constructor(options: TB.MapLibreGL.LeafletMapLibreGLMapOptions) {
super();
options = {...options, ...{
updateInterval: 32,
padding: 0.1,
interactive: false,
pane: 'tilePane'
}};
options.attribution = this._loadAttribution(options);
this._prepareTransformRequest(options);
L.setOptions(this, options);
this._throttledUpdate = L.Util.throttle(this._update, this.options.updateInterval, this);
}
onAdd(map: L.Map): this {
let update = false;
if (!this._container) {
this._initContainer();
} else {
update = true;
}
const paneName = this.getPaneName();
map.getPane(paneName).appendChild(this._container);
this._initGL();
this._offset = this._map.containerPointToLayerPoint([0, 0]);
if ((this._map as any)._proxy && map.options.zoomAnimation) {
L.DomEvent.on((map as any)._proxy, L.DomUtil.TRANSITION_END, this._transitionEnd, this);
}
if (update) {
this._update();
}
return this;
}
onRemove(map: L.Map): this {
if ((this._map as any)._proxy && this._map.options.zoomAnimation) {
L.DomEvent.off((map as any)._proxy, L.DomUtil.TRANSITION_END, this._transitionEnd, this);
}
const paneName = this.getPaneName();
map.getPane(paneName).removeChild(this._container);
this._glMap.remove();
this._glMap = null;
return this;
}
getEvents(): { [p: string]: L.LeafletEventHandlerFn } {
return {
move: this._throttledUpdate, // sensibly throttle updating while panning
zoomanim: this._animateZoom, // applys the zoom animation to the <canvas>
zoom: this._pinchZoom, // animate every zoom event for smoother pinch-zooming
zoomstart: this._zoomStart, // flag starting a zoom to disable panning
zoomend: this._zoomEnd,
resize: this._resize
};
}
getMapLibreGLMap(): MapLibreGLMap {
return this._glMap;
}
getCanvas(): HTMLCanvasElement {
return this._glMap.getCanvas();
}
getSize(): L.Point {
return this._map.getSize().multiplyBy(1 + this.options.padding * 2);
}
getBounds(): L.LatLngBounds {
const halfSize = this.getSize().multiplyBy(0.5);
const center = this._map.latLngToContainerPoint(this._map.getCenter());
return L.latLngBounds(
this._map.containerPointToLatLng(center.subtract(halfSize)),
this._map.containerPointToLatLng(center.add(halfSize))
);
}
getContainer(): HTMLDivElement {
return this._container;
}
getPaneName(): string {
return this._map.getPane(this.options.pane) ? this.options.pane : 'tilePane';
}
private _roundPoint(p: L.Point): L.Point {
return new L.Point(Math.round(p.x), Math.round(p.y));
}
private _initContainer() {
const container = this._container = L.DomUtil.create('div', 'leaflet-gl-layer');
const size = this.getSize();
const offset = this._map.getSize().multiplyBy(this.options.padding);
container.style.width = size.x + 'px';
container.style.height = size.y + 'px';
const topLeft = this._map.containerPointToLayerPoint([0, 0]).subtract(offset);
L.DomUtil.setPosition(container, this._roundPoint(topLeft));
}
private _initGL() {
const center = this._map.getCenter();
const options = L.extend({}, this.options, {
container: this._container,
center: [center.lng, center.lat],
zoom: this._map.getZoom() - 1,
attributionControl: false
});
this._glMap = new MapLibreGLMap(options);
this._glMap.setMaxBounds(null);
this._transformGL(this._glMap);
this._actualCanvas = this._glMap._canvas;
const canvas = this._actualCanvas;
L.DomUtil.addClass(canvas, 'leaflet-image-layer');
L.DomUtil.addClass(canvas, 'leaflet-zoom-animated');
if (this.options.interactive) {
L.DomUtil.addClass(canvas, 'leaflet-interactive');
}
if (this.options.className) {
L.DomUtil.addClass(canvas, this.options.className);
}
}
private _update() {
if (!this._map) {
return;
}
this._offset = this._map.containerPointToLayerPoint([0, 0]);
if (this._zooming) {
return;
}
const size = this.getSize(),
container = this._container,
gl = this._glMap,
offset = this._map.getSize().multiplyBy(this.options.padding),
topLeft = this._map.containerPointToLayerPoint([0, 0]).subtract(offset);
L.DomUtil.setPosition(container, this._roundPoint(topLeft));
this._transformGL(gl);
if (gl.transform.width !== size.x || gl.transform.height !== size.y) {
container.style.width = size.x + 'px';
container.style.height = size.y + 'px';
gl.resize();
} else {
gl._update();
}
}
private _transformGL(gl: MapLibreGLMap) {
const center = this._map.getCenter();
const tr = gl._getTransformForUpdate();
tr.setCenter(MapLibreGLLngLat.convert([center.lng, center.lat]));
tr.setZoom(this._map.getZoom() - 1);
gl.transform.apply(tr);
gl._fireMoveEvents();
}
private _pinchZoom() {
this._glMap.jumpTo({
zoom: this._map.getZoom() - 1,
center: this._map.getCenter()
});
}
private _animateZoom(e: L.ZoomAnimEvent) {
const scale = this._map.getZoomScale(e.zoom);
const padding = this._map.getSize().multiplyBy(this.options.padding * scale);
const viewHalf = this.getSize().divideBy(2);
const topLeft = this._map.project(e.center, e.zoom)
.subtract(viewHalf)
.add((this._map as any)._getMapPanePos()
.add(padding)).round();
const offset = this._map.project(this._map.getBounds().getNorthWest(), e.zoom)
.subtract(topLeft);
L.DomUtil.setTransform(
this._actualCanvas,
offset.subtract(this._offset),
scale
);
}
private _zoomStart() {
this._zooming = true;
}
private _zoomEnd() {
const scale = this._map.getZoomScale(this._map.getZoom());
L.DomUtil.setTransform(
this._actualCanvas,
null,
scale
);
this._zooming = false;
this._update();
}
private _transitionEnd() {
L.Util.requestAnimFrame(() => {
const zoom = this._map.getZoom();
const center = this._map.getCenter();
const offset = this._map.latLngToContainerPoint(
this._map.getBounds().getNorthWest()
);
L.DomUtil.setTransform(this._actualCanvas, offset, 1);
this._glMap.once('moveend', () => {
this._zoomEnd();
});
this._glMap.jumpTo({
center: center,
zoom: zoom - 1
});
});
}
private _resize() {
this._transitionEnd();
}
private _loadAttribution(options: TB.MapLibreGL.LeafletMapLibreGLMapOptions): string {
if (options.attributionControl !== false && typeof options.attributionControl?.customAttribution === 'string') {
return options.attributionControl.customAttribution;
}
if (options.attributionControl !== false) {
const style = options.style;
if (typeof style !== 'string' && style?.sources) {
return Object.keys(style.sources)
.map((sourceId) => {
const source = style.sources[sourceId];
return (source && source.type !== 'video' && source.type !== 'image'
&& typeof source.attribution === 'string') ? source.attribution.trim() : null;
})
.filter(Boolean) // Remove null/undefined values
.join(', ');
}
}
return '';
}
private _prepareTransformRequest(options: TB.MapLibreGL.LeafletMapLibreGLMapOptions) {
if (!options.transformRequest) {
const style = options.style;
if (typeof style !== 'string' && style.glyphs) {
const glyphs = style.glyphs;
const glyphsRegexString = glyphs.replace(/\//g, '\\/').replace(/\./g, '\\.').replace('{fontstack}', '(.*)').replace('{range}', '(.*)');
const glyphsRegex = new RegExp(glyphsRegexString);
options.transformRequest = (url, resourceType) => {
if (resourceType === 'Glyphs' && glyphsRegex && glyphsRegex.test(url)) {
const res = glyphsRegex.exec(url);
if (res.length === 3) {
const fontStack = res[1];
const fonts = fontStack.split(',');
if (fonts.length > 1) {
const newFontStack = fonts[0];
url = url.replace(fontStack, newFontStack);
}
}
}
return {url};
};
}
}
}
}
const mapLibreGLLayer = (options: TB.MapLibreGL.LeafletMapLibreGLMapOptions): TB.MapLibreGL.MapLibreGLLayer => {
return new MapLibreGLLayer(options);
}
L.TB = L.TB || {
SidebarControl,
SidebarPaneControl,
@ -675,5 +961,9 @@ L.TB = L.TB || {
},
tileLayer: {
chinaProvider
},
MapLibreGL: {
MapLibreGLLayer,
mapLibreGLLayer
}
}

View File

@ -26,7 +26,8 @@ import {
HereMapLayerSettings,
MapLayerSettings,
MapProvider,
OpenStreetMapLayerSettings, ReferenceLayerType,
OpenStreetMapLayerSettings,
ReferenceLayerType,
TencentMapLayerSettings
} from '@shared/models/widget/maps/map.models';
import { WidgetContext } from '@home/models/widget-component.models';
@ -37,13 +38,13 @@ import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe';
import L from 'leaflet';
import { catchError, map } from 'rxjs/operators';
import { ResourcesService } from '@core/services/resources.service';
import { StyleSpecification, VectorSourceSpecification } from '@maplibre/maplibre-gl-style-spec';
import { ResourceType } from 'maplibre-gl';
import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec';
const referenceLayerStyleUrlMap = new Map<ReferenceLayerType, string>(
[
[ReferenceLayerType.openstreetmap_hybrid, '/assets/map/openstreetmap_hybrid_reference_style.json'],
[ReferenceLayerType.world_edition_hybrid, '/assets/map/world_edition_hybrid_reference_style.json']
[ReferenceLayerType.world_edition_hybrid, '/assets/map/world_edition_hybrid_reference_style.json'],
[ReferenceLayerType.enhanced_contrast_hybrid, '/assets/map/enhanced_contrast_hybrid_reference_style.json']
]
);
@ -52,7 +53,6 @@ const referenceLayerCache = new Map<ReferenceLayerType, Observable<StyleSpecific
interface TbMapLayerData {
layer: L.Layer;
attribution: boolean;
onAdd?: () => void;
}
export abstract class TbMapLayer<S extends MapLayerSettings> {
@ -93,12 +93,7 @@ export abstract class TbMapLayer<S extends MapLayerSettings> {
title: this.title(),
attributionPrefix: attributionPrefix,
layer: layerData.layer,
mini: miniLayerData.layer,
onAdd: () => {
if (layerData.onAdd) {
layerData.onAdd();
}
}
mini: miniLayerData.layer
};
} else {
return null;
@ -125,10 +120,7 @@ export abstract class TbMapLayer<S extends MapLayerSettings> {
referenceLayer.addTo(layer);
return {
layer,
attribution: !!baseLayer.getAttribution() || !!referenceLayer.getAttribution(),
onAdd: () => {
(referenceLayer as any)._update();
}
attribution: !!baseLayer.getAttribution() || !!referenceLayer.getAttribution()
};
} else {
return {
@ -164,13 +156,9 @@ export abstract class TbMapLayer<S extends MapLayerSettings> {
}
return spec$.pipe(
map(spec => {
const sourceSpec = (spec.sources['esri'] as VectorSourceSpecification);
const attribution = sourceSpec.attribution;
const gl = L.maplibreGL({
return L.TB.MapLibreGL.mapLibreGLLayer({
style: spec,
});
gl.options.attribution = attribution;
return gl;
})
);
}

View File

@ -758,6 +758,7 @@ export const mapProviderTranslationMap = new Map<MapProvider, string>(
export enum ReferenceLayerType {
openstreetmap_hybrid = 'openstreetmap_hybrid',
world_edition_hybrid = 'world_edition_hybrid',
enhanced_contrast_hybrid = 'enhanced_contrast_hybrid'
}
export const referenceLayerTypes = Object.keys(ReferenceLayerType) as ReferenceLayerType[];
@ -765,7 +766,8 @@ export const referenceLayerTypes = Object.keys(ReferenceLayerType) as ReferenceL
export const referenceLayerTypeTranslationMap = new Map<ReferenceLayerType, string>(
[
[ReferenceLayerType.openstreetmap_hybrid, 'widgets.maps.layer.reference.openstreetmap-hybrid'],
[ReferenceLayerType.world_edition_hybrid, 'widgets.maps.layer.reference.world-edition-hybrid']
[ReferenceLayerType.world_edition_hybrid, 'widgets.maps.layer.reference.world-edition-hybrid'],
[ReferenceLayerType.enhanced_contrast_hybrid, 'widgets.maps.layer.reference.enhanced-contrast-hybrid']
]
);

View File

@ -7910,7 +7910,8 @@
"reference-layer": "Reference layer",
"no-layer": "No layer",
"openstreetmap-hybrid": "OpenStreetMap Hybrid",
"world-edition-hybrid": "World Edition Hybrid"
"world-edition-hybrid": "World Edition Hybrid",
"enhanced-contrast-hybrid": "Enhanced Contrast Hybrid"
},
"provider": {
"provider": "Provider",

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"version": 8,
"sprite": "https://cdn.arcgis.com/sharing/rest/content/items/f240fe360b434afc87dd989bf0c0b825/resources/sprites/sprite",
"sprite": "https://basemaps.arcgis.com/arcgis/rest/services/OpenStreetMap_v2/VectorTileServer/resources/sprites/sprite",
"glyphs": "https://basemaps.arcgis.com/arcgis/rest/services/OpenStreetMap_v2/VectorTileServer/resources/fonts/{fontstack}/{range}.pbf",
"sources": {
"esri": {

View File

@ -1,6 +1,6 @@
{
"version": 8,
"sprite": "https://cdn.arcgis.com/sharing/rest/content/items/30d6b8271e1849cd9c3042060001f425/resources/sprites/sprite",
"sprite": "https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer/resources/sprites/sprite",
"glyphs": "https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer/resources/fonts/{fontstack}/{range}.pbf",
"sources": {
"esri": {

View File

@ -16,6 +16,7 @@
import { FormattedData } from '@shared/models/widget.models';
import L from 'leaflet';
import { Map as MapLibreGLMap, MapOptions as MapLibreGLMapOptions } from 'maplibre-gl';
import { TbMapDatasource } from '@shared/models/widget/maps/map.models';
import { MatIconRegistry } from '@angular/material/icon';
@ -61,7 +62,6 @@ declare module 'leaflet' {
attributionPrefix?: string;
layer: Layer;
mini: Layer;
onAdd?: () => void;
}
interface LayersControlOptions extends SidebarPaneControlOptions {
@ -183,5 +183,26 @@ declare module 'leaflet' {
namespace tileLayer {
function chinaProvider(type: string, options?: TileLayerOptions): TileLayer.ChinaProvider;
}
namespace MapLibreGL {
interface LeafletMapLibreGLMapOptions extends L.InteractiveLayerOptions, Omit<MapLibreGLMapOptions, "container"> {
updateInterval?: number;
padding?: number;
className?: string;
}
class MapLibreGLLayer extends L.Layer {
constructor(options: LeafletMapLibreGLMapOptions);
getMapLibreGLMap(): MapLibreGLMap
getCanvas(): HTMLCanvasElement
getSize(): L.Point
getBounds(): L.LatLngBounds
getContainer(): HTMLDivElement
getPaneName(): string
}
function mapLibreGLLayer(options: LeafletMapLibreGLMapOptions): MapLibreGLLayer;
}
}
}

View File

@ -1971,21 +1971,16 @@
resolved "https://registry.yarnpkg.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz#497c67a1cef50d1a2459ba60f315e448d2ad87fe"
integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==
"@maplibre/maplibre-gl-leaflet@^0.0.22":
version "0.0.22"
resolved "https://registry.yarnpkg.com/@maplibre/maplibre-gl-leaflet/-/maplibre-gl-leaflet-0.0.22.tgz#04c4d5b65f44be0962330f8b5a7cafc8c3bd2676"
integrity sha512-9p4DSPLUE5t0StXH/IcZw37/UL7Ekea4jcVdkSsb2jWI+sM3K7mYHLOnti/2wn+HqxG4jMVUHah7Xun3bs00AQ==
"@maplibre/maplibre-gl-style-spec@^20.3.1":
version "20.4.0"
resolved "https://registry.yarnpkg.com/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.4.0.tgz#408339e051fb51e022b40af2235e0beb037937ea"
integrity sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==
"@maplibre/maplibre-gl-style-spec@^23.1.0":
version "23.1.0"
resolved "https://registry.yarnpkg.com/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-23.1.0.tgz#ad59731b0547ee0986ba4ccff699894dd60f0650"
integrity sha512-R6/ihEuC5KRexmKIYkWqUv84Gm+/QwsOUgHyt1yy2XqCdGdLvlBWVWIIeTZWN4NGdwmY6xDzdSGU2R9oBLNg2w==
dependencies:
"@mapbox/jsonlint-lines-primitives" "~2.0.2"
"@mapbox/unitbezier" "^0.0.1"
json-stringify-pretty-compact "^4.0.0"
minimist "^1.2.8"
quickselect "^2.0.0"
quickselect "^3.0.0"
rw "^1.3.3"
tinyqueue "^3.0.0"
@ -2731,7 +2726,7 @@
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca"
integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==
"@types/geojson@^7946.0.14":
"@types/geojson@^7946.0.16":
version "7946.0.16"
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a"
integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==
@ -4793,7 +4788,7 @@ domutils@^3.0.1:
domelementtype "^2.3.0"
domhandler "^5.0.3"
earcut@^3.0.0:
earcut@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/earcut/-/earcut-3.0.1.tgz#f60b3f671c5657cca9d3e131c5527c5dde00ef38"
integrity sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw==
@ -6933,10 +6928,10 @@ make-plural@^7.0.0:
resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-7.4.0.tgz#fa6990dd550dea4de6b20163f74e5ed83d8a8d6d"
integrity sha512-4/gC9KVNTV6pvYg2gFeQYTW3mWaoJt7WZE5vrp1KnQDgW92JtYZnzmZT81oj/dUTqAIu0ufI2x3dkgu3bB1tYg==
maplibre-gl@^4.7.1:
version "4.7.1"
resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-4.7.1.tgz#06a524438ee2aafbe8bcd91002a4e01468ea5486"
integrity sha512-lgL7XpIwsgICiL82ITplfS7IGwrB1OJIw/pCvprDp2dhmSSEBgmPzYRvwYYYvJGJD7fxUv1Tvpih4nZ6VrLuaA==
maplibre-gl@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-5.2.0.tgz#e3cdb66c82232cffbe149b032776484722caee4e"
integrity sha512-9zZKD0M80qtDsqBet+EDuAhoCeA/cnAuZAA0p3hcGKGbyjM/SH+R6wQvnBEgvJz9UhDynnkoKdUwhI+fUkHoXQ==
dependencies:
"@mapbox/geojson-rewind" "^0.5.2"
"@mapbox/jsonlint-lines-primitives" "^2.0.2"
@ -6945,14 +6940,14 @@ maplibre-gl@^4.7.1:
"@mapbox/unitbezier" "^0.0.1"
"@mapbox/vector-tile" "^1.3.1"
"@mapbox/whoots-js" "^3.1.0"
"@maplibre/maplibre-gl-style-spec" "^20.3.1"
"@types/geojson" "^7946.0.14"
"@maplibre/maplibre-gl-style-spec" "^23.1.0"
"@types/geojson" "^7946.0.16"
"@types/geojson-vt" "3.2.5"
"@types/mapbox__point-geometry" "^0.1.4"
"@types/mapbox__vector-tile" "^1.3.4"
"@types/pbf" "^3.0.5"
"@types/supercluster" "^7.1.3"
earcut "^3.0.0"
earcut "^3.0.1"
geojson-vt "^4.0.2"
gl-matrix "^3.4.3"
global-prefix "^4.0.0"