diff --git a/projects/arlas-map/src/lib/arlas-map-logic.service.ts b/projects/arlas-map/src/lib/arlas-map-logic.service.ts index 639c39b7..9ed728e4 100644 --- a/projects/arlas-map/src/lib/arlas-map-logic.service.ts +++ b/projects/arlas-map/src/lib/arlas-map-logic.service.ts @@ -214,4 +214,79 @@ export abstract class ArlasMapFunctionalService { } + public updateLayoutVisibility(visualisationName: string, visualisationSetsConfig: VisualisationSetConfig[], map: AbstractArlasMapGL) { + const visuStatus = !this.visualisationsSets.status.get(visualisationName); + visualisationSetsConfig.find(v => v.name === visualisationName).enabled = visuStatus; + if (!visuStatus) { + const layersSet = new Set(this.visualisationsSets.visualisations.get(visualisationName)); + this.visualisationsSets.visualisations.forEach((ls, v) => { + if (v !== visualisationName) { + ls.forEach(ll => { + if (layersSet && layersSet.has(ll)) { + layersSet.delete(ll); + } + }); + } + }); + layersSet.forEach(ll => { + this.mapService.setLayerVisibility(ll, false, map); + }); + } + this.visualisationsSets.status.set(visualisationName, visuStatus); + const layers = new Set(); + this.visualisationsSets.visualisations.forEach((ls, v) => { + if (this.visualisationsSets.status.get(v)) { + ls.forEach(l => { + layers.add(l); + this.mapService.setLayerVisibility(l, true, map); + }); + } + }); + return layers; + } + + public updateVisibility(visibilityStatus: Map, visualisationSetsConfig: VisualisationSetConfig[], map: AbstractArlasMapGL) { + visibilityStatus.forEach((visibilityStatus, l) => { + let layerInVisualisations = false; + if (!visibilityStatus) { + visualisationSetsConfig.forEach(v => { + const ls = new Set(v.layers); + if (!layerInVisualisations) { + layerInVisualisations = ls.has(l); + } + }); + if (layerInVisualisations) { + this.mapService.setLayerVisibility(l, false, map); + } + } else { + let oneVisualisationEnabled = false; + visualisationSetsConfig.forEach(v => { + const ls = new Set(v.layers); + if (!layerInVisualisations) { + layerInVisualisations = ls.has(l); + } + if (ls.has(l) && v.enabled) { + oneVisualisationEnabled = true; + this.mapService.setLayerVisibility(l, true, map); + } + }); + if (!oneVisualisationEnabled && layerInVisualisations) { + this.mapService.setLayerVisibility(l, false, map); + } + } + }); + } + + + public findVisualisationSetLayer(visuName: string, visualisationSetsConfig: VisualisationSetConfig[]) { + return visualisationSetsConfig.find(v => v.name === visuName).layers; + } + public setVisualisationSetLayers(visuName: string, layers: string[], visualisationSetsConfig: VisualisationSetConfig[]) { + const f = visualisationSetsConfig.find(v => v.name === visuName); + if (f) { + f.layers = layers; + } + } + + } \ No newline at end of file diff --git a/projects/arlas-map/src/lib/arlas-map.component.ts b/projects/arlas-map/src/lib/arlas-map.component.ts index d4a87b5b..bc98b2eb 100644 --- a/projects/arlas-map/src/lib/arlas-map.component.ts +++ b/projects/arlas-map/src/lib/arlas-map.component.ts @@ -2,7 +2,7 @@ import { Component, EventEmitter, HostListener, Input, OnInit, Output, SimpleCha import { marker } from '@biesbjerg/ngx-translate-extract-marker'; import { Feature, FeatureCollection, Geometry, Polygon, polygon } from '@turf/helpers'; import { BasemapStyle } from './basemaps/basemap.config'; -import { ArlasMapOffset, AbstractArlasMapGL, ElementIdentifier, MapConfig, ZOOM_IN, ZOOM_OUT, RESET_BEARING } from './map/AbstractArlasMapGL'; +import { ArlasMapOffset, AbstractArlasMapGL, ElementIdentifier, MapConfig, ZOOM_IN, ZOOM_OUT, RESET_BEARING, CROSS_LAYER_PREFIX } from './map/AbstractArlasMapGL'; import { IconConfig, ControlPosition, DrawControlsOption } from './map/model/controls'; import { AoiDimensions, BboxDrawCommand } from './draw/draw.models'; import { LegendData } from './legend/legend.config'; @@ -34,7 +34,7 @@ import centroid from '@turf/centroid'; import { ARLAS_VSET } from './map/model/layers'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { OnMoveResult } from './map/model/map'; -import { MapLayerMouseEvent, MapMouseEvent } from './map/model/events'; +import { MapMouseEvent } from './map/model/events'; import { ArlasMapFunctionalService } from './arlas-map-logic.service'; @Component({ @@ -307,20 +307,11 @@ export class ArlasMapComponent implements OnInit { downloadType: string; }> = new Subject(); - - protected ICONS_BASE_PATH = 'assets/icons/'; - /** ------------------------------------------------------- VISUAL SEPERATOR - INIT ----------------------------------------- */ - - - - - - public constructor(private http: HttpClient, private drawService: MapboxAoiDrawService, private basemapService: BasemapService, private _snackBar: MatSnackBar, private translate: TranslateService, protected mapService: ArlasMapService, @@ -414,9 +405,6 @@ export class ArlasMapComponent implements OnInit { } } - - - /** ------------------------------------------------------- VISUAL SEPERATOR - MAP ----------------------------------------- */ /** If transformRequest' @Input was not set, set a default value : a function that maintains the same url */ @@ -453,15 +441,13 @@ export class ArlasMapComponent implements OnInit { } protected queryRender(e, map: AbstractArlasMapGL,) { - const hasCrossOrDrawLayer = map.hasCrossOrDrawLayer(e); + const hasCrossOrDrawLayer = this.mapService.queryFeatures(e, this.map, CROSS_LAYER_PREFIX); if (!this.isDrawingBbox && !this.isDrawingPolygon && !this.isDrawingCircle && !this.isInSimpleDrawMode && !hasCrossOrDrawLayer) { - map.onEvent(e); + this.onFeatureClic.next({ features: e.features, point: [e.lngLat.lng, e.lngLat.lat] }); } } - /** - * Adds the custom icons given in the component's input - */ + /** Adds the custom icons given in the component's input */ public addIcons() { this.icons.forEach(icon => { const iconName = icon.path.split('.')[0]; @@ -483,28 +469,6 @@ export class ArlasMapComponent implements OnInit { mapLayers: this.mapLayers, maxWidthScale: this.maxWidthScale, unitScale: this.unitScale, - mapLayersEventBind: { - zoomOnClick: [{ event: 'click', fn: this.defaultOnZoom }], - onHover: [ - { - event: 'mousemove', - fn: (e) => { - this.onFeatureOver.next({ features: e.features, point: [e.lngLat.lng, e.lngLat.lat] }); - } - }, - { - event: 'mouseleave', - fn: (e) => { - this.onFeatureOver.next({}); - } - } - ], - emitOnClick: [ - { - event: 'click', - fn: this.queryRender - }], - }, customEventBind: (m: AbstractArlasMapGL) => this.mapService.getCustomEventsToDrawLayers(m), mapProviderOptions: { container: this.id, @@ -541,15 +505,7 @@ export class ArlasMapComponent implements OnInit { } } }; - console.log('declare'); this.map = this.mapService.createMap(config); - this.map.eventEmitter$.subscribe({ - next: (e: MapLayerMouseEvent) => { - if (e.type === 'click') { - this.onFeatureClic.next({ features: e.features, point: [e.lngLat.lng, e.lngLat.lat] }); - } - } - }) fromEvent(window, 'beforeunload').subscribe(() => { this.onMapClosed.next(this.map.getMapExtend()); }); @@ -608,7 +564,6 @@ export class ArlasMapComponent implements OnInit { */ this.map.onCustomEvent('beforeOnLoadInit', () => { - // TODO: should change the this.basemapService.declareProtomapProtocol(this.map); this.basemapService.addProtomapBasemap(this.map); this.addIcons(); @@ -616,18 +571,16 @@ export class ArlasMapComponent implements OnInit { this.mapFunctionalService.declareBasemapSources(this.mapSources, this.map); this.mapFunctionalService.declareLabelSources('', this.polygonlabeldata, this.map); this.mapFunctionalService.addArlasDataLayers(this.visualisationSetsConfig, this.mapLayers, this.map); - + this.bindLayerEvents(); }); this.map.on('load', () => { - this.draw.changeMode('static'); if (this.mapLayers !== null) { this.visibilityUpdater.subscribe(visibilityStatus => { - this.map.updateVisibility(visibilityStatus); + this.mapFunctionalService.updateVisibility(visibilityStatus, this.visualisationSetsConfig, this.map); }); } - this.canvas = this.map.getCanvasContainer(); this.canvas.addEventListener('mousedown', this.mousedown, true); this.draw.on('draw.create', (e) => { @@ -679,12 +632,12 @@ export class ArlasMapComponent implements OnInit { this.draw.onDrawOnStart((e) => { window.removeEventListener('mousemove', mouseMoveForDraw); this.drawClickCounter = 0; - this.map.setCursorStyle(''); + this.mapService.setMapCursor(this.map, ''); }); this.draw.onDrawOnStop((e) => { window.removeEventListener('mousemove', mouseMoveForDraw); this.drawClickCounter = 0; - this.map.setCursorStyle(''); + this.mapService.setMapCursor(this.map, ''); }); this.draw.onDrawInvalidGeometry((e) => { @@ -708,7 +661,7 @@ export class ArlasMapComponent implements OnInit { this.draw.add(currentFeature); } this.openInvalidGeometrySnackBar(); - this.map.setCursorStyle(''); + this.mapService.setMapCursor(this.map, ''); }); this.draw.onDrawEditSaveInitialFeature((edition) => { @@ -740,7 +693,7 @@ export class ArlasMapComponent implements OnInit { this.isDrawingStrip = false; this.isInSimpleDrawMode = false; this.draw.changeMode('static'); - this.map.setCursorStyle(''); + this.mapService.setMapCursor(this.map, ''); } }); this.draw.onDrawModeChange((e) => { @@ -753,7 +706,7 @@ export class ArlasMapComponent implements OnInit { if (e.mode === 'simple_select') { this.isInSimpleDrawMode = true; } else if (e.mode === 'static') { - this.map.setCursorStyle(''); + this.mapService.setMapCursor(this.map, ''); } else if (e.mode === 'direct_select') { const selectedFeatures = this.draw.getSelectedFeatures(); const selectedIds = this.draw.getSelectedIds(); @@ -780,7 +733,7 @@ export class ArlasMapComponent implements OnInit { } } else { this.isInSimpleDrawMode = false; - this.map.setCursorStyle(''); + this.mapService.setMapCursor(this.map, ''); } } }); @@ -844,7 +797,7 @@ export class ArlasMapComponent implements OnInit { this.map.on('mousemove', (e: MapMouseEvent) => { const lngLat = e.lngLat; if (this.isDrawingBbox || this.isDrawingPolygon) { - this.map.setCursorStyle('crosshair'); + this.mapService.setMapCursor(this.map, 'crosshair'); this.map.movelngLat = lngLat; } if (this.drawService.bboxEditionState.isDrawing) { @@ -879,39 +832,41 @@ export class ArlasMapComponent implements OnInit { if (!!this.redrawSource) { this.redrawSource.subscribe(sd => { - this.map.redrawSource(sd.source, sd.data); - }); + this.mapService.setDataToGeojsonSource(sd.source, { + 'type': 'FeatureCollection', + 'features': sd.data + }) + }); } } + public bindLayerEvents() { + this.mapLayers.events.zoomOnClick.forEach(layerId => { + this.mapService.onLayerEvent('click', this.map, layerId, (e) => this.defaultOnZoom(e)); + }); + this.mapLayers.events.onHover.forEach(layerId => { + this.mapService.onLayerEvent('mousemove', this.map, layerId, (e) => + this.onFeatureOver.next({ features: e.features, point: [e.lngLat.lng, e.lngLat.lat] })); + this.mapService.onLayerEvent('mouseleave', this.map, layerId, (e) => + this.onFeatureOver.next({})); + }); - - - - - - - + this.mapLayers.events.emitOnClick.forEach(layerId => { + this.mapService.onLayerEvent('click', this.map, layerId, (e) => + this.queryRender(e, this.map)); + }); + } /** ------------------------------------------------------- VISUAL SEPERATOR - LAYERS ----------------------------------------- */ - - - - /** Sets the layers order according to the order of `visualisationSetsConfig` list*/ public reorderLayers() { this.mapFunctionalService.reorderLayers(this.visualisationSetsConfig, this.map); } - - /** ------------------------------------------------------- VISUAL SEPERATOR - DRAWING ----------------------------------------- */ - - - private mousedown = (e) => { // Continue the rest of the function if we add a geobox. if (!this.isDrawingBbox) { @@ -952,7 +907,7 @@ export class ArlasMapComponent implements OnInit { const f = this.mapService.getPointFromScreen(e, this.canvas); document.removeEventListener('mousemove', this.mousemove); document.removeEventListener('mouseup', this.mouseup); - this.map.setCursorStyle(''); + this.mapService.setMapCursor(this.map, ''); this.map.enableDragPan(); // Capture xy coordinates if (this.start.x !== f.x && this.start.y !== f.y) { @@ -984,7 +939,6 @@ export class ArlasMapComponent implements OnInit { } } - /** * Emits the newly drawn bbox. It completes the drawBbox event emitted by the drawService. * @param east @@ -1026,7 +980,7 @@ export class ArlasMapComponent implements OnInit { /** @description Displays the geobox */ public addGeoBox() { - this.map.setCursorStyle('crosshair'); + this.mapService.setMapCursor(this.map, 'crosshair'); this.drawService.enableBboxEdition(); this.isDrawingBbox = true; } @@ -1035,7 +989,7 @@ export class ArlasMapComponent implements OnInit { * @description Removes all the aois if none of them is selected. Otherwise it removes the selected one only */ public removeAois() { - this.map.setCursorStyle(''); + this.mapService.setMapCursor(this.map, ''); this.isDrawingBbox = false; this.deleteSelectedItem(); } @@ -1138,11 +1092,11 @@ export class ArlasMapComponent implements OnInit { @HostListener('document:keydown', ['$event']) public handleKeyboardEvent(event: KeyboardEvent) { if (event.key === 'Escape' && this.isDrawingBbox) { - this.map.setCursorStyle(''); + this.mapService.setMapCursor(this.map, ''); this.isDrawingBbox = false; document.removeEventListener('mousemove', this.mousemove); document.removeEventListener('mouseup', this.mouseup); - this.map.setCursorStyle(''); + this.mapService.setMapCursor(this.map, ''); if (this.box) { this.box.parentNode.removeChild(this.box); this.box = undefined; @@ -1166,7 +1120,7 @@ export class ArlasMapComponent implements OnInit { } public emitVisualisations(visualisationName: string) { - const layers = this.map.updateLayoutVisibility(visualisationName); + const layers = this.mapFunctionalService.updateLayoutVisibility(visualisationName, this.visualisationSetsConfig, this.map); this.visualisations.emit(layers); this.reorderLayers(); } @@ -1185,15 +1139,15 @@ export class ArlasMapComponent implements OnInit { /** puts the visualisation set list in the new order after dropping */ public drop(event: CdkDragDrop) { - moveItemInArray(this.map.visualisationSetsConfig, event.previousIndex, event.currentIndex); + moveItemInArray(this.visualisationSetsConfig, event.previousIndex, event.currentIndex); this.reorderLayers(); } /** puts the layers list in the new order after dropping */ public dropLayer(event: CdkDragDrop, visuName: string) { - const layers = Array.from(this.map.findVisualisationSetLayer(visuName)); + const layers = Array.from(this.mapFunctionalService.findVisualisationSetLayer(visuName, this.visualisationSetsConfig)); moveItemInArray(layers, event.previousIndex, event.currentIndex); - this.map.setVisualisationSetLayers(visuName, layers); + this.mapFunctionalService.setVisualisationSetLayers(visuName, layers, this.visualisationSetsConfig); this.reorderLayers(); } @@ -1215,7 +1169,7 @@ export class ArlasMapComponent implements OnInit { } public selectFeaturesByCollection(features: Array, collection: string) { - this.map.selectFeaturesByCollection(features, collection); + this.mapFunctionalService.selectFeaturesByCollection(this.mapLayers, this.map, features, collection); } public hideBasemapSwitcher() { @@ -1260,14 +1214,11 @@ export class ArlasMapComponent implements OnInit { } private highlightFeature(featureToHightLight: { isleaving: boolean; elementidentifier: ElementIdentifier; }) { - this.map.highlightFeature(featureToHightLight); + this.mapFunctionalService.highlightFeature(this.mapLayers, this.map, featureToHightLight); } private selectFeatures(elementToSelect: Array) { - this.map.selectFeatures(elementToSelect); + this.mapFunctionalService.selectFeatures(this.mapLayers, this.map, elementToSelect); } - - - } diff --git a/projects/arlas-map/src/lib/basemaps/basemap.component.ts b/projects/arlas-map/src/lib/basemaps/basemap.component.ts index bb0cea82..f2006397 100644 --- a/projects/arlas-map/src/lib/basemaps/basemap.component.ts +++ b/projects/arlas-map/src/lib/basemaps/basemap.component.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { Subject } from 'rxjs/internal/Subject'; import { AbstractArlasMapGL } from '../map/AbstractArlasMapGL'; import { ArlasMapSource } from '../map/model/sources'; @@ -26,14 +26,16 @@ import { BasemapService } from './basemap.service'; import { BasemapStyle } from './basemap.config'; import { ArlasMapService } from '../map/service/arlas-map.service'; import { ArlasMapFunctionalService } from '../arlas-map-logic.service'; +import { takeUntil } from 'rxjs'; @Component({ selector: 'arlas-basemap', templateUrl: './basemap.component.html', styleUrls: ['./basemap.component.scss'] }) -export class BasemapComponent implements OnInit { - protected LOCAL_STORAGE_BASEMAPS = 'arlas_last_base_map'; +export class BasemapComponent implements OnInit, OnDestroy { + + private _onDestroy$ = new Subject(); @Input() public map: AbstractArlasMapGL; @Input() public mapSources: Array>; @@ -44,7 +46,13 @@ export class BasemapComponent implements OnInit { public showList = false; public basemaps: ArlasBasemaps; - public constructor(protected basemapService: BasemapService, protected mapFunctionalService: ArlasMapFunctionalService) { } + public constructor(protected basemapService: BasemapService, + protected mapFunctionalService: ArlasMapFunctionalService, + protected mapService: ArlasMapService) { + + this.basemapService.basemapChanged$.pipe(takeUntil(this._onDestroy$)).subscribe(() => this.basemapChanged.emit()); + + } public ngOnInit(): void { this.initBasemaps(); @@ -81,59 +89,13 @@ export class BasemapComponent implements OnInit { public setBaseMapStyle(newBasemap: BasemapStyle) { if (this.map) { - this.setStyle(this.basemaps.getSelected().styleFile as any, newBasemap); + this.basemapService.setBasemap(this.basemaps.getSelected().styleFile as any, newBasemap, this.map, this.mapSources); } } - // TODO: s to any try to find a good type or interface for all layer - /** Set mapbox new style. - * !!NOTE: mapbox setStyle removes all added layers from the map; thus the following description : - * This method saves all the currently added layers to the map, applies the 'map.setStyle' and adds all the saved layers afterwards. - */ - public setStyle(s: any, newBasemap: BasemapStyle) { - const selectedBasemapLayersSet = new Set(); - // TODO: Array to any try to find a good type or interface for all layer - const layers: Array = this.map.getLayers(); - const sources = this.map.getStyle().sources; - if (s.layers) { - s.layers.forEach(l => selectedBasemapLayersSet.add(l.id)); - } - // TODO: Array to any try to find a good type or interface for all layer - const layersToSave = new Array(); - const sourcesToSave = new Array>(); - layers.filter((l: any) => !selectedBasemapLayersSet.has(l.id) && !!l.source).forEach(l => { - layersToSave.push(l); - if (sourcesToSave.filter(ms => ms.id === l.source.toString()).length === 0) { - sourcesToSave.push({ id: l.source.toString(), source: sources[l.source.toString()] }); - } - }); - const sourcesToSaveSet = new Set(); - sourcesToSave.forEach(mapSource => sourcesToSaveSet.add(mapSource.id)); - if (this.mapSources) { - this.mapSources.forEach(mapSource => { - if (!sourcesToSaveSet.has(mapSource.id)) { - sourcesToSave.push(mapSource); - } - }); - } - const initStyle = this.basemapService.getInitStyle(newBasemap); - this.map.setStyle(initStyle).once('styledata', () => { - setTimeout(() => { - /** the timeout fixes a mapboxgl bug related to layer placement*/ - this.mapFunctionalService.declareBasemapSources(sourcesToSave, this.map); - layersToSave.forEach(l => { - if (!this.map.getLayer(l.id)) { - this.map.addLayer(l); - } - }); - localStorage.setItem(this.LOCAL_STORAGE_BASEMAPS, JSON.stringify(newBasemap)); - this.basemaps.setSelected(newBasemap); - if (newBasemap.type === 'protomap') { - this.basemapService.addProtomapBasemap(this.map); - this.basemapService.notifyProtomapAddition(); - } - this.basemapChanged.emit(); - }, 0); - }); + public ngOnDestroy() { + this._onDestroy$.next(true); + this._onDestroy$.complete(); } + } diff --git a/projects/arlas-map/src/lib/basemaps/basemap.service.ts b/projects/arlas-map/src/lib/basemaps/basemap.service.ts index 7780c788..035fb9f9 100644 --- a/projects/arlas-map/src/lib/basemaps/basemap.service.ts +++ b/projects/arlas-map/src/lib/basemaps/basemap.service.ts @@ -31,6 +31,11 @@ import { ArlasMapService } from '../map/service/arlas-map.service'; export abstract class BasemapService { protected POWERED_BY_ARLAS = ' Powered by ARLAS.'; + protected LOCAL_STORAGE_BASEMAPS = 'arlas_last_base_map'; + + + protected basemapChangedSource: Subject = new Subject(); + public basemapChanged$ = this.basemapChangedSource.asObservable(); public basemaps: ArlasBasemaps; protected protomapBasemapAddedSource = new Subject(); @@ -53,10 +58,8 @@ export abstract class BasemapService { protected addProtomapLayerToMap(map: AbstractArlasMapGL, styleFile: any) { styleFile.layers.forEach(l => { - if (!!map.getLayer(l.id)) { - map.removeLayer(l.id); - } - map.addLayer(l as any); + this.mapService.removeLayer(map, l.id); + this.mapService.addLayer(map, l); }); } @@ -92,4 +95,5 @@ export abstract class BasemapService { public abstract getInitStyle(selected: BasemapStyle): any; public abstract fetchSources$(): Observable; protected abstract getStyleFile(b: BasemapStyle): Observable; + public abstract setBasemap(s: any, newBasemap: BasemapStyle, map: AbstractArlasMapGL, options?: any); } diff --git a/projects/arlas-map/src/lib/map-import/map-import.component.ts b/projects/arlas-map/src/lib/map-import/map-import.component.ts index f8f00e99..ead92dc7 100644 --- a/projects/arlas-map/src/lib/map-import/map-import.component.ts +++ b/projects/arlas-map/src/lib/map-import/map-import.component.ts @@ -513,7 +513,7 @@ export class MapImportComponent { /** *************/ public clearPolygons() { // Clean source of imported polygons - const labelSource = this.mapComponent.map.getSource(this.SOURCE_NAME_POLYGON_LABEL); + const labelSource = this.mapService.getSource(this.SOURCE_NAME_POLYGON_LABEL, this.mapComponent.map); this.featureIndex = 0; this.mapComponent.onAoiChanged.next(this.emptyData); if (labelSource !== undefined) { diff --git a/projects/arlas-map/src/lib/map/AbstractArlasMapGL.ts b/projects/arlas-map/src/lib/map/AbstractArlasMapGL.ts index b53b813f..76bd6e46 100644 --- a/projects/arlas-map/src/lib/map/AbstractArlasMapGL.ts +++ b/projects/arlas-map/src/lib/map/AbstractArlasMapGL.ts @@ -58,11 +58,6 @@ export interface MapConfig { wrapLatLng: boolean; offset: ArlasMapOffset; mapLayers: MapLayers; - mapLayersEventBind: { - onHover: MapEventBinds[]; - emitOnClick: MapEventBinds[]; - zoomOnClick: MapEventBinds[]; - }; customEventBind: (map: AbstractArlasMapGL) => BindLayerToEvent[]; mapProviderOptions?: T; maxWidthScale?: number; @@ -108,8 +103,6 @@ export abstract class AbstractArlasMapGL implements MapInterface { * ex: endlnglat will have a type Maplibre.Pointlike/ Mapbox.Point */ - private eventEmitter: Subject = new Subject(); - public eventEmitter$ = this.eventEmitter.asObservable(); public abstract startlngLat: LngLat; public abstract endlngLat: LngLat; public abstract movelngLat: LngLat; @@ -122,7 +115,6 @@ export abstract class AbstractArlasMapGL implements MapInterface { // @Override protected _mapLayers: MapLayers; // todo: find common type protected _controls: ControlsOption; - public visualisationSetsConfig: Array; protected _icons: Array; public mapSources: Array>; // todo: find common type protected _maxWidthScale?: number; @@ -295,18 +287,12 @@ export abstract class AbstractArlasMapGL implements MapInterface { this._updateBounds(); this._updateZoom(); this.firstDrawLayer = this.getColdOrHotLayers()[0]; - this._initMapLayers(this); this._bindCustomEvent(this); // Fit bounds on current bounds to emit init position in moveend bus this.getMapProvider().fitBounds(this.getBounds()); - this._initVisualisationSet(); }); } - onEvent(e) { - this.eventEmitter.next(e); - } - onCustomEvent(event: string, loadFn: () => void) { this.evented.addEventListener(event, loadFn); } @@ -330,32 +316,6 @@ export abstract class AbstractArlasMapGL implements MapInterface { this._updateOnMoveEnd(); } - protected _initMapLayers(map: AbstractArlasMapGL) { - if (this._mapLayers) { - console.log('init maplayers'); - this.setLayersMap(this._mapLayers as MapLayers); - this._addExternalEventLayers(); - - this.bindLayersToMapEvent( - map, - this._mapLayers.events.zoomOnClick, - this.config.mapLayersEventBind.zoomOnClick - ); - - this.bindLayersToMapEvent( - map, - this.config.mapLayers.events.emitOnClick, - this.config.mapLayersEventBind.emitOnClick - ); - - this.bindLayersToMapEvent( - map, - this.config.mapLayers.events.onHover, - this.config.mapLayersEventBind.onHover - ); - } - } - protected _updateBounds(): void { console.log('_updateBounds call'); @@ -445,16 +405,6 @@ export abstract class AbstractArlasMapGL implements MapInterface { } } - protected _initVisualisationSet() { - if (this.visualisationSetsConfig) { - console.log('_initVisualisationSet'); - this.visualisationSetsConfig.forEach(visu => { - this.visualisationsSets.visualisations.set(visu.name, new Set(visu.layers)); - this.visualisationsSets.status.set(visu.name, visu.enabled); - }); - } - } - public onMoveEnd(cb?: () => void) { return this._moveEnd$ .pipe(map(_ => { @@ -475,7 +425,6 @@ export abstract class AbstractArlasMapGL implements MapInterface { public abstract bindLayersToMapEvent(map: AbstractArlasMapGL, layers: string[] | Set, binds: MapEventBinds[]): void; public abstract calcOffsetPoint(): any; - public abstract redrawSource(id: string, data): void; public abstract getColdOrHotLayers(); public abstract addVisualisation(visualisation: VisualisationSetConfig, layers: Array, sources: Array>): void; public abstract paddedFitBounds(bounds: any, options?: any); @@ -488,7 +437,6 @@ export abstract class AbstractArlasMapGL implements MapInterface { public abstract getSouthBounds(): any; public abstract getSouthWestBounds(): any; public abstract getEastBounds(): any; - public abstract setCursorStyle(cursor: string): void; public abstract getMapProvider(): any; public abstract getMapExtend(): MapExtent; public abstract onLoad(fn: () => void): void; @@ -500,12 +448,10 @@ export abstract class AbstractArlasMapGL implements MapInterface { public abstract paddedBounds(npad: number, spad: number, epad: number, wpad: number, map: any, SW, NE): LngLat[]; - public abstract getLayers(): any; public abstract addControl(control: any, position?: ControlPosition, eventOverride?: { event: string; fn: (e?) => void; }); - public abstract setLayersMap(mapLayers: MapLayers, layers?: Array); protected setStrokeLayoutVisibility(layerId: string, visibility: string): void { @@ -528,96 +474,7 @@ export abstract class AbstractArlasMapGL implements MapInterface { } } - public addLayerInWritePlaceIfNotExist(layerId: string): void { - const layer = this.layersMap.get(layerId); - if (layer !== undefined && layer.id === layerId) { - /** Add the layer if it is not already added */ - if (this.getMapProvider().getLayer(layerId) === undefined) { - if (this.firstDrawLayer && this.firstDrawLayer.length > 0) { - /** draw layers must be on the top of the layers */ - this.getMapProvider().addLayer(layer, this.firstDrawLayer); - } else { - this.getMapProvider().addLayer(layer); - } - } - } else { - throw new Error('The layer `' + layerId + '` is not declared in `mapLayers.layers`'); - } - } - - - public updateLayoutVisibility(visualisationName: string) { - const visuStatus = !this.visualisationsSets.status.get(visualisationName); - this.visualisationSetsConfig.find(v => v.name === visualisationName).enabled = visuStatus; - if (!visuStatus) { - const layersSet = new Set(this.visualisationsSets.visualisations.get(visualisationName)); - this.visualisationsSets.visualisations.forEach((ls, v) => { - if (v !== visualisationName) { - ls.forEach(ll => { - if (layersSet && layersSet.has(ll)) { - layersSet.delete(ll); - } - }); - } - }); - layersSet.forEach(ll => { - this.disableLayoutVisibility(ll); - }); - } - this.visualisationsSets.status.set(visualisationName, visuStatus); - const layers = new Set(); - this.visualisationsSets.visualisations.forEach((ls, v) => { - if (this.visualisationsSets.status.get(v)) { - ls.forEach(l => { - layers.add(l); - this.enableLayoutVisibility(l); - }); - } - }); - return layers; - } - - public updateVisibility(visibilityStatus: Map) { - visibilityStatus.forEach((visibilityStatus, l) => { - let layerInVisualisations = false; - if (!visibilityStatus) { - this.visualisationSetsConfig.forEach(v => { - const ls = new Set(v.layers); - if (!layerInVisualisations) { - layerInVisualisations = ls.has(l); - } - }); - if (layerInVisualisations) { - this.disableLayoutVisibility(l); - } - } else { - let oneVisualisationEnabled = false; - this.visualisationSetsConfig.forEach(v => { - const ls = new Set(v.layers); - if (!layerInVisualisations) { - layerInVisualisations = ls.has(l); - } - if (ls.has(l) && v.enabled) { - oneVisualisationEnabled = true; - this.enableLayoutVisibility(l); - } - }); - if (!oneVisualisationEnabled && layerInVisualisations) { - this.disableLayoutVisibility(l); - } - } - }); - } - - - - public hasCrossOrDrawLayer(e: any): boolean { - const features = this.queryRenderedFeatures(e.point); - return (!!features && !!features.find(f => f && f.layer && f.layer.id && f.layer.id.startsWith(CROSS_LAYER_PREFIX))); - } - - - + public disableLayoutVisibility(layer: string) { this.getMapProvider().setLayoutProperty(layer, 'visibility', 'none'); this.setStrokeLayoutVisibility(layer, 'none'); @@ -635,15 +492,6 @@ export abstract class AbstractArlasMapGL implements MapInterface { return this; } - public findVisualisationSetLayer(visuName: string) { - return this.visualisationSetsConfig.find(v => v.name === visuName).layers; - } - public setVisualisationSetLayers(visuName: string, layers: string[]) { - const f = this.visualisationSetsConfig.find(v => v.name === visuName); - if (f) { - f.layers = layers; - } - } public unsubscribeEvents() { this._eventSubscription.forEach(s => s.unsubscribe()); diff --git a/projects/arlas-map/src/lib/map/service/arlas-map.service.ts b/projects/arlas-map/src/lib/map/service/arlas-map.service.ts index 5361347a..5af13f73 100644 --- a/projects/arlas-map/src/lib/map/service/arlas-map.service.ts +++ b/projects/arlas-map/src/lib/map/service/arlas-map.service.ts @@ -63,6 +63,7 @@ export abstract class ArlasMapService { [{ event: 'mousemove', fn: (e) => { + map.setCursorStyle('pointer'); } } @@ -99,12 +100,14 @@ export abstract class ArlasMapService { */ public abstract addLayer(map: AbstractArlasMapGL, layer: any, beforeId?: string); public abstract addArlasDataLayer(map: AbstractArlasMapGL, layer: any, layersMap: Map, beforeId?: string); - public abstract getLayersFromPattern(map: AbstractArlasMapGL, layersIdPattern: string): any[] + public abstract getLayersFromPattern(map: AbstractArlasMapGL, layersIdPattern: string): any[]; + public abstract getAllLayers(map: AbstractArlasMapGL): any[]; + public abstract hasLayer(map: AbstractArlasMapGL, layer: any); public abstract hasLayersFromPattern(map: AbstractArlasMapGL, layersIdPattern: string); public abstract moveLayer(map: AbstractArlasMapGL, layer: any, beforeId?: string); public abstract moveArlasDataLayer(map: AbstractArlasMapGL, layer: any, layersMap: Map, beforeId?: string); - public abstract onLayerEvent(eventName: any, map: AbstractArlasMapGL, layer: any, fn: () => void); + public abstract onLayerEvent(eventName: any, map: AbstractArlasMapGL, layer: any, fn: (e) => void); public abstract removeLayer(map: AbstractArlasMapGL, layer: any); public abstract removeLayers(map: AbstractArlasMapGL, layers: any) public abstract removeLayersFromPattern(map: AbstractArlasMapGL, layersIdPattern: string); @@ -112,8 +115,11 @@ export abstract class ArlasMapService { public abstract isLayerVisible(layer: any): boolean; public abstract getLayer(map: AbstractArlasMapGL, layerId: string): any; + + public abstract queryFeatures(e: any, map: AbstractArlasMapGL, layersIdPattern: string, options?: any); public abstract hasSource(map: AbstractArlasMapGL, source: any); public abstract getSource(sourceId: string, options: any): any; + public abstract getAllSources(options: any): any; public abstract setSource(sourceId: string, source: any, options: any); public abstract removeSource(map: AbstractArlasMapGL, source: any); @@ -141,6 +147,4 @@ export abstract class ArlasMapService { public abstract createRasterSource(url: string, bounds: number[], maxZoom: number, minZoom: number, tileSize: number): any; - - } diff --git a/projects/arlas-mapbox/src/lib/map/ArlasMapboxGL.ts b/projects/arlas-mapbox/src/lib/map/ArlasMapboxGL.ts index cac4d42b..c4b4cffe 100644 --- a/projects/arlas-mapbox/src/lib/map/ArlasMapboxGL.ts +++ b/projects/arlas-mapbox/src/lib/map/ArlasMapboxGL.ts @@ -52,11 +52,6 @@ import bbox from '@turf/bbox'; export interface ArlasMapboxConfig extends MapConfig { mapLayers: MapLayers; customEventBind: (map: AbstractArlasMapGL) => BindLayerToEvent[]; - mapLayersEventBind: { - onHover: MapEventBinds[]; - emitOnClick: MapEventBinds[]; - zoomOnClick: MapEventBinds[]; - }; } export class ArlasMapboxGL extends AbstractArlasMapGL { @@ -203,10 +198,6 @@ export class ArlasMapboxGL extends AbstractArlasMapGL { return this; } - public setCursorStyle(cursor: string) { - this.getMapProvider().getCanvas().style.cursor = cursor; - } - public enableDragPan() { this.getMapProvider().dragPan.enable(); } @@ -235,31 +226,22 @@ export class ArlasMapboxGL extends AbstractArlasMapGL { public addVisualisation(visualisation: VisualisationSetConfig, layers: Array, sources: Array>): void { - sources.forEach((s) => { - if (typeof (s.source) !== 'string') { - this.getMapProvider().addSource(s.id, s.source); - } - }); - this.visualisationSetsConfig.unshift(visualisation); - this.visualisationsSets.visualisations.set(visualisation.name, new Set(visualisation.layers)); - this.visualisationsSets.status.set(visualisation.name, visualisation.enabled); - layers.forEach(layer => { - this.addLayer(layer); - }); - - this.setLayersMap(this._mapLayers as MapLayers, layers); - this.reorderLayers(); + // sources.forEach((s) => { + // if (typeof (s.source) !== 'string') { + // this.getMapProvider().addSource(s.id, s.source); + // } + // }); + // this.visualisationSetsConfig.unshift(visualisation); + // this.visualisationsSets.visualisations.set(visualisation.name, new Set(visualisation.layers)); + // this.visualisationsSets.status.set(visualisation.name, visualisation.enabled); + // layers.forEach(layer => { + // this.addLayer(layer); + // }); + + // this.setLayersMap(this._mapLayers as MapLayers, layers); + // this.reorderLayers(); } - protected _addExternalEventLayers() { - if (!!this._mapLayers.externalEventLayers) { - this._mapLayers.layers - .filter(layer => this._mapLayers.externalEventLayers.map(e => e.id).indexOf(layer.id) >= 0) - .forEach(l => this.addLayerInWritePlaceIfNotExist(l.id)); - } - } - - public onLoad(fn: () => void): void { this.getMapProvider().on('load', fn); } diff --git a/projects/arlas-maplibre/src/lib/arlas-map-logic.service.ts b/projects/arlas-maplibre/src/lib/arlas-map-logic.service.ts index f647b595..e8554ce4 100644 --- a/projects/arlas-maplibre/src/lib/arlas-map-logic.service.ts +++ b/projects/arlas-maplibre/src/lib/arlas-map-logic.service.ts @@ -5,15 +5,14 @@ import { FeatureCollection } from '@turf/helpers'; import { ArlasMaplibreGL } from './map/ArlasMaplibreGL'; import { ArlasMapSource } from 'arlas-map'; import { MaplibreSourceType } from './map/model/sources'; -import { TypedStyleLayer } from 'maplibre-gl'; +import { GeoJSONSourceSpecification, TypedStyleLayer } from 'maplibre-gl'; import { MapLayers } from 'arlas-map'; @Injectable({ providedIn: 'root' }) export class MapLogicService extends ArlasMapFunctionalService{ - /** IMPORTANT NOTE: All the attributes/params that are typed with "any", will have the right type in the implementation. */ - public dataSources: any[] = []; + public dataSources: GeoJSONSourceSpecification[] = []; public layersMap: Map; public constructor(public mapService: ArlasMaplibreService) { super(mapService); @@ -49,120 +48,11 @@ export class MapLogicService extends ArlasMapFunctionalService{ } public initMapLayers(mapLayers: MapLayers, map: ArlasMaplibreGL) { - if (mapLayers) { - this.setLayersMap(mapLayers as MapLayers); - this.addVisualLayers(); - this._addExternalEventLayers(); - - this.bindLayersToMapEvent( - map, - mapLayers.events.zoomOnClick, - this.config.mapLayersEventBind.zoomOnClick - ); - - this.bindLayersToMapEvent( - map, - this.config.mapLayers.events.emitOnClick, - this.config.mapLayersEventBind.emitOnClick - ); - - this.bindLayersToMapEvent( - map, - this.config.mapLayers.events.onHover, - this.config.mapLayersEventBind.onHover - ); - } + super.initMapLayers(mapLayers, map); } - public reorderLayers() { - // parses the visulisation list from bottom in order to put the fist ones first - for (let i = this.visualisationSetsConfig.length - 1; i >= 0; i--) { - const visualisation: VisualisationSetConfig = this.visualisationSetsConfig[i]; - if (!!visualisation.layers && visualisation.enabled) { - for (let j = visualisation.layers.length - 1; j >= 0; j--) { - const l = visualisation.layers[j]; - const layer = this.layersMap.get(l); - const scrollableId = layer.id.replace(ARLAS_ID, SCROLLABLE_ARLAS_ID); - const scrollableLayer = this.layersMap.get(scrollableId); - if (!!scrollableLayer && !!this.getLayer(scrollableId)) { - this.moveLayer(scrollableId); - } - if (!!this.getLayer(l)) { - this.moveLayer(l); - if (layer.type === 'fill') { - const strokeId = layer.id.replace(ARLAS_ID, FILLSTROKE_LAYER_PREFIX); - const strokeLayer = this.layersMap.get(strokeId); - if (!!strokeLayer && !!this.getLayer(strokeId)) { - this.moveLayer(strokeId); - } - if (!!strokeLayer && !!strokeLayer.id) { - const selectId = 'arlas-' + ExternalEvent.select.toString() + '-' + strokeLayer.id; - const selectLayer = this.layersMap.get(selectId); - if (!!selectLayer && !!this.getLayer(selectId)) { - this.moveLayer(selectId); - } - const hoverId = 'arlas-' + ExternalEvent.hover.toString() + '-' + strokeLayer.id; - const hoverLayer = this.layersMap.get(hoverId); - if (!!hoverLayer && !!this.getLayer(hoverId)) { - this.moveLayer(hoverId); - } - } - } - } - const selectId = 'arlas-' + ExternalEvent.select.toString() + '-' + layer.id; - const selectLayer = this.layersMap.get(selectId); - if (!!selectLayer && !!this.getLayer(selectId)) { - this.moveLayer(selectId); - } - const hoverId = 'arlas-' + ExternalEvent.hover.toString() + '-' + layer.id; - const hoverLayer = this.layersMap.get(hoverId); - if (!!hoverLayer && !!this.getLayer(hoverId)) { - this.moveLayer(hoverId); - } - } - } - } - this.getColdOrHotLayers().forEach(id => this.moveLayer(id)); - } - public updateLayersVisibility(visibilityCondition: boolean, visibilityFilter: Array, visibilityEvent: ExternalEvent, - collection?: string): void { - if (this._mapLayers && this._mapLayers.externalEventLayers) { - this._mapLayers.externalEventLayers.filter(layer => layer.on === visibilityEvent).forEach(layer => { - if (this.getLayer(layer.id) !== undefined) { - let originalLayerIsVisible = false; - const fullLayer = this.layersMap.get(layer.id); - const isCollectionCompatible = (!collection || (!!collection && (fullLayer.source as string).includes(collection))); - if (isCollectionCompatible) { - const originalLayerId = layer.id.replace('arlas-' + visibilityEvent.toString() + '-', ''); - const originalLayer = this.getMapProvider().getStyle().layers.find(l => l.id === originalLayerId); - if (!!originalLayer) { - originalLayerIsVisible = this.isLayerVisible(originalLayer); - } - const layerFilter: Array = []; - const externalEventLayer = this.layersMap.get(layer.id); - if (!!externalEventLayer && !!externalEventLayer.filter) { - externalEventLayer.filter.forEach(f => { - layerFilter.push(f); - }); - } - if (layerFilter.length === 0) { - layerFilter.push('all'); - } - if (visibilityCondition && originalLayerIsVisible) { - layerFilter.push(visibilityFilter); - this.setFilter(layer.id, layerFilter); - this.setLayoutProperty(layer.id, 'visibility', 'visible'); - } else { - this.setFilter(layer.id, (layer as any).filter); - this.setLayoutProperty(layer.id, 'visibility', 'none'); - } - } - } - }); - } - } } \ No newline at end of file diff --git a/projects/arlas-maplibre/src/lib/arlas-maplibre.service.ts b/projects/arlas-maplibre/src/lib/arlas-maplibre.service.ts index 7042aa4f..270bb74d 100644 --- a/projects/arlas-maplibre/src/lib/arlas-maplibre.service.ts +++ b/projects/arlas-maplibre/src/lib/arlas-maplibre.service.ts @@ -1,6 +1,11 @@ import { Injectable } from '@angular/core'; -import { ARLAS_ID, ArlasMapService, FILLSTROKE_LAYER_PREFIX, SCROLLABLE_ARLAS_ID } from 'arlas-map'; -import { AddLayerObject, CanvasSourceSpecification, GeoJSONSource, GeoJSONSourceSpecification, LayerSpecification, LngLatBounds, Point, Popup, RasterLayerSpecification, RasterSourceSpecification, ResourceType, SourceSpecification, SymbolLayerSpecification, TypedStyleLayer } from 'maplibre-gl'; +import { AbstractArlasMapGL, ARLAS_ID, ArlasMapService, FILLSTROKE_LAYER_PREFIX, SCROLLABLE_ARLAS_ID } from 'arlas-map'; +import { + AddLayerObject, CanvasSourceSpecification, GeoJSONSource, + GeoJSONSourceSpecification, LayerSpecification, LngLatBounds, Point, Popup, + RasterLayerSpecification, RasterSourceSpecification, ResourceType, + SourceSpecification, SymbolLayerSpecification, TypedStyleLayer +} from 'maplibre-gl'; import { ArlasMaplibreConfig, ArlasMaplibreGL } from './map/ArlasMaplibreGL'; import { ArlasDraw } from './draw/ArlasDraw'; import { LngLat } from 'arlas-map'; @@ -13,6 +18,8 @@ import { ExternalEvent } from 'arlas-map'; @Injectable() export class ArlasMaplibreService extends ArlasMapService { + + public constructor() { super(); } @@ -146,6 +153,11 @@ export class ArlasMaplibreService extends ArlasMapService { } }; + + public getAllSources(map: ArlasMaplibreGL) { + return map.getMapProvider().getStyle().sources; + } + /** * @override Maplibre implementation. * Adds the given layer to the map instance, optionnaly before a layer. Otherwise it's added to the top. @@ -234,7 +246,7 @@ export class ArlasMaplibreService extends ArlasMapService { this.moveLayer(map, hoverId); } - + } @@ -246,7 +258,7 @@ export class ArlasMaplibreService extends ArlasMapService { * @param layer * @param fn */ - public onLayerEvent(eventName: 'click' | 'mousemove' | 'mouseleave' | 'mouseenter', map: ArlasMaplibreGL, layer: string, fn: () => void): void { + public onLayerEvent(eventName: 'click' | 'mousemove' | 'mouseleave' | 'mouseenter', map: ArlasMaplibreGL, layer: string, fn: (e) => void): void { map.getMapProvider().on(eventName, layer, fn); } @@ -276,14 +288,21 @@ export class ArlasMaplibreService extends ArlasMapService { } }; + + /** + * @overload * Returns the layer object of the given layer id. * @param map Map instance. * @param layer Layer identifier * @returns the layer object. */ - private getLayer(map: ArlasMaplibreGL, layer: string) { - return map.getMapProvider().getLayer(layer); + public getLayer(map: ArlasMaplibreGL, layer: string): TypedStyleLayer { + return map.getMapProvider().getLayer(layer) as TypedStyleLayer; + } + + public getAllLayers(map: ArlasMaplibreGL): TypedStyleLayer[] { + return map.getMapProvider().getStyle().layers as TypedStyleLayer[]; } /** @@ -520,6 +539,21 @@ export class ArlasMaplibreService extends ArlasMapService { map.getMapProvider().setFilter(layerId, filter); } + public queryFeatures(e: any, map: ArlasMaplibreGL, layersIdPattern: string, options: any) { + map.getMapProvider().queryRenderedFeatures(e.point, options).filter(f => !!f.layer && !!f.layer.id && f.layer.id.includes(layersIdPattern)); + }; + + + public isLayerVisible(layer: LayerSpecification): boolean { + return layer.layout.visibility === 'visible'; + } + + public getSource(sourceId: string, map: ArlasMaplibreGL) { + return map.getMapProvider().getSource(sourceId); + } + + + diff --git a/projects/arlas-maplibre/src/lib/basemaps/mapgl-basemap.component.ts b/projects/arlas-maplibre/src/lib/basemaps/mapgl-basemap.component.ts index 985b7b47..6ef98857 100644 --- a/projects/arlas-maplibre/src/lib/basemaps/mapgl-basemap.component.ts +++ b/projects/arlas-maplibre/src/lib/basemaps/mapgl-basemap.component.ts @@ -24,6 +24,7 @@ import { MaplibreBasemapService } from './maplibre-basemap.service'; import { ArlasMapSource } from 'arlas-map'; import { MaplibreSourceType } from '../map/model/sources'; import { ArlasMapFunctionalService } from 'arlas-map'; +import { ArlasMaplibreService } from '../arlas-maplibre.service'; @Component({ selector: 'arlas-maplibre-basemap', @@ -33,7 +34,9 @@ import { ArlasMapFunctionalService } from 'arlas-map'; export class MaplibreBasemapComponent extends BasemapComponent implements OnInit { @Input() public map: ArlasMaplibreGL; @Input() public mapSources: Array>; - public constructor(protected basemapService: MaplibreBasemapService, protected mapFunctionalService: ArlasMapFunctionalService) { - super(basemapService, mapFunctionalService); + public constructor(protected basemapService: MaplibreBasemapService, + protected mapFunctionalService: ArlasMapFunctionalService, + protected mapService: ArlasMaplibreService) { + super(basemapService, mapFunctionalService, mapService); } } diff --git a/projects/arlas-maplibre/src/lib/basemaps/maplibre-basemap.service.ts b/projects/arlas-maplibre/src/lib/basemaps/maplibre-basemap.service.ts index 2d7055e2..9d2567dc 100644 --- a/projects/arlas-maplibre/src/lib/basemaps/maplibre-basemap.service.ts +++ b/projects/arlas-maplibre/src/lib/basemaps/maplibre-basemap.service.ts @@ -22,23 +22,26 @@ import * as pmtiles from 'pmtiles'; import { MapLibreBasemapStyle } from './basemap.config'; import { catchError, forkJoin, Observable, of, tap } from 'rxjs'; import { HttpClient } from '@angular/common/http'; -import maplibre, { GetResourceResponse, RequestParameters, VectorSourceSpecification } from 'maplibre-gl'; -import { BackgroundLayerSpecification } from '@maplibre/maplibre-gl-style-spec'; -import { BasemapService } from 'arlas-map'; +import maplibre, { AddLayerObject, RequestParameters, TypedStyleLayer } from 'maplibre-gl'; +import { BackgroundLayerSpecification, LayerSpecification } from '@maplibre/maplibre-gl-style-spec'; +import { BasemapService, BasemapStyle } from 'arlas-map'; import { ArlasMaplibreGL } from '../map/ArlasMaplibreGL'; import { ArlasMaplibreService } from '../arlas-maplibre.service'; +import { ArlasMapSource } from 'arlas-map'; +import { MapLogicService } from '../arlas-map-logic.service'; +import { MaplibreSourceType } from '../map/model/sources'; @Injectable({ providedIn: 'root' }) export class MaplibreBasemapService extends BasemapService { - - public constructor(protected http: HttpClient, protected mapService: ArlasMaplibreService) { + public constructor(protected http: HttpClient, protected mapService: ArlasMaplibreService, + private mapLogicService: MapLogicService + ) { super(http, mapService); } - public addProtomapBasemap(map: ArlasMaplibreGL) { const selectedBasemap = this.basemaps.getSelected(); if (selectedBasemap.type === 'protomap') { @@ -120,4 +123,48 @@ export class MaplibreBasemapService extends BasemapService { return of(b.styleFile); } } + + public setBasemap(s: any, newBasemap: BasemapStyle, map: ArlasMaplibreGL, mapSources: Array>) { + const selectedBasemapLayersSet = new Set(); + const layers: Array = this.mapService.getAllLayers(map); + const sources = this.mapService.getAllSources(map); + if (s.layers) { + s.layers.forEach(l => selectedBasemapLayersSet.add(l.id)); + } + // TODO: Array to any try to find a good type or interface for all layer + const layersToSave = new Array(); + const sourcesToSave = new Array>(); + layers.filter((l: any) => !selectedBasemapLayersSet.has(l.id) && !!l.source).forEach(l => { + layersToSave.push(l as AddLayerObject); + if (sourcesToSave.filter(ms => ms.id === l.source.toString()).length === 0) { + sourcesToSave.push({ id: l.source.toString(), source: sources[l.source.toString()] as MaplibreSourceType }); + } + }); + const sourcesToSaveSet = new Set(); + sourcesToSave.forEach(mapSource => sourcesToSaveSet.add(mapSource.id)); + if (mapSources) { + mapSources.forEach(mapSource => { + if (!sourcesToSaveSet.has(mapSource.id)) { + sourcesToSave.push(mapSource); + } + }); + } + const initStyle = this.getInitStyle(newBasemap); + map.getMapProvider().setStyle(initStyle).once('styledata', () => { + setTimeout(() => { + /** the timeout fixes a mapboxgl bug related to layer placement*/ + this.mapLogicService.declareBasemapSources(sourcesToSave, map); + layersToSave.forEach(l => { + this.mapService.addLayer(map, l); + }); + localStorage.setItem(this.LOCAL_STORAGE_BASEMAPS, JSON.stringify(newBasemap)); + this.basemaps.setSelected(newBasemap); + if (newBasemap.type === 'protomap') { + this.addProtomapBasemap(map); + this.notifyProtomapAddition(); + } + this.basemapChangedSource.next(); + }, 0); + }); + } } diff --git a/projects/arlas-maplibre/src/lib/map/ArlasMaplibreGL.ts b/projects/arlas-maplibre/src/lib/map/ArlasMaplibreGL.ts index 75e52171..bea367f0 100644 --- a/projects/arlas-maplibre/src/lib/map/ArlasMaplibreGL.ts +++ b/projects/arlas-maplibre/src/lib/map/ArlasMaplibreGL.ts @@ -60,11 +60,6 @@ import bbox from '@turf/bbox'; export interface ArlasMaplibreConfig extends MapConfig { mapLayers: MapLayers; customEventBind: (map: AbstractArlasMapGL) => BindLayerToEvent[]; - mapLayersEventBind: { - onHover: MapEventBinds[]; - emitOnClick: MapEventBinds[]; - zoomOnClick: MapEventBinds[]; - }; } export class ArlasMaplibreGL extends AbstractArlasMapGL { @@ -265,23 +260,22 @@ export class ArlasMaplibreGL extends AbstractArlasMapGL { - // TODO : should fix any in source public addVisualisation(visualisation: VisualisationSetConfig, layers: Array, sources: Array>): void { - sources.forEach((s) => { - if (typeof (s.source) !== 'string') { - this.getMapProvider().addSource(s.id, s.source); - } - }); - this.visualisationSetsConfig.unshift(visualisation); - this.visualisationsSets.visualisations.set(visualisation.name, new Set(visualisation.layers)); - this.visualisationsSets.status.set(visualisation.name, visualisation.enabled); - layers.forEach(layer => { - this.addLayer(layer); - }); - // TODO : should fix any in source - this.setLayersMap(this._mapLayers as MapLayers, layers); - this.reorderLayers(); + // sources.forEach((s) => { + // if (typeof (s.source) !== 'string') { + // this.getMapProvider().addSource(s.id, s.source); + // } + // }); + // this.visualisationSetsConfig.unshift(visualisation); + // this.visualisationsSets.visualisations.set(visualisation.name, new Set(visualisation.layers)); + // this.visualisationsSets.status.set(visualisation.name, visualisation.enabled); + // layers.forEach(layer => { + // this.addLayer(layer); + // }); + // // TODO : should fix any in source + // this.setLayersMap(this._mapLayers as MapLayers, layers); + // this.reorderLayers(); } public disableDragPan(): void { @@ -416,24 +410,13 @@ export class ArlasMaplibreGL extends AbstractArlasMapGL { this.fitBounds(bounds, paddedOptions); } - public redrawSource(id: string, data) { - if (this.getSource(id) !== undefined) { - (this.getSource(id) as maplibregl.GeoJSONSource).setData({ - 'type': 'FeatureCollection', - 'features': data - }); - } - } + public resize(eventData?: unknown): this { this._mapProvider.resize(eventData); return this; } - public setCursorStyle(cursor: string): void { - this.getMapProvider().getCanvas().style.cursor = cursor; - } - public setLayersMap(mapLayers: MapLayers, layers?: Array) { if (mapLayers) { const mapLayersCopy = mapLayers;