@giro3d/giro3d
Version:
A JS/WebGL framework for 3D geospatial data visualization
145 lines (134 loc) • 5.28 kB
JavaScript
/*
* Copyright (c) 2015-2018, IGN France.
* Copyright (c) 2018-2026, Giro3D team.
* SPDX-License-Identifier: MIT
*/
import { MathUtils, Spherical, Vector3 } from 'three';
import Coordinates from './Coordinates';
import CoordinateSystem from './CoordinateSystem';
import Ellipsoid from './Ellipsoid';
function computeJulianDate(date) {
let year = date.getUTCFullYear();
let month = date.getUTCMonth() + 1;
const day = date.getUTCDate();
const hour = date.getUTCHours();
const minute = date.getUTCMinutes();
const second = date.getUTCSeconds();
if (month <= 2) {
year -= 1;
month += 12;
}
const A = Math.floor(year / 100);
const B = 2 - A + Math.floor(A / 4);
const JD0h = Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + B - 1524.5;
return JD0h + (hour + minute / 60 + second / 3600) / 24;
}
function normalizedDegreesLongitude(degrees) {
const lon = degrees % 360;
return lon > 180 ? lon - 360 : lon < -180 ? 360 + lon : lon;
}
function normalizeAngle360(degrees) {
const angle = degrees % 360;
return angle >= 0 ? angle : angle < 0 ? 360 + angle : 360 - angle;
}
function celestialToGeographic(celestialLocation, date) {
const julianDate = computeJulianDate(date);
//number of days (positive or negative) since Greenwich noon, Terrestrial Time, on 1 January 2000 (J2000.0)
//Greenwich Mean Sidereal Time
const GMST = normalizeAngle360(280.46061837 + 360.98564736629 * (julianDate - 2451545));
//Greenwich Hour Angle
const GHA = normalizeAngle360(GMST - celestialLocation.rightAscension);
const longitude = normalizedDegreesLongitude(-GHA);
return {
latitude: celestialLocation.declination,
longitude: longitude
};
}
/**
* Gets the position of the sun in [**equatorial coordinates**](https://en.wikipedia.org/wiki/Position_of_the_Sun#Equatorial_coordinates)
* at the given date.
*
* Note: the geographic position of the sun is the location on earth where the sun is at the zenith.
* @param date - The date to compute the geographic position. If unspecified, the current date is used.
* @returns The geographic position of the sun at the given date.
*/
export function getGeographicPosition(date, target) {
date = date ?? new Date();
const JD = computeJulianDate(date);
const numDays = JD - 2451545;
// Mean longitude of the sun, in degrees
const meanLongitude = normalizeAngle360(280.46 + 0.9856474 * numDays);
// Mean anomaly of the sun, in radians
const meanAnomalyRad = normalizeAngle360(357.528 + 0.9856003 * numDays) * MathUtils.DEG2RAD;
// Ecliptic longitude of the sun, in degrees
const eclipticLongitude = meanLongitude + 1.915 * Math.sin(meanAnomalyRad) + 0.02 * Math.sin(2 * meanAnomalyRad);
const eclipticLongitudeRad = eclipticLongitude * MathUtils.DEG2RAD;
// Obliquity of the ecliptic, in radians
const obliquityOfTheEcliptic = MathUtils.DEG2RAD * (23.439 - 0.0000004 * numDays);
const declination = Math.asin(Math.sin(obliquityOfTheEcliptic) * Math.sin(eclipticLongitudeRad)) * MathUtils.RAD2DEG;
let rightAscension = Math.atan(Math.cos(obliquityOfTheEcliptic) * Math.tan(eclipticLongitudeRad)) * MathUtils.RAD2DEG;
//compensate for atan result
if (eclipticLongitude >= 90 && eclipticLongitude < 270) {
rightAscension += 180;
}
rightAscension = normalizeAngle360(rightAscension);
const {
latitude,
longitude
} = celestialToGeographic({
rightAscension,
declination
}, date);
target = target ?? new Coordinates(CoordinateSystem.epsg4326, 0, 0);
target.set(CoordinateSystem.epsg4326, longitude, latitude);
return target;
}
/**
* Returns the local position of the sun, given the zenith and azimuth.
*/
export function getLocalPosition(params, target) {
const zenith = MathUtils.clamp(params.zenith, 0, 90);
const point = params.point ?? new Vector3(0, 0, 0);
const theta = MathUtils.degToRad(params.azimuth);
const phi = MathUtils.degToRad(zenith);
const spherical = new Spherical(params.distance ?? 1, phi, theta);
target = target ?? new Vector3();
const raw = target.setFromSpherical(spherical);
// The spherical is Y-up, but we are Z-up
const {
y,
z
} = raw;
raw.setY(z);
raw.setZ(y);
return raw.add(point);
}
/**
* Gets the direction vector of sun rays at a given date, in the ECEF coordinate system.
*/
export function getDirection(date) {
const sunGeo = getGeographicPosition(date);
const dir = Ellipsoid.WGS84.toCartesian(sunGeo.latitude, sunGeo.longitude, 0).normalize();
return dir.negate();
}
/**
* Gets the direction vector of sun rays at a given date in the ENU
* coordinate system centered at the observer location.
* Note: this assumes that the target coordinate system is north up.
*/
export function getLocalFrameDirection(observer, date) {
const observerGeo = observer.as(CoordinateSystem.epsg4326);
const observerFrame = Ellipsoid.WGS84.getEastNorthUpMatrix(observerGeo.latitude, observerGeo.longitude);
const sunCartesian = getDirection(date);
const sunLocal = sunCartesian.applyMatrix4(observerFrame.invert());
return sunLocal;
}
/**
* Utility functions related to the position of the sun.
*/
export default {
getGeographicPosition,
getLocalPosition,
getLocalFrameDirection,
getDirection
};