diff --git a/src/app/map/map.component.html b/src/app/map/map.component.html
index 328608e..15f1cf1 100644
--- a/src/app/map/map.component.html
+++ b/src/app/map/map.component.html
@@ -1,9 +1,11 @@
+
+ {{ showLoader }}
-{{ showLoader }}
diff --git a/src/app/map/map.component.ts b/src/app/map/map.component.ts
index fa1149f..1016e00 100644
--- a/src/app/map/map.component.ts
+++ b/src/app/map/map.component.ts
@@ -1,9 +1,28 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
-import { control, latLng, Layer, LeafletEvent, Map as LeafletMap, MapOptions } from 'leaflet';
+import {
+ CircleMarker,
+ control,
+ GeoJSON,
+ latLng,
+ Layer,
+ LayerGroup,
+ LeafletEvent,
+ Map as LeafletMap,
+ MapOptions
+} from 'leaflet';
import { MapService } from './map.service';
import { NGXLogger } from 'ngx-logger';
-import { centerOfEurope, defaultZoom } from './map.constants';
-import { Subscription } from 'rxjs';
+import {
+ attributionOptions,
+ centerOfEurope,
+ defaultCapitolsStyle,
+ defaultZoom,
+ defaultZoomPanOptions,
+ euBorderStyle,
+ LayerIndices,
+ noDrawStyle
+} from './map.constants';
+import { Subject, takeUntil } from 'rxjs';
@Component({
selector: 'app-map',
@@ -24,53 +43,71 @@ export class MapComponent implements OnInit, OnDestroy {
attributionControl: false
};
- layers: Layer[] = [];
+ layers: Promise;
showLoader: boolean = false;
- private map?: LeafletMap;
+ private theMap?: LeafletMap;
private theZoom?: number;
- private onReset?: Subscription;
- private onToMyLocation?: Subscription;
+
+ private onUnsubscribe$: Subject = new Subject();
constructor(
private log: NGXLogger,
private mapService: MapService
- ) {}
+ ) {
+ this.layers = this.loadAllLayers();
+ }
ngOnInit(): void {
- this.onReset = this.mapService.resetMap.subscribe(() => {
+ this.mapService.resetMap$.pipe(takeUntil(this.onUnsubscribe$)).subscribe(() => {
this.resetMap();
});
- this.onToMyLocation = this.mapService.toMyLocation.subscribe(() => {
+ this.mapService.toMyLocation$.pipe(takeUntil(this.onUnsubscribe$)).subscribe(() => {
this.myLocation();
});
+
+ this.mapService.showEuBorders$.pipe(takeUntil(this.onUnsubscribe$)).subscribe(value => {
+ this.showEuBorders(value);
+ });
+
+ this.mapService.showCapitols$.pipe(takeUntil(this.onUnsubscribe$)).subscribe(value => {
+ this.showCapitols(value);
+ });
}
ngOnDestroy(): void {
- this.onReset!.unsubscribe();
- this.onToMyLocation!.unsubscribe();
+ this.unsubscribeAll();
+ this.disposeMap();
+ }
- this.log.info('Disposing map');
+ private unsubscribeAll() {
+ this.onUnsubscribe$.next(true);
+ this.onUnsubscribe$.complete();
+ }
- this.map!.clearAllEventListeners();
- this.map!.remove();
+ private disposeMap() {
+ this.theMap!.clearAllEventListeners();
+ this.theMap!.remove();
+ this.log.info('Disposed theMap');
}
onMapReady(map: LeafletMap) {
- this.map = map;
-
- this.map.addControl(
- control.attribution({
- position: 'bottomleft',
- prefix: ''
- })
- );
+ this.theMap = map;
+ this.log.info('Leaflet theMap ready');
+ this.theMap.addControl(control.attribution(attributionOptions));
+ this.theZoom = map.getZoom();
+ }
- this.layers.push(this.mapService.getBaseLayer());
- this.mapService.load(this.layers);
+ private async loadAllLayers() {
+ return this.mapService.loadAllLayers().then(([baseLayer, euBorderLayer, capitolsLayer]) => {
+ const layers = new Array(3);
+ layers[LayerIndices.BASE_LAYER_INDEX] = baseLayer;
+ layers[LayerIndices.EU_LAYER_INDEX] = euBorderLayer;
+ layers[LayerIndices.CAPITOLS_LAYER_INDEX] = capitolsLayer;
- this.theZoom = map.getZoom();
+ return layers;
+ });
}
onMapZoomStart() {
@@ -85,17 +122,18 @@ export class MapComponent implements OnInit, OnDestroy {
}
resetMap() {
- this.map!.flyTo(centerOfEurope, defaultZoom);
+ this.theMap!.flyTo(centerOfEurope, defaultZoom);
// TODO clear my location marker
}
myLocation() {
navigator.geolocation.getCurrentPosition(
position => {
- this.map!.flyTo(latLng(position.coords.latitude, position.coords.longitude), defaultZoom + 2, {
- animate: true,
- duration: 1
- });
+ this.theMap!.flyTo(
+ latLng(position.coords.latitude, position.coords.longitude),
+ defaultZoom + 2,
+ defaultZoomPanOptions
+ );
},
err => {
this.log.error('Could not get geolocation', err);
@@ -104,4 +142,26 @@ export class MapComponent implements OnInit, OnDestroy {
}
);
}
+
+ private showEuBorders(value: boolean) {
+ this.layers
+ .then(layer => {
+ return layer[LayerIndices.EU_LAYER_INDEX] as GeoJSON;
+ })
+ .then(l => l.setStyle(value ? euBorderStyle : noDrawStyle));
+ }
+
+ private showCapitols(value: boolean) {
+ this.theMap!.closePopup();
+
+ this.layers
+ .then(layer => {
+ return layer[LayerIndices.CAPITOLS_LAYER_INDEX] as LayerGroup;
+ })
+ .then(capitols => capitols.getLayers().forEach(capitol => this.markerVisible(capitol, value)));
+ }
+
+ private markerVisible(layer: Layer, visible: boolean) {
+ (layer as CircleMarker).setStyle(visible ? defaultCapitolsStyle : noDrawStyle);
+ }
}
diff --git a/src/app/map/map.constants.ts b/src/app/map/map.constants.ts
index 64275f9..1bc8d25 100644
--- a/src/app/map/map.constants.ts
+++ b/src/app/map/map.constants.ts
@@ -1,13 +1,37 @@
// TODO find a way to configure color
-import { circleMarker, latLng, layerGroup, tileLayer } from 'leaflet';
+import {
+ CircleMarker,
+ circleMarker,
+ Control,
+ latLng,
+ LayerGroup,
+ layerGroup,
+ tileLayer,
+ ZoomPanOptions
+} from 'leaflet';
import { environment } from '../../environments/environment';
+import AttributionOptions = Control.AttributionOptions;
+
+export const attributionOptions: AttributionOptions = {
+ position: 'bottomleft',
+ prefix: ''
+};
+
+export const defaultZoomPanOptions: ZoomPanOptions = {
+ animate: true,
+ duration: 1
+};
+
+export const noDrawStyle = { radius: 0, opacity: 0, fill: false, stroke: false, popup: false };
export const euBorderStyle = {
color: '#fff',
fillColor: '#fff',
opacity: 0.4,
weight: 0.6,
- fillOpacity: 0.06
+ fillOpacity: 0.06,
+ fill: true,
+ stroke: true
};
export const darkMatterNoLabelsLayer = tileLayer(environment.tileServerUrl, {
@@ -18,14 +42,24 @@ export const darkMatterNoLabelsLayer = tileLayer(environment.tileServerUrl, {
export const centerOfEurope = latLng(54.526, 15.2551);
// TODO find a way to configure color
-const berlin = circleMarker([52.52, 13.405], {
+export const defaultCapitolsStyle = {
radius: 8,
weight: 2,
- color: '#ffd617'
-}).bindPopup('Berlin', {
- closeButton: false
-});
+ color: '#ffd617',
+ opacity: 1.0,
+ fill: true,
+ stroke: true,
+ popup: true
+};
-export const capitols = layerGroup([berlin]);
+const berlin = circleMarker([52.52, 13.405], defaultCapitolsStyle).bindPopup('Berlin', { closeButton: false });
+
+export const capitols: LayerGroup = layerGroup([berlin]);
export const defaultZoom = 5;
+
+export enum LayerIndices {
+ BASE_LAYER_INDEX = 0,
+ EU_LAYER_INDEX = 1,
+ CAPITOLS_LAYER_INDEX = 2
+}
diff --git a/src/app/map/map.service.ts b/src/app/map/map.service.ts
index 78f7024..00fc64f 100644
--- a/src/app/map/map.service.ts
+++ b/src/app/map/map.service.ts
@@ -1,44 +1,78 @@
-import { EventEmitter, Injectable, Output } from '@angular/core';
-import { geoJson, Layer } from 'leaflet';
+import { EventEmitter, Injectable } from '@angular/core';
+import { GeoJSON, geoJson, Layer } from 'leaflet';
import { HttpClient } from '@angular/common/http';
import { NGXLogger } from 'ngx-logger';
import { GeoJsonObject } from 'geojson';
import { capitols, centerOfEurope, darkMatterNoLabelsLayer, euBorderStyle } from './map.constants';
+import { firstValueFrom } from 'rxjs';
@Injectable()
export class MapService {
- @Output() resetMap = new EventEmitter();
- @Output() toMyLocation = new EventEmitter();
+ resetMap$ = new EventEmitter();
+ toMyLocation$ = new EventEmitter();
+ showEuBorders$ = new EventEmitter();
+ showCapitols$ = new EventEmitter();
+
+ private readonly euBordersLayer: Promise;
constructor(
private http: HttpClient,
private log: NGXLogger
- ) {}
+ ) {
+ this.euBordersLayer = this.initEuBordersLayer();
+ }
- getBaseLayer() {
- return darkMatterNoLabelsLayer;
+ private async initEuBordersLayer() {
+ return firstValueFrom(this.http.get('./assets/geo-data/european-borders.json'))
+ .then(jsonResponse => {
+ this.log.debug('Loaded borders json', jsonResponse);
+ return geoJson(jsonResponse, { style: euBorderStyle });
+ })
+ .catch(err => {
+ this.log.error('Error loading EU borders geo json', err);
+ // TODO error notification
+ return geoJson();
+ });
}
getCenterOfEurope() {
return centerOfEurope;
}
- load(layers: Layer[]) {
- this.http.get('./assets/geo-data/european-borders.json').subscribe((json: GeoJsonObject) => {
- this.log.info('Loaded borders json', json);
+ async loadAllLayers(): Promise {
+ return Promise.all([this.getBaseLayer(), this.getEuBordersLayer(), this.getCapitolsLayer()]);
+ }
+
+ private async getBaseLayer() {
+ return darkMatterNoLabelsLayer;
+ }
- layers.push(geoJson(json, { style: euBorderStyle }));
- layers.push(capitols);
- });
+ private async getEuBordersLayer() {
+ return this.euBordersLayer;
}
- resetMapToEuropeanCenter() {
+ // TODO load capitols via HTTP
+ private async getCapitolsLayer() {
+ return capitols;
+ }
+
+ async resetMapToEuropeanCenter() {
this.log.info('Reset map');
- this.resetMap.emit();
+ this.resetMap$.emit();
}
- moveMapToMyLocation() {
+ async moveMapToMyLocation() {
this.log.info('Move map to my location');
- this.toMyLocation.emit();
+ this.toMyLocation$.emit();
+ }
+
+ async doShowEuBorders(value: boolean) {
+ this.log.trace('Show EU borders', value);
+ this.showEuBorders$.emit(value);
+ }
+
+ async doShowCapitols(value: boolean) {
+ this.log.trace('Show capitols', value);
+ this.showCapitols$.emit(value);
}
}
diff --git a/src/app/option-pane/option-pane.component.html b/src/app/option-pane/option-pane.component.html
index 197ba1f..a3d0c7a 100644
--- a/src/app/option-pane/option-pane.component.html
+++ b/src/app/option-pane/option-pane.component.html
@@ -17,15 +17,15 @@
+ (change)="euBordersChanged($event)">
{{ 'option-pane.show-eu-borders' | transloco }}
- {{ 'option-pane.show-berlin' | transloco }}
+ [checked]="showCapitols"
+ (change)="capitolsChanged($event)">
+ {{ 'option-pane.show-capitols' | transloco }}
diff --git a/src/app/option-pane/option-pane.component.ts b/src/app/option-pane/option-pane.component.ts
index c6205af..c41b308 100644
--- a/src/app/option-pane/option-pane.component.ts
+++ b/src/app/option-pane/option-pane.component.ts
@@ -1,5 +1,6 @@
-import { Component } from '@angular/core';
-import { NGXLogger } from 'ngx-logger';
+import { Component, Input } from '@angular/core';
+import { MapService } from '../map/map.service';
+import { MatSlideToggleChange } from '@angular/material/slide-toggle';
@Component({
selector: 'app-option-pane',
@@ -7,11 +8,12 @@ import { NGXLogger } from 'ngx-logger';
styleUrl: 'option-pane.component.scss'
})
export class OptionPaneComponent {
- expanded: boolean = true;
- showEuBorders: boolean = true;
- showBerlin: boolean = true;
+ expanded: boolean = false;
- constructor(private log: NGXLogger) {}
+ @Input() showEuBorders: boolean = true;
+ @Input() showCapitols: boolean = true;
+
+ constructor(private mapService: MapService) {}
protected expand() {
this.expanded = true;
@@ -21,11 +23,11 @@ export class OptionPaneComponent {
this.expanded = false;
}
- euBordersChanged() {
- this.log.debug('EU border changed');
+ euBordersChanged(event: MatSlideToggleChange) {
+ this.mapService.doShowEuBorders(event.checked).then(() => (this.showEuBorders = event.checked));
}
- berlinChanged() {
- this.log.debug('Berlin changed');
+ capitolsChanged(event: MatSlideToggleChange) {
+ this.mapService.doShowCapitols(event.checked).then(() => (this.showCapitols = event.checked));
}
}
diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json
index 8faeaf2..2239488 100644
--- a/src/assets/i18n/de.json
+++ b/src/assets/i18n/de.json
@@ -3,11 +3,11 @@
"title": "Europas Städte"
},
"toolbar": {
- "reset": "Zurücksetzen",
+ "reset": "Karte zurücksetzen",
"my-location": "Wo bin ich"
},
"option-pane":{
"show-eu-borders": "EU Grenzen (UN)",
- "show-berlin": "Berlin anzeigen"
+ "show-capitols": "Hauptstädte anzeigen"
}
}
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index fdc4180..e46cd9f 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -3,11 +3,11 @@
"title": "Cities of Europe"
},
"toolbar": {
- "reset": "Reset",
+ "reset": "Reset map",
"my-location": "My location"
},
"option-pane": {
"show-eu-borders": "European borders (UN)",
- "show-berlin": "Show Berlin"
+ "show-capitols": "Show capitols"
}
}