import { GeoBox , GeoCoordinates , GeoCoordinatesLike , MathUtils , OrientedBox3 , Projection , ProjectionType , sphereProjection , TileKeyUtils , Vector2Like , Vector3Like } from "@here/harp-geoutils" ; import { GeoCoordLike } from "@here/harp-geoutils/lib/coordinates/GeoCoordLike" ; import { EarthConstants } from "@here/harp-geoutils/lib/projection/EarthConstants" ; import { assert , DOMUtils , LoggerManager } from "@here/harp-utils" ; import * as THREE from "three" ; import { CameraUtils } from "./CameraUtils" ; import { ElevationProvider } from "./ElevationProvider" ; import { Object3DUtils } from "./geometry/Object3DUtils" ; import { MapView } from "./MapView" ; const logger = LoggerManager . instance . create ( "MapViewUtils" ) ; /**
 * Zoom level to request terrain tiles for getting the height of the camera above terrain.
 */ const TERRAIN_ZOOM_LEVEL = 4 ; const groundNormalPlanarProj = new THREE . Vector3 ( 0 , 0 , 1 ) ; const groundPlane = new THREE . Plane ( groundNormalPlanarProj . clone ( ) ) ; const groundSphere = new THREE . Sphere ( undefined , EarthConstants . EQUATORIAL_RADIUS ) ; const rayCaster = new THREE . Raycaster ( ) ; const epsilon = 1e-5 ; /**
 * Cached ThreeJS instances for realtime maths.
 */ const space = { x : new THREE . Vector3 ( ) , y : new THREE . Vector3 ( ) , z : new THREE . Vector3 ( ) } ; const tangentSpace = { x : new THREE . Vector3 ( ) , y : new THREE . Vector3 ( ) , z : new THREE . Vector3 ( ) } ; const cache = { box3 : [ new THREE . Box3 ( ) ] , obox3 : [ new OrientedBox3 ( ) ] , quaternions : [ new THREE . Quaternion ( ) , new THREE . Quaternion ( ) ] , vector2 : [ new THREE . Vector2 ( ) , new THREE . Vector2 ( ) ] , vector3 : [ new THREE . Vector3 ( ) , new THREE . Vector3 ( ) , new THREE . Vector3 ( ) , new THREE . Vector3 ( ) ] , matrix4 : [ new THREE . Matrix4 ( ) , new THREE . Matrix4 ( ) ] , transforms : [ { xAxis : new THREE . Vector3 ( ) , yAxis : new THREE . Vector3 ( ) , zAxis : new THREE . Vector3 ( ) , position : new THREE . Vector3 ( ) } ] } ; const tmpCamera = new THREE . PerspectiveCamera ( ) ; /**
 * Rounds a given zoom level up to the nearest integer value if it's close enough.
 *
 * The zoom level set in  MapView after a zoom level
 * target is given to  {@link (MapView.lookAt:WITH_PARAMS)} or
 *  {@link @here/harp-map-controls#MapControls} never matches
 * exactly the target due to the precision loss caused by the
 * conversion from zoom level to camera distance (done in
 *  {@link (MapView.lookAt:WITH_PARAMS)} and  {@link @here/harp-map-controls#MapControls} )
 * and from distance back to zoom level (done at every frame on camera update).
 * As a result, given a fixed integer zoom level input, the final zoom level computed at every frame
 * may fall sometimes below the integer value and others above. This causes flickering since each
 * frame will use different tile levels and different style evaluations for object visibility.
 * See HARP-9673 and HARP-8523.
 *  param zoomLevel return function snapToCeilingZoomLevel ( zoomLevel ) { const eps = 1e-6 ; const ceiling = Math . ceil ( zoomLevel ) ; return ceiling - zoomLevel < eps ? ceiling : zoomLevel ; } /**
 * MapView utilities: View transformations, camera setup, view bounds computation...
 */ export namespace MapViewUtils { export const MAX_TILT_DEG = 89 ; export const MAX_TILT_RAD = MAX_TILT_DEG * THREE . MathUtils . DEG2RAD ; /**
     * The anti clockwise rotation of an object along the axes of its tangent space, with itself
     * as origin.
     */ export interface Attitude { /**
         * Rotation of the object along its vertical axis.
         */ yaw : number ; /**
         * Rotation of the object along its horizontal axis.
         */ pitch : number ; /**
         * Rotation of the object along its forward axis.
         */ roll : number ; } deprecated export interface MemoryUsage extends Object3DUtils . MemoryUsage { } param mapView param targetNDCx param targetNDCy param zoomLevel param maxTiltAngle returns `false` if requested zoom cannot be achieved due to the map view's maximum bounds
     *  MapView . geoMaxBounds ,`true` otherwise.
      export function zoomOnTargetPosition ( mapView , targetNDCx , targetNDCy , zoomLevel , maxTiltAngle = MAX_TILT_RAD ) { const { elevationProvider , camera , projection } = mapView ; const elevation = elevationProvider ? elevationProvider . getHeight ( projection . unprojectPoint ( camera . position ) , TERRAIN_ZOOM_LEVEL ) : undefined ; const zoomTarget = rayCastWorldCoordinates ( mapView , targetNDCx , targetNDCy , elevation ) ; const cameraTarget = MapViewUtils . getTargetAndDistance ( projection , camera , elevationProvider ) . target ; const newCameraDistance = calculateDistanceFromZoomLevel ( mapView , zoomLevel ) ; if ( mapView . geoMaxBounds ) { const constrained = constrainTargetAndDistanceToViewBounds ( cameraTarget , newCameraDistance , mapView ) ; if ( constrained . distance !== newCameraDistance ) { return zoomLevel >= mapView . zoomLevel ; } } camera . getWorldDirection ( camera . position ) . multiplyScalar ( - newCameraDistance ) . add ( cameraTarget ) ; if ( projection . type === ProjectionType . Spherical ) { const tilt = extractCameraTilt ( camera , projection ) ; const deltaTilt = tilt - maxTiltAngle ; if ( deltaTilt > 0 ) { orbitAroundScreenPoint ( mapView , { deltaTilt , maxTiltAngle } ) ; } } const newZoomTarget = rayCastWorldCoordinates ( mapView , targetNDCx , targetNDCy , elevation ) ; if ( ! zoomTarget || ! newZoomTarget ) { return true ; } if ( projection . type === ProjectionType . Planar ) { zoomTarget . sub ( newZoomTarget ) ; panCameraAboveFlatMap ( mapView , zoomTarget . x , zoomTarget . y ) ; } else if ( projection . type === ProjectionType . Spherical ) { panCameraAroundGlobe ( mapView , zoomTarget , newZoomTarget ) ; } return true ; } /**
     * Parameters for  orbitAroundScreenPoint .
      export interface OrbitParams { /**
         * Delta azimuth in radians (default 0).
         */ deltaAzimuth ? : number ; /**
         * Delta tilt in radians (default 0);
         */ deltaTilt ? : number ; /**
         * Maximum tilt between the camera and its target in radians.
         */ maxTiltAngle : number ; see CameraUtils . getPrincipalPoint .
          center ? : Vector2Like ; } param mapView - The  MapView instance to manipulate.
     *  param offsetX param offsetY param deltaAzimuth param deltaTilt param maxTiltAngle deprecated Use overload with  OrbitParams object parameter.
      export function orbitAroundScreenPoint ( mapView , offsetX , offsetY , deltaAzimuth , deltaTilt , maxTiltAngle ) ; param mapView - The  MapView instance to manipulate.
     *  param orbitParams -  OrbitParams .
      export function orbitAroundScreenPoint ( mapView , orbitParams ) ; export function orbitAroundScreenPoint ( mapView , offsetXOrOrbitParams , offsetY ? , deltaAzimuth ? , deltaTilt ? , maxTiltAngle ? ) { const ppalPoint = CameraUtils . getPrincipalPoint ( mapView . camera , cache . vector2 [ 0 ] ) ; const mapTargetWorld = MapViewUtils . rayCastWorldCoordinates ( mapView , ppalPoint . x , ppalPoint . y ) ; if ( mapTargetWorld === null ) { return ; } let orbitCenter ; if ( typeof offsetXOrOrbitParams === "number" ) { orbitCenter = cache . vector2 [ 1 ] . set ( offsetXOrOrbitParams , offsetY ! ) ; } else { const params = offsetXOrOrbitParams ; orbitCenter = params . center ?? ppalPoint ; deltaAzimuth = params . deltaAzimuth ?? 0 ; deltaTilt = params . deltaTilt ?? 0 ; maxTiltAngle = params . maxTiltAngle ; } const orbitAroundPpalPoint = orbitCenter . x === ppalPoint . x && orbitCenter . y === ppalPoint . y ; const rotationTargetWorld = orbitAroundPpalPoint ? mapTargetWorld : MapViewUtils . rayCastWorldCoordinates ( mapView , orbitCenter . x , orbitCenter . y ) ; if ( rotationTargetWorld === null ) { return ; } applyAzimuthAroundTarget ( mapView , rotationTargetWorld , - deltaAzimuth ! ) ; const tiltAxis = new THREE . Vector3 ( 1 , 0 , 0 ) . applyQuaternion ( mapView . camera . quaternion ) ; const clampedDeltaTilt = computeClampedDeltaTilt ( mapView , orbitCenter . y - ppalPoint . y , deltaTilt ! , maxTiltAngle ! , mapTargetWorld , rotationTargetWorld , tiltAxis ) ; applyTiltAroundTarget ( mapView , rotationTargetWorld , clampedDeltaTilt , tiltAxis ) ; } hidden internal function applyAzimuthAroundTarget ( mapView , rotationTargetWorld , deltaAzimuth ) { const camera = mapView . camera ; const projection = mapView . projection ; const headingAxis = projection . surfaceNormal ( rotationTargetWorld , cache . vector3 [ 0 ] ) ; const headingQuat = cache . quaternions [ 0 ] . setFromAxisAngle ( headingAxis , deltaAzimuth ) ; camera . quaternion . premultiply ( headingQuat ) ; camera . position . sub ( rotationTargetWorld ) ; camera . position . applyQuaternion ( headingQuat ) ; camera . position . add ( rotationTargetWorld ) ; } hidden internal function computeClampedDeltaTilt ( mapView , offsetY , deltaTilt , maxTiltAngle , mapTargetWorld , rotationTargetWorld , tiltAxis ) { const camera = mapView . camera ; const projection = mapView . projection ; const tilt = extractTiltAngleFromLocation ( projection , camera , mapTargetWorld , tiltAxis ) ; if ( tilt + deltaTilt < 0 ) { return - tilt ; } else if ( deltaTilt <= 0 ) { return deltaTilt ; } else if ( mapTargetWorld . equals ( rotationTargetWorld ) || offsetY < 0 ) { return MathUtils . clamp ( deltaTilt + tilt , 0 , maxTiltAngle ) - tilt ; } const rotationCenterTilt = extractTiltAngleFromLocation ( projection , camera , rotationTargetWorld ! , tiltAxis ) ; const maxRotationTiltAngle = THREE . MathUtils . degToRad ( 89 ) ; if ( rotationCenterTilt > maxRotationTiltAngle ) { return 0 ; } let angleBetweenNormals = 0 ; if ( projection === sphereProjection ) { const projectedRotationTargetNormal = projection . surfaceNormal ( rotationTargetWorld , cache . vector3 [ 0 ] ) . projectOnPlane ( tiltAxis ) . normalize ( ) ; const mapTargetNormal = projection . surfaceNormal ( mapTargetWorld , cache . vector3 [ 1 ] ) ; angleBetweenNormals = projectedRotationTargetNormal . angleTo ( mapTargetNormal ) ; } const ninetyRad = THREE . MathUtils . degToRad ( 90 ) ; const MRpC = ninetyRad + angleBetweenNormals - rotationCenterTilt ; const CMRp = ninetyRad + tilt ; const RpCM = ninetyRad * 2 - ( MRpC + CMRp ) ; const CMRpMaxTilt = ninetyRad * 2 - RpCM - ninetyRad - maxTiltAngle ; const maxTilt = ninetyRad + angleBetweenNormals - CMRpMaxTilt ; const clampedDeltaTilt = MathUtils . clamp ( deltaTilt + rotationCenterTilt , 0 , Math . min ( maxTilt , maxRotationTiltAngle ) ) - rotationCenterTilt ; return clampedDeltaTilt ; } hidden internal function applyTiltAroundTarget ( mapView , rotationTargetWorld , deltaTilt , tiltAxis ) { const camera = mapView . camera ; const posBackup = camera . position . clone ( ) ; const quatBackup = camera . quaternion . clone ( ) ; const tiltQuat = cache . quaternions [ 0 ] . setFromAxisAngle ( tiltAxis , deltaTilt ) ; camera . quaternion . premultiply ( tiltQuat ) ; camera . position . sub ( rotationTargetWorld ) ; camera . position . applyQuaternion ( tiltQuat ) ; camera . position . add ( rotationTargetWorld ) ; if ( MapViewUtils . rayCastWorldCoordinates ( mapView , 0 , 0 ) === null ) { logger . warn ( "Target got invalidated during rotation." ) ; camera . position . copy ( posBackup ) ; camera . quaternion . copy ( quatBackup ) ; } } see getTargetPositionFromCamera param camera param projection param elevation deprecated export function getGeoTargetFromCamera ( camera , projection , elevation ? ) { const targetWorldPos = getWorldTargetFromCamera ( camera , projection , elevation ) ; if ( targetWorldPos !== null ) { return projection . unprojectPoint ( targetWorldPos ) ; } return null ; } param camera param projection param elevation deprecated export function getWorldTargetFromCamera ( camera , projection , elevation ? ) { const cameraPos = cache . vector3 [ 0 ] . copy ( camera . position ) ; const cameraLookAt = camera . getWorldDirection ( cache . vector3 [ 1 ] ) ; rayCaster . set ( cameraPos , cameraLookAt ) ; if ( elevation !== undefined ) { groundPlane . constant -= elevation ; groundSphere . radius += elevation ; } const targetWorldPos = new THREE . Vector3 ( ) ; const result = projection . type === ProjectionType . Planar ? rayCaster . ray . intersectPlane ( groundPlane , targetWorldPos ) : rayCaster . ray . intersectSphere ( groundSphere , targetWorldPos ) ; if ( elevation !== undefined ) { groundPlane . constant = 0 ; groundSphere . radius = EarthConstants . EQUATORIAL_RADIUS ; } return result ; } /**
     * Constrains given camera target and distance to  MapView . maxBounds .
     *
     *  remarks param target param distance param mapView returns export function constrainTargetAndDistanceToViewBounds ( target , distance , mapView ) { const unconstrained = { target , distance } ; const worldMaxBounds = mapView . worldMaxBounds ; const camera = mapView . camera ; const projection = mapView . projection ; if ( ! worldMaxBounds ) { return unconstrained ; } /**
         * Constraints are checked similarly for planar and sphere. The extents of a top down view
         * (even if camera isn't top down) using the given camera distance are compared with those
         * of the maximum bounds to compute a scale. There are two options:
         * a) scale > 1. The view covers a larger area than the maximum bounds. The distance is
         * is reduced to match the bounds extents and the target is set at the bounds center.
         * b) scale <= 1. The view may fit within the bounds without changing the distance, only the
         * target is moved to fit the whole view within the bounds.
         **/ const boundsSize = worldMaxBounds . getSize ( cache . vector3 [ 1 ] ) ; const screenSize = mapView . renderer . getSize ( cache . vector2 [ 0 ] ) ; const viewHeight = CameraUtils . convertScreenToWorldSize ( mapView . focalLength , unconstrained . distance , screenSize . height ) ; const viewWidth = viewHeight * camera . aspect ; const scale = Math . max ( viewWidth / boundsSize . x , viewHeight / boundsSize . y ) ; const viewHalfSize = new THREE . Vector3 ( viewWidth / 2 , viewHeight / 2 , 0 ) ; const constrained = { target : unconstrained . target . clone ( ) , distance : unconstrained . distance } ; if ( projection . type === ProjectionType . Planar ) { if ( scale > 1 ) { constrained . distance /= scale ; camera . getWorldDirection ( camera . position ) . multiplyScalar ( - constrained . distance ) . add ( worldMaxBounds . getCenter ( constrained . target ) ) ; } else { const targetBounds = cache . box3 [ 0 ] . copy ( worldMaxBounds as THREE . Box3 ) . expandByVector ( viewHalfSize . multiplyScalar ( - 1 ) ) ; targetBounds . clampPoint ( unconstrained . target , constrained . target ) . setZ ( unconstrained . target . z ) ; if ( constrained . target . equals ( unconstrained . target ) ) { return unconstrained ; } camera . position . x += constrained . target . x - unconstrained . target . x ; camera . position . y += constrained . target . y - unconstrained . target . y ; } return constrained ; } if ( scale > 1 ) { worldMaxBounds . getCenter ( constrained . target ) ; constrained . target . setLength ( unconstrained . target . length ( ) ) ; constrained . distance /= scale ; } else { const targetMaxBounds = cache . obox3 [ 0 ] ; targetMaxBounds . copy ( worldMaxBounds as OrientedBox3 ) ; targetMaxBounds . position . setLength ( unconstrained . target . length ( ) ) ; targetMaxBounds . extents . sub ( viewHalfSize ) ; const rotMatrix = targetMaxBounds . getRotationMatrix ( cache . matrix4 [ 0 ] ) ; const localTarget = cache . vector3 [ 1 ] . copy ( constrained . target ) . sub ( targetMaxBounds . position ) . applyMatrix4 ( cache . matrix4 [ 1 ] . copy ( rotMatrix ) . transpose ( ) ) . setZ ( 0 ) ; const constrainedLocalTarget = cache . vector3 [ 2 ] . copy ( localTarget ) . clamp ( cache . vector3 [ 3 ] . copy ( targetMaxBounds . extents ) . multiplyScalar ( - 1 ) , targetMaxBounds . extents ) ; if ( constrainedLocalTarget . equals ( localTarget ) ) { return unconstrained ; } constrained . target . copy ( constrainedLocalTarget ) . applyMatrix4 ( rotMatrix ) . add ( targetMaxBounds . position ) ; const targetHeightSq = targetMaxBounds . position . lengthSq ( ) ; const constTargetDistSq = constrained . target . distanceToSquared ( targetMaxBounds . position ) ; const constTargetDistToGround = Math . sqrt ( targetHeightSq ) - Math . sqrt ( targetHeightSq - constTargetDistSq ) ; constrained . target . addScaledVector ( targetMaxBounds . zAxis , - constTargetDistToGround ) ; constrained . target . setLength ( unconstrained . target . length ( ) ) ; } MapViewUtils . panCameraAroundGlobe ( mapView , cache . vector3 [ 1 ] . copy ( constrained . target ) , cache . vector3 [ 2 ] . copy ( unconstrained . target ) ) ; camera . getWorldDirection ( camera . position ) . multiplyScalar ( - constrained . distance ) . add ( constrained . target ) ; return constrained ; } internal param projection param camera param elevationProvider returns export function getTargetAndDistance ( projection , camera , elevationProvider ? ) { const cameraPitch = extractAttitude ( { projection } , camera ) . pitch ; const elevation = elevationProvider ? elevationProvider . getHeight ( projection . unprojectPoint ( camera . position ) , TERRAIN_ZOOM_LEVEL ) : undefined ; const final = ! elevationProvider || elevation !== undefined ; const target = cameraPitch < MAX_TILT_RAD ? getWorldTargetFromCamera ( camera , projection , elevation ) : null ; if ( target !== null ) { const distance = camera . position . distanceTo ( target ) ; return { target , distance , final } ; } else { const groundDistance = projection . groundDistance ( camera . position ) ; const heightAboveTerrain = Math . max ( groundDistance - ( elevation ?? 0 ) , 0 ) ; const distance = projection . type === ProjectionType . Planar ? heightAboveTerrain / Math . cos ( Math . min ( cameraPitch , MAX_TILT_RAD ) ) : Math . sqrt ( Math . pow ( heightAboveTerrain + EarthConstants . EQUATORIAL_RADIUS , 2 ) - Math . pow ( EarthConstants . EQUATORIAL_RADIUS , 2 ) ) ; const cameraDir = camera . getWorldDirection ( cache . vector3 [ 0 ] ) ; cameraDir . multiplyScalar ( distance ) ; const fallbackTarget = cache . vector3 [ 1 ] ; fallbackTarget . copy ( camera . position ) . add ( cameraDir ) ; return { target : fallbackTarget , distance , final } ; } } /**
     * Returns the  {@link @here/harp-geoutils#GeoCoordinates} of the camera,
     * given its target coordinates on the map and its
     * zoom, yaw and pitch.
     *
     *  param targetCoordinates param distance param yawDeg param pitchDeg param projection param result returns export function getCameraPositionFromTargetCoordinates ( targetCoordinates , distance , yawDeg , pitchDeg , projection , result = new THREE . Vector3 ( ) ) { const pitchRad = THREE . MathUtils . degToRad ( pitchDeg ) ; const altitude = Math . cos ( pitchRad ) * distance ; const yawRad = THREE . MathUtils . degToRad ( yawDeg ) ; projection . projectPoint ( targetCoordinates , result ) ; const groundDistance = distance * Math . sin ( pitchRad ) ; if ( projection . type === ProjectionType . Planar ) { result . x = result . x + Math . sin ( yawRad ) * groundDistance ; result . y = result . y - Math . cos ( yawRad ) * groundDistance ; result . z = result . z + altitude ; } else if ( projection . type === ProjectionType . Spherical ) { tangentSpace . z . copy ( result ) . normalize ( ) ; tangentSpace . y . set ( 0 , 0 , 1 ) . projectOnPlane ( tangentSpace . z ) . normalize ( ) ; cache . quaternions [ 0 ] . setFromAxisAngle ( tangentSpace . z , yawRad - Math . PI ) ; tangentSpace . y . applyQuaternion ( cache . quaternions [ 0 ] ) ; tangentSpace . y . setLength ( groundDistance ) ; const height = distance * Math . cos ( pitchRad ) ; result . add ( tangentSpace . y ) . add ( tangentSpace . z . setLength ( height ) ) ; const a = EarthConstants . EQUATORIAL_RADIUS + altitude ; const b = Math . sin ( pitchRad ) * distance ; const cameraHeight = Math . sqrt ( a * a + b * b ) ; result . setLength ( cameraHeight ) ; } return result ; } hidden internal remarks export function wrapGeoPointsToScreen ( points , startPosition ? ) { let startIndex = 0 ; if ( startPosition === undefined ) { startPosition = GeoCoordinates . fromObject ( points [ 0 ] ) ; startIndex = 1 ; } let north = startPosition . latitude ; let south = startPosition . latitude ; let lonCenter = MathUtils . normalizeLongitudeDeg ( startPosition . longitude ) ; let lonSpan = 0 ; let east = startPosition . longitude ; let west = startPosition . longitude ; const result = [ ] ; result . push ( new GeoCoordinates ( north , lonCenter ) ) ; for ( let i = startIndex ; i < points . length ; i ++ ) { const p = GeoCoordinates . fromObject ( points [ i ] ) ; if ( p . latitude > north ) { north = p . latitude ; } else if ( p . latitude < south ) { south = p . latitude ; } let longitude = MathUtils . normalizeLongitudeDeg ( p . longitude ) ; const relToCenter = MathUtils . angleDistanceDeg ( lonCenter , longitude ) ; longitude = lonCenter - relToCenter ; if ( relToCenter < 0 && - relToCenter > lonSpan / 2 ) { east = Math . max ( east , lonCenter - relToCenter ) ; lonSpan = east - west ; lonCenter = ( east + west ) / 2 ; } else if ( relToCenter > 0 && relToCenter > lonSpan / 2 ) { west = Math . min ( west , longitude ) ; lonSpan = east - west ; lonCenter = ( east + west ) / 2 ; } result . push ( new GeoCoordinates ( p . latitude , longitude ) ) ; } return result ; } hidden internal remarks export function wrapWorldPointsToView ( points , cameraPos ) { const cameraPosNormalized = cameraPos . clone ( ) . normalize ( ) ; for ( const point of points ) { if ( point . angleTo ( cameraPos ) > Math . PI / 2 ) { const pointLen = point . length ( ) ; point . projectOnPlane ( cameraPosNormalized ) . setLength ( pointLen ) ; } } } hidden internal Return `GeoPoints` bounding  {@link @here/harp-geoutils#GeoBox} * applicable for  getFitBoundsDistance .
     *
     *  returns { link here export function geoBoxToGeoPoints ( box ) { const center = box . center ; return [ new GeoCoordinates ( box . north , box . west ) , new GeoCoordinates ( box . north , box . east ) , new GeoCoordinates ( center . latitude , box . west ) , new GeoCoordinates ( center . latitude , box . east ) , new GeoCoordinates ( box . south , box . west ) , new GeoCoordinates ( box . south , box . east ) , new GeoCoordinates ( box . north , center . longitude ) , new GeoCoordinates ( box . south , center . longitude ) ] ; } hidden internal param points param worldTarget - readonly, world target of  MapView param camera returns new distance to camera to be used with  {@link (MapView.lookAt:WITH_PARAMS)} export function getFitBoundsDistance ( points , worldTarget , camera ) { const targetDist = cache . vector3 [ 0 ] . copy ( worldTarget ) . sub ( camera . position ) . length ( ) ; const ppalPoint = CameraUtils . getPrincipalPoint ( camera ) ; let newDistance = targetDist ; const getDistanceFactor = ( pointNDC , ppNDC ) => { const maxNDC = 0.99 ; return Math . abs ( pointNDC ) > 1 ? Math . abs ( ( pointNDC - ppNDC ) / ( maxNDC * Math . sign ( pointNDC ) - ppNDC ) ) : 1 ; } ; for ( const point of points ) { const pEyeZ = - cache . vector3 [ 0 ] . copy ( point ) . applyMatrix4 ( camera . matrixWorldInverse ) . z ; const pointNDC = cache . vector3 [ 0 ] . applyMatrix4 ( camera . projectionMatrix ) ; const maxFactor = Math . max ( getDistanceFactor ( pointNDC . x , ppalPoint . x ) , getDistanceFactor ( pointNDC . y , ppalPoint . y ) ) ; if ( maxFactor > 1 ) { const constDist = targetDist - pEyeZ ; const newPEyeZ = Math . abs ( pEyeZ ) * maxFactor + constDist ; newDistance = Math . max ( newDistance , newPEyeZ ) ; } } return newDistance ; } hidden internal export interface FitPointParams { tilt : number ; heading : number ; projection : Projection ; minDistance : number ; camera : THREE . PerspectiveCamera ; } hidden internal Get  LookAtParams that fit all `worldPoints`
     * giving that  MapView will target at
     * `geoTarget`.
     *
     *  param geoTarget - desired target (see  MapView . target ) as geo point
     *  param worldTarget param worldPoints param params - other params derived from  MapView .
      export function getFitBoundsLookAtParams ( geoTarget , worldTarget , worldPoints , params ) { const { tilt , heading , projection } = params ; const startDistance = params . minDistance ; const tmpCamera = params . camera . clone ( ) as THREE . PerspectiveCamera ; getCameraRotationAtTarget ( projection , geoTarget , - heading , tilt , tmpCamera . quaternion ) ; getCameraPositionFromTargetCoordinates ( geoTarget , startDistance , - heading , tilt , projection , tmpCamera . position ) ; tmpCamera . updateMatrixWorld ( true ) ; if ( projection . type === ProjectionType . Spherical ) { wrapWorldPointsToView ( worldPoints , tmpCamera . position ) ; } const distance = getFitBoundsDistance ( worldPoints , worldTarget , tmpCamera ) ; return { target : geoTarget , distance , heading , tilt } ; } deprecated export function getCameraCoordinatesFromTargetCoordinates ( targetCoordinates , distance , yawDeg , pitchDeg , mapView ) { return mapView . projection . unprojectPoint ( getCameraPositionFromTargetCoordinates ( targetCoordinates , distance , yawDeg , pitchDeg , mapView . projection , cache . vector3 [ 1 ] ) ) ; } param mapView param pointOnScreenXinNDC param pointOnScreenYinNDC param elevation returns export function rayCastWorldCoordinates ( mapView , pointOnScreenXinNDC , pointOnScreenYinNDC , elevation ? ) { const pointInNDCPosition = cache . vector3 [ 0 ] . set ( pointOnScreenXinNDC , pointOnScreenYinNDC , 0 ) ; mapView . camera . updateMatrixWorld ( ) ; const cameraPos = cache . vector3 [ 1 ] . copy ( mapView . camera . position ) ; cache . matrix4 [ 0 ] . extractRotation ( mapView . camera . matrixWorld ) ; cache . matrix4 [ 1 ] . multiplyMatrices ( cache . matrix4 [ 0 ] , cache . matrix4 [ 1 ] . copy ( mapView . camera . projectionMatrix ) . invert ( ) ) ; const pointInCameraSpace = pointInNDCPosition . applyMatrix4 ( cache . matrix4 [ 1 ] ) ; rayCaster . set ( cameraPos , pointInCameraSpace . normalize ( ) ) ; if ( elevation !== undefined ) { groundPlane . constant -= elevation ; groundSphere . radius += elevation ; } const worldPosition = new THREE . Vector3 ( ) ; const result = mapView . projection . type === ProjectionType . Planar ? rayCaster . ray . intersectPlane ( groundPlane , worldPosition ) : rayCaster . ray . intersectSphere ( groundSphere , worldPosition ) ; if ( elevation !== undefined ) { groundPlane . constant = 0 ; groundSphere . radius = EarthConstants . EQUATORIAL_RADIUS ; } return result ; } param mapView param xOffset param yOffset export function panCameraAboveFlatMap ( mapView , offsetX , offsetY ) { mapView . camera . position . x += offsetX ; mapView . camera . position . y += offsetY ; } /**
     * The function doing a pan in the spherical space
     * when  MapView 's active [[ProjectionType]]
     * is spherical. In other words, the function that rotates the camera around the globe.
     *
     *  param mapView param fromWorld param toWorld export function panCameraAroundGlobe ( mapView , fromWorld , toWorld ) { cache . quaternions [ 0 ] . setFromUnitVectors ( fromWorld . normalize ( ) , toWorld . normalize ( ) ) . invert ( ) ; cache . matrix4 [ 0 ] . makeRotationFromQuaternion ( cache . quaternions [ 0 ] ) ; mapView . camera . applyMatrix4 ( cache . matrix4 [ 0 ] ) ; mapView . camera . updateMatrixWorld ( ) ; } param mapView - The  MapView instance in use.
     *  param deltaYawDeg param deltaPitchDeg param maxTiltAngleRad export function rotate ( mapView , deltaYawDeg , deltaPitchDeg = 0 , maxTiltAngleRad = Math . PI / 4 ) { mapView . camera . rotateOnWorldAxis ( mapView . projection . type === ProjectionType . Spherical ? cache . vector3 [ 0 ] . copy ( mapView . camera . position ) . normalize ( ) : cache . vector3 [ 0 ] . set ( 0 , 0 , 1 ) , THREE . MathUtils . degToRad ( - deltaYawDeg ) ) ; mapView . camera . updateMatrixWorld ( ) ; if ( deltaPitchDeg === 0 ) { return ; } const pitch = MapViewUtils . extractAttitude ( mapView , mapView . camera ) . pitch ; let newPitch = THREE . MathUtils . clamp ( pitch + THREE . MathUtils . degToRad ( deltaPitchDeg ) , 0 , maxTiltAngleRad ) ; if ( mapView . projection . type === ProjectionType . Spherical ) { const maxPitch = Math . asin ( ( EarthConstants . EQUATORIAL_RADIUS * Math . sin ( Math . PI - maxTiltAngleRad ) ) / mapView . camera . position . length ( ) ) ; newPitch = Math . min ( newPitch , maxPitch ) ; } mapView . camera . rotateX ( newPitch - pitch ) ; } param projection param target param yawDeg param pitchDeg export function getCameraRotationAtTarget ( projection , target , yawDeg , pitchDeg , result = new THREE . Quaternion ( ) ) { const transform = cache . transforms [ 0 ] ; projection . localTangentSpace ( target , transform ) ; cache . matrix4 [ 0 ] . makeBasis ( transform . xAxis , transform . yAxis , transform . zAxis ) ; result . setFromRotationMatrix ( cache . matrix4 [ 0 ] ) ; cache . quaternions [ 0 ] . setFromAxisAngle ( cache . vector3 [ 1 ] . set ( 0 , 0 , 1 ) , THREE . MathUtils . degToRad ( yawDeg ) ) ; cache . quaternions [ 1 ] . setFromAxisAngle ( cache . vector3 [ 1 ] . set ( 1 , 0 , 0 ) , THREE . MathUtils . degToRad ( pitchDeg ) ) ; result . multiply ( cache . quaternions [ 0 ] ) ; result . multiply ( cache . quaternions [ 1 ] ) ; return result ; } param mapView param yawDeg param pitchDeg export function setRotation ( mapView , yawDeg , pitchDeg ) { getCameraRotationAtTarget ( mapView . projection , mapView . geoCenter , yawDeg , pitchDeg , mapView . camera . quaternion ) ; } param camera param projection - The  {@link @here/harp-geoutils#Projection} used to
     *                     convert between geo and world coordinates.
     *
     *  deprecated export function extractCameraTilt ( camera , projection ) { if ( projection . type === ProjectionType . Planar ) { const lookAt = camera . getWorldDirection ( cache . vector3 [ 0 ] ) . normalize ( ) ; const normal = projection . surfaceNormal ( camera . position , cache . vector3 [ 1 ] ) . negate ( ) ; const cosTheta = lookAt . dot ( normal ) ; return Math . acos ( THREE . MathUtils . clamp ( cosTheta , - 1 , 1 ) ) ; } else { assert ( projection . type === ProjectionType . Spherical ) ; const targetGeoCoords = MapViewUtils . getGeoTargetFromCamera ( camera , projection ) ; if ( targetGeoCoords !== null ) { return MapViewUtils . extractTiltAngleFromLocation ( projection , camera , targetGeoCoords ) ; } else { logger . warn ( "MapView camera is pointing in the void, using maxTilt: " , MAX_TILT_RAD ) ; return MAX_TILT_RAD ; } } } see https param options - Subset of necessary  MapView properties.
     *  param object export function extractAttitude ( mapView , object ) { cache . vector3 [ 1 ] . setFromMatrixPosition ( object . matrixWorld ) ; mapView . projection . localTangentSpace ( cache . vector3 [ 1 ] , { xAxis : tangentSpace . x , yAxis : tangentSpace . y , zAxis : tangentSpace . z , position : cache . vector3 [ 0 ] } ) ; cache . matrix4 [ 1 ] . makeBasis ( tangentSpace . x , tangentSpace . y , tangentSpace . z ) ; cache . matrix4 [ 0 ] . copy ( cache . matrix4 [ 1 ] ) . invert ( ) . multiply ( object . matrixWorld ) ; space . x . setFromMatrixColumn ( cache . matrix4 [ 0 ] , 0 ) ; space . y . setFromMatrixColumn ( cache . matrix4 [ 0 ] , 1 ) ; space . z . setFromMatrixColumn ( cache . matrix4 [ 0 ] , 2 ) ; let yaw = 0 ; let pitch = 0 ; let roll = 0 ; const d = space . z . dot ( cache . vector3 [ 1 ] . set ( 0 , 0 , 1 ) ) ; if ( d < 1.0 - Number . EPSILON ) { if ( d > - 1.0 + Number . EPSILON ) { yaw = Math . atan2 ( space . z . x , - space . z . y ) ; pitch = Math . acos ( space . z . z ) ; roll = Math . atan2 ( space . x . z , space . y . z ) ; } else { yaw = - Math . atan2 ( - space . y . x , space . x . x ) ; pitch = 180 ; roll = 0 ; } } else { yaw = Math . atan2 ( - space . y . x , space . x . x ) ; pitch = 0.0 ; roll = 0.0 ; } return { yaw , pitch , roll } ; } param mapView - The  MapView instance to consider.
     *  param object param location export function extractSphericalCoordinatesFromLocation ( mapView , object , location ) { mapView . projection . localTangentSpace ( location , { xAxis : tangentSpace . x , yAxis : tangentSpace . y , zAxis : tangentSpace . z , position : cache . vector3 [ 0 ] } ) ; let tilt = 0 ; let azimuth = 0 ; cache . vector3 [ 1 ] . copy ( object . position ) . sub ( cache . vector3 [ 0 ] ) . normalize ( ) ; if ( cache . vector3 [ 1 ] . dot ( tangentSpace . z ) > 1 - Number . EPSILON ) { azimuth = Math . PI - extractAttitude ( mapView , object ) . yaw ; azimuth = Math . atan2 ( Math . sin ( azimuth ) , Math . cos ( azimuth ) ) ; tilt = 0 ; return { tilt , azimuth } ; } tilt = cache . vector3 [ 1 ] . angleTo ( tangentSpace . z ) ; cache . vector3 [ 1 ] . copy ( object . position ) . sub ( cache . vector3 [ 0 ] ) . projectOnPlane ( tangentSpace . z ) . normalize ( ) ; azimuth = cache . vector3 [ 1 ] . angleTo ( tangentSpace . y ) ; if ( cache . vector3 [ 1 ] . cross ( tangentSpace . y ) . dot ( tangentSpace . z ) < 0 ) { azimuth = - azimuth ; } return { tilt , azimuth } ; } param projection - The  {@link @here/harp-geoutils#Projection} used when
     *                     converting from geo to world coordinates.
     *  param object param location param tiltAxis export function extractTiltAngleFromLocation ( projection , object , location , tiltAxis ? ) { projection . localTangentSpace ( location , { xAxis : tangentSpace . x , yAxis : tangentSpace . y , zAxis : tangentSpace . z , position : cache . vector3 [ 0 ] } ) ; const dirVec = cache . vector3 [ 2 ] . copy ( object . position ) . sub ( cache . vector3 [ 0 ] ) ; if ( tiltAxis ) { dirVec . projectOnPlane ( tiltAxis ) ; tangentSpace . z . projectOnPlane ( tiltAxis ) . normalize ( ) ; } const dirLen = dirVec . length ( ) ; if ( dirLen < epsilon ) { logger . error ( "Can not calculate tilt for the zero length vector!" ) ; return 0 ; } dirVec . divideScalar ( dirLen ) ; const cosTheta = dirVec . dot ( tangentSpace . z ) ; if ( cosTheta >= 1 - Number . EPSILON ) { return 0 ; } return Math . acos ( THREE . MathUtils . clamp ( cosTheta , - 1 , 1 ) ) ; } deprecated return export function getCameraFrustumPlanes ( camera ) { const near = camera . near ; const far = camera . far ; let top = ( near * Math . tan ( THREE . MathUtils . degToRad ( 0.5 * camera . fov ) ) ) / camera . zoom ; let height = 2 * top ; let width = camera . aspect * height ; let left = - 0.5 * width ; const view = camera . view ; if ( view !== null && view . enabled ) { const fullWidth = view . fullWidth ; const fullHeight = view . fullHeight ; left += ( view . offsetX * width ) / fullWidth ; top -= ( view . offsetY * height ) / fullHeight ; width *= view . width / fullWidth ; height *= view . height / fullHeight ; } left += camera . filmOffset !== 0 ? ( near * camera . filmOffset ) / camera . getFilmWidth ( ) : 0 ; return { left , right : left + width , top , bottom : top - height , near , far } ; } param mapView param pointOnScreenXNDC param pointOnScreenYNDC returns export function rayCastGeoCoordinates ( mapView , pointOnScreenXinNDC , pointOnScreenYinNDC ) { const worldCoordinates = rayCastWorldCoordinates ( mapView , pointOnScreenXinNDC , pointOnScreenYinNDC ) ; if ( ! worldCoordinates ) { return null ; } return mapView . projection . unprojectPoint ( worldCoordinates ) ; } param mapView param options - Subset of necessary  MapView properties.
      export function calculateDistanceToGroundFromZoomLevel ( mapView , zoomLevel ) { const cameraPitch = extractAttitude ( mapView , mapView . camera ) . pitch ; const tileSize = EarthConstants . EQUATORIAL_CIRCUMFERENCE / Math . pow ( 2 , zoomLevel ) ; return ( ( mapView . focalLength * tileSize ) / 256 ) * Math . cos ( cameraPitch ) ; } param options param zoomLevel export function calculateDistanceFromZoomLevel ( options , zoomLevel ) { const tileSize = EarthConstants . EQUATORIAL_CIRCUMFERENCE / Math . pow ( 2 , zoomLevel ) ; return ( options . focalLength * tileSize ) / 256 ; } param options - Subset of necessary  MapView properties.
     *  param distance - The distance in meters, which are scene units in  MapView .
      export function calculateZoomLevelFromDistance ( options , distance ) { const tileSize = ( 256 * distance ) / options . focalLength ; const zoomLevel = THREE . MathUtils . clamp ( Math . log2 ( EarthConstants . EQUATORIAL_CIRCUMFERENCE / tileSize ) , options . minZoomLevel , options . maxZoomLevel ) ; return snapToCeilingZoomLevel ( zoomLevel ) ; } deprecated param clipDistance param camera export function calculateDepthFromClipDistance ( clipDistance , camera ) { const perspCam = camera as THREE . PerspectiveCamera ; const cameraRange = perspCam . far - perspCam . near ; const viewSpaceDistance = clipDistance * perspCam . far ; return ( 1.0 - perspCam . near / viewSpaceDistance ) * ( perspCam . far / cameraRange ) ; } deprecated param distance param camera export function cameraToWorldDistance ( distance , camera ) { const perspCam = camera as THREE . PerspectiveCamera ; return distance * perspCam . far ; } deprecated export function calculateVerticalFovByHorizontalFov ( hFov , aspect ) { return 2 * Math . atan ( Math . tan ( hFov / 2 ) / aspect ) ; } deprecated Use  CameraUtils . getHorizontalFov .
      export function calculateHorizontalFovByVerticalFov ( vFov , aspect ) { tmpCamera . fov = THREE . MathUtils . radToDeg ( vFov ) ; tmpCamera . aspect = aspect ; return CameraUtils . getHorizontalFov ( tmpCamera ) ; } deprecated Use  CameraUtils . setVerticalFov .
      export function calculateFocalLengthByVerticalFov ( vFov , height ) { CameraUtils . setPrincipalPoint ( tmpCamera , new THREE . Vector2 ( ) ) ; CameraUtils . setVerticalFov ( tmpCamera , vFov , height ) ; return CameraUtils . getFocalLength ( tmpCamera ) ! ; } deprecated Use  CameraUtils . setFocalLength .
      export function calculateFovByFocalLength ( focalLength , height ) { CameraUtils . setPrincipalPoint ( tmpCamera , new THREE . Vector2 ( ) ) ; CameraUtils . setFocalLength ( tmpCamera , focalLength , height ) ; return tmpCamera . fov ; } deprecated Use  CameraUtils . convertWorldToScreenSize .
      export const calculateScreenSizeByFocalLength = CameraUtils . convertWorldToScreenSize ; deprecated Use  CameraUtils . convertScreenToWorldSize .
      export const calculateWorldSizeByFocalLength = CameraUtils . convertScreenToWorldSize ; deprecated export const estimateObject3dSize = Object3DUtils . estimateSize ; returns export function mapViewIsLoading ( mapView ) { let numTilesLoading = 0 ; for ( const tileList of mapView . visibleTileSet . dataSourceTileList ) { numTilesLoading += tileList . numTilesLoading ; for ( const tile of tileList . visibleTiles ) { if ( ! tile . allGeometryLoaded ) { numTilesLoading ++ ; } } } let isLoading = numTilesLoading > 0 ; if ( mapView . textElementsRenderer !== undefined ) { isLoading = isLoading || mapView . textElementsRenderer . loading ; } isLoading = isLoading || ! mapView . poiTableManager . finishedLoading || ! mapView . visibleTileSet . allVisibleTilesLoaded ; return isLoading ; } export function closeToFrustum ( point , camera , eps = 1e-13 ) { const ndcPoint = new THREE . Vector3 ( ) . copy ( point ) . project ( camera ) ; if ( Math . abs ( ndcPoint . x ) - eps < 1 && Math . abs ( ndcPoint . y ) - eps < 1 && Math . abs ( ndcPoint . z ) - eps < 1 ) { return true ; } return false ; } deprecated Use  {@link @here/harp-utils#DOMUtils.getBrowserLanguages} export const getBrowserLanguages = DOMUtils . getBrowserLanguages ; } export namespace TileOffsetUtils { deprecated Use  {@link @here/harp-geoutils#TileKeyUtils.getKeyForTileKeyAndOffset} .
      export const getKeyForTileKeyAndOffset = TileKeyUtils . getKeyForTileKeyAndOffset ; deprecated Use  {@link @here/harp-geoutils#TileKeyUtils.getKeyForTileKeyAndOffset} .
      export const extractOffsetAndMortonKeyFromKey = TileKeyUtils . extractOffsetAndMortonKeyFromKey ; deprecated Use  {@link @here/harp-geoutils#TileKeyUtils.getParentKeyFromKey} .
      export const getParentKeyFromKey = TileKeyUtils . getParentKeyFromKey ; }