Skip to content

Commit

Permalink
reimplementation of getDistance and getCenter
Browse files Browse the repository at this point in the history
  • Loading branch information
manuelbieh committed May 23, 2019
1 parent d4491e1 commit e7d48cf
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 3 deletions.
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ tabWidth: 4
trailingComma: es5
semi: true
arrowParens: always
quoteProps: consistent

overrides:
- files: "*.json"
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Breaking Changes in 3.0.0

- removed limitation to 8 decimal places in decimal coordinates
- removed artificial limitation to 8 decimal places in decimal coordinates
- `sexagesimal2decimal` was renamed to `sexagesimalToDecimal`
- `getCenter()` is not returning the distance in addition to the center anymore
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { LongitudeKeys, LatitudeKeys, AltitudeKeys } from './types';

export const sexagesimalPattern = /^([0-9]{1,3})°\s*([0-9]{1,3}(?:\.(?:[0-9]{1,2}))?)'\s*(([0-9]{1,3}(\.([0-9]{1,4}))?)"\s*)?([NEOSW]?)$/;
export const radius = 6378137;
export const earthRadius = 6378137;
export const minLat = -90;
export const maxLat = 90;
export const minLon = -180;
Expand Down
58 changes: 58 additions & 0 deletions src/getCenter.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import getCenter from './getCenter';

const cities = {
Berlin: {
latitude: 52.518611,
longitude: 13.408056,
},
Boston: {
latitude: 42.357778,
longitude: '71° 3\' 34" W',
},
Dortmund: {
latitude: '51° 31\' 10.11" N',
longitude: '7° 28\' 01" E',
},
London: {
latitude: "51° 31' N",
longitude: "0° 7' W",
},
Manchester: {
latitude: "53° 29' N",
longitude: "2° 14' W",
},
NewYorkCity: {
latitude: 40.715517,
longitude: -73.9991,
},
SanFrancisco: {
latitude: 37.774514,
longitude: -122.418079,
},
Sydney: [151.210046, -33.869085],
Moscow: {
latitude: 55.751667,
longitude: 37.617778,
},
};

describe('getCenter', () => {
it('gets the center of two points', () => {
expect(getCenter([cities.Berlin, cities.Moscow])).toEqual({
longitude: 25.0332388360222,
latitude: 54.74368339960522,
});
expect(getCenter([cities.Sydney, cities.SanFrancisco])).toEqual({
longitude: -166.9272249630353,
latitude: 2.6764932317022576,
});
});

it('gets the center of multiple points', () => {
const values = Object.values(cities);
expect(getCenter(values)).toEqual({
latitude: 65.41916196002177,
longitude: -28.01313266917171,
});
});
});
39 changes: 39 additions & 0 deletions src/getCenter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import toRad from './toRad';
import toDeg from './toDeg';
import getLatitude from './getLatitude';
import getLongitude from './getLongitude';
import { GeolibInputCoordinates } from './types';

// Calculates the center of a collection of points
const getCenter = (points: GeolibInputCoordinates[]) => {
if (Array.isArray(points) === false || points.length === 0) {
return false;
}

const numberOfPoints = points.length;

const sum = points.reduce(
(acc, point) => {
const pointLat = toRad(getLatitude(point));
const pointLon = toRad(getLongitude(point));

return {
X: acc.X + Math.cos(pointLat) * Math.cos(pointLon),
Y: acc.Y + Math.cos(pointLat) * Math.sin(pointLon),
Z: acc.Z + Math.sin(pointLat),
};
},
{ X: 0, Y: 0, Z: 0 }
);

const X = sum.X / numberOfPoints;
const Y = sum.Y / numberOfPoints;
const Z = sum.Z / numberOfPoints;

return {
longitude: toDeg(Math.atan2(Y, X)),
latitude: toDeg(Math.atan2(Z, Math.sqrt(X * X + Y * Y))),
};
};

export default getCenter;
31 changes: 31 additions & 0 deletions src/getDistance.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import getDistance from './getDistance';

describe('getDistance', () => {
it('calculates the distance between any two points', () => {
expect(
getDistance(
{ latitude: 52.518611, longitude: 13.408056 },
{ latitude: 51.519475, longitude: 7.46694444 }
)
).toEqual(421786);

expect(
getDistance(
{ latitude: 52.518611, longitude: 13.408056 },
{ latitude: 51.519475, longitude: 7.46694444 },
100
)
).toEqual(421800);

expect(
getDistance(
{ latitude: 37.774514, longitude: -122.418079 },
{ latitude: 51.519475, longitude: 7.46694444 }
)
).toEqual(8967172);

expect(
getDistance([-122.418079, 37.774514], [7.46694444, 51.519475])
).toEqual(8967172);
});
});
34 changes: 34 additions & 0 deletions src/getDistance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import getLatitude from './getLatitude';
import getLongitude from './getLongitude';
import toRad from './toRad';
import { earthRadius } from './constants';
import { GeolibInputCoordinates } from './types';

// Calculates the distance between two points.
// This method is simple but also more inaccurate
const getDistance = (
from: GeolibInputCoordinates,
to: GeolibInputCoordinates,
accuracy: number = 1
) => {
accuracy =
typeof accuracy !== 'undefined' && !isNaN(accuracy)
? Math.floor(accuracy)
: 1;

const distance = Math.round(
Math.acos(
Math.sin(toRad(getLatitude(to))) *
Math.sin(toRad(getLatitude(from))) +
Math.cos(toRad(getLatitude(to))) *
Math.cos(toRad(getLatitude(from))) *
Math.cos(
toRad(getLongitude(from)) - toRad(getLongitude(to))
)
) * earthRadius
);

return Math.floor(Math.round(distance / accuracy) * accuracy);
};

export default getDistance;
2 changes: 1 addition & 1 deletion src/getLatitude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { latitudeKeys } from './constants';
import getCoordinateKey from './getCoordinateKey';
import toDecimal from './toDecimal';

const getLatitude = (point: GeolibInputCoordinates, raw: boolean) => {
const getLatitude = (point: GeolibInputCoordinates, raw?: boolean) => {
const latKey = getCoordinateKey(point, latitudeKeys);

if (typeof latKey === 'undefined' || latKey === null) {
Expand Down
23 changes: 23 additions & 0 deletions src/getLongitude.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import getLongitude from './getLongitude';

describe('getLongitude', () => {
it('gets the longitude for a point', () => {
expect(getLongitude({ lng: 1 })).toEqual(1);
});

it('converts the longitude for a point to decimal', () => {
expect(getLongitude({ lng: "71° 0'" })).toEqual(71);
});

it('gets the longitude from a GeoJSON array', () => {
expect(getLongitude([1, 2])).toEqual(1);
});

it('does not convert to decimal if second parameter is set to true', () => {
expect(getLongitude({ lng: "71° 0'" }, true)).toEqual("71° 0'");
});

it('gets the longitude from a GeoJSON array without conversion', () => {
expect(getLongitude(["71° 0'", "71° 0'"], true)).toEqual("71° 0'");
});
});
18 changes: 18 additions & 0 deletions src/getLongitude.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { GeolibInputCoordinates, LongitudeKeys } from './types';
import { longitudeKeys } from './constants';
import getCoordinateKey from './getCoordinateKey';
import toDecimal from './toDecimal';

const getLongitude = (point: GeolibInputCoordinates, raw?: boolean) => {
const latKey = getCoordinateKey(point, longitudeKeys);

if (typeof latKey === 'undefined' || latKey === null) {
return;
}

const value = point[latKey as keyof LongitudeKeys];

return raw === true ? value : toDecimal(value);
};

export default getLongitude;

0 comments on commit e7d48cf

Please sign in to comment.