diff --git a/package-lock.json b/package-lock.json index 50ae93321..48a38cd52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "@bimdata/viewer": "2.17.0-beta.4", "@paddle/paddle-js": "^1.6.2", "async": "^3.2.6", - "dms-conversion": "^3.1.4", "lodash": "^4.17.23", "maplibre-gl": "^5.20.1", "oidc-client-ts": "^3.5.0", @@ -6605,12 +6604,6 @@ "node": ">=8" } }, - "node_modules/dms-conversion": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/dms-conversion/-/dms-conversion-3.1.4.tgz", - "integrity": "sha512-o8umMOgrbh3dS3RJH0jbjEYGHA/mycyLbO8/vA8BIhHZhQj0to5dZwn2EchHTNa6YLv8VtOIMcN4Kv0TOVBN8g==", - "license": "Unlicense" - }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", diff --git a/package.json b/package.json index 006f82ce2..f38b7b11e 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "@bimdata/viewer": "2.17.0-beta.4", "@paddle/paddle-js": "^1.6.2", "async": "^3.2.6", - "dms-conversion": "^3.1.4", "lodash": "^4.17.23", "maplibre-gl": "^5.20.1", "oidc-client-ts": "^3.5.0", diff --git a/src/utils/location.js b/src/utils/location.js index 851c6102b..e606f0343 100644 --- a/src/utils/location.js +++ b/src/utils/location.js @@ -10,14 +10,68 @@ * DD is negative if South or West */ -import DmsCoordinates, { parseDms } from "dms-conversion"; + +// --- + +/** + * The DMS_REGEX and parseDMS/parseDD utils function are extracted from "dms-conversion" package (https://github.com/WSDOT-GIS/dms-js). + * We had to do this because vite 8 doesn't seem to handle `import DmsCoordinates, { parseDms } from "dms-conversion"` properly... + */ + +// Matches DMS DmsCoordinates +const DMS_REGEX = /^(-?\d+(?:\.\d+)?)[°:d]?\s?(?:(\d+(?:\.\d+)?)['′ʹ:]?\s?(?:(\d+(?:\.\d+)?)["″ʺ]?)?)?\s?([NSEW])?/i; + +const truncate = n => n > 0 ? Math.floor(n) : Math.ceil(n); + +/** + * 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. + */ +function parseDMS(dmsStr) { + let output = NaN; + const dmsMatch = DMS_REGEX.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; +} + + /** + * + * @param {Number} ddValue A number (coordinate) in decimal degrees + * @param {String} type Either "latitude" or "longitude" + * @returns {Array} DMS array + */ +function parseDD(ddValue, type) { + const direction = type === "longitude" + ? ddValue < 0 ? "W" : "E" + : ddValue < 0 ? "S" : "N"; + const absDD = Math.abs(ddValue); + const degrees = truncate(absDD); + const minutes = truncate((absDD - degrees) * 60); + const seconds = (absDD - degrees - minutes / 60) * Math.pow(60, 2); + return [degrees, minutes, seconds, direction]; +} + +// --- /** * Convert an array of DMS coordinate values into DD system equivalent. * * @param {Array} param DMS coordinate - * @param {String} param either latitude or longitude + * @param {String} type either "latitude" or "longitude" * @returns {Number} DD coordinate */ function DMS2DD([degrees, minutes, seconds, secondsFraction = 0], type) { @@ -44,7 +98,7 @@ function DMS2DD([degrees, minutes, seconds, secondsFraction = 0], type) { } seconds += secondsFraction/1000000; const dmsString = `${degrees}°${minutes}′${seconds}″ ${direction}`; - return parseDms(dmsString); + return parseDMS(dmsString); } /** @@ -55,16 +109,14 @@ function DMS2DD([degrees, minutes, seconds, secondsFraction = 0], type) { * @returns {[Array, Array]} latitude and longitude DMS coordinate */ function DD2DMS(lat, long) { - const dmsCoords = new DmsCoordinates(lat, long); - const { longitude, latitude } = dmsCoords.dmsArrays; - let [latD, latM, latS, latDir] = latitude; + let [latD, latM, latS, latDir] = parseDD(lat, "latitude"); if (latDir == "S") { latD *= -1; latM *= -1; latS *= -1; } - let [longD, longM, longS, longDir] = longitude; + let [longD, longM, longS, longDir] = parseDD(long, "longitude"); if (longDir == "W") { longD *= -1; longM *= -1; diff --git a/tests/unit/utils/location.spec.js b/tests/unit/utils/location.spec.js index 4c94cf4e5..2908554f2 100644 --- a/tests/unit/utils/location.spec.js +++ b/tests/unit/utils/location.spec.js @@ -8,22 +8,22 @@ describe("DMS2DD location convert", () => { it("Should work with valid negative values", () => { expect(DMS2DD([-33, -52, -11.438039999997507])).toBeCloseTo(-33.8698439); }); + it("Should work with valid positive values", () => { expect(DMS2DD([55, 45, 1.948320000002468])).toBeCloseTo(55.7505412); }); - it("Should work with string", () => { // We have strings in some old IFCs + it("Should work with string", () => { // We have strings in some old IFCs expect(DMS2DD(["55", "45", "1.948320000002468"])).toBeCloseTo(55.7505412); }); }); - describe("DD2DMS location convert", () => { it("Should work with legacy errors values", () => { expect(DD2DMS(-33.8698439, 151.2082848 )).toStrictEqual([[-33, -52, -11.438039999997507], [151, 12, 29.82528000000453]]); }); + it("Should work with valid positive values", () => { expect(DD2DMS(55.7505412, 37.6174782)).toStrictEqual([[55, 45, 1.948320000002468], [37, 37, 2.9215200000026087]]); }); - });