Improved handling of negative numbers and weirder inputs.

Negative numbers shouldn't make it go weird any more.
Automatic detection of input formats should be more reliable.
This commit is contained in:
j433866 2019-01-17 13:53:42 +00:00
parent 439654ed7f
commit 4bd923dc06

View file

@ -23,8 +23,7 @@ export const FORMATS = [
]; ];
/** /**
* Formats that should be passed to Geodesy module as-is * Formats that should be passed to the conversion module as-is
* Spaces are still removed
*/ */
const NO_CHANGE = [ const NO_CHANGE = [
"Geohash", "Geohash",
@ -48,40 +47,50 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
let isPair = false, let isPair = false,
split, split,
latlon, latlon,
conv, convLat,
inLatDir, convLon,
inLongDir; conv;
// Can't have a precision less than 0!
if (precision < 0) {
precision = 0;
}
if (inDelim === "Auto") { if (inDelim === "Auto") {
// Try to detect a delimiter in the input.
inDelim = findDelim(input); inDelim = findDelim(input);
if (inDelim === null) {
throw "Unable to detect the input delimiter automatically.";
}
} else { } else {
// Convert the delimiter argument value to the actual character
inDelim = realDelim(inDelim); inDelim = realDelim(inDelim);
} }
if (inFormat === "Auto") { if (inFormat === "Auto") {
// Try to detect the format of the input data
inFormat = findFormat(input, inDelim); inFormat = findFormat(input, inDelim);
if (inFormat === null) { if (inFormat === null) {
throw "Unable to detect the input format automatically."; throw "Unable to detect the input format automatically.";
} }
} }
if (inDelim === null && !inFormat.includes("Direction")) { // Convert the output delimiter argument to the real character
throw "Unable to detect the input delimiter automatically.";
}
outDelim = realDelim(outDelim); outDelim = realDelim(outDelim);
if (!NO_CHANGE.includes(inFormat)) { if (!NO_CHANGE.includes(inFormat)) {
split = input.split(inDelim); split = input.split(inDelim);
// Replace any co-ordinate symbols with spaces so we can split on them later
for (let i = 0; i < split.length; i++) {
split[i] = split[i].replace(/[°˝´'"]/g, " ");
}
if (split.length > 1) { if (split.length > 1) {
isPair = true; isPair = true;
} }
} else { } else {
// Remove any delimiters from the input
input = input.replace(inDelim, ""); input = input.replace(inDelim, "");
isPair = true; isPair = true;
} }
if (inFormat.includes("Degrees")) { // Conversions from the input format into a geodesy latlon object
[inLatDir, inLongDir] = findDirs(input, inDelim);
}
if (inFormat === "Geohash") { if (inFormat === "Geohash") {
const hash = geohash.decode(input.replace(/[^A-Za-z0-9]/g, "")); const hash = geohash.decode(input.replace(/[^A-Za-z0-9]/g, ""));
latlon = new geodesy.LatLonEllipsoidal(hash.latitude, hash.longitude); latlon = new geodesy.LatLonEllipsoidal(hash.latitude, hash.longitude);
@ -92,6 +101,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
const osng = geodesy.OsGridRef.parse(input.replace(/[^A-Za-z0-9]/g, "")); const osng = geodesy.OsGridRef.parse(input.replace(/[^A-Za-z0-9]/g, ""));
latlon = geodesy.OsGridRef.osGridToLatLon(osng); latlon = geodesy.OsGridRef.osGridToLatLon(osng);
} else if (inFormat === "Universal Transverse Mercator") { } else if (inFormat === "Universal Transverse Mercator") {
// Geodesy needs a space between the first 2 digits and the next letter
if (/^[\d]{2}[A-Za-z]/.test(input)) { if (/^[\d]{2}[A-Za-z]/.test(input)) {
input = input.slice(0, 2) + " " + input.slice(2); input = input.slice(0, 2) + " " + input.slice(2);
} }
@ -99,23 +109,25 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
latlon = utm.toLatLonE(); latlon = utm.toLatLonE();
} else if (inFormat === "Degrees Minutes Seconds") { } else if (inFormat === "Degrees Minutes Seconds") {
if (isPair) { if (isPair) {
split[0] = split[0].replace(/[NnEeSsWw]/g, "").trim(); // Split up the lat/long into degrees / minutes / seconds values
split[1] = split[1].replace(/[NnEeSsWw]/g, "").trim(); const splitLat = splitInput(split[0]),
const splitLat = split[0].split(/[°′″'"\s]/g), splitLong = splitInput(split[1]);
splitLong = split[1].split(/[°′″'"\s]/g);
if (splitLat.length >= 3 && splitLong.length >= 3) { if (splitLat.length >= 3 && splitLong.length >= 3) {
const lat = convDMSToDD(parseFloat(splitLat[0]), parseFloat(splitLat[1]), parseFloat(splitLat[2]), 10); const lat = convDMSToDD(splitLat[0], splitLat[1], splitLat[2], 10);
const long = convDMSToDD(parseFloat(splitLong[0]), parseFloat(splitLong[1]), parseFloat(splitLong[2]), 10); const long = convDMSToDD(splitLong[0], splitLong[1], splitLong[2], 10);
latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees); latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees);
} else {
throw "Invalid co-ordinate format for Degrees Minutes Seconds";
} }
} else { } else {
// Create a new latlon object anyway, but we can ignore the lon value // Not a pair, so only try to convert one set of co-ordinates
split[0] = split[0].replace(/[NnEeSsWw]/g, "").trim(); const splitLat = splitInput(split[0]);
const splitLat = split[0].split(/[°′″'"\s]/g);
if (splitLat.length >= 3) { if (splitLat.length >= 3) {
const lat = convDMSToDD(parseFloat(splitLat[0]), parseFloat(splitLat[1]), parseFloat(splitLat[2])); const lat = convDMSToDD(splitLat[0], splitLat[1], splitLat[2]);
latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lat.degrees); latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lat.degrees);
} else {
throw "Invalid co-ordinate format for Degrees Minutes Seconds";
} }
} }
} else if (inFormat === "Degrees Decimal Minutes") { } else if (inFormat === "Degrees Decimal Minutes") {
@ -125,10 +137,12 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
if (splitLat.length !== 2 || splitLong.length !== 2) { if (splitLat.length !== 2 || splitLong.length !== 2) {
throw "Invalid co-ordinate format for Degrees Decimal Minutes."; throw "Invalid co-ordinate format for Degrees Decimal Minutes.";
} }
// Convert to decimal degrees, and then convert to a geodesy object
const lat = convDDMToDD(splitLat[0], splitLat[1], 10); const lat = convDDMToDD(splitLat[0], splitLat[1], 10);
const long = convDDMToDD(splitLong[0], splitLong[1], 10); const long = convDDMToDD(splitLong[0], splitLong[1], 10);
latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees); latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees);
} else { } else {
// Not a pair, so only try to convert one set of co-ordinates
const splitLat = splitInput(input); const splitLat = splitInput(input);
if (splitLat.length !== 2) { if (splitLat.length !== 2) {
throw "Invalid co-ordinate format for Degrees Decimal Minutes."; throw "Invalid co-ordinate format for Degrees Decimal Minutes.";
@ -145,6 +159,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
} }
latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLong[0]); latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLong[0]);
} else { } else {
// Not a pair, so only try to convert one set of co-ordinates
const splitLat = splitInput(split[0]); const splitLat = splitInput(split[0]);
if (splitLat.length !== 1) { if (splitLat.length !== 1) {
throw "Invalid co-ordinate format for Decimal Degrees."; throw "Invalid co-ordinate format for Decimal Degrees.";
@ -152,88 +167,115 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLat[0]); latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLat[0]);
} }
} else { } else {
throw "Invalid input co-ordinate format selected."; throw `Unknown input format '${inFormat}'`;
} }
// Everything is now a geodesy latlon object // Everything is now a geodesy latlon object
// These store the latitude and longitude as decimal
if (inFormat.includes("Degrees")) {
// If the input string contains directions, we need to check if they're S or W.
// If either of the directions are, we should make the decimal value negative
const dirs = input.match(/[NnEeSsWw]/g);
if (dirs && dirs.length >= 1) {
// Make positive lat/lon values with S/W directions into negative values
if (dirs[0] === "S" || dirs[0] === "W" && latlon.lat > 0) {
latlon.lat = 0 - latlon.lat;
}
if (dirs.length >= 2) {
if (dirs[1] === "S" || dirs[1] === "W" && latlon.lon > 0) {
latlon.lon = 0 - latlon.lon;
}
}
}
}
// Try to find the compass directions of the lat and long
const [latDir, longDir] = findDirs(latlon.lat + "," + latlon.lon, ",");
// Output conversions for each output format
if (outFormat === "Decimal Degrees") { if (outFormat === "Decimal Degrees") {
conv = latlon.toString("d", precision); // We could use the built in latlon.toString(),
if (!isPair) { // but this makes adjusting the output harder
conv = conv.split(",")[0]; const lat = convDDToDD(latlon.lat, precision);
} const lon = convDDToDD(latlon.lon, precision);
convLat = lat.string;
convLon = lon.string;
} else if (outFormat === "Degrees Decimal Minutes") { } else if (outFormat === "Degrees Decimal Minutes") {
conv = latlon.toString("dm", precision); const lat = convDDToDDM(latlon.lat, precision);
if (!isPair) { const lon = convDDToDDM(latlon.lon, precision);
conv = conv.split(",")[0]; convLat = lat.string;
} convLon = lon.string;
} else if (outFormat === "Degrees Minutes Seconds") { } else if (outFormat === "Degrees Minutes Seconds") {
conv = latlon.toString("dms", precision); const lat = convDDToDMS(latlon.lat, precision);
if (!isPair) { const lon = convDDToDMS(latlon.lon, precision);
conv = conv.split(",")[0]; convLat = lat.string;
} convLon = lon.string;
} else if (outFormat === "Geohash") { } else if (outFormat === "Geohash") {
conv = geohash.encode(latlon.lat.toString(), latlon.lon.toString(), precision); convLat = geohash.encode(latlon.lat, latlon.lon, precision);
} else if (outFormat === "Military Grid Reference System") { } else if (outFormat === "Military Grid Reference System") {
const utm = latlon.toUtm(); const utm = latlon.toUtm();
const mgrs = utm.toMgrs(); const mgrs = utm.toMgrs();
conv = mgrs.toString(precision); // MGRS wants a precision that's an even number between 2 and 10
if (precision % 2 !== 0) {
precision = precision + 1;
}
if (precision > 10) {
precision = 10;
}
convLat = mgrs.toString(precision);
} else if (outFormat === "Ordnance Survey National Grid") { } else if (outFormat === "Ordnance Survey National Grid") {
const osng = geodesy.OsGridRef.latLonToOsGrid(latlon); const osng = geodesy.OsGridRef.latLonToOsGrid(latlon);
if (osng.toString() === "") { if (osng.toString() === "") {
throw "Could not convert co-ordinates to OS National Grid. Are the co-ordinates in range?"; throw "Could not convert co-ordinates to OS National Grid. Are the co-ordinates in range?";
} }
conv = osng.toString(precision); // OSNG wants a precision that's an even number between 2 and 10
if (precision % 2 !== 0) {
precision = precision + 1;
}
if (precision > 10) {
precision = 10;
}
convLat = osng.toString(precision);
} else if (outFormat === "Universal Transverse Mercator") { } else if (outFormat === "Universal Transverse Mercator") {
const utm = latlon.toUtm(); const utm = latlon.toUtm();
conv = utm.toString(precision); convLat = utm.toString(precision);
} }
if (conv === undefined) { if (convLat === undefined) {
throw "Error converting co-ordinates."; throw "Error converting co-ordinates.";
} }
if (outFormat.includes("Degrees")) { if (outFormat.includes("Degrees")) {
let [latDir, longDir] = findDirs(conv, outDelim); // Format DD/DDM/DMS for output
if (inLatDir !== undefined) { // If we're outputting a compass direction, remove the negative sign
latDir = inLatDir; if (latDir === "S" && includeDir !== "None") {
convLat = convLat.replace("-", "");
} }
if (inLongDir !== undefined) { if (longDir === "W" && includeDir !== "None") {
longDir = inLongDir; convLon = convLon.replace("-", "");
} }
// DMS/DDM/DD
conv = conv.replace(", ", outDelim);
// Remove any directions from the current string,
// so we can put them where we want them
conv = conv.replace(/[NnEeSsWw]/g, "");
if (includeDir !== "None") {
let outConv = ""; let outConv = "";
if (!isPair) {
if (includeDir === "Before") {
outConv += latDir + " " + conv;
} else {
outConv += conv + " " + latDir;
}
} else {
const splitConv = conv.split(outDelim);
if (splitConv.length === 2) {
if (includeDir === "Before") { if (includeDir === "Before") {
outConv += latDir + " "; outConv += latDir + " ";
} }
outConv += splitConv[0];
outConv += convLat;
if (includeDir === "After") { if (includeDir === "After") {
outConv += " " + latDir; outConv += " " + latDir;
} }
outConv += outDelim; outConv += outDelim;
if (isPair) {
if (includeDir === "Before") { if (includeDir === "Before") {
outConv += longDir + " "; outConv += longDir + " ";
} }
outConv += splitConv[1]; outConv += convLon;
if (includeDir === "After") { if (includeDir === "After") {
outConv += " " + longDir; outConv += " " + longDir;
} }
} outConv += outDelim;
} }
conv = outConv; conv = outConv;
} } else {
conv = convLat + outDelim;
} }
return conv; return conv;
@ -247,10 +289,11 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli
function splitInput (input){ function splitInput (input){
const split = []; const split = [];
input.split(/[°′″'"\s]/).forEach(item => { input.split(/\s+/).forEach(item => {
// Remove any character that isn't a digit // Remove any character that isn't a digit, decimal point or negative sign
item = item.replace(/[^0-9.-]/g, ""); item = item.replace(/[^0-9.-]/g, "");
if (item.length > 0){ if (item.length > 0){
// Turn the item into a float
split.push(parseFloat(item)); split.push(parseFloat(item));
} }
}); });
@ -266,10 +309,17 @@ function splitInput (input){
* @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string)
*/ */
function convDMSToDD (degrees, minutes, seconds, precision){ function convDMSToDD (degrees, minutes, seconds, precision){
const converted = new Object(); const absDegrees = Math.abs(degrees);
converted.degrees = degrees + (minutes / 60) + (seconds / 3600); let conv = absDegrees + (minutes / 60) + (seconds / 3600);
converted.string = (Math.round(converted.degrees * precision) / precision) + "°"; let outString = round(conv, precision) + "°";
return converted; if (isNegativeZero(degrees) || degrees < 0) {
conv = -conv;
outString = "-" + outString;
}
return {
"degrees": conv,
"string": outString
};
} }
/** /**
@ -280,10 +330,17 @@ function convDMSToDD (degrees, minutes, seconds, precision){
* @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string)
*/ */
function convDDMToDD (degrees, minutes, precision) { function convDDMToDD (degrees, minutes, precision) {
const converted = new Object(); const absDegrees = Math.abs(degrees);
converted.degrees = degrees + minutes / 60; let conv = absDegrees + minutes / 60;
converted.string = ((Math.round(converted.degrees * precision) / precision) + "°"); let outString = round(conv, precision) + "°";
return converted; if (isNegativeZero(degrees) || degrees < 0) {
conv = -conv;
outString = "-" + outString;
}
return {
"degrees": conv,
"string": outString
};
} }
/** /**
@ -294,28 +351,34 @@ function convDDMToDD (degrees, minutes, precision) {
* @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string)
*/ */
function convDDToDD (degrees, precision) { function convDDToDD (degrees, precision) {
const converted = new Object(); return {
converted.degrees = degrees; "degrees": degrees,
converted.string = Math.round(converted.degrees * precision) / precision + "°"; "string": round(degrees, precision) + "°"
return converted; };
} }
/** /**
* Convert Decimal Degrees to Degrees Minutes Seconds * Convert Decimal Degrees to Degrees Minutes Seconds
* @param {number} decDegrees - The input data to be converted * @param {number} decDegrees - The input data to be converted
* @param {number} precision - The precision which the result should be rounded to
* @returns {{string: string, degrees: number, minutes: number, seconds: number}} An object containing the raw converted value as separate numbers (.degrees, .minutes, .seconds), and a formatted string version (obj.string) * @returns {{string: string, degrees: number, minutes: number, seconds: number}} An object containing the raw converted value as separate numbers (.degrees, .minutes, .seconds), and a formatted string version (obj.string)
*/ */
function convDDToDMS (decDegrees) { function convDDToDMS (decDegrees, precision) {
const degrees = Math.floor(decDegrees); const absDegrees = Math.abs(decDegrees);
const minutes = Math.floor(60 * (decDegrees - degrees)); let degrees = Math.floor(absDegrees);
const seconds = Math.round(3600 * (decDegrees - degrees) - 60 * minutes); const minutes = Math.floor(60 * (absDegrees - degrees)),
seconds = round(3600 * (absDegrees - degrees) - 60 * minutes, precision);
const converted = new Object(); let outString = degrees + "° " + minutes + "' " + seconds + "\"";
converted.degrees = degrees; if (isNegativeZero(decDegrees) || decDegrees < 0) {
converted.minutes = minutes; degrees = -degrees;
converted.seconds = seconds; outString = "-" + outString;
converted.string = degrees + "° " + minutes + "' " + seconds + "\""; }
return converted; return {
"degrees": degrees,
"minutes": minutes,
"seconds": seconds,
"string": outString
};
} }
/** /**
@ -325,15 +388,21 @@ function convDDToDMS (decDegrees) {
* @returns {{string: string, degrees: number, minutes: number}} An object containing the raw converted value as separate numbers (.degrees, .minutes), and a formatted string version (obj.string) * @returns {{string: string, degrees: number, minutes: number}} An object containing the raw converted value as separate numbers (.degrees, .minutes), and a formatted string version (obj.string)
*/ */
function convDDToDDM (decDegrees, precision) { function convDDToDDM (decDegrees, precision) {
const degrees = Math.floor(decDegrees); const absDegrees = Math.abs(decDegrees);
const minutes = decDegrees - degrees; let degrees = Math.floor(absDegrees);
const decMinutes = Math.round((minutes * 60) * precision) / precision; const minutes = absDegrees - degrees,
decMinutes = round(minutes * 60, precision);
let outString = degrees + "° " + decMinutes + "'";
if (decDegrees < 0 || isNegativeZero(decDegrees)) {
degrees = -degrees;
outString = "-" + outString;
}
const converted = new Object(); return {
converted.degrees = degrees; "degrees": degrees,
converted.minutes = decMinutes; "minutes": decMinutes,
converted.string = degrees + "° " + decMinutes + "'"; "string": outString,
return converted; };
} }
/** /**
@ -348,8 +417,9 @@ export function findDirs(input, delim) {
const dirs = upperInput.match(dirExp); const dirs = upperInput.match(dirExp);
if (dirExp.test(upperInput)) { if (dirs) {
// If there's actually compass directions in the string // If there's actually compass directions
// in the input, use these to work out the direction
if (dirs.length <= 2 && dirs.length >= 1) { if (dirs.length <= 2 && dirs.length >= 1) {
if (dirs.length === 2) { if (dirs.length === 2) {
return [dirs[0], dirs[1]]; return [dirs[0], dirs[1]];
@ -366,15 +436,13 @@ export function findDirs(input, delim) {
if (!delim.includes("Direction")) { if (!delim.includes("Direction")) {
if (upperInput.includes(delim)) { if (upperInput.includes(delim)) {
const split = upperInput.split(delim); const split = upperInput.split(delim);
if (split.length > 1) { if (split.length >= 1) {
if (split[0] === "") { if (split[0] !== "") {
lat = split[1];
} else {
lat = split[0]; lat = split[0];
} }
if (split.length > 2) { if (split.length >= 2) {
if (split[2] !== "") { if (split[1] !== "") {
long = split[2]; long = split[1];
} }
} }
} }
@ -423,9 +491,9 @@ export function findDirs(input, delim) {
export function findFormat (input, delim) { export function findFormat (input, delim) {
let testData; let testData;
const mgrsPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]{1}\s?[A-HJ-NP-Z][A-HJ-NP-V]\s?[0-9\s]+/), const mgrsPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]{1}\s?[A-HJ-NP-Z][A-HJ-NP-V]\s?[0-9\s]+/),
osngPattern = new RegExp(/^[STNHO][A-HJ-Z][0-9]+$/), osngPattern = new RegExp(/^[A-HJ-Z]{2}\s+[0-9\s]+$/),
geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/), geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/),
utmPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]\s[0-9\.]+\s?[0-9\.]+$/), utmPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]\s[0-9.]+\s?[0-9.]+$/),
degPattern = new RegExp(/[°'"]/g); degPattern = new RegExp(/[°'"]/g);
input = input.trim(); input = input.trim();
if (delim !== null && delim.includes("Direction")) { if (delim !== null && delim.includes("Direction")) {
@ -452,31 +520,16 @@ export function findFormat (input, delim) {
} }
} }
// Test MGRS and Geohash // Test non-degrees formats
if (!degPattern.test(input)) { if (!degPattern.test(input)) {
const filteredInput = input.toUpperCase(); const filteredInput = input.toUpperCase().replace(delim, "");
const isMgrs = mgrsPattern.test(filteredInput); const isMgrs = mgrsPattern.test(filteredInput);
const isOsng = osngPattern.test(filteredInput); const isOsng = osngPattern.test(filteredInput);
const isGeohash = geohashPattern.test(filteredInput); const isGeohash = geohashPattern.test(filteredInput);
const isUtm = utmPattern.test(filteredInput); const isUtm = utmPattern.test(filteredInput);
if (isMgrs && (isOsng || isGeohash)) {
if (filteredInput.includes("I")) {
// Only MGRS can have an i!
return "Military Grid Reference System";
}
}
if (isUtm) { if (isUtm) {
return "Universal Transverse Mercator"; return "Universal Transverse Mercator";
} }
if (isOsng && isGeohash) {
// Geohash doesn't have A, L or O, but OSNG does.
const testExp = new RegExp(/[ALO]/g);
if (testExp.test(filteredInput)) {
return "Ordnance Survey National Grid";
} else {
return "Geohash";
}
}
if (isMgrs) { if (isMgrs) {
return "Military Grid Reference System"; return "Military Grid Reference System";
} }
@ -510,7 +563,7 @@ export function findFormat (input, delim) {
*/ */
export function findDelim (input) { export function findDelim (input) {
input = input.trim(); input = input.trim();
const delims = [",", ";", ":", " "]; const delims = [",", ";", ":"];
const testDir = input.match(/[NnEeSsWw]/g); const testDir = input.match(/[NnEeSsWw]/g);
if (testDir !== null && testDir.length > 0 && testDir.length < 3) { if (testDir !== null && testDir.length > 0 && testDir.length < 3) {
// Possibly contains a direction // Possibly contains a direction
@ -554,3 +607,22 @@ export function realDelim (delim) {
"Colon": ":" "Colon": ":"
}[delim]; }[delim];
} }
/**
* Returns true if a zero is negative
* @param {number} zero
*/
function isNegativeZero(zero) {
return zero === 0 && (1/zero < 0);
}
/**
* Rounds a number to a specified number of decimal places
* @param {number} input - The number to be rounded
* @param {precision} precision - The number of decimal places the number should be rounded to
* @returns {number}
*/
function round(input, precision) {
precision = Math.pow(10, precision);
return Math.round(input * precision) / precision;
}