"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Views
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ViewGlobalLocationConstants = void 0;
exports.metersToRange = metersToRange;
exports.queryTerrainElevationOffset = queryTerrainElevationOffset;
exports.areaToEyeHeight = areaToEyeHeight;
exports.areaToEyeHeightFromGcs = areaToEyeHeightFromGcs;
exports.rangeToCartographicArea = rangeToCartographicArea;
exports.eyeToCartographicOnGlobe = eyeToCartographicOnGlobe;
exports.eyeToCartographicOnGlobeFromGcs = eyeToCartographicOnGlobeFromGcs;
exports.viewGlobalLocation = viewGlobalLocation;
const core_common_1 = require("@itwin/core-common");
const internal_1 = require("./tile/internal");
const ViewState_1 = require("./ViewState");
/** @internal */
class ViewGlobalLocationConstants {
}
exports.ViewGlobalLocationConstants = ViewGlobalLocationConstants;
ViewGlobalLocationConstants.birdHeightAboveEarthInMeters = 713.0;
ViewGlobalLocationConstants.satelliteHeightAboveEarthInMeters = 402336.0 * 24;
ViewGlobalLocationConstants.largestEarthArc = 20037500.0; // distance from point on earth to opposite point in meters
ViewGlobalLocationConstants.birdPitchAngleRadians = 0.0; // Angle.piOver4Radians;
ViewGlobalLocationConstants.maximumDistanceToDrive = 96560.6; // The distance in meters that we will "drive" instead of fly to a destination (60 miles)
/** Converts a distance in meters to some range (smaller distances result in lower number; longer distances result in larger number)
 * Uses 500 for minimum output and 3000 for maximum output, unless either are specified.
 * Uses [[ViewGlobalLocationConstants.satelliteHeightAboveEarthInMeters]] as maximum input meters unless specified.
 * A good use of this is to convert meters to some transition duration.
 * @internal
 */
function metersToRange(inputMeters, minimumOutput = 500, maximumOutput = 3000, maximumInputMeters = ViewGlobalLocationConstants.satelliteHeightAboveEarthInMeters) {
    let output;
    if (inputMeters <= 0)
        output = minimumOutput;
    else if (inputMeters >= maximumInputMeters)
        output = maximumOutput;
    else {
        const quickLerp = (start, end, amt) => {
            return (1 - amt) * start + amt * end;
        };
        output = quickLerp(minimumOutput, maximumOutput, inputMeters / maximumInputMeters);
    }
    return output;
}
/** Queries the actual elevation of a cartographic point on the globe (using Bing elevation services)
 * @public
 * @extensions
 */
async function queryTerrainElevationOffset(viewport, carto) {
    const bingElevationProvider = new internal_1.BingElevationProvider();
    if (viewport && viewport.view instanceof ViewState_1.ViewState3d && viewport.iModel.isGeoLocated) {
        const view3d = viewport.view;
        if (view3d.displayStyle.displayTerrain) {
            const elevationOffset = await bingElevationProvider.getHeight(carto, view3d.globeMode === core_common_1.GlobeMode.Ellipsoid);
            if (elevationOffset !== undefined)
                return elevationOffset;
        }
    }
    return 0;
}
function _areaToEyeHeight(view3d, ne, sw, offset = 0) {
    if (ne === undefined || sw === undefined)
        return 0;
    const diagonal = ne.distance(sw);
    view3d.camera.validateLens();
    const td = Math.tan(view3d.camera.getLensAngle().radians / 2.0);
    return 0 !== td ? (diagonal / (2 * td) + offset) : offset;
}
/** Converts a cartographic area on the globe to an ideal eye height to view that area.
 * Offset in meters, which defaults to 0, is applied to final eye height.
 * @internal
 */
function areaToEyeHeight(view3d, area, offset = 0) {
    const ne = view3d.cartographicToRoot(area.northeast);
    const sw = view3d.cartographicToRoot(area.southwest);
    return _areaToEyeHeight(view3d, ne, sw, offset);
}
/** Converts a cartographic area on the globe to an ideal eye height to view that area using the GCS.
 * Offset in meters, which defaults to 0, is applied to final eye height.
 * @internal
 */
async function areaToEyeHeightFromGcs(view3d, area, offset = 0) {
    const corners = await view3d.cartographicToRootUsingGcs([area.northeast, area.southwest]);
    if (!corners)
        return 0;
    return _areaToEyeHeight(view3d, corners[0], corners[1], offset);
}
/** Converts a root range (often project extents) to a cartographic area.
 * @internal
 */
function rangeToCartographicArea(view3d, range) {
    const low = view3d.rootToCartographic(range.low);
    const high = view3d.rootToCartographic(range.high);
    if (low === undefined || high === undefined)
        return undefined;
    return low.latitude < high.latitude ? { northeast: high, southwest: low } : { northeast: low, southwest: high };
}
/** Converts the eye of the camera to a cartographic location on the globe as if it was at height 0.
 * If preserveHeight is set to true, then height will remain untouched.
 * @internal
 */
function eyeToCartographicOnGlobe(viewport, preserveHeight = false) {
    if (!(viewport.view instanceof ViewState_1.ViewState3d) || !viewport.iModel.isGeoLocated)
        return undefined;
    const view3d = viewport.view;
    const eyePointCartographic = view3d.rootToCartographic(view3d.getEyeOrOrthographicViewPoint());
    if (eyePointCartographic !== undefined) {
        if (!preserveHeight)
            eyePointCartographic.height = 0.0;
        return eyePointCartographic;
    }
    return undefined;
}
/** Converts the eye of the camera to a cartographic location on the globe as if it was at height 0 using the GCS.
 * If preserveHeight is set to true, then height will remain untouched.
 * @internal
 */
async function eyeToCartographicOnGlobeFromGcs(viewport, preserveHeight = false) {
    if (!(viewport.view instanceof ViewState_1.ViewState3d) || !viewport.iModel.isGeoLocated)
        return undefined;
    const view3d = viewport.view;
    const eyePointCartographic = await view3d.rootToCartographicFromGcs(view3d.getEyeOrOrthographicViewPoint());
    if (eyePointCartographic !== undefined) {
        if (!preserveHeight)
            eyePointCartographic.height = 0.0;
        return eyePointCartographic;
    }
    return undefined;
}
/** @internal */
function viewGlobalLocation(viewport, doAnimate, eyeHeight = ViewGlobalLocationConstants.birdHeightAboveEarthInMeters, pitchAngleRadians = 0, location) {
    if (!(viewport.view instanceof ViewState_1.ViewState3d))
        return 0;
    const before = viewport.getFrustum();
    const view3d = viewport.view;
    const transitionDistance = view3d.lookAtGlobalLocation(eyeHeight, pitchAngleRadians, location);
    viewport.synchWithView();
    if (doAnimate)
        viewport.animateToCurrent(before, { animationTime: metersToRange(transitionDistance) });
    return transitionDistance;
}
//# sourceMappingURL=ViewGlobalLocation.js.map