UNPKG

@itwin/core-common

Version:

iTwin.js components common to frontend and backend

211 lines • 8.45 kB
"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 Utils */ Object.defineProperty(exports, "__esModule", { value: true }); exports.calculateSolarAngles = calculateSolarAngles; exports.calculateSolarDirection = calculateSolarDirection; exports.calculateSolarDirectionFromAngles = calculateSolarDirectionFromAngles; exports.calculateSunriseOrSunset = calculateSunriseOrSunset; const core_geometry_1 = require("@itwin/core-geometry"); // cspell:ignore mrad sinm sint aarg // Code below loosely translated from https://www.esrl.noaa.gov/gmd/grad/solcalc/ function calcTimeJulianCent(jd) { const T = (jd - 2451545.0) / 36525.0; return T; } function radToDeg(angleRad) { return (180.0 * angleRad / Math.PI); } function degToRad(angleDeg) { return (Math.PI * angleDeg / 180.0); } function calcGeomMeanLongSun(t) { let L0 = 280.46646 + t * (36000.76983 + t * (0.0003032)); while (L0 > 360.0) { L0 -= 360.0; } while (L0 < 0.0) { L0 += 360.0; } return L0; // in degrees } function calcGeomMeanAnomalySun(t) { const M = 357.52911 + t * (35999.05029 - 0.0001537 * t); return M; // in degrees } function calcEccentricityEarthOrbit(t) { const e = 0.016708634 - t * (0.000042037 + 0.0000001267 * t); return e; // unitless } function calcSunEqOfCenter(t) { const m = calcGeomMeanAnomalySun(t); const mrad = degToRad(m); const sinm = Math.sin(mrad); const sin2m = Math.sin(mrad + mrad); const sin3m = Math.sin(mrad + mrad + mrad); const C = sinm * (1.914602 - t * (0.004817 + 0.000014 * t)) + sin2m * (0.019993 - 0.000101 * t) + sin3m * 0.000289; return C; // in degrees } function calcSunTrueLong(t) { const l0 = calcGeomMeanLongSun(t); const c = calcSunEqOfCenter(t); const O = l0 + c; return O; // in degrees } function calcSunApparentLong(t) { const o = calcSunTrueLong(t); const omega = 125.04 - 1934.136 * t; const lambda = o - 0.00569 - 0.00478 * Math.sin(degToRad(omega)); return lambda; // in degrees } function calcMeanObliquityOfEcliptic(t) { const seconds = 21.448 - t * (46.8150 + t * (0.00059 - t * (0.001813))); const e0 = 23.0 + (26.0 + (seconds / 60.0)) / 60.0; return e0; // in degrees } function calcObliquityCorrection(t) { const e0 = calcMeanObliquityOfEcliptic(t); const omega = 125.04 - 1934.136 * t; const e = e0 + 0.00256 * Math.cos(degToRad(omega)); return e; // in degrees } function calcSunDeclination(t) { const e = calcObliquityCorrection(t); const lambda = calcSunApparentLong(t); const sint = Math.sin(degToRad(e)) * Math.sin(degToRad(lambda)); const theta = radToDeg(Math.asin(sint)); return theta; // in degrees } function calcEquationOfTime(t) { const epsilon = calcObliquityCorrection(t); const l0 = calcGeomMeanLongSun(t); const e = calcEccentricityEarthOrbit(t); const m = calcGeomMeanAnomalySun(t); let y = Math.tan(degToRad(epsilon) / 2.0); y *= y; const sin2l0 = Math.sin(2.0 * degToRad(l0)); const sinm = Math.sin(degToRad(m)); const cos2l0 = Math.cos(2.0 * degToRad(l0)); const sin4l0 = Math.sin(4.0 * degToRad(l0)); const sin2m = Math.sin(2.0 * degToRad(m)); const eTime = y * sin2l0 - 2.0 * e * sinm + 4.0 * e * y * sinm * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m; return radToDeg(eTime) * 4.0; // in minutes of time } function calcAzEl(t, localTime, latitude, longitude, zone) { const eqTime = calcEquationOfTime(t); const theta = calcSunDeclination(t); const solarTimeFix = eqTime + 4.0 * longitude - 60.0 * zone; let trueSolarTime = localTime + solarTimeFix; while (trueSolarTime > 1440) trueSolarTime -= 1440; let hourAngle = trueSolarTime / 4.0 - 180.0; if (hourAngle < -180) { hourAngle += 360.0; } const haRad = degToRad(hourAngle); let csz = Math.sin(degToRad(latitude)) * Math.sin(degToRad(theta)) + Math.cos(degToRad(latitude)) * Math.cos(degToRad(theta)) * Math.cos(haRad); if (csz > 1.0) { csz = 1.0; } else if (csz < -1.0) { csz = -1.0; } const zenith = radToDeg(Math.acos(csz)); const azDenom = (Math.cos(degToRad(latitude)) * Math.sin(degToRad(zenith))); let azimuth; if (Math.abs(azDenom) > 0.001) { let azRad = ((Math.sin(degToRad(latitude)) * Math.cos(degToRad(zenith))) - Math.sin(degToRad(theta))) / azDenom; if (Math.abs(azRad) > 1.0) { if (azRad < 0) { azRad = -1.0; } else { azRad = 1.0; } } azimuth = 180.0 - radToDeg(Math.acos(azRad)); if (hourAngle > 0.0) { azimuth = -azimuth; } } else { if (latitude > 0.0) { azimuth = 180.0; } else { azimuth = 0.0; } } if (azimuth < 0.0) { azimuth += 360.0; } return { azimuth, elevation: 90 - zenith }; } function calculateJulianDay(date) { return Math.floor(date.getTime() / 86400000) + 2440587.5; // https://stackoverflow.com/questions/11759992/calculating-jdayjulian-day-in-javascript } /** @public * calculate solar angles (in radians) based at a given date/time and cartographic location. */ function calculateSolarAngles(date, location) { const jDay = calculateJulianDay(date); const latitude = location.latitudeDegrees; const longitude = location.longitudeDegrees; const utcMinutes = date.getUTCHours() * 60 + date.getUTCMinutes() + date.getUTCSeconds() / 60; const zone = Math.floor(.5 + longitude / 15.0); // date.getTimeZoneOffset mixes in DST. const localMinutes = utcMinutes + zone * 60; const jTotal = jDay + utcMinutes / 1440.0; const T = calcTimeJulianCent(jTotal); return calcAzEl(T, localMinutes, latitude, longitude, zone); } /** @public * calculate solar direction based at a given date/time and cartpgrphic location. */ function calculateSolarDirection(date, location) { return calculateSolarDirectionFromAngles(calculateSolarAngles(date, location)); } /** @public * calculate solar direction corresponding to the given azimuth and elevation (altitude) angles in degrees. */ function calculateSolarDirectionFromAngles(azimuthElevation) { const azimuth = core_geometry_1.Angle.degreesToRadians(azimuthElevation.azimuth); const elevation = core_geometry_1.Angle.degreesToRadians(azimuthElevation.elevation); const cosElevation = Math.cos(elevation); const sinElevation = Math.sin(elevation); return core_geometry_1.Vector3d.create(-Math.sin(azimuth) * cosElevation, -Math.cos(azimuth) * cosElevation, -sinElevation); } function dateFromUtcMinutes(date, utcMinutes) { const utcHours = Math.floor(utcMinutes / 60.0); const output = new Date(date); output.setUTCHours(utcHours); output.setUTCMinutes(Math.floor(.5 + utcMinutes - 60.0 * utcHours)); output.setUTCSeconds(0); return output; } function calcSunriseUtcMinutes(rise, lat, longitude, jDay) { const t = calcTimeJulianCent(jDay); const eqTime = calcEquationOfTime(t); const solarDec = calcSunDeclination(t); const latRad = degToRad(lat); const sdRad = degToRad(solarDec); const hAarg = (Math.cos(degToRad(90.833)) / (Math.cos(latRad) * Math.cos(sdRad)) - Math.tan(latRad) * Math.tan(sdRad)); const hourAngle = Math.acos(hAarg); const delta = longitude + radToDeg(rise ? hourAngle : -hourAngle); return 720 - (4.0 * delta) - eqTime; // in UTC minutes } /** @public * calculate solar sunrise or sunset for a given day and cartographic location. */ function calculateSunriseOrSunset(date, location, sunrise) { const jDay = calculateJulianDay(date); const longitude = location.longitudeDegrees; const latitude = location.latitudeDegrees; const utcMinutes = calcSunriseUtcMinutes(sunrise, latitude, longitude, jDay); return sunrise ? dateFromUtcMinutes(date, utcMinutes) : dateFromUtcMinutes(date, calcSunriseUtcMinutes(sunrise, latitude, longitude, jDay + utcMinutes / 1440)); } //# sourceMappingURL=SolarCalculate.js.map