From ad4451a757f0650ee492c41a89014b36ead8993b Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 15 Jan 2019 10:13:11 +0000 Subject: [PATCH] Rewrite MGRS to use new Geodesy module. Added Ordnance Survey grid reference support --- package.json | 1 + src/core/lib/ConvertCoordinates.mjs | 43 ++++++++++++++++--- .../operations/ConvertCoordinateFormat.mjs | 25 +++++------ 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 414e4d50..4e38e4fa 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "esprima": "^4.0.1", "exif-parser": "^0.1.12", "file-saver": "^2.0.0-rc.4", + "geodesy": "^1.1.3", "highlight.js": "^9.13.1", "jquery": "^3.3.1", "js-crc": "^0.2.0", diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index 1c6ae7e2..06e40f02 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -7,7 +7,7 @@ */ import geohash from "ngeohash"; -import mgrs from "mgrs"; +import geodesy from "geodesy"; /** * Co-ordinate formats @@ -17,7 +17,19 @@ export const FORMATS = [ "Degrees Decimal Minutes", "Decimal Degrees", "Geohash", - "Military Grid Reference System" + "Military Grid Reference System", + "Ordnance Survey National Grid" +]; + +/** + * Formats that are made up of one string + * These formats skip bits like filtering delimiters and + * are outputted differently (only one output) + */ +export const STRING_FORMATS = [ + "Geohash", + "Military Grid Reference System", + "Ordnance Survey National Grid" ]; /** @@ -37,9 +49,22 @@ export function convertCoordinates (inLat, inLong, inFormat, outFormat, precisio convLat = hash.latitude.toString(); convLong = hash.longitude.toString(); } else if (inFormat === "Military Grid Reference System") { - const result = mgrs.toPoint(inLat.replace(" ", "")); - convLat = result[1]; - convLong = result[0]; + const utm = geodesy.Mgrs.parse(inLat).toUtm(); + const result = utm.toLatLonE().toString("d", 4).replace(/[^0-9.,]/g, ""); + const splitResult = result.split(","); + if (splitResult.length === 2) { + convLat = splitResult[0]; + convLong = splitResult[1]; + } + } else if (inFormat === "Ordnance Survey National Grid") { + const osng = geodesy.OsGridRef.parse(inLat); + const latlon = geodesy.OsGridRef.osGridToLatLon(osng, geodesy.LatLonEllipsoidal.datum.WGS84); + const result = latlon.toString("d", 4).replace(/[^0-9.,]/g, ""); + const splitResult = result.split(","); + if (splitResult.length === 2) { + convLat = splitResult[0]; + convLong = splitResult[1]; + } } else { convLat = convertSingleCoordinate(inLat, inFormat, "Decimal Degrees", 15).split("°"); convLong = convertSingleCoordinate(inLong, inFormat, "Decimal Degrees", 15).split("°"); @@ -49,7 +74,13 @@ export function convertCoordinates (inLat, inLong, inFormat, outFormat, precisio if (outFormat === "Geohash") { convLat = geohash.encode(parseFloat(convLat), parseFloat(convLong), precision); } else if (outFormat === "Military Grid Reference System") { - convLat = mgrs.forward([parseFloat(convLong), parseFloat(convLat)], precision); + const utm = new geodesy.LatLonEllipsoidal(parseFloat(convLat), parseFloat(convLong)).toUtm(); + const mgrs = utm.toMgrs(); + convLat = mgrs.toString(); + } else if (outFormat === "Ordnance Survey National Grid") { + const latlon = new geodesy.LatLonEllipsoidal(parseFloat(convLat), parseFloat(convLong)); + const osng = geodesy.OsGridRef.latLonToOsGrid(latlon); + convLat = osng.toString(); } else { convLat = convertSingleCoordinate(convLat.toString(), "Decimal Degrees", outFormat, precision); convLong = convertSingleCoordinate(convLong.toString(), "Decimal Degrees", outFormat, precision); diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index b9a9766c..afc95982 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -6,7 +6,7 @@ import Operation from "../Operation"; import OperationError from "../errors/OperationError"; -import {FORMATS, convertCoordinates, convertSingleCoordinate, findDelim, findFormat} from "../lib/ConvertCoordinates"; +import {FORMATS, STRING_FORMATS, convertCoordinates, convertSingleCoordinate, findDelim, findFormat} from "../lib/ConvertCoordinates"; import Utils from "../Utils"; /** @@ -22,7 +22,7 @@ class ConvertCoordinateFormat extends Operation { this.name = "Convert co-ordinate format"; this.module = "Hashing"; - this.description = "Convert geographical coordinates between different formats.

Supported formats:"; + this.description = "Convert geographical coordinates between different formats.

Supported formats:"; this.infoURL = "https://wikipedia.org/wiki/Geographic_coordinate_conversion"; this.inputType = "string"; this.outputType = "string"; @@ -107,14 +107,14 @@ class ConvertCoordinateFormat extends Operation { } } - if (inDelim === "" && (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System")) { + if (inDelim === "" && (!STRING_FORMATS.includes(inFormat))) { throw new OperationError("Could not automatically detect the input delimiter."); } // Prepare input data - if (inFormat === "Geohash" || inFormat === "Military Grid Reference System") { + if (STRING_FORMATS.includes(inFormat)) { // Geohash only has one value, so just use the input - // Replace anything that isn't a valid character in Geohash / MGRS + // Replace anything that isn't a valid character in Geohash / MGRS / OSNG inLat = input.replace(/[^A-Za-z0-9]/, ""); } else if (inDelim === "Direction Preceding") { // Split on the compass directions @@ -152,7 +152,7 @@ class ConvertCoordinateFormat extends Operation { } } - if (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System" && outDelim.includes("Direction")) { + if (!STRING_FORMATS.includes(inFormat) && outDelim.includes("Direction")) { // Match on compass directions, and store the first 2 matches for the output const dir = input.match(/[NnEeSsWw]/g); if (dir !== null) { @@ -173,14 +173,15 @@ class ConvertCoordinateFormat extends Operation { // Convert the co-ordinates if (inLat !== undefined) { if (inLong === undefined) { - if (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System") { - if (outFormat === "Geohash" || outFormat === "Military Grid Reference System"){ + if (!STRING_FORMATS.includes(inFormat)) { + if (STRING_FORMATS.includes(outFormat)){ throw new OperationError(`${outFormat} needs both a latitude and a longitude to be calculated`); } } - if (inFormat === "Geohash" || inFormat === "Military Grid Reference System") { + if (STRING_FORMATS.includes(inFormat)) { // Geohash conversion is in convertCoordinates despite needing // only one input as it needs to output two values + inLat = inLat.replace(/[^A-Za-z0-9]/g, ""); [outLat, outLong] = convertCoordinates(inLat, inLat, inFormat, outFormat, precision); } else { outLat = convertSingleCoordinate(inLat, inFormat, outFormat, precision); @@ -195,16 +196,16 @@ class ConvertCoordinateFormat extends Operation { // Output conversion results if successful if (outLat !== undefined) { let output = ""; - if (outDelim === "Direction Preceding" && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + if (outDelim === "Direction Preceding" && !STRING_FORMATS.includes(outFormat)) { output += latDir += " "; } output += outLat; - if (outDelim === "Direction Following" && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + if (outDelim === "Direction Following" && !STRING_FORMATS.includes(outFormat)) { output += " " + latDir; } output += outSeparator; - if (outLong !== undefined && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + if (outLong !== undefined && !STRING_FORMATS.includes(outFormat)) { if (outDelim === "Direction Preceding") { output += longDir + " "; }