Skip to content

Commit

Permalink
fix: increase accuracy
Browse files Browse the repository at this point in the history
  • Loading branch information
manuelbieh committed Oct 11, 2021
1 parent 47850ea commit 2a7b443
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 31 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ geolib.getPathLength([

Returns the length of the path in meters as number.

### `getDistanceFromLine(point, lineStart, lineEnd)`
### `getDistanceFromLine(point, lineStart, lineEnd, accuracy = 1)`

Gets the minimum distance from a point to a line of two points.

Expand All @@ -322,6 +322,8 @@ geolib.getDistanceFromLine(

Returns the shortest distance to the given line as number.

**Note:** if all points are too close together the function might return NaN. In this case it usually helps to slightly increase the accuracy (e.g. `0.01`).

### `getBoundsOfDistance(point, distance)`

Computes the bounding coordinates of all points on the surface of the earth less than or equal to the specified great circle distance.
Expand Down
24 changes: 20 additions & 4 deletions src/decimalToSexagesimal.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,35 @@ import decimalToSexagesimal from './decimalToSexagesimal';

describe('decimalToSexagesimal', () => {
it('should return minutes and seconds with a leading 0 if < 10', () => {
expect(decimalToSexagesimal(121.135)).toEqual('121° 08\' 06"');
expect(decimalToSexagesimal(121.135)).toEqual('121° 08\' 06.0"');
});

it('should still return 00 and 00 if there are no minutes or seconds', () => {
expect(decimalToSexagesimal(50)).toEqual('50° 00\' 00"');
it('should still return 00 and 00.0 if there are no minutes or seconds', () => {
expect(decimalToSexagesimal(50)).toEqual('50° 00\' 00.0"');
});

it('should always return a positive value', () => {
expect(decimalToSexagesimal(-19.37555556)).toEqual('19° 22\' 32"');
expect(decimalToSexagesimal(-19.37555556)).toEqual('19° 22\' 32.0"');
});

it('should return seconds with decimal places if needed', () => {
expect(decimalToSexagesimal(51.519475)).toEqual('51° 31\' 10.11"');
expect(decimalToSexagesimal(51.516975)).toEqual('51° 31\' 01.11"');
});

it('should return precise values', () => {
expect(decimalToSexagesimal(31.011306)).toEqual('31° 00\' 40.7016"');
});

it('should handle precision correctly', () => {
expect(decimalToSexagesimal(90.99999996)).toEqual('90° 59\' 59.9999"');
expect(decimalToSexagesimal(90.9999999)).toEqual('90° 59\' 59.9996"');
expect(decimalToSexagesimal(90.999999)).toEqual('90° 59\' 59.9964"');
expect(decimalToSexagesimal(90.99999)).toEqual('90° 59\' 59.964"');
expect(decimalToSexagesimal(90.9999)).toEqual('90° 59\' 59.64"');
expect(decimalToSexagesimal(90.999)).toEqual('90° 59\' 56.4"');
expect(decimalToSexagesimal(90.99)).toEqual('90° 59\' 24.0"');
expect(decimalToSexagesimal(90.9)).toEqual('90° 54\' 00.0"');
expect(decimalToSexagesimal(90.1)).toEqual('90° 06\' 00.0"');
});
});
37 changes: 15 additions & 22 deletions src/decimalToSexagesimal.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,32 @@
// trying to sanitize floating point fuckups here to a certain extent
const imprecise = (number: number) => {
const factor = Math.pow(10, 12);
const imprecise = (number: number, decimals: number = 4) => {
const factor = Math.pow(10, decimals);
return Math.round(number * factor) / factor;
};

// Converts a decimal coordinate value to sexagesimal format
const decimal2sexagesimal = (decimal: number) => {
const decimal2sexagesimalNext = (decimal: number) => {
const [pre, post] = decimal.toString().split('.');

const deg = Math.abs(Number(pre));
const minFull = imprecise(Number('0.' + (post || 0)) * 60);
const min = Math.floor(minFull);
const sec = imprecise((minFull % min || 0) * 60);
const min0 = Number('0.' + (post || 0)) * 60;
const sec0 = min0.toString().split('.');

const min = Math.floor(min0);
const sec = imprecise(Number('0.' + (sec0[1] || 0)) * 60).toString();

const [secPreDec, secDec = '0'] = sec.split('.');

// We're limiting minutes and seconds to a maximum of 6/4 decimal places
// here purely for aesthetical reasons. That results in an inaccuracy of
// a few millimeters. If you're working for NASA that's possibly not
// accurate enough for you. For all others that should be fine.
// Feel free to create an issue on GitHub if not, please.
return (
deg +
'° ' +
Number(min.toFixed(6))
.toString()
.split('.')
.map((v, i) => (i === 0 ? v.padStart(2, '0') : v))
.join('.') +
min.toString().padStart(2, '0') +
"' " +
Number(sec.toFixed(4))
.toString()
.split('.')
.map((v, i) => (i === 0 ? v.padStart(2, '0') : v))
.join('.') +
secPreDec.padStart(2, '0') +
'.' +
secDec.padEnd(1, '0') +
'"'
);
};

export default decimal2sexagesimal;
export default decimal2sexagesimalNext;
113 changes: 113 additions & 0 deletions src/getDistanceFromLine.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,117 @@ describe('getDistanceFromLine', () => {

expect(getDistanceFromLine(point, lineStart, lineEnd)).toEqual(1409);
});

it('https://github.com/manuelbieh/geolib/issues/129', () => {
expect(
getDistanceFromLine(
{
latitude: 53.0281161107639,
longitude: 5.64420448614743,
},
{
latitude: 53.028118,
longitude: 5.644203,
},
{
latitude: 53.029021,
longitude: 5.646562,
},
0.1
)
).not.toBeNaN();

expect(
getDistanceFromLine(
{
latitude: 53.0515182362456,
longitude: 5.67842625473533,
},
{
latitude: 53.051521,
longitude: 5.678421,
},
{
latitude: 53.051652,
longitude: 5.67852,
},
0.1
)
).not.toBeNaN();

expect(
getDistanceFromLine(
{
latitude: 53.0933224175307,
longitude: 5.61011575344944,
},
{
latitude: 53.093321,
longitude: 5.610115,
},
{
latitude: 53.093236,
longitude: 5.610037,
},
0.1
)
).not.toBeNaN();

expect(
getDistanceFromLine(
{
latitude: 53.0867058030163,
longitude: 5.59876618900706,
},
{
latitude: 53.086705,
longitude: 5.598759,
},
{
latitude: 53.085538,
longitude: 5.597901,
},
0.1
)
).not.toBeNaN();

expect(
getDistanceFromLine(
{
latitude: 53.0657207151762,
longitude: 5.60056383087291,
},
{
latitude: 53.065721,
longitude: 5.600568,
},
{
latitude: 53.062609,
longitude: 5.600793,
},
0.1
)
).not.toBeNaN();

// TODO: If the point is directly on the line(?) it returns NaN
// Verify and fix
// https://github.com/manuelbieh/geolib/issues/129
// expect(
// getDistanceFromLine(
// {
// latitude: 53,
// longitude: 5,
// },
// {
// latitude: 53,
// longitude: 5,
// },
// {
// latitude: 54,
// longitude: 6,
// },
// 1
// )
// ).not.toBeNaN();
});
});
11 changes: 7 additions & 4 deletions src/getDistanceFromLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import { GeolibInputCoordinates } from './types';
const getDistanceFromLine = (
point: GeolibInputCoordinates,
lineStart: GeolibInputCoordinates,
lineEnd: GeolibInputCoordinates
lineEnd: GeolibInputCoordinates,
accuracy: number = 1
) => {
const d1 = getDistance(lineStart, point);
const d2 = getDistance(point, lineEnd);
const d3 = getDistance(lineStart, lineEnd);
const d1 = getDistance(lineStart, point, accuracy);
const d2 = getDistance(point, lineEnd, accuracy);
const d3 = getDistance(lineStart, lineEnd, accuracy);

// alpha is the angle between the line from start to point, and from start to end
const alpha = Math.acos(
Expand All @@ -33,6 +34,8 @@ const getDistanceFromLine = (
return d2;
}

// console.log(Math.sin(alpha), Math.sin(alpha) * d1);

// otherwise the minimum distance is achieved through a line perpendicular
// to the start-end line, which goes from the start-end line to the point
return Math.sin(alpha) * d1;
Expand Down

0 comments on commit 2a7b443

Please sign in to comment.