From 88d77e8bb737ae7dcc18eb2a389a0984376ae0bb Mon Sep 17 00:00:00 2001 From: kamataryo <6292312+kamataryo@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:58:16 +0900 Subject: [PATCH 1/2] Add a test --- src/index.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/index.test.ts b/src/index.test.ts index 03b7ac9..fdec5e8 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -85,6 +85,9 @@ const successCases: Case[] = [ { title: 'Japanese Postfix, order: 西 -> 南', input: '西135.6789, 南35.1234', output: [{ lat: -35.1234, lng: -135.6789 }] }, { title: 'Japanese Postfix, order: 北 -> 東, overflow +1π', input: '北35.1234, 東315.6789', output: [{ lat: 35.1234, lng: -44.3211 }] }, + // DD.DDD° + { title: 'DD.DDD°, order: N -> E', input: 'N35.1234°, E135.6789°', output: [{ lat: 35.1234, lng: 135.6789 }] }, + // DD°MM'SS" { title: 'DD°MM\'SS", order: lat -> lng', input: '35°12\'34", 135°43\'21.01"', output: [{ lat: 35 + 12 / 60 + 34 / 3600, lng: 135 + 43 / 60 + 21.01 / 3600 }] }, { title: 'DD°MM\'SS", order: lng -> lat', input: '135°43\'21.01", 35°12\'34"', output: [{ lat: 35 + 12 / 60 + 34 / 3600, lng: 135 + 43 / 60 + 21.01 / 3600 }] }, From 28bc7769e36b60d349636578aa649f2acf262a52 Mon Sep 17 00:00:00 2001 From: kamataryo <6292312+kamataryo@users.noreply.github.com> Date: Mon, 4 Sep 2023 18:55:40 +0900 Subject: [PATCH 2/2] Detect "fat degree format" discussing at #2 --- src/index.test.ts | 4 +++ src/index.ts | 9 ++++++ src/lib.test.ts | 77 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.ts | 22 ++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 src/lib.test.ts create mode 100644 src/lib.ts diff --git a/src/index.test.ts b/src/index.test.ts index fdec5e8..5df0f7e 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -118,6 +118,10 @@ const successCases: Case[] = [ { title: 'https://ja.wikipedia.org/wiki/諫早湾', input: '北緯32度53分9.35秒 東経130度11分9.34秒', output: [{ lat: 32 + 53 / 60 + 9.35 / 3600, lng: 130 + 11 / 60 + 9.34 / 3600 }] }, { title: 'https://en.wikipedia.org/wiki/Canada', input: '45°24′N 75°40′W', output: [{ lat: 45 + 24 / 60, lng: -1 * (75 + 40 / 60) }] }, + // https://github.com/geolonia/normalize-any-latlng/issues/2 + { title: '「度分秒.秒」 type', input: '北緯360613.58925 東経1400516.27815', output: [ { lat: 36 + 6 / 60 + 13.58925 / 3600, lng: 140 + 5 / 60 + 16.27815 / 3600 } ] }, + { title: '「度分秒.秒」 type', input: '南緯360613.58925 西経1400516.27815', output: [ { lat: -36 - 6 / 60 - 13.58925 / 3600, lng: -140 - 5 / 60 - 16.27815 / 3600 } ] }, + // errors { title: 'invalid latlng', input: 'aaa bbb ccc', output: [{lat: null, lng: null}] }, ]; diff --git a/src/index.ts b/src/index.ts index cc00957..f6936c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,5 @@ +import { isFatDegreeFormat } from './lib'; + export type NormalizationResult = { lat: number | null lng: number | null @@ -148,6 +150,13 @@ export const normalize: Normalize = (latlngStr) => { } } + const latInFatDegree = lat !== null ? isFatDegreeFormat(lat) : false; + const lngInFatDegree = lng !== null ? isFatDegreeFormat(lng) : false; + if (latInFatDegree && lngInFatDegree) { + lat = latInFatDegree.degree + latInFatDegree.minute / 60 + latInFatDegree.second / 3600; + lng = lngInFatDegree.degree + lngInFatDegree.minute / 60 + lngInFatDegree.second / 3600; + } + if ((typeof lat === 'number' && (lat < -90 || lat > 90) || Number.isNaN(lat))) { lat = null; } diff --git a/src/lib.test.ts b/src/lib.test.ts new file mode 100644 index 0000000..64e06d0 --- /dev/null +++ b/src/lib.test.ts @@ -0,0 +1,77 @@ +import { isFatDegreeFormat } from './lib'; + +test('should be interpreted as DMS', () => { + const input = 360613.58925; + const output = isFatDegreeFormat(input); + expect(output).toStrictEqual({ + degree: 36, + minute: 6, + second: 13.58925, + }); +}); + +test('should be interpreted as DMS', () => { + const input = -360613.58925; + const output = isFatDegreeFormat(input); + expect(output).toStrictEqual({ + degree: -36, + minute: -6, + second: -13.58925, + }); +}); + +test('should be interpreted as DMS', () => { + const input = 1400516.27815; + const output = isFatDegreeFormat(input); + expect(output).toStrictEqual({ + degree: 140, + minute: 5, + second: 16.27815, + }); +}); + +test('should be interpreted as DMS', () => { + const input = -1400516.27815; + const output = isFatDegreeFormat(input); + expect(output).toStrictEqual({ + degree: -140, + minute: -5, + second: -16.27815, + }); +}); + +test('too big number should not be interpreted as DMS', () => { + const input = 12345654.25; + const output = isFatDegreeFormat(input); + expect(output).toBe(false); +}); + +test('minute < 1 should not be interpreted as DMS', () => { + const input = 58.9234; + const output = isFatDegreeFormat(input); + expect(output).toBe(false); +}); + +test('minute > -1 should not be interpreted as DMS', () => { + const input = -58.9234; + const output = isFatDegreeFormat(input); + expect(output).toBe(false); +}); + +test('too small number should not be interpreted as DMS', () => { + const input = -12345654.25; + const output = isFatDegreeFormat(input); + expect(output).toBe(false); +}); + +test('minute over 59 should not be interpreted as DMS', () => { + const input = 366059.58925; + const output = isFatDegreeFormat(input); + expect(output).toBe(false); +}); + +test('second over 59 should not be interpreted as DMS', () => { + const input = 365960.58925; + const output = isFatDegreeFormat(input); + expect(output).toBe(false); +}); diff --git a/src/lib.ts b/src/lib.ts new file mode 100644 index 0000000..caa2702 --- /dev/null +++ b/src/lib.ts @@ -0,0 +1,22 @@ +/** + * We call numbers like 360613.58925 as fat degree format. + * It should be interpreted as 36°06'13.58925". + */ +export const isFatDegreeFormat = (deg: number) => { + if (Math.abs(deg) > 1800000) return false; + + const unit = deg < 0 ? -1 : 1; + + const [integer, decimal = ''] = Math.abs(deg).toString().split('.'); + + const numberStr = integer.padStart(7, '0'); + const degree = unit * parseInt(numberStr.slice(0, 3)); + const minute = unit * parseInt(numberStr.slice(3, 5)); + const second = unit * (parseInt(numberStr.slice(5, 7)) + parseInt(decimal) / 100000); + if ( + Math.abs(minute) < 1 || + Math.abs(minute) > 59 || + Math.abs(second) > 60 + ) return false; + return { degree, minute, second }; +};