diff --git a/src/constants.js b/src/constants.js index 5e373a0..bffeabd 100644 --- a/src/constants.js +++ b/src/constants.js @@ -31,10 +31,8 @@ export const UPDATE_REPORTS_DISTANCE_THRESHOLD = 500; // in meters // Minimal number of downvotes needed for a report to be masked export const REPORT_DOWNVOTES_THRESHOLD = 1; -// Earth radius as recommended by the International Union of Geodesy and -// Geophysics -// https://rosettacode.org/wiki/Haversine_formula -export const EARTH_RADIUS = 6371000; // in meters +// Earth radius at equator +export const EARTH_RADIUS = 6378137; // in meters // Keep reports only in a given radius around map center. export const KEEP_REPORTS_METERS_AROUND = 10000; // in meters diff --git a/src/tools/index.js b/src/tools/index.js index a1b1836..87ed4a7 100644 --- a/src/tools/index.js +++ b/src/tools/index.js @@ -1,6 +1,5 @@ import { - EARTH_RADIUS, MOCK_LOCATION_GPX_PLAYBACK_SPEED, MOCK_LOCATION_UPDATE_INTERVAL, MOCK_LOCATION_USE_GPX, @@ -15,20 +14,25 @@ if (process.env.NODE_ENV !== 'production') { mockGPX = require('mock_gpx.json'); // eslint-disable-line global-require } +/** + * Cheap distance computation based on + * https://blog.mapbox.com/fast-geodesic-approximations-with-cheap-ruler-106f229ad016. + */ export function distance(latLng1, latLng2) { - const lat1 = (latLng1[0] * Math.PI) / 180; - const lng1 = (latLng1[1] * Math.PI) / 180; + const cos = Math.cos((latLng1[0] + latLng2[0]) / 2 * Math.PI / 180); + const cos2 = 2 * cos * cos - 1; + const cos3 = 2 * cos * cos2 - cos; + const cos4 = 2 * cos * cos3 - cos2; + const cos5 = 2 * cos * cos4 - cos3; - const lat2 = (latLng2[0] * Math.PI) / 180; - const lng2 = (latLng2[1] * Math.PI) / 180; + // Multipliers for converting longitude and latitude degrees into distance + // (http://1.usa.gov/1Wb1bv7) + kx = 1000 * (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5); + ky = 1000 * (111.13209 - 0.56605 * cos2 + 0.0012 * cos4); - const a = ( - (Math.sin((lat2 - lat1) / 2.0) ** 2) - + (Math.cos(lat1) * Math.cos(lat2) * (Math.sin((lng2 - lng1) / 2.0) ** 2)) - ); - const c = 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - - return EARTH_RADIUS * c; + const dx = (latLng1[1] - latLng2[1]) * kx; + const dy = (latLng1[0] - latLng2[0]) * ky; + return Math.sqrt(dx * dx + dy * dy); } export function mockLocationRandom() {