diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 9078e250..abb25146 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -321,6 +321,7 @@ "Entropy", "Frequency distribution", "Chi Square", + "Haversine distance", "Detect File Type", "Scan for Embedded Files", "Disassemble x86", diff --git a/src/core/operations/HaversineDistance.mjs b/src/core/operations/HaversineDistance.mjs new file mode 100644 index 00000000..3e7bc506 --- /dev/null +++ b/src/core/operations/HaversineDistance.mjs @@ -0,0 +1,58 @@ +/** + * @author Dachande663 [dachande663@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; + +/** + * HaversineDistance operation + */ +class HaversineDistance extends Operation { + + /** + * HaversineDistance constructor + */ + constructor() { + super(); + + this.name = "Haversine distance"; + this.module = "Default"; + this.description = "Returns the distance between two pairs of GPS latitude and longitude co-ordinates in metres.

e.g. 51.487263,-0.124323, 38.9517,-77.1467"; + this.inputType = "string"; + this.outputType = "number"; + this.args = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {number} + */ + run(input, args) { + + const values = input.match(/^(-?\d+(\.\d+)?), ?(-?\d+(\.\d+)?), ?(-?\d+(\.\d+)?), ?(-?\d+(\.\d+)?)$/); + if (!values) { + throw new OperationError("Input must in the format lat1, lng1, lat2, lng2"); + } + + const lat1 = parseFloat(values[1]); + const lng1 = parseFloat(values[3]); + const lat2 = parseFloat(values[6]); + const lng2 = parseFloat(values[8]); + + const TO_RAD = Math.PI / 180; + const dLat = (lat2-lat1) * TO_RAD; + const dLng = (lng2-lng1) * TO_RAD; + const a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(lat1 * TO_RAD) * Math.cos(lat2 * TO_RAD) * Math.sin(dLng/2) * Math.sin(dLng/2); + const metres = 6371000 * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + + return metres; + + } + +} + +export default HaversineDistance; diff --git a/test/index.mjs b/test/index.mjs index f9cbac7e..e06a1470 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -44,6 +44,7 @@ import "./tests/operations/ConditionalJump"; import "./tests/operations/Register"; import "./tests/operations/Comment"; import "./tests/operations/Hash"; +import "./tests/operations/HaversineDistance"; import "./tests/operations/Hexdump"; import "./tests/operations/Image"; import "./tests/operations/MorseCode"; diff --git a/test/tests/operations/HaversineDistance.mjs b/test/tests/operations/HaversineDistance.mjs new file mode 100644 index 00000000..dc10492b --- /dev/null +++ b/test/tests/operations/HaversineDistance.mjs @@ -0,0 +1,22 @@ +/** + * Haversine distance tests. + * + * @author Dachande663 [dachande663@gmail.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +import TestRegister from "../../TestRegister"; + +TestRegister.addTests([ + { + name: "Haversine distance", + input: "51.487263,-0.124323, 38.9517,-77.1467", + expectedOutput: "5619355.701829259", + recipeConfig: [ + { + "op": "Haversine distance", + "args": [] + } + ], + } +]);