import { environment } from './../../../environments/environment' ; import { AppConfig } from './../../core/AppConfig' ; import { SettingsPage } from './../settings/settings' ; import { TranslateService } from '@ngx-translate/core' ; import { RoutePlannerPage } from './../route-planner/route-planner' ; import { POIDetailsPage } from './../poi-details/poi-details' ; import { GeoLatLng , GeoPosition } from './../../model/GeoPosition' ; import { POISearchParams } from './../../model/POISearchParams' ; import { Utils } from './../../core/Utils' ; import { Logging , LogLevel } from './../../services/Logging' ; import { JourneyManager } from './../../services/JourneyManager' ; import { Mapping } from './../../services/mapping/Mapping' ; import { POIManager } from './../../services/POIManager' ; import { AppManager } from './../../services/AppManager' ; import { Component , OnInit , NgZone , ViewChild , AfterViewInit } from '@angular/core' ; import { NavController , Platform , ModalController , AlertController } from '@ionic/angular' ; import { PlaceSearch } from '../../components/place-search/place-search' ; import { PlaceSearchResult } from '../../model/AppModels' ; import { fromEvent } from 'rxjs' ; import { debounceTime , map } from 'rxjs/operators' ; import { Events } from '../../services/Events' ; import { Geolocation , PositionOptions } from '@capacitor/geolocation' ; @ Component ( { templateUrl : 'search.html' , styleUrls : [ './search.scss' ] } ) export class SearchPage implements OnInit , AfterViewInit { private mapDisplayed : boolean = false ; private debouncedRefreshMapResults : any ; private mapCanvasID : string ; private initialResultsShown : boolean = false ; private searchOnDemand : boolean = false ; public poiViewMode : string = 'modal' ; public sideViewAvailable = false ; private searchPolyline : string ; private routePlanningMode : boolean = true ; public searchKeyword : string = '' ; public selectedPOI : any ; public appConfig = new AppConfig ( ) ; public defaultMapZoom : number = 15 ; public defaultStartPos = new GeoLatLng ( 37.415328 , - 122.076575 ) ; @ ViewChild ( PlaceSearch , { static : true } ) placeSearchMapPOI : PlaceSearch ; constructor ( public appManager , public nav , public events , public translate , public platform , public poiManager , public mapping , public journeyManager , public zone , public modalController , private alertController , public logging ) { this . mapCanvasID = 'map-canvas' ; this . mapping . setMapType ( appManager . searchSettings . MapType ) ; this . mapping . setMapAPI ( environment . defaultMapProvider ) ; const resizeEvent = fromEvent ( window , 'resize' ) ; resizeEvent . pipe ( debounceTime ( 500 ) ) . subscribe ( ( event ) => { let size = { width : Utils . getClientHeight ( ) , height : Utils . getClientWidth ( ) } ; this . events . publish ( 'ocm:window:resized' , [ size ] ) ; } ) ; } ionViewDidEnter ( ) { this . logging . log ( 'Entered search page.' , LogLevel . VERBOSE ) ; this . mapping . focusMap ( ) ; this . mapping . updateMapSize ( ) ; } ionViewWillLeave ( ) { this . logging . log ( 'Leaving search page.' , LogLevel . VERBOSE ) ; this . mapping . unfocusMap ( ) ; } getPreferredMapHeight ( clientHeight ) { if ( clientHeight == null ) { clientHeight = Utils . getClientHeight ( ) ; } const preferredContentHeight = clientHeight - 56 ; return preferredContentHeight ; } enforceMapHeight ( size ) { this . logging . log ( 'Would resize map:' + size . width + ' ' + size . height , LogLevel . VERBOSE ) ; this . checkViewportMode ( ) ; const preferredContentHeight = this . getPreferredMapHeight ( size [ 0 ] ) ; if ( document . getElementById ( this . mapCanvasID ) . offsetHeight !== preferredContentHeight ) { document . getElementById ( this . mapCanvasID ) . style . height = preferredContentHeight + 'px' ; } if ( this . mapping ) { this . logging . log ( 'Map height:' + preferredContentHeight , LogLevel . VERBOSE ) ; this . mapping . updateMapSize ( ) ; } } checkViewportMode ( ) { } async initialiseMapping ( ) { await this . platform . ready ( ) ; this . debouncedRefreshMapResults = Utils . debounce ( this . refreshMapResults , 1000 , false ) ; this . events . subscribe ( 'ocm:mapping:ready' , async ( ) => { this . logging . log ( "init map: mapping ready" ) ; if ( this . mapping ) { this . mapping . updateMapSize ( ) ; } if ( ! this . initialResultsShown ) { if ( this . appManager . searchSettings . StartSearchPosition ) { this . searchOnDemand = true ; this . mapping . updateMapCentrePos ( this . appManager . searchSettings . StartSearchPosition . latitude , this . appManager . searchSettings . StartSearchPosition . longitude , true , this . defaultMapZoom ) ; } else { let locationEnabled = localStorage . getItem ( "_locationEnabled" ) ; if ( this . appManager . isEmbeddedMode == true && locationEnabled != 'true' ) { const alert = await this . alertController . create ( { header : 'Search using your location?' , message : 'Select OK to continue.' , buttons : [ { text : 'Cancel' , role : 'cancel' , cssClass : 'secondary' , handler : async ( blah ) => { await this . useFallbackPosition ( ) ; } } , { text : 'OK' , handler : async ( ) => { await this . locateUser ( ) ; } } ] } ) ; await alert . present ( ) ; } else { await this . locateUser ( ) ; if ( this . mapping ) { this . mapping . updateMapSize ( ) ; } } } } } ) ; this . events . subscribe ( 'ocm:mapping:zoom' , ( ) => { this . debouncedRefreshMapResults ( ) ; } ) ; this . events . subscribe ( 'ocm:mapping:dragend' , ( ) => { this . debouncedRefreshMapResults ( ) ; } ) ; this . events . subscribe ( 'ocm:poiList:updated' , ( listType ) => { this . showPOIListOnMap ( listType ) ; } ) ; this . events . subscribe ( 'ocm:poiList:cleared' , ( ) => { this . mapping . clearMarkers ( ) ; setTimeout ( ( ) => { this . debouncedRefreshMapResults ( ) ; } , 500 ) ; } ) ; this . events . subscribe ( 'ocm:window:resized' , ( size ) => { if ( size != null && size . length > 0 ) { this . enforceMapHeight ( size [ 0 ] ) ; } } ) ; this . events . subscribe ( 'ocm:poi:selected' , ( args ) => { if ( args . poi ) { this . viewPOIDetails ( args , args . poi ) ; } else { let poi = this . getPOIByID ( args . poiId ) ; this . viewPOIDetails ( args , poi ) ; } } ) ; this . checkViewportMode ( ) ; this . mapping . initMap ( this . mapCanvasID ) ; } async ngAfterViewInit ( ) { await this . initialiseMapping ( ) ; } async ngOnInit ( ) { this . logging . log ( 'Refreshing reference data ..' , LogLevel . VERBOSE ) ; const refreshedOK = await this . appManager . referenceDataManager . refreshReferenceData ( this . appManager . api ) ; if ( ! refreshedOK ) { this . appManager . isOffline = true ; } } showPOIListOnMap ( listType ) { const preferredMapHeight = this . getPreferredMapHeight ( null ) ; this . mapping . refreshMapView ( preferredMapHeight , this . poiManager . poiList , null ) ; if ( ! this . mapDisplayed ) { this . mapDisplayed = true ; } } getIconForPOI ( poi ) { return Utils . getIconForPOI ( poi ) ; } getPOIByID ( poiID ) { const poiList = this . poiManager . poiList ; for ( let i = 0 ; i < poiList . length ; i ++ ) { if ( poiList [ i ] . ID === poiID ) { return poiList [ i ] ; } } return null ; } async refreshMapResults ( ) { if ( ! this . searchOnDemand ) { this . logging . log ( 'Skipping refresh, search on demand disabled..' , LogLevel . VERBOSE ) ; return ; } else { this . logging . log ( 'Refreshing Results..' , LogLevel . VERBOSE ) ; } /**
         * given a StartSearchPosition a lat/lng centered search will be performed, otherwise we perform a query based on the current map bounds
         */ const params = new POISearchParams ( ) ; let mapcentre = null ; if ( this . appManager . searchSettings . StartSearchPosition ) { mapcentre = new GeoPosition ( this . appManager . searchSettings . StartSearchPosition . latitude , this . appManager . searchSettings . StartSearchPosition . longitude ) ; this . appManager . searchSettings . StartSearchPosition = null ; } if ( mapcentre && mapcentre . coords . latitude == 0 && mapcentre . coords . longitude == 0 ) { this . logging . log ( 'Map center is zero. No query will be performed' , LogLevel . WARNING ) ; return ; } if ( mapcentre != null ) { params . latitude = mapcentre . coords . latitude ; params . longitude = mapcentre . coords . longitude ; params . distance = 25 ; params . boundingbox = null ; this . appManager . searchSettings . LastSearchPosition = new GeoLatLng ( mapcentre . coords . latitude , mapcentre . coords . longitude ) ; } else { params . latitude = null ; params . longitude = null ; let bounds = await this . mapping . getMapBounds ( ) . toPromise ( ) ; params . boundingbox = '(' + bounds [ 0 ] . latitude + ',' + bounds [ 0 ] . longitude + '),(' + bounds [ 1 ] . latitude + ',' + bounds [ 1 ] . longitude + ')' ; this . logging . log ( JSON . stringify ( bounds ) , LogLevel . VERBOSE ) ; } params . includeComments = true ; params . enableCaching = true ; if ( this . appManager . searchSettings != null ) { if ( this . appManager . searchSettings . ConnectionTypeList != null ) { params . connectionTypeIdList = this . appManager . searchSettings . ConnectionTypeList ; } if ( this . appManager . searchSettings . UsageTypeList != null ) { params . usageTypeIdList = this . appManager . searchSettings . UsageTypeList ; } if ( this . appManager . searchSettings . StatusTypeList != null ) { params . statusTypeIdList = this . appManager . searchSettings . StatusTypeList ; } if ( this . appManager . searchSettings . OperatorList != null ) { params . operatorIdList = this . appManager . searchSettings . OperatorList ; } if ( this . appManager . searchSettings . MinPowerKW != null && this . appManager . searchSettings . MinPowerKW > 0 ) { params . minPowerKW = this . appManager . searchSettings . MinPowerKW ; } if ( this . appManager . searchSettings . MaxPowerKW != null && this . appManager . searchSettings . MaxPowerKW > 0 ) { params . maxPowerKW = this . appManager . searchSettings . MaxPowerKW ; } if ( this . journeyManager . getRoutePolyline ( ) != null ) { params . polyline = this . journeyManager . getRoutePolyline ( ) ; params . boundingbox = null ; params . levelOfDetail = null ; params . latitude = null ; params . longitude = null ; } } this . appManager . isRequestInProgress = true ; let numResults = await this . poiManager . refreshPOIList ( params ) ; this . appManager . isRequestInProgress = false ; this . initialResultsShown = true ; if ( numResults >= params . maxResults ) { this . appManager . showToastNotification ( "A maximum of " + numResults + " results are returned per search. Zoom in for details." ) ; } } viewPOIDetails ( data , p ) { this . logging . log ( 'Viewing/fetching [' + this . poiViewMode + '] POI Details ' + data . poiId ) ; if ( p != null ) { this . mapping . unfocusMap ( ) ; this . modalController . create ( { component : POIDetailsPage , componentProps : { item : p } } ) . then ( m => { m . onDidDismiss ( ) . then ( ( ) => { this . logging . log ( 'Dismissing POI Details.' ) ; this . mapping . focusMap ( ) ; this . searchOnDemand = true ; } ) ; m . present ( ) ; } ) ; } else { this . poiManager . getPOIById ( data . poiId , true ) . then ( poi => { this . logging . log ( 'Got POI Details ' + poi . ID ) ; if ( this . poiViewMode === 'modal' ) { this . searchOnDemand = false ; this . modalController . create ( { component : POIDetailsPage , componentProps : { item : poi } } ) . then ( m => { m . onDidDismiss ( ) . then ( ( ) => { this . logging . log ( 'Dismissing POI Details.' ) ; this . mapping . focusMap ( ) ; this . searchOnDemand = true ; } ) ; m . present ( ) ; } ) ; this . mapping . unfocusMap ( ) ; } if ( this . poiViewMode === 'side' ) { this . selectedPOI = poi ; } } , ( err ) => { this . appManager . showToastNotification ( 'POI Details not available' ) ; } ) ; } } closePOIDetails ( ) { this . selectedPOI = null ; } openRoutePlannerModal ( ) { this . searchOnDemand = false ; this . mapping . unfocusMap ( ) ; this . modalController . create ( { component : RoutePlannerPage } ) . then ( m => { m . onDidDismiss ( ) . then ( ( data ) => { this . logging . log ( 'Dismissing Route Planner Details.' ) ; this . mapping . focusMap ( ) ; this . searchOnDemand = true ; } ) ; m . present ( ) ; } ) ; } openSearchOptions ( ) { this . searchOnDemand = false ; this . mapping . unfocusMap ( ) ; this . modalController . create ( { component : SettingsPage } ) . then ( m => { m . onDidDismiss ( ) . then ( ( data ) => { this . mapping . focusMap ( ) ; this . searchOnDemand = true ; } ) ; m . present ( ) ; } ) ; } openSideView ( ) { this . poiViewMode = 'side' ; this . mapping . updateMapSize ( ) ; } closeSideView ( ) { this . poiViewMode = 'modal' ; this . mapping . updateMapSize ( ) ; } planRoute ( ) { this . routePlanningMode = true ; } search ( ev ) { this . placeSearchMapPOI . getPlacesAutoComplete ( ev , 'poiSearch' ) ; } useFallbackPosition ( ) { let searchPos = this . defaultStartPos ; if ( this . appManager . searchSettings . LastSearchPosition != null ) { searchPos = this . appManager . searchSettings . LastSearchPosition ; } this . appManager . searchSettings . StartSearchPosition = searchPos ; this . appManager . searchSettings . LastSearchPosition = null ; this . searchOnDemand = true ; this . mapping . updateMapCentrePos ( searchPos . latitude , searchPos . longitude , true , this . defaultMapZoom ) ; } _watchId = null ; private async getPosition ( options = { } ) { return new Promise < GeolocationPosition > ( ( resolve , reject ) => { this . _watchId = Geolocation . watchPosition ( options , ( position , err ) => { Geolocation . clearWatch ( { id : this . _watchId } ) ; if ( err ) { reject ( err ) ; return ; } resolve ( position ) ; } ) ; } ) ; } async locateUser ( ) { try { this . logging . log ( 'Attempting to locate user..' ) ; const position = await this . getPosition ( ) ; if ( ! position ) { throw "Failed to get user location." ; } this . logging . log ( 'Got user location.' ) ; this . appManager . searchSettings . StartSearchPosition = new GeoLatLng ( position . coords . latitude , position . coords . longitude ) ; this . searchOnDemand = true ; this . mapping . updateMapCentrePos ( position . coords . latitude , position . coords . longitude , true , this . defaultMapZoom ) ; localStorage . setItem ( "_locationEnabled" , "true" ) ; return true ; } catch ( err ) { this . logging . log ( 'Failed to get user location. Searching using default or last position.' + err ) ; this . appManager . showToastNotification ( 'Your location could not be determined.' ) ; this . useFallbackPosition ( ) ; return false ; } } placeSelected ( place ) { this . searchKeyword = place . Address ; this . logging . log ( 'Got place details:' + place . Address ) ; this . mapping . focusMap ( ) ; this . mapping . updateMapCentrePos ( place . Location . latitude , place . Location . longitude , true ) ; this . debouncedRefreshMapResults ( ) ; } }