diff --git a/package-lock.json b/package-lock.json index a8d603ff..959bc6df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4197,14 +4197,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4219,20 +4217,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -4349,8 +4344,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -4362,7 +4356,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4377,7 +4370,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4489,8 +4481,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -4623,7 +4614,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -7275,6 +7265,11 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "ngeohash": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/ngeohash/-/ngeohash-0.6.0.tgz", + "integrity": "sha1-MpcT6ec9HxpG2SqrC5StuUUz9oc=" + }, "nice-try": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", diff --git a/package.json b/package.json index c6b91376..774ff6f9 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,7 @@ "loglevel-message-prefix": "^3.0.0", "moment": "^2.22.2", "moment-timezone": "^0.5.21", + "ngeohash": "^0.6.0", "node-forge": "^0.7.5", "node-md6": "^0.1.0", "notepack.io": "^2.1.3", diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 66663f4a..ab3bc486 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -289,7 +289,9 @@ "Adler-32 Checksum", "CRC-16 Checksum", "CRC-32 Checksum", - "TCP/IP Checksum" + "TCP/IP Checksum", + "To Geohash", + "From Geohash" ] }, { diff --git a/src/core/operations/FromGeohash.mjs b/src/core/operations/FromGeohash.mjs new file mode 100644 index 00000000..b70273da --- /dev/null +++ b/src/core/operations/FromGeohash.mjs @@ -0,0 +1,44 @@ +/** + * @author gchq77703 [] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import geohash from "ngeohash"; + +/** + * From Geohash operation + */ +class FromGeohash extends Operation { + + /** + * FromGeohash constructor + */ + constructor() { + super(); + + this.name = "From Geohash"; + this.module = "Hashing"; + this.description = "Converts Geohash strings into Lat/Long coordinates. For example, ww8p1r4t8 becomes 37.8324,112.5584."; + this.infoURL = "https://wikipedia.org/wiki/Geohash"; + this.inputType = "string"; + this.outputType = "string"; + this.args = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + return input.split("\n").map(line => { + const coords = geohash.decode(line); + return [coords.latitude, coords.longitude].join(","); + }).join("\n"); + } + +} + +export default FromGeohash; diff --git a/src/core/operations/ToGeohash.mjs b/src/core/operations/ToGeohash.mjs new file mode 100644 index 00000000..0e7f53ac --- /dev/null +++ b/src/core/operations/ToGeohash.mjs @@ -0,0 +1,53 @@ +/** + * @author gchq77703 [] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import geohash from "ngeohash"; + +/** + * To Geohash operation + */ +class ToGeohash extends Operation { + + /** + * ToGeohash constructor + */ + constructor() { + super(); + + this.name = "To Geohash"; + this.module = "Hashing"; + this.description = "Converts Lat/Long coordinates into a Geohash string. For example, 37.8324,112.5584 becomes ww8p1r4t8."; + this.infoURL = "https://wikipedia.org/wiki/Geohash"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Precision", + type: "number", + value: 9 + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [precision] = args; + + return input.split("\n").map(line => { + line = line.replace(/ /g, ""); + if (line === "") return ""; + return geohash.encode(...line.split(",").map(num => parseFloat(num)), precision); + }).join("\n"); + } + +} + +export default ToGeohash; diff --git a/test/index.mjs b/test/index.mjs index 8cf69732..5d0ce3ff 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -64,6 +64,8 @@ import "./tests/operations/SetUnion"; import "./tests/operations/SymmetricDifference"; import "./tests/operations/TranslateDateTimeFormat"; import "./tests/operations/Magic"; +import "./tests/operations/ToGeohash.mjs"; +import "./tests/operations/FromGeohash.mjs"; let allTestsPassing = true; const testStatusCounts = { diff --git a/test/tests/operations/FromGeohash.mjs b/test/tests/operations/FromGeohash.mjs new file mode 100644 index 00000000..2ac68c58 --- /dev/null +++ b/test/tests/operations/FromGeohash.mjs @@ -0,0 +1,55 @@ +/** + * To Geohash tests + * + * @author gchq77703 + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +import TestRegister from "../../TestRegister"; + +TestRegister.addTests([ + { + name: "From Geohash", + input: "ww8p1r4t8", + expectedOutput: "37.83238649368286,112.55838632583618", + recipeConfig: [ + { + op: "From Geohash", + args: [], + }, + ], + }, + { + name: "From Geohash", + input: "ww8p1r", + expectedOutput: "37.83416748046875,112.5604248046875", + recipeConfig: [ + { + op: "From Geohash", + args: [], + }, + ], + }, + { + name: "From Geohash", + input: "ww8", + expectedOutput: "37.265625,113.203125", + recipeConfig: [ + { + op: "From Geohash", + args: [], + }, + ], + }, + { + name: "From Geohash", + input: "w", + expectedOutput: "22.5,112.5", + recipeConfig: [ + { + op: "From Geohash", + args: [], + }, + ], + }, +]); diff --git a/test/tests/operations/ToGeohash.mjs b/test/tests/operations/ToGeohash.mjs new file mode 100644 index 00000000..b50e7280 --- /dev/null +++ b/test/tests/operations/ToGeohash.mjs @@ -0,0 +1,55 @@ +/** + * To Geohash tests + * + * @author gchq77703 + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +import TestRegister from "../../TestRegister"; + +TestRegister.addTests([ + { + name: "To Geohash", + input: "37.8324,112.5584", + expectedOutput: "ww8p1r4t8", + recipeConfig: [ + { + op: "To Geohash", + args: [9], + }, + ], + }, + { + name: "To Geohash", + input: "37.9324,-112.2584", + expectedOutput: "9w8pv3ruj", + recipeConfig: [ + { + op: "To Geohash", + args: [9], + }, + ], + }, + { + name: "To Geohash", + input: "37.8324,112.5584", + expectedOutput: "ww8", + recipeConfig: [ + { + op: "To Geohash", + args: [3], + }, + ], + }, + { + name: "To Geohash", + input: "37.9324,-112.2584", + expectedOutput: "9w8pv3rujxy5b99", + recipeConfig: [ + { + op: "To Geohash", + args: [15], + }, + ], + }, +]);