import * as leaflet from 'leaflet' ; import { AntPath , LatLng } from 'leaflet-ant-path' ; import { ManagedComponent } from '../ManagedComponent' ; const calculateAngleFromLatlon = ( p1 , p2 ) => 360 - ( ( Math . atan2 ( p2 [ 0 ] - p1 [ 0 ] , p2 [ 1 ] - p1 [ 1 ] ) * ( 180 / Math . PI ) + 360 ) % 360 ) ; const { PI , asin , sin , cos , sqrt , pow } = Math ; const EARTH_RADIUS_KM = 6371 ; const latLonDist = ( p1 , p2 ) => asin ( sqrt ( pow ( sin ( ( ( p2 [ 0 ] * PI ) / 180 - ( p1 [ 0 ] * PI ) / 180 ) / 2 ) , 2 ) + cos ( p1 [ 0 ] ) * cos ( p2 [ 0 ] ) * pow ( sin ( ( ( p2 [ 1 ] * PI ) / 180 - ( p1 [ 1 ] * PI ) / 180 ) / 2 ) , 2 ) ) ) * EARTH_RADIUS_KM * 2 ; const VIEW_CENTERING_THRESHOLD_DISTANCE_KM = 0.01 ; const VIEW_CENTERING_THRESHOLD_MS = 10000 ; export class ManagedMap extends ManagedComponent { private _map ? : leaflet . Map ; private _marker ? : leaflet . Marker ; private _path ? : AntPath ; private _dragable = false ; public get dragable ( ) : boolean { return this . _dragable ; } public set dragable ( dragable ) { if ( this . _dragable === dragable ) return ; this . _dragable = dragable ; if ( this . _map ) this . _map . options . dragging = dragable ; } private _zoom = 13 ; public get zoom ( ) : number { return this . _map ?. getZoom ( ) ?? this . _zoom ; } public set zoom ( level ) { this . _zoom = level ; this . _map ?. setZoom ( level ) ; } public setProperties = ( ) : void => undefined ; public update ( ) { cancelAnimationFrame ( this . rafHandle ) ; this . rafHandle = requestAnimationFrame ( this . _update . bind ( this ) ) ; } private _lastPoint ? : LatLng ; private _lastPointTime ? : number ; private rafHandle = - 1 ; private _update ( ) { if ( ! this . _path || ! this . _map || ! this . _marker ) return ; const _points = this . manager . getLatLonTime ( ) ; this . _path . setLatLngs ( _points as any ) ; if ( _points . length ) { const last = ( _points [ _points . length - 1 ] as any ) as LatLng ; const last2 = ( _points [ _points . length - 2 ] as any ) as LatLng ; try { if ( ! this . _lastPoint || ! this . _lastPointTime || latLonDist ( this . _lastPoint , last ) > VIEW_CENTERING_THRESHOLD_DISTANCE_KM || this . _lastPointTime < Date . now ( ) - VIEW_CENTERING_THRESHOLD_MS ) { this . _map . setView ( last , this . _map . getZoom ( ) , { animate : true , pan : { duration : 1 , } , } as any ) ; this . _lastPoint = last ; this . _lastPointTime = Date . now ( ) ; } this . _marker . setLatLng ( last ) ; if ( last2 ) this . _marker . setRotationAngle ?. ( calculateAngleFromLatlon ( last , last2 ) - 90 ) ; } catch ( e ) { console . warn ( `error thrown by leaflet map: ${ e ?. message ?? e ?? 'unspecific error' }` ) ; } } else { this . _marker . setLatLng ( [ 0 , 0 ] ) ; this . _lastPoint = undefined ; } } protected _destroy ( ) { this . _map ?. remove ?. ( ) ; this . _map = undefined ; } _render ( ) { const _points = this . manager . getLatLonTime ( ) ; const point = _points ?. length ? ( _points [ _points . length - 1 ] as any ) : [ 51.4921374 , - 0.1928784 ] ; const map = leaflet . map ( this . element , { scrollWheelZoom : false , dragging : this . _dragable } ) . setView ( point , this . _zoom ) ; leaflet . tileLayer ( 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' , { attribution : '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' , maxZoom : 18 , } ) . addTo ( map ) ; this . _map = map ; this . _marker = leaflet . marker ( [ 0 , 0 ] , { icon : new leaflet . Icon ( { iconUrl : '/car.png' , shadowUrl : '' , iconSize : [ 60 / 3 , 179 / 3 ] , iconAnchor : [ 60 / 6 , 179 / 6 ] , } ) , } ) . addTo ( map ) ; this . _path = new AntPath ( [ ] ) . addTo ( map ) ; setTimeout ( ( ) => window . dispatchEvent ( new Event ( 'resize' ) ) , 2000 ) ; this . update ( ) ; } onDataChanged ( ) { this . update ( ) ; } }