UNPKG

@openhps/core

Version:

Open Hybrid Positioning System - Core component

179 lines (163 loc) 6.32 kB
import { AngleUnit } from '../../utils/unit/AngleUnit'; import { LengthUnit } from '../../utils/unit/LengthUnit'; import { SerializableObject } from '../decorators'; import { Absolute3DPosition } from './Absolute3DPosition'; import { GCS, HAVERSINE, Unit, Vector3 } from '../../utils'; /** * Geographical WGS 84 position stored as an 3D vector in ISO 6709. * @category Position */ @SerializableObject() export class GeographicalPosition extends Absolute3DPosition { constructor(lat?: number, lng?: number, amsl?: number, gcs: GCS = GCS.WGS84) { super(); this.fromVector(new Vector3(lng, lat, amsl), gcs); } /** * Geographical Latitude * @returns {number} Latitude */ get latitude(): number { return this.y; } set latitude(lat: number) { this.y = lat; } /** * Geographical Longitude * @returns {number} Longitude */ get longitude(): number { return this.x; } set longitude(lng: number) { this.x = lng; } /** * Altitude above mean sea level * @returns {number} Altitude */ get altitude(): number { return this.z; } set altitude(amsl: number) { this.z = amsl; } /** * Get the distance from this location to a destination * @param {GeographicalPosition} destination Destination location * @returns {number} Distance between this point and destination */ distanceTo(destination: GeographicalPosition): number { return super.distanceTo(destination as this, HAVERSINE); } /** * Get the bearing in degrees from this location to a destination * @param {GeographicalPosition} destination Destination location * @returns {number} Bearing in degrees from this position to destination */ bearing(destination: GeographicalPosition): number { return AngleUnit.RADIAN.convert(this.angleTo(destination), AngleUnit.DEGREE); } /** * Get the bearing in radians from this location to a destination * @param {GeographicalPosition} destination Destination location * @returns {number} Bearing in radians from this position to destination */ angleTo(destination: GeographicalPosition): number { const lonRadA = AngleUnit.DEGREE.convert(this.longitude, AngleUnit.RADIAN); const latRadA = AngleUnit.DEGREE.convert(this.latitude, AngleUnit.RADIAN); const lonRadB = AngleUnit.DEGREE.convert(destination.longitude, AngleUnit.RADIAN); const latRadB = AngleUnit.DEGREE.convert(destination.latitude, AngleUnit.RADIAN); const y = Math.sin(lonRadB - lonRadA) * Math.cos(latRadB); const x = Math.cos(latRadA) * Math.sin(latRadB) - Math.sin(latRadA) * Math.cos(latRadB) * Math.cos(lonRadB - lonRadA); return Math.atan2(y, x); } destination( bearing: number, distance: number, bearingUnit = AngleUnit.DEGREE, distanceUnit = LengthUnit.METER, ): GeographicalPosition { distance = distanceUnit.convert(distance, LengthUnit.METER); const brng = bearingUnit.convert(bearing, AngleUnit.RADIAN); const lonRadA = bearingUnit.convert(this.longitude, AngleUnit.RADIAN); const latRadA = bearingUnit.convert(this.latitude, AngleUnit.RADIAN); const latX = Math.asin( Math.sin(latRadA) * Math.cos(distance / GCS.EARTH_RADIUS_MEAN) + Math.cos(latRadA) * Math.sin(distance / GCS.EARTH_RADIUS_MEAN) * Math.cos(brng), ); const lonX = lonRadA + Math.atan2( Math.sin(brng) * Math.sin(distance / GCS.EARTH_RADIUS_MEAN) * Math.cos(latRadA), Math.cos(distance / GCS.EARTH_RADIUS_MEAN) - Math.sin(latRadA) * Math.sin(latX), ); const location = new GeographicalPosition(); location.latitude = AngleUnit.RADIAN.convert(latX, AngleUnit.DEGREE); location.longitude = AngleUnit.RADIAN.convert(lonX, AngleUnit.DEGREE); location.altitude = this.altitude; location.unit = this.unit; return location; } fromVector(vector: Vector3, unit?: LengthUnit): this; fromVector(vector: Vector3, unit?: GCS): this; fromVector(vector: Vector3, unit: Unit = GCS.WGS84): this { let converted: Vector3; if (unit instanceof LengthUnit) { converted = GCS.ECEF.convert( new Vector3( unit.convert(vector.x, LengthUnit.METER), unit.convert(vector.y, LengthUnit.METER), unit.convert(vector.z, LengthUnit.METER), ), GCS.WGS84, ); } else if (unit instanceof GCS) { converted = unit !== GCS.WGS84 ? unit.convert(vector, GCS.WGS84) : vector; } this.x = converted.x; this.y = converted.y; this.z = converted.z; return this; } /** * Convert the geographical position to a vector * with geographical coordinate system ECEF. * @param {LengthUnit} [unit] Metric length unit * @returns {Vector3} Vector of the position */ toVector3(unit?: LengthUnit): Vector3; /** * Convert the geographical position to a vector * @param {GCS} [unit=GCS.WGS84] coordinate system * @returns {Vector3} Vector of the position */ toVector3(unit?: GCS): Vector3; toVector3(unit: Unit = GCS.WGS84): Vector3 { if (unit instanceof GCS) { return GCS.WGS84.convert(new Vector3(this.x, this.y, this.z), unit); } else if (unit instanceof LengthUnit) { return GCS.WGS84.convert( new Vector3( LengthUnit.METER.convert(this.x, unit), LengthUnit.METER.convert(this.y, unit), LengthUnit.METER.convert(this.z, unit), ), GCS.ECEF, ); } } /** * Clone the position * @returns {GeographicalPosition} Cloned geographical position */ clone(): this { const position = super.clone(); position.latitude = this.latitude; position.longitude = this.longitude; position.z = this.altitude; return position as this; } }