declare const Promise ; import Kernel from './Kernel' ; import Utils from './Utils' ; import { EventEmitter } from './Events' ; import MathUtils from './math/Utils' ; import Vertice from './math/Vertice' ; import Vector from './math/Vector' ; import Line from './math/Line' ; import Plan from './math/Plan' ; import TileGrid , { TileGridPosition } from './TileGrid' ; import Matrix from './math/Matrix' ; import Object3D from './Object3D' ; import Extent from './Extent' ; import { devicePixelRatio } from './Env' ; export class CameraCore { constructor ( private fov , private aspect , private near , private far , private floatLevel , private matrix ) { } getFov ( ) { return this . fov ; } getAspect ( ) { return this . aspect ; } getNear ( ) { return this . near ; } getFar ( ) { return this . far ; } getFloatLevel ( ) { return this . floatLevel ; } getMatrix ( ) { return this . matrix ; } equals ( other ) { if ( ! other ) { return false ; } return this . fov === other . getFov ( ) && this . aspect === other . getAspect ( ) && this . near === other . getNear ( ) && this . far === other . getFar ( ) && this . floatLevel === other . getFloatLevel ( ) && this . matrix . equals ( other . getMatrix ( ) ) ; } } const realResolutionCache = { } ; ( function ( ) { for ( var i = 0 ; i <= Kernel . MAX_LEVEL ; i ++ ) { realResolutionCache [ i ] = Kernel . MAX_REAL_RESOLUTION / Math . pow ( 2 , i ) ; } } ) ( ) ; class Camera extends Object3D { private readonly initFov : number ; private readonly animationDuration : number = 200 ; private readonly nearFactor : number = 0.6 ; private readonly maxPitch : number = 40 ; private resolutionFactor1 : number ; private resolutionFactor2 : number ; private isZeroPitch : boolean = true ; private level : number = - 1 ; private floatLevel : number = - 2 ; private lastFloatLevel : number = - 3 ; private lastMatrix : Matrix ; private lastFov : number = - 1 ; private lastAspect : number = - 1 ; private lastNear : number = - 1 ; private lastFar : number = - 1 ; private viewMatrix : Matrix ; private projMatrix : Matrix ; private projViewMatrix : Matrix ; private matrixForDraw : Matrix ; private viewMatrixForDraw : Matrix ; private projMatrixForDraw : Matrix ; private projViewMatrixForDraw : Matrix ; private lonlatsOfBoundary : number [ ] [ ] = null ; private animating : boolean = false ; private eventEmitter : EventEmitter = null ; constructor ( private width , private height , private fov = 45 , private aspect = 1 , private near = 1 , private far = 100 , level = 3 , lonlat = [ 0 , 0 ] , resolutionFactor = Math . pow ( 2 , 0.3752950 ) ) { super ( ) ; if ( ! ( resolutionFactor > 0 ) ) { resolutionFactor = Math . pow ( 2 , 0.3752950 ) ; } this . resolutionFactor1 = resolutionFactor ; this . resolutionFactor2 = this . resolutionFactor1 * 2 ; this . eventEmitter = new EventEmitter ( ) ; this . lonlatsOfBoundary = [ ] ; this . initFov = this . fov ; this . lastMatrix = new Matrix ( ) ; this . lastMatrix . setUniqueValue ( 0 ) ; this . projMatrix = new Matrix ( ) ; this . _rawSetPerspectiveMatrix ( this . fov , this . aspect , this . near , this . far ) ; this . _initCameraPosition ( level , lonlat [ 0 ] , lonlat [ 1 ] ) ; this . update ( true ) ; } getExtent ( ) { var extent = null ; if ( this . isEarthFullOverlapScreen ( ) ) { var lons = [ ] ; var lats = [ ] ; this . lonlatsOfBoundary . forEach ( function ( lonlat ) { lons . push ( lonlat [ 0 ] ) ; lats . push ( lonlat [ 1 ] ) ; } ) ; var minLon = Math . min ( ... lons ) ; var maxLon = Math . max ( ... lons ) ; var minLat = Math . min ( ... lats ) ; var maxLat = Math . max ( ... lats ) ; extent = new Extent ( minLon , minLat , maxLon , maxLat ) ; } return extent ; } getEventEmitter ( ) { return this . eventEmitter ; } isEarthFullOverlapScreen ( ) { return this . lonlatsOfBoundary . length === 8 ; } getTileGridsOfBoundary ( level , filterRepeat ) { var tileGridsOfBoundary = this . lonlatsOfBoundary . map ( ( lonlat ) => { return TileGrid . getTileGridByGeo ( lonlat [ 0 ] , lonlat [ 1 ] , level ) ; } ) ; return filterRepeat ? Utils . filterRepeatArray ( tileGridsOfBoundary ) : tileGridsOfBoundary ; } toJson ( ) { function matrixToJson ( mat ) { return mat ? mat . toJson ( ) : null ; } var json = { matrix : matrixToJson ( this . matrix ) , isZeroPitch : this . isZeroPitch , level : this . level , floatLevel : this . floatLevel , lastFloatLevel : this . lastFloatLevel , lastMatrix : matrixToJson ( this . lastMatrix ) , lastFov : this . lastFov , lastAspect : this . lastAspect , lastNear : this . lastNear , lastFar : this . lastFar , viewMatrix : matrixToJson ( this . viewMatrix ) , projMatrix : matrixToJson ( this . projMatrix ) , projViewMatrix : matrixToJson ( this . projViewMatrix ) , matrixForDraw : matrixToJson ( this . matrixForDraw ) , viewMatrixForDraw : matrixToJson ( this . viewMatrixForDraw ) , projMatrixForDraw : matrixToJson ( this . projMatrixForDraw ) , projViewMatrixForDraw : matrixToJson ( this . projViewMatrixForDraw ) , animating : this . animating } ; return json ; } toJsonString ( ) { return JSON . stringify ( this . toJson ( ) ) ; } fromJson ( json ) { this . matrix = Matrix . fromJson ( json . matrix ) ; this . isZeroPitch = json . isZeroPitch ; this . level = json . level ; this . floatLevel = json . floatLevel ; this . lastFloatLevel = json . lastFloatLevel ; this . lastMatrix = Matrix . fromJson ( json . lastMatrix ) ; this . lastFov = json . lastFov ; this . lastAspect = json . lastAspect ; this . lastNear = json . lastNear ; this . lastFar = json . lastFar ; this . viewMatrix = Matrix . fromJson ( json . viewMatrix ) ; this . projMatrix = Matrix . fromJson ( json . projMatrix ) ; this . projViewMatrix = Matrix . fromJson ( json . projViewMatrix ) ; this . matrixForDraw = Matrix . fromJson ( json . matrixForDraw ) ; this . viewMatrixForDraw = Matrix . fromJson ( json . viewMatrixForDraw ) ; this . projMatrixForDraw = Matrix . fromJson ( json . projMatrixForDraw ) ; this . projViewMatrixForDraw = Matrix . fromJson ( json . projViewMatrixForDraw ) ; this . animating = json . animating ; this . update ( true ) ; } fromJsonString ( jsonStr ) { this . fromJson ( JSON . parse ( jsonStr ) ) ; } private _setPerspectiveMatrix ( fov , aspect , near , far ) { this . _rawSetPerspectiveMatrix ( fov , aspect , near , far ) ; this . _updateFar ( ) ; } private _rawSetPerspectiveMatrix ( fov , aspect , near , far , projMatrix = this . projMatrix ) { if ( this . projMatrix === projMatrix ) { this . fov = fov ; this . aspect = aspect ; this . near = near ; this . far = far ; } var mat = [ 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 ] ; var halfFov = fov * Math . PI / 180 / 2 ; var f = 1 / Math . tan ( halfFov ) ; var nf = 1 / ( near - far ) ; mat [ 0 ] = f / aspect ; mat [ 5 ] = f ; mat [ 10 ] = ( far + near ) * nf ; mat [ 11 ] = - 1 ; mat [ 14 ] = 2 * near * far * nf ; mat [ 15 ] = 0 ; projMatrix . setElements ( mat [ 0 ] , mat [ 1 ] , mat [ 2 ] , mat [ 3 ] , mat [ 4 ] , mat [ 5 ] , mat [ 6 ] , mat [ 7 ] , mat [ 8 ] , mat [ 9 ] , mat [ 10 ] , mat [ 11 ] , mat [ 12 ] , mat [ 13 ] , mat [ 14 ] , mat [ 15 ] ) ; } private _setFov ( fov ) { if ( ! ( fov > 0 ) ) { throw "invalid fov:" + fov ; } this . _setPerspectiveMatrix ( fov , this . aspect , this . near , this . far ) ; } setSize ( width , height ) { this . width = width ; this . height = height ; this . setAspect ( this . width / this . height ) ; } private setAspect ( aspect ) { if ( ! ( aspect > 0 ) ) { throw "invalid aspect:" + aspect ; } this . _setPerspectiveMatrix ( this . fov , aspect , this . near , this . far ) ; this . setLevel ( this . level , true ) ; } private _updateFar ( ) { } private _getMinimalFar ( cameraPosition ) { var distance2EarthOrigin = Vector . fromVertice ( cameraPosition ) . getLength ( ) ; var far = Math . sqrt ( distance2EarthOrigin * distance2EarthOrigin - Kernel . EARTH_RADIUS * Kernel . EARTH_RADIUS ) ; far *= 1.05 ; return far ; } update ( force = false ) { var shouldUpdate = this . _updateCore ( force ) ; if ( shouldUpdate ) { this . _updateTileGridsOfBoundary ( ) ; } return shouldUpdate ; } private _updateCore ( force = false ) { var shouldUpdate = force || this . _isNeedUpdate ( ) ; if ( shouldUpdate ) { this . _normalUpdate ( ) ; this . _updateProjViewMatrixForDraw ( ) ; } this . lastFov = this . fov ; this . lastAspect = this . aspect ; this . lastNear = this . near ; this . lastFar = this . far ; this . lastFloatLevel = this . floatLevel ; this . lastMatrix . setMatrixByOther ( this . matrix ) ; return shouldUpdate ; } private _updateTileGridsOfBoundary ( ) { var lonlatsOfBoundary = [ ] ; var ndcs = [ [ - 1 , 1 ] , [ - 1 , 0 ] , [ - 1 , - 1 ] , [ 1 , 1 ] , [ 1 , 0 ] , [ 1 , - 1 ] , [ 0 , 1 ] , [ 0 , - 1 ] ] ; ndcs . forEach ( ( ndcXY ) => { var lonlat = this . _getPickLonLatByNDC ( ndcXY [ 0 ] , ndcXY [ 1 ] ) ; if ( lonlat && lonlat . length > 0 ) { lonlatsOfBoundary . push ( lonlat ) ; } } ) ; this . lonlatsOfBoundary = lonlatsOfBoundary ; } getCameraCore ( ) { return new CameraCore ( this . fov , this . aspect , this . near , this . far , this . floatLevel , this . matrix . clone ( ) ) ; } private _isNeedUpdate ( ) { return ( this . fov !== this . lastFov ) || ( this . aspect !== this . lastAspect ) || ( this . near !== this . lastNear ) || ( this . far !== this . lastFar ) || ( this . floatLevel !== this . lastFloatLevel ) || ( ! this . matrix . equals ( this . lastMatrix ) ) ; } getProjViewMatrixForDraw ( ) { return this . projViewMatrixForDraw ; } _normalUpdate ( ) { this . viewMatrix = this . matrix . getInverseMatrix ( ) ; this . _updateFar ( ) ; this . projViewMatrix = this . projMatrix . multiplyMatrix ( this . viewMatrix ) ; } _updateProjViewMatrixForDraw ( ) { this . matrixForDraw = this . matrix . clone ( ) ; var newFov = this . _updatePositionAndFov ( this . matrixForDraw ) ; var aspect = this . aspect ; var near = this . near ; var newFar = this . far ; this . projMatrixForDraw = new Matrix ( ) ; this . _rawSetPerspectiveMatrix ( newFov , aspect , near , newFar , this . projMatrixForDraw ) ; this . viewMatrixForDraw = this . matrixForDraw . getInverseMatrix ( ) ; this . projViewMatrixForDraw = this . projMatrixForDraw . multiplyMatrix ( this . viewMatrixForDraw ) ; } private _updatePositionAndFov ( cameraMatrix ) { const currentLevel = this . animating ? this . floatLevel : this . level ; var safeLevel = this . _getSafeThresholdLevelForNear ( ) ; if ( currentLevel > safeLevel ) { this . _updatePositionByLevel ( safeLevel , cameraMatrix ) ; var deltaLevel = currentLevel - safeLevel ; var newFov = this . _calculateFovByDeltaLevel ( this . initFov , deltaLevel ) ; return newFov ; } else { this . _updatePositionByLevel ( currentLevel , cameraMatrix ) ; return this . initFov ; } } private _getSafeThresholdLevelForNear ( ) { var thresholdNear = this . near * this . nearFactor ; var result = this . _calculateResolutionAndBestDisplayLevelByDistance2EarthSurface ( thresholdNear ) ; var level = result [ 1 ] ; return level ; } private _calculateDeltaLevelByFov ( oldFov , newFov ) { var radianOldFov = MathUtils . degreeToRadian ( oldFov ) ; var halfRadianOldFov = radianOldFov / 2 ; var tanOld = Math . tan ( halfRadianOldFov ) ; var radianNewFov = MathUtils . degreeToRadian ( newFov ) ; var halfRadianNewFov = radianNewFov / 2 ; var tanNew = Math . tan ( halfRadianNewFov ) ; var deltaLevel = MathUtils . log2 ( tanOld / tanNew ) ; return deltaLevel ; } private _calculateFovByDeltaLevel ( oldFov , deltaLevel ) { var radianOldFov = MathUtils . degreeToRadian ( oldFov ) ; var halfRadianOldFov = radianOldFov / 2 ; var tanOld = Math . tan ( halfRadianOldFov ) ; var tanNew = tanOld / Math . pow ( 2 , deltaLevel ) ; var halfRadianNewFov = Math . atan ( tanNew ) ; var radianNewFov = halfRadianNewFov * 2 ; var newFov = MathUtils . radianToDegree ( radianNewFov ) ; return newFov ; } getResolution ( ) { const { resolutionX , bestDisplayLevelFloatX , resolutionY , bestDisplayLevelFloatY } = this . measureXYResolutionAndBestDisplayLevel ( ) ; return ( resolutionX + resolutionY ) / 2 / this . resolutionFactor1 ; } getResolutionInWorld ( ) { return this . getResolution ( ) / Kernel . SCALE_FACTOR ; } private getResolutionInWorld2 ( ) { if ( realResolutionCache . hasOwnProperty ( this . level ) ) { return realResolutionCache [ this . level ] ; } else { return Kernel . MAX_REAL_RESOLUTION / Math . pow ( 2 , this . level ) ; } } private measureXYResolutionAndBestDisplayLevel ( ) { var p = this . matrix . getPosition ( ) ; var dir = Vector . fromVertice ( p ) ; var line = new Line ( p , dir ) ; var pickResult1 = this . _getPickCartesianCoordInEarthByLine ( line ) ; var p1 = pickResult1 [ 0 ] ; var ndc1 = this . _convertVerticeFromWorldToNDC ( p1 ) ; var canvasXY1 = MathUtils . convertPointFromNdcToCanvas ( this . width , this . height , ndc1 . x , ndc1 . y ) ; var centerX = canvasXY1 [ 0 ] ; var centerY = canvasXY1 [ 1 ] ; var offsetPixel = 10 ; var leftPickResult = this . getPickCartesianCoordInEarthByCanvas ( centerX - offsetPixel , centerY ) ; var vLeft = Vector . fromVertice ( leftPickResult [ 0 ] ) ; var rightPickResult = this . getPickCartesianCoordInEarthByCanvas ( centerX + offsetPixel , centerY ) ; var vRight = Vector . fromVertice ( rightPickResult [ 0 ] ) ; var α = Vector . getRadianOfTwoVectors ( vLeft , vRight ) ; var resolutionX = α * Kernel . EARTH_RADIUS / ( 2 * offsetPixel ) * this . resolutionFactor1 ; var bestDisplayLevelFloatX = this . _calculateLevelByResolution ( resolutionX ) ; var topPickResult = this . getPickCartesianCoordInEarthByCanvas ( centerX , centerY + offsetPixel ) ; var vTop = Vector . fromVertice ( topPickResult [ 0 ] ) ; var bottomPickResult = this . getPickCartesianCoordInEarthByCanvas ( centerX , centerY - offsetPixel ) ; var vBottom = Vector . fromVertice ( bottomPickResult [ 0 ] ) ; var β = Vector . getRadianOfTwoVectors ( vTop , vBottom ) ; var resolutionY = β * Kernel . EARTH_RADIUS / ( 2 * offsetPixel ) * this . resolutionFactor1 ; var bestDisplayLevelFloatY = this . _calculateLevelByResolution ( resolutionY ) ; return { resolutionX : resolutionX , bestDisplayLevelFloatX : bestDisplayLevelFloatX , resolutionY : resolutionY , bestDisplayLevelFloatY : bestDisplayLevelFloatY } ; } private calculateCurrentResolutionAndBestDisplayLevel ( ) { var distance2EarthOrigin = this . getDistance2EarthOrigin ( ) ; return this . _calculateResolutionAndBestDisplayLevelByDistance2EarthOrigin ( distance2EarthOrigin ) ; } private _calculateResolutionAndBestDisplayLevelByDistance2EarthOrigin ( distance2EarthOrigin ) { var α2 = MathUtils . degreeToRadian ( this . fov / 2 ) ; var α1 = Math . atan ( 2 / this . height * Math . tan ( α2 ) ) ; var δ = Math . asin ( distance2EarthOrigin * Math . sin ( α1 ) / Kernel . EARTH_RADIUS ) ; var β = δ - α1 ; var resolution = β * Kernel . EARTH_RADIUS * this . resolutionFactor2 ; var bestDisplayLevelFloat = this . _calculateLevelByResolution ( resolution ) ; return [ resolution , bestDisplayLevelFloat ] ; } private _calculateResolutionAndBestDisplayLevelByDistance2EarthSurface ( distance2EarthSurface ) { var distance2EarthOrigin = distance2EarthSurface + Kernel . EARTH_RADIUS ; return this . _calculateResolutionAndBestDisplayLevelByDistance2EarthOrigin ( distance2EarthOrigin ) ; } private _calculateDistance2EarthSurfaceByBestDisplayLevel ( level ) { return this . _calculateDistance2EarthOriginByBestDisplayLevel ( level ) - Kernel . EARTH_RADIUS ; } private _calculateDistance2EarthOriginByBestDisplayLevel ( level ) { var resolution = this . _calculateResolutionByLevel ( level ) ; return this . _calculateDistance2EarthOriginByResolution ( resolution ) ; } private _calculateDistance2EarthOriginByResolution ( resolution ) { resolution /= this . resolutionFactor2 ; var α2 = MathUtils . degreeToRadian ( this . fov / 2 ) ; var α1 = Math . atan ( 2 / this . height * Math . tan ( α2 ) ) ; var β = resolution / Kernel . EARTH_RADIUS ; var δ = α1 + β ; var distance2EarthOrigin = Kernel . EARTH_RADIUS * Math . sin ( δ ) / Math . sin ( α1 ) ; return distance2EarthOrigin ; } private _calculateLevelByResolution ( resolution ) { var pow2value = Kernel . MAX_RESOLUTION / resolution ; var bestDisplayLevelFloat = MathUtils . log2 ( pow2value ) ; return bestDisplayLevelFloat ; } private _calculateResolutionByLevel ( level ) { return Kernel . MAX_RESOLUTION / Math . pow ( 2 , level ) ; } getVertice ( ) { const origin2PositionVector = Vector . fromVertice ( this . getPosition ( ) ) ; origin2PositionVector . setLength ( Kernel . EARTH_RADIUS ) ; const p = origin2PositionVector . getVertice ( ) ; return p ; } getLonlat ( ) { const p = this . getVertice ( ) ; const lonlat = MathUtils . cartesianCoordToGeographic ( p ) ; return lonlat ; } getLevel ( ) { return this . level ; } setLevel ( level , force = false ) { if ( ! ( Utils . isNonNegativeInteger ( level ) ) ) { throw "invalid level:" + level ; } if ( level < Kernel . MIN_LEVEL ) { level = Kernel . MIN_LEVEL ; } if ( level > Kernel . MAX_LEVEL ) { level = Kernel . MAX_LEVEL ; } const oldLevel = this . level ; const levelChanged = level !== this . level ; if ( levelChanged || force ) { this . _updatePositionByLevel ( level , this . matrix ) ; this . level = level ; this . floatLevel = level ; } if ( levelChanged ) { Utils . publish ( 'level-change' , { oldLevel : oldLevel , newLevel : this . level } ) ; } } private _initCameraPosition ( level , lon , lat ) { var initDistanceToOrigin = this . _calculateDistance2EarthOriginByBestDisplayLevel ( level ) ; var initPosition = MathUtils . geographicToCartesianCoord ( lon , lat , initDistanceToOrigin ) ; var origin = new Vertice ( 0 , 0 , 0 ) ; var vector = this . getLightDirection ( ) . getOpposite ( ) ; vector . setLength ( initDistanceToOrigin ) ; this . _look ( initPosition , origin ) ; this . setLevel ( level ) ; } private _updatePositionByLevel ( level , cameraMatrix ) { var intersects = this . _getDirectionIntersectPointWithEarth ( cameraMatrix ) ; if ( intersects . length === 0 ) { throw "no intersect" ; } var intersect = intersects [ 0 ] ; var theoryDistance2Interscet = this . _calculateDistance2EarthSurfaceByBestDisplayLevel ( level ) ; var vector = cameraMatrix . getVectorZ ( ) ; vector . setLength ( theoryDistance2Interscet ) ; var newCameraPosition = Vector . verticePlusVector ( intersect , vector ) ; cameraMatrix . setPosition ( newCameraPosition ) ; } setDeltaPitch ( deltaPitch ) { if ( this . level < Kernel . MIN_PITCH_LEVEL || ! this . isEarthFullOverlapScreen ( ) ) { return ; } var currentPitch = this . getPitch ( ) ; var newPitch = currentPitch + deltaPitch ; if ( newPitch > this . maxPitch ) { return ; } if ( newPitch < 0 ) { newPitch = 0 ; } deltaPitch = newPitch - currentPitch ; if ( deltaPitch === 0 ) { return ; } var intersects = this . _getDirectionIntersectPointWithEarth ( this . matrix ) ; if ( intersects . length === 0 ) { throw "no intersects" ; } var intersect = intersects [ 0 ] ; var deltaRadian = MathUtils . degreeToRadian ( deltaPitch ) ; var matrix = this . matrix . clone ( ) ; matrix . setPosition ( intersect ) ; matrix . localRotateX ( deltaRadian ) ; this . _updatePositionByLevel ( this . level , matrix ) ; this . isZeroPitch = newPitch === 0 ; this . matrix = matrix ; } getPitch ( ) { if ( this . isZeroPitch ) { return 0 ; } var intersects = this . _getDirectionIntersectPointWithEarth ( this . matrix ) ; if ( intersects . length === 0 ) { throw "no intersects" ; } var intersect = intersects [ 0 ] ; var vectorOrigin2Intersect = Vector . fromVertice ( intersect ) ; var length1 = vectorOrigin2Intersect . getLength ( ) ; var vectorIntersect2Camera = Vector . verticeMinusVertice ( this . getPosition ( ) , intersect ) ; var length2 = vectorIntersect2Camera . getLength ( ) ; var cosθ = vectorOrigin2Intersect . dot ( vectorIntersect2Camera ) / ( length1 * length2 ) ; var radian = MathUtils . acosSafely ( cosθ ) ; var crossVector = vectorOrigin2Intersect . cross ( vectorIntersect2Camera ) ; var xAxisDirection = this . matrix . getVectorX ( ) ; if ( crossVector . dot ( xAxisDirection ) ) { radian = Math . abs ( radian ) ; } else { radian = - Math . abs ( radian ) ; } var pitch = MathUtils . radianToDegree ( radian ) ; if ( pitch >= 90 ) { throw `Invalid pitch: ${ pitch }` ; } return pitch ; } param canvasX param canvasY param verticesResult getPickInfoByCanvas ( canvasX , canvasY , verticesResult = false ) { const result = { line : null , vertices : [ ] } ; this . _updateCore ( ) ; var matrix = this . matrix ; var viewMatrix = this . viewMatrix ; var projMatrix = this . projMatrix ; var projViewMatrix = this . projViewMatrix ; this . matrix = this . matrixForDraw ; this . viewMatrix = this . viewMatrixForDraw ; this . projMatrix = this . projMatrixForDraw ; this . projViewMatrix = this . projViewMatrixForDraw ; var pickDirection = this . _getPickDirectionByCanvas ( canvasX , canvasY ) ; var p = this . getPosition ( ) ; result . line = new Line ( p , pickDirection ) ; if ( verticesResult ) { result . vertices = this . _getPickCartesianCoordInEarthByLine ( result . line ) ; } this . matrix = matrix ; this . viewMatrix = viewMatrix ; this . projMatrix = projMatrix ; this . projViewMatrix = projViewMatrix ; return result ; } getPickCartesianCoordInEarthByCanvas ( canvasX , canvasY ) { const pickInfo = this . getPickInfoByCanvas ( canvasX , canvasY , true ) ; return pickInfo . vertices ; } getLightDirection ( ) { var dirVertice = this . matrix . getVectorZ ( ) ; var direction = new Vector ( - dirVertice . x , - dirVertice . y , - dirVertice . z ) ; direction . normalize ( ) ; return direction ; } getDistance2EarthSurface ( ) { var position = this . getPosition ( ) ; var length2EarthSurface = Vector . fromVertice ( position ) . getLength ( ) - Kernel . EARTH_RADIUS ; return length2EarthSurface ; } getDistance2EarthOrigin ( ) { var position = this . getPosition ( ) ; return Vector . fromVertice ( position ) . getLength ( ) ; } isAnimating ( ) { return this . animating ; } centerTo ( newLon , newLat , newLevel = this . getLevel ( ) ) { if ( newLevel !== this . getLevel ( ) ) { const newPosition = this . _safelyGetNewPositonByLevel ( newLevel ) ; const newDistance = Vector . fromVertice ( newPosition ) . getLength ( ) ; this . _setPositionByLonLatDistance ( newLon , newLat , newDistance ) ; } else { this . _setPositionByLonLatDistance ( newLon , newLat ) ; } this . setLevel ( newLevel , true ) ; } animateTo ( newLon , newLat , newLevel = this . getLevel ( ) , duration = 1000 ) { const promise = new Promise ( ( resolve , reject ) => { if ( this . isAnimating ( ) ) { reject ( "be animating" ) ; return ; } const [ startLon , startLat ] = this . getLonlat ( ) ; const singleSpan = 1000 / 60 ; const count = Math . floor ( duration / singleSpan ) ; let start = - 1 ; const deltaLon = ( newLon - startLon ) / count ; const deltaLat = ( newLat - startLat ) / count ; let deltaLevel = 0 ; let deltaHeight = 0 ; if ( newLevel !== this . level ) { deltaLevel = ( newLevel - this . level ) / count ; const startDistance = this . getDistance2EarthOrigin ( ) ; const newPosition = this . _safelyGetNewPositonByLevel ( newLevel ) ; const newDistance = Vector . fromVertice ( newPosition ) . getLength ( ) ; deltaHeight = ( newDistance - startDistance ) / count ; } this . animating = true ; const callback = ( timestap ) => { if ( start < 0 ) { start = timestap ; } var a = timestap - start ; if ( a >= duration ) { this . animating = false ; this . floatLevel = newLevel ; this . centerTo ( newLon , newLat , newLevel ) ; resolve ( ) ; } else { this . floatLevel += deltaLevel ; this . _setPositionByDeltaLonLatDistance ( deltaLon , deltaLat , deltaHeight ) ; requestAnimationFrame ( callback ) ; } } ; requestAnimationFrame ( callback ) ; } ) ; return promise ; } animateToLevel ( newLevel , cb ? ) { if ( this . isAnimating ( ) ) { return ; } if ( ! ( Utils . isNonNegativeInteger ( newLevel ) ) ) { throw "invalid level:" + newLevel ; } const newPosition = this . _safelyGetNewPositonByLevel ( newLevel ) ; var oldPosition = this . getPosition ( ) ; var span = this . animationDuration ; var singleSpan = 1000 / 60 ; var count = Math . floor ( span / singleSpan ) ; var deltaX = ( newPosition . x - oldPosition . x ) / count ; var deltaY = ( newPosition . y - oldPosition . y ) / count ; var deltaZ = ( newPosition . z - oldPosition . z ) / count ; var deltaLevel = ( newLevel - this . level ) / count ; var start = - 1 ; this . floatLevel = this . level ; this . animating = true ; var callback = ( timestap ) => { if ( start < 0 ) { start = timestap ; } var a = timestap - start ; if ( a >= span ) { this . animating = false ; this . floatLevel = newLevel ; this . setLevel ( newLevel ) ; if ( cb ) { cb ( ) ; } } else { this . floatLevel += deltaLevel ; var p = this . getPosition ( ) ; this . setPosition ( new Vertice ( p . x + deltaX , p . y + deltaY , p . z + deltaZ ) ) ; requestAnimationFrame ( callback ) ; } } ; requestAnimationFrame ( callback ) ; } setExtent ( extent ) { if ( extent ) { const [ lon , lat , level ] = this . _calculateLonLatLevelByExtent ( extent ) ; this . centerTo ( lon , lat , level ) ; } } animateToExtent ( extent , duration = 1000 ) { const [ lon , lat , level ] = this . _calculateLonLatLevelByExtent ( extent ) ; return this . animateTo ( lon , lat , level , duration ) ; } private _safelyGetNewPositonByLevel ( newLevel ) { const newCameraMatrix = this . matrix . clone ( ) ; this . _updatePositionByLevel ( newLevel , newCameraMatrix ) ; const newPosition = newCameraMatrix . getPosition ( ) ; return newPosition ; } private _setPositionByLonLatDistance ( newLon , newLat , newLengthFromOrigin2Positon ? ) { const [ lon , lat ] = this . getLonlat ( ) ; const deltaLon = newLon - lon ; const deltaLat = newLat - lat ; this . _setPositionByDeltaLonLatDistance ( deltaLon , deltaLat ) ; if ( newLengthFromOrigin2Positon > 0 ) { this . _setPositionByDistanceFromOrigin2Camera ( newLengthFromOrigin2Positon ) ; } } private _setPositionByDeltaLonLatDistance ( deltaLon , deltaLat , deltaHeight ? ) { const deltaLonRadian = MathUtils . degreeToRadian ( deltaLon ) ; const deltaLatRadian = MathUtils . degreeToRadian ( deltaLat ) ; this . worldRotateY ( deltaLonRadian ) ; const vector1 = Vector . fromVertice ( this . getPosition ( ) ) ; const vector2 = new Vector ( 0 , 1 , 0 ) ; const crossAxis = vector1 . cross ( vector2 ) ; this . worldRotateByVector ( deltaLatRadian , crossAxis ) ; if ( deltaHeight > 0 || deltaHeight < 0 ) { const vectorFromOrigin2Position = Vector . fromVertice ( this . getPosition ( ) ) ; const newLength = vectorFromOrigin2Position . getLength ( ) + deltaHeight ; vectorFromOrigin2Position . setLength ( newLength ) ; const newPosition = vectorFromOrigin2Position . getVertice ( ) ; this . setPosition ( newPosition ) ; } } private _setPositionByDistanceFromOrigin2Camera ( newLengthFromOrigin2Positon ) { const vectorFromOrigin2Position = Vector . fromVertice ( this . getPosition ( ) ) ; vectorFromOrigin2Position . setLength ( newLengthFromOrigin2Positon ) ; const newPosition = vectorFromOrigin2Position . getVertice ( ) ; this . setPosition ( newPosition ) ; } private _safelyGetValidLevel ( level ) { if ( level > Kernel . MAX_LEVEL ) { level = Kernel . MAX_LEVEL ; } else if ( level < Kernel . MIN_LEVEL ) { level = Kernel . MIN_LEVEL ; } return level ; } private _calculateLonLatLevelByExtent ( extent ) { const centerLon = ( extent . getMinLon ( ) + extent . getMaxLon ( ) ) / 2 ; const centerLat = ( extent . getMinLat ( ) + extent . getMaxLat ( ) ) / 2 ; const deltaLon = extent . getMaxLon ( ) - extent . getMinLon ( ) ; const deltaLonRadian = MathUtils . degreeToRadian ( deltaLon ) ; const deltaLength = Kernel . EARTH_RADIUS * deltaLonRadian ; const resolution = deltaLength / this . width ; const bestFloatLevel = this . _calculateLevelByResolution ( resolution ) ; let level = Math . floor ( bestFloatLevel ) ; level = this . _safelyGetValidLevel ( level ) ; return [ centerLon , centerLat , level ] ; } private _look ( cameraPnt , targetPnt , upDirection = new Vector ( 0 , 1 , 0 ) ) { var cameraPntCopy = cameraPnt . clone ( ) ; var targetPntCopy = targetPnt . clone ( ) ; var up = upDirection . clone ( ) ; var zAxis = new Vector ( cameraPntCopy . x - targetPntCopy . x , cameraPntCopy . y - targetPntCopy . y , cameraPntCopy . z - targetPntCopy . z ) ; zAxis . normalize ( ) ; var xAxis = up . cross ( zAxis ) . normalize ( ) ; var yAxis = zAxis . cross ( xAxis ) . normalize ( ) ; this . matrix . setVectorX ( xAxis ) ; this . matrix . setVectorY ( yAxis ) ; this . matrix . setVectorZ ( zAxis ) ; this . matrix . setPosition ( cameraPntCopy ) ; this . matrix . setLastRowDefault ( ) ; this . _updateFar ( ) ; } private _lookAt ( targetPnt , upDirection ? ) { var targetPntCopy = targetPnt . clone ( ) ; var position = this . getPosition ( ) ; this . _look ( position , targetPntCopy , upDirection ) ; } private _getPickDirectionByCanvas ( canvasX , canvasY ) { var ndcXY = MathUtils . convertPointFromCanvasToNDC ( this . width , this . height , canvasX , canvasY ) ; var pickDirection = this . _getPickDirectionByNDC ( ndcXY [ 0 ] , ndcXY [ 1 ] ) ; return pickDirection ; } private _getDirectionIntersectPointWithEarth ( cameraMatrix ) { var dir = cameraMatrix . getVectorZ ( ) . getOpposite ( ) ; var p = cameraMatrix . getPosition ( ) ; var line = new Line ( p , dir ) ; var result = this . _getPickCartesianCoordInEarthByLine ( line ) ; return result ; } private _getPickDirectionByNDC ( ndcX , ndcY ) { var verticeInNDC = new Vertice ( ndcX , ndcY , 0.499 ) ; var verticeInWorld = this . _convertVerticeFromNdcToWorld ( verticeInNDC ) ; var cameraPositon = this . getPosition ( ) ; var pickDirection = Vector . verticeMinusVertice ( verticeInWorld , cameraPositon ) ; pickDirection . normalize ( ) ; return pickDirection ; } private _getPickCartesianCoordInEarthByLine ( line ) { var result = [ ] ; var pickVertices = MathUtils . getLineIntersectPointWithEarth ( line ) ; if ( pickVertices . length === 0 ) { result = [ ] ; } else if ( pickVertices . length == 1 ) { result = pickVertices ; } else if ( pickVertices . length == 2 ) { var pickVerticeA = pickVertices [ 0 ] ; var pickVerticeB = pickVertices [ 1 ] ; var cameraVertice = this . getPosition ( ) ; var lengthA = MathUtils . getLengthFromVerticeToVertice ( cameraVertice , pickVerticeA ) ; var lengthB = MathUtils . getLengthFromVerticeToVertice ( cameraVertice , pickVerticeB ) ; result = lengthA <= lengthB ? [ pickVerticeA , pickVerticeB ] : [ pickVerticeB , pickVerticeA ] ; } return result ; } private _getPickLonLatByNDC ( ndcX , ndcY ) { var result = null ; var vertices = this . _getPickCartesianCoordInEarthByNDC ( ndcX , ndcY ) ; if ( vertices . length > 0 ) { result = MathUtils . cartesianCoordToGeographic ( vertices [ 0 ] ) ; } return result ; } private _getPickCartesianCoordInEarthByNDC ( ndcX , ndcY ) { var pickDirection = this . _getPickDirectionByNDC ( ndcX , ndcY ) ; var p = this . getPosition ( ) ; var line = new Line ( p , pickDirection ) ; var result = this . _getPickCartesianCoordInEarthByLine ( line ) ; return result ; } private _getPlanXOZ ( ) { var position = this . getPosition ( ) ; var direction = this . getLightDirection ( ) ; var plan = MathUtils . getCrossPlaneByLine ( position , direction ) ; return plan ; } private _convertVerticeFromWorldToNDC ( verticeInWorld ) { var columnWorld = [ verticeInWorld . x , verticeInWorld . y , verticeInWorld . z , 1 ] ; var columnProject = this . projViewMatrix . multiplyColumn ( columnWorld ) ; var w = columnProject [ 3 ] ; var columnNDC = [ ] ; columnNDC [ 0 ] = columnProject [ 0 ] / w ; columnNDC [ 1 ] = columnProject [ 1 ] / w ; columnNDC [ 2 ] = columnProject [ 2 ] / w ; columnNDC [ 3 ] = 1 ; var verticeInNDC = new Vertice ( columnNDC [ 0 ] , columnNDC [ 1 ] , columnNDC [ 2 ] ) ; return verticeInNDC ; } private _convertVerticeFromNdcToWorld ( verticeInNDC ) { var columnNDC = [ verticeInNDC . x , verticeInNDC . y , verticeInNDC . z , 1 ] ; var inverseProj = this . projMatrix . getInverseMatrix ( ) ; var columnCameraTemp = inverseProj . multiplyColumn ( columnNDC ) ; var cameraX = columnCameraTemp [ 0 ] / columnCameraTemp [ 3 ] ; var cameraY = columnCameraTemp [ 1 ] / columnCameraTemp [ 3 ] ; var cameraZ = columnCameraTemp [ 2 ] / columnCameraTemp [ 3 ] ; var cameraW = 1 ; var columnCamera = [ cameraX , cameraY , cameraZ , cameraW ] ; var columnWorld = this . matrix . multiplyColumn ( columnCamera ) ; var verticeInWorld = new Vertice ( columnWorld [ 0 ] , columnWorld [ 1 ] , columnWorld [ 2 ] ) ; return verticeInWorld ; } private _convertVerticeFromCameraToWorld ( verticeInCamera ) { var verticeInCameraCopy = verticeInCamera . clone ( ) ; var column = [ verticeInCameraCopy . x , verticeInCameraCopy . y , verticeInCameraCopy . z , 1 ] ; var column2 = this . matrix . multiplyColumn ( column ) ; var verticeInWorld = new Vertice ( column2 [ 0 ] , column2 [ 1 ] , column2 [ 2 ] ) ; return verticeInWorld ; } private _convertVectorFromCameraToWorld ( vectorInCamera ) { var vectorInCameraCopy = vectorInCamera . clone ( ) ; var verticeInCamera = vectorInCameraCopy . getVertice ( ) ; var verticeInWorld = this . _convertVerticeFromCameraToWorld ( verticeInCamera ) ; var originInWorld = this . getPosition ( ) ; var vectorInWorld = Vector . verticeMinusVertice ( verticeInWorld , originInWorld ) ; vectorInWorld . normalize ( ) ; return vectorInWorld ; } private _isWorldVerticeVisibleInCanvas ( verticeInWorld , options = { } ) { var threshold = typeof options . threshold == "number" ? Math . abs ( options . threshold ) : 1 ; var cameraP = this . getPosition ( ) ; var dir = Vector . verticeMinusVertice ( verticeInWorld , cameraP ) ; var line = new Line ( cameraP , dir ) ; var pickResult = this . _getPickCartesianCoordInEarthByLine ( line ) ; if ( pickResult . length > 0 ) { var pickVertice = pickResult [ 0 ] ; var length2Vertice = MathUtils . getLengthFromVerticeToVertice ( cameraP , verticeInWorld ) ; var length2Pick = MathUtils . getLengthFromVerticeToVertice ( cameraP , pickVertice ) ; if ( length2Vertice < length2Pick + 5 ) { if ( ! ( options . verticeInNDC instanceof Vertice ) ) { options . verticeInNDC = this . _convertVerticeFromWorldToNDC ( verticeInWorld ) ; } var result = options . verticeInNDC . x >= - 1 && options . verticeInNDC . x <= 1 && options . verticeInNDC . y >= - threshold && options . verticeInNDC . y <= 1 ; return result ; } } return false ; } private _isGeoVisibleInCanvas ( lon , lat , options ? ) { var verticeInWorld = MathUtils . geographicToCartesianCoord ( lon , lat ) ; var result = this . _isWorldVerticeVisibleInCanvas ( verticeInWorld , options ) ; return result ; } /**
     * 算法，一个切片需要渲染需要满足如下三个条件:
     * 1.至少要有一个点在Canvas中可见
     * 2.NDC面积足够大
     * 3.形成的NDC四边形是顺时针方向
     */ getVisibleTilesByLevel ( level , options = { } ) { if ( ! ( level >= 0 ) ) { throw "invalid level" ; } var result = [ ] ; var LOOP_LIMIT = Math . min ( 10 , Math . pow ( 2 , level ) - 1 ) ; var mathOptions = { maxSize : Math . pow ( 2 , level ) } ; function checkVisible ( visibleInfo ) { if ( visibleInfo . area >= 5000 && visibleInfo . clockwise ) { if ( visibleInfo . visibleCount >= 1 ) { return true ; } } return false ; } function handleRow ( centerRow , centerColumn ) { var result = [ ] ; var grid = new TileGrid ( level , centerRow , centerColumn ) ; var visibleInfo = this . _getTileVisibleInfo ( grid . level , grid . row , grid . column , options ) ; var isRowCenterVisible = checkVisible ( visibleInfo ) ; if ( isRowCenterVisible ) { ( grid as any ) . visibleInfo = visibleInfo ; result . push ( grid ) ; var leftLoopTime = 0 ; var leftColumn = centerColumn ; var visible ; while ( leftLoopTime < LOOP_LIMIT ) { leftLoopTime ++ ; grid = TileGrid . getTileGridByBrother ( level , centerRow , leftColumn , TileGridPosition . LEFT , mathOptions ) ; leftColumn = grid . column ; visibleInfo = this . _getTileVisibleInfo ( grid . level , grid . row , grid . column , options ) ; visible = checkVisible ( visibleInfo ) ; if ( visible ) { ( < any > grid ) . visibleInfo = visibleInfo ; result . push ( grid ) ; } else { break ; } } var rightLoopTime = 0 ; var rightColumn = centerColumn ; while ( rightLoopTime < LOOP_LIMIT ) { rightLoopTime ++ ; grid = TileGrid . getTileGridByBrother ( level , centerRow , rightColumn , TileGridPosition . RIGHT , mathOptions ) ; rightColumn = grid . column ; visibleInfo = this . _getTileVisibleInfo ( grid . level , grid . row , grid . column , options ) ; visible = checkVisible ( visibleInfo ) ; if ( visible ) { ( < any > grid ) . visibleInfo = visibleInfo ; result . push ( grid ) ; } else { break ; } } } return result ; } var centerGrid = null ; var verticalCenterInfo = this . _getVerticalVisibleCenterInfo ( ) ; if ( TileGrid . isValidLatitude ( verticalCenterInfo . lat ) ) { centerGrid = TileGrid . getTileGridByGeo ( verticalCenterInfo . lon , verticalCenterInfo . lat , level ) ; } else { centerGrid = new TileGrid ( level , 0 , 0 ) ; } var handleRowThis = handleRow . bind ( this ) ; var rowResult = handleRowThis ( centerGrid . row , centerGrid . column ) ; result = result . concat ( rowResult ) ; var grid ; var bottomLoopTime = 0 ; var bottomRow = centerGrid . row ; while ( bottomLoopTime < LOOP_LIMIT ) { bottomLoopTime ++ ; grid = TileGrid . getTileGridByBrother ( level , bottomRow , centerGrid . column , TileGridPosition . BOTTOM , mathOptions ) ; bottomRow = grid . row ; rowResult = handleRowThis ( grid . row , grid . column ) ; if ( rowResult . length > 0 ) { result = result . concat ( rowResult ) ; } else { break ; } } var topLoopTime = 0 ; var topRow = centerGrid . row ; while ( topLoopTime < LOOP_LIMIT ) { topLoopTime ++ ; grid = TileGrid . getTileGridByBrother ( level , topRow , centerGrid . column , TileGridPosition . TOP , mathOptions ) ; topRow = grid . row ; rowResult = handleRowThis ( grid . row , grid . column ) ; if ( rowResult . length > 0 ) { result = result . concat ( rowResult ) ; } else { break ; } } return result ; } private _getTileVisibleInfo ( level , row , column , options ) { if ( ! ( level >= 0 ) ) { throw "invalid level" ; } if ( ! ( row >= 0 ) ) { throw "invalid row" ; } if ( ! ( column >= 0 ) ) { throw "invalid column" ; } var threshold = typeof options . threshold == "number" ? Math . abs ( options . threshold ) : 1 ; options . threshold = threshold ; var result = { lb : { lon : null , lat : null , verticeInWorld : null , verticeInNDC : null , visible : false } , lt : { lon : null , lat : null , verticeInWorld : null , verticeInNDC : null , visible : false } , rt : { lon : null , lat : null , verticeInWorld : null , verticeInNDC : null , visible : false } , rb : { lon : null , lat : null , verticeInWorld : null , verticeInNDC : null , visible : false } , Egeo : null , visibleCount : 0 , clockwise : false , width : null , height : null , area : null } ; result . Egeo = MathUtils . getTileGeographicEnvelopByGrid ( level , row , column ) ; var tileMinLon = result . Egeo . minLon ; var tileMaxLon = result . Egeo . maxLon ; var tileMinLat = result . Egeo . minLat ; var tileMaxLat = result . Egeo . maxLat ; result . lb . lon = tileMinLon ; result . lb . lat = tileMinLat ; result . lb . verticeInWorld = MathUtils . geographicToCartesianCoord ( result . lb . lon , result . lb . lat ) ; result . lb . verticeInNDC = this . _convertVerticeFromWorldToNDC ( result . lb . verticeInWorld ) ; result . lb . visible = this . _isWorldVerticeVisibleInCanvas ( result . lb . verticeInWorld , { verticeInNDC : result . lb . verticeInNDC , threshold : threshold } ) ; if ( result . lb . visible ) { result . visibleCount ++ ; } result . lt . lon = tileMinLon ; result . lt . lat = tileMaxLat ; result . lt . verticeInWorld = MathUtils . geographicToCartesianCoord ( result . lt . lon , result . lt . lat ) ; result . lt . verticeInNDC = this . _convertVerticeFromWorldToNDC ( result . lt . verticeInWorld ) ; result . lt . visible = this . _isWorldVerticeVisibleInCanvas ( result . lt . verticeInWorld , { verticeInNDC : result . lt . verticeInNDC , threshold : threshold } ) ; if ( result . lt . visible ) { result . visibleCount ++ ; } result . rt . lon = tileMaxLon ; result . rt . lat = tileMaxLat ; result . rt . verticeInWorld = MathUtils . geographicToCartesianCoord ( result . rt . lon , result . rt . lat ) ; result . rt . verticeInNDC = this . _convertVerticeFromWorldToNDC ( result . rt . verticeInWorld ) ; result . rt . visible = this . _isWorldVerticeVisibleInCanvas ( result . rt . verticeInWorld , { verticeInNDC : result . rt . verticeInNDC , threshold : threshold } ) ; if ( result . rt . visible ) { result . visibleCount ++ ; } result . rb . lon = tileMaxLon ; result . rb . lat = tileMinLat ; result . rb . verticeInWorld = MathUtils . geographicToCartesianCoord ( result . rb . lon , result . rb . lat ) ; result . rb . verticeInNDC = this . _convertVerticeFromWorldToNDC ( result . rb . verticeInWorld ) ; result . rb . visible = this . _isWorldVerticeVisibleInCanvas ( result . rb . verticeInWorld , { verticeInNDC : result . rb . verticeInNDC , threshold : threshold } ) ; if ( result . rb . visible ) { result . visibleCount ++ ; } var ndcs = [ result . lb . verticeInNDC , result . lt . verticeInNDC , result . rt . verticeInNDC , result . rb . verticeInNDC ] ; var vector03 = Vector . verticeMinusVertice ( ndcs [ 3 ] , ndcs [ 0 ] ) ; vector03 . z = 0 ; var vector01 = Vector . verticeMinusVertice ( ndcs [ 1 ] , ndcs [ 0 ] ) ; vector01 . z = 0 ; var cross = vector03 . cross ( vector01 ) ; result . clockwise = cross . z > 0 ; var topWidth = Math . sqrt ( Math . pow ( ndcs [ 1 ] . x - ndcs [ 2 ] . x , 2 ) + Math . pow ( ndcs [ 1 ] . y - ndcs [ 2 ] . y , 2 ) ) * this . width / 2 ; var bottomWidth = Math . sqrt ( Math . pow ( ndcs [ 0 ] . x - ndcs [ 3 ] . x , 2 ) + Math . pow ( ndcs [ 0 ] . y - ndcs [ 3 ] . y , 2 ) ) * this . width / 2 ; result . width = Math . floor ( ( topWidth + bottomWidth ) / 2 ) ; var leftHeight = Math . sqrt ( Math . pow ( ndcs [ 0 ] . x - ndcs [ 1 ] . x , 2 ) + Math . pow ( ndcs [ 0 ] . y - ndcs [ 1 ] . y , 2 ) ) * this . height / 2 ; var rightHeight = Math . sqrt ( Math . pow ( ndcs [ 2 ] . x - ndcs [ 3 ] . x , 2 ) + Math . pow ( ndcs [ 2 ] . y - ndcs [ 3 ] . y , 2 ) ) * this . height / 2 ; result . height = Math . floor ( ( leftHeight + rightHeight ) / 2 ) ; result . area = result . width * result . height ; return result ; } private _getVerticalVisibleCenterInfo ( ) { var result = { ndcY : < number > null , pIntersect : < Vertice > null , lon : < number > null , lat : < number > null } ; var pickResults ; if ( this . isZeroPitch ) { result . ndcY = 0 ; } else { var count = 10 ; var delta = 2.0 / count ; var topNdcY = 1 ; var bottomNdcY = - 1 ; var ndcY ; for ( ndcY = 1.0 ; ndcY >= - 1.0 ; ndcY -= delta ) { pickResults = this . _getPickCartesianCoordInEarthByNDC ( 0 , ndcY ) ; if ( pickResults . length > 0 ) { topNdcY = ndcY ; break ; } } for ( ndcY = - 1.0 ; ndcY <= 1.0 ; ndcY += delta ) { pickResults = this . _getPickCartesianCoordInEarthByNDC ( 0 , ndcY ) ; if ( pickResults . length > 0 ) { bottomNdcY = ndcY ; break ; } } result . ndcY = ( topNdcY + bottomNdcY ) / 2 ; } pickResults = this . _getPickCartesianCoordInEarthByNDC ( 0 , result . ndcY ) ; result . pIntersect = pickResults [ 0 ] ; var lonlat = MathUtils . cartesianCoordToGeographic ( result . pIntersect ) ; result . lon = lonlat [ 0 ] ; result . lat = lonlat [ 1 ] ; return result ; } } export default Camera ;