declare function require ( name ) ; import Kernel from '../Kernel' ; import Utils from '../Utils' ; import MathUtils from '../math/Utils' ; import Vertice from '../math/Vertice' ; import Vector from '../math/Vector' ; import MeshVertice from '../geometries/MeshVertice' ; import Triangle from '../geometries/Triangle' ; import Mesh from '../geometries/Mesh' ; import MeshColorGraphic from '../graphics/MeshColorGraphic' ; import ColorMaterial from '../materials/ColorMaterial' ; import MarkerTextureMaterial from '../materials/MarkerTextureMaterial' ; import Graphic from '../graphics/Graphic' ; import MultiPointsGraphic from '../graphics/MultiPointsGraphic' ; import GraphicGroup from '../GraphicGroup' ; import { Drawable } from '../Definitions.d' ; import Camera from '../Camera' ; import Service from '../Service' ; import Extent from '../Extent' ; import Program from '../Program' ; const startPointImageUrl = require ( '../images/start.png' ) ; const startPointImageSize = 80 ; const endPointImageUrl = require ( '../images/end.png' ) ; const endPointImageSize = 80 ; interface RoutePoint { lonlat : number [ ] ; vertice : Vertice ; start2EndVector : Vector ; v1 : MeshVertice ; v3 : MeshVertice ; } type RouteType = 'driving' | 'bus' | 'walking' ; class MeshRouteGraphic extends MeshColorGraphic { private readonly inflexionPointAngle = 30 ; constructor ( private originalLonlats , private pixelWidth , resolution , material ) { super ( null , material ) ; this . _removeDuplicatePoints ( this . originalLonlats ) ; this . updateGeometry ( resolution ) ; } updateGeometry ( resolution ) { var lonlats = this . _handleCurveJoin ( this . originalLonlats , resolution , this . pixelWidth ) ; const geometry = this . _getRouteGeometryByLonlats ( lonlats , resolution , this . pixelWidth ) ; this . setGeometry ( geometry ) ; } private _removeDuplicatePoints ( lonlats ) { let cursor = 0 ; while ( cursor <= ( lonlats . length - 2 ) ) { let currentLonlat = lonlats [ cursor ] ; let nextLonlat = lonlats [ cursor + 1 ] ; if ( currentLonlat [ 0 ] === nextLonlat [ 0 ] && currentLonlat [ 1 ] === nextLonlat [ 1 ] ) { lonlats . splice ( cursor + 1 , 1 ) ; } else { cursor += 1 ; } } } param _lonlats private _handleCurveJoin ( _lonlats , resolution , pixelWidth ) { var lonlats = [ ] . concat ( _lonlats ) ; var cursor = 1 ; const offsetLonlat = MathUtils . radianToDegree ( resolution * pixelWidth / Kernel . EARTH_RADIUS ) ; while ( cursor <= ( lonlats . length - 2 ) ) { const currentLonlat = lonlats [ cursor ] ; const prevLonlat = lonlats [ cursor - 1 ] ; const nextLonlat = lonlats [ cursor + 1 ] ; const currentPoint = new Vertice ( currentLonlat [ 0 ] , currentLonlat [ 1 ] , 0 ) ; const prevPoint = new Vertice ( prevLonlat [ 0 ] , prevLonlat [ 1 ] , 0 ) ; const nextPoint = new Vertice ( nextLonlat [ 0 ] , nextLonlat [ 1 ] , 0 ) ; const vector1 = Vector . verticeMinusVertice ( currentPoint , prevPoint ) ; const vector2 = Vector . verticeMinusVertice ( nextPoint , currentPoint ) ; const radian = Vector . getRadianOfTwoVectors ( vector1 , vector2 ) ; const angle = MathUtils . radianToDegree ( radian ) ; if ( angle > this . inflexionPointAngle ) { let p0 = null ; let p1 = currentLonlat ; let p2 = null ; let length1 = vector1 . getLength ( ) ; let length2 = vector2 . getLength ( ) ; let minLength = Math . min ( length1 , length2 ) ; let deltaLength = Math . min ( offsetLonlat , minLength / 2 ) ; let p0Vertice = Vector . verticePlusVector ( currentPoint , vector1 . getOpposite ( ) . setLength ( deltaLength ) ) ; p0 = [ p0Vertice . x , p0Vertice . y ] ; let p2Vertice = Vector . verticePlusVector ( currentPoint , vector2 . clone ( ) . setLength ( deltaLength ) ) ; p2 = [ p2Vertice . x , p2Vertice . y ] ; const curveLonlats = MathUtils . quad ( p0 , p1 , p2 , 10 ) ; lonlats . splice ( cursor , 1 , ... curveLonlats ) ; cursor += curveLonlats . length ; } else { cursor ++ ; } } return lonlats ; } private _getRouteGeometryByLonlats ( lonlats , resolution , pixelWidth ) { const mesh = new Mesh ( ) ; const points = lonlats . map ( ( lonlat ) => { return { lonlat : lonlat , vertice : MathUtils . geographicToCartesianCoord ( lonlat [ 0 ] , lonlat [ 1 ] ) , start2EndVector : null , v1 : null , v3 : null } as RoutePoint ; } ) ; points . forEach ( ( startPoint , index ) => { if ( index !== points . length - 1 ) { const endPoint = points [ index + 1 ] ; startPoint . start2EndVector = Vector . verticeMinusVertice ( endPoint . vertice , startPoint . vertice ) ; } else { const prevPoint = points [ index - 1 ] ; startPoint . start2EndVector = prevPoint . start2EndVector ; } } ) ; let startIndex = 0 ; points . forEach ( ( point , index ) => { const result = this . _getRouteVertices ( startIndex , point . vertice , point . start2EndVector , resolution , pixelWidth ) ; startIndex = result . startIndex ; point . v1 = result . v1 ; point . v3 = result . v3 ; mesh . vertices . push ( point . v1 , point . v3 ) ; if ( index !== 0 ) { const prevPoint = points [ index - 1 ] ; const v1 = prevPoint . v1 ; const v3 = prevPoint . v3 ; const v0 = point . v1 ; const v2 = point . v3 ; const triangles = Mesh . buildPlane ( v0 , v1 , v2 , v3 ) ; mesh . triangles . push ( ... triangles ) ; } } ) ; return mesh ; } private _getRouteVertices ( startIndex , startVertice , start2EndVector , resolution , pixelWidth ) { const origin2StartVector = Vector . fromVertice ( startVertice ) ; const end2StartVector = start2EndVector . getOpposite ( ) ; const offset = resolution * pixelWidth / 2 ; const start2LeftBottomVector = origin2StartVector . cross ( start2EndVector ) . setLength ( offset ) ; const p1 = Vector . verticePlusVector ( startVertice , start2LeftBottomVector ) . getArray ( ) ; const v1 = new MeshVertice ( { i : startIndex ++ , p : p1 } ) ; const start2RightBottomVector = origin2StartVector . cross ( end2StartVector ) . setLength ( offset ) ; const p3 = Vector . verticePlusVector ( startVertice , start2RightBottomVector ) . getArray ( ) ; const v3 = new MeshVertice ( { i : startIndex ++ , p : p3 } ) ; return { startIndex : startIndex , v1 : v1 , v3 : v3 } ; } } export default class RouteLayer extends GraphicGroup < Drawable > { private pixelWidth : number = 5 ; private busColor : number [ ] = [ 82 , 153 , 255 ] ; private drivingColor : number [ ] = [ 0 , 189 , 0 ] ; private walkingColor : number [ ] = [ 76 , 221 , 38 ] ; private startLonlat : number [ ] = null ; private endLonlat : number [ ] = null ; private route : any = null ; private deltaLonlatSquareThreshold : number = Math . pow ( 1 / ( 2 * Math . PI * Kernel . REAL_EARTH_RADIUS ) * 360 , 2 ) ; private constructor ( private camera , private key ) { super ( ) ; Utils . subscribe ( 'level-change' , ( ) => { if ( this . children . length > 0 ) { const resolution = this . camera . getResolution ( ) ; this . children . forEach ( ( graphic ) => { if ( graphic instanceof MeshRouteGraphic ) { graphic . updateGeometry ( resolution ) ; } } ) ; } } ) ; } test ( pixelWidth = 5 , segments = 100 , rgb = [ 0 , 255 , 0 ] ) { const resolution = this . camera . getResolution ( ) ; this . _addRouteByLonlats ( [ [ 90 , 0 ] , [ 120 , 0 ] , [ 120 , 40 ] ] , resolution , this . pixelWidth , rgb ) ; } private _addRouteByLonlat ( startLonLat , endLonLat , resolution , pixelWidth , segments , rgb ) { const lonlats = this . _getLonlatsBySegments ( startLonLat , endLonLat , segments ) ; return this . _addRouteByLonlats ( lonlats , resolution , pixelWidth , rgb ) ; } private _addRouteByLonlats ( lonlats , resolution , pixelWidth , rgb ) { var graphic = null ; if ( lonlats . length >= 2 ) { let validLonlats = lonlats ; if ( validLonlats . length >= 2 ) { graphic = new MeshRouteGraphic ( validLonlats , pixelWidth , resolution , new ColorMaterial ( rgb ) ) ; this . add ( graphic ) ; } } return graphic ; } private _getLonlatsBySegments ( startLonLat , endLonLat , segments ) { const deltaLon = ( endLonLat [ 0 ] - startLonLat [ 0 ] ) / segments ; const deltaLat = ( endLonLat [ 1 ] - startLonLat [ 1 ] ) / segments ; const lonlats = [ ] ; for ( let i = 0 ; i < segments ; i ++ ) { let lonlat = [ startLonLat [ 0 ] + deltaLon * i , startLonLat [ 1 ] + deltaLat * i ] ; lonlats . push ( lonlat ) ; } lonlats . push ( endLonLat ) ; return lonlats ; } showPath ( pathIndex ) { if ( this . route ) { if ( this . route . type === 'driving' ) { this . _showDrivingPath ( pathIndex ) ; } else if ( this . route . type === 'bus' ) { this . _showBusPath ( pathIndex ) ; } else if ( this . route . type === 'walking' ) { this . _showWalkingPath ( pathIndex ) ; } } } routeByDriving ( fromLon , fromLat , toLon , toLat , strategy = 5 ) { return Service . routeByDriving ( fromLon , fromLat , toLon , toLat , this . key , strategy ) . then ( ( response ) => { this . _clearAll ( ) ; if ( response . route && response . route . paths && response . route . paths . length > 0 ) { this . startLonlat = [ fromLon , fromLat ] ; this . endLonlat = [ toLon , toLat ] ; this . route = response . route ; this . showPath ( 0 ) ; } return response ; } ) ; } private _showDrivingPath ( pathIndex ) { if ( this . route && this . route . paths && this . route . paths . length > 0 ) { const path = this . route . paths [ pathIndex ] ; if ( path && path . steps && path . steps . length > 0 ) { this . clear ( ) ; const lonlats = [ ] ; path . steps . forEach ( ( step , index , steps ) => { lonlats . push ( ... step . lonlats ) ; } ) ; const extent = Extent . fromLonlats ( lonlats ) ; if ( extent ) { this . camera . setExtent ( extent ) ; setTimeout ( ( ) => { const resolution = this . camera . getResolution ( ) ; this . _addRouteByLonlats ( lonlats , resolution , this . pixelWidth , this . drivingColor ) ; this . _showStartEndPoints ( ) ; } , 0 ) ; } } } } routeByBus ( fromLon , fromLat , toLon , toLat , startCity , endCity , strategy = 0 ) { return Service . routeByBus ( fromLon , fromLat , toLon , toLat , startCity , endCity , this . key , strategy ) . then ( ( response ) => { this . _clearAll ( ) ; if ( response . route && response . route . transits && response . route . transits . length > 0 ) { this . startLonlat = [ fromLon , fromLat ] ; this . endLonlat = [ toLon , toLat ] ; this . route = response . route ; this . showPath ( 0 ) ; } return response ; } ) ; } private _showBusPath ( pathIndex ) { if ( this . route && this . route . transits && this . route . transits . length > 0 ) { const transit = this . route . transits [ pathIndex ] ; if ( transit && transit . segments && transit . segments . length > 0 ) { this . clear ( ) ; const lonlats = [ ] ; const lonlatsSegments = [ ] ; transit . segments . forEach ( ( segment ) => { if ( segment . walking && segment . walking . lonlats && segment . walking . lonlats . length > 0 ) { segment . walking . lonlats . color = this . walkingColor ; lonlatsSegments . push ( segment . walking . lonlats ) ; lonlats . push ( ... segment . walking . lonlats ) ; } if ( segment . bus && segment . bus . lonlats && segment . bus . lonlats . length > 0 ) { segment . bus . lonlats . color = this . busColor ; lonlatsSegments . push ( segment . bus . lonlats ) ; lonlats . push ( ... segment . bus . lonlats ) ; } if ( segment . railway && segment . railway . lonlats && segment . railway . lonlats . length > 0 ) { segment . railway . lonlats . color = this . busColor ; lonlatsSegments . push ( segment . railway . lonlats ) ; lonlats . push ( ... segment . railway . lonlats ) ; } } ) ; const extent = Extent . fromLonlats ( lonlats ) ; if ( extent ) { this . camera . setExtent ( extent ) ; setTimeout ( ( ) => { const resolution = this . camera . getResolution ( ) ; lonlatsSegments . forEach ( ( lonlats ) => { this . _addRouteByLonlats ( lonlats , resolution , this . pixelWidth , ( lonlats as any ) . color ) ; } ) ; this . _showStartEndPoints ( ) ; } , 0 ) ; } } } } routeByWalking ( fromLon , fromLat , toLon , toLat ) { return Service . routeByWalking ( fromLon , fromLat , toLon , toLat , this . key ) . then ( ( response ) => { this . _clearAll ( ) ; if ( response . route && response . route . paths && response . route . paths . length > 0 ) { this . startLonlat = [ fromLon , fromLat ] ; this . endLonlat = [ toLon , toLat ] ; this . route = response . route ; this . _showWalkingPath ( 0 ) ; } return response ; } ) ; } private _showWalkingPath ( pathIndex ) { if ( this . route && this . route . paths && this . route . paths . length > 0 ) { const path = this . route . paths [ pathIndex ] ; if ( path && path . steps && path . steps . length > 0 ) { this . clear ( ) ; const lonlats = [ ] ; path . steps . forEach ( ( step ) => { lonlats . push ( ... step . lonlats ) ; } ) ; const extent = Extent . fromLonlats ( lonlats ) ; if ( extent ) { this . camera . setExtent ( extent ) ; setTimeout ( ( ) => { const resolution = this . camera . getResolution ( ) ; this . _addRouteByLonlats ( lonlats , resolution , this . pixelWidth , this . walkingColor ) ; this . _showStartEndPoints ( ) ; } , 0 ) ; } } } } private _showStartEndPoints ( ) { if ( this . startLonlat ) { let material = new MarkerTextureMaterial ( startPointImageUrl , startPointImageSize ) ; let startPointGraphic = new MultiPointsGraphic ( material ) ; startPointGraphic . setLonlats ( [ this . startLonlat ] ) ; this . add ( startPointGraphic ) ; } if ( this . endLonlat ) { let material = new MarkerTextureMaterial ( endPointImageUrl , endPointImageSize ) ; let endPointGraphic = new MultiPointsGraphic ( material ) ; endPointGraphic . setLonlats ( [ this . endLonlat ] ) ; this . add ( endPointGraphic ) ; } } private _clearAll ( ) { this . startLonlat = null ; this . endLonlat = null ; this . route = null ; this . clear ( ) ; } protected onDraw ( camera ) { const gl = Kernel . gl ; gl . disable ( Kernel . gl . DEPTH_TEST ) ; gl . depthMask ( false ) ; super . onDraw ( camera ) ; gl . enable ( Kernel . gl . DEPTH_TEST ) ; gl . depthMask ( true ) ; } destroy ( ) { this . camera = null ; super . destroy ( ) ; } test2 ( str ) { const splits = str . split ( ";" ) ; const splits1 = splits [ 0 ] . split ( "," ) ; const splits2 = splits [ 1 ] . split ( "," ) ; var lon1 = parseFloat ( splits1 [ 0 ] ) ; var lat1 = parseFloat ( splits1 [ 1 ] ) ; var lon2 = parseFloat ( splits2 [ 0 ] ) ; var lat2 = parseFloat ( splits2 [ 1 ] ) ; var s = Math . pow ( ( lon1 - lon2 ) , 2 ) + Math . pow ( ( lat1 - lat2 ) , 2 ) ; const distance = MathUtils . getRealArcDistanceBetweenLonLats ( lon1 , lat1 , lon2 , lat2 ) ; return { lon1 : lon1 , lat1 : lat1 , lon2 : lon2 , lat2 : lat2 , s : s , distance : distance } ; } static getInstance ( camera , key ) { return new RouteLayer ( camera , key ) ; } } ;