2019-01-11 11:59:13 +00:00
|
|
|
/**
|
|
|
|
* Co-ordinate conversion resources.
|
|
|
|
*
|
|
|
|
* @author j433866 [j433866@gmail.com]
|
|
|
|
* @copyright Crown Copyright 2019
|
|
|
|
* @license Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
import geohash from "ngeohash";
|
2019-01-15 10:13:11 +00:00
|
|
|
import geodesy from "geodesy";
|
2019-01-11 11:59:13 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Co-ordinate formats
|
|
|
|
*/
|
|
|
|
export const FORMATS = [
|
|
|
|
"Degrees Minutes Seconds",
|
|
|
|
"Degrees Decimal Minutes",
|
|
|
|
"Decimal Degrees",
|
|
|
|
"Geohash",
|
2019-01-15 10:13:11 +00:00
|
|
|
"Military Grid Reference System",
|
2019-01-15 15:55:49 +00:00
|
|
|
"Ordnance Survey National Grid",
|
|
|
|
"Universal Transverse Mercator"
|
2019-01-15 10:13:11 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
/**
|
2019-01-15 15:55:49 +00:00
|
|
|
* Formats that should be passed to Geodesy module as-is
|
|
|
|
* Spaces are still removed
|
2019-01-15 10:13:11 +00:00
|
|
|
*/
|
2019-01-15 15:55:49 +00:00
|
|
|
const NO_CHANGE = [
|
2019-01-15 10:13:11 +00:00
|
|
|
"Geohash",
|
|
|
|
"Military Grid Reference System",
|
2019-01-15 15:55:49 +00:00
|
|
|
"Ordnance Survey National Grid",
|
|
|
|
"Universal Transverse Mercator",
|
2019-01-11 11:59:13 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert a given latitude and longitude into a different format.
|
2019-01-15 15:55:49 +00:00
|
|
|
* @param {string} input - Input string to be converted
|
2019-01-11 11:59:13 +00:00
|
|
|
* @param {string} inFormat - Format of the input coordinates
|
2019-01-15 15:55:49 +00:00
|
|
|
* @param {string} inDelim - The delimiter splitting the lat/long of the input
|
2019-01-11 11:59:13 +00:00
|
|
|
* @param {string} outFormat - Format to convert to
|
2019-01-15 15:55:49 +00:00
|
|
|
* @param {string} outDelim - The delimiter to separate the output with
|
|
|
|
* @param {string} includeDir - Whether or not to include the compass direction in the output
|
2019-01-11 11:59:13 +00:00
|
|
|
* @param {number} precision - Precision of the result
|
2019-01-15 15:55:49 +00:00
|
|
|
* @returns {string} A formatted string of the converted co-ordinates
|
2019-01-11 11:59:13 +00:00
|
|
|
*/
|
2019-01-15 15:55:49 +00:00
|
|
|
export function convertCoordinates (input, inFormat, inDelim, outFormat, outDelim, includeDir, precision) {
|
|
|
|
let isPair = false,
|
|
|
|
split,
|
|
|
|
latlon,
|
|
|
|
conv,
|
|
|
|
inLatDir,
|
|
|
|
inLongDir;
|
|
|
|
|
|
|
|
if (inDelim === "Auto") {
|
|
|
|
inDelim = findDelim(input);
|
|
|
|
} else {
|
|
|
|
inDelim = realDelim(inDelim);
|
|
|
|
}
|
|
|
|
if (inFormat === "Auto") {
|
|
|
|
inFormat = findFormat(input, inDelim);
|
|
|
|
if (inFormat === null) {
|
|
|
|
throw "Unable to detect the input format automatically.";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (inDelim === null && !inFormat.includes("Direction")) {
|
|
|
|
throw "Unable to detect the input delimiter automatically.";
|
|
|
|
}
|
|
|
|
outDelim = realDelim(outDelim);
|
|
|
|
|
|
|
|
if (!NO_CHANGE.includes(inFormat)) {
|
|
|
|
split = input.split(inDelim);
|
|
|
|
if (split.length > 1) {
|
|
|
|
isPair = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
input = input.replace(inDelim, "");
|
|
|
|
isPair = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inFormat.includes("Degrees")) {
|
|
|
|
[inLatDir, inLongDir] = findDirs(input, inDelim);
|
|
|
|
}
|
|
|
|
|
2019-01-11 11:59:13 +00:00
|
|
|
if (inFormat === "Geohash") {
|
2019-01-15 15:55:49 +00:00
|
|
|
const hash = geohash.decode(input.replace(/[^A-Za-z0-9]/g, ""));
|
|
|
|
latlon = new geodesy.LatLonEllipsoidal(hash.latitude, hash.longitude);
|
2019-01-11 11:59:13 +00:00
|
|
|
} else if (inFormat === "Military Grid Reference System") {
|
2019-01-15 15:55:49 +00:00
|
|
|
const utm = geodesy.Mgrs.parse(input.replace(/[^A-Za-z0-9]/g, "")).toUtm();
|
|
|
|
latlon = utm.toLatLonE();
|
2019-01-15 10:13:11 +00:00
|
|
|
} else if (inFormat === "Ordnance Survey National Grid") {
|
2019-01-15 15:55:49 +00:00
|
|
|
const osng = geodesy.OsGridRef.parse(input.replace(/[^A-Za-z0-9]/g, ""));
|
|
|
|
latlon = geodesy.OsGridRef.osGridToLatLon(osng);
|
|
|
|
} else if (inFormat === "Universal Transverse Mercator") {
|
|
|
|
if (/^[\d]{2}[A-Za-z]/.test(input)) {
|
|
|
|
input = input.slice(0, 2) + " " + input.slice(2);
|
|
|
|
}
|
|
|
|
const utm = geodesy.Utm.parse(input);
|
|
|
|
latlon = utm.toLatLonE();
|
|
|
|
} else if (inFormat === "Degrees Minutes Seconds") {
|
|
|
|
if (isPair) {
|
|
|
|
split[0] = split[0].replace(/[NnEeSsWw]/g, "").trim();
|
|
|
|
split[1] = split[1].replace(/[NnEeSsWw]/g, "").trim();
|
|
|
|
const splitLat = split[0].split(/[°′″'"\s]/g),
|
|
|
|
splitLong = split[1].split(/[°′″'"\s]/g);
|
|
|
|
|
|
|
|
if (splitLat.length >= 3 && splitLong.length >= 3) {
|
|
|
|
const lat = convDMSToDD(parseFloat(splitLat[0]), parseFloat(splitLat[1]), parseFloat(splitLat[2]), 10);
|
|
|
|
const long = convDMSToDD(parseFloat(splitLong[0]), parseFloat(splitLong[1]), parseFloat(splitLong[2]), 10);
|
|
|
|
latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Create a new latlon object anyway, but we can ignore the lon value
|
|
|
|
split[0] = split[0].replace(/[NnEeSsWw]/g, "").trim();
|
|
|
|
const splitLat = split[0].split(/[°′″'"\s]/g);
|
|
|
|
if (splitLat.length >= 3) {
|
|
|
|
const lat = convDMSToDD(parseFloat(splitLat[0]), parseFloat(splitLat[1]), parseFloat(splitLat[2]));
|
|
|
|
latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lat.degrees);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (inFormat === "Degrees Decimal Minutes") {
|
|
|
|
if (isPair) {
|
|
|
|
const splitLat = splitInput(split[0]);
|
|
|
|
const splitLong = splitInput(split[1]);
|
|
|
|
if (splitLat.length !== 2 || splitLong.length !== 2) {
|
|
|
|
throw "Invalid co-ordinate format for Degrees Decimal Minutes.";
|
|
|
|
}
|
|
|
|
const lat = convDDMToDD(splitLat[0], splitLat[1], 10);
|
|
|
|
const long = convDDMToDD(splitLong[0], splitLong[1], 10);
|
|
|
|
latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees);
|
|
|
|
} else {
|
|
|
|
const splitLat = splitInput(input);
|
|
|
|
if (splitLat.length !== 2) {
|
|
|
|
throw "Invalid co-ordinate format for Degrees Decimal Minutes.";
|
|
|
|
}
|
|
|
|
const lat = convDDMToDD(splitLat[0], splitLat[1], 10);
|
|
|
|
latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lat.degrees);
|
|
|
|
}
|
|
|
|
} else if (inFormat === "Decimal Degrees") {
|
|
|
|
if (isPair) {
|
|
|
|
const splitLat = splitInput(split[0]);
|
|
|
|
const splitLong = splitInput(split[1]);
|
|
|
|
if (splitLat.length !== 1 || splitLong.length !== 1) {
|
|
|
|
throw "Invalid co-ordinate format for Decimal Degrees.";
|
|
|
|
}
|
|
|
|
latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLong[0]);
|
|
|
|
} else {
|
|
|
|
const splitLat = splitInput(split[0]);
|
|
|
|
if (splitLat.length !== 1) {
|
|
|
|
throw "Invalid co-ordinate format for Decimal Degrees.";
|
|
|
|
}
|
|
|
|
latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLat[0]);
|
2019-01-15 10:13:11 +00:00
|
|
|
}
|
2019-01-11 11:59:13 +00:00
|
|
|
} else {
|
2019-01-15 15:55:49 +00:00
|
|
|
throw "Invalid input co-ordinate format selected.";
|
2019-01-11 11:59:13 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 15:55:49 +00:00
|
|
|
// Everything is now a geodesy latlon object
|
|
|
|
if (outFormat === "Decimal Degrees") {
|
|
|
|
conv = latlon.toString("d", precision);
|
|
|
|
if (!isPair) {
|
|
|
|
conv = conv.split(",")[0];
|
|
|
|
}
|
|
|
|
} else if (outFormat === "Degrees Decimal Minutes") {
|
|
|
|
conv = latlon.toString("dm", precision);
|
|
|
|
if (!isPair) {
|
|
|
|
conv = conv.split(",")[0];
|
|
|
|
}
|
|
|
|
} else if (outFormat === "Degrees Minutes Seconds") {
|
|
|
|
conv = latlon.toString("dms", precision);
|
|
|
|
if (!isPair) {
|
|
|
|
conv = conv.split(",")[0];
|
|
|
|
}
|
|
|
|
} else if (outFormat === "Geohash") {
|
|
|
|
conv = geohash.encode(latlon.lat.toString(), latlon.lon.toString(), precision);
|
2019-01-11 11:59:13 +00:00
|
|
|
} else if (outFormat === "Military Grid Reference System") {
|
2019-01-15 15:55:49 +00:00
|
|
|
const utm = latlon.toUtm();
|
2019-01-15 10:13:11 +00:00
|
|
|
const mgrs = utm.toMgrs();
|
2019-01-15 15:55:49 +00:00
|
|
|
conv = mgrs.toString(precision);
|
2019-01-15 10:13:11 +00:00
|
|
|
} else if (outFormat === "Ordnance Survey National Grid") {
|
|
|
|
const osng = geodesy.OsGridRef.latLonToOsGrid(latlon);
|
2019-01-15 15:55:49 +00:00
|
|
|
if (osng.toString() === "") {
|
|
|
|
throw "Could not convert co-ordinates to OS National Grid. Are the co-ordinates in range?";
|
2019-01-15 10:25:49 +00:00
|
|
|
}
|
2019-01-15 15:55:49 +00:00
|
|
|
conv = osng.toString(precision);
|
|
|
|
} else if (outFormat === "Universal Transverse Mercator") {
|
|
|
|
const utm = latlon.toUtm();
|
|
|
|
conv = utm.toString(precision);
|
2019-01-11 11:59:13 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 15:55:49 +00:00
|
|
|
if (conv === undefined) {
|
|
|
|
throw "Error converting co-ordinates.";
|
|
|
|
}
|
|
|
|
if (outFormat.includes("Degrees")) {
|
|
|
|
let [latDir, longDir] = findDirs(conv, outDelim);
|
|
|
|
if (inLatDir !== undefined) {
|
|
|
|
latDir = inLatDir;
|
|
|
|
}
|
|
|
|
if (inLongDir !== undefined) {
|
|
|
|
longDir = inLongDir;
|
|
|
|
}
|
|
|
|
// 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 = "";
|
|
|
|
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") {
|
|
|
|
outConv += latDir + " ";
|
|
|
|
}
|
|
|
|
outConv += splitConv[0];
|
|
|
|
if (includeDir === "After") {
|
|
|
|
outConv += " " + latDir;
|
|
|
|
}
|
|
|
|
outConv += outDelim;
|
|
|
|
if (includeDir === "Before") {
|
|
|
|
outConv += longDir + " ";
|
|
|
|
}
|
|
|
|
outConv += splitConv[1];
|
|
|
|
if (includeDir === "After") {
|
|
|
|
outConv += " " + longDir;
|
|
|
|
}
|
|
|
|
}
|
2019-01-11 11:59:13 +00:00
|
|
|
}
|
2019-01-15 15:55:49 +00:00
|
|
|
conv = outConv;
|
|
|
|
}
|
2019-01-11 11:59:13 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 15:55:49 +00:00
|
|
|
return conv;
|
2019-01-11 11:59:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-01-15 15:55:49 +00:00
|
|
|
* Split up the input using a space or degrees signs, and sanitise the result
|
2019-01-11 11:59:13 +00:00
|
|
|
* @param {string} input - The input data to be split
|
|
|
|
* @returns {number[]} An array of the different items in the string, stored as floats
|
|
|
|
*/
|
|
|
|
function splitInput (input){
|
|
|
|
const split = [];
|
|
|
|
|
2019-01-15 15:55:49 +00:00
|
|
|
input.split(/[°′″'"\s]/).forEach(item => {
|
2019-01-11 11:59:13 +00:00
|
|
|
// Remove any character that isn't a digit
|
|
|
|
item = item.replace(/[^0-9.-]/g, "");
|
|
|
|
if (item.length > 0){
|
2019-01-15 15:55:49 +00:00
|
|
|
split.push(parseFloat(item));
|
2019-01-11 11:59:13 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
return split;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert Degrees Minutes Seconds to Decimal Degrees
|
|
|
|
* @param {number} degrees - The degrees of the input co-ordinates
|
|
|
|
* @param {number} minutes - The minutes of the input co-ordinates
|
|
|
|
* @param {number} seconds - The seconds of the input co-ordinates
|
|
|
|
* @param {number} precision - The precision the result should be rounded to
|
|
|
|
* @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){
|
|
|
|
const converted = new Object();
|
|
|
|
converted.degrees = degrees + (minutes / 60) + (seconds / 3600);
|
|
|
|
converted.string = (Math.round(converted.degrees * precision) / precision) + "°";
|
|
|
|
return converted;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert Decimal Degrees Minutes to Decimal Degrees
|
|
|
|
* @param {number} degrees - The input degrees to be converted
|
|
|
|
* @param {number} minutes - The input minutes to be converted
|
|
|
|
* @param {number} precision - The precision which the result should be rounded to
|
|
|
|
* @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) {
|
|
|
|
const converted = new Object();
|
|
|
|
converted.degrees = degrees + minutes / 60;
|
|
|
|
converted.string = ((Math.round(converted.degrees * precision) / precision) + "°");
|
|
|
|
return converted;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert Decimal Degrees to Decimal Degrees
|
|
|
|
* Doesn't affect the input, just puts it into an object
|
|
|
|
* @param {number} degrees - The input degrees to be converted
|
|
|
|
* @param {number} precision - The precision which the result should be rounded to
|
|
|
|
* @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) {
|
|
|
|
const converted = new Object();
|
|
|
|
converted.degrees = degrees;
|
|
|
|
converted.string = Math.round(converted.degrees * precision) / precision + "°";
|
|
|
|
return converted;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert Decimal Degrees to Degrees Minutes Seconds
|
|
|
|
* @param {number} decDegrees - The input data to be converted
|
|
|
|
* @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) {
|
|
|
|
const degrees = Math.floor(decDegrees);
|
|
|
|
const minutes = Math.floor(60 * (decDegrees - degrees));
|
|
|
|
const seconds = Math.round(3600 * (decDegrees - degrees) - 60 * minutes);
|
|
|
|
|
|
|
|
const converted = new Object();
|
|
|
|
converted.degrees = degrees;
|
|
|
|
converted.minutes = minutes;
|
|
|
|
converted.seconds = seconds;
|
|
|
|
converted.string = degrees + "° " + minutes + "' " + seconds + "\"";
|
|
|
|
return converted;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert Decimal Degrees to Degrees Decimal Minutes
|
|
|
|
* @param {number} decDegrees - The input degrees to be converted
|
|
|
|
* @param {number} precision - The precision the input data should be rounded to
|
|
|
|
* @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) {
|
|
|
|
const degrees = Math.floor(decDegrees);
|
|
|
|
const minutes = decDegrees - degrees;
|
|
|
|
const decMinutes = Math.round((minutes * 60) * precision) / precision;
|
|
|
|
|
|
|
|
const converted = new Object();
|
|
|
|
converted.degrees = degrees;
|
|
|
|
converted.minutes = decMinutes;
|
|
|
|
converted.string = degrees + "° " + decMinutes + "'";
|
|
|
|
return converted;
|
|
|
|
}
|
2019-01-14 11:49:57 +00:00
|
|
|
|
|
|
|
/**
|
2019-01-15 15:55:49 +00:00
|
|
|
* Finds and returns the compass directions in an input string
|
|
|
|
* @param {string} input - The input co-ordinates containing the direction
|
|
|
|
* @param {string} delim - The delimiter separating latitide and longitude
|
|
|
|
* @returns {string[]} String array containing the latitude and longitude directions
|
|
|
|
*/
|
|
|
|
export function findDirs(input, delim) {
|
|
|
|
const upperInput = input.toUpperCase();
|
|
|
|
const dirExp = new RegExp(/[NESW]/g);
|
|
|
|
|
|
|
|
const dirs = upperInput.match(dirExp);
|
|
|
|
|
|
|
|
if (dirExp.test(upperInput)) {
|
|
|
|
// If there's actually compass directions in the string
|
|
|
|
if (dirs.length <= 2 && dirs.length >= 1) {
|
|
|
|
if (dirs.length === 2) {
|
|
|
|
return [dirs[0], dirs[1]];
|
|
|
|
} else {
|
|
|
|
return [dirs[0], ""];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Nothing was returned, so guess the directions
|
|
|
|
let lat = upperInput,
|
|
|
|
long,
|
|
|
|
latDir = "",
|
|
|
|
longDir = "";
|
|
|
|
if (!delim.includes("Direction")) {
|
|
|
|
if (upperInput.includes(delim)) {
|
|
|
|
const split = upperInput.split(delim);
|
|
|
|
if (split.length > 1) {
|
|
|
|
if (split[0] === "") {
|
|
|
|
lat = split[1];
|
|
|
|
} else {
|
|
|
|
lat = split[0];
|
|
|
|
}
|
|
|
|
if (split.length > 2) {
|
|
|
|
if (split[2] !== "") {
|
|
|
|
long = split[2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const split = upperInput.split(dirExp);
|
|
|
|
if (split.length > 1) {
|
|
|
|
if (split[0] === "") {
|
|
|
|
lat = split[1];
|
|
|
|
} else {
|
|
|
|
lat = split[0];
|
|
|
|
}
|
|
|
|
if (split.length > 2) {
|
|
|
|
if (split[2] !== "") {
|
|
|
|
long = split[2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (lat) {
|
|
|
|
lat = parseFloat(lat);
|
|
|
|
if (lat < 0) {
|
|
|
|
latDir = "S";
|
|
|
|
} else {
|
|
|
|
latDir = "N";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (long) {
|
|
|
|
long = parseFloat(long);
|
|
|
|
if (long < 0) {
|
|
|
|
longDir = "W";
|
|
|
|
} else {
|
|
|
|
longDir = "E";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return [latDir, longDir];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Detects the co-ordinate format of the input data
|
2019-01-14 11:49:57 +00:00
|
|
|
* @param {string} input - The input data whose format we need to detect
|
|
|
|
* @param {string} delim - The delimiter separating the data in input
|
|
|
|
* @returns {string} The input format
|
|
|
|
*/
|
|
|
|
export function findFormat (input, delim) {
|
|
|
|
let testData;
|
2019-01-15 15:55:49 +00:00
|
|
|
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]+$/),
|
|
|
|
geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/),
|
|
|
|
utmPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]\s[0-9\.]+\s?[0-9\.]+$/),
|
|
|
|
degPattern = new RegExp(/[°'"]/g);
|
|
|
|
input = input.trim();
|
|
|
|
if (delim !== null && delim.includes("Direction")) {
|
2019-01-14 11:49:57 +00:00
|
|
|
const split = input.split(/[NnEeSsWw]/);
|
2019-01-15 15:55:49 +00:00
|
|
|
if (split.length > 1) {
|
2019-01-14 11:49:57 +00:00
|
|
|
if (split[0] === "") {
|
|
|
|
testData = split[1];
|
|
|
|
} else {
|
|
|
|
testData = split[0];
|
|
|
|
}
|
|
|
|
}
|
2019-01-15 15:55:49 +00:00
|
|
|
} else if (delim !== null && delim !== "") {
|
|
|
|
if (input.includes(delim)) {
|
|
|
|
const split = input.split(delim);
|
|
|
|
if (split.length > 1) {
|
|
|
|
if (split[0] === "") {
|
|
|
|
testData = split[1];
|
|
|
|
} else {
|
|
|
|
testData = split[0];
|
|
|
|
}
|
2019-01-14 11:49:57 +00:00
|
|
|
}
|
2019-01-15 15:55:49 +00:00
|
|
|
} else {
|
|
|
|
testData = input;
|
2019-01-14 11:49:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test MGRS and Geohash
|
2019-01-15 15:55:49 +00:00
|
|
|
if (!degPattern.test(input)) {
|
|
|
|
const filteredInput = input.toUpperCase();
|
|
|
|
const isMgrs = mgrsPattern.test(filteredInput);
|
|
|
|
const isOsng = osngPattern.test(filteredInput);
|
|
|
|
const isGeohash = geohashPattern.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) {
|
|
|
|
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) {
|
2019-01-14 11:49:57 +00:00
|
|
|
return "Military Grid Reference System";
|
2019-01-15 15:55:49 +00:00
|
|
|
}
|
|
|
|
if (isOsng) {
|
|
|
|
return "Ordnance Survey National Grid";
|
|
|
|
}
|
|
|
|
if (isGeohash) {
|
2019-01-14 11:49:57 +00:00
|
|
|
return "Geohash";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test DMS/DDM/DD formats
|
|
|
|
if (testData !== undefined) {
|
|
|
|
const split = splitInput(testData);
|
2019-01-14 14:58:41 +00:00
|
|
|
switch (split.length){
|
|
|
|
case 3:
|
|
|
|
return "Degrees Minutes Seconds";
|
|
|
|
case 2:
|
|
|
|
return "Degrees Decimal Minutes";
|
|
|
|
case 1:
|
|
|
|
return "Decimal Degrees";
|
2019-01-14 11:49:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Automatically find the delimeter type from the given input
|
|
|
|
* @param {string} input
|
|
|
|
* @returns {string} Delimiter type
|
|
|
|
*/
|
|
|
|
export function findDelim (input) {
|
|
|
|
input = input.trim();
|
2019-01-15 15:55:49 +00:00
|
|
|
const delims = [",", ";", ":", " "];
|
2019-01-14 11:49:57 +00:00
|
|
|
const testDir = input.match(/[NnEeSsWw]/g);
|
|
|
|
if (testDir !== null && testDir.length > 0 && testDir.length < 3) {
|
2019-01-14 14:58:41 +00:00
|
|
|
// Possibly contains a direction
|
2019-01-14 11:49:57 +00:00
|
|
|
const splitInput = input.split(/[NnEeSsWw]/);
|
|
|
|
if (splitInput.length <= 3 && splitInput.length > 0) {
|
2019-01-14 14:58:41 +00:00
|
|
|
// If there's 3 splits (one should be empty), then assume we have directions
|
2019-01-14 11:49:57 +00:00
|
|
|
if (splitInput[0] === "") {
|
|
|
|
return "Direction Preceding";
|
|
|
|
} else if (splitInput[splitInput.length - 1] === "") {
|
|
|
|
return "Direction Following";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-14 13:00:14 +00:00
|
|
|
|
|
|
|
// Loop through the standard delimiters, and try to find them in the input
|
2019-01-14 11:49:57 +00:00
|
|
|
for (let i = 0; i < delims.length; i++) {
|
|
|
|
const delim = delims[i];
|
|
|
|
if (input.includes(delim)) {
|
|
|
|
const splitInput = input.split(delim);
|
|
|
|
if (splitInput.length <= 3 && splitInput.length > 0) {
|
2019-01-14 14:58:41 +00:00
|
|
|
// Don't want to try and convert more than 2 co-ordinates
|
2019-01-14 11:49:57 +00:00
|
|
|
return delim;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2019-01-15 15:55:49 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the real string for a delimiter name.
|
|
|
|
* @param {string} delim The delimiter to be matched
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
export function realDelim (delim) {
|
|
|
|
return {
|
|
|
|
"Auto": "Auto",
|
|
|
|
"Space": " ",
|
|
|
|
"\\n": "\n",
|
|
|
|
"Comma": ",",
|
|
|
|
"Semi-colon": ";",
|
|
|
|
"Colon": ":"
|
|
|
|
}[delim];
|
|
|
|
}
|