UNPKG

signalk-server

Version:

An implementation of a [Signal K](http://signalk.org) server for boats.

100 lines 4.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.rebasePreviousPointFromXte = exports.crossTrackDistance = void 0; const EARTH_RADIUS_M = 6371000; const XTE_ZERO_EPSILON_M = 1; const toRadians = (degrees) => (degrees * Math.PI) / 180; const toDegrees = (radians) => (radians * 180) / Math.PI; const isFiniteNumber = (value) => typeof value === 'number' && Number.isFinite(value); const angularDistance = (a, b) => { const lat1 = toRadians(a.latitude); const lat2 = toRadians(b.latitude); const deltaLat = lat2 - lat1; const deltaLon = toRadians(b.longitude - a.longitude); const sinDeltaLat = Math.sin(deltaLat / 2); const sinDeltaLon = Math.sin(deltaLon / 2); return (2 * Math.asin(Math.sqrt(sinDeltaLat * sinDeltaLat + Math.cos(lat1) * Math.cos(lat2) * sinDeltaLon * sinDeltaLon))); }; const initialBearing = (a, b) => { const lat1 = toRadians(a.latitude); const lat2 = toRadians(b.latitude); const deltaLon = toRadians(b.longitude - a.longitude); return Math.atan2(Math.sin(deltaLon) * Math.cos(lat2), Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(deltaLon)); }; const crossTrackDistance = (position, start, destination) => { const distanceStartToPosition = angularDistance(start, position); const bearingStartToPosition = initialBearing(start, position); const bearingStartToDestination = initialBearing(start, destination); const value = Math.asin(Math.sin(distanceStartToPosition) * Math.sin(bearingStartToPosition - bearingStartToDestination)) * EARTH_RADIUS_M; return Number.isFinite(value) ? value : undefined; }; exports.crossTrackDistance = crossTrackDistance; const localPoint = (position, origin) => { const lat0 = toRadians(origin.latitude); const lat = toRadians(position.latitude); return { x: toRadians(position.longitude - origin.longitude) * EARTH_RADIUS_M * Math.cos((lat + lat0) / 2), y: (lat - lat0) * EARTH_RADIUS_M }; }; const geographicPoint = (point, origin) => { const lat = toRadians(origin.latitude); const latitude = lat + point.y / EARTH_RADIUS_M; return { latitude: toDegrees(latitude), longitude: origin.longitude + toDegrees(point.x / (EARTH_RADIUS_M * Math.cos((latitude + lat) / 2))) }; }; const candidateStartFromTangent = (vesselPosition, destination, xteRadius, side) => { const destinationLocal = localPoint(destination, vesselPosition); const distanceSquared = destinationLocal.x * destinationLocal.x + destinationLocal.y * destinationLocal.y; const radiusSquared = xteRadius * xteRadius; if (distanceSquared <= radiusSquared) { return undefined; } const scale = radiusSquared / distanceSquared; const offset = (side * xteRadius * Math.sqrt(distanceSquared - radiusSquared)) / distanceSquared; const tangent = { x: scale * destinationLocal.x - offset * destinationLocal.y, y: scale * destinationLocal.y + offset * destinationLocal.x }; const dx = tangent.x - destinationLocal.x; const dy = tangent.y - destinationLocal.y; const length = Math.hypot(dx, dy); if (!Number.isFinite(length) || length === 0) { return undefined; } const extension = xteRadius; return geographicPoint({ x: tangent.x + (dx / length) * extension, y: tangent.y + (dy / length) * extension }, vesselPosition); }; const rebasePreviousPointFromXte = (vesselPosition, destination, xte) => { if (Math.abs(xte) <= XTE_ZERO_EPSILON_M) { return vesselPosition; } const radius = Math.abs(xte); const candidates = [ candidateStartFromTangent(vesselPosition, destination, radius, 1), candidateStartFromTangent(vesselPosition, destination, radius, -1) ].filter((candidate) => candidate !== undefined); return candidates .map((candidate) => ({ candidate, calculatedXte: (0, exports.crossTrackDistance)(vesselPosition, candidate, destination) })) .filter((item) => isFiniteNumber(item.calculatedXte)) .sort((a, b) => Math.abs(a.calculatedXte - xte) - Math.abs(b.calculatedXte - xte))[0]?.candidate; }; exports.rebasePreviousPointFromXte = rebasePreviousPointFromXte; //# sourceMappingURL=xteGeometry.js.map