@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.
- package/README.md +93 -39
- package/dist/chunk-55G7QUFA.js +293 -0
- package/dist/chunk-55G7QUFA.js.map +1 -0
- package/dist/chunk-ARYB2X4B.js +348 -0
- package/dist/chunk-ARYB2X4B.js.map +1 -0
- package/dist/chunk-HF3HJPXD.js +435 -0
- package/dist/chunk-HF3HJPXD.js.map +1 -0
- package/dist/chunk-ZI6ZF26Q.js +149 -0
- package/dist/chunk-ZI6ZF26Q.js.map +1 -0
- package/dist/copilot/index.cjs +302 -0
- package/dist/copilot/index.cjs.map +1 -0
- package/dist/copilot/index.d.cts +108 -0
- package/dist/copilot/index.d.ts +108 -0
- package/dist/copilot/index.js +3 -0
- package/dist/copilot/index.js.map +1 -0
- package/dist/geoai/index.cjs +449 -0
- package/dist/geoai/index.cjs.map +1 -0
- package/dist/geoai/index.d.cts +212 -0
- package/dist/geoai/index.d.ts +212 -0
- package/dist/geoai/index.js +3 -0
- package/dist/geoai/index.js.map +1 -0
- package/dist/index.cjs +1246 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/llm/index.cjs +152 -0
- package/dist/llm/index.cjs.map +1 -0
- package/dist/llm/index.d.cts +81 -0
- package/dist/llm/index.d.ts +81 -0
- package/dist/llm/index.js +3 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/nlpg/index.cjs +359 -0
- package/dist/nlpg/index.cjs.map +1 -0
- package/dist/nlpg/index.d.cts +160 -0
- package/dist/nlpg/index.d.ts +160 -0
- package/dist/nlpg/index.js +3 -0
- package/dist/nlpg/index.js.map +1 -0
- package/package.json +36 -4
|
@@ -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
|