import { HttpClient } from '@angular/common/http' ; import { Injectable } from '@angular/core' ; import Collection from 'ol/Collection' ; import Feature from 'ol/Feature' ; import Geometry from 'ol/geom/Geometry' ; import VectorLayer from 'ol/layer/Vector' ; import VectorSource from 'ol/source/Vector' ; import { Draw , Modify } from 'ol/interaction' ; import { Fill , Icon , Stroke , Style , Text } from 'ol/style' ; import { GeoJSON } from 'ol/format' ; import { Point } from 'ol/geom' ; import { catchError , timeout } from 'rxjs/operators' ; import { of } from 'rxjs' ; import { transform } from 'ol/proj' ; import Layer from 'ol/layer/Layer' ; import Source from 'ol/source/Source' ; import { HsEventBusService } from '../core/event-bus.service' ; import { HsLanguageService } from '../language/language.service' ; import { HsLayerUtilsService } from '../utils/layer-utils.service' ; import { HsLayoutService } from '../layout/layout.service' ; import { HsMapService } from '../map/map.service' ; import { HsShareUrlService } from '../permalink/share-url.service' ; import { HsToastService } from '../layout/toast/toast.service' ; import { HsUtilsService } from '../utils/utils.service' ; import { getHighlighted } from '../../common/feature-extensions' ; import { getTitle , setTitle } from '../../common/layer-extensions' ; export type Waypoint = { name : string ; lon : number ; lat : number ; hash : number ; routes : { from : Feature < Geometry > ; to : Feature < Geometry > ; } ; featureId ; editMode ? : boolean ; loading : boolean ; } ; const WAYPOINT = 'wp' ; export function setWaypoint ( feature , wp ) { feature . set ( WAYPOINT , wp ) ; } export function getWaypoint ( feature ) { return feature . get ( WAYPOINT ) ; } @ Injectable ( { providedIn : 'root' , } ) export class HsTripPlannerService { waypoints : Waypoint [ ] = [ ] ; trip : any = { } ; movable_features = new Collection < Feature < Geometry > > ( ) ; modify = new Modify ( { features : this . movable_features , } ) ; waypointSource : VectorSource < Point > ; waypointLayer : VectorLayer < VectorSource < Point > > ; routeSource : VectorSource < Geometry > ; routeLayer : VectorLayer < VectorSource < Geometry > > ; timer : any ; vectorLayers : { layer : VectorLayer < VectorSource < Geometry > > ; title : string ; } [ ] ; selectedLayerWrapper : { route ? : { layer : VectorLayer < VectorSource < Geometry > > ; title : string ; } ; waypoints ? : { layer : VectorLayer < VectorSource < Geometry > > ; title : string ; } ; } = { } ; waypointRouteStyle = ( feature , resolution ) => { return [ new Style ( { fill : new Fill ( { color : 'rgba(255, 255, 255, 0.6)' , } ) , stroke : new Stroke ( { color : '#337AB7' , width : 3 , } ) , image : new Icon ( { src : getHighlighted ( feature ) ? this . HsUtilsService . getAssetsPath ( ) + 'img/pin_white_red32.png' : this . HsUtilsService . getAssetsPath ( ) + 'img/pin_white_blue32.png' , crossOrigin : 'anonymous' , anchor : [ 0.5 , 1 ] , } ) , text : new Text ( { font : '12px Calibri,sans-serif' , overflow : true , fill : new Fill ( { color : '#000' , } ) , stroke : new Stroke ( { color : '#fff' , width : 3 , } ) , offsetY : - 40 , text : this . getTextOnFeature ( feature ) , } ) , } ) , ] ; } ; constructor ( public HsMapService , public HsUtilsService , private $http , public HsShareUrlService , public HsEventBusService , private HsToastService , public HsLanguageService , private HsLayerUtilsService , private HsLayoutService ) { this . HsMapService . loaded ( ) . then ( ( map ) => { map . addInteraction ( this . modify ) ; } ) ; this . HsEventBusService . mapClicked . subscribe ( ( { coordinates } ) => { if ( this . HsLayoutService . mainpanel != 'tripPlanner' ) { return ; } if ( ! this . waypointLayer ) { this . createWaypointLayer ( ) ; } if ( ! this . routeLayer ) { this . createRouteLayer ( ) ; } if ( this . HsMapService . map . getInteractions ( ) . getArray ( ) . find ( ( i ) => i . getActive ( ) && this . HsUtilsService . instOf ( i , Draw ) ) ) { return ; } this . addWaypoint ( { x : coordinates . mapProjCoordinate [ 0 ] , y : coordinates . mapProjCoordinate [ 1 ] , lon : coordinates . epsg4326Coordinate [ 0 ] , lat : coordinates . epsg4326Coordinate [ 1 ] , } ) ; } ) ; } async fillVectorLayers ( ) { this . HsMapService . loaded ( ) . then ( ( map ) => { this . vectorLayers = [ { layer : null , title : 'newLayer' , } , ... this . HsMapService . getLayersArray ( ) . filter ( ( layer ) => this . HsLayerUtilsService . isLayerDrawable ( layer ) ) . map ( ( layer ) => { return { layer , title : getTitle ( layer ) } ; } ) , ] ; this . fillDefaultLayerWrapper ( 'route' ) ; this . fillDefaultLayerWrapper ( 'waypoints' ) ; } ) ; } private fillDefaultLayerWrapper ( usage ) { if ( this . selectedLayerWrapper [ usage ] ) { this . selectedLayerWrapper [ usage ] = this . vectorLayers . find ( ( w ) => w . layer == this . selectedLayerWrapper [ usage ] . layer ) ; } else { this . selectedLayerWrapper [ usage ] = this . vectorLayers [ 0 ] ; } } createWaypointLayer ( ) { this . waypointSource = new VectorSource ( ) ; this . waypointLayer = new VectorLayer ( { source : this . waypointSource , style : this . waypointRouteStyle , } ) ; setTitle ( this . waypointLayer , this . HsLanguageService . getTranslation ( 'TRIP_PLANNER.waypoints' ) ) ; this . HsMapService . map . addLayer ( this . waypointLayer ) ; } createRouteLayer ( ) { this . routeSource = new VectorSource ( ) ; this . routeLayer = new VectorLayer ( { source : this . routeSource , style : this . waypointRouteStyle , } ) ; setTitle ( this . routeLayer , this . HsLanguageService . getTranslation ( 'TRIP_PLANNER.travelRoute' ) ) ; this . HsMapService . map . addLayer ( this . routeLayer ) ; } param layer param usage async selectLayer ( layer , usage ) { if ( usage == 'route' ) { this . routeLayer = layer . layer ; if ( this . routeLayer ) { this . routeSource = this . routeLayer . getSource ( ) ; } this . selectedLayerWrapper . route = layer ; } if ( usage == 'waypoints' ) { this . waypointLayer = layer . layer as VectorLayer < VectorSource < Point > > ; if ( this . waypointLayer ) { this . waypointSource = this . waypointLayer . getSource ( ) ; } this . routeSource . clear ( ) ; this . waypoints . length = 0 ; this . selectedLayerWrapper . waypoints = layer ; for ( const feature of this . waypointSource . getFeatures ( ) ) { const new_cords = transform ( feature . getGeometry ( ) . getCoordinates ( ) , this . HsMapService . getCurrentProj ( ) . getCode ( ) , 'EPSG:4326' ) ; const wp = { lon : new_cords [ 0 ] , lat : new_cords [ 1 ] , name : 'Waypoint ' + ( this . waypoints . length + 1 ) , hash : this . HsUtilsService . hashCode ( JSON . stringify ( 'Waypoint ' + this . waypoints . length + Math . random ( ) ) ) , routes : { from : null , to : null } , featureId : feature . getId ( ) , loading : false , } ; this . waypoints . push ( wp ) ; if ( this . waypointAdded !== undefined ) { this . waypointAdded ( wp ) ; } await this . calculateRoutes ( ) ; } } } getTextOnFeature ( feature ) { let tmp = '' ; const wp = getWaypoint ( feature ) ; if ( wp ) { tmp = wp . name ; if ( wp ?. routes ?. to ) { tmp += ` (${ this . formatDistance ( wp , 'to' ) })` ; } } return tmp ; } param { number } lon param { number } lat addWaypoint ( { x , y , lon , lat } ) { const wp = { lon , lat , name : 'Waypoint ' + ( this . waypoints . length + 1 ) , hash : this . HsUtilsService . hashCode ( JSON . stringify ( 'Waypoint ' + this . waypoints . length + Math . random ( ) ) ) , routes : { from : null , to : null } , featureId : null , loading : false , } ; const feature = new Feature ( { 'wp' : wp , geometry : new Point ( [ x , y ] ) , id : this . HsUtilsService . generateUuid ( ) , } ) as Feature < Point > ; feature . setId ( feature . get ( 'id' ) ) ; wp . featureId = feature . getId ( ) ; this . waypointSource . addFeature ( feature ) ; this . waypoints . push ( wp ) ; if ( this . waypointAdded !== undefined ) { this . waypointAdded ( wp ) ; } this . calculateRoutes ( ) ; } param wp waypointAdded ( wp ) { const feature = this . waypointSource . getFeatureById ( wp . featureId ) ; this . movable_features . push ( feature ) ; feature . getGeometry ( ) . on ( 'change' , ( e ) => { this . removeRoutesForWaypoint ( wp ) ; const new_cords = transform ( feature . getGeometry ( ) . getCoordinates ( ) , this . HsMapService . getCurrentProj ( ) . getCode ( ) , 'EPSG:4326' ) ; wp . lon = new_cords [ 0 ] ; wp . lat = new_cords [ 1 ] ; const prev_index = this . waypoints . indexOf ( wp ) - 1 ; if ( prev_index > - 1 ) { this . waypoints [ prev_index ] . routes . from = null ; this . routeRemoved ( this . waypoints [ prev_index ] . routes . from ) ; } if ( this . timer !== null ) { clearTimeout ( this . timer ) ; } this . timer = setTimeout ( ( ) => { this . calculateRoutes ( ) ; } , 500 ) ; } ) ; } param feature routeRemoved ( feature ) { try { if ( feature ) { this . routeSource . removeFeature ( feature ) ; } } catch ( ex ) { throw ex ; } } param wp removeRoutesForWaypoint ( wp ) { this . routeRemoved ( wp . routes . from ) ; this . routeRemoved ( wp . routes . to ) ; wp . routes = { from : null , to : null } ; } param wp waypointRemoved ( wp ) { try { this . waypointSource . removeFeature ( this . waypointSource . getFeatureById ( wp . featureId ) ) ; } catch ( ex ) { throw ex ; } } param { object } wp removeWaypoint ( wp ) { const wpIndex = this . waypoints . indexOf ( wp ) ; const prev_index = wpIndex - 1 ; if ( prev_index > - 1 ) { this . waypoints [ prev_index ] . routes . from = null ; } this . routeRemoved ( wp . routes . from ) ; this . routeRemoved ( wp . routes . to ) ; const next_index = wpIndex + 1 ; if ( next_index < this . waypoints . length ) { this . waypoints [ next_index ] . routes . to = null ; } this . waypointRemoved ( wp ) ; this . waypoints . splice ( this . waypoints . indexOf ( wp ) , 1 ) ; this . calculateRoutes ( ) ; } /**
     * Clear all waypoints from service and layer
     */ clearAll ( ) { this . waypoints = [ ] ; this . waypointSource . clear ( ) ; this . routeSource . clear ( ) ; } param feature routeAdded ( features ) { this . routeSource . addFeatures ( features ) ; } /**
     * Calculate routes between stored waypoints
     */ async calculateRoutes ( ) { for ( let i = 0 ; i < this . waypoints . length - 1 ; i ++ ) { const wpf = this . waypoints [ i ] ; if ( wpf . routes . from === null ) { const wpt = this . waypoints [ i + 1 ] ; wpt . loading = true ; const url = this . HsUtilsService . proxify ( 'https://api.openrouteservice.org/v2/directions/driving-car/geojson' ) ; const response = await this . $http . post ( url , { 'coordinates' : [ [ wpf . lon , wpf . lat ] , [ wpt . lon , wpt . lat ] , ] , } ) . pipe ( timeout ( 10000 ) , catchError ( ( e ) => { let title = this . HsLanguageService . getTranslation ( 'TRIP_PLANNER.serviceDown' ) ; if ( e . status == 404 ) { title = this . HsLanguageService . getTranslation ( 'TRIP_PLANNER.missingAuth' ) ; } this . HsToastService . createToastPopupMessage ( title , this . HsLanguageService . getTranslationIgnoreNonExisting ( 'ERRORMESSAGES' , e . message , { url : url } ) , { disableLocalization : true , serviceCalledFrom : 'HsTripPlannerService' , } ) ; return of ( null ) ; } ) ) . toPromise ( ) ; if ( ! response ) { return ; } wpt . loading = false ; const format = new GeoJSON ( ) ; const features = format . readFeatures ( response ) ; features [ 0 ] . getGeometry ( ) . transform ( 'EPSG:4326' , this . HsMapService . getCurrentProj ( ) ) ; wpf . routes . from = features [ 0 ] ; wpt . routes . to = features [ 0 ] ; if ( this . routeAdded !== undefined ) { this . routeAdded ( features ) ; } } } } param wp param which returns formatDistance ( wp , which ? ) { which = which !== undefined ? which : 'from' ; if ( wp . routes [ which ] ) { const route = wp . routes [ which ] ; const distance = route ?. get ( 'summary' ) . distance / 1000.0 ; if ( distance == undefined ) { return '' ; } else { return distance . toFixed ( 2 ) + 'km' ; } } } }