UI: Map - improve MapLibre GL integration.
This commit is contained in:
parent
0c2b6fb499
commit
f04303954a
@ -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",
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@ -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']
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
7937
ui-ngx/src/assets/map/enhanced_contrast_hybrid_reference_style.json
Normal file
7937
ui-ngx/src/assets/map/enhanced_contrast_hybrid_reference_style.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -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": {
|
||||
|
||||
@ -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": {
|
||||
|
||||
23
ui-ngx/src/typings/leaflet-extend-tb.d.ts
vendored
23
ui-ngx/src/typings/leaflet-extend-tb.d.ts
vendored
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user