Fix AI streaming, learn images, and full city regions.

Remove use server from stream helper to fix RSC errors; support OPENAI_API_BASE alias; render HTML tables via rehype-raw with gua-image API; expand regions to 356 prefecture-level cities.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-10 22:04:17 +08:00
parent 698a20a1d4
commit 3933905d66
11 changed files with 1565 additions and 77 deletions
+363 -42
View File
@@ -1,5 +1,5 @@
/**
* 生成 lib/data/regions.json — 全国省级 + 主要城市
* 生成 lib/data/regions.json — 全国省级 + 地级市(含经度)
* 运行:node scripts/generate-regions.mjs
*/
import fs from "fs";
@@ -8,55 +8,376 @@ import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
/** [code, name, lon, cities: [code, name, lon][]] */
const DATA = [
["110000", "北京市", 116.4074, [["110101", "东城区", 116.4164], ["110105", "朝阳区", 116.4434], ["110108", "海淀区", 116.2983], ["110114", "昌平区", 116.2312]]],
["120000", "天津市", 117.2010, [["120101", "和平区", 117.2147], ["120103", "河西区", 117.2234], ["120110", "东丽区", 117.3143], ["120116", "滨海新区", 117.7105]]],
["130000", "河北省", 114.5149, [["130100", "石家庄市", 114.5149], ["130200", "唐山市", 118.1802], ["130300", "秦皇岛市", 119.6005], ["130600", "保定市", 115.4648], ["131000", "廊坊市", 116.6838]]],
["140000", "山西省", 112.5624, [["140100", "太原市", 112.5624], ["140200", "大同市", 113.3001], ["140500", "晋城市", 112.8513], ["140700", "晋中市", 112.7528]]],
["150000", "内蒙古自治区", 111.7652, [["150100", "呼和浩特市", 111.7652], ["150200", "包头市", 109.8403], ["150400", "赤峰市", 118.8869], ["150500", "通辽市", 122.2434]]],
["210000", "辽宁省", 123.4315, [["210100", "沈阳市", 123.4315], ["210200", "大连市", 121.6147], ["210300", "鞍山市", 122.9946], ["210600", "丹东市", 124.3545]]],
["220000", "吉林省", 125.3235, [["220100", "长春市", 125.3235], ["220200", "吉林市", 126.5494], ["220300", "四平市", 124.3505], ["222400", "延边州", 129.5132]]],
["230000", "黑龙江省", 126.6425, [["230100", "哈尔滨市", 126.6425], ["230600", "大庆市", 125.1031], ["231000", "牡丹江市", 129.6332], ["230200", "齐齐哈尔市", 123.9182]]],
["310000", "上海市", 121.4737, [["310101", "黄浦区", 121.4903], ["310104", "徐汇区", 121.4365], ["310115", "浦东新区", 121.5447], ["310117", "松江区", 121.2277]]],
["320000", "江苏省", 118.7969, [["320100", "南京市", 118.7969], ["320200", "无锡市", 120.3119], ["320500", "苏州市", 120.5853], ["320300", "徐州市", 117.1848], ["320600", "南通市", 120.8945]]],
["330000", "浙江省", 120.1536, [["330100", "杭州市", 120.1551], ["330200", "宁波市", 121.5503], ["330300", "温州市", 120.6994], ["330400", "嘉兴市", 120.7555], ["330700", "金华市", 119.6474]]],
["340000", "安徽省", 117.2830, [["340100", "合肥市", 117.2830], ["340200", "芜湖市", 118.4329], ["340300", "蚌埠市", 117.3889], ["341200", "阜阳市", 115.8142]]],
["350000", "福建省", 119.2965, [["350100", "福州市", 119.2965], ["350200", "厦门市", 118.0894], ["350500", "泉州市", 118.6757], ["350600", "漳州市", 117.6471]]],
["360000", "江西省", 115.9092, [["360100", "南昌市", 115.9092], ["360400", "九江市", 115.9928], ["360700", "赣州市", 114.9350], ["360900", "宜春市", 114.4168]]],
["370000", "山东省", 117.0009, [["370100", "济南市", 117.1205], ["370200", "青岛市", 120.3826], ["370300", "淄博市", 118.0550], ["370600", "烟台市", 121.4479], ["370700", "潍坊市", 119.1619], ["371300", "临沂市", 118.3565], ["371400", "德州市", 116.3575], ["371500", "聊城市", 115.9855]]],
["410000", "河南省", 113.6254, [["410100", "郑州市", 113.6254], ["410300", "洛阳市", 112.4540], ["410700", "新乡市", 113.9268], ["411300", "南阳市", 112.5288], ["411400", "商丘市", 115.6564]]],
["420000", "湖北省", 114.3419, [["420100", "武汉市", 114.3055], ["420500", "宜昌市", 111.2865], ["420600", "襄阳市", 112.1226], ["421000", "荆州市", 112.2390]]],
["430000", "湖南省", 112.9834, [["430100", "长沙市", 112.9388], ["430200", "株洲市", 113.1340], ["430300", "湘潭市", 112.9440], ["430600", "岳阳市", 113.1289], ["430700", "常德市", 111.6985]]],
["440000", "广东省", 113.2665, [["440100", "广州市", 113.2644], ["440300", "深圳市", 114.0579], ["440400", "珠海市", 113.5765], ["440600", "佛山市", 113.1214], ["441300", "惠州市", 114.4162], ["441900", "东莞市", 113.7518], ["442000", "中山市", 113.3928]]],
["450000", "广西壮族自治区", 108.3275, [["450100", "南宁市", 108.3275], ["450300", "桂林市", 110.2990], ["450500", "北海市", 109.1201], ["450700", "钦州市", 108.6544]]],
["460000", "海南省", 110.3492, [["460100", "海口市", 110.3492], ["460200", "三亚市", 109.5119], ["469006", "万宁市", 110.3911]]],
["500000", "重庆市", 106.5516, [["500103", "渝中区", 106.5629], ["500112", "渝北区", 106.6304], ["500106", "沙坪坝区", 106.4569], ["500117", "合川区", 106.2656]]],
["510000", "四川省", 104.0665, [["510100", "成都市", 104.0665], ["510700", "绵阳市", 104.6796], ["511300", "南充市", 106.1107], ["511500", "宜宾市", 104.6432], ["510500", "泸州市", 105.4433]]],
["520000", "贵州省", 106.7135, [["520100", "贵阳市", 106.7135], ["520300", "遵义市", 106.9274], ["520500", "毕节市", 105.2850]]],
["530000", "云南省", 102.7100, [["530100", "昆明市", 102.7100], ["530300", "曲靖市", 103.7962], ["532900", "大理州", 100.2257], ["532500", "红河州", 103.3840]]],
["540000", "西藏自治区", 91.1172, [["540100", "拉萨市", 91.1172], ["540200", "日喀则市", 88.8851]]],
["610000", "陕西省", 108.9398, [["610100", "西安市", 108.9398], ["610300", "宝鸡市", 107.2376], ["610400", "咸阳市", 108.7093], ["610800", "榆林市", 109.7346]]],
["620000", "甘肃省", 103.8343, [["620100", "兰州市", 103.8343], ["620500", "天水市", 105.7249], ["620700", "张掖市", 100.4498]]],
["630000", "青海省", 101.7801, [["630100", "西宁市", 101.7801], ["632800", "海西州", 97.3701]]],
["640000", "宁夏回族自治区", 106.2586, [["640100", "银川市", 106.2586], ["640200", "石嘴山市", 106.3833]]],
["650000", "新疆维吾尔自治区", 87.6177, [["650100", "乌鲁木齐市", 87.6177], ["650200", "克拉玛依市", 84.8739], ["652900", "阿克苏地区", 80.2606]]],
["710000", "台湾省", 121.5091, [["710100", "台北市", 121.5654], ["710200", "高雄市", 120.3014]]],
["810000", "香港特别行政区", 114.1694, [["810001", "中西区", 114.1544], ["810012", "湾仔区", 114.1829]]],
["820000", "澳门特别行政区", 113.5439, [["820001", "花地玛堂区", 113.5491], ["820003", "大堂区", 113.5536]]],
/** 省级:code(6位), name, longitude */
const PROVINCES = [
["110000", "北京市", 116.4074],
["120000", "天津市", 117.201],
["130000", "河北省", 114.5149],
["140000", "山西省", 112.5624],
["150000", "内蒙古自治区", 111.7652],
["210000", "辽宁省", 123.4315],
["220000", "吉林省", 125.3235],
["230000", "黑龙江省", 126.6425],
["310000", "上海市", 121.4737],
["320000", "江苏省", 118.7969],
["330000", "浙江省", 120.1536],
["340000", "安徽省", 117.283],
["350000", "福建省", 119.2965],
["360000", "江西省", 115.9092],
["370000", "山东省", 117.0009],
["410000", "河南省", 113.6254],
["420000", "湖北省", 114.3419],
["430000", "湖南省", 112.9834],
["440000", "广东省", 113.2665],
["450000", "广西壮族自治区", 108.3275],
["460000", "海南省", 110.3492],
["500000", "重庆市", 106.5516],
["510000", "四川省", 104.0665],
["520000", "贵州省", 106.7135],
["530000", "云南省", 102.71],
["540000", "西藏自治区", 91.1172],
["610000", "陕西省", 108.9398],
["620000", "甘肃省", 103.8343],
["630000", "青海省", 101.7801],
["640000", "宁夏回族自治区", 106.2586],
["650000", "新疆维吾尔自治区", 87.6177],
["710000", "台湾省", 121.5091],
["810000", "香港特别行政区", 114.1694],
["820000", "澳门特别行政区", 113.5439],
];
/** 地级市经度(6 位 adcode → 经度),未列出的城市使用省会经度 */
const CITY_LON = {
110101: 116.4164, 110105: 116.4434, 110108: 116.2983, 110114: 116.2312,
120101: 117.2147, 120103: 117.2234, 120110: 117.3143, 120116: 117.7105,
130100: 114.5149, 130200: 118.1802, 130300: 119.6005, 130400: 114.5391,
130500: 114.5045, 130600: 115.4648, 130700: 114.8863, 130800: 117.9634,
130900: 116.8388, 131000: 116.6838, 131100: 115.6705,
140100: 112.5624, 140200: 113.3001, 140300: 113.5805, 140400: 113.1163,
140500: 112.8513, 140600: 112.4329, 140700: 112.7528, 140800: 111.0075,
140900: 112.7341, 141000: 111.5189, 141100: 111.1344,
150100: 111.7652, 150200: 109.8403, 150300: 106.8256, 150400: 118.8869,
150500: 122.2434, 150600: 109.7813, 150700: 119.7658, 150800: 107.3877,
150900: 113.1326, 152200: 122.0703, 152500: 116.0476, 152900: 105.7066,
210100: 123.4315, 210200: 121.6147, 210300: 122.9946, 210400: 123.9572,
210500: 123.7705, 210600: 124.3545, 210700: 121.1271, 210800: 122.2354,
210900: 121.6703, 211000: 123.1815, 211100: 122.0696, 211200: 123.8443,
211300: 120.4504, 211400: 120.8364,
220100: 125.3235, 220200: 126.5494, 220300: 124.3505, 220400: 125.1437,
220500: 125.9397, 220600: 126.4278, 220700: 124.8251, 220800: 122.8387,
222400: 129.5132,
230100: 126.6425, 230200: 123.9182, 230300: 130.9693, 230400: 130.2979,
230500: 131.1573, 230600: 125.1031, 230700: 128.8994, 230800: 130.3618,
230900: 131.0031, 231000: 129.6332, 231100: 127.4990, 231200: 126.9929,
232700: 124.7115,
310101: 121.4903, 310104: 121.4365, 310115: 121.5447, 310117: 121.2277,
320100: 118.7969, 320200: 120.3119, 320300: 117.1848, 320400: 119.9740,
320500: 120.5853, 320600: 120.8945, 320700: 119.2216, 320800: 119.0153,
320900: 120.1636, 321000: 119.4129, 321100: 119.4250, 321200: 119.9229,
321300: 118.2752,
330100: 120.1551, 330200: 121.5503, 330300: 120.6994, 330400: 120.7555,
330500: 120.0868, 330600: 120.5820, 330700: 119.6474, 330800: 118.8590,
330900: 122.2072, 331000: 121.4206, 331100: 119.9229,
340100: 117.2830, 340200: 118.4329, 340300: 117.3889, 340400: 116.9998,
340500: 118.5079, 340600: 116.7948, 340700: 117.8166, 340800: 117.0635,
341000: 118.3375, 341100: 118.3163, 341200: 115.8142, 341300: 116.9641,
341500: 116.5077, 341600: 115.7789, 341700: 117.4892, 341800: 118.7587,
350100: 119.2965, 350200: 118.0894, 350300: 119.0078, 350400: 117.6387,
350500: 118.6757, 350600: 117.6471, 350700: 118.1777, 350800: 117.0179,
350900: 119.5271,
360100: 115.9092, 360200: 117.1784, 360300: 113.8546, 360400: 115.9928,
360500: 114.9308, 360600: 117.0692, 360700: 114.9350, 360800: 114.9928,
360900: 114.4168, 361000: 116.3582, 361100: 117.9434,
370100: 117.1205, 370200: 120.3826, 370300: 118.0550, 370400: 117.3237,
370500: 118.6747, 370600: 121.4479, 370700: 119.1619, 370800: 116.5871,
370900: 117.0889, 371000: 122.1204, 371100: 119.5269, 371300: 118.3565,
371400: 116.3575, 371500: 115.9855, 371600: 117.9707, 371700: 115.4809,
410100: 113.6254, 410200: 114.3076, 410300: 112.4540, 410400: 113.1924,
410500: 114.3924, 410600: 114.2973, 410700: 113.9268, 410800: 113.2418,
410900: 115.0293, 411000: 113.8524, 411100: 114.0166, 411200: 111.2001,
411300: 112.5288, 411400: 115.6564, 411500: 114.0912, 411600: 114.6497,
411700: 114.0223,
420100: 114.3055, 420200: 115.0388, 420300: 110.7879, 420500: 111.2865,
420600: 112.1226, 420700: 114.8949, 420800: 112.1993, 420900: 113.9169,
421000: 112.2390, 421100: 114.8724, 421200: 114.3225, 421300: 113.3825,
422800: 109.4882,
430100: 112.9388, 430200: 113.1340, 430300: 112.9440, 430400: 112.5719,
430500: 111.4677, 430600: 113.1289, 430700: 111.6985, 430800: 110.4792,
430900: 112.3550, 431000: 113.0321, 431100: 111.6134, 431200: 109.9782,
431300: 111.9935, 433100: 109.7397,
440100: 113.2644, 440200: 113.5972, 440300: 114.0579, 440400: 113.5765,
440500: 116.6819, 440600: 113.1214, 440700: 113.0815, 440800: 110.3594,
440900: 110.9255, 441200: 112.4651, 441300: 114.4162, 441400: 116.1222,
441500: 115.3753, 441600: 114.6978, 441700: 111.9826, 441800: 113.0560,
441900: 113.7518, 442000: 113.3928, 445100: 116.6226, 445200: 116.3729,
445300: 112.0444,
450100: 108.3275, 450200: 109.4281, 450300: 110.2990, 450400: 111.2792,
450500: 109.1201, 450600: 108.3548, 450700: 108.6544, 450800: 109.5989,
450900: 110.1648, 451000: 106.6186, 451100: 111.5667, 451200: 108.0856,
451300: 109.2215, 451400: 107.3647,
460100: 110.3492, 460200: 109.5119, 460300: 112.3387, 460400: 109.5808,
469006: 110.3911,
500103: 106.5629, 500112: 106.6304, 500106: 106.4569, 500117: 106.2656,
510100: 104.0665, 510300: 104.7734, 510400: 101.7184, 510500: 105.4433,
510600: 104.3980, 510700: 104.6796, 510800: 105.8298, 510900: 105.5929,
511000: 105.0584, 511100: 103.7654, 511300: 106.1107, 511400: 103.8485,
511500: 104.6432, 511600: 106.6332, 511700: 107.4680, 511800: 103.0133,
511900: 106.7475, 512000: 104.6279, 513200: 102.2247, 513300: 101.9638,
513400: 102.2587,
520100: 106.7135, 520200: 104.8304, 520300: 106.9274, 520400: 105.9476,
520500: 105.2850, 520600: 109.1896, 522300: 104.9064, 522600: 107.9828,
522700: 107.5172,
530100: 102.7100, 530300: 103.7962, 530400: 102.5439, 530500: 99.1618,
530600: 103.7172, 530700: 100.2330, 530800: 100.9723, 530900: 100.0869,
532300: 101.5460, 532500: 103.3840, 532600: 104.2440, 532800: 100.7979,
532900: 100.2257, 533100: 98.5894, 533300: 98.8543, 533400: 99.7022,
540100: 91.1172, 540200: 88.8851, 540300: 97.1785, 540400: 94.3615,
540500: 91.7731, 540600: 92.0512, 542500: 80.1055,
610100: 108.9398, 610200: 108.9796, 610300: 107.2376, 610400: 108.7093,
610500: 109.4897, 610600: 109.4897, 610700: 107.0233, 610800: 109.7346,
610900: 109.0293, 611000: 109.9404,
620100: 103.8343, 620200: 98.2892, 620300: 102.1879, 620400: 104.1386,
620500: 105.7249, 620600: 102.6380, 620700: 100.4498, 620800: 106.6650,
620900: 98.4945, 621000: 107.6436, 621100: 104.6263, 621200: 104.9294,
622900: 103.2120, 623000: 102.9110,
630100: 101.7801, 630200: 102.1043, 632200: 100.9010, 632300: 102.0153,
632500: 100.6195, 632600: 100.2421, 632700: 97.0085, 632800: 97.3701,
640100: 106.2586, 640200: 106.3833, 640300: 106.1988, 640400: 106.2852,
640500: 105.1896,
650100: 87.6177, 650200: 84.8739, 650400: 89.1841, 650500: 93.5149,
652300: 87.3040, 652700: 82.0748, 652800: 86.1509, 652900: 80.2606,
653000: 76.1728, 653100: 75.9891, 653200: 79.9253, 654000: 81.3179,
654200: 82.9857, 654300: 88.1396,
710100: 121.5654, 710200: 120.3014,
810001: 114.1544, 810012: 114.1829,
820001: 113.5491, 820003: 113.5536,
};
/** modood cities.json — provinceCode(2位) → [code(4位), name][] */
const CITIES_BY_PROVINCE = {
11: [["1101", "北京市"]],
12: [["1201", "天津市"]],
13: [
["1301", "石家庄市"], ["1302", "唐山市"], ["1303", "秦皇岛市"], ["1304", "邯郸市"],
["1305", "邢台市"], ["1306", "保定市"], ["1307", "张家口市"], ["1308", "承德市"],
["1309", "沧州市"], ["1310", "廊坊市"], ["1311", "衡水市"],
],
14: [
["1401", "太原市"], ["1402", "大同市"], ["1403", "阳泉市"], ["1404", "长治市"],
["1405", "晋城市"], ["1406", "朔州市"], ["1407", "晋中市"], ["1408", "运城市"],
["1409", "忻州市"], ["1410", "临汾市"], ["1411", "吕梁市"],
],
15: [
["1501", "呼和浩特市"], ["1502", "包头市"], ["1503", "乌海市"], ["1504", "赤峰市"],
["1505", "通辽市"], ["1506", "鄂尔多斯市"], ["1507", "呼伦贝尔市"], ["1508", "巴彦淖尔市"],
["1509", "乌兰察布市"], ["1522", "兴安盟"], ["1525", "锡林郭勒盟"], ["1529", "阿拉善盟"],
],
21: [
["2101", "沈阳市"], ["2102", "大连市"], ["2103", "鞍山市"], ["2104", "抚顺市"],
["2105", "本溪市"], ["2106", "丹东市"], ["2107", "锦州市"], ["2108", "营口市"],
["2109", "阜新市"], ["2110", "辽阳市"], ["2111", "盘锦市"], ["2112", "铁岭市"],
["2113", "朝阳市"], ["2114", "葫芦岛市"],
],
22: [
["2201", "长春市"], ["2202", "吉林市"], ["2203", "四平市"], ["2204", "辽源市"],
["2205", "通化市"], ["2206", "白山市"], ["2207", "松原市"], ["2208", "白城市"],
["2224", "延边朝鲜族自治州"],
],
23: [
["2301", "哈尔滨市"], ["2302", "齐齐哈尔市"], ["2303", "鸡西市"], ["2304", "鹤岗市"],
["2305", "双鸭山市"], ["2306", "大庆市"], ["2307", "伊春市"], ["2308", "佳木斯市"],
["2309", "七台河市"], ["2310", "牡丹江市"], ["2311", "黑河市"], ["2312", "绥化市"],
["2327", "大兴安岭地区"],
],
31: [["3101", "上海市"]],
32: [
["3201", "南京市"], ["3202", "无锡市"], ["3203", "徐州市"], ["3204", "常州市"],
["3205", "苏州市"], ["3206", "南通市"], ["3207", "连云港市"], ["3208", "淮安市"],
["3209", "盐城市"], ["3210", "扬州市"], ["3211", "镇江市"], ["3212", "泰州市"],
["3213", "宿迁市"],
],
33: [
["3301", "杭州市"], ["3302", "宁波市"], ["3303", "温州市"], ["3304", "嘉兴市"],
["3305", "湖州市"], ["3306", "绍兴市"], ["3307", "金华市"], ["3308", "衢州市"],
["3309", "舟山市"], ["3310", "台州市"], ["3311", "丽水市"],
],
34: [
["3401", "合肥市"], ["3402", "芜湖市"], ["3403", "蚌埠市"], ["3404", "淮南市"],
["3405", "马鞍山市"], ["3406", "淮北市"], ["3407", "铜陵市"], ["3408", "安庆市"],
["3410", "黄山市"], ["3411", "滁州市"], ["3412", "阜阳市"], ["3413", "宿州市"],
["3415", "六安市"], ["3416", "亳州市"], ["3417", "池州市"], ["3418", "宣城市"],
],
35: [
["3501", "福州市"], ["3502", "厦门市"], ["3503", "莆田市"], ["3504", "三明市"],
["3505", "泉州市"], ["3506", "漳州市"], ["3507", "南平市"], ["3508", "龙岩市"],
["3509", "宁德市"],
],
36: [
["3601", "南昌市"], ["3602", "景德镇市"], ["3603", "萍乡市"], ["3604", "九江市"],
["3605", "新余市"], ["3606", "鹰潭市"], ["3607", "赣州市"], ["3608", "吉安市"],
["3609", "宜春市"], ["3610", "抚州市"], ["3611", "上饶市"],
],
37: [
["3701", "济南市"], ["3702", "青岛市"], ["3703", "淄博市"], ["3704", "枣庄市"],
["3705", "东营市"], ["3706", "烟台市"], ["3707", "潍坊市"], ["3708", "济宁市"],
["3709", "泰安市"], ["3710", "威海市"], ["3711", "日照市"], ["3713", "临沂市"],
["3714", "德州市"], ["3715", "聊城市"], ["3716", "滨州市"], ["3717", "菏泽市"],
],
41: [
["4101", "郑州市"], ["4102", "开封市"], ["4103", "洛阳市"], ["4104", "平顶山市"],
["4105", "安阳市"], ["4106", "鹤壁市"], ["4107", "新乡市"], ["4108", "焦作市"],
["4109", "濮阳市"], ["4110", "许昌市"], ["4111", "漯河市"], ["4112", "三门峡市"],
["4113", "南阳市"], ["4114", "商丘市"], ["4115", "信阳市"], ["4116", "周口市"],
["4117", "驻马店市"],
],
42: [
["4201", "武汉市"], ["4202", "黄石市"], ["4203", "十堰市"], ["4205", "宜昌市"],
["4206", "襄阳市"], ["4207", "鄂州市"], ["4208", "荆门市"], ["4209", "孝感市"],
["4210", "荆州市"], ["4211", "黄冈市"], ["4212", "咸宁市"], ["4213", "随州市"],
["4228", "恩施土家族苗族自治州"],
],
43: [
["4301", "长沙市"], ["4302", "株洲市"], ["4303", "湘潭市"], ["4304", "衡阳市"],
["4305", "邵阳市"], ["4306", "岳阳市"], ["4307", "常德市"], ["4308", "张家界市"],
["4309", "益阳市"], ["4310", "郴州市"], ["4311", "永州市"], ["4312", "怀化市"],
["4313", "娄底市"], ["4331", "湘西土家族苗族自治州"],
],
44: [
["4401", "广州市"], ["4402", "韶关市"], ["4403", "深圳市"], ["4404", "珠海市"],
["4405", "汕头市"], ["4406", "佛山市"], ["4407", "江门市"], ["4408", "湛江市"],
["4409", "茂名市"], ["4412", "肇庆市"], ["4413", "惠州市"], ["4414", "梅州市"],
["4415", "汕尾市"], ["4416", "河源市"], ["4417", "阳江市"], ["4418", "清远市"],
["4419", "东莞市"], ["4420", "中山市"], ["4451", "潮州市"], ["4452", "揭阳市"],
["4453", "云浮市"],
],
45: [
["4501", "南宁市"], ["4502", "柳州市"], ["4503", "桂林市"], ["4504", "梧州市"],
["4505", "北海市"], ["4506", "防城港市"], ["4507", "钦州市"], ["4508", "贵港市"],
["4509", "玉林市"], ["4510", "百色市"], ["4511", "贺州市"], ["4512", "河池市"],
["4513", "来宾市"], ["4514", "崇左市"],
],
46: [
["4601", "海口市"], ["4602", "三亚市"], ["4603", "三沙市"], ["4604", "儋州市"],
["469006", "万宁市"],
],
50: [
["500103", "渝中区"], ["500112", "渝北区"], ["500106", "沙坪坝区"], ["500117", "合川区"],
],
51: [
["5101", "成都市"], ["5103", "自贡市"], ["5104", "攀枝花市"], ["5105", "泸州市"],
["5106", "德阳市"], ["5107", "绵阳市"], ["5108", "广元市"], ["5109", "遂宁市"],
["5110", "内江市"], ["5111", "乐山市"], ["5113", "南充市"], ["5114", "眉山市"],
["5115", "宜宾市"], ["5116", "广安市"], ["5117", "达州市"], ["5118", "雅安市"],
["5119", "巴中市"], ["5120", "资阳市"], ["5132", "阿坝藏族羌族自治州"],
["5133", "甘孜藏族自治州"], ["5134", "凉山彝族自治州"],
],
52: [
["5201", "贵阳市"], ["5202", "六盘水市"], ["5203", "遵义市"], ["5204", "安顺市"],
["5205", "毕节市"], ["5206", "铜仁市"], ["5223", "黔西南布依族苗族自治州"],
["5226", "黔东南苗族侗族自治州"], ["5227", "黔南布依族苗族自治州"],
],
53: [
["5301", "昆明市"], ["5303", "曲靖市"], ["5304", "玉溪市"], ["5305", "保山市"],
["5306", "昭通市"], ["5307", "丽江市"], ["5308", "普洱市"], ["5309", "临沧市"],
["5323", "楚雄彝族自治州"], ["5325", "红河哈尼族彝族自治州"], ["5326", "文山壮族苗族自治州"],
["5328", "西双版纳傣族自治州"], ["5329", "大理白族自治州"], ["5331", "德宏傣族景颇族自治州"],
["5333", "怒江傈僳族自治州"], ["5334", "迪庆藏族自治州"],
],
54: [
["5401", "拉萨市"], ["5402", "日喀则市"], ["5403", "昌都市"], ["5404", "林芝市"],
["5405", "山南市"], ["5406", "那曲市"], ["5425", "阿里地区"],
],
61: [
["6101", "西安市"], ["6102", "铜川市"], ["6103", "宝鸡市"], ["6104", "咸阳市"],
["6105", "渭南市"], ["6106", "延安市"], ["6107", "汉中市"], ["6108", "榆林市"],
["6109", "安康市"], ["6110", "商洛市"],
],
62: [
["6201", "兰州市"], ["6202", "嘉峪关市"], ["6203", "金昌市"], ["6204", "白银市"],
["6205", "天水市"], ["6206", "武威市"], ["6207", "张掖市"], ["6208", "平凉市"],
["6209", "酒泉市"], ["6210", "庆阳市"], ["6211", "定西市"], ["6212", "陇南市"],
["6229", "临夏回族自治州"], ["6230", "甘南藏族自治州"],
],
63: [
["6301", "西宁市"], ["6302", "海东市"], ["6322", "海北藏族自治州"], ["6323", "黄南藏族自治州"],
["6325", "海南藏族自治州"], ["6326", "果洛藏族自治州"], ["6327", "玉树藏族自治州"],
["6328", "海西蒙古族藏族自治州"],
],
64: [
["6401", "银川市"], ["6402", "石嘴山市"], ["6403", "吴忠市"], ["6404", "固原市"],
["6405", "中卫市"],
],
65: [
["6501", "乌鲁木齐市"], ["6502", "克拉玛依市"], ["6504", "吐鲁番市"], ["6505", "哈密市"],
["6523", "昌吉回族自治州"], ["6527", "博尔塔拉蒙古自治州"], ["6528", "巴音郭楞蒙古自治州"],
["6529", "阿克苏地区"], ["6530", "克孜勒苏柯尔克孜自治州"], ["6531", "喀什地区"],
["6532", "和田地区"], ["6540", "伊犁哈萨克自治州"], ["6542", "塔城地区"], ["6543", "阿勒泰地区"],
],
71: [["710100", "台北市"], ["710200", "高雄市"]],
81: [["810001", "中西区"], ["810012", "湾仔区"]],
82: [["820001", "花地玛堂区"], ["820003", "大堂区"]],
};
const MUNICIPALITY_DISTRICTS = {
110000: [
["110101", "东城区"], ["110105", "朝阳区"], ["110108", "海淀区"], ["110114", "昌平区"],
],
120000: [
["120101", "和平区"], ["120103", "河西区"], ["120110", "东丽区"], ["120116", "滨海新区"],
],
310000: [
["310101", "黄浦区"], ["310104", "徐汇区"], ["310115", "浦东新区"], ["310117", "松江区"],
],
};
function cityLongitude(code, provinceLon) {
const key = Number(code);
return CITY_LON[key] ?? provinceLon;
}
function buildChildren(provinceCode, provinceLon) {
const districts = MUNICIPALITY_DISTRICTS[provinceCode];
if (districts) {
return Object.fromEntries(
districts.map(([code, name]) => [
code,
{ name, longitude: cityLongitude(code, provinceLon) },
]),
);
}
const provKey = provinceCode.slice(0, 2);
const cities = CITIES_BY_PROVINCE[provKey] ?? CITIES_BY_PROVINCE[provinceCode.slice(0, 2)];
if (!cities) {
return {};
}
return Object.fromEntries(
cities.map(([code, name]) => {
const fullCode = code.length === 6 ? code : `${code}00`;
return [
fullCode,
{ name, longitude: cityLongitude(fullCode, provinceLon) },
];
}),
);
}
const regions = {};
for (const [pCode, pName, pLon, cities] of DATA) {
for (const [pCode, pName, pLon] of PROVINCES) {
regions[pCode] = {
name: pName,
longitude: pLon,
children: Object.fromEntries(
cities.map(([cCode, cName, cLon]) => [cCode, { name: cName, longitude: cLon }]),
),
children: buildChildren(pCode, pLon),
};
}
const out = path.join(__dirname, "../lib/data/regions.json");
fs.writeFileSync(out, JSON.stringify(regions, null, 2) + "\n", "utf-8");
console.log("Wrote", out, "provinces:", Object.keys(regions).length);
let cityCount = 0;
for (const p of Object.values(regions)) {
cityCount += Object.keys(p.children ?? {}).length;
}
console.log("Wrote", out);
console.log("provinces:", Object.keys(regions).length, "cities:", cityCount);