diff --git a/.env.example b/.env.example index 574cc79..4e1f1f1 100644 --- a/.env.example +++ b/.env.example @@ -7,8 +7,9 @@ # AI 接口密钥(必填) OPENAI_API_KEY= -# AI 接口地址 +# AI 接口地址(OPENAI_BASE_URL 与 OPENAI_API_BASE 二选一) OPENAI_BASE_URL=https://op.bz121.com/v1 +# OPENAI_API_BASE=https://op.bz121.com/v1 # AI 模型 OPENAI_MODEL=huihui_ai/gemma-4-abliterated:e4b diff --git a/app/api/gua-image/[variant]/[guaNum]/[filename]/route.ts b/app/api/gua-image/[variant]/[guaNum]/[filename]/route.ts new file mode 100644 index 0000000..0214e98 --- /dev/null +++ b/app/api/gua-image/[variant]/[guaNum]/[filename]/route.ts @@ -0,0 +1,53 @@ +import fs from "fs/promises"; +import path from "path"; +import { NextResponse } from "next/server"; +import { markFromNum } from "@/lib/content/zhouyi"; + +const MIME: Record = { + ".png": "image/png", + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".gif": "image/gif", + ".webp": "image/webp", +}; + +export async function GET( + _request: Request, + { + params, + }: { + params: Promise<{ variant: string; guaNum: string; filename: string }>; + }, +) { + const { variant, guaNum, filename } = await params; + + if (!/^\d{1,2}$/.test(guaNum) || !/^[\w.-]+\.(png|jpe?g|gif|webp)$/i.test(filename)) { + return new NextResponse("Not found", { status: 404 }); + } + + const learnVariant = variant === "simplified" ? "simplified" : "traditional"; + const guaMark = await markFromNum(guaNum.padStart(2, "0").slice(-2), learnVariant); + if (!guaMark) { + return new NextResponse("Not found", { status: 404 }); + } + + const root = + learnVariant === "simplified" + ? path.join(process.cwd(), "content", "zhouyi", "docs", "other") + : path.join(process.cwd(), "content", "zhouyi", "docs"); + + const filePath = path.join(root, guaMark, path.basename(filename)); + + try { + const data = await fs.readFile(filePath); + const ext = path.extname(filename).toLowerCase(); + return new NextResponse(data, { + headers: { + "Content-Type": MIME[ext] ?? "application/octet-stream", + "Cache-Control": "public, max-age=86400, immutable", + }, + }); + } catch { + return new NextResponse("Not found", { status: 404 }); + } +} diff --git a/app/learn/[slug]/page.tsx b/app/learn/[slug]/page.tsx index 5b375b8..c7bbabf 100644 --- a/app/learn/[slug]/page.tsx +++ b/app/learn/[slug]/page.tsx @@ -61,7 +61,7 @@ export default async function GuaDetailPage({
第 {getGuaNumber(guaMark)} 卦 · {getGuaName(guaMark)}
- + ); diff --git a/app/learn/other/[slug]/page.tsx b/app/learn/other/[slug]/page.tsx index eaaecb1..85f5586 100644 --- a/app/learn/other/[slug]/page.tsx +++ b/app/learn/other/[slug]/page.tsx @@ -57,7 +57,7 @@ export default async function GuaOtherDetailPage({
第 {getGuaNumber(guaMark)} 卦 · {getGuaName(guaMark)}
- + ); diff --git a/components/learn/markdown-content.tsx b/components/learn/markdown-content.tsx index a42f8c1..f56e44f 100644 --- a/components/learn/markdown-content.tsx +++ b/components/learn/markdown-content.tsx @@ -1,6 +1,8 @@ import Link from "next/link"; import Markdown from "react-markdown"; +import rehypeRaw from "rehype-raw"; import type { LearnVariant } from "@/lib/content/gua-utils"; +import { guaNumFromMark } from "@/lib/content/gua-utils"; function resolveLearnHref( href: string | undefined, @@ -31,19 +33,48 @@ function resolveLearnHref( const num = mark.split(".")[0]; return `${base}/${num}`; } + if (/^\.\.\/\d{2}\./.test(href)) { + const num = href.replace(/^\.\.\//, "").split(".")[0]; + return `${base}/${num}`; + } + if (/^\d{2}\./.test(href)) { + const num = href.split(".")[0]; + return `${base}/${num}`; + } return href; } +function resolveImageSrc( + src: string | undefined, + variant: LearnVariant, + guaNum: string, +): string | undefined { + if (!src) { + return src; + } + if (src.startsWith("http://") || src.startsWith("https://")) { + return src; + } + const file = src.replace(/^\.\//, "").split("/").pop() ?? src; + const apiVariant = variant === "traditional" ? "traditional" : "simplified"; + return `/api/gua-image/${apiVariant}/${guaNum}/${encodeURIComponent(file)}`; +} + export default function MarkdownContent({ content, variant = "traditional", + guaMark, }: { content: string; variant?: LearnVariant; + guaMark: string; }) { + const guaNum = guaNumFromMark(guaMark); + return ( { const resolved = resolveLearnHref(href, variant); @@ -60,15 +91,24 @@ export default function MarkdownContent({ ); }, - img: ({ src, alt, ...props }) => { - if (typeof src === "string" && !src.startsWith("http")) { - return ( - - [{alt || "卦象图片"}] - - ); + img: ({ src, alt }) => { + const resolved = resolveImageSrc( + typeof src === "string" ? src : undefined, + variant, + guaNum, + ); + if (!resolved) { + return null; } - return {alt}; + return ( + // eslint-disable-next-line @next/next/no-img-element + {alt + ); }, }} > diff --git a/lib/ai/stream.ts b/lib/ai/stream.ts index e39ac3c..a8b87c8 100644 --- a/lib/ai/stream.ts +++ b/lib/ai/stream.ts @@ -1,27 +1,33 @@ -"use server"; - import { streamText } from "ai"; import { createOpenAI } from "@ai-sdk/openai"; import { createStreamableValue } from "ai/rsc"; import { ERROR_PREFIX } from "@/lib/constant"; +function getOpenAiBaseUrl(): string { + return ( + process.env.OPENAI_BASE_URL ?? + process.env.OPENAI_API_BASE ?? + "https://op.bz121.com/v1" + ); +} + const model = process.env.OPENAI_MODEL ?? "huihui_ai/gemma-4-abliterated:e4b"; -const openai = createOpenAI({ - baseURL: process.env.OPENAI_BASE_URL ?? "https://op.bz121.com/v1", -}); - -const STREAM_INTERVAL = 60; -const MAX_SIZE = 6; export async function streamAIResponse( system: string, user: string, ): Promise<{ data?: ReturnType>["value"]; error?: string }> { - if (!process.env.OPENAI_API_KEY?.trim()) { + const apiKey = process.env.OPENAI_API_KEY?.trim(); + if (!apiKey) { return { error: "未配置 OPENAI_API_KEY,请在 .env.local 或 Docker env_file 中设置" }; } + const openai = createOpenAI({ + apiKey, + baseURL: getOpenAiBaseUrl(), + }); + const stream = createStreamableValue(); try { @@ -43,15 +49,15 @@ export async function streamAIResponse( stream.done(); return; } - if (buffer.length <= MAX_SIZE) { + if (buffer.length <= 6) { stream.update(buffer); buffer = ""; } else { - const chunk = buffer.slice(0, MAX_SIZE); - buffer = buffer.slice(MAX_SIZE); + const chunk = buffer.slice(0, 6); + buffer = buffer.slice(6); stream.update(chunk); } - }, STREAM_INTERVAL); + }, 60); (async () => { for await (const part of fullStream) { @@ -67,7 +73,10 @@ export async function streamAIResponse( } } })() - .catch(console.error) + .catch((err) => { + const message = err instanceof Error ? err.message : String(err); + stream.update(ERROR_PREFIX + message); + }) .finally(() => { done = true; }); diff --git a/lib/content/gua-utils.ts b/lib/content/gua-utils.ts index 3f256be..59f5321 100644 --- a/lib/content/gua-utils.ts +++ b/lib/content/gua-utils.ts @@ -41,11 +41,14 @@ export function extractChangeDetails( return changeList; } - guaChange - .split(":")[1] - .trim() - .split(",") - .forEach((change) => { + const changePart = guaChange.includes(":") + ? guaChange.split(":").slice(1).join(":").trim() + : guaChange.trim(); + if (!changePart) { + return changeList; + } + + changePart.split(",").forEach((change) => { const detail = guaDetail .match(`(\\*\\*${change}變卦[\\s\\S]*?)(?=${guaTitle}|$)`)?.[1] ?.replaceAll("\n\n", "\n"); diff --git a/lib/data/regions.json b/lib/data/regions.json index 3c625d7..1d4d5ab 100644 --- a/lib/data/regions.json +++ b/lib/data/regions.json @@ -59,13 +59,37 @@ "name": "秦皇岛市", "longitude": 119.6005 }, + "130400": { + "name": "邯郸市", + "longitude": 114.5391 + }, + "130500": { + "name": "邢台市", + "longitude": 114.5045 + }, "130600": { "name": "保定市", "longitude": 115.4648 }, + "130700": { + "name": "张家口市", + "longitude": 114.8863 + }, + "130800": { + "name": "承德市", + "longitude": 117.9634 + }, + "130900": { + "name": "沧州市", + "longitude": 116.8388 + }, "131000": { "name": "廊坊市", "longitude": 116.6838 + }, + "131100": { + "name": "衡水市", + "longitude": 115.6705 } } }, @@ -81,13 +105,41 @@ "name": "大同市", "longitude": 113.3001 }, + "140300": { + "name": "阳泉市", + "longitude": 113.5805 + }, + "140400": { + "name": "长治市", + "longitude": 113.1163 + }, "140500": { "name": "晋城市", "longitude": 112.8513 }, + "140600": { + "name": "朔州市", + "longitude": 112.4329 + }, "140700": { "name": "晋中市", "longitude": 112.7528 + }, + "140800": { + "name": "运城市", + "longitude": 111.0075 + }, + "140900": { + "name": "忻州市", + "longitude": 112.7341 + }, + "141000": { + "name": "临汾市", + "longitude": 111.5189 + }, + "141100": { + "name": "吕梁市", + "longitude": 111.1344 } } }, @@ -103,6 +155,10 @@ "name": "包头市", "longitude": 109.8403 }, + "150300": { + "name": "乌海市", + "longitude": 106.8256 + }, "150400": { "name": "赤峰市", "longitude": 118.8869 @@ -110,6 +166,34 @@ "150500": { "name": "通辽市", "longitude": 122.2434 + }, + "150600": { + "name": "鄂尔多斯市", + "longitude": 109.7813 + }, + "150700": { + "name": "呼伦贝尔市", + "longitude": 119.7658 + }, + "150800": { + "name": "巴彦淖尔市", + "longitude": 107.3877 + }, + "150900": { + "name": "乌兰察布市", + "longitude": 113.1326 + }, + "152200": { + "name": "兴安盟", + "longitude": 122.0703 + }, + "152500": { + "name": "锡林郭勒盟", + "longitude": 116.0476 + }, + "152900": { + "name": "阿拉善盟", + "longitude": 105.7066 } } }, @@ -129,9 +213,49 @@ "name": "鞍山市", "longitude": 122.9946 }, + "210400": { + "name": "抚顺市", + "longitude": 123.9572 + }, + "210500": { + "name": "本溪市", + "longitude": 123.7705 + }, "210600": { "name": "丹东市", "longitude": 124.3545 + }, + "210700": { + "name": "锦州市", + "longitude": 121.1271 + }, + "210800": { + "name": "营口市", + "longitude": 122.2354 + }, + "210900": { + "name": "阜新市", + "longitude": 121.6703 + }, + "211000": { + "name": "辽阳市", + "longitude": 123.1815 + }, + "211100": { + "name": "盘锦市", + "longitude": 122.0696 + }, + "211200": { + "name": "铁岭市", + "longitude": 123.8443 + }, + "211300": { + "name": "朝阳市", + "longitude": 120.4504 + }, + "211400": { + "name": "葫芦岛市", + "longitude": 120.8364 } } }, @@ -151,8 +275,28 @@ "name": "四平市", "longitude": 124.3505 }, + "220400": { + "name": "辽源市", + "longitude": 125.1437 + }, + "220500": { + "name": "通化市", + "longitude": 125.9397 + }, + "220600": { + "name": "白山市", + "longitude": 126.4278 + }, + "220700": { + "name": "松原市", + "longitude": 124.8251 + }, + "220800": { + "name": "白城市", + "longitude": 122.8387 + }, "222400": { - "name": "延边州", + "name": "延边朝鲜族自治州", "longitude": 129.5132 } } @@ -169,13 +313,49 @@ "name": "齐齐哈尔市", "longitude": 123.9182 }, + "230300": { + "name": "鸡西市", + "longitude": 130.9693 + }, + "230400": { + "name": "鹤岗市", + "longitude": 130.2979 + }, + "230500": { + "name": "双鸭山市", + "longitude": 131.1573 + }, "230600": { "name": "大庆市", "longitude": 125.1031 }, + "230700": { + "name": "伊春市", + "longitude": 128.8994 + }, + "230800": { + "name": "佳木斯市", + "longitude": 130.3618 + }, + "230900": { + "name": "七台河市", + "longitude": 131.0031 + }, "231000": { "name": "牡丹江市", "longitude": 129.6332 + }, + "231100": { + "name": "黑河市", + "longitude": 127.499 + }, + "231200": { + "name": "绥化市", + "longitude": 126.9929 + }, + "232700": { + "name": "大兴安岭地区", + "longitude": 124.7115 } } }, @@ -217,6 +397,10 @@ "name": "徐州市", "longitude": 117.1848 }, + "320400": { + "name": "常州市", + "longitude": 119.974 + }, "320500": { "name": "苏州市", "longitude": 120.5853 @@ -224,6 +408,34 @@ "320600": { "name": "南通市", "longitude": 120.8945 + }, + "320700": { + "name": "连云港市", + "longitude": 119.2216 + }, + "320800": { + "name": "淮安市", + "longitude": 119.0153 + }, + "320900": { + "name": "盐城市", + "longitude": 120.1636 + }, + "321000": { + "name": "扬州市", + "longitude": 119.4129 + }, + "321100": { + "name": "镇江市", + "longitude": 119.425 + }, + "321200": { + "name": "泰州市", + "longitude": 119.9229 + }, + "321300": { + "name": "宿迁市", + "longitude": 118.2752 } } }, @@ -247,9 +459,33 @@ "name": "嘉兴市", "longitude": 120.7555 }, + "330500": { + "name": "湖州市", + "longitude": 120.0868 + }, + "330600": { + "name": "绍兴市", + "longitude": 120.582 + }, "330700": { "name": "金华市", "longitude": 119.6474 + }, + "330800": { + "name": "衢州市", + "longitude": 118.859 + }, + "330900": { + "name": "舟山市", + "longitude": 122.2072 + }, + "331000": { + "name": "台州市", + "longitude": 121.4206 + }, + "331100": { + "name": "丽水市", + "longitude": 119.9229 } } }, @@ -269,9 +505,57 @@ "name": "蚌埠市", "longitude": 117.3889 }, + "340400": { + "name": "淮南市", + "longitude": 116.9998 + }, + "340500": { + "name": "马鞍山市", + "longitude": 118.5079 + }, + "340600": { + "name": "淮北市", + "longitude": 116.7948 + }, + "340700": { + "name": "铜陵市", + "longitude": 117.8166 + }, + "340800": { + "name": "安庆市", + "longitude": 117.0635 + }, + "341000": { + "name": "黄山市", + "longitude": 118.3375 + }, + "341100": { + "name": "滁州市", + "longitude": 118.3163 + }, "341200": { "name": "阜阳市", "longitude": 115.8142 + }, + "341300": { + "name": "宿州市", + "longitude": 116.9641 + }, + "341500": { + "name": "六安市", + "longitude": 116.5077 + }, + "341600": { + "name": "亳州市", + "longitude": 115.7789 + }, + "341700": { + "name": "池州市", + "longitude": 117.4892 + }, + "341800": { + "name": "宣城市", + "longitude": 118.7587 } } }, @@ -287,6 +571,14 @@ "name": "厦门市", "longitude": 118.0894 }, + "350300": { + "name": "莆田市", + "longitude": 119.0078 + }, + "350400": { + "name": "三明市", + "longitude": 117.6387 + }, "350500": { "name": "泉州市", "longitude": 118.6757 @@ -294,6 +586,18 @@ "350600": { "name": "漳州市", "longitude": 117.6471 + }, + "350700": { + "name": "南平市", + "longitude": 118.1777 + }, + "350800": { + "name": "龙岩市", + "longitude": 117.0179 + }, + "350900": { + "name": "宁德市", + "longitude": 119.5271 } } }, @@ -305,17 +609,45 @@ "name": "南昌市", "longitude": 115.9092 }, + "360200": { + "name": "景德镇市", + "longitude": 117.1784 + }, + "360300": { + "name": "萍乡市", + "longitude": 113.8546 + }, "360400": { "name": "九江市", "longitude": 115.9928 }, + "360500": { + "name": "新余市", + "longitude": 114.9308 + }, + "360600": { + "name": "鹰潭市", + "longitude": 117.0692 + }, "360700": { "name": "赣州市", "longitude": 114.935 }, + "360800": { + "name": "吉安市", + "longitude": 114.9928 + }, "360900": { "name": "宜春市", "longitude": 114.4168 + }, + "361000": { + "name": "抚州市", + "longitude": 116.3582 + }, + "361100": { + "name": "上饶市", + "longitude": 117.9434 } } }, @@ -335,6 +667,14 @@ "name": "淄博市", "longitude": 118.055 }, + "370400": { + "name": "枣庄市", + "longitude": 117.3237 + }, + "370500": { + "name": "东营市", + "longitude": 118.6747 + }, "370600": { "name": "烟台市", "longitude": 121.4479 @@ -343,6 +683,22 @@ "name": "潍坊市", "longitude": 119.1619 }, + "370800": { + "name": "济宁市", + "longitude": 116.5871 + }, + "370900": { + "name": "泰安市", + "longitude": 117.0889 + }, + "371000": { + "name": "威海市", + "longitude": 122.1204 + }, + "371100": { + "name": "日照市", + "longitude": 119.5269 + }, "371300": { "name": "临沂市", "longitude": 118.3565 @@ -354,6 +710,14 @@ "371500": { "name": "聊城市", "longitude": 115.9855 + }, + "371600": { + "name": "滨州市", + "longitude": 117.9707 + }, + "371700": { + "name": "菏泽市", + "longitude": 115.4809 } } }, @@ -365,14 +729,50 @@ "name": "郑州市", "longitude": 113.6254 }, + "410200": { + "name": "开封市", + "longitude": 114.3076 + }, "410300": { "name": "洛阳市", "longitude": 112.454 }, + "410400": { + "name": "平顶山市", + "longitude": 113.1924 + }, + "410500": { + "name": "安阳市", + "longitude": 114.3924 + }, + "410600": { + "name": "鹤壁市", + "longitude": 114.2973 + }, "410700": { "name": "新乡市", "longitude": 113.9268 }, + "410800": { + "name": "焦作市", + "longitude": 113.2418 + }, + "410900": { + "name": "濮阳市", + "longitude": 115.0293 + }, + "411000": { + "name": "许昌市", + "longitude": 113.8524 + }, + "411100": { + "name": "漯河市", + "longitude": 114.0166 + }, + "411200": { + "name": "三门峡市", + "longitude": 111.2001 + }, "411300": { "name": "南阳市", "longitude": 112.5288 @@ -380,6 +780,18 @@ "411400": { "name": "商丘市", "longitude": 115.6564 + }, + "411500": { + "name": "信阳市", + "longitude": 114.0912 + }, + "411600": { + "name": "周口市", + "longitude": 114.6497 + }, + "411700": { + "name": "驻马店市", + "longitude": 114.0223 } } }, @@ -391,6 +803,14 @@ "name": "武汉市", "longitude": 114.3055 }, + "420200": { + "name": "黄石市", + "longitude": 115.0388 + }, + "420300": { + "name": "十堰市", + "longitude": 110.7879 + }, "420500": { "name": "宜昌市", "longitude": 111.2865 @@ -399,9 +819,37 @@ "name": "襄阳市", "longitude": 112.1226 }, + "420700": { + "name": "鄂州市", + "longitude": 114.8949 + }, + "420800": { + "name": "荆门市", + "longitude": 112.1993 + }, + "420900": { + "name": "孝感市", + "longitude": 113.9169 + }, "421000": { "name": "荆州市", "longitude": 112.239 + }, + "421100": { + "name": "黄冈市", + "longitude": 114.8724 + }, + "421200": { + "name": "咸宁市", + "longitude": 114.3225 + }, + "421300": { + "name": "随州市", + "longitude": 113.3825 + }, + "422800": { + "name": "恩施土家族苗族自治州", + "longitude": 109.4882 } } }, @@ -421,6 +869,14 @@ "name": "湘潭市", "longitude": 112.944 }, + "430400": { + "name": "衡阳市", + "longitude": 112.5719 + }, + "430500": { + "name": "邵阳市", + "longitude": 111.4677 + }, "430600": { "name": "岳阳市", "longitude": 113.1289 @@ -428,6 +884,34 @@ "430700": { "name": "常德市", "longitude": 111.6985 + }, + "430800": { + "name": "张家界市", + "longitude": 110.4792 + }, + "430900": { + "name": "益阳市", + "longitude": 112.355 + }, + "431000": { + "name": "郴州市", + "longitude": 113.0321 + }, + "431100": { + "name": "永州市", + "longitude": 111.6134 + }, + "431200": { + "name": "怀化市", + "longitude": 109.9782 + }, + "431300": { + "name": "娄底市", + "longitude": 111.9935 + }, + "433100": { + "name": "湘西土家族苗族自治州", + "longitude": 109.7397 } } }, @@ -439,6 +923,10 @@ "name": "广州市", "longitude": 113.2644 }, + "440200": { + "name": "韶关市", + "longitude": 113.5972 + }, "440300": { "name": "深圳市", "longitude": 114.0579 @@ -447,14 +935,54 @@ "name": "珠海市", "longitude": 113.5765 }, + "440500": { + "name": "汕头市", + "longitude": 116.6819 + }, "440600": { "name": "佛山市", "longitude": 113.1214 }, + "440700": { + "name": "江门市", + "longitude": 113.0815 + }, + "440800": { + "name": "湛江市", + "longitude": 110.3594 + }, + "440900": { + "name": "茂名市", + "longitude": 110.9255 + }, + "441200": { + "name": "肇庆市", + "longitude": 112.4651 + }, "441300": { "name": "惠州市", "longitude": 114.4162 }, + "441400": { + "name": "梅州市", + "longitude": 116.1222 + }, + "441500": { + "name": "汕尾市", + "longitude": 115.3753 + }, + "441600": { + "name": "河源市", + "longitude": 114.6978 + }, + "441700": { + "name": "阳江市", + "longitude": 111.9826 + }, + "441800": { + "name": "清远市", + "longitude": 113.056 + }, "441900": { "name": "东莞市", "longitude": 113.7518 @@ -462,6 +990,18 @@ "442000": { "name": "中山市", "longitude": 113.3928 + }, + "445100": { + "name": "潮州市", + "longitude": 116.6226 + }, + "445200": { + "name": "揭阳市", + "longitude": 116.3729 + }, + "445300": { + "name": "云浮市", + "longitude": 112.0444 } } }, @@ -473,17 +1013,57 @@ "name": "南宁市", "longitude": 108.3275 }, + "450200": { + "name": "柳州市", + "longitude": 109.4281 + }, "450300": { "name": "桂林市", "longitude": 110.299 }, + "450400": { + "name": "梧州市", + "longitude": 111.2792 + }, "450500": { "name": "北海市", "longitude": 109.1201 }, + "450600": { + "name": "防城港市", + "longitude": 108.3548 + }, "450700": { "name": "钦州市", "longitude": 108.6544 + }, + "450800": { + "name": "贵港市", + "longitude": 109.5989 + }, + "450900": { + "name": "玉林市", + "longitude": 110.1648 + }, + "451000": { + "name": "百色市", + "longitude": 106.6186 + }, + "451100": { + "name": "贺州市", + "longitude": 111.5667 + }, + "451200": { + "name": "河池市", + "longitude": 108.0856 + }, + "451300": { + "name": "来宾市", + "longitude": 109.2215 + }, + "451400": { + "name": "崇左市", + "longitude": 107.3647 } } }, @@ -499,6 +1079,14 @@ "name": "三亚市", "longitude": 109.5119 }, + "460300": { + "name": "三沙市", + "longitude": 112.3387 + }, + "460400": { + "name": "儋州市", + "longitude": 109.5808 + }, "469006": { "name": "万宁市", "longitude": 110.3911 @@ -535,21 +1123,85 @@ "name": "成都市", "longitude": 104.0665 }, + "510300": { + "name": "自贡市", + "longitude": 104.7734 + }, + "510400": { + "name": "攀枝花市", + "longitude": 101.7184 + }, "510500": { "name": "泸州市", "longitude": 105.4433 }, + "510600": { + "name": "德阳市", + "longitude": 104.398 + }, "510700": { "name": "绵阳市", "longitude": 104.6796 }, + "510800": { + "name": "广元市", + "longitude": 105.8298 + }, + "510900": { + "name": "遂宁市", + "longitude": 105.5929 + }, + "511000": { + "name": "内江市", + "longitude": 105.0584 + }, + "511100": { + "name": "乐山市", + "longitude": 103.7654 + }, "511300": { "name": "南充市", "longitude": 106.1107 }, + "511400": { + "name": "眉山市", + "longitude": 103.8485 + }, "511500": { "name": "宜宾市", "longitude": 104.6432 + }, + "511600": { + "name": "广安市", + "longitude": 106.6332 + }, + "511700": { + "name": "达州市", + "longitude": 107.468 + }, + "511800": { + "name": "雅安市", + "longitude": 103.0133 + }, + "511900": { + "name": "巴中市", + "longitude": 106.7475 + }, + "512000": { + "name": "资阳市", + "longitude": 104.6279 + }, + "513200": { + "name": "阿坝藏族羌族自治州", + "longitude": 102.2247 + }, + "513300": { + "name": "甘孜藏族自治州", + "longitude": 101.9638 + }, + "513400": { + "name": "凉山彝族自治州", + "longitude": 102.2587 } } }, @@ -561,13 +1213,37 @@ "name": "贵阳市", "longitude": 106.7135 }, + "520200": { + "name": "六盘水市", + "longitude": 104.8304 + }, "520300": { "name": "遵义市", "longitude": 106.9274 }, + "520400": { + "name": "安顺市", + "longitude": 105.9476 + }, "520500": { "name": "毕节市", "longitude": 105.285 + }, + "520600": { + "name": "铜仁市", + "longitude": 109.1896 + }, + "522300": { + "name": "黔西南布依族苗族自治州", + "longitude": 104.9064 + }, + "522600": { + "name": "黔东南苗族侗族自治州", + "longitude": 107.9828 + }, + "522700": { + "name": "黔南布依族苗族自治州", + "longitude": 107.5172 } } }, @@ -583,13 +1259,61 @@ "name": "曲靖市", "longitude": 103.7962 }, + "530400": { + "name": "玉溪市", + "longitude": 102.5439 + }, + "530500": { + "name": "保山市", + "longitude": 99.1618 + }, + "530600": { + "name": "昭通市", + "longitude": 103.7172 + }, + "530700": { + "name": "丽江市", + "longitude": 100.233 + }, + "530800": { + "name": "普洱市", + "longitude": 100.9723 + }, + "530900": { + "name": "临沧市", + "longitude": 100.0869 + }, + "532300": { + "name": "楚雄彝族自治州", + "longitude": 101.546 + }, "532500": { - "name": "红河州", + "name": "红河哈尼族彝族自治州", "longitude": 103.384 }, + "532600": { + "name": "文山壮族苗族自治州", + "longitude": 104.244 + }, + "532800": { + "name": "西双版纳傣族自治州", + "longitude": 100.7979 + }, "532900": { - "name": "大理州", + "name": "大理白族自治州", "longitude": 100.2257 + }, + "533100": { + "name": "德宏傣族景颇族自治州", + "longitude": 98.5894 + }, + "533300": { + "name": "怒江傈僳族自治州", + "longitude": 98.8543 + }, + "533400": { + "name": "迪庆藏族自治州", + "longitude": 99.7022 } } }, @@ -604,6 +1328,26 @@ "540200": { "name": "日喀则市", "longitude": 88.8851 + }, + "540300": { + "name": "昌都市", + "longitude": 97.1785 + }, + "540400": { + "name": "林芝市", + "longitude": 94.3615 + }, + "540500": { + "name": "山南市", + "longitude": 91.7731 + }, + "540600": { + "name": "那曲市", + "longitude": 92.0512 + }, + "542500": { + "name": "阿里地区", + "longitude": 80.1055 } } }, @@ -615,6 +1359,10 @@ "name": "西安市", "longitude": 108.9398 }, + "610200": { + "name": "铜川市", + "longitude": 108.9796 + }, "610300": { "name": "宝鸡市", "longitude": 107.2376 @@ -623,9 +1371,29 @@ "name": "咸阳市", "longitude": 108.7093 }, + "610500": { + "name": "渭南市", + "longitude": 109.4897 + }, + "610600": { + "name": "延安市", + "longitude": 109.4897 + }, + "610700": { + "name": "汉中市", + "longitude": 107.0233 + }, "610800": { "name": "榆林市", "longitude": 109.7346 + }, + "610900": { + "name": "安康市", + "longitude": 109.0293 + }, + "611000": { + "name": "商洛市", + "longitude": 109.9404 } } }, @@ -637,13 +1405,57 @@ "name": "兰州市", "longitude": 103.8343 }, + "620200": { + "name": "嘉峪关市", + "longitude": 98.2892 + }, + "620300": { + "name": "金昌市", + "longitude": 102.1879 + }, + "620400": { + "name": "白银市", + "longitude": 104.1386 + }, "620500": { "name": "天水市", "longitude": 105.7249 }, + "620600": { + "name": "武威市", + "longitude": 102.638 + }, "620700": { "name": "张掖市", "longitude": 100.4498 + }, + "620800": { + "name": "平凉市", + "longitude": 106.665 + }, + "620900": { + "name": "酒泉市", + "longitude": 98.4945 + }, + "621000": { + "name": "庆阳市", + "longitude": 107.6436 + }, + "621100": { + "name": "定西市", + "longitude": 104.6263 + }, + "621200": { + "name": "陇南市", + "longitude": 104.9294 + }, + "622900": { + "name": "临夏回族自治州", + "longitude": 103.212 + }, + "623000": { + "name": "甘南藏族自治州", + "longitude": 102.911 } } }, @@ -655,8 +1467,32 @@ "name": "西宁市", "longitude": 101.7801 }, + "630200": { + "name": "海东市", + "longitude": 102.1043 + }, + "632200": { + "name": "海北藏族自治州", + "longitude": 100.901 + }, + "632300": { + "name": "黄南藏族自治州", + "longitude": 102.0153 + }, + "632500": { + "name": "海南藏族自治州", + "longitude": 100.6195 + }, + "632600": { + "name": "果洛藏族自治州", + "longitude": 100.2421 + }, + "632700": { + "name": "玉树藏族自治州", + "longitude": 97.0085 + }, "632800": { - "name": "海西州", + "name": "海西蒙古族藏族自治州", "longitude": 97.3701 } } @@ -672,6 +1508,18 @@ "640200": { "name": "石嘴山市", "longitude": 106.3833 + }, + "640300": { + "name": "吴忠市", + "longitude": 106.1988 + }, + "640400": { + "name": "固原市", + "longitude": 106.2852 + }, + "640500": { + "name": "中卫市", + "longitude": 105.1896 } } }, @@ -687,9 +1535,53 @@ "name": "克拉玛依市", "longitude": 84.8739 }, + "650400": { + "name": "吐鲁番市", + "longitude": 89.1841 + }, + "650500": { + "name": "哈密市", + "longitude": 93.5149 + }, + "652300": { + "name": "昌吉回族自治州", + "longitude": 87.304 + }, + "652700": { + "name": "博尔塔拉蒙古自治州", + "longitude": 82.0748 + }, + "652800": { + "name": "巴音郭楞蒙古自治州", + "longitude": 86.1509 + }, "652900": { "name": "阿克苏地区", "longitude": 80.2606 + }, + "653000": { + "name": "克孜勒苏柯尔克孜自治州", + "longitude": 76.1728 + }, + "653100": { + "name": "喀什地区", + "longitude": 75.9891 + }, + "653200": { + "name": "和田地区", + "longitude": 79.9253 + }, + "654000": { + "name": "伊犁哈萨克自治州", + "longitude": 81.3179 + }, + "654200": { + "name": "塔城地区", + "longitude": 82.9857 + }, + "654300": { + "name": "阿勒泰地区", + "longitude": 88.1396 } } }, diff --git a/package-lock.json b/package-lock.json index 56eafd6..b1ef41e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^9.0.3", + "rehype-raw": "^7.0.0", "server-only": "^0.0.1", "tailwind-merge": "^2.6.0" }, @@ -3496,6 +3497,18 @@ "dev": true, "license": "MIT" }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.24.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", @@ -4582,6 +4595,64 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmmirror.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", @@ -4609,6 +4680,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", @@ -4622,6 +4712,23 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmmirror.com/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/html-url-attributes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", @@ -4632,6 +4739,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -6539,6 +6656,18 @@ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7175,6 +7304,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", @@ -8525,6 +8669,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmmirror.com/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vfile-message": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", @@ -8539,6 +8697,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index bf758dd..0853772 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^9.0.3", + "rehype-raw": "^7.0.0", "server-only": "^0.0.1", "tailwind-merge": "^2.6.0" }, diff --git a/scripts/generate-regions.mjs b/scripts/generate-regions.mjs index c3715eb..c604748 100644 --- a/scripts/generate-regions.mjs +++ b/scripts/generate-regions.mjs @@ -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);