From f56171dd9e724dd8d4b78ee381bb233ba3e0c761 Mon Sep 17 00:00:00 2001
From: Holger Stolzenberg <h.stolzenberg@ewerk.com>
Date: Wed, 6 Mar 2024 14:32:37 +0100
Subject: [PATCH] Moved map model from component to service

---
 src/app/map/map.component.ts | 54 ++++++-------------------------
 src/app/map/map.service.ts   | 61 +++++++++++++++++++++++-------------
 2 files changed, 50 insertions(+), 65 deletions(-)

diff --git a/src/app/map/map.component.ts b/src/app/map/map.component.ts
index 57d4749..665d942 100644
--- a/src/app/map/map.component.ts
+++ b/src/app/map/map.component.ts
@@ -3,8 +3,6 @@ import { MapService } from './map.service';
 import { NGXLogger } from 'ngx-logger';
 import { Subject, takeUntil } from 'rxjs';
 import { NotificationService } from '../notifications/notification.service';
-import { Deck } from '@deck.gl/core/typed';
-import { INITIAL_VIEW_STATE, LayerIndices } from './map.constants';
 import { DeckMetrics } from '@deck.gl/core/typed/lib/deck';
 
 @Component({
@@ -13,29 +11,30 @@ import { DeckMetrics } from '@deck.gl/core/typed/lib/deck';
   styleUrl: './map.component.scss'
 })
 export class MapComponent implements OnInit, AfterViewInit, OnDestroy {
+  @ViewChild('deckGlMap', { static: false }) private mapDiv?: ElementRef<HTMLDivElement>;
+
   showLoader: boolean = false;
   showMetrics: boolean = true;
   loadedTileId: string = '';
 
-  readonly metrics$?: Subject<DeckMetrics> = new Subject<DeckMetrics>();
+  readonly metrics$: Subject<DeckMetrics> = new Subject<DeckMetrics>();
 
   private readonly onUnsubscribe$: Subject<boolean> = new Subject<boolean>();
 
-  @ViewChild('deckGlMap', { static: false }) private mapDiv?: ElementRef<HTMLDivElement>;
-  private map?: Deck;
-
   constructor(
     private log: NGXLogger,
     private mapService: MapService,
     private notificationService: NotificationService
-  ) {}
+  ) {
+    this.metrics$.pipe(takeUntil(this.onUnsubscribe$));
+  }
 
   ngOnInit(): void {
     this.initAllSubscriptions();
   }
 
   ngAfterViewInit() {
-    this.initDeckGlMap();
+    this.mapService.initDeckGlMap(this.mapDiv!, this.metrics$!);
   }
 
   ngOnDestroy(): void {
@@ -43,33 +42,11 @@ export class MapComponent implements OnInit, AfterViewInit, OnDestroy {
     this.disposeMap();
   }
 
-  initDeckGlMap() {
-    this.mapService.getLayers().then(layers => {
-      this.map = new Deck({
-        parent: this.mapDiv!.nativeElement,
-        initialViewState: INITIAL_VIEW_STATE,
-        style: { position: 'relative', top: '0', bottom: '0' },
-        controller: true,
-        useDevicePixels: false,
-        layers: [layers],
-
-        onWebGLInitialized: gl => {
-          gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE, gl.ONE_MINUS_DST_ALPHA, gl.ONE);
-          gl.blendEquation(gl.FUNC_ADD);
-        },
-
-        _onMetrics: metrics => {
-          this.metrics$!.next(metrics);
-        }
-      });
-    });
-  }
-
   private initAllSubscriptions() {
     // prettier-ignore
     this.mapService.loading$
-      .pipe(takeUntil(this.onUnsubscribe$))
-      .subscribe(tileId => this.showHideLoader(tileId));
+        .pipe(takeUntil(this.onUnsubscribe$))
+        .subscribe(tileId => this.showHideLoader(tileId));
 
     // TODO deck.gl: this.resetMap();
     this.mapService.resetMap$.pipe(takeUntil(this.onUnsubscribe$)).subscribe(() => {
@@ -80,29 +57,18 @@ export class MapComponent implements OnInit, AfterViewInit, OnDestroy {
     this.mapService.toMyLocation$.pipe(takeUntil(this.onUnsubscribe$)).subscribe(() => {
       this.notificationService.showWarnLocalized('common.not-implemented');
     });
-
-    this.mapService.showEuBorders$.pipe(takeUntil(this.onUnsubscribe$)).subscribe(value => {
-      this.mapService.changeLayerVisibility(this.map!, LayerIndices.EU_LAYER_INDEX, value);
-    });
-
-    this.mapService.showCapitols$.pipe(takeUntil(this.onUnsubscribe$)).subscribe(value => {
-      this.mapService.changeLayerVisibility(this.map!, LayerIndices.CAPITOLS_LAYER_INDEX, value);
-    });
   }
 
   private showHideLoader(tileId: string) {
     this.loadedTileId = tileId;
     this.showLoader = true;
-    setTimeout(() => (this.showLoader = false), 500);
+    setTimeout(() => (this.showLoader = false), 1000);
   }
 
   private unsubscribeAll() {
     this.onUnsubscribe$.next(true);
     this.onUnsubscribe$.complete();
     this.onUnsubscribe$!.unsubscribe();
-
-    this.metrics$!.complete();
-    this.metrics$!.unsubscribe();
   }
 
   // TODO deck.gl: dispose deck map
diff --git a/src/app/map/map.service.ts b/src/app/map/map.service.ts
index 659b50f..526bb10 100644
--- a/src/app/map/map.service.ts
+++ b/src/app/map/map.service.ts
@@ -1,26 +1,27 @@
-import { EventEmitter, Injectable } from '@angular/core';
+import { ElementRef, EventEmitter, Injectable } from '@angular/core';
 import { HttpClient } from '@angular/common/http';
 import { NGXLogger } from 'ngx-logger';
-import { CAPITOLS_LAYER, CENTER_OF_EUROPE } from './map.constants';
+import { CAPITOLS_LAYER, CENTER_OF_EUROPE, INITIAL_VIEW_STATE, LayerIndices } from './map.constants';
 import { NotificationService } from '../notifications/notification.service';
 import { Deck, Layer } from '@deck.gl/core/typed';
 import { BitmapLayer, GeoJsonLayer } from '@deck.gl/layers/typed';
-import { firstValueFrom } from 'rxjs';
+import { firstValueFrom, 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';
 
 @Injectable()
 export class MapService {
   resetMap$ = new EventEmitter<string>();
   toMyLocation$ = new EventEmitter<string>();
-  showEuBorders$ = new EventEmitter<boolean>();
-  showCapitols$ = new EventEmitter<boolean>();
   loading$ = new EventEmitter<string>();
 
   private readonly mapLayer: Promise<TileLayer>;
   private readonly euBordersLayer: Promise<GeoJsonLayer>;
   private readonly layers: Promise<Layer[]>;
 
+  private map?: Deck;
+
   constructor(
     private readonly http: HttpClient,
     private readonly log: NGXLogger,
@@ -31,22 +32,10 @@ export class MapService {
     this.layers = this.loadAllLayers();
   }
 
-  getLayers() {
-    return this.layers;
-  }
-
   async loadAllLayers(): Promise<Layer[]> {
     return Promise.all([this.getMapLayer(), this.getEuBordersLayer(), this.getCapitolsLayer()]);
   }
 
-  changeLayerVisibility(map: Deck, layerIndex: number, value: boolean) {
-    this.layers.then(layers => {
-      const clonedLayers = layers.slice();
-      clonedLayers[layerIndex] = layers[layerIndex].clone({ visible: value });
-      map.setProps({ layers: clonedLayers });
-    });
-  }
-
   async resetMapToEuropeanCenter() {
     this.log.debug('Reset map');
     this.resetMap$.emit();
@@ -58,13 +47,35 @@ export class MapService {
   }
 
   async doShowEuBorders(value: boolean) {
-    this.log.trace('Show EU borders', value);
-    this.showEuBorders$.emit(value);
+    this.changeLayerVisibility(LayerIndices.EU_LAYER_INDEX, value).then(() => this.log.trace('Show EU borders', value));
   }
 
   async doShowCapitols(value: boolean) {
-    this.log.trace('Show capitols', value);
-    this.showCapitols$.emit(value);
+    this.changeLayerVisibility(LayerIndices.CAPITOLS_LAYER_INDEX, value).then(() =>
+      this.log.trace('Show capitols', value)
+    );
+  }
+
+  initDeckGlMap(mapDiv: ElementRef<HTMLDivElement>, metricsRef: Subject<DeckMetrics>) {
+    this.layers.then(layers => {
+      this.map = new Deck({
+        parent: mapDiv.nativeElement,
+        initialViewState: INITIAL_VIEW_STATE,
+        style: { position: 'relative', top: '0', bottom: '0' },
+        controller: true,
+        useDevicePixels: false,
+        layers: [layers],
+
+        onWebGLInitialized: gl => {
+          gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE, gl.ONE_MINUS_DST_ALPHA, gl.ONE);
+          gl.blendEquation(gl.FUNC_ADD);
+        },
+
+        _onMetrics: metrics => {
+          metricsRef.next(metrics);
+        }
+      });
+    });
   }
 
   private async initMapLayer() {
@@ -121,6 +132,14 @@ export class MapService {
       });
   }
 
+  private async changeLayerVisibility(layerIndex: number, value: boolean) {
+    this.layers.then(layers => {
+      const clonedLayers = layers.slice();
+      clonedLayers[layerIndex] = layers[layerIndex].clone({ visible: value });
+      this.map!.setProps({ layers: clonedLayers });
+    });
+  }
+
   // TODO deck.gl: do not forget this method
   private async getCenterOfEurope() {
     return CENTER_OF_EUROPE;