| /******************************************************************************** |
| * Copyright (c) 2020 Contributors to the Eclipse Foundation |
| * |
| * See the NOTICE file(s) distributed with this work for additional |
| * information regarding copyright ownership. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0 |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| ********************************************************************************/ |
| |
| import {AfterViewInit, Directive, ElementRef, forwardRef, Inject, Input, NgZone, OnDestroy, OnInit, Optional, Output} from "@angular/core"; |
| import {InvalidateSizeOptions, LatLngLiteral, LeafletMouseEvent, Map, PopupEvent, TileLayer} from "leaflet"; |
| import {defer, Observable} from "rxjs"; |
| import {debounceTime, delay, map, takeUntil, throttleTime} from "rxjs/operators"; |
| import {runInZone} from "../../../../util/rxjs"; |
| import {ILeafletConfiguration, LEAFLET_CONFIGURATION_TOKEN, LEAFLET_RESIZE_TOKEN} from "../../leaflet-configuration.token"; |
| import {latLngZoomToString} from "../../util"; |
| import {LeafletHandler} from "./LeafletHandler"; |
| |
| export interface ILeafletBounds { |
| northWest: LatLngLiteral; |
| northEast: LatLngLiteral; |
| southEast: LatLngLiteral; |
| southWest: LatLngLiteral; |
| center: LatLngLiteral; |
| zoom: number; |
| } |
| |
| @Directive({ |
| selector: "[appLeaflet]", |
| exportAs: "appLeaflet", |
| providers: [ |
| { |
| provide: LeafletHandler, |
| useExisting: forwardRef(() => LeafletDirective) |
| } |
| ] |
| }) |
| export class LeafletDirective extends LeafletHandler implements OnInit, OnDestroy, AfterViewInit { |
| |
| public readonly instance: Map; |
| |
| @Output() |
| public appClick = defer(() => this.on<LeafletMouseEvent>("click")); |
| |
| @Output() |
| public appPopupClose = defer(() => this.on<PopupEvent>("popupclose")).pipe(delay(0)); |
| |
| @Output() |
| public appUnload$ = defer(() => this.on("unload")); |
| |
| @Output() |
| public appLatLngZoomChange = defer(() => this.on("moveend zoomend", true)).pipe( |
| debounceTime(10), |
| map(() => latLngZoomToString(this.instance.getCenter(), this.instance.getZoom())), |
| runInZone(this.ngZone) |
| ); |
| |
| public constructor( |
| public readonly elementRef: ElementRef<HTMLElement>, |
| public readonly ngZone: NgZone, |
| @Inject(LEAFLET_CONFIGURATION_TOKEN) public readonly configuration: ILeafletConfiguration, |
| @Optional() @Inject(LEAFLET_RESIZE_TOKEN) public resize$: Observable<any> |
| ) { |
| super(); |
| this.instance = this.ngZone.runOutsideAngular(() => { |
| const tileLayer = new TileLayer(this.configuration.urlTemplate, { |
| attribution: this.configuration.attribution |
| }); |
| const result = new Map(this.elementRef.nativeElement, {layers: [tileLayer]}); |
| result.setView({lat: configuration.lat, lng: configuration.lng}, configuration.zoom); |
| return result; |
| }); |
| } |
| |
| @Input() |
| public set appCenter(value: { lat: number, lng: number, zoom: number }) { |
| if (value != null) { |
| this.ngZone.runOutsideAngular(() => this.instance.setView(value, value.zoom)); |
| } |
| } |
| |
| @Input() |
| public set appDisabled(value: boolean) { |
| const handlers = [ |
| this.instance.dragging, |
| this.instance.touchZoom, |
| this.instance.doubleClickZoom, |
| this.instance.scrollWheelZoom, |
| this.instance.boxZoom, |
| this.instance.keyboard |
| ]; |
| handlers.forEach((handler) => value ? handler.disable() : handler.enable()); |
| value ? this.instance.removeControl(this.instance.zoomControl) : this.instance.addControl(this.instance.zoomControl); |
| } |
| |
| public ngOnInit() { |
| if (this.resize$ instanceof Observable) { |
| this.resize$.pipe(throttleTime(10), takeUntil(this.appUnload$)) |
| .subscribe((_) => this.invalidateSize()); |
| } |
| } |
| |
| public ngAfterViewInit() { |
| setTimeout(() => this.invalidateSize()); |
| } |
| |
| public ngOnDestroy() { |
| this.ngZone.runOutsideAngular(() => this.instance.remove()); |
| } |
| |
| public getZoom(): number { |
| return this.ngZone.runOutsideAngular(() => this.instance.getZoom()); |
| } |
| |
| public getBounds(): ILeafletBounds { |
| return this.ngZone.runOutsideAngular(() => { |
| const bounds = this.instance.getBounds(); |
| const zoom = this.getZoom(); |
| const result: ILeafletBounds = { |
| northWest: bounds.getNorthWest(), |
| northEast: bounds.getNorthEast(), |
| southEast: bounds.getSouthEast(), |
| southWest: bounds.getSouthWest(), |
| center: bounds.getCenter(), |
| zoom |
| }; |
| return this.ngZone.run(() => result); |
| }); |
| } |
| |
| public invalidateSize(options?: boolean | InvalidateSizeOptions) { |
| this.ngZone.runOutsideAngular(() => this.instance.invalidateSize(options)); |
| } |
| |
| } |