thai-address-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +539 -0
- package/THIRD_PARTY_LICENSES.md +27 -0
- package/dist/index.cjs +75335 -0
- package/dist/index.d.cts +100 -0
- package/dist/index.d.ts +100 -0
- package/dist/index.js +75296 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
# thai-address-sdk
|
|
2
|
+
|
|
3
|
+
ไลบรารี TypeScript/JavaScript สำหรับค้นหา กรอง ตรวจคำสะกด และจัดรูปแบบข้อมูล
|
|
4
|
+
จังหวัด อำเภอ/เขต และตำบล/แขวงของประเทศไทยแบบ offline
|
|
5
|
+
|
|
6
|
+
ข้อมูลและระบบค้นหาถูก bundle อยู่ใน package จึงไม่ต้องเรียก REST API, ไม่ต้องใช้ API
|
|
7
|
+
key และไม่ส่งข้อความที่อยู่ของผู้ใช้ออกจาก application
|
|
8
|
+
|
|
9
|
+
## ความสามารถ
|
|
10
|
+
|
|
11
|
+
- ข้อมูลครบ 77 จังหวัด พร้อมอำเภอ/เขตและตำบล/แขวง
|
|
12
|
+
- ค้นหาด้วยชื่อไทย ชื่ออังกฤษ และ alias ที่ใช้ทั่วไป
|
|
13
|
+
- รองรับ exact, prefix, contains และ fuzzy search
|
|
14
|
+
- ช่วยแก้คำสะกด เช่น `อยูทยา` → `พระนครศรีอยุธยา`
|
|
15
|
+
- ตรวจความสัมพันธ์จังหวัด → อำเภอ → ตำบลจากข้อความหลายส่วน
|
|
16
|
+
- กรองข้อมูลสำหรับทำ dependent dropdown
|
|
17
|
+
- จัดรูปแบบที่อยู่ไทยเต็ม ไทยย่อ ไม่มีคำนำหน้า และภาษาอังกฤษ
|
|
18
|
+
- รองรับ ESM, CommonJS และมี TypeScript declarations
|
|
19
|
+
- ทำงานได้ทั้ง Node.js และ browser ผ่าน bundler
|
|
20
|
+
|
|
21
|
+
## ขอบเขตของ package
|
|
22
|
+
|
|
23
|
+
Package นี้จัดการเฉพาะข้อมูลต่อไปนี้:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
จังหวัด → อำเภอ/เขต → ตำบล/แขวง → รหัสไปรษณีย์
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
ยังไม่แยกเลขที่บ้าน หมู่บ้าน อาคาร ซอย ถนน พิกัด GPS หรือข้อมูลส่วนบุคคลอื่น
|
|
30
|
+
|
|
31
|
+
## การติดตั้ง
|
|
32
|
+
|
|
33
|
+
เมื่อนำ package ขึ้น npm registry แล้ว:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install thai-address-sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
หรือใช้ package manager อื่น:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pnpm add thai-address-sdk
|
|
43
|
+
yarn add thai-address-sdk
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
ระหว่างพัฒนาใน repository นี้ สามารถติดตั้งจากโฟลเดอร์โดยตรง:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install ../thai-address/npm-sdk
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
ต้องใช้ Node.js 18 ขึ้นไปสำหรับการพัฒนาและทดสอบ package
|
|
53
|
+
|
|
54
|
+
## เริ่มต้นใช้งาน
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import {
|
|
58
|
+
getDistricts,
|
|
59
|
+
getProvinces,
|
|
60
|
+
getSubdistricts,
|
|
61
|
+
normalizeAddress,
|
|
62
|
+
search,
|
|
63
|
+
} from "thai-address-sdk";
|
|
64
|
+
|
|
65
|
+
// ค้นหาชื่อที่สะกดผิด
|
|
66
|
+
const results = search("อยูทยา");
|
|
67
|
+
|
|
68
|
+
// วิเคราะห์หลายส่วนและคืน hierarchy ที่สัมพันธ์กัน
|
|
69
|
+
const normalized = normalizeAddress("บางปะอิน อยูทยา");
|
|
70
|
+
|
|
71
|
+
// ใช้สร้าง dropdown จังหวัด → อำเภอ → ตำบล
|
|
72
|
+
const provinces = getProvinces();
|
|
73
|
+
const districts = getDistricts({ provinceCode: 14 });
|
|
74
|
+
const subdistricts = getSubdistricts({ districtCode: 1406 });
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
ทุกฟังก์ชันเป็น synchronous เพราะประมวลผลจากข้อมูลใน memory:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const results = search("เชียงใหม่"); // ไม่ต้อง await
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## การ Import
|
|
84
|
+
|
|
85
|
+
### ESM และ TypeScript
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { search, normalizeAddress } from "thai-address-sdk";
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### CommonJS
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
const { search, normalizeAddress } = require("thai-address-sdk");
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## `search(query, options?)`
|
|
98
|
+
|
|
99
|
+
ใช้ค้นหาหน่วยการปกครองจากคำเดียว โดยค้นหาได้ทั้งจังหวัด อำเภอ และตำบล
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const results = search("อยูทยา", {
|
|
103
|
+
levels: ["province"],
|
|
104
|
+
limit: 5,
|
|
105
|
+
minScore: 0.72,
|
|
106
|
+
format: "full_th",
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Search options
|
|
111
|
+
|
|
112
|
+
| Option | Type | Default | ความหมาย |
|
|
113
|
+
| ---------- | ----------------------------------------------- | ----------- | ------------------------------------- |
|
|
114
|
+
| `levels` | `("province" \| "district" \| "subdistrict")[]` | ทุกระดับ | จำกัดประเภทผลลัพธ์ |
|
|
115
|
+
| `limit` | `number` | `10` | จำนวนผลลัพธ์ สูงสุดภายในระบบคือ `100` |
|
|
116
|
+
| `minScore` | `number` | `0.72` | คะแนนต่ำสุดที่ยอมรับ |
|
|
117
|
+
| `format` | `"full_th" \| "short_th" \| "plain_th" \| "en"` | `"full_th"` | รูปแบบ `formattedAddress` |
|
|
118
|
+
|
|
119
|
+
### ตัวอย่าง fuzzy search
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
const [result] = search("อยูทยา", {
|
|
123
|
+
levels: ["province"],
|
|
124
|
+
limit: 1,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
console.log(result);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
ผลลัพธ์:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"type": "province",
|
|
135
|
+
"province": {
|
|
136
|
+
"id": 5,
|
|
137
|
+
"provinceCode": 14,
|
|
138
|
+
"provinceNameEn": "Phra Nakhon Si Ayutthaya",
|
|
139
|
+
"provinceNameTh": "พระนครศรีอยุธยา"
|
|
140
|
+
},
|
|
141
|
+
"formattedAddress": "จังหวัดพระนครศรีอยุธยา",
|
|
142
|
+
"match": {
|
|
143
|
+
"input": "อยูทยา",
|
|
144
|
+
"matchedText": "อยุธยา",
|
|
145
|
+
"matchType": "fuzzy",
|
|
146
|
+
"confidence": 0.817,
|
|
147
|
+
"corrected": true
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### ประเภทการ match
|
|
153
|
+
|
|
154
|
+
| `matchType` | ความหมาย |
|
|
155
|
+
| ----------- | ------------------------------------------ |
|
|
156
|
+
| `exact` | ตรงกับชื่อใน dataset ทุกตัวอักษร |
|
|
157
|
+
| `alias` | ตรงกับชื่อย่อหรือชื่อที่ใช้ทั่วไป |
|
|
158
|
+
| `prefix` | คำค้นหรือชื่อข้อมูลขึ้นต้นตรงกัน |
|
|
159
|
+
| `contains` | คำค้นเป็นส่วนหนึ่งของชื่อ หรือในทางกลับกัน |
|
|
160
|
+
| `fuzzy` | สะกดใกล้เคียงจากการคำนวณระยะห่างของคำ |
|
|
161
|
+
|
|
162
|
+
`confidence` อยู่ระหว่าง `0` ถึง `1` และเป็นคะแนนจัดอันดับภายใน SDK
|
|
163
|
+
ไม่ควรนำไปตีความเป็นเปอร์เซ็นต์ความถูกต้องทางสถิติ
|
|
164
|
+
|
|
165
|
+
ถ้าต้องการให้ผลลัพธ์เข้มงวดขึ้น สามารถเพิ่ม `minScore`:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
const strictResults = search("อยูทยา", { minScore: 0.9 });
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## `normalizeAddress(input, options?)`
|
|
172
|
+
|
|
173
|
+
ใช้เมื่อตัว input มีหลายส่วน เช่นจังหวัดร่วมกับอำเภอหรือตำบล ระบบจะใช้ hierarchy
|
|
174
|
+
ช่วยเลือกผลลัพธ์ที่สัมพันธ์กัน
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
const result = normalizeAddress("บางปะอิน อยูทยา");
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
ผลลัพธ์:
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"input": "บางปะอิน อยูทยา",
|
|
185
|
+
"normalizedInput": "บางปะอิน อยูทยา",
|
|
186
|
+
"status": "matched",
|
|
187
|
+
"bestMatch": {
|
|
188
|
+
"province": {
|
|
189
|
+
"provinceCode": 14,
|
|
190
|
+
"provinceNameTh": "พระนครศรีอยุธยา",
|
|
191
|
+
"provinceNameEn": "Phra Nakhon Si Ayutthaya"
|
|
192
|
+
},
|
|
193
|
+
"district": {
|
|
194
|
+
"districtCode": 1406,
|
|
195
|
+
"districtNameTh": "บางปะอิน",
|
|
196
|
+
"districtNameEn": "Bang Pa-In"
|
|
197
|
+
},
|
|
198
|
+
"postalCode": 13160,
|
|
199
|
+
"formattedAddress": "อำเภอบางปะอิน จังหวัดพระนครศรีอยุธยา"
|
|
200
|
+
},
|
|
201
|
+
"confidence": 0.929,
|
|
202
|
+
"alternatives": [],
|
|
203
|
+
"corrections": [
|
|
204
|
+
{
|
|
205
|
+
"input": "อยูทยา",
|
|
206
|
+
"normalized": "พระนครศรีอยุธยา",
|
|
207
|
+
"field": "province",
|
|
208
|
+
"matchType": "fuzzy"
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
> ตัวอย่างด้านบนตัด field `id`, code เชื่อมโยง และ `postalCode` บางส่วนออกเพื่อให้อ่านง่าย
|
|
215
|
+
> object ที่ได้จริงจะมี field ครบตาม TypeScript types
|
|
216
|
+
|
|
217
|
+
### Normalize options
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
const result = normalizeAddress("ต สุเทพ อ เมือง จ เชียงใหม่", {
|
|
221
|
+
format: "short_th",
|
|
222
|
+
limit: 5,
|
|
223
|
+
minScore: 0.72,
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
| Option | Default | ความหมาย |
|
|
228
|
+
| ---------- | ----------- | ---------------------------------------------- |
|
|
229
|
+
| `format` | `"full_th"` | รูปแบบที่อยู่ผลลัพธ์ |
|
|
230
|
+
| `limit` | `5` | จำนวน candidate ที่ใช้พิจารณา สูงสุด `20` |
|
|
231
|
+
| `minScore` | `0.72` | คะแนนขั้นต่ำของแต่ละส่วนที่นำมาสร้าง hierarchy |
|
|
232
|
+
|
|
233
|
+
### Normalize status
|
|
234
|
+
|
|
235
|
+
| Status | ความหมาย |
|
|
236
|
+
| ----------- | ------------------------------------------------------ |
|
|
237
|
+
| `matched` | พบผลลัพธ์ที่ผ่านเกณฑ์และไม่มีคู่แข่งคะแนนใกล้เคียง |
|
|
238
|
+
| `partial` | พบข้อมูลบางส่วนแต่คะแนนยังไม่สูงพอ |
|
|
239
|
+
| `ambiguous` | มีมากกว่าหนึ่งผลลัพธ์ที่คะแนนใกล้กัน ควรให้ผู้ใช้เลือก |
|
|
240
|
+
| `not_found` | ไม่พบผลลัพธ์ที่ผ่าน `minScore` |
|
|
241
|
+
|
|
242
|
+
ควรตรวจ `status` และ `bestMatch` ทุกครั้งก่อนนำข้อมูลไปบันทึก:
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
const result = normalizeAddress(userInput);
|
|
246
|
+
|
|
247
|
+
switch (result.status) {
|
|
248
|
+
case "matched":
|
|
249
|
+
saveAddress(result.bestMatch);
|
|
250
|
+
break;
|
|
251
|
+
case "ambiguous":
|
|
252
|
+
showAddressChoices([result.bestMatch, ...result.alternatives]);
|
|
253
|
+
break;
|
|
254
|
+
case "partial":
|
|
255
|
+
askForMoreAddressDetails(result.bestMatch);
|
|
256
|
+
break;
|
|
257
|
+
case "not_found":
|
|
258
|
+
showNotFoundMessage();
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## `smartSearch(input, options?)`
|
|
264
|
+
|
|
265
|
+
`smartSearch` เป็น alias ของ `normalizeAddress` และคืนผลลัพธ์รูปแบบเดียวกัน:
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
import { smartSearch } from "thai-address-sdk";
|
|
269
|
+
|
|
270
|
+
const result = smartSearch("ต สุเทพ อ เมือง จ เชียงใหม่");
|
|
271
|
+
console.log(result.bestMatch?.formattedAddress);
|
|
272
|
+
// ตำบลสุเทพ อำเภอเมืองเชียงใหม่ จังหวัดเชียงใหม่
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
เลือกใช้ชื่อที่เหมาะกับบริบทของ application ได้ แต่ไม่จำเป็นต้องเรียกทั้งสองฟังก์ชัน
|
|
276
|
+
|
|
277
|
+
## Filter API สำหรับ dropdown
|
|
278
|
+
|
|
279
|
+
### จังหวัดทั้งหมด
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
import { getProvinces } from "thai-address-sdk";
|
|
283
|
+
|
|
284
|
+
const provinces = getProvinces();
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### อำเภอทั้งหมดในจังหวัด
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
import { getDistricts } from "thai-address-sdk";
|
|
291
|
+
|
|
292
|
+
const districts = getDistricts({ provinceCode: 14 });
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### ตำบลทั้งหมดในอำเภอ
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
import { getSubdistricts } from "thai-address-sdk";
|
|
299
|
+
|
|
300
|
+
const subdistricts = getSubdistricts({ districtCode: 1406 });
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
สามารถกรองตำบลด้วยทั้งจังหวัดและอำเภอพร้อมกัน:
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
const subdistricts = getSubdistricts({
|
|
307
|
+
provinceCode: 14,
|
|
308
|
+
districtCode: 1406,
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### ค้นหาด้วย code
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
import { getDistrict, getProvince, getSubdistrict } from "thai-address-sdk";
|
|
316
|
+
|
|
317
|
+
const province = getProvince(14);
|
|
318
|
+
const district = getDistrict(1406);
|
|
319
|
+
const subdistrict = getSubdistrict(140601);
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
ฟังก์ชันแบบ code คืน `undefined` เมื่อไม่พบข้อมูล:
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
const province = getProvince(999);
|
|
326
|
+
|
|
327
|
+
if (!province) {
|
|
328
|
+
console.log("ไม่พบจังหวัด");
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## ตัวอย่าง dependent dropdown
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
import { getDistricts, getProvinces, getSubdistricts } from "thai-address-sdk";
|
|
336
|
+
|
|
337
|
+
const state = {
|
|
338
|
+
provinceCode: undefined as number | undefined,
|
|
339
|
+
districtCode: undefined as number | undefined,
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const provinceOptions = getProvinces().map((province) => ({
|
|
343
|
+
value: province.provinceCode,
|
|
344
|
+
label: province.provinceNameTh,
|
|
345
|
+
}));
|
|
346
|
+
|
|
347
|
+
function onProvinceChange(provinceCode: number) {
|
|
348
|
+
state.provinceCode = provinceCode;
|
|
349
|
+
state.districtCode = undefined;
|
|
350
|
+
|
|
351
|
+
return getDistricts({ provinceCode }).map((district) => ({
|
|
352
|
+
value: district.districtCode,
|
|
353
|
+
label: district.districtNameTh,
|
|
354
|
+
}));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function onDistrictChange(districtCode: number) {
|
|
358
|
+
state.districtCode = districtCode;
|
|
359
|
+
|
|
360
|
+
return getSubdistricts({ districtCode }).map((subdistrict) => ({
|
|
361
|
+
value: subdistrict.subdistrictCode,
|
|
362
|
+
label: subdistrict.subdistrictNameTh,
|
|
363
|
+
postalCode: subdistrict.postalCode,
|
|
364
|
+
}));
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
เมื่อเปลี่ยนจังหวัดควร reset อำเภอและตำบลที่เลือกไว้ เพื่อไม่ให้เกิด hierarchy ที่ไม่สัมพันธ์กัน
|
|
369
|
+
|
|
370
|
+
## `formatAddress(address, format?)`
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
import {
|
|
374
|
+
formatAddress,
|
|
375
|
+
getDistrict,
|
|
376
|
+
getProvince,
|
|
377
|
+
getSubdistrict,
|
|
378
|
+
} from "thai-address-sdk";
|
|
379
|
+
|
|
380
|
+
const province = getProvince(10);
|
|
381
|
+
const district = getDistrict(1047);
|
|
382
|
+
const subdistrict = getSubdistrict(104702);
|
|
383
|
+
|
|
384
|
+
if (province && district && subdistrict) {
|
|
385
|
+
formatAddress({ province, district, subdistrict }, "full_th");
|
|
386
|
+
// แขวงบางนาเหนือ เขตบางนา กรุงเทพมหานคร
|
|
387
|
+
|
|
388
|
+
formatAddress({ province, district, subdistrict }, "plain_th");
|
|
389
|
+
// บางนาเหนือ บางนา กรุงเทพมหานคร
|
|
390
|
+
|
|
391
|
+
formatAddress({ province, district, subdistrict }, "en");
|
|
392
|
+
// Bang Na Nuea, Bang Na, Bangkok
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
รูปแบบที่รองรับ:
|
|
397
|
+
|
|
398
|
+
| Format | ตัวอย่าง |
|
|
399
|
+
| ---------- | ------------------------------------------------ |
|
|
400
|
+
| `full_th` | `ตำบลสุเทพ อำเภอเมืองเชียงใหม่ จังหวัดเชียงใหม่` |
|
|
401
|
+
| `short_th` | `ต.สุเทพ อ.เมืองเชียงใหม่ จ.เชียงใหม่` |
|
|
402
|
+
| `plain_th` | `สุเทพ เมืองเชียงใหม่ เชียงใหม่` |
|
|
403
|
+
| `en` | `Suthep, Mueang Chiang Mai, Chiang Mai` |
|
|
404
|
+
|
|
405
|
+
กรุงเทพมหานครจะใช้ `แขวง` และ `เขต` อัตโนมัติ
|
|
406
|
+
|
|
407
|
+
## Text utilities
|
|
408
|
+
|
|
409
|
+
ฟังก์ชันเหล่านี้เป็น utility ระดับต่ำสำหรับกรณีที่ application ต้องการจัดการข้อความเอง:
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
import {
|
|
413
|
+
damerauLevenshtein,
|
|
414
|
+
normalizeText,
|
|
415
|
+
stripAddressLabels,
|
|
416
|
+
} from "thai-address-sdk";
|
|
417
|
+
|
|
418
|
+
normalizeText(" กรุงเทพฯ ");
|
|
419
|
+
// กรุงเทพ
|
|
420
|
+
|
|
421
|
+
stripAddressLabels("ต. สุเทพ อ. เมืองเชียงใหม่ จ. เชียงใหม่");
|
|
422
|
+
// สุเทพ เมืองเชียงใหม่ เชียงใหม่
|
|
423
|
+
|
|
424
|
+
damerauLevenshtein("อยูทยา", "อยุธยา");
|
|
425
|
+
// ระยะห่างของตัวอักษร
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
สำหรับ use case ทั่วไปควรใช้ `search` หรือ `normalizeAddress` แทน utility เหล่านี้
|
|
429
|
+
|
|
430
|
+
## TypeScript types
|
|
431
|
+
|
|
432
|
+
Package export types หลักดังนี้:
|
|
433
|
+
|
|
434
|
+
```ts
|
|
435
|
+
import type {
|
|
436
|
+
AddressFormat,
|
|
437
|
+
AddressHierarchy,
|
|
438
|
+
AddressLevel,
|
|
439
|
+
District,
|
|
440
|
+
NormalizeResult,
|
|
441
|
+
Province,
|
|
442
|
+
SearchOptions,
|
|
443
|
+
SearchResult,
|
|
444
|
+
Subdistrict,
|
|
445
|
+
} from "thai-address-sdk";
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Code ทุกชนิดเป็น `number`:
|
|
449
|
+
|
|
450
|
+
```ts
|
|
451
|
+
province.provinceCode; // 2 หลัก เช่น 14
|
|
452
|
+
district.districtCode; // 4 หลัก เช่น 1406
|
|
453
|
+
subdistrict.subdistrictCode; // 6 หลัก เช่น 140601
|
|
454
|
+
subdistrict.postalCode; // 5 หลัก เช่น 13160
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
ควรใช้ code เป็น identifier และใช้ชื่อสำหรับแสดงผล เนื่องจากชื่อพื้นที่อาจซ้ำกันได้
|
|
458
|
+
|
|
459
|
+
## ตัวอย่าง autocomplete ใน browser
|
|
460
|
+
|
|
461
|
+
ควร debounce การค้นหาเพื่อไม่ให้ fuzzy search ทำงานทุกครั้งที่ผู้ใช้กดปุ่ม:
|
|
462
|
+
|
|
463
|
+
```ts
|
|
464
|
+
import { search } from "thai-address-sdk";
|
|
465
|
+
|
|
466
|
+
let timer: ReturnType<typeof setTimeout>;
|
|
467
|
+
|
|
468
|
+
searchInput.addEventListener("input", (event) => {
|
|
469
|
+
clearTimeout(timer);
|
|
470
|
+
|
|
471
|
+
timer = setTimeout(() => {
|
|
472
|
+
const query = (event.target as HTMLInputElement).value;
|
|
473
|
+
const results = query.length >= 2 ? search(query, { limit: 8 }) : [];
|
|
474
|
+
renderSuggestions(results);
|
|
475
|
+
}, 200);
|
|
476
|
+
});
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
เนื่องจาก package bundle dataset มาด้วย JavaScript bundle ที่ build แล้วมีขนาดประมาณ 1.8 MB
|
|
480
|
+
ก่อน compression ควรใช้ dynamic import หากหน้าเว็บไม่ได้ใช้ข้อมูลที่อยู่ทันที:
|
|
481
|
+
|
|
482
|
+
```ts
|
|
483
|
+
const thaiAddress = await import("thai-address-sdk");
|
|
484
|
+
const results = thaiAddress.search("เชียงใหม่");
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## แนวทางจัดการ fuzzy result
|
|
488
|
+
|
|
489
|
+
- อย่าบันทึกผลลัพธ์ fuzzy โดยไม่ให้ผู้ใช้ตรวจสอบในงานที่ต้องการความแม่นยำสูง
|
|
490
|
+
- ถ้า `status` เป็น `ambiguous` ให้แสดง `alternatives` เพื่อให้ผู้ใช้เลือก
|
|
491
|
+
- ใช้จังหวัดหรืออำเภอที่ผู้ใช้เลือกไว้ช่วยจำกัดบริบท
|
|
492
|
+
- เพิ่ม `minScore` เมื่อต้องการลด false positive
|
|
493
|
+
- ใช้ debounce สำหรับช่อง autocomplete
|
|
494
|
+
- เก็บ code ของพื้นที่ ไม่ควรเก็บเฉพาะชื่อ
|
|
495
|
+
|
|
496
|
+
## การพัฒนา package
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
cd npm-sdk
|
|
500
|
+
npm install
|
|
501
|
+
npm run typecheck
|
|
502
|
+
npm test
|
|
503
|
+
npm run format:check
|
|
504
|
+
npm run build
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
Scripts:
|
|
508
|
+
|
|
509
|
+
| Command | หน้าที่ |
|
|
510
|
+
| ---------------------- | ----------------------------------------- |
|
|
511
|
+
| `npm run build` | สร้าง ESM, CommonJS และ declaration files |
|
|
512
|
+
| `npm run typecheck` | ตรวจ TypeScript แบบ strict |
|
|
513
|
+
| `npm test` | Build และรัน integration tests |
|
|
514
|
+
| `npm run format` | จัดรูปแบบ source และเอกสารด้วย Prettier |
|
|
515
|
+
| `npm run format:check` | ตรวจรูปแบบโดยไม่แก้ไฟล์ |
|
|
516
|
+
|
|
517
|
+
ไฟล์ที่พร้อม publish จะอยู่ใน `dist/` สามารถตรวจ package ก่อน publish ได้ด้วย:
|
|
518
|
+
|
|
519
|
+
```bash
|
|
520
|
+
npm pack --dry-run
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
## ข้อมูลและ License
|
|
524
|
+
|
|
525
|
+
ข้อมูลจังหวัด อำเภอ ตำบล และรหัสไปรษณีย์มาจาก
|
|
526
|
+
[`thailand-geography-data/thailand-geography-json`](https://github.com/thailand-geography-data/thailand-geography-json)
|
|
527
|
+
และถูก pin ไว้ที่ source commit ที่ระบุใน
|
|
528
|
+
[`THIRD_PARTY_LICENSES.md`](./THIRD_PARTY_LICENSES.md)
|
|
529
|
+
|
|
530
|
+
ก่อนอัปเดต dataset ควรตรวจจำนวนรายการ code ซ้ำ parent code ที่ไม่มีอยู่ และรัน test
|
|
531
|
+
ทั้งหมดอีกครั้ง
|
|
532
|
+
|
|
533
|
+
## ข้อจำกัดปัจจุบัน
|
|
534
|
+
|
|
535
|
+
- Alias ที่มากับ SDK ยังเป็นชุดเริ่มต้น ไม่ครอบคลุมชื่อเรียกท้องถิ่นทั้งหมด
|
|
536
|
+
- Fuzzy threshold ยังต้องปรับจากคำค้นจริงเพิ่มเติม
|
|
537
|
+
- การค้นหาและ normalize เป็น synchronous และอาจใช้เวลามากขึ้นบนอุปกรณ์กำลังต่ำ
|
|
538
|
+
- Dataset เป็น snapshot และจะไม่อัปเดตจากอินเทอร์เน็ตอัตโนมัติ
|
|
539
|
+
- Package ยังไม่รองรับบ้านเลขที่ ถนน ซอย หมู่บ้าน และ geocoding
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Third-party data
|
|
2
|
+
|
|
3
|
+
The bundled Thailand geography dataset is sourced from
|
|
4
|
+
[`thailand-geography-data/thailand-geography-json`](https://github.com/thailand-geography-data/thailand-geography-json)
|
|
5
|
+
at commit `b8b3fb91c7df1129ff5b43cb46f7fcffadd2156b`.
|
|
6
|
+
|
|
7
|
+
MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2023-Present Joe Takara
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|