From 5bcdd4b7c8825c8c846e6c4e26a522591fdd820a Mon Sep 17 00:00:00 2001 From: Holger Stolzenberg Date: Mon, 11 Mar 2024 11:08:49 +0100 Subject: [PATCH] Refactor: use observable for layer loading and management Fixes the bug of layers in service not being updated --- src/app/map/map.component.ts | 10 ++- src/app/map/map.service.ts | 121 ++++++++++++++++++----------------- 2 files changed, 69 insertions(+), 62 deletions(-) diff --git a/src/app/map/map.component.ts b/src/app/map/map.component.ts index bf1f9d2..0713e37 100644 --- a/src/app/map/map.component.ts +++ b/src/app/map/map.component.ts @@ -1,6 +1,6 @@ import { AfterViewInit, Component, ElementRef, OnDestroy, ViewChild } from '@angular/core'; import { MapService } from './map.service'; -import { BehaviorSubject, Subject, takeUntil } from 'rxjs'; +import { BehaviorSubject, delay, of, Subject, take, takeUntil } from 'rxjs'; import { DeckMetrics } from '@deck.gl/core/typed/lib/deck'; @Component({ @@ -39,7 +39,13 @@ export class MapComponent implements AfterViewInit, OnDestroy { private showHideLoader(tileId: string) { this.loadedTileId = tileId; this.showLoader$.next(true); - setTimeout(() => this.showLoader$.next(false), 1000); + + of([]).pipe( + delay(1000), + take(1) + ).subscribe(() => { + this.showLoader$.next(false); + }); } private unsubscribeAll() { diff --git a/src/app/map/map.service.ts b/src/app/map/map.service.ts index 8cc2379..77ccba7 100644 --- a/src/app/map/map.service.ts +++ b/src/app/map/map.service.ts @@ -13,7 +13,7 @@ import { import { NotificationService } from '../notifications/notification.service'; import { Deck, FlyToInterpolator, Layer } from '@deck.gl/core/typed'; import { BitmapLayer, GeoJsonLayer } from '@deck.gl/layers/typed'; -import { firstValueFrom, Subject } from 'rxjs'; +import { catchError, delay, firstValueFrom, forkJoin, map, Observable, of, Subject } from 'rxjs'; import { TileLayer } from '@deck.gl/geo-layers/typed'; import { environment } from '../../environments/environment'; import { DeckMetrics } from '@deck.gl/core/typed/lib/deck'; @@ -23,15 +23,13 @@ import { GeoService } from './geo.service'; export class MapService { loading$ = new EventEmitter(); - private readonly mapLayer: Promise; - private readonly euBordersLayer: Promise; - private readonly layers: Promise; - private currentViewState = INITIAL_VIEW_STATE; + private layers$ = new Observable(); + private layers = new Array(3); + private myLocation?: GeolocationCoordinates; private loadingIndicator$?: Subject; - private theMap?: Deck; constructor( @@ -40,21 +38,33 @@ export class MapService { private readonly geoService: GeoService, private readonly notificationService: NotificationService ) { - this.mapLayer = this.initMapLayer(); - this.euBordersLayer = this.initEuBordersLayer(); - this.layers = this.loadAllLayers(); + this.layers$ = this.loadAllLayers(); + } + + private loadEuGeoJson() { + return this.http.get('./assets/geo-data/eu-borders.json'); } - async loadAllLayers(): Promise { - return Promise.all([this.getMapLayer(), this.getEuBordersLayer(), this.getCapitolsLayer()]).then( - ([map, euBorder, capitols]) => { - //doing this for full control over ordering - const layers = new Array(3); - layers[LayerIndices.MAP_LAYER] = map; - layers[LayerIndices.EU_BORDERS_LAYER] = euBorder; - layers[LayerIndices.CAPITOLS_LAYER] = capitols; - return layers; + loadAllLayers(): Observable { + return forkJoin( + { + mapLayer: this.initMapLayer(), + euGeoJsonData: this.loadEuGeoJson(), + capitolsLayer: this.initCapitolsLayer() } + ).pipe( + map( + data => { + const layers = new Array(3); + layers[LayerIndices.MAP_LAYER] = data.mapLayer; + layers[LayerIndices.EU_BORDERS_LAYER] = this.initEuBordersLayer(data.euGeoJsonData); + layers[LayerIndices.CAPITOLS_LAYER] = data.capitolsLayer; + return layers; + }), + catchError(err => { + this.notificationService.showError('Error loading EU borders geo json', err); + throw err; + }) ); } @@ -89,11 +99,13 @@ export class MapService { mapDiv: ElementRef, metricsRef: Subject, showLoader$: Subject, - mapHidden: Subject + mapHidden$: Subject ) { this.loadingIndicator$ = showLoader$; - this.layers.then(layers => { + this.layers$.subscribe(layers => { + this.layers = layers; + this.theMap = new Deck({ parent: mapDiv.nativeElement, viewState: this.currentViewState, @@ -114,8 +126,12 @@ export class MapService { }, onLoad: () => { - this.log.debug('Deck GL map is ready'); - setTimeout(() => mapHidden.next(false), 1000); + of([]).pipe( + delay(1000) + ).subscribe(() => { + this.log.debug('Deck GL map is ready'); + mapHidden$.next(false); + }); } }); }); @@ -151,8 +167,8 @@ export class MapService { }); } - private async initMapLayer() { - return new TileLayer({ + private initMapLayer() { + const mapLayer = new TileLayer({ id: 'map-layer', data: environment.tileServerUrls, maxRequests: 20, @@ -179,50 +195,35 @@ export class MapService { ]; } }); + + return of(mapLayer); } - private async initEuBordersLayer() { - return firstValueFrom(this.http.get('./assets/geo-data/eu-borders.json')) - .then(geoJson => { - this.log.info('Loaded borders json', geoJson); - - return new GeoJsonLayer({ - id: 'eu-borders-layer', - data: geoJson, - pickable: false, - stroked: true, - filled: true, - lineWidthMinPixels: 1, - getFillColor: [255, 214, 23, 10], - getLineColor: [255, 214, 23, 50], - getElevation: 0, - visible: true - }); - }) - .catch(err => { - this.notificationService.showError('Error loading EU borders geo json', err); - return new GeoJsonLayer({ id: 'eu-borders-layer' }); - }); + private initEuBordersLayer(geoJson: JSON) { + return new GeoJsonLayer({ + id: 'eu-borders-layer', + data: geoJson, + pickable: false, + stroked: true, + filled: true, + lineWidthMinPixels: 1, + getFillColor: [255, 214, 23, 10], + getLineColor: [255, 214, 23, 50], + getElevation: 0, + visible: true + }); } // TODO feature: load capitols via HTTP and add population - private async getCapitolsLayer() { - return CAPITOLS_LAYER; + private initCapitolsLayer() { + return of(CAPITOLS_LAYER); } private async changeLayerVisibility(index: number, value: boolean) { - this.layers.then(layers => { - const clonedLayers = layers.slice(); - clonedLayers[index] = layers[index].clone({ visible: value }); - this.theMap!.setProps({ layers: clonedLayers }); - }); - } - - private async getMapLayer() { - return this.mapLayer; - } + const clonedLayers = this.layers.slice(); + clonedLayers[index] = this.layers[index].clone({ visible: value }); - private async getEuBordersLayer() { - return this.euBordersLayer; + this.theMap!.setProps({ layers: clonedLayers }); + this.layers = clonedLayers; } }