zoneminder/web/js/dms.js

191 lines
6.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* dms module
* @module dms
*/
// Just return a value to define the module export.
// This example returns an object, but the module
// can return a function as the exported value.
// Matches DMS DmsCoordinates
// http://regexpal.com/?flags=gim&regex=^%28-%3F\d%2B%28%3F%3A\.\d%2B%29%3F%29[%C2%B0%3Ad]%3F\s%3F%28%3F%3A%28\d%2B%28%3F%3A\.\d%2B%29%3F%29[%27%E2%80%B2%3A]%3F\s%3F%28%3F%3A%28\d%2B%28%3F%3A\.\d%2B%29%3F%29[%22%E2%80%B3]%3F%29%3F%29%3F\s%3F%28[NSEW]%29%3F&input=40%3A26%3A46N%2C79%3A56%3A55W%0A40%3A26%3A46.302N%2079%3A56%3A55.903W%0A40%C2%B026%E2%80%B247%E2%80%B3N%2079%C2%B058%E2%80%B236%E2%80%B3W%0A40d%2026%E2%80%B2%2047%E2%80%B3%20N%2079d%2058%E2%80%B2%2036%E2%80%B3%20W%0A40.446195N%2079.948862W%0A40.446195%2C%20-79.948862%0A40%C2%B0%2026.7717%2C%20-79%C2%B0%2056.93172%0A
const dmsRe = /^(-?\d+(?:\.\d+)?)[°:d]?\s?(?:(\d+(?:\.\d+)?)['ʹ:]?\s?(?:(\d+(?:\.\d+)?)["″ʺ]?)?)?\s?([NSEW])?/i;
/**
* Removes the decimal part of a number without rounding up.
* @param {number} n
* @returns {number}
* @private
*/
function truncate(n) {
return n > 0 ? Math.floor(n) : Math.ceil(n);
}
export class Dms {
_dd;
_hemisphere;
/**
* Value in decimal degrees
* @member {number}
* @readonly
*/
get dd() {
return this._dd;
}
/**
* Hemisphere
* @member {string}
* @readonly
*/
get hemisphere() {
return this._hemisphere;
}
/**
* @constructor module:dms.Dms
* @param {number} dd
* @param {string} longOrLat
*/
constructor(dd, longOrLat) {
this._dd = dd;
this._hemisphere = /^[WE]|(?:lon)/i.test(longOrLat) ? dd < 0 ? "W" : "E" : dd < 0 ? "S" : "N";
}
/**
* Returns the DMS parts as an array.
* The first three elements of the returned array are numbers:
* degrees, minutes, and seconds respectively. The fourth
* element is a string indicating the hemisphere: "N", "S", "E", or "W".
* @returns {Array.<(number|string)>}
* @deprecated
*/
getDmsArray() {
return this.dmsArray;
}
/**
* Returns the DMS parts as an array.
* The first three elements of the returned array are numbers:
* degrees, minutes, and seconds respectively. The fourth
* element is a string indicating the hemisphere: "N", "S", "E", or "W".
* @returns {Array.<(number|string)>}
*/
get dmsArray() {
const absDD = Math.abs(this._dd);
const degrees = truncate(absDD);
const minutes = truncate((absDD - degrees) * 60);
const seconds = (absDD - degrees - minutes / 60) * Math.pow(60, 2);
return [degrees, minutes, seconds, this._hemisphere];
}
/**
* Returns the DMS value as a string.
* @param {number} [precision] - number of digits after the decimal point in seconds
* @returns {string}
*/
toString(precision) {
const dmsArray = this.getDmsArray();
const second = isNaN(Number(precision)) ? dmsArray[2] : dmsArray[2].toFixed(precision);
return `${dmsArray[0]}°${dmsArray[1]}${second}${dmsArray[3]}`;
}
}
/**
* @typedef {Object} DmsArrays
* @property {Array.<(number|string)>} longitude
* @property {Array.<(number|string)>} latitude
*/
export default class DmsCoordinates {
lat;
lon;
// Results of match will be [full coords string, Degrees, minutes (if any), seconds (if any), hemisphere (if any)]
// E.g., ["40:26:46.302N", "40", "26", "46.302", "N"]
// E.g., ["40.446195N", "40.446195", undefined, undefined, "N"]
/**
* A regular expression matching DMS coordinate.
* Example matches:
* E.g., ["40:26:46.302N", "40", "26", "46.302", "N"]
* E.g., ["40.446195N", "40.446195", undefined, undefined, "N"]
* @type {RegExp}
* @static
*/
static dmsRe = dmsRe;
_longitude;
_latitude;
/**
* Longitude
* @type {module:dms.Dms} longitude - Longitude (X coordinate);
*/
get longitude() {
return this._longitude;
}
/**
* Latitude
* @type {module:dms.Dms} longitude - Latitude (y coordinate);
*/
get latitude() {
return this._latitude;
}
/**
* Represents a location on the earth in WGS 1984 coordinates.
* @constructor module:dms.DmsCoordinates
* @param {number} latitude - WGS 84 Y coordinates
* @param {number} longitude - WGS 84 X coordinates
* @throws {TypeError} - latitude and longitude must be numbers.
* @throws {RangeError} - latitude must be between -180 and 180, and longitude between -90 and 90. Neither can be NaN.
*/
constructor(lat, lon) {
this.lat = lat;
this.lon = lon;
if (typeof lat !== "number" || typeof lon !== "number") {
throw TypeError("The longitude and latitude parameters must be numbers.");
}
if (isNaN(lon) || lon < -180 || lon > 180) {
throw RangeError("longitude must be between -180 and 180");
}
if (isNaN(lat) || lat < -90 || lat > 90) {
throw RangeError("latitude must be between -90 and 90");
}
this._longitude = new Dms(lon, "long");
this._latitude = new Dms(lat, "lat");
}
/**
* Returns an object containing arrays containing degree / minute / second components.
* @returns {DmsArrays}
* @deprecated
*/
getDmsArrays() {
return this.dmsArrays;
}
/**
* Returns an object containing arrays containing degree / minute / second components.
* @type {DmsArrays}
*/
get dmsArrays() {
return {
longitude: this.longitude.dmsArray,
latitude: this.latitude.dmsArray,
};
}
/**
* Returns the coordinates to a comma-separated string.
* @returns {string}
*/
toString() {
return [this.latitude, this.longitude].join(", ");
}
}
/**
* Parses a Degrees Minutes Seconds string into a Decimal Degrees number.
* @param {string} dmsStr A string containing a coordinate in either DMS or DD format.
* @return {Number} If dmsStr is a valid coordinate string, the value in decimal degrees will be returned. Otherwise NaN will be returned.
*/
export function parseDms(dmsStr) {
let output = NaN;
const dmsMatch = dmsRe.exec(dmsStr);
if (dmsMatch) {
const degrees = Number(dmsMatch[1]);
const minutes = typeof (dmsMatch[2]) !== "undefined" ? Number(dmsMatch[2]) / 60 : 0;
const seconds = typeof (dmsMatch[3]) !== "undefined" ? Number(dmsMatch[3]) / 3600 : 0;
const hemisphere = dmsMatch[4] || null;
if (hemisphere !== null && /[SW]/i.test(hemisphere)) {
output = -Math.abs(degrees) - minutes - seconds;
}
else {
output = degrees + minutes + seconds;
}
}
return output;
}