Skip to content

Commit

Permalink
Release v1.1.0
Browse files Browse the repository at this point in the history
Merge pull request #39 from VGavara/master
  • Loading branch information
VGavara committed Dec 2, 2022
2 parents fba4be8 + 9460e16 commit d585b1a
Show file tree
Hide file tree
Showing 13 changed files with 336 additions and 105 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ jobs:
- name: Build
run: npm run build
- name: Set NPM environment
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
- name: Publish to NPM
run: npm publish
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

112 changes: 78 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Main package features:

- Allows calculating distances between to points in metres, kilometres or miles.
- Allows using decimal degree (DD) or degrees minutes seconds (DMS) coordinates.
- Allows calculating start and end bearings of a path between two points.
- Allows specifying custom sphere radius (default is Earth equatorial radius).

## At a glance
Expand All @@ -36,24 +37,15 @@ const distance = haversine.getDistance(newYork, madrid);
console.log(`The distance from New York to Madrid is ${distance} kilometres.`);
```

### Calculate the distance, in miles, between two decimal degrees coordinates
### Calculate the distance, in miles, between two degrees minutes seconds (DMS) coordinates

```typescript
import { DDPoint, Haversine, UnitOfDistance } from "haversine-ts";

const newYork = new DDPoint(40.73061, -73.935242);
const madrid = new DDPoint(40.416775, -3.70379);

const haversine = new Haversine(UnitOfDistance.Mile);
const distance = haversine.getDistance(newYork, madrid);

console.log(`The distance from New York to Madrid is ${distance} miles.`);
```

### Calculate the distance, in kilometers, between two degrees minutes seconds (DMS) coordinates

```typescript
import { DMSCoordinate, DMSPoint, Haversine } from "haversine-ts";
import {
DMSCoordinate,
DMSPoint,
Haversine,
UnitOfDistance
} from "haversine-ts";

// New York DMS coordinates are
// latitude 40° 43' 50.1960'' N,
Expand All @@ -71,35 +63,25 @@ const madrid = new DMSPoint(
new DMSCoordinate(3, 42, 13.644)
);

const haversine = new Haversine();
const haversine = new Haversine(UnitOfDistance.Mile);
const distance = haversine.getDistance(newYork.toDDPoint(), madrid.toDDPoint());

console.log(`The distance from New York to Madrid is ${distance} kilometres.`);
console.log(`The distance from New York to Madrid is ${distance} miles.`);
```

### Calculate the distance, in metres, between two coordinates specifying a custom sphere radius
### Calculate the bearing between two points

```typescript
import { DDPoint, Haversine, UnitOfDistance } from "haversine-ts";

const newYork = new DDPoint(40.73061, -73.935242);
const madrid = new DDPoint(40.416775, -3.70379);

// Use the Earth volumetric mean radius (6371 km)
// instead of the default Earth equatorial radius (6378.137 km).
// Radius must be in the same magnitude
// than the haversine specified unit of distance
// (metres in this example)
const earthVolumetricMeanRadius = 6371 * 1000;

const haversine = new Haversine(
UnitOfDistance.Metre,
earthVolumetricMeanRadius
);
const distance = haversine.getDistance(newYork, madrid);
const haversine = new Haversine();
const bearing = haversine.getBearing(newYork, madrid);

console.log(
`The distance from New York to Madrid is ${distance} metres using Earth volumetric mean radius.`
`The start bearing of the path from New York to Madrid is ${bearing.start} degrees, and the end bearing is ${bearing.end} degrees.`
);
```

Expand All @@ -115,7 +97,11 @@ npm install haversine-ts

### Overview

The [Haversine](#Haversine) class supports the implementation of the distance resolver. It uses as input decimal degrees (DD) coordinates defined as [DDPoint](#DDPoint) class object instances, that can be converted into degrees minutes seconds (DMS) coordinates as instances of the [DMSPoint](#DMSPoint) class. Each [DMSPoint](#DMSPoint) object instance is composed by two [DMSCoordinate](#DMSCoordinate) class object instances.
The [Haversine](#Haversine) class supports the implementation of the distance and bearing resolvers.

It uses as input decimal degrees (DD) coordinates defined as [DDPoint](#DDPoint) class object instances, that can be converted into degrees minutes seconds (DMS) coordinates as instances of the [DMSPoint](#DMSPoint) class. Each [DMSPoint](#DMSPoint) object instance is composed by two [DMSCoordinate](#DMSCoordinate) class object instances.

The [SphereBearing](#SphereBearing) class represents a tuple with the start and end bearings of a sphere path (orthodrome) between two points.

<a name="DDPoint"></a>

Expand Down Expand Up @@ -163,7 +149,7 @@ const newYork = new DDPoint(40.73061, -73.935242);
const newYorkDMS = newYork.toDMSPoint();

console.log(
`The coordinates of New York, in DMS notation, are ${newYorkDMS.degrees} degrees, {newYorkDMS.minutes} minutes, {newYorkDMS.seconds} seconds`
`The coordinates of New York, in DMS notation, are ${newYorkDMS.degrees} degrees, ${newYorkDMS.minutes} minutes, ${newYorkDMS.seconds} seconds`
);
```

Expand Down Expand Up @@ -275,6 +261,7 @@ Haversine formula resolver.
- [new Haversine([uod], [sphereRadius])](#new_Haversine_new)
- Methods:
- [.getDistance(pointA, pointB)](#Haversine+getDistance) ⇒ <code>number</code>
- [.getBearing(pointA, pointB)](#Haversine+getBearing)[<code>SphereBearing</code>](#SphereBearing)

<a name="new_Haversine_new"></a>

Expand Down Expand Up @@ -318,6 +305,63 @@ console.log(`The distance from New York to Madrid is ${distance} kilometres.`);
- <code>number</code> - Distance between the points, in the unit of distance set
in the class constructor.

<a name="Haversine+getBearing"></a>

#### haversine.getBearing(pointA, pointB) ⇒ <code>SphereBearing</code>

Calculates the sphere bearing, or start and end bearings, of the path between two points in a sphere.

```typescript
import { DDPoint, Haversine, UnitOfDistance } from "haversine-ts";

const newYork = new DDPoint(40.73061, -73.935242);
const madrid = new DDPoint(40.416775, -3.70379);

const haversine = new Haversine();
const bearing = haversine.getBearing(newYork, madrid);

console.log(
`The start bearing of the path from New York to Madrid is ${bearing.start} degrees, and the end bearing is ${bearing.end} degrees.`
);
```

- Parameters:
- pointA (<code>DDPoint</code>): Point A, in decimal degrees coordinates.
- pointB (<code>DDPoint</code>): Point B, in decimal degrees coordinates.
- Returns:
- [<code>SphereBearing</code>](#SphereBearing) - Bearings of the path from pointA to pointB, in degrees (0 to 360, clockwise from North).

<a name="SphereBearing"></a>

### SphereBearing

Sphere bearing as a tuple of start and end bearings of a sphere path (orthodrome) between two points.

- Constructors:
- [new SphereBearing(start, end)](#new_SphereBearing_new)

<a name="new_SphereBearing_new"></a>

#### new SphereBearing(start, end)

Initializes a sphere bearing object instance.

```typescript
import { SphereBearing } from "haversine-ts";

const bearing = new SphereBearing(60.5, 181);

console.log(
`The start bearing of the path from A to B is ${bearing.start} degrees, and the end bearing is ${bearing.end} degrees.`
);
```

- Parameters
- start (`number`): Start bearing, from 0 to <360 clockwise from North.
- end (`number`): End bearing, from 0 to <360 clockwise from North.
- Throws:
- Error if start or end bearings are out of range.

<a name="UnitOfDistance"></a>

### UnitOfDistance enum
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "haversine-ts",
"version": "1.0.6",
"description": "Typescript minimalistic library with utilities for distance calculation on a sphere surface",
"version": "1.1.0",
"description": "Typescript library with utilities for calculations on a sphere surface",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
Expand All @@ -21,6 +21,7 @@
"haversine",
"sphere",
"distance",
"bearing",
"latitude",
"longitude"
],
Expand Down
2 changes: 1 addition & 1 deletion src/dmsPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class DMSCoordinate {
degrees < -180 ||
degrees > 180 ||
minutes < 0 ||
minutes >= 60 ||
minutes > 59 ||
seconds < 0 ||
seconds >= 60
)
Expand Down
42 changes: 42 additions & 0 deletions src/haversine.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DDPoint } from "./ddPoint";
import { SphereBearing } from "./sphereBearing";
import { toRadians } from "./helpers";

const equatorialEarthRadii = {
Expand Down Expand Up @@ -64,4 +65,45 @@ export class Haversine {

return c * this.sphereRadius;
}

/**
* Calculates the sphere bearing, or start and end bearings, of the path
* between two points in a sphere.
*
* @param {DDPoint} pointA - Point A, in decimal degrees coordinates.
* @param {DDPoint} pointB - Point B, in decimal degrees coordinates.
* @returns {SphereBearing} Bearings of the path from pointA to pointB, in
* degrees (0 to 360, clockwise from North).
*/
getBearing(pointA: DDPoint, pointB: DDPoint): SphereBearing {
const startBearing = this.getStartBearing(pointA, pointB);
const endBearing = (this.getStartBearing(pointB, pointA) + 180) % 360;

return new SphereBearing(startBearing, endBearing);
}

/**
* Calculates the start bearing of the path between two points in a sphere.
*
* @param {DDPoint} pointA - Point A, in decimal degrees coordinates.
* @param {DDPoint} pointB - Point B, in decimal degrees coordinates.
* @returns {number} Bearing of the path from pointA to pointB, in degrees (0
* to 360, clockwise from North).
*/
private getStartBearing(pointA: DDPoint, pointB: DDPoint): number {
const radALatitude = toRadians(pointA.latitude);
const radBLatitude = toRadians(pointB.latitude);
const deltaLongitude =
toRadians(pointB.longitude) - toRadians(pointA.longitude);

const y = Math.sin(deltaLongitude) * Math.cos(radBLatitude);
const x =
Math.cos(radALatitude) * Math.sin(radBLatitude) -
Math.sin(radALatitude) *
Math.cos(radBLatitude) *
Math.cos(deltaLongitude);
const theta = Math.atan2(y, x);

return ((theta * 180) / Math.PI + 360) % 360;
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { DDPoint } from "./ddPoint";
export { DMSCoordinate, DMSPoint } from "./dmsPoint";
export { SphereBearing } from "./sphereBearing";
export { Haversine, UnitOfDistance } from "./haversine";
28 changes: 28 additions & 0 deletions src/sphereBearing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Sphere bearing as a tuple of start and end bearings of a sphere path
* (orthodrome) between two points.
*/
export class SphereBearing {
start: number;
end: number;

/**
* Initializes a sphere bearing object instance.
*
* @param {number} start - Start bearing, from 0 to <360 clockwise from North.
* @param {number} end - End bearing, from 0 to <360 clockwise from North.
*/
constructor(start: number, end: number) {
if (start < 0 || start >= 360)
throw new Error(
"Start bearing out of range: It must be between 0 and <360"
);
if (end < 0 || end >= 360)
throw new Error(
"End bearing out of range: It must be between 0 and <360"
);

this.start = start;
this.end = end;
}
}
47 changes: 24 additions & 23 deletions tests/ddPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,30 @@ import {
decimalLatitude,
decimalLongitude,
dmsLatitude,
dmsLongitude
dmsLongitude,
minLatitude,
maxLatitude,
minLongitude,
maxLongitude,
offset
} from "./mocks";
import { round } from "./helpers";

const tryCreateDDPoint = (latitude: number, longitude: number): boolean => {
try {
new DDPoint(latitude, longitude);
return true;
} catch {
return false;
}
};

export default () => {
it("should create a decimal degrees point", () => {
expect(tryCreateDDPoint(minLatitude, minLongitude)).to.be.true;
expect(tryCreateDDPoint(maxLatitude, maxLongitude)).to.be.true;
});

it("should return a degrees minutes seconds point equivalent", () => {
const ddPoint = new DDPoint(decimalLatitude, decimalLongitude);

Expand All @@ -28,30 +47,12 @@ export default () => {
});

it("should throw an error if the latitude is out of bounds", () => {
const trySetLatitude = (latitude: number): boolean => {
try {
new DDPoint(latitude, 0);
return true;
} catch {
return false;
}
};

expect(trySetLatitude(-90.1)).to.be.false;
expect(trySetLatitude(90.1)).to.be.false;
expect(tryCreateDDPoint(minLatitude - offset, 0)).to.be.false;
expect(tryCreateDDPoint(maxLatitude + offset, 0)).to.be.false;
});

it("should throw an error if the longitude is out of bounds", () => {
const trySetLongitude = (longitude: number): boolean => {
try {
new DDPoint(0, longitude);
return true;
} catch {
return false;
}
};

expect(trySetLongitude(-180.1)).to.be.false;
expect(trySetLongitude(180.1)).to.be.false;
expect(tryCreateDDPoint(0, minLongitude - offset)).to.be.false;
expect(tryCreateDDPoint(0, maxLongitude + offset)).to.be.false;
});
};
Loading

0 comments on commit d585b1a

Please sign in to comment.