@caoguo/maplibre-ai 0.0.2 → 0.0.4

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.
@@ -0,0 +1,435 @@
1
+ // src/geoai/addressParser.ts
2
+ var PROVINCES = [
3
+ "\u5317\u4EAC",
4
+ "\u4E0A\u6D77",
5
+ "\u5929\u6D25",
6
+ "\u91CD\u5E86",
7
+ "\u6CB3\u5317",
8
+ "\u5C71\u897F",
9
+ "\u8FBD\u5B81",
10
+ "\u5409\u6797",
11
+ "\u9ED1\u9F99\u6C5F",
12
+ "\u6C5F\u82CF",
13
+ "\u6D59\u6C5F",
14
+ "\u5B89\u5FBD",
15
+ "\u798F\u5EFA",
16
+ "\u6C5F\u897F",
17
+ "\u5C71\u4E1C",
18
+ "\u6CB3\u5357",
19
+ "\u6E56\u5317",
20
+ "\u6E56\u5357",
21
+ "\u5E7F\u4E1C",
22
+ "\u6D77\u5357",
23
+ "\u56DB\u5DDD",
24
+ "\u8D35\u5DDE",
25
+ "\u4E91\u5357",
26
+ "\u9655\u897F",
27
+ "\u7518\u8083",
28
+ "\u9752\u6D77",
29
+ "\u5185\u8499\u53E4",
30
+ "\u5E7F\u897F",
31
+ "\u897F\u85CF",
32
+ "\u5B81\u590F",
33
+ "\u65B0\u7586",
34
+ "\u9999\u6E2F",
35
+ "\u6FB3\u95E8",
36
+ "\u53F0\u6E7E"
37
+ ];
38
+ var CITIES = {
39
+ \u6E56\u5317: ["\u6B66\u6C49", "\u9EC4\u77F3", "\u8944\u9633", "\u5B9C\u660C", "\u8346\u5DDE", "\u5341\u5830", "\u5B5D\u611F", "\u9EC4\u5188", "\u54B8\u5B81", "\u968F\u5DDE", "\u6069\u65BD", "\u9102\u5DDE", "\u8346\u95E8", "\u4ED9\u6843", "\u5929\u95E8", "\u6F5C\u6C5F"],
40
+ \u5E7F\u4E1C: ["\u5E7F\u5DDE", "\u6DF1\u5733", "\u73E0\u6D77", "\u4F5B\u5C71", "\u4E1C\u839E", "\u4E2D\u5C71", "\u60E0\u5DDE", "\u6C5F\u95E8", "\u6E5B\u6C5F", "\u8302\u540D", "\u8087\u5E86", "\u6C55\u5934", "\u97F6\u5173"],
41
+ \u6C5F\u82CF: ["\u5357\u4EAC", "\u82CF\u5DDE", "\u65E0\u9521", "\u5E38\u5DDE", "\u5357\u901A", "\u5F90\u5DDE", "\u626C\u5DDE", "\u9547\u6C5F", "\u6CF0\u5DDE", "\u76D0\u57CE", "\u8FDE\u4E91\u6E2F", "\u6DEE\u5B89", "\u5BBF\u8FC1"],
42
+ \u6D59\u6C5F: ["\u676D\u5DDE", "\u5B81\u6CE2", "\u6E29\u5DDE", "\u5609\u5174", "\u6E56\u5DDE", "\u7ECD\u5174", "\u91D1\u534E", "\u8862\u5DDE", "\u821F\u5C71", "\u53F0\u5DDE", "\u4E3D\u6C34"],
43
+ \u56DB\u5DDD: ["\u6210\u90FD", "\u7EF5\u9633", "\u5FB7\u9633", "\u5B9C\u5BBE", "\u6CF8\u5DDE", "\u5357\u5145", "\u8FBE\u5DDE", "\u4E50\u5C71", "\u5185\u6C5F", "\u81EA\u8D21"]
44
+ };
45
+ var FLAT_CITIES = [
46
+ ...Object.values(CITIES).flat(),
47
+ "\u5317\u4EAC",
48
+ "\u4E0A\u6D77",
49
+ "\u5929\u6D25",
50
+ "\u91CD\u5E86"
51
+ ];
52
+ var WUHAN_DISTRICTS = [
53
+ "\u6C5F\u5CB8\u533A",
54
+ "\u6C5F\u6C49\u533A",
55
+ "\u785A\u53E3\u533A",
56
+ "\u6C49\u9633\u533A",
57
+ "\u6B66\u660C\u533A",
58
+ "\u9752\u5C71\u533A",
59
+ "\u6D2A\u5C71\u533A",
60
+ "\u4E1C\u897F\u6E56\u533A",
61
+ "\u6C49\u5357\u533A",
62
+ "\u8521\u7538\u533A",
63
+ "\u6C5F\u590F\u533A",
64
+ "\u9EC4\u9642\u533A",
65
+ "\u65B0\u6D32\u533A"
66
+ ];
67
+ var POI_DICT = [
68
+ "\u5149\u8C37",
69
+ "\u5149\u8C37\u5E7F\u573A",
70
+ "\u6C49\u53E3\u706B\u8F66\u7AD9",
71
+ "\u6B66\u660C\u706B\u8F66\u7AD9",
72
+ "\u6B66\u6C49\u7AD9",
73
+ "\u5929\u6CB3\u673A\u573A",
74
+ "\u6B66\u6C49\u5927\u5B66",
75
+ "\u534E\u4E2D\u79D1\u6280\u5927\u5B66",
76
+ "\u9EC4\u9E64\u697C",
77
+ "\u4E1C\u6E56",
78
+ "\u957F\u6C5F\u5927\u6865",
79
+ "\u6C5F\u6C49\u8DEF",
80
+ "\u695A\u6CB3\u6C49\u8857",
81
+ "\u8857\u9053\u53E3",
82
+ "\u4E2D\u5357\u8DEF",
83
+ "\u5F90\u4E1C",
84
+ "\u738B\u5BB6\u6E7E",
85
+ "\u949F\u5BB6\u6751",
86
+ "\u6C49\u6B63\u8857",
87
+ "\u8F6F\u4EF6\u56ED",
88
+ "\u91D1\u878D\u6E2F",
89
+ "\u751F\u7269\u57CE",
90
+ "\u672A\u6765\u79D1\u6280\u57CE"
91
+ ];
92
+ function parseAddress(raw) {
93
+ const input = (raw ?? "").trim();
94
+ if (!input) return { raw: input, normalized: "", confidence: 0 };
95
+ const result = { raw: input, normalized: input, confidence: 0.3 };
96
+ let matched = 0;
97
+ for (const p of PROVINCES) {
98
+ if (input.includes(p)) {
99
+ result.province = p;
100
+ matched++;
101
+ break;
102
+ }
103
+ }
104
+ if (result.province) {
105
+ const provinceCities = CITIES[result.province] ?? [];
106
+ for (const c of provinceCities) {
107
+ if (input.includes(c)) {
108
+ result.city = c;
109
+ matched++;
110
+ break;
111
+ }
112
+ }
113
+ if (!result.city && ["\u5317\u4EAC", "\u4E0A\u6D77", "\u5929\u6D25", "\u91CD\u5E86"].includes(result.province)) {
114
+ result.city = result.province;
115
+ matched++;
116
+ }
117
+ }
118
+ if (!result.city) {
119
+ for (const c of FLAT_CITIES) {
120
+ if (input.includes(c)) {
121
+ result.city = c;
122
+ matched++;
123
+ break;
124
+ }
125
+ }
126
+ }
127
+ for (const d of WUHAN_DISTRICTS) {
128
+ if (input.includes(d)) {
129
+ result.district = d;
130
+ matched++;
131
+ break;
132
+ }
133
+ }
134
+ if (!result.district) {
135
+ const districtMatch = input.match(/[\u4e00-\u9fa5]{2,4}(区|县)/);
136
+ if (districtMatch && !districtMatch[0].startsWith("\u6B66\u6C49")) {
137
+ result.district = districtMatch[0];
138
+ matched++;
139
+ }
140
+ }
141
+ for (const poi of POI_DICT) {
142
+ if (input.includes(poi)) {
143
+ result.poi = poi;
144
+ matched++;
145
+ break;
146
+ }
147
+ }
148
+ const streetMatch = input.match(/[\u4e00-\u9fa5]{2,8}(?:路|街|道|大道|大街)/);
149
+ if (streetMatch) {
150
+ result.street = streetMatch[0];
151
+ matched++;
152
+ }
153
+ const numMatch = input.match(/(\d+)\s*号/);
154
+ if (numMatch) {
155
+ result.number = numMatch[1];
156
+ matched++;
157
+ }
158
+ result.normalized = [
159
+ result.province,
160
+ result.city && result.city !== result.province ? result.city : "",
161
+ result.district,
162
+ result.poi,
163
+ result.street,
164
+ result.number ? `${result.number}\u53F7` : ""
165
+ ].filter(Boolean).join("");
166
+ result.confidence = Math.min(0.95, 0.3 + matched * 0.15);
167
+ return result;
168
+ }
169
+ function isValidAddress(parsed) {
170
+ return Boolean(parsed.province || parsed.city || parsed.district || parsed.poi);
171
+ }
172
+
173
+ // src/geoai/headerDetector.ts
174
+ var ADDRESS_KEYS = [
175
+ "\u5730\u5740",
176
+ "\u4F4D\u7F6E",
177
+ "\u5730\u70B9",
178
+ "\u6240\u5728\u5730",
179
+ "\u8BE6\u7EC6\u5730\u5740",
180
+ "\u4F4F\u5740",
181
+ "\u5730\u7406\u4F4D\u7F6E",
182
+ "\u8054\u7CFB\u5730\u5740",
183
+ "address",
184
+ "addr",
185
+ "location",
186
+ "place"
187
+ ];
188
+ var NAME_KEYS = ["\u540D\u79F0", "\u540D\u5B57", "\u5355\u4F4D", "\u673A\u6784", "\u7AD9\u70B9", "\u573A\u6240", "name", "title", "label"];
189
+ var CATEGORY_KEYS = ["\u7C7B\u578B", "\u5206\u7C7B", "\u7C7B\u522B", "\u884C\u4E1A", "\u4E1A\u6001", "category", "type", "kind", "class"];
190
+ var LNG_KEYS = ["\u7ECF\u5EA6", "lng", "lon", "longitude", "x\u5750\u6807", "\u7ECF\u5EA6\u5750\u6807", "long", "x"];
191
+ var LAT_KEYS = ["\u7EAC\u5EA6", "lat", "latitude", "y\u5750\u6807", "\u7EAC\u5EA6\u5750\u6807", "y"];
192
+ function normalize(h) {
193
+ return h.trim().toLowerCase().replace(/[\s_-]+/g, "");
194
+ }
195
+ function matchHeader(headers, keys) {
196
+ const normalized = headers.map(normalize);
197
+ for (let i = 0; i < normalized.length; i++) {
198
+ const h = normalized[i];
199
+ for (const k of keys) {
200
+ if (h === normalize(k) || h.includes(normalize(k))) {
201
+ return i;
202
+ }
203
+ }
204
+ }
205
+ return -1;
206
+ }
207
+ function detectHeaders(headers) {
208
+ const addressCol = matchHeader(headers, ADDRESS_KEYS);
209
+ const nameCol = matchHeader(headers, NAME_KEYS);
210
+ const categoryCol = matchHeader(headers, CATEGORY_KEYS);
211
+ const lngCol = matchHeader(headers, LNG_KEYS);
212
+ const latCol = matchHeader(headers, LAT_KEYS);
213
+ const detail = {};
214
+ if (addressCol >= 0) detail.address = headers[addressCol];
215
+ if (nameCol >= 0) detail.name = headers[nameCol];
216
+ if (categoryCol >= 0) detail.category = headers[categoryCol];
217
+ if (lngCol >= 0) detail.lng = headers[lngCol];
218
+ if (latCol >= 0) detail.lat = headers[latCol];
219
+ return { addressCol, nameCol, categoryCol, lngCol, latCol, detail };
220
+ }
221
+
222
+ // src/geoai/crsDetector.ts
223
+ var CHINA_BOUNDS = {
224
+ lngMin: 73,
225
+ lngMax: 135,
226
+ latMin: 18,
227
+ latMax: 54
228
+ };
229
+ var A = 6378245;
230
+ var EE = 0.006693421622965943;
231
+ function outOfChina(lng, lat) {
232
+ return lng < CHINA_BOUNDS.lngMin || lng > CHINA_BOUNDS.lngMax || lat < CHINA_BOUNDS.latMin || lat > CHINA_BOUNDS.latMax;
233
+ }
234
+ function transformLat(x, y) {
235
+ let ret = -100 + 2 * x + 3 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
236
+ ret += (20 * Math.sin(6 * x * Math.PI) + 20 * Math.sin(2 * x * Math.PI)) * 2 / 3;
237
+ ret += (20 * Math.sin(y * Math.PI) + 40 * Math.sin(y / 3 * Math.PI)) * 2 / 3;
238
+ ret += (160 * Math.sin(y / 12 * Math.PI) + 320 * Math.sin(y * Math.PI / 30)) * 2 / 3;
239
+ return ret;
240
+ }
241
+ function transformLng(x, y) {
242
+ let ret = 300 + x + 2 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
243
+ ret += (20 * Math.sin(6 * x * Math.PI) + 20 * Math.sin(2 * x * Math.PI)) * 2 / 3;
244
+ ret += (20 * Math.sin(x * Math.PI) + 40 * Math.sin(x / 3 * Math.PI)) * 2 / 3;
245
+ ret += (150 * Math.sin(x / 12 * Math.PI) + 300 * Math.sin(x / 30 * Math.PI)) * 2 / 3;
246
+ return ret;
247
+ }
248
+ function wgs84ToGcj02(lng, lat) {
249
+ if (outOfChina(lng, lat)) return [lng, lat];
250
+ let dLat = transformLat(lng - 105, lat - 35);
251
+ let dLng = transformLng(lng - 105, lat - 35);
252
+ const radLat = lat / 180 * Math.PI;
253
+ let magic = Math.sin(radLat);
254
+ magic = 1 - EE * magic * magic;
255
+ const sqrtMagic = Math.sqrt(magic);
256
+ dLat = dLat * 180 / (A * (1 - EE) / (magic * sqrtMagic) * Math.PI);
257
+ dLng = dLng * 180 / (A / sqrtMagic * Math.cos(radLat) * Math.PI);
258
+ return [lng + dLng, lat + dLat];
259
+ }
260
+ function gcj02ToWgs84(lng, lat) {
261
+ if (outOfChina(lng, lat)) return [lng, lat];
262
+ let [wgsLng, wgsLat] = [lng, lat];
263
+ for (let i = 0; i < 5; i++) {
264
+ const [gLng, gLat] = wgs84ToGcj02(wgsLng, wgsLat);
265
+ const dLng = gLng - lng;
266
+ const dLat = gLat - lat;
267
+ wgsLng -= dLng;
268
+ wgsLat -= dLat;
269
+ }
270
+ return [wgsLng, wgsLat];
271
+ }
272
+ function isInChina(lng, lat) {
273
+ return !outOfChina(lng, lat);
274
+ }
275
+ function detectCRS(source, sample = []) {
276
+ const s = (source ?? "").toLowerCase();
277
+ if (/(高德|腾讯|amap|qqmap|gcj|火星)/.test(s)) return "gcj02";
278
+ if (/(天地图|tianditu|cgcs|2000)/.test(s)) return "cgcs2000";
279
+ if (/(gps|wgs84|84)/.test(s)) return "wgs84";
280
+ if (sample.length > 0) {
281
+ let inChinaCount = 0;
282
+ for (const [lng, lat] of sample) {
283
+ if (isInChina(lng, lat)) {
284
+ inChinaCount++;
285
+ }
286
+ }
287
+ if (sample.length > 0 && inChinaCount / sample.length < 0.5) return "wgs84";
288
+ }
289
+ return "wgs84";
290
+ }
291
+
292
+ // src/geoai/geocoder.ts
293
+ var LOCAL_GEO_DB = {
294
+ // 主要城市
295
+ \u6B66\u6C49: [114.305, 30.593],
296
+ \u5317\u4EAC: [116.407, 39.904],
297
+ \u4E0A\u6D77: [121.474, 31.23],
298
+ \u5E7F\u5DDE: [113.264, 23.129],
299
+ \u6DF1\u5733: [114.058, 22.543],
300
+ \u6210\u90FD: [104.067, 30.573],
301
+ \u676D\u5DDE: [120.155, 30.274],
302
+ \u5357\u4EAC: [118.796, 32.06],
303
+ \u91CD\u5E86: [106.551, 29.563],
304
+ \u897F\u5B89: [108.94, 34.341],
305
+ \u5929\u6D25: [117.2, 39.084],
306
+ // 武汉各区
307
+ \u6C5F\u5CB8\u533A: [114.31, 30.6],
308
+ \u6C5F\u6C49\u533A: [114.27, 30.6],
309
+ \u785A\u53E3\u533A: [114.21, 30.58],
310
+ \u6C49\u9633\u533A: [114.22, 30.55],
311
+ \u6B66\u660C\u533A: [114.32, 30.55],
312
+ \u9752\u5C71\u533A: [114.39, 30.63],
313
+ \u6D2A\u5C71\u533A: [114.34, 30.5],
314
+ \u4E1C\u897F\u6E56\u533A: [114.14, 30.62],
315
+ \u6C49\u5357\u533A: [114.08, 30.31],
316
+ \u8521\u7538\u533A: [114.03, 30.58],
317
+ \u6C5F\u590F\u533A: [114.32, 30.35],
318
+ \u9EC4\u9642\u533A: [114.38, 30.88],
319
+ \u65B0\u6D32\u533A: [114.8, 30.85],
320
+ // 常见 POI(口语化地址兜底)
321
+ \u5149\u8C37: [114.428, 30.507],
322
+ \u5149\u8C37\u5E7F\u573A: [114.4, 30.507],
323
+ \u6C49\u53E3\u706B\u8F66\u7AD9: [114.255, 30.618],
324
+ \u6B66\u660C\u706B\u8F66\u7AD9: [114.317, 30.531],
325
+ \u6B66\u6C49\u7AD9: [114.424, 30.607],
326
+ \u5929\u6CB3\u673A\u573A: [114.208, 30.774],
327
+ \u6B66\u6C49\u5927\u5B66: [114.362, 30.541],
328
+ \u534E\u4E2D\u79D1\u6280\u5927\u5B66: [114.418, 30.513],
329
+ \u9EC4\u9E64\u697C: [114.302, 30.544],
330
+ \u4E1C\u6E56: [114.4, 30.55],
331
+ \u957F\u6C5F\u5927\u6865: [114.294, 30.55],
332
+ \u6C5F\u6C49\u8DEF: [114.278, 30.581],
333
+ \u695A\u6CB3\u6C49\u8857: [114.337, 30.561],
334
+ \u8857\u9053\u53E3: [114.351, 30.531],
335
+ \u4E2D\u5357\u8DEF: [114.331, 30.539],
336
+ \u5F90\u4E1C: [114.348, 30.583],
337
+ \u738B\u5BB6\u6E7E: [114.204, 30.557],
338
+ \u949F\u5BB6\u6751: [114.246, 30.549],
339
+ \u6C49\u6B63\u8857: [114.272, 30.573],
340
+ \u8F6F\u4EF6\u56ED: [114.4, 30.47],
341
+ \u91D1\u878D\u6E2F: [114.42, 30.47],
342
+ \u751F\u7269\u57CE: [114.48, 30.49],
343
+ \u672A\u6765\u79D1\u6280\u57CE: [114.47, 30.44]
344
+ };
345
+ function geocode(parsed) {
346
+ if (parsed.poi && LOCAL_GEO_DB[parsed.poi]) {
347
+ const c = LOCAL_GEO_DB[parsed.poi];
348
+ return { lng: c[0], lat: c[1], matched: parsed.poi, confidence: 0.95 };
349
+ }
350
+ if (parsed.district && LOCAL_GEO_DB[parsed.district]) {
351
+ const c = LOCAL_GEO_DB[parsed.district];
352
+ return { lng: c[0], lat: c[1], matched: parsed.district, confidence: 0.8 };
353
+ }
354
+ if (parsed.city && LOCAL_GEO_DB[parsed.city]) {
355
+ const c = LOCAL_GEO_DB[parsed.city];
356
+ return { lng: c[0], lat: c[1], matched: parsed.city, confidence: 0.6 };
357
+ }
358
+ if (parsed.province && LOCAL_GEO_DB[parsed.province]) {
359
+ const c = LOCAL_GEO_DB[parsed.province];
360
+ return { lng: c[0], lat: c[1], matched: parsed.province, confidence: 0.4 };
361
+ }
362
+ return null;
363
+ }
364
+ function hasLocalGeo(name) {
365
+ return name in LOCAL_GEO_DB;
366
+ }
367
+ function batchGeocode(rows, parse) {
368
+ return rows.map((row) => {
369
+ if (typeof row.lng === "number" && typeof row.lat === "number" && !Number.isNaN(row.lng) && !Number.isNaN(row.lat)) {
370
+ return { lng: row.lng, lat: row.lat, source: "provided", confidence: 1 };
371
+ }
372
+ const parsed = parse(row.address);
373
+ const geo = geocode(parsed);
374
+ if (geo) {
375
+ return { lng: geo.lng, lat: geo.lat, source: "geocoded", matched: geo.matched, confidence: geo.confidence };
376
+ }
377
+ return { lng: 0, lat: 0, source: "failed", confidence: 0 };
378
+ });
379
+ }
380
+
381
+ // src/geoai/dataImporter.ts
382
+ function importToGeoJSON(headers, rows, opts = {}) {
383
+ const t0 = typeof performance !== "undefined" ? performance.now() : Date.now();
384
+ const parse = opts.parse ?? parseAddress;
385
+ const det = detectHeaders(headers);
386
+ const { addressCol, nameCol, categoryCol, lngCol, latCol } = det;
387
+ const samples = [];
388
+ if (lngCol >= 0 && latCol >= 0) {
389
+ for (const row of rows.slice(0, 50)) {
390
+ const lng = Number(row[lngCol]);
391
+ const lat = Number(row[latCol]);
392
+ if (!Number.isNaN(lng) && !Number.isNaN(lat)) samples.push([lng, lat]);
393
+ }
394
+ }
395
+ const crs = detectCRS(opts.source, samples);
396
+ const geocodeInput = rows.map((row) => ({
397
+ address: String(row[addressCol] ?? ""),
398
+ lng: lngCol >= 0 ? Number(row[lngCol]) : void 0,
399
+ lat: latCol >= 0 ? Number(row[latCol]) : void 0
400
+ }));
401
+ const coded = batchGeocode(geocodeInput, parse);
402
+ const features = rows.map((row, i) => {
403
+ const c = coded[i];
404
+ let lng = c.lng;
405
+ let lat = c.lat;
406
+ if (c.source !== "failed" && crs === "gcj02") {
407
+ [lng, lat] = gcj02ToWgs84(lng, lat);
408
+ }
409
+ const properties = {};
410
+ if (nameCol >= 0) properties.name = row[nameCol];
411
+ if (categoryCol >= 0) properties.category = row[categoryCol];
412
+ if (addressCol >= 0) properties.address = row[addressCol];
413
+ if (c.matched) properties.matchedPlace = c.matched;
414
+ const geometry = c.source !== "failed" ? { type: "Point", coordinates: [lng, lat] } : { type: "Point", coordinates: [] };
415
+ return { type: "Feature", geometry, properties };
416
+ });
417
+ const success = coded.filter((c) => c.source !== "failed").length;
418
+ const durationMs = (typeof performance !== "undefined" ? performance.now() : Date.now()) - t0;
419
+ return {
420
+ type: "FeatureCollection",
421
+ features,
422
+ stats: {
423
+ total: rows.length,
424
+ success,
425
+ failed: rows.length - success,
426
+ successRate: rows.length > 0 ? success / rows.length : 0,
427
+ headers: det,
428
+ durationMs
429
+ }
430
+ };
431
+ }
432
+
433
+ export { CHINA_BOUNDS, LOCAL_GEO_DB, batchGeocode, detectCRS, detectHeaders, gcj02ToWgs84, geocode, hasLocalGeo, importToGeoJSON, isInChina, isValidAddress, parseAddress, wgs84ToGcj02 };
434
+ //# sourceMappingURL=chunk-HF3HJPXD.js.map
435
+ //# sourceMappingURL=chunk-HF3HJPXD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/geoai/addressParser.ts","../src/geoai/headerDetector.ts","../src/geoai/crsDetector.ts","../src/geoai/geocoder.ts","../src/geoai/dataImporter.ts"],"names":[],"mappings":"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","file":"chunk-HF3HJPXD.js","sourcesContent":["/**\n * GeoAI 中文地址解析(PRD phase-0 §5.6 G-2)\n *\n * 将非标准中文地址(含口语化、简称)解析为标准化结构:\n * province / city / district / street / poi / number\n *\n * 采用「词典匹配 + 规则」的传统 NLP 方案(不依赖大模型),保证速度与可控性。\n */\n\nexport interface ParsedAddress {\n /** 原始输入 */\n raw: string;\n /** 省 */\n province?: string;\n /** 市 */\n city?: string;\n /** 区/县 */\n district?: string;\n /** 街道/路/道 */\n street?: string;\n /** 门牌号 */\n number?: string;\n /** 地标/POI(如\"光谷\"\"汉口火车站\") */\n poi?: string;\n /** 标准化地址(可读拼接) */\n normalized: string;\n /** 解析置信度 0-1 */\n confidence: number;\n}\n\n/** 行政区词典(省/市/区) */\nconst PROVINCES = [\n '北京', '上海', '天津', '重庆',\n '河北', '山西', '辽宁', '吉林', '黑龙江',\n '江苏', '浙江', '安徽', '福建', '江西', '山东',\n '河南', '湖北', '湖南', '广东', '海南',\n '四川', '贵州', '云南', '陕西', '甘肃', '青海',\n '内蒙古', '广西', '西藏', '宁夏', '新疆',\n '香港', '澳门', '台湾',\n];\n\nconst CITIES: Record<string, string[]> = {\n 湖北: ['武汉', '黄石', '襄阳', '宜昌', '荆州', '十堰', '孝感', '黄冈', '咸宁', '随州', '恩施', '鄂州', '荆门', '仙桃', '天门', '潜江'],\n 广东: ['广州', '深圳', '珠海', '佛山', '东莞', '中山', '惠州', '江门', '湛江', '茂名', '肇庆', '汕头', '韶关'],\n 江苏: ['南京', '苏州', '无锡', '常州', '南通', '徐州', '扬州', '镇江', '泰州', '盐城', '连云港', '淮安', '宿迁'],\n 浙江: ['杭州', '宁波', '温州', '嘉兴', '湖州', '绍兴', '金华', '衢州', '舟山', '台州', '丽水'],\n 四川: ['成都', '绵阳', '德阳', '宜宾', '泸州', '南充', '达州', '乐山', '内江', '自贡'],\n};\n\n/** 扁平城市列表(含直辖市,用于无省名时的城市识别) */\nconst FLAT_CITIES = [\n ...Object.values(CITIES).flat(),\n '北京', '上海', '天津', '重庆',\n];\n\n/** 武汉各区(项目背景城市,优先覆盖) */\nconst WUHAN_DISTRICTS = [\n '江岸区', '江汉区', '硚口区', '汉阳区', '武昌区', '青山区', '洪山区',\n '东西湖区', '汉南区', '蔡甸区', '江夏区', '黄陂区', '新洲区',\n];\n\n/** 通用区县后缀 */\nconst DISTRICT_SUFFIX = /(区|县|市)$/;\n\n/** 常见地标/POI 词典(用于口语化地址识别) */\nconst POI_DICT = [\n '光谷', '光谷广场', '汉口火车站', '武昌火车站', '武汉站', '天河机场',\n '武汉大学', '华中科技大学', '黄鹤楼', '东湖', '长江大桥', '江汉路',\n '楚河汉街', '街道口', '中南路', '徐东', '王家湾', '钟家村', '汉正街',\n '软件园', '金融港', '生物城', '未来科技城',\n];\n\n/** 街道通名 */\nconst STREET_SUFFIX = /(路|街|道|大道|大街|巷|里|弄|街坊|桥|港)$/;\n\n/**\n * 解析中文地址。\n *\n * 覆盖:\n * - 完整地址:\"湖北省武汉市洪山区光谷大道 1 号\"\n * - 简称:\"武汉光谷\"、\"洪山区\"\n * - 口语化:\"光谷那边\"、\"汉口火车站附近\"\n */\nexport function parseAddress(raw: string): ParsedAddress {\n const input = (raw ?? '').trim();\n if (!input) return { raw: input, normalized: '', confidence: 0 };\n\n const result: ParsedAddress = { raw: input, normalized: input, confidence: 0.3 };\n let matched = 0;\n\n // 1) 省\n for (const p of PROVINCES) {\n if (input.includes(p)) {\n result.province = p;\n matched++;\n break;\n }\n }\n\n // 2) 市(先按省匹配,无省则从扁平城市列表匹配)\n if (result.province) {\n const provinceCities = CITIES[result.province] ?? [];\n for (const c of provinceCities) {\n if (input.includes(c)) {\n result.city = c;\n matched++;\n break;\n }\n }\n // 直辖市本身即市\n if (!result.city && ['北京', '上海', '天津', '重庆'].includes(result.province)) {\n result.city = result.province;\n matched++;\n }\n }\n if (!result.city) {\n for (const c of FLAT_CITIES) {\n if (input.includes(c)) {\n result.city = c;\n matched++;\n break;\n }\n }\n }\n\n // 3) 区/县\n for (const d of WUHAN_DISTRICTS) {\n if (input.includes(d)) {\n result.district = d;\n matched++;\n break;\n }\n }\n if (!result.district) {\n const districtMatch = input.match(/[\\u4e00-\\u9fa5]{2,4}(区|县)/);\n if (districtMatch && !districtMatch[0].startsWith('武汉')) {\n result.district = districtMatch[0];\n matched++;\n }\n }\n\n // 4) 地标/POI(口语化识别)\n for (const poi of POI_DICT) {\n if (input.includes(poi)) {\n result.poi = poi;\n matched++;\n break;\n }\n }\n\n // 5) 街道/路/道\n const streetMatch = input.match(/[\\u4e00-\\u9fa5]{2,8}(?:路|街|道|大道|大街)/);\n if (streetMatch) {\n result.street = streetMatch[0];\n matched++;\n }\n\n // 6) 门牌号\n const numMatch = input.match(/(\\d+)\\s*号/);\n if (numMatch) {\n result.number = numMatch[1];\n matched++;\n }\n\n // 标准化拼接 + 置信度\n result.normalized = [\n result.province,\n result.city && result.city !== result.province ? result.city : '',\n result.district,\n result.poi,\n result.street,\n result.number ? `${result.number}号` : '',\n ].filter(Boolean).join('');\n\n result.confidence = Math.min(0.95, 0.3 + matched * 0.15);\n return result;\n}\n\n/** 判断是否为有效地址(至少命中省/市/区/POI 之一) */\nexport function isValidAddress(parsed: ParsedAddress): boolean {\n return Boolean(parsed.province || parsed.city || parsed.district || parsed.poi);\n}\n","/**\n * GeoAI 表头自动识别(PRD phase-0 §5.6 G-1)\n *\n * 从 CSV/Excel 表头中自动识别:\n * - 地址列(addressCol)\n * - 名称列(nameCol)\n * - 分类列(categoryCol)\n * - 经度列(lngCol)/ 纬度列(latCol)\n *\n * 采用「列名语义 + 正则匹配」方案,命中率目标 ≥ 90%。\n */\n\nexport interface HeaderDetection {\n /** 地址列索引(-1 表示未识别) */\n addressCol: number;\n /** 名称列索引 */\n nameCol: number;\n /** 分类列索引 */\n categoryCol: number;\n /** 经度列索引 */\n lngCol: number;\n /** 纬度列索引 */\n latCol: number;\n /** 识别详情(供上层展示/纠错) */\n detail: Record<string, string>;\n}\n\n/** 地址列候选关键词 */\nconst ADDRESS_KEYS = [\n '地址', '位置', '地点', '所在地', '详细地址', '住址', '地理位置', '联系地址',\n 'address', 'addr', 'location', 'place',\n];\n\n/** 名称列候选关键词 */\nconst NAME_KEYS = ['名称', '名字', '单位', '机构', '站点', '场所', 'name', 'title', 'label'];\n\n/** 分类列候选关键词 */\nconst CATEGORY_KEYS = ['类型', '分类', '类别', '行业', '业态', 'category', 'type', 'kind', 'class'];\n\n/** 经度列候选关键词 */\nconst LNG_KEYS = ['经度', 'lng', 'lon', 'longitude', 'x坐标', '经度坐标', 'long', 'x'];\n\n/** 纬度列候选关键词 */\nconst LAT_KEYS = ['纬度', 'lat', 'latitude', 'y坐标', '纬度坐标', 'y'];\n\n/** 归一化:去空白、去下划线/连字符、转小写 */\nfunction normalize(h: string): string {\n return h.trim().toLowerCase().replace(/[\\s_-]+/g, '');\n}\n\n/** 在表头中匹配指定关键词集合,返回首个命中索引 */\nfunction matchHeader(headers: string[], keys: string[]): number {\n const normalized = headers.map(normalize);\n for (let i = 0; i < normalized.length; i++) {\n const h = normalized[i];\n for (const k of keys) {\n if (h === normalize(k) || h.includes(normalize(k))) {\n return i;\n }\n }\n }\n return -1;\n}\n\n/**\n * 识别表头。\n * @param headers 表头数组(CSV 首行 / Excel 首行)\n */\nexport function detectHeaders(headers: string[]): HeaderDetection {\n const addressCol = matchHeader(headers, ADDRESS_KEYS);\n const nameCol = matchHeader(headers, NAME_KEYS);\n const categoryCol = matchHeader(headers, CATEGORY_KEYS);\n const lngCol = matchHeader(headers, LNG_KEYS);\n const latCol = matchHeader(headers, LAT_KEYS);\n\n const detail: Record<string, string> = {};\n if (addressCol >= 0) detail.address = headers[addressCol];\n if (nameCol >= 0) detail.name = headers[nameCol];\n if (categoryCol >= 0) detail.category = headers[categoryCol];\n if (lngCol >= 0) detail.lng = headers[lngCol];\n if (latCol >= 0) detail.lat = headers[latCol];\n\n return { addressCol, nameCol, categoryCol, lngCol, latCol, detail };\n}\n","/**\n * GeoAI 坐标系判断(PRD phase-0 §5.6 G-3)\n *\n * 判断数据坐标所属坐标系:WGS84 / GCJ-02 / CGCS2000。\n *\n * 说明:单点无法 100% 判定坐标系(GCJ-02 偏移仅数百米),\n * 本模块采用「范围校验 + 数据来源推断 + 中国境外判定」的组合启发式,\n * 并提供 GCJ-02 ↔ WGS84 双向纠偏转换。\n */\n\nexport type DetectedCRS = 'wgs84' | 'gcj02' | 'cgcs2000' | 'unknown';\n\n/** 中国经纬度范围(粗判) */\nexport const CHINA_BOUNDS = {\n lngMin: 73,\n lngMax: 135,\n latMin: 18,\n latMax: 54,\n};\n\n/** GCJ-02 → WGS84 纠偏常量 */\nconst A = 6378245.0;\nconst EE = 0.00669342162296594323;\n\nfunction outOfChina(lng: number, lat: number): boolean {\n return lng < CHINA_BOUNDS.lngMin || lng > CHINA_BOUNDS.lngMax || lat < CHINA_BOUNDS.latMin || lat > CHINA_BOUNDS.latMax;\n}\n\nfunction transformLat(x: number, y: number): number {\n let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));\n ret += ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0) / 3.0;\n ret += ((20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin((y / 3.0) * Math.PI)) * 2.0) / 3.0;\n ret += ((160.0 * Math.sin((y / 12.0) * Math.PI) + 320 * Math.sin((y * Math.PI) / 30.0)) * 2.0) / 3.0;\n return ret;\n}\n\nfunction transformLng(x: number, y: number): number {\n let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));\n ret += ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0) / 3.0;\n ret += ((20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin((x / 3.0) * Math.PI)) * 2.0) / 3.0;\n ret += ((150.0 * Math.sin((x / 12.0) * Math.PI) + 300.0 * Math.sin((x / 30.0) * Math.PI)) * 2.0) / 3.0;\n return ret;\n}\n\n/** WGS84 → GCJ-02(加密偏移) */\nexport function wgs84ToGcj02(lng: number, lat: number): [number, number] {\n if (outOfChina(lng, lat)) return [lng, lat];\n let dLat = transformLat(lng - 105.0, lat - 35.0);\n let dLng = transformLng(lng - 105.0, lat - 35.0);\n const radLat = (lat / 180.0) * Math.PI;\n let magic = Math.sin(radLat);\n magic = 1 - EE * magic * magic;\n const sqrtMagic = Math.sqrt(magic);\n dLat = (dLat * 180.0) / (((A * (1 - EE)) / (magic * sqrtMagic)) * Math.PI);\n dLng = (dLng * 180.0) / ((A / sqrtMagic) * Math.cos(radLat) * Math.PI);\n return [lng + dLng, lat + dLat];\n}\n\n/** GCJ-02 → WGS84(纠偏,迭代逼近) */\nexport function gcj02ToWgs84(lng: number, lat: number): [number, number] {\n if (outOfChina(lng, lat)) return [lng, lat];\n let [wgsLng, wgsLat] = [lng, lat];\n for (let i = 0; i < 5; i++) {\n const [gLng, gLat] = wgs84ToGcj02(wgsLng, wgsLat);\n const dLng = gLng - lng;\n const dLat = gLat - lat;\n wgsLng -= dLng;\n wgsLat -= dLat;\n }\n return [wgsLng, wgsLat];\n}\n\n/** 坐标是否在中国境内 */\nexport function isInChina(lng: number, lat: number): boolean {\n return !outOfChina(lng, lat);\n}\n\n/**\n * 判断坐标系。\n * @param source 数据来源标注(如 '高德' / '腾讯' / '天地图' / 'GPS' / undefined)\n * @param sample 抽样坐标对\n */\nexport function detectCRS(\n source: string | undefined,\n sample: Array<[number, number]> = []\n): DetectedCRS {\n // 1) 来源推断优先\n const s = (source ?? '').toLowerCase();\n if (/(高德|腾讯|amap|qqmap|gcj|火星)/.test(s)) return 'gcj02';\n if (/(天地图|tianditu|cgcs|2000)/.test(s)) return 'cgcs2000';\n if (/(gps|wgs84|84)/.test(s)) return 'wgs84';\n\n // 2) 坐标范围粗判\n if (sample.length > 0) {\n let inChinaCount = 0;\n let inChina = false;\n for (const [lng, lat] of sample) {\n if (isInChina(lng, lat)) {\n inChinaCount++;\n inChina = true;\n }\n }\n // 多数坐标在中国境外 → 判定为 WGS84(GCJ 只对境内加密)\n if (sample.length > 0 && inChinaCount / sample.length < 0.5) return 'wgs84';\n void inChina;\n }\n\n // 3) 无来源标注且无法判定 → 默认 WGS84(保守,避免二次纠偏)\n return 'wgs84';\n}\n","/**\n * GeoAI 批量地理编码(PRD phase-0 §5.6 G-4)\n *\n * 将解析后的中文地址编码为坐标 [lng, lat]。\n * v1 使用「本地坐标库 + 模糊匹配」(离线、不依赖天地图/高德 API),\n * 命中率目标:标准地址 ≥ 90%,口语化地址 ≥ 60%。\n */\n\nimport type { ParsedAddress } from './addressParser';\n\n/** 本地地理坐标库:地名 → [lng, lat](WGS84) */\nexport const LOCAL_GEO_DB: Record<string, [number, number]> = {\n // 主要城市\n 武汉: [114.305, 30.593],\n 北京: [116.407, 39.904],\n 上海: [121.474, 31.23],\n 广州: [113.264, 23.129],\n 深圳: [114.058, 22.543],\n 成都: [104.067, 30.573],\n 杭州: [120.155, 30.274],\n 南京: [118.796, 32.06],\n 重庆: [106.551, 29.563],\n 西安: [108.94, 34.341],\n 天津: [117.2, 39.084],\n\n // 武汉各区\n 江岸区: [114.31, 30.6],\n 江汉区: [114.27, 30.6],\n 硚口区: [114.21, 30.58],\n 汉阳区: [114.22, 30.55],\n 武昌区: [114.32, 30.55],\n 青山区: [114.39, 30.63],\n 洪山区: [114.34, 30.5],\n 东西湖区: [114.14, 30.62],\n 汉南区: [114.08, 30.31],\n 蔡甸区: [114.03, 30.58],\n 江夏区: [114.32, 30.35],\n 黄陂区: [114.38, 30.88],\n 新洲区: [114.8, 30.85],\n\n // 常见 POI(口语化地址兜底)\n 光谷: [114.428, 30.507],\n 光谷广场: [114.4, 30.507],\n 汉口火车站: [114.255, 30.618],\n 武昌火车站: [114.317, 30.531],\n 武汉站: [114.424, 30.607],\n 天河机场: [114.208, 30.774],\n 武汉大学: [114.362, 30.541],\n 华中科技大学: [114.418, 30.513],\n 黄鹤楼: [114.302, 30.544],\n 东湖: [114.4, 30.55],\n 长江大桥: [114.294, 30.55],\n 江汉路: [114.278, 30.581],\n 楚河汉街: [114.337, 30.561],\n 街道口: [114.351, 30.531],\n 中南路: [114.331, 30.539],\n 徐东: [114.348, 30.583],\n 王家湾: [114.204, 30.557],\n 钟家村: [114.246, 30.549],\n 汉正街: [114.272, 30.573],\n 软件园: [114.4, 30.47],\n 金融港: [114.42, 30.47],\n 生物城: [114.48, 30.49],\n 未来科技城: [114.47, 30.44],\n};\n\nexport interface GeocodeResult {\n lng: number;\n lat: number;\n /** 匹配到的地名 */\n matched: string;\n /** 置信度 0-1 */\n confidence: number;\n}\n\n/**\n * 地理编码:地址 → 坐标。\n *\n * 匹配优先级:\n * 1. POI(最精确)\n * 2. 区/县\n * 3. 城市\n * 4. 省(降级)\n *\n * 无法匹配返回 null。\n */\nexport function geocode(parsed: ParsedAddress): GeocodeResult | null {\n // 1) POI\n if (parsed.poi && LOCAL_GEO_DB[parsed.poi]) {\n const c = LOCAL_GEO_DB[parsed.poi];\n return { lng: c[0], lat: c[1], matched: parsed.poi, confidence: 0.95 };\n }\n\n // 2) 区/县\n if (parsed.district && LOCAL_GEO_DB[parsed.district]) {\n const c = LOCAL_GEO_DB[parsed.district];\n return { lng: c[0], lat: c[1], matched: parsed.district, confidence: 0.8 };\n }\n\n // 3) 城市\n if (parsed.city && LOCAL_GEO_DB[parsed.city]) {\n const c = LOCAL_GEO_DB[parsed.city];\n return { lng: c[0], lat: c[1], matched: parsed.city, confidence: 0.6 };\n }\n\n // 4) 省(直辖市/省会)\n if (parsed.province && LOCAL_GEO_DB[parsed.province]) {\n const c = LOCAL_GEO_DB[parsed.province];\n return { lng: c[0], lat: c[1], matched: parsed.province, confidence: 0.4 };\n }\n\n return null;\n}\n\n/** 查询本地库是否存在某地名 */\nexport function hasLocalGeo(name: string): boolean {\n return name in LOCAL_GEO_DB;\n}\n\nexport interface BatchGeocodeInput {\n /** 原始地址字符串 */\n address: string;\n /** 已有经度(可选,优先级最高) */\n lng?: number;\n /** 已有纬度(可选) */\n lat?: number;\n}\n\nexport interface BatchGeocodeOutput {\n lng: number;\n lat: number;\n /** 坐标来源:'provided' | 'geocoded' | 'failed' */\n source: 'provided' | 'geocoded' | 'failed';\n matched?: string;\n confidence: number;\n}\n\n/**\n * 批量地理编码:对一批记录并行编码。\n *\n * 优先级:已有经纬度 > 地址解析 + 本地库编码。\n */\nexport function batchGeocode(\n rows: BatchGeocodeInput[],\n parse: (raw: string) => ParsedAddress\n): BatchGeocodeOutput[] {\n return rows.map((row) => {\n // 已有经纬度直接使用\n if (typeof row.lng === 'number' && typeof row.lat === 'number' && !Number.isNaN(row.lng) && !Number.isNaN(row.lat)) {\n return { lng: row.lng, lat: row.lat, source: 'provided', confidence: 1 };\n }\n // 地址解析 + 编码\n const parsed = parse(row.address);\n const geo = geocode(parsed);\n if (geo) {\n return { lng: geo.lng, lat: geo.lat, source: 'geocoded', matched: geo.matched, confidence: geo.confidence };\n }\n return { lng: 0, lat: 0, source: 'failed', confidence: 0 };\n });\n}\n","/**\n * GeoAI 数据入图主入口(PRD phase-0 §5.6)\n *\n * 完整管线:\n * CSV/Excel 表头 → 表头识别 → 逐行地址解析 → 地理编码 / 坐标纠偏 → GeoJSON\n *\n * 用法:\n * const result = importToGeoJSON(headers, rows);\n * result.features // GeoJSON FeatureCollection\n * result.stats // 命中率 / 编码耗时等\n */\n\nimport type { ParsedAddress } from './addressParser';\nimport { parseAddress } from './addressParser';\nimport { detectHeaders, type HeaderDetection } from './headerDetector';\nimport { batchGeocode } from './geocoder';\nimport { detectCRS, gcj02ToWgs84 } from './crsDetector';\n\nexport interface ImportedFeature {\n type: 'Feature';\n geometry:\n | { type: 'Point'; coordinates: [number, number] }\n | { type: 'Point'; coordinates: [] };\n properties: Record<string, unknown>;\n}\n\nexport interface ImportStats {\n /** 总记录数 */\n total: number;\n /** 成功编码数 */\n success: number;\n /** 失败数 */\n failed: number;\n /** 成功率 0-1 */\n successRate: number;\n /** 识别出的表头 */\n headers: HeaderDetection;\n /** 编码耗时(ms) */\n durationMs: number;\n}\n\nexport interface ImportResult {\n type: 'FeatureCollection';\n features: ImportedFeature[];\n stats: ImportStats;\n}\n\nexport interface ImportOptions {\n /** 数据来源标注(用于坐标系判断,如 '高德'/'GPS') */\n source?: string;\n /** 自定义地址解析函数(默认内置 parseAddress) */\n parse?: (raw: string) => ParsedAddress;\n}\n\n/**\n * 表格式数据 → GeoJSON。\n * @param headers 表头数组\n * @param rows 数据行(二维数组,与 headers 对齐)\n */\nexport function importToGeoJSON(\n headers: string[],\n rows: Array<Array<string | number>>,\n opts: ImportOptions = {}\n): ImportResult {\n const t0 = typeof performance !== 'undefined' ? performance.now() : Date.now();\n const parse = opts.parse ?? parseAddress;\n\n const det = detectHeaders(headers);\n const { addressCol, nameCol, categoryCol, lngCol, latCol } = det;\n\n // 抽样已有坐标,判断坐标系\n const samples: Array<[number, number]> = [];\n if (lngCol >= 0 && latCol >= 0) {\n for (const row of rows.slice(0, 50)) {\n const lng = Number(row[lngCol]);\n const lat = Number(row[latCol]);\n if (!Number.isNaN(lng) && !Number.isNaN(lat)) samples.push([lng, lat]);\n }\n }\n const crs = detectCRS(opts.source, samples);\n\n // 批量编码\n const geocodeInput = rows.map((row) => ({\n address: String(row[addressCol] ?? ''),\n lng: lngCol >= 0 ? Number(row[lngCol]) : undefined,\n lat: latCol >= 0 ? Number(row[latCol]) : undefined,\n }));\n const coded = batchGeocode(geocodeInput, parse);\n\n // 组装 GeoJSON\n const features: ImportedFeature[] = rows.map((row, i) => {\n const c = coded[i];\n let lng = c.lng;\n let lat = c.lat;\n // 坐标系纠偏:GCJ-02 → WGS84\n if (c.source !== 'failed' && crs === 'gcj02') {\n [lng, lat] = gcj02ToWgs84(lng, lat);\n }\n\n const properties: Record<string, unknown> = {};\n if (nameCol >= 0) properties.name = row[nameCol];\n if (categoryCol >= 0) properties.category = row[categoryCol];\n // 保留原始地址与匹配信息\n if (addressCol >= 0) properties.address = row[addressCol];\n if (c.matched) properties.matchedPlace = c.matched;\n\n const geometry: ImportedFeature['geometry'] =\n c.source !== 'failed'\n ? { type: 'Point', coordinates: [lng, lat] }\n : { type: 'Point', coordinates: [] };\n\n return { type: 'Feature', geometry, properties };\n });\n\n const success = coded.filter((c) => c.source !== 'failed').length;\n const durationMs = (typeof performance !== 'undefined' ? performance.now() : Date.now()) - t0;\n\n return {\n type: 'FeatureCollection',\n features,\n stats: {\n total: rows.length,\n success,\n failed: rows.length - success,\n successRate: rows.length > 0 ? success / rows.length : 0,\n headers: det,\n durationMs,\n },\n };\n}\n"]}
@@ -0,0 +1,149 @@
1
+ // src/llm/deepseek.ts
2
+ var DEFAULT_BASE_URL = "https://api.deepseek.com";
3
+ var DEFAULT_MODEL = "deepseek-chat";
4
+ function normalizeError(body) {
5
+ const err = new Error(body || "DeepSeek API \u8BF7\u6C42\u5931\u8D25");
6
+ err.name = "DeepSeekError";
7
+ return err;
8
+ }
9
+ var DeepSeekClient = class {
10
+ config;
11
+ lastRequestId = 0;
12
+ constructor(config) {
13
+ if (!config.apiKey) throw new Error("DeepSeekClient: apiKey \u4E0D\u80FD\u4E3A\u7A7A");
14
+ this.config = {
15
+ apiKey: config.apiKey,
16
+ baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
17
+ model: config.model ?? DEFAULT_MODEL,
18
+ temperature: config.temperature ?? 0.3,
19
+ maxTokens: config.maxTokens ?? 2048,
20
+ timeoutMs: config.timeoutMs ?? 3e4,
21
+ retries: config.retries ?? 2,
22
+ fetchImpl: config.fetchImpl ?? (globalThis.fetch ?? fetch)
23
+ };
24
+ }
25
+ /** 发起一次聊天补全 */
26
+ async chat(messages, opts = {}) {
27
+ return this.requestWithRetry(messages, opts);
28
+ }
29
+ /** 聊天 + JSON 解析 */
30
+ async chatJson(messages) {
31
+ const result = await this.chat(messages, { json: true });
32
+ const raw = result.content.trim();
33
+ const cleaned = raw.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
34
+ let data;
35
+ try {
36
+ data = JSON.parse(cleaned);
37
+ } catch (e) {
38
+ const match = cleaned.match(/\{[\s\S]*\}|\[[\s\S]*\]/);
39
+ if (match) {
40
+ data = JSON.parse(match[0]);
41
+ } else {
42
+ throw new Error(`DeepSeek \u8FD4\u56DE\u4E86\u975E JSON \u5185\u5BB9: ${raw.slice(0, 200)}`);
43
+ }
44
+ }
45
+ return { data, raw, model: result.model };
46
+ }
47
+ async requestWithRetry(messages, opts) {
48
+ let lastError = null;
49
+ for (let attempt = 0; attempt <= this.config.retries; attempt++) {
50
+ try {
51
+ return await this.requestOnce(messages, opts);
52
+ } catch (e) {
53
+ lastError = e;
54
+ const retryable = !(e instanceof Error) || !e.name.includes("DeepSeek") || this.isRetryable(e);
55
+ if (!retryable && !this.isRetryable(e)) break;
56
+ if (attempt < this.config.retries) {
57
+ await this.sleep(Math.min(1e3 * 2 ** attempt, 8e3));
58
+ }
59
+ }
60
+ }
61
+ throw lastError ?? new Error("DeepSeek \u8BF7\u6C42\u5931\u8D25");
62
+ }
63
+ isRetryable(err) {
64
+ const msg = err.message ?? "";
65
+ return /429|500|502|503|504|timeout|network|ECONN|abort|fetch failed/i.test(msg);
66
+ }
67
+ async requestOnce(messages, opts) {
68
+ const { fetchImpl } = this.config;
69
+ const controller = new AbortController();
70
+ const timer = setTimeout(() => controller.abort(), this.config.timeoutMs);
71
+ const body = {
72
+ model: this.config.model,
73
+ messages,
74
+ temperature: this.config.temperature,
75
+ max_tokens: this.config.maxTokens,
76
+ stream: Boolean(opts.onChunk)
77
+ };
78
+ if (opts.json) {
79
+ body.response_format = { type: "json_object" };
80
+ }
81
+ try {
82
+ const res = await fetchImpl(`${this.config.baseUrl}/chat/completions`, {
83
+ method: "POST",
84
+ headers: {
85
+ "Content-Type": "application/json",
86
+ Authorization: `Bearer ${this.config.apiKey}`
87
+ },
88
+ body: JSON.stringify(body),
89
+ signal: controller.signal
90
+ });
91
+ if (!res.ok) {
92
+ const text = await res.text().catch(() => "");
93
+ throw normalizeError(`DeepSeek HTTP ${res.status}: ${text}`);
94
+ }
95
+ if (opts.onChunk && res.body) {
96
+ return this.parseStream(res.body, opts.onChunk);
97
+ }
98
+ const json = await res.json();
99
+ const content = json.choices?.[0]?.message?.content ?? "";
100
+ return {
101
+ content,
102
+ model: json.model ?? this.config.model,
103
+ usage: json.usage ? { promptTokens: json.usage.prompt_tokens, completionTokens: json.usage.completion_tokens, totalTokens: json.usage.total_tokens } : void 0
104
+ };
105
+ } finally {
106
+ clearTimeout(timer);
107
+ }
108
+ }
109
+ /** 解析 SSE 流式响应 */
110
+ async parseStream(body, onChunk) {
111
+ const reader = body.getReader();
112
+ const decoder = new TextDecoder();
113
+ let content = "";
114
+ let buffer = "";
115
+ while (true) {
116
+ const { done, value } = await reader.read();
117
+ if (done) break;
118
+ buffer += decoder.decode(value, { stream: true });
119
+ const lines = buffer.split("\n");
120
+ buffer = lines.pop() ?? "";
121
+ for (const line of lines) {
122
+ const trimmed = line.trim();
123
+ if (!trimmed.startsWith("data:")) continue;
124
+ const data = trimmed.slice(5).trim();
125
+ if (data === "[DONE]") continue;
126
+ try {
127
+ const json = JSON.parse(data);
128
+ const delta = json.choices?.[0]?.delta?.content ?? "";
129
+ if (delta) {
130
+ content += delta;
131
+ onChunk(delta);
132
+ }
133
+ } catch {
134
+ }
135
+ }
136
+ }
137
+ return { content, model: this.config.model };
138
+ }
139
+ sleep(ms) {
140
+ return new Promise((r) => setTimeout(r, ms));
141
+ }
142
+ };
143
+ function createDeepSeekClient(config) {
144
+ return new DeepSeekClient(config);
145
+ }
146
+
147
+ export { DeepSeekClient, createDeepSeekClient };
148
+ //# sourceMappingURL=chunk-ZI6ZF26Q.js.map
149
+ //# sourceMappingURL=chunk-ZI6ZF26Q.js.map