Skip to content

Commit 34ed3b5

Browse files
authored
Merge pull request #10 from jleh/latlng-to-locator
Convert coordinates to locator string.
2 parents 57a1961 + 2db2581 commit 34ed3b5

File tree

4 files changed

+54
-20
lines changed

4 files changed

+54
-20
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ Operations with [Maidenhead locator system](https://en.wikipedia.org/wiki/Maiden
55
## Usage
66

77
```javascript
8-
const { locatorToLatLng, distance } = require('qth-locator');
8+
const { locatorToLatLng, distance, bearingDistance, latLngToLocator } = require('qth-locator');
99

1010
locatorToLatLng('IO91wm'); // [51.521, -0.125]
1111
distance('IO91wm', 'KP20le'); // 1821.5 km
1212
bearingDistance('FN20qr', 'KP21ol') // 6586.72 km, 49.16 degrees
13-
13+
latLngToLocator(60.179, 24.945) // KP21le
1414
```
1515

1616
## License

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "qth-locator",
3-
"version": "2.0.0",
3+
"version": "2.1.0",
44
"description": "Maidenhead locator calculator",
55
"repository": {
66
"type": "git",

src/index.js

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const CHAR_CODE_OFFSET = 65;
66
const isValidLocatorString = locatorString => locatorString.match(/^[A-Ra-r][A-Ra-r]\d\d[A-Xa-x][A-Xa-x]/) !== null;
77

88
const charToNumber = char => char.toUpperCase().charCodeAt(0) - CHAR_CODE_OFFSET;
9+
const numberToChar = number => String.fromCharCode(number + CHAR_CODE_OFFSET);
910

1011
const locatorToLatLng = (locatorString) => {
1112
locatorString += 'll'; // append subsquare in case is 4 chars long... If not, is ignored.
@@ -56,9 +57,34 @@ const bearingDistance = (from, to) => {
5657

5758
const distance = (from, to) => bearingDistance(from, to).km;
5859

60+
const isValidPoint = (lat, lng) => (lat >= -90 && lat <= 90) && (lng >= -180 && lng <= 180);
61+
62+
const latLngToLocator = (lat, lng) => {
63+
if (!isValidPoint(lat, lng)) {
64+
throw new Error('Input is not a valid coordinate');
65+
}
66+
67+
const longitude = lng + 180;
68+
const latitude = lat + 90;
69+
70+
const squareLng = numberToChar(Math.floor(longitude / 20));
71+
const squareLat = numberToChar(Math.floor(latitude / 10));
72+
73+
const fieldLng = Math.floor(longitude % 20 / 2);
74+
const fieldLat = Math.floor(latitude % 10);
75+
76+
const subsquareLng = numberToChar(Math.floor((longitude % 20 % 2) * 12)).toLowerCase();
77+
const subsquareLat = numberToChar((latitude % 10 - fieldLat) * 24).toLowerCase();
78+
79+
return squareLng + squareLat
80+
+ fieldLng + fieldLat
81+
+ subsquareLng + subsquareLat;
82+
};
83+
5984
module.exports = {
60-
isValidLocatorString: isValidLocatorString,
61-
locatorToLatLng: locatorToLatLng,
62-
distance: distance,
63-
bearingDistance: bearingDistance
85+
isValidLocatorString,
86+
locatorToLatLng,
87+
distance,
88+
bearingDistance,
89+
latLngToLocator
6490
};

test/index.spec.js

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,9 @@ describe('QTH locator', () => {
2121
expect(BDPair.deg).toBeCloseTo(deg);
2222
};
2323

24-
const expectInvalifGridErr = (fn, a, b) => {
25-
expect.assertions(2);
26-
27-
try {
28-
fn(a, b);
29-
} catch (error) {
30-
expect(error).toHaveProperty('message', 'Input is not valid locator string');
31-
expect(error).toBeInstanceOf(Error);
32-
}
33-
};
24+
const expectInvalidGridErr = fn => expect(fn).toThrow('Input is not valid locator string');
25+
26+
const expectInvalidLatLngErr = fn => expect(fn).toThrow('Input is not a valid coordinate');
3427

3528
it('Converts locator string to coordinates', () => {
3629
expectCoordinates(qthLocator.locatorToLatLng('KP20le'), 60.188, 24.958);
@@ -55,18 +48,33 @@ describe('QTH locator', () => {
5548
});
5649

5750
it('Detect invalid grid', () => {
58-
expectInvalifGridErr(qthLocator.locatorToLatLng, 'RZ73');
51+
expectInvalidGridErr(() => qthLocator.locatorToLatLng('RZ73'));
5952
});
6053

6154
it('Locate debatable grid - It is in spec!', () => {
6255
expectCoordinates(qthLocator.locatorToLatLng('RR73'), 83.479, 174.96);
6356
});
6457

6558
it('Detect short grid', () => {
66-
expectInvalifGridErr(qthLocator.locatorToLatLng, 'R73');
59+
expectInvalidGridErr(() => qthLocator.locatorToLatLng('R73'));
6760
});
6861

6962
it('detect invalid grid in bearingDistance 1', () => {
70-
expectInvalifGridErr(qthLocator.bearingDistance, 'FN20qr', 'F030ll');
63+
expectInvalidGridErr(() => qthLocator.bearingDistance('FN20qr', 'F030ll'));
64+
});
65+
66+
it('Converts latLng to grid', () => {
67+
expect(qthLocator.latLngToLocator(14.3125, -32.125)).toBe('HK34wh');
68+
expect(qthLocator.latLngToLocator(60.179, 24.945)).toBe('KP20le');
69+
expect(qthLocator.latLngToLocator(-33.886048, 151.193546)).toBe('QF56oc');
70+
expect(qthLocator.latLngToLocator(-22.904788, -43.184915)).toBe('GG87jc');
71+
});
72+
73+
it('Throws error for invalid coordinates', () => {
74+
expectInvalidLatLngErr(() => qthLocator.latLngToLocator(91, 120));
75+
expectInvalidLatLngErr(() => qthLocator.latLngToLocator(-91, 120));
76+
expectInvalidLatLngErr(() => qthLocator.latLngToLocator(55, 181));
77+
expectInvalidLatLngErr(() => qthLocator.latLngToLocator(55, -181));
78+
expectInvalidLatLngErr(() => qthLocator.latLngToLocator(-91, -181));
7179
});
7280
});

0 commit comments

Comments
 (0)