Skip to content

Commit

Permalink
feat: add DmsUnsignedDelimitedSuffixedHemisphereFormat for parsing DM…
Browse files Browse the repository at this point in the history
…S unsigned delimited suffixed hemisphere coordinates and include related tests
  • Loading branch information
reskume committed Dec 4, 2024
1 parent d1c192c commit 6833c49
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 69 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ Currently the out-of-the-box format parsers supports various formats and handles
- `40:7:23 -74:7:23`
- `40:7:23, -74:7:23`
- `40:7:23.123, -74:7:23.123`
- ``
- ``
- ``
- ``
- `40:7:23N 74:7:23W`
- `40:7:23N, 74:7:23W`
- `40:7:23N74:7:23W`
- `40:7:23.123N 74:7:23.123W`
- ``
- ``
- ``
81 changes: 81 additions & 0 deletions src/formats/dms-unsigned-delimited-suffixed-hemisphere-format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { z } from 'zod';
import type { Coordinate } from '../types.js';
import { validateSchema } from '../validate-schema.js';
import { BaseFormat } from './base-format.js';

const REGEX = /^(\d+):(\d+):(\d+(?:\.\d+)?)\s*([NS])\s*,?\s*(\d+):(\d+):(\d+(?:\.\d+)?)\s*([EW])$/;

/**
* Parses coordinates strings in DMS unsigned delimited suffixed hemisphere format. Coordinate ordering is
* always latitude, longitude.
*
* Supported formats:
*
* 40:7:23N 74:7:23W
* 40:7:23N, 74:7:23W
* 40:7:23N74:7:23W
* 40:7:23.123N 74:7:23.123W
*/
export class DmsUnsignedDelimitedSuffixedHemisphereFormat extends BaseFormat {
parse(coordinateString: string): Coordinate {
validateSchema(coordinateString, z.string(), { assert: true, name: 'coordinateString' });

if (DmsUnsignedDelimitedSuffixedHemisphereFormat.canParse(coordinateString) === false) {
throw new Error('Invalid coordinate string');
}
// use the regex to parse the latitude and longitude
const match = coordinateString.match(REGEX);
if (match == null) {
throw new Error('Invalid coordinate string');
}
const matchLatDegree = match[1];
const matchLatMinutes = match[2];
const matchLatSeconds = match[3];
const matchLatDirection = match[4];
const matchLonDegree = match[5];
const matchLonMinutes = match[6];
const matchLonSeconds = match[7];
const matchLonDirection = match[8];

this.enforceValidLatitudeDegrees(matchLatDegree, false);
this.enforceValidMinutes(matchLatMinutes);
this.enforceValidSeconds(matchLatSeconds);
this.enforceValidLongitudeDegrees(matchLonDegree, false);
this.enforceValidMinutes(matchLonMinutes);
this.enforceValidSeconds(matchLonSeconds);

const latDegree = Number.parseInt(matchLatDegree);
const latMinutes = Number.parseInt(matchLatMinutes);
const latSeconds = Number.parseFloat(matchLatSeconds);
const lonDegree = Number.parseInt(matchLonDegree);
const lonMinutes = Number.parseInt(matchLonMinutes);
const lonSeconds = Number.parseFloat(matchLonSeconds);

const decimalLat = this.dmsToDecimal({
degrees: Math.abs(latDegree),
minutes: latMinutes,
seconds: latSeconds,
direction: matchLatDirection,
});
const decimalLon = this.dmsToDecimal({
degrees: Math.abs(lonDegree),
minutes: lonMinutes,
seconds: lonSeconds,
direction: matchLonDirection,
});

const lat = decimalLat.toFixed(this.precision);
const lon = decimalLon.toFixed(this.precision);

return {
latitude: parseFloat(lat),
longitude: parseFloat(lon),
};
}

static canParse(coordinateString: string): boolean {
validateSchema(coordinateString, z.string(), { assert: true, name: 'coordinateString' });

return REGEX.test(coordinateString);
}
}
2 changes: 2 additions & 0 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { DmsSignedFormat } from './formats/dms-signed-format.js';
import { DmsSignedPrefixedHemisphereFormat } from './formats/dms-signed-prefixed-hemisphere-format.js';
import { DmsSignedSuffixedHemisphereFormat } from './formats/dms-signed-suffixed-hemisphere-format.js';
import { DmsUnsignedDelimitedFormat } from './formats/dms-unsigned-delimited-format.js';
import { DmsUnsignedDelimitedSuffixedHemisphereFormat } from './formats/dms-unsigned-delimited-suffixed-hemisphere-format.js';
import { DmsUnsignedFormat } from './formats/dms-unsigned-format.js';
import type { Coordinate } from './types.js';
import { validateSchema } from './validate-schema.js';
Expand Down Expand Up @@ -61,6 +62,7 @@ export class Parser {
new DmsSignedPrefixedHemisphereFormat({ precision: precision }),
new DmsSignedSuffixedHemisphereFormat({ precision: precision }),
new DmsUnsignedDelimitedFormat({ precision: precision }),
new DmsUnsignedDelimitedSuffixedHemisphereFormat({ precision: precision }),
new DmsUnsignedFormat({ precision: precision }),
];
let formatParsers = options?.formatParsers || defaultParsers;
Expand Down
65 changes: 0 additions & 65 deletions tests/dms-signed-suffixed-hemisphereformat.test.ts

This file was deleted.

49 changes: 49 additions & 0 deletions tests/dms-unsigned-delimited-suffixed-hemisphere-format.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { describe, expect, it } from 'vitest';
import { DmsUnsignedDelimitedSuffixedHemisphereFormat } from '../src/formats/dms-unsigned-delimited-suffixed-hemisphere-format.js';

describe('canParse', () => {
it('returns true for known formats', () => {
expect(DmsUnsignedDelimitedSuffixedHemisphereFormat.canParse(`40:7:23N 74:7:23W`)).toBe(true);
expect(DmsUnsignedDelimitedSuffixedHemisphereFormat.canParse(`40:7:23N74:7:23W`)).toBe(true);
expect(DmsUnsignedDelimitedSuffixedHemisphereFormat.canParse(`40:7:23N, 74:7:23W`)).toBe(true);
expect(DmsUnsignedDelimitedSuffixedHemisphereFormat.canParse(`40:7:23N,74:7:23W`)).toBe(true);
expect(DmsUnsignedDelimitedSuffixedHemisphereFormat.canParse(`40:7:23.9999N, 74:7:23.9999W`)).toBe(true);
});
});
describe('parse', () => {
it(`returns the correct latitude and longitude for 40:7:23N 74:7:23W`, () => {
const formatParser = new DmsUnsignedDelimitedSuffixedHemisphereFormat();
const result = formatParser.parse(`40:7:23N 74:7:23W`);
expect(result.latitude).toBe(40.123);
expect(result.longitude).toBe(-74.123);
});

it(`returns the correct latitude and longitude for 40:7:23N74:7:23W`, () => {
const formatParser = new DmsUnsignedDelimitedSuffixedHemisphereFormat();
const result = formatParser.parse(`40:7:23N74:7:23W`);
expect(result.latitude).toBe(40.123);
expect(result.longitude).toBe(-74.123);
});

it(`returns the correct latitude and longitude for 40:7:23N, 74:7:23W`, () => {
const formatParser = new DmsUnsignedDelimitedSuffixedHemisphereFormat();
const result = formatParser.parse(`40:7:23N, 74:7:23W`);
expect(result.latitude).toBe(40.123);
expect(result.longitude).toBe(-74.123);
});

it(`returns the correct latitude and longitude for 40:7:23N,74:7:23W`, () => {
const formatParser = new DmsUnsignedDelimitedSuffixedHemisphereFormat();
const result = formatParser.parse(`40:7:23N,74:7:23W`);
expect(result.latitude).toBe(40.123);
expect(result.longitude).toBe(-74.123);
});

it(`returns the correct latitude and longitude for 40:7:23.9999N, 74:7:23.9999W`, () => {
const formatParser = new DmsUnsignedDelimitedSuffixedHemisphereFormat({ precision: 5 });
const result = formatParser.parse(`40:7:23.9999N, 74:7:23.9999W`);

expect(result.latitude).toBe(40.12333);
expect(result.longitude).toBe(-74.12333);
});
});
62 changes: 62 additions & 0 deletions tests/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,69 @@ describe('Test that all configured format parsers do not interfere', () => {
});
});
describe('test dms unsigned delimited format', () => {
it(`returns the correct latitude and longitude for 40:7:23 -74:7:23`, () => {
const parser = new Parser();
const result = parser.parse(`40:7:23 -74:7:23`);
expect(result.latitude).toBe(40.123);
expect(result.longitude).toBe(-74.123);
});

it(`returns the correct latitude and longitude for 40:7:23, -74:7:23`, () => {
const parser = new Parser();
const result = parser.parse(`40:7:23, -74:7:23`);
expect(result.latitude).toBe(40.123);
expect(result.longitude).toBe(-74.123);
});

it(`returns the correct latitude and longitude for 40:7:23,-74:7:23`, () => {
const parser = new Parser();
const result = parser.parse(`40:7:23,-74:7:23`);
expect(result.latitude).toBe(40.123);
expect(result.longitude).toBe(-74.123);
});

it(`returns the correct latitude and longitude for 40:7:23.9999", -74:7:23.9999`, () => {
const parser = new Parser({ precision: 5 });
const result = parser.parse(`40:7:23.9999, -74:7:23.9999`);
expect(result.latitude).toBe(40.12333);
expect(result.longitude).toBe(-74.12333);
});
});
describe('test dms unsigned delimited suffixed hemisphere format', () => {
it(`returns the correct latitude and longitude for 40:7:23N 74:7:23W`, () => {
const parser = new Parser();
const result = parser.parse(`40:7:23N 74:7:23W`);
expect(result.latitude).toBe(40.123);
expect(result.longitude).toBe(-74.123);
});

it(`returns the correct latitude and longitude for 40:7:23N74:7:23W`, () => {
const parser = new Parser();
const result = parser.parse(`40:7:23N74:7:23W`);
expect(result.latitude).toBe(40.123);
expect(result.longitude).toBe(-74.123);
});

it(`returns the correct latitude and longitude for 40:7:23N, 74:7:23W`, () => {
const parser = new Parser();
const result = parser.parse(`40:7:23N, 74:7:23W`);
expect(result.latitude).toBe(40.123);
expect(result.longitude).toBe(-74.123);
});

it(`returns the correct latitude and longitude for 40:7:23N,74:7:23W`, () => {
const parser = new Parser();
const result = parser.parse(`40:7:23N,74:7:23W`);
expect(result.latitude).toBe(40.123);
expect(result.longitude).toBe(-74.123);
});

it(`returns the correct latitude and longitude for 40:7:23.9999N, 74:7:23.9999W`, () => {
const parser = new Parser({ precision: 5 });
const result = parser.parse(`40:7:23.9999N, 74:7:23.9999W`);
expect(result.latitude).toBe(40.12333);
expect(result.longitude).toBe(-74.12333);
});
});
describe('test dms unsigned format', () => {
it(`returns the correct latitude and longitude for 40 7 23 -74 7 23`, () => {
Expand Down

0 comments on commit 6833c49

Please sign in to comment.