japanese_address_parser 3.0.5 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -2
- data/Gemfile.lock +6 -6
- data/js/node_modules/.package-lock.json +86 -1
- data/js/node_modules/@geolonia/normalize-japanese-addresses/dist/lib/cacheRegexes.d.ts +9 -2
- data/js/node_modules/@geolonia/normalize-japanese-addresses/dist/main-browser.js +141 -25
- data/js/node_modules/@geolonia/normalize-japanese-addresses/dist/main-es.d.ts +3 -0
- data/js/node_modules/@geolonia/normalize-japanese-addresses/dist/main-es.js +1922 -0
- data/js/node_modules/@geolonia/normalize-japanese-addresses/dist/main-node.d.ts +6 -0
- data/js/node_modules/@geolonia/normalize-japanese-addresses/dist/main-node.js +203 -54
- data/js/node_modules/@geolonia/normalize-japanese-addresses/dist/normalize.d.ts +47 -3
- data/js/node_modules/@geolonia/normalize-japanese-addresses/package.json +9 -5
- data/js/node_modules/@geolonia/normalize-japanese-addresses/rollup.config.ts +9 -0
- data/js/node_modules/@geolonia/normalize-japanese-addresses/src/config.ts +1 -0
- data/js/node_modules/@geolonia/normalize-japanese-addresses/src/lib/cacheRegexes.ts +71 -11
- data/js/node_modules/@geolonia/normalize-japanese-addresses/src/lib/dict.ts +3 -2
- data/js/node_modules/@geolonia/normalize-japanese-addresses/src/lib/kan2num.ts +6 -2
- data/js/node_modules/@geolonia/normalize-japanese-addresses/src/main-es.ts +4 -0
- data/js/node_modules/@geolonia/normalize-japanese-addresses/src/main-node.ts +41 -12
- data/js/node_modules/@geolonia/normalize-japanese-addresses/src/normalize.ts +165 -37
- data/js/package-lock.json +7 -7
- data/js/package.json +1 -1
- data/lib/japanese_address_parser/data/03-03206.csv +1 -0
- data/lib/japanese_address_parser/data/04-04421.csv +8 -8
- data/lib/japanese_address_parser/data/05-05203.csv +5 -0
- data/lib/japanese_address_parser/data/08-08222.csv +4 -0
- data/lib/japanese_address_parser/data/11-11442.csv +2 -0
- data/lib/japanese_address_parser/data/13-13225.csv +3 -0
- data/lib/japanese_address_parser/data/14-14134.csv +3 -0
- data/lib/japanese_address_parser/data/15-15104.csv +1 -1
- data/lib/japanese_address_parser/data/15-15222.csv +1 -1
- data/lib/japanese_address_parser/data/15-15225.csv +1 -1
- data/lib/japanese_address_parser/data/18-18202.csv +1 -1
- data/lib/japanese_address_parser/data/22-22102.csv +6 -0
- data/lib/japanese_address_parser/data/23-23202.csv +1 -0
- data/lib/japanese_address_parser/data/23-23207.csv +5 -0
- data/lib/japanese_address_parser/data/23-23213.csv +1 -0
- data/lib/japanese_address_parser/data/23-23236.csv +1 -0
- data/lib/japanese_address_parser/data/25-25206.csv +4 -0
- data/lib/japanese_address_parser/data/27-27212.csv +4 -0
- data/lib/japanese_address_parser/data/28-28108.csv +1 -0
- data/lib/japanese_address_parser/data/28-28204.csv +1 -1
- data/lib/japanese_address_parser/data/28-28226.csv +1 -0
- data/lib/japanese_address_parser/data/29-29201.csv +1 -1
- data/lib/japanese_address_parser/data/34-34207.csv +3 -0
- data/lib/japanese_address_parser/data/35-35215.csv +3 -0
- data/lib/japanese_address_parser/data/38-38204.csv +17 -0
- data/lib/japanese_address_parser/data/40-40131.csv +1 -0
- data/lib/japanese_address_parser/data/40-40342.csv +4 -0
- data/lib/japanese_address_parser/data/44-44201.csv +27 -0
- data/lib/japanese_address_parser/data/46-46201.csv +4 -0
- data/lib/japanese_address_parser/data/47-47205.csv +4 -0
- data/lib/japanese_address_parser/version.rb +1 -1
- metadata +6 -3
@@ -5,7 +5,7 @@ import { currentConfig } from '../config'
|
|
5
5
|
import { __internals } from '../normalize'
|
6
6
|
import { findKanjiNumbers } from '@geolonia/japanese-numeral'
|
7
7
|
|
8
|
-
type PrefectureList = { [key: string]: string[] }
|
8
|
+
export type PrefectureList = { [key: string]: string[] }
|
9
9
|
interface SingleTown {
|
10
10
|
town: string
|
11
11
|
originalTown?: string
|
@@ -13,8 +13,13 @@ interface SingleTown {
|
|
13
13
|
lat: string
|
14
14
|
lng: string
|
15
15
|
}
|
16
|
-
type TownList = SingleTown[]
|
17
|
-
|
16
|
+
export type TownList = SingleTown[]
|
17
|
+
interface SingleAddr {
|
18
|
+
addr: string
|
19
|
+
lat: string | null
|
20
|
+
lng: string | null
|
21
|
+
}
|
22
|
+
export type AddrList = SingleAddr[]
|
18
23
|
interface GaikuListItem {
|
19
24
|
gaiku: string
|
20
25
|
lat: string
|
@@ -40,16 +45,16 @@ let cachedPrefectures: PrefectureList | undefined = undefined
|
|
40
45
|
const cachedTowns: { [key: string]: TownList } = {}
|
41
46
|
const cachedGaikuListItem: { [key: string]: GaikuListItem[] } = {}
|
42
47
|
const cachedResidentials: { [key: string]: ResidentialList } = {}
|
43
|
-
|
44
|
-
|
45
|
-
|
48
|
+
const cachedAddrs: { [key: string]: AddrList } = {} // TODO: use LRU
|
49
|
+
let cachedSameNamedPrefectureCityRegexPatterns: [string, string][] | undefined =
|
50
|
+
undefined
|
46
51
|
|
47
52
|
export const getPrefectures = async () => {
|
48
53
|
if (typeof cachedPrefectures !== 'undefined') {
|
49
54
|
return cachedPrefectures
|
50
55
|
}
|
51
56
|
|
52
|
-
const prefsResp = await __internals.fetch('.json') // ja.json
|
57
|
+
const prefsResp = await __internals.fetch('.json', { level: 1 }) // ja.json
|
53
58
|
const data = (await prefsResp.json()) as PrefectureList
|
54
59
|
return cachePrefectures(data)
|
55
60
|
}
|
@@ -104,6 +109,7 @@ export const getTowns = async (pref: string, city: string) => {
|
|
104
109
|
|
105
110
|
const townsResp = await __internals.fetch(
|
106
111
|
['', encodeURI(pref), encodeURI(city) + '.json'].join('/'),
|
112
|
+
{ level: 3, pref, city },
|
107
113
|
)
|
108
114
|
const towns = (await townsResp.json()) as TownList
|
109
115
|
return (cachedTowns[cacheKey] = towns)
|
@@ -114,7 +120,13 @@ export const getGaikuList = async (
|
|
114
120
|
city: string,
|
115
121
|
town: string,
|
116
122
|
) => {
|
117
|
-
|
123
|
+
if (currentConfig.interfaceVersion > 1) {
|
124
|
+
throw new Error(
|
125
|
+
`Invalid config.interfaceVersion: ${currentConfig.interfaceVersion}'}. Please set config.interfaceVersion to 1.`,
|
126
|
+
)
|
127
|
+
}
|
128
|
+
|
129
|
+
const cacheKey = `${pref}-${city}-${town}-v${currentConfig.interfaceVersion}`
|
118
130
|
const cache = cachedGaikuListItem[cacheKey]
|
119
131
|
if (typeof cache !== 'undefined') {
|
120
132
|
return cache
|
@@ -136,7 +148,13 @@ export const getResidentials = async (
|
|
136
148
|
city: string,
|
137
149
|
town: string,
|
138
150
|
) => {
|
139
|
-
|
151
|
+
if (currentConfig.interfaceVersion > 1) {
|
152
|
+
throw new Error(
|
153
|
+
`Invalid config.interfaceVersion: ${currentConfig.interfaceVersion}'}. Please set config.interfaceVersion to 1.`,
|
154
|
+
)
|
155
|
+
}
|
156
|
+
|
157
|
+
const cacheKey = `${pref}-${city}-${town}-v${currentConfig.interfaceVersion}`
|
140
158
|
const cache = cachedResidentials[cacheKey]
|
141
159
|
if (typeof cache !== 'undefined') {
|
142
160
|
return cache
|
@@ -166,6 +184,34 @@ export const getResidentials = async (
|
|
166
184
|
return (cachedResidentials[cacheKey] = residentials)
|
167
185
|
}
|
168
186
|
|
187
|
+
export const getAddrs = async (pref: string, city: string, town: string) => {
|
188
|
+
if (currentConfig.interfaceVersion < 2) {
|
189
|
+
throw new Error(
|
190
|
+
`Invalid config.interfaceVersion: ${currentConfig.interfaceVersion}'}. Please set config.interfaceVersion to 2 or higher`,
|
191
|
+
)
|
192
|
+
}
|
193
|
+
|
194
|
+
const cacheKey = `${pref}-${city}-${town}-v${currentConfig.interfaceVersion}`
|
195
|
+
const cache = cachedAddrs[cacheKey]
|
196
|
+
if (typeof cache !== 'undefined') {
|
197
|
+
return cache
|
198
|
+
}
|
199
|
+
|
200
|
+
const addrsResp = await __internals.fetch(
|
201
|
+
['', encodeURI(pref), encodeURI(city), encodeURI(town) + '.json'].join('/'),
|
202
|
+
{ level: 8, pref, city, town },
|
203
|
+
)
|
204
|
+
let addrs: AddrList
|
205
|
+
try {
|
206
|
+
addrs = (await addrsResp.json()) as AddrList
|
207
|
+
} catch {
|
208
|
+
addrs = []
|
209
|
+
}
|
210
|
+
|
211
|
+
addrs.sort((res1, res2) => res1.addr.length - res2.addr.length)
|
212
|
+
return (cachedAddrs[cacheKey] = addrs)
|
213
|
+
}
|
214
|
+
|
169
215
|
// 十六町 のように漢数字と町が連結しているか
|
170
216
|
const isKanjiNumberFollewedByCho = (targetTownName: string) => {
|
171
217
|
const xCho = targetTownName.match(/.町/g)
|
@@ -260,15 +306,29 @@ export const getTownRegexPatterns = async (pref: string, city: string) => {
|
|
260
306
|
const _pattern = `(${patterns.join(
|
261
307
|
'|',
|
262
308
|
)})((丁|町)目?|番(町|丁)|条|軒|線|の町?|地割|号|[--﹣−‐⁃‑‒–—﹘―⎯⏤ーー─━])`
|
263
|
-
|
264
309
|
return _pattern // デバッグのときにめんどくさいので変数に入れる。
|
265
310
|
},
|
266
311
|
),
|
267
312
|
)
|
268
|
-
|
269
313
|
return [town, pattern]
|
270
314
|
}) as [SingleTown, string][]
|
271
315
|
|
316
|
+
// X丁目の丁目なしの数字だけ許容するため、最後に数字だけ追加していく
|
317
|
+
for (const town of towns) {
|
318
|
+
const chomeMatch = town.town.match(
|
319
|
+
/([^一二三四五六七八九十]+)([一二三四五六七八九十]+)(丁目?)/,
|
320
|
+
)
|
321
|
+
if (!chomeMatch) {
|
322
|
+
continue
|
323
|
+
}
|
324
|
+
const chomeNamePart = chomeMatch[1]
|
325
|
+
const chomeNum = chomeMatch[2]
|
326
|
+
const pattern = toRegexPattern(
|
327
|
+
`^${chomeNamePart}(${chomeNum}|${kan2num(chomeNum)})`,
|
328
|
+
)
|
329
|
+
patterns.push([town, pattern])
|
330
|
+
}
|
331
|
+
|
272
332
|
cachedTownRegexes.set(`${pref}-${city}`, patterns)
|
273
333
|
return patterns
|
274
334
|
}
|
@@ -1,8 +1,8 @@
|
|
1
1
|
// JIS 第2水準 => 第1水準 及び 旧字体 => 新字体
|
2
|
-
const JIS_OLD_KANJI = '
|
2
|
+
const JIS_OLD_KANJI = '亞,圍,壹,榮,驛,應,櫻,假,會,懷,覺,樂,陷,歡,氣,戲,據,挾,區,徑,溪,輕,藝,儉,圈,權,嚴,恆,國,齋,雜,蠶,殘,兒,實,釋,從,縱,敍,燒,條,剩,壤,釀,眞,盡,醉,髓,聲,竊,淺,錢,禪,爭,插,騷,屬,對,滯,擇,單,斷,癡,鑄,敕,鐵,傳,黨,鬪,屆,腦,廢,發,蠻,拂,邊,瓣,寶,沒,滿,藥,餘,樣,亂,兩,禮,靈,爐,灣,惡,醫,飮,營,圓,歐,奧,價,繪,擴,學,罐,勸,觀,歸,犧,擧,狹,驅,莖,經,繼,缺,劍,檢,顯,廣,鑛,碎,劑,參,慘,絲,辭,舍,壽,澁,肅,將,證,乘,疊,孃,觸,寢,圖,穗,樞,齊,攝,戰,潛,雙,莊,裝,藏,續,體,臺,澤,膽,彈,蟲,廳,鎭,點,燈,盜,獨,貳,霸,賣,髮,祕,佛,變,辯,豐,飜,默,與,譽,謠,覽,獵,勵,齡,勞,壓,爲,隱,衞,鹽,毆,穩,畫,壞,殼,嶽,卷,關,顏,僞,舊,峽,曉,勳,惠,螢,鷄,縣,險,獻,驗,效,號,濟,册,棧,贊,齒,濕,寫,收,獸,處,稱,奬,淨,繩,讓,囑,愼,粹,隨,數,靜,專,踐,纖,壯,搜,總,臟,墮,帶,瀧,擔,團,遲,晝,聽,遞,轉,當,稻,讀,惱,拜,麥,拔,濱,竝,辨,舖,襃,萬,譯,豫,搖,來,龍,壘,隸,戀,樓,鰺,鶯,蠣,攪,竈,灌,諫,頸,礦,蘂,靱,賤,壺,礪,檮,濤,邇,蠅,檜,儘,藪,籠,彌,麩'.split(
|
3
3
|
/,/,
|
4
4
|
)
|
5
|
-
const JIS_NEW_KANJI = '
|
5
|
+
const JIS_NEW_KANJI = '亜,囲,壱,栄,駅,応,桜,仮,会,懐,覚,楽,陥,歓,気,戯,拠,挟,区,径,渓,軽,芸,倹,圏,権,厳,恒,国,斎,雑,蚕,残,児,実,釈,従,縦,叙,焼,条,剰,壌,醸,真,尽,酔,髄,声,窃,浅,銭,禅,争,挿,騒,属,対,滞,択,単,断,痴,鋳,勅,鉄,伝,党,闘,届,脳,廃,発,蛮,払,辺,弁,宝,没,満,薬,余,様,乱,両,礼,霊,炉,湾,悪,医,飲,営,円,欧,奥,価,絵,拡,学,缶,勧,観,帰,犠,挙,狭,駆,茎,経,継,欠,剣,検,顕,広,鉱,砕,剤,参,惨,糸,辞,舎,寿,渋,粛,将,証,乗,畳,嬢,触,寝,図,穂,枢,斉,摂,戦,潜,双,荘,装,蔵,続,体,台,沢,胆,弾,虫,庁,鎮,点,灯,盗,独,弐,覇,売,髪,秘,仏,変,弁,豊,翻,黙,与,誉,謡,覧,猟,励,齢,労,圧,為,隠,衛,塩,殴,穏,画,壊,殻,岳,巻,関,顔,偽,旧,峡,暁,勲,恵,蛍,鶏,県,険,献,験,効,号,済,冊,桟,賛,歯,湿,写,収,獣,処,称,奨,浄,縄,譲,嘱,慎,粋,随,数,静,専,践,繊,壮,捜,総,臓,堕,帯,滝,担,団,遅,昼,聴,逓,転,当,稲,読,悩,拝,麦,抜,浜,並,弁,舗,褒,万,訳,予,揺,来,竜,塁,隷,恋,楼,鯵,鴬,蛎,撹,竃,潅,諌,頚,砿,蕊,靭,賎,壷,砺,梼,涛,迩,蝿,桧,侭,薮,篭,弥,麸'.split(
|
6
6
|
/,/,
|
7
7
|
)
|
8
8
|
|
@@ -29,6 +29,7 @@ export const toRegexPattern = (string: string) => {
|
|
29
29
|
_str = _str
|
30
30
|
.replace(/三栄町|四谷三栄町/g, '(三栄町|四谷三栄町)')
|
31
31
|
.replace(/鬮野川|くじ野川|くじの川/g, '(鬮野川|くじ野川|くじの川)')
|
32
|
+
.replace(/柿碕町|柿さき町/g, '(柿碕町|柿さき町)')
|
32
33
|
.replace(/通り|とおり/g, '(通り|とおり)')
|
33
34
|
.replace(/埠頭|ふ頭/g, '(埠頭|ふ頭)')
|
34
35
|
.replace(/番町|番丁/g, '(番町|番丁)')
|
@@ -3,8 +3,12 @@ import { kanji2number, findKanjiNumbers } from '@geolonia/japanese-numeral'
|
|
3
3
|
export const kan2num = (string: string) => {
|
4
4
|
const kanjiNumbers = findKanjiNumbers(string)
|
5
5
|
for (let i = 0; i < kanjiNumbers.length; i++) {
|
6
|
-
|
7
|
-
|
6
|
+
try {
|
7
|
+
// @ts-ignore
|
8
|
+
string = string.replace(kanjiNumbers[i], kanji2number(kanjiNumbers[i]));
|
9
|
+
} catch (error) {
|
10
|
+
// ignore
|
11
|
+
}
|
8
12
|
}
|
9
13
|
|
10
14
|
return string
|
@@ -1,26 +1,55 @@
|
|
1
1
|
import * as Normalize from './normalize'
|
2
2
|
import { promises as fs } from 'fs'
|
3
3
|
import unfetch from 'isomorphic-unfetch'
|
4
|
+
import { TransformRequestQuery } from './normalize'
|
4
5
|
|
5
|
-
const
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
fileURL.search = `?geolonia-api-key=${Normalize.config.geoloniaApiKey}`
|
12
|
-
}
|
13
|
-
return unfetch(fileURL.toString())
|
14
|
-
} else if (fileURL.protocol === 'file:') {
|
15
|
-
const filePath = decodeURI(fileURL.pathname)
|
6
|
+
export const requestHandlers = {
|
7
|
+
file: (fileURL: URL) => {
|
8
|
+
const filePath =
|
9
|
+
process.platform === 'win32'
|
10
|
+
? decodeURI(fileURL.pathname).substr(1)
|
11
|
+
: decodeURI(fileURL.pathname)
|
16
12
|
return {
|
17
13
|
json: async () => {
|
18
14
|
const contents = await fs.readFile(filePath)
|
19
15
|
return JSON.parse(contents.toString('utf-8'))
|
20
16
|
},
|
21
17
|
}
|
18
|
+
},
|
19
|
+
http: (fileURL: URL) => {
|
20
|
+
if (Normalize.config.geoloniaApiKey) {
|
21
|
+
fileURL.search = `?geolonia-api-key=${Normalize.config.geoloniaApiKey}`
|
22
|
+
}
|
23
|
+
return unfetch(fileURL.toString())
|
24
|
+
},
|
25
|
+
}
|
26
|
+
|
27
|
+
/**
|
28
|
+
* 正規化のためのデータを取得する
|
29
|
+
* @param input - Path part like '東京都/文京区.json'
|
30
|
+
* @param requestOptions - input を構造化したデータ
|
31
|
+
*/
|
32
|
+
const fetchOrReadFile = async (
|
33
|
+
input: string,
|
34
|
+
requestOptions: TransformRequestQuery = { level: -1 },
|
35
|
+
): Promise<Response | { json: () => Promise<unknown> }> => {
|
36
|
+
const fileURL = new URL(`${Normalize.config.japaneseAddressesApi}${input}`)
|
37
|
+
if (Normalize.config.transformRequest && requestOptions.level !== -1) {
|
38
|
+
const result = await Normalize.config.transformRequest(
|
39
|
+
fileURL,
|
40
|
+
requestOptions,
|
41
|
+
)
|
42
|
+
return {
|
43
|
+
json: async () => Promise.resolve(result),
|
44
|
+
}
|
22
45
|
} else {
|
23
|
-
|
46
|
+
if (fileURL.protocol === 'http:' || fileURL.protocol === 'https:') {
|
47
|
+
return requestHandlers.http(fileURL)
|
48
|
+
} else if (fileURL.protocol === 'file:') {
|
49
|
+
return requestHandlers.file(fileURL)
|
50
|
+
} else {
|
51
|
+
throw new Error(`Unknown URL schema: ${fileURL.protocol}`)
|
52
|
+
}
|
24
53
|
}
|
25
54
|
}
|
26
55
|
|
@@ -9,21 +9,51 @@ import {
|
|
9
9
|
getCityRegexPatterns,
|
10
10
|
getTownRegexPatterns,
|
11
11
|
getSameNamedPrefectureCityRegexPatterns,
|
12
|
+
// interface version 1
|
12
13
|
getResidentials,
|
13
14
|
getGaikuList,
|
15
|
+
// interface version 2
|
16
|
+
getAddrs,
|
17
|
+
PrefectureList,
|
18
|
+
TownList,
|
19
|
+
AddrList,
|
14
20
|
} from './lib/cacheRegexes'
|
15
21
|
import unfetch from 'isomorphic-unfetch'
|
16
22
|
|
23
|
+
type TransformRequestResponse = null | PrefectureList | TownList | AddrList
|
24
|
+
|
25
|
+
export type TransformRequestQuery = {
|
26
|
+
level: number // level = -1 は旧 API。 transformRequestFunction を設定しても無視する
|
27
|
+
pref?: string
|
28
|
+
city?: string
|
29
|
+
town?: string
|
30
|
+
}
|
31
|
+
|
32
|
+
export type TransformRequestFunction = (
|
33
|
+
url: URL,
|
34
|
+
query: TransformRequestQuery,
|
35
|
+
) => TransformRequestResponse | Promise<TransformRequestResponse>
|
36
|
+
|
17
37
|
/**
|
18
38
|
* normalize {@link Normalizer} の動作オプション。
|
19
39
|
*/
|
20
40
|
export interface Config {
|
41
|
+
/**
|
42
|
+
* レスポンス型のバージョン。デフォルト 1
|
43
|
+
* 1 の場合は jyukyo: string, gaiku: string
|
44
|
+
* 2 の場合は addr: string, other: string
|
45
|
+
*/
|
46
|
+
interfaceVersion: number
|
47
|
+
|
21
48
|
/** 住所データを URL 形式で指定。 file:// 形式で指定するとローカルファイルを参照できます。 */
|
22
49
|
japaneseAddressesApi: string
|
23
50
|
|
24
51
|
/** 町丁目のデータを何件までキャッシュするか。デフォルト 1,000 */
|
25
52
|
townCacheSize: number
|
26
53
|
|
54
|
+
/** 住所データへのリクエストを変形するオプション。 interfaceVersion === 2 で有効 */
|
55
|
+
transformRequest?: TransformRequestFunction
|
56
|
+
|
27
57
|
geoloniaApiKey?: string
|
28
58
|
}
|
29
59
|
export const config: Config = currentConfig
|
@@ -31,7 +61,7 @@ export const config: Config = currentConfig
|
|
31
61
|
/**
|
32
62
|
* 住所の正規化結果として戻されるオブジェクト
|
33
63
|
*/
|
34
|
-
|
64
|
+
interface NormalizeResult_v1 {
|
35
65
|
/** 都道府県 */
|
36
66
|
pref: string
|
37
67
|
/** 市区町村 */
|
@@ -46,7 +76,7 @@ export interface NormalizeResult {
|
|
46
76
|
addr: string
|
47
77
|
/** 緯度。データが存在しない場合は null */
|
48
78
|
lat: number | null
|
49
|
-
/**
|
79
|
+
/** 経度。データが存在しない場合は null */
|
50
80
|
lng: number | null
|
51
81
|
/**
|
52
82
|
* 住所文字列をどこまで判別できたかを表す正規化レベル
|
@@ -60,6 +90,34 @@ export interface NormalizeResult {
|
|
60
90
|
level: number
|
61
91
|
}
|
62
92
|
|
93
|
+
type NormalizeResult_v2 = {
|
94
|
+
/** 都道府県 */
|
95
|
+
pref: string
|
96
|
+
/** 市区町村 */
|
97
|
+
city: string
|
98
|
+
/** 町丁目 */
|
99
|
+
town: string
|
100
|
+
/** 住居表示または地番 */
|
101
|
+
addr: string
|
102
|
+
/** 正規化後の住所文字列 */
|
103
|
+
other?: string
|
104
|
+
/** 緯度。データが存在しない場合は null */
|
105
|
+
lat: number | null
|
106
|
+
/** 経度。データが存在しない場合は null */
|
107
|
+
lng: number | null
|
108
|
+
/**
|
109
|
+
* 住所文字列をどこまで判別できたかを表す正規化レベル
|
110
|
+
* - 0 - 都道府県も判別できなかった。
|
111
|
+
* - 1 - 都道府県まで判別できた。
|
112
|
+
* - 2 - 市区町村まで判別できた。
|
113
|
+
* - 3 - 町丁目まで判別できた。
|
114
|
+
* - 8 - 住居表示住所の街区符号・住居番号までの判別または地番住所の判別ができた。
|
115
|
+
*/
|
116
|
+
level: number
|
117
|
+
}
|
118
|
+
|
119
|
+
export type NormalizeResult = NormalizeResult_v1 | NormalizeResult_v2
|
120
|
+
|
63
121
|
/**
|
64
122
|
* 正規化関数の {@link normalize} のオプション
|
65
123
|
*/
|
@@ -92,6 +150,7 @@ export type Normalizer = (
|
|
92
150
|
|
93
151
|
export type FetchLike = (
|
94
152
|
input: string,
|
153
|
+
requestQuery?: TransformRequestQuery,
|
95
154
|
) => Promise<Response | { json: () => Promise<unknown> }>
|
96
155
|
|
97
156
|
const defaultOption = {
|
@@ -143,7 +202,13 @@ const normalizeResidentialPart = async (
|
|
143
202
|
pref: string,
|
144
203
|
city: string,
|
145
204
|
town: string,
|
146
|
-
)
|
205
|
+
): Promise<{
|
206
|
+
gaiku: string
|
207
|
+
jyukyo?: string
|
208
|
+
addr: string
|
209
|
+
lat: string
|
210
|
+
lng: string
|
211
|
+
} | null> => {
|
147
212
|
const [gaikuListItem, residentials] = await Promise.all([
|
148
213
|
getGaikuList(pref, city, town),
|
149
214
|
getResidentials(pref, city, town),
|
@@ -183,6 +248,27 @@ const normalizeResidentialPart = async (
|
|
183
248
|
return null
|
184
249
|
}
|
185
250
|
|
251
|
+
const normalizeAddrPart = async (
|
252
|
+
addr: string,
|
253
|
+
pref: string,
|
254
|
+
city: string,
|
255
|
+
town: string,
|
256
|
+
) => {
|
257
|
+
const addrListItem = await getAddrs(pref, city, town)
|
258
|
+
|
259
|
+
// 住居表示住所、および地番住所が見つからなかった
|
260
|
+
if (addrListItem.length === 0) {
|
261
|
+
return null
|
262
|
+
}
|
263
|
+
|
264
|
+
const addrItem = addrListItem.find((item) => addr.startsWith(item.addr))
|
265
|
+
if (addrItem) {
|
266
|
+
const other = addr.replace(addrItem.addr, '').trim()
|
267
|
+
return { addr: addrItem.addr, other, lat: addrItem.lat, lng: addrItem.lng }
|
268
|
+
}
|
269
|
+
return null
|
270
|
+
}
|
271
|
+
|
186
272
|
export const normalize: Normalizer = async (
|
187
273
|
address,
|
188
274
|
_option = defaultOption,
|
@@ -353,7 +439,7 @@ export const normalize: Normalizer = async (
|
|
353
439
|
'$1 $5',
|
354
440
|
)
|
355
441
|
.replace(
|
356
|
-
/([0-9〇一二三四五六七八九十百千]+)(番地?)([0-9〇一二三四五六七八九十百千]+)
|
442
|
+
/([0-9〇一二三四五六七八九十百千]+)\s*(番地?)\s*([0-9〇一二三四五六七八九十百千]+)\s*号?/,
|
357
443
|
'$1-$3',
|
358
444
|
)
|
359
445
|
.replace(/([0-9〇一二三四五六七八九十百千]+)番地?/, '$1')
|
@@ -378,7 +464,7 @@ export const normalize: Normalizer = async (
|
|
378
464
|
// `-1` のようなケース
|
379
465
|
return kan2num(s)
|
380
466
|
})
|
381
|
-
.replace(/-[^0-9]
|
467
|
+
.replace(/-[^0-9]([0-9〇一二三四五六七八九十百千]+)/, (s) => {
|
382
468
|
// `-あ1` のようなケース
|
383
469
|
return kan2num(zen2han(s))
|
384
470
|
})
|
@@ -392,43 +478,85 @@ export const normalize: Normalizer = async (
|
|
392
478
|
|
393
479
|
addr = patchAddr(pref, city, town, addr)
|
394
480
|
|
395
|
-
// 住居表示住所リストを使い番地号までの正規化を行う
|
396
|
-
if (option.level > 3 && normalized && town) {
|
397
|
-
normalized = await normalizeResidentialPart(addr, pref, city, town)
|
398
|
-
}
|
399
|
-
if (normalized) {
|
400
|
-
lat = parseFloat(normalized.lat)
|
401
|
-
lng = parseFloat(normalized.lng)
|
402
|
-
}
|
403
|
-
|
404
|
-
if (Number.isNaN(lat) || Number.isNaN(lng)) {
|
405
|
-
lat = null
|
406
|
-
lng = null
|
407
|
-
}
|
408
|
-
|
409
481
|
if (pref) level = level + 1
|
410
482
|
if (city) level = level + 1
|
411
483
|
if (town) level = level + 1
|
412
484
|
|
413
|
-
|
414
|
-
pref,
|
415
|
-
city,
|
416
|
-
town,
|
417
|
-
addr,
|
418
|
-
lat,
|
419
|
-
lng,
|
420
|
-
level,
|
485
|
+
if (option.level <= 3 || level < 3) {
|
486
|
+
return { pref, city, town, addr, level, lat, lng }
|
421
487
|
}
|
422
488
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
489
|
+
// ======================== Advanced section ========================
|
490
|
+
// これ以下は地番住所または住居表示住所までの正規化・ジオコーディングを行う処理
|
491
|
+
// 現状、インターフェース v1 と v2 が存在する
|
492
|
+
// japanese-addresses のフォーマット、および normalize 関数の戻り値が異なる
|
493
|
+
// 将来的に v2 に統一することを検討中
|
494
|
+
// ==================================================================
|
495
|
+
|
496
|
+
// v2 のインターフェース
|
497
|
+
if (currentConfig.interfaceVersion === 2) {
|
498
|
+
const normalizedAddrPart = await normalizeAddrPart(addr, pref, city, town)
|
499
|
+
let other = undefined
|
500
|
+
if (normalizedAddrPart) {
|
501
|
+
addr = normalizedAddrPart.addr
|
502
|
+
if (normalizedAddrPart.other) {
|
503
|
+
other = normalizedAddrPart.other
|
504
|
+
}
|
505
|
+
if (normalizedAddrPart.lat !== null)
|
506
|
+
lat = parseFloat(normalizedAddrPart.lat)
|
507
|
+
if (normalizedAddrPart.lng !== null)
|
508
|
+
lng = parseFloat(normalizedAddrPart.lng)
|
509
|
+
level = 8
|
510
|
+
}
|
511
|
+
const result: NormalizeResult_v2 = {
|
512
|
+
pref,
|
513
|
+
city,
|
514
|
+
town,
|
515
|
+
addr,
|
516
|
+
level,
|
517
|
+
lat,
|
518
|
+
lng,
|
519
|
+
}
|
520
|
+
if (other) {
|
521
|
+
result.other = other
|
522
|
+
}
|
523
|
+
return result
|
524
|
+
} else if (currentConfig.interfaceVersion === 1) {
|
525
|
+
// 住居表示住所リストを使い番地号までの正規化を行う
|
526
|
+
if (option.level > 3 && normalized && town) {
|
527
|
+
normalized = await normalizeResidentialPart(addr, pref, city, town)
|
528
|
+
}
|
529
|
+
if (normalized) {
|
530
|
+
lat = parseFloat(normalized.lat)
|
531
|
+
lng = parseFloat(normalized.lng)
|
532
|
+
}
|
533
|
+
|
534
|
+
if (Number.isNaN(lat) || Number.isNaN(lng)) {
|
535
|
+
lat = null
|
536
|
+
lng = null
|
537
|
+
}
|
538
|
+
|
539
|
+
const result: NormalizeResult_v1 = {
|
540
|
+
pref,
|
541
|
+
city,
|
542
|
+
town,
|
543
|
+
addr,
|
544
|
+
lat,
|
545
|
+
lng,
|
546
|
+
level,
|
547
|
+
}
|
548
|
+
if (normalized && 'gaiku' in normalized) {
|
549
|
+
result.addr = normalized.addr
|
550
|
+
result.gaiku = normalized.gaiku
|
551
|
+
result.level = 7
|
552
|
+
}
|
553
|
+
if (normalized && 'jyukyo' in normalized) {
|
554
|
+
result.jyukyo = normalized.jyukyo
|
555
|
+
result.level = 8
|
556
|
+
}
|
432
557
|
|
433
|
-
|
558
|
+
return result
|
559
|
+
} else {
|
560
|
+
throw new Error('invalid interfaceVersion')
|
561
|
+
}
|
434
562
|
}
|
data/js/package-lock.json
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
"": {
|
7
7
|
"name": "japanese_address_parser",
|
8
8
|
"dependencies": {
|
9
|
-
"@geolonia/normalize-japanese-addresses": "^2.
|
9
|
+
"@geolonia/normalize-japanese-addresses": "^2.9.2"
|
10
10
|
},
|
11
11
|
"devDependencies": {}
|
12
12
|
},
|
@@ -16,9 +16,9 @@
|
|
16
16
|
"integrity": "sha512-xzSsIHhyyjqNpW8qSh5bFMu3FJvyJGBbNZ/t8clPHkigjUdZ7Ck8wkzCkcwlVd00RkoHwGDLnvXx5W0WiGK0TQ=="
|
17
17
|
},
|
18
18
|
"node_modules/@geolonia/normalize-japanese-addresses": {
|
19
|
-
"version": "2.
|
20
|
-
"resolved": "https://registry.npmjs.org/@geolonia/normalize-japanese-addresses/-/normalize-japanese-addresses-2.
|
21
|
-
"integrity": "sha512-
|
19
|
+
"version": "2.9.2",
|
20
|
+
"resolved": "https://registry.npmjs.org/@geolonia/normalize-japanese-addresses/-/normalize-japanese-addresses-2.9.2.tgz",
|
21
|
+
"integrity": "sha512-sbeV/seJ/ue6pn2sLo+Ccunl6eB3yNhrUIzNpT2IiR9Fw6fW5f1lLdApODqhTmz++xr1dyPwZOzTXCGEaXejDA==",
|
22
22
|
"dependencies": {
|
23
23
|
"@geolonia/japanese-numeral": "^0.1.16",
|
24
24
|
"isomorphic-unfetch": "^3.1.0",
|
@@ -101,9 +101,9 @@
|
|
101
101
|
"integrity": "sha512-xzSsIHhyyjqNpW8qSh5bFMu3FJvyJGBbNZ/t8clPHkigjUdZ7Ck8wkzCkcwlVd00RkoHwGDLnvXx5W0WiGK0TQ=="
|
102
102
|
},
|
103
103
|
"@geolonia/normalize-japanese-addresses": {
|
104
|
-
"version": "2.
|
105
|
-
"resolved": "https://registry.npmjs.org/@geolonia/normalize-japanese-addresses/-/normalize-japanese-addresses-2.
|
106
|
-
"integrity": "sha512-
|
104
|
+
"version": "2.9.2",
|
105
|
+
"resolved": "https://registry.npmjs.org/@geolonia/normalize-japanese-addresses/-/normalize-japanese-addresses-2.9.2.tgz",
|
106
|
+
"integrity": "sha512-sbeV/seJ/ue6pn2sLo+Ccunl6eB3yNhrUIzNpT2IiR9Fw6fW5f1lLdApODqhTmz++xr1dyPwZOzTXCGEaXejDA==",
|
107
107
|
"requires": {
|
108
108
|
"@geolonia/japanese-numeral": "^0.1.16",
|
109
109
|
"isomorphic-unfetch": "^3.1.0",
|
data/js/package.json
CHANGED
@@ -353,6 +353,7 @@ name,name_kana,name_romaji,nickname,latitude,longitude
|
|
353
353
|
下鬼柳,シモオニヤナギ,SHIMONIYANAGI,十六地割,39.266345,141.096501
|
354
354
|
下鬼柳,シモオニヤナギ,SHIMONIYANAGI,十四地割,39.266478,141.102455
|
355
355
|
下鬼柳,シモオニヤナギ,SHIMONIYANAGI,四地割,39.282827,141.091036
|
356
|
+
しらゆり,シラユリ,SHIRAYURI,,,
|
356
357
|
新穀町一丁目,シンコクチョウ 1,SHINKOKUCHO 1,,39.289124,141.112788
|
357
358
|
新穀町二丁目,シンコクチョウ 2,SHINKOKUCHO 2,,39.289984,141.110576
|
358
359
|
諏訪町一丁目,スワチョウ 1,SUWACHO 1,,39.286243,141.11941
|
@@ -1,12 +1,4 @@
|
|
1
1
|
name,name_kana,name_romaji,nickname,latitude,longitude
|
2
|
-
落合桧和田,,,,38.430514,140.947568
|
3
|
-
落合桧和田,,,中道,38.430873,140.92928
|
4
|
-
落合桧和田,,,八幡堂,38.427406,140.938836
|
5
|
-
落合桧和田,,,千刈田,38.430733,140.932626
|
6
|
-
落合桧和田,,,新高原,38.432263,140.930077
|
7
|
-
落合桧和田,,,糯田,38.429822,140.932751
|
8
|
-
落合桧和田,,,遠浦,38.431684,140.925735
|
9
|
-
落合桧和田,,,高原,38.430335,140.93046
|
10
2
|
落合相川,オチアイアイカワ,OCHIAI AIKAWA,,38.442109,140.921086
|
11
3
|
落合相川,オチアイアイカワ,OCHIAI AIKAWA,塚越,38.438884,140.930493
|
12
4
|
落合相川,オチアイアイカワ,OCHIAI AIKAWA,塚越三番,38.439941,140.935737
|
@@ -31,6 +23,14 @@ name,name_kana,name_romaji,nickname,latitude,longitude
|
|
31
23
|
落合蒜袋,オチアイヒルブクロ,OCHIAI HIRUBUKURO,菱柄,38.447649,140.911786
|
32
24
|
落合蒜袋,オチアイヒルブクロ,OCHIAI HIRUBUKURO,行屋下,38.449988,140.909907
|
33
25
|
落合蒜袋,オチアイヒルブクロ,OCHIAI HIRUBUKURO,長町,38.449781,140.908786
|
26
|
+
落合桧和田,オチアイヒワダ,,,38.430514,140.947568
|
27
|
+
落合桧和田,オチアイヒワダ,,中道,38.430873,140.92928
|
28
|
+
落合桧和田,オチアイヒワダ,,八幡堂,38.427406,140.938836
|
29
|
+
落合桧和田,オチアイヒワダ,,千刈田,38.430733,140.932626
|
30
|
+
落合桧和田,オチアイヒワダ,,新高原,38.432263,140.930077
|
31
|
+
落合桧和田,オチアイヒワダ,,糯田,38.429822,140.932751
|
32
|
+
落合桧和田,オチアイヒワダ,,遠浦,38.431684,140.925735
|
33
|
+
落合桧和田,オチアイヒワダ,,高原,38.430335,140.93046
|
34
34
|
落合報恩寺,オチアイホウオンジ,OCHIAI HOONJI,,38.443497,140.953906
|
35
35
|
落合報恩寺,オチアイホウオンジ,OCHIAI HOONJI,大久保,38.44271,140.944656
|
36
36
|
落合報恩寺,オチアイホウオンジ,OCHIAI HOONJI,竹沢下,38.438117,140.944617
|
@@ -43,9 +43,13 @@ name,name_kana,name_romaji,nickname,latitude,longitude
|
|
43
43
|
猪岡,イノオカ,INOKA,水上,39.31756,140.521665
|
44
44
|
猪岡,イノオカ,INOKA,長瀞,39.315335,140.519002
|
45
45
|
梅の木町,ウメノキマチ,UMENOKIMACHI,,39.315702,140.557751
|
46
|
+
駅西一丁目,エキニシ 1,EKINISHI 1,,,
|
47
|
+
駅西二丁目,エキニシ 2,EKINISHI 2,,,
|
48
|
+
駅西三丁目,エキニシ 3,EKINISHI 3,,,
|
46
49
|
駅前町,エキマエチョウ,EKIMAECHO,,39.308784,140.561408
|
47
50
|
駅南一丁目,エキミナミ 1,EKIMINAMI 1,,39.302415,140.56071
|
48
51
|
駅南二丁目,エキミナミ 2,EKIMINAMI 2,,39.302577,140.558314
|
52
|
+
駅南三丁目,エキミナミ 3,EKIMINAMI 3,,,
|
49
53
|
追廻一丁目,オイマワシ 1,OIMAWASHI 1,,39.332247,140.568572
|
50
54
|
追廻二丁目,オイマワシ 2,OIMAWASHI 2,,39.331959,140.570628
|
51
55
|
追廻三丁目,オイマワシ 3,OIMAWASHI 3,,39.334844,140.572056
|
@@ -408,6 +412,7 @@ name,name_kana,name_romaji,nickname,latitude,longitude
|
|
408
412
|
三本柳,サンボンヤナギ,SAMBONYANAGI,街道下,39.321213,140.538111
|
409
413
|
三本柳,サンボンヤナギ,SAMBONYANAGI,馬場尻,39.324461,140.53328
|
410
414
|
三枚橋一丁目,サンマイバシ 1,SAMMAIBASHI 1,,39.313272,140.558151
|
415
|
+
三枚橋二丁目,サンマイバシ 2,SANMAIBASHI 2,,,
|
411
416
|
静町,シズカマチ,SHIZUKAMACHI,,39.332264,140.545279
|
412
417
|
静町,シズカマチ,SHIZUKAMACHI,北小屋,39.336115,140.543985
|
413
418
|
静町,シズカマチ,SHIZUKAMACHI,払小屋,39.334205,140.543283
|
@@ -76,6 +76,10 @@ name,name_kana,name_romaji,nickname,latitude,longitude
|
|
76
76
|
鉢形台一丁目,ハチガタダイ 1,HACHIGATADAI 1,,35.96364,140.647669
|
77
77
|
鉢形台二丁目,ハチガタダイ 2,HACHIGATADAI 2,,35.961016,140.648239
|
78
78
|
鉢形台三丁目,ハチガタダイ 3,HACHIGATADAI 3,,35.95818,140.649236
|
79
|
+
平井東一丁目,ヒライヒガシ 1,HIRAIHIGASHI 1,,,
|
80
|
+
平井東二丁目,ヒライヒガシ 2,HIRAIHIGASHI 2,,,
|
81
|
+
平井東三丁目,ヒライヒガシ 3,HIRAIHIGASHI 3,,,
|
82
|
+
平井東四丁目,ヒライヒガシ 4,HIRAIHIGASHI 4,,,
|
79
83
|
緑ヶ丘一丁目,ミドリガオカ 1,MIDORIGAOKA 1,,35.973731,140.623658
|
80
84
|
緑ヶ丘二丁目,ミドリガオカ 2,MIDORIGAOKA 2,,35.972016,140.622091
|
81
85
|
緑ヶ丘三丁目,ミドリガオカ 3,MIDORIGAOKA 3,,35.971853,140.618483
|