Skip to content

Commit

Permalink
refactor: update test descriptions for known and unknown formats, and…
Browse files Browse the repository at this point in the history
… enhance coverage in decimal formats
  • Loading branch information
reskume committed Dec 4, 2024
1 parent 99cc325 commit 3833e46
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 180 deletions.
55 changes: 55 additions & 0 deletions src/formats/decimal-signed-prefixed-hemisphere-format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { z } from 'zod';
import { isNumeric } from '../is-numeric.js';
import type { Coordinate } from '../types.js';
import { validateSchema } from '../validate-schema.js';
import { BaseFormat } from './base-format.js';

const REGEX = /^([NS])\s*(-?\d{1,2}(\.\d+)?)\s*(°)\s*[, ]?\s*([EW])\s*(-?\d{1,3}(\.\d+)?)\s*(°)\s*$/;

/**
* Parses coordinates strings in decimal format with sexagesimal/hemisphere notation. Coordinate ordering is
* always latitude, longitude.
*
* Supported formats:
*
* N 12° E 5°
* N 1.234° E 5.678°
* N 1.234°, E 5.678°
* N 1.234°,E5.678°
* N1.234°E5.678°
*/
export class DecimalSignedPrefixedHemisphereFormat extends BaseFormat {
parse(coordinateString: string): Coordinate {
validateSchema(coordinateString, z.string(), { assert: true, name: 'coordinateString' });

if (DecimalSignedPrefixedHemisphereFormat.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 latitude = match[2];
const longitude = match[6];
if (isNumeric(latitude) === false || isNumeric(longitude) === false) {
throw new Error('Invalid coordinate string');
}

this.enforceValidLatitude(latitude);
this.enforceValidLongitude(longitude);
const lat = parseFloat(latitude).toFixed(this.precision);
const lon = parseFloat(longitude).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);
}
}
15 changes: 3 additions & 12 deletions src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { z } from 'zod';
import type { IFormatParser } from './formats/base-format.js';
import { DecimalSignedFormat } from './formats/decimal-signed-format.js';
import { DecimalSignedPrefixedHemisphereFormat } from './formats/decimal-signed-prefixed-hemisphere-format.js';
import { DecimalSignedSuffixedHemisphereFormat } from './formats/decimal-signed-suffixed-hemisphere-format.js';
import { DecimalUnsignedFormat } from './formats/decimal-unsigned-format.js';
import { DecimalUnsignedSuffixedHemisphereFormat } from './formats/decimal-unsigned-suffixed-hemisphere-format.js';
Expand All @@ -26,14 +27,12 @@ export type Options = {
};

export class Parser {
originalString: string;
opts: Options;
latitude: number | undefined;
longitude: number | undefined;
parsers: IFormatParser[];

constructor(coordinateString: string, options?: Options) {
validateSchema(coordinateString, z.string(), { assert: true, name: 'coordinateString' });
constructor(options?: Options) {
validateSchema(options, OptionsSchema, { assert: true, name: 'options' });

const defaultOptions = {
Expand All @@ -44,6 +43,7 @@ export class Parser {
// set default format parsers to use if not provided
const defaultParsers = [
new DecimalUnsignedFormat({ precision: precision }),
new DecimalSignedPrefixedHemisphereFormat({ precision: precision }),
new DecimalUnsignedSuffixedHemisphereFormat({ precision: precision }),
new DecimalSignedFormat({ precision: precision }),
new DecimalSignedSuffixedHemisphereFormat({ precision: precision }),
Expand All @@ -53,19 +53,10 @@ export class Parser {
if (formatParsers.length > 0 && extendFormatParsers === true) {
formatParsers = [...defaultParsers, ...formatParsers];
}
this.originalString = coordinateString;
this.opts = { precision: precision };
this.latitude = undefined;
this.longitude = undefined;
this.parsers = formatParsers;

try {
const { longitude, latitude } = this.parse(coordinateString);
this.longitude = longitude;
this.latitude = latitude;
} catch (err) {
throw new Error(`Coordinate string "${coordinateString}" could not be parsed: ${err.message}`);
}
}

parse(coordinateString: string): Coordinate {
Expand Down
14 changes: 2 additions & 12 deletions tests/decimal-signed-format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ import { describe, expect, it } from 'vitest';
import { DecimalSignedFormat } from '../src/formats/decimal-signed-format.js';

describe('canParse', () => {
it('returns true for valid decimal format', () => {
it('returns true for known formats', () => {
expect(DecimalSignedFormat.canParse('1.234° 5.678°')).toBe(true);
});

it("returns true for valid decimal format '1.234 °, 5.678 °' with comma", () => {
expect(DecimalSignedFormat.canParse('1.234 °, 5.678 °')).toBe(true);
});

it("returns false for invalid decimal format '1.234 5.678'", () => {
it('returns false for unknown formats', () => {
expect(DecimalSignedFormat.canParse('1.234 5.678')).toBe(false);
});
});
Expand Down Expand Up @@ -56,11 +53,4 @@ describe('parse', () => {
expect(result.latitude).toBe(1.234);
expect(result.longitude).toBe(5.678);
});

it("returns the correct latitude and longitude for '1.23412312° 5.6782356°' with precision 4", () => {
const formatParser = new DecimalSignedFormat({ precision: 4 });
const result = formatParser.parse('1.23412312° 5.6782356°');
expect(result.latitude).toBe(1.2341);
expect(result.longitude).toBe(5.6782);
});
});
54 changes: 54 additions & 0 deletions tests/decimal-signed-prefixed-hemisphere-format.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { describe, expect, it } from 'vitest';
import { DecimalSignedPrefixedHemisphereFormat } from '../src/formats/decimal-signed-prefixed-hemisphere-format.js';

describe('canParse', () => {
it('returns true for known formats', () => {
expect(DecimalSignedPrefixedHemisphereFormat.canParse('N 12° E 5°')).toBe(true);
expect(DecimalSignedPrefixedHemisphereFormat.canParse('N 1.234° E 5.678°')).toBe(true);
expect(DecimalSignedPrefixedHemisphereFormat.canParse('N 1.234°, E 5.678°')).toBe(true);
expect(DecimalSignedPrefixedHemisphereFormat.canParse('N 1.234°,E5.678°')).toBe(true);
expect(DecimalSignedPrefixedHemisphereFormat.canParse('N1.234°E5.678°')).toBe(true);
});

it('returns false for unknown formats', () => {
expect(DecimalSignedPrefixedHemisphereFormat.canParse('1.234 5.678')).toBe(false);
expect(DecimalSignedPrefixedHemisphereFormat.canParse('1.234 N 5.678 P')).toBe(false);
expect(DecimalSignedPrefixedHemisphereFormat.canParse('1.234 N 5.678 ')).toBe(false);
});
});
describe('parse', () => {
it("returns the correct latitude and longitude for 'N 12° E 5°'", () => {
const formatParser = new DecimalSignedPrefixedHemisphereFormat();
const result = formatParser.parse('N 12° E 5°');
expect(result.latitude).toBe(12);
expect(result.longitude).toBe(5);
});

it("returns the correct latitude and longitude for 'N 1.234° E 5.678°'", () => {
const formatParser = new DecimalSignedPrefixedHemisphereFormat();
const result = formatParser.parse('N 1.234° E 5.678°');
expect(result.latitude).toBe(1.234);
expect(result.longitude).toBe(5.678);
});

it("returns the correct latitude and longitude for 'N 1.234°, E 5.678°'", () => {
const formatParser = new DecimalSignedPrefixedHemisphereFormat();
const result = formatParser.parse('N 1.234°, E 5.678°');
expect(result.latitude).toBe(1.234);
expect(result.longitude).toBe(5.678);
});

it("returns the correct latitude and longitude for 'N 1.234°,E5.678°'", () => {
const formatParser = new DecimalSignedPrefixedHemisphereFormat();
const result = formatParser.parse('N 1.234°,E5.678°');
expect(result.latitude).toBe(1.234);
expect(result.longitude).toBe(5.678);
});

it("returns the correct latitude and longitude for 'N1.234°E5.678°'", () => {
const formatParser = new DecimalSignedPrefixedHemisphereFormat();
const result = formatParser.parse('N1.234°E5.678°');
expect(result.latitude).toBe(1.234);
expect(result.longitude).toBe(5.678);
});
});
26 changes: 7 additions & 19 deletions tests/decimal-signed-suffixed-hemisphere-format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,18 @@ import { describe, expect, it } from 'vitest';
import { DecimalSignedSuffixedHemisphereFormat } from '../src/formats/decimal-signed-suffixed-hemisphere-format.js';

describe('canParse', () => {
it('returns true for valid decimal format', () => {
it('returns true for known formats', () => {
expect(DecimalSignedSuffixedHemisphereFormat.canParse('12° N 5° E')).toBe(true);
expect(DecimalSignedSuffixedHemisphereFormat.canParse('1.234° N 5.678° E')).toBe(true);
expect(DecimalSignedSuffixedHemisphereFormat.canParse('1.234° N, 5.678° E')).toBe(true);
expect(DecimalSignedSuffixedHemisphereFormat.canParse('1.234°N, 5.678°E')).toBe(true);
expect(DecimalSignedSuffixedHemisphereFormat.canParse('1.234°N,5.678°E')).toBe(true);
expect(DecimalSignedSuffixedHemisphereFormat.canParse('1.234°N5.678°E')).toBe(true);
});

it("returns true for valid decimal format '1.234 ° N, 5.678 ° E' with comma", () => {
expect(DecimalSignedSuffixedHemisphereFormat.canParse('1.234 ° N, 5.678 ° E')).toBe(true);
});

it("returns false for invalid decimal format '1.234 5.678'", () => {
it('returns false for unknown formats', () => {
expect(DecimalSignedSuffixedHemisphereFormat.canParse('1.234 5.678')).toBe(false);
});

it("returns false for invalid decimal format '1.234 N 5.678 P'", () => {
expect(DecimalSignedSuffixedHemisphereFormat.canParse('1.234 N 5.678 P')).toBe(false);
});

it("returns false for invalid decimal format '1.234 N 5.678 '", () => {
expect(DecimalSignedSuffixedHemisphereFormat.canParse('1.234 N 5.678 ')).toBe(false);
});
});
Expand Down Expand Up @@ -64,11 +59,4 @@ describe('parse', () => {
expect(result.latitude).toBe(1.234);
expect(result.longitude).toBe(5.678);
});

it("returns the correct latitude and longitude for '1.23412312°N 5.6782356°E' with precision 4", () => {
const formatParser = new DecimalSignedSuffixedHemisphereFormat({ precision: 4 });
const result = formatParser.parse('1.23412312°N 5.6782356°E');
expect(result.latitude).toBe(1.2341);
expect(result.longitude).toBe(5.6782);
});
});
12 changes: 3 additions & 9 deletions tests/decimal-unsigned-format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { describe, expect, it } from 'vitest';
import { DecimalUnsignedFormat } from '../src/formats/decimal-unsigned-format.js';

describe('canParse', () => {
it('returns true for valid decimal format', () => {
it('returns true for known formats', () => {
expect(DecimalUnsignedFormat.canParse('1.234, 5.678')).toBe(true);
expect(DecimalUnsignedFormat.canParse('1.234 5.678')).toBe(true);
});

it('returns false for invalid decimal format', () => {
it('returns false for unknown formats', () => {
expect(DecimalUnsignedFormat.canParse('1.234N 5.678E')).toBe(false);
});
});
Expand Down Expand Up @@ -59,11 +60,4 @@ describe('parse', () => {
expect(result.latitude).toBe(1.2341);
expect(result.longitude).toBe(-5.6782);
});

it("returns the correct latitude and longitude for '-1.23412312 5.6782356' with precision 4", () => {
const formatParser = new DecimalUnsignedFormat({ precision: 4 });
const result = formatParser.parse('-1.23412312 5.6782356');
expect(result.latitude).toBe(-1.2341);
expect(result.longitude).toBe(5.6782);
});
});
14 changes: 2 additions & 12 deletions tests/decimal-unsigned-suffixed-hemisphere-format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ import { describe, expect, it } from 'vitest';
import { DecimalUnsignedSuffixedHemisphereFormat } from '../src/formats/decimal-unsigned-suffixed-hemisphere-format.js';

describe('canParse', () => {
it('returns true for valid decimal format', () => {
it('returns true for known formats', () => {
expect(DecimalUnsignedSuffixedHemisphereFormat.canParse('1.234N 5.678E')).toBe(true);
});

it("returns true for valid decimal format '1.234 N, 5.678 E' with comma", () => {
expect(DecimalUnsignedSuffixedHemisphereFormat.canParse('1.234 N, 5.678 E')).toBe(true);
});

it("returns false for invalid decimal format '1.234 5.678'", () => {
it('returns false for unknown formats', () => {
expect(DecimalUnsignedSuffixedHemisphereFormat.canParse('1.234 5.678')).toBe(false);
});
});
Expand Down Expand Up @@ -56,11 +53,4 @@ describe('parse', () => {
expect(result.latitude).toBe(1.234);
expect(result.longitude).toBe(5.678);
});

it("returns the correct latitude and longitude for '1.23412312N 5.6782356E' with precision 4", () => {
const formatParser = new DecimalUnsignedSuffixedHemisphereFormat({ precision: 4 });
const result = formatParser.parse('1.23412312N 5.6782356E');
expect(result.latitude).toBe(1.2341);
expect(result.longitude).toBe(5.6782);
});
});
13 changes: 2 additions & 11 deletions tests/dm-unsigned-suffixed-hemisphere-format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,14 @@ import { describe, expect, it } from 'vitest';
import { DmUnsignedSuffixedHemisphereFormat } from '../src/formats/dm-unsigned-suffixed-hemisphere-format.js';

describe('canParse', () => {
it('returns true for valid decimal format', () => {
it('returns true for known formats', () => {
expect(DmUnsignedSuffixedHemisphereFormat.canParse('4007.38N7407.38W')).toBe(true);
});

it("returns true for valid decimal format '4007.38N 7407.38W' with space", () => {
expect(DmUnsignedSuffixedHemisphereFormat.canParse('4007.38N 7407.38W')).toBe(true);
});

it("returns false for invalid decimal format '4007.38N740738W'", () => {
it('returns false for unknown formats', () => {
expect(DmUnsignedSuffixedHemisphereFormat.canParse('4007.38N740738W')).toBe(false);
});

it("returns false for invalid decimal format '4007.38N7407.38P'", () => {
expect(DmUnsignedSuffixedHemisphereFormat.canParse('4007.38N7407.38P')).toBe(false);
});

it("returns false for invalid decimal format '4007.38N7407.38 '", () => {
expect(DmUnsignedSuffixedHemisphereFormat.canParse('4007.38N7407.38 ')).toBe(false);
});
});
Expand Down
Loading

0 comments on commit 3833e46

Please sign in to comment.