From a11d561dc0d24d4467a77ccc50f8addf08ca1270 Mon Sep 17 00:00:00 2001 From: abearxiong Date: Fri, 9 Jan 2026 22:54:07 +0800 Subject: [PATCH] udpate --- .env.example | 1 + .gitignore | 3 + address.json | 6 ++ distance-results.json | 8 ++ package.json | 21 +++++ plan/v0.0.1.md | 34 ++++++++ pnpm-lock.yaml | 56 +++++++++++++ public/docs.md | 109 +++++++++++++++++++++++++ public/index.html | 183 ++++++++++++++++++++++++++++++++++++++++++ public/index.ts | 133 ++++++++++++++++++++++++++++++ 10 files changed, 554 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 address.json create mode 100644 distance-results.json create mode 100644 package.json create mode 100644 plan/v0.0.1.md create mode 100644 pnpm-lock.yaml create mode 100644 public/docs.md create mode 100644 public/index.html create mode 100644 public/index.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d7e3ea3 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +AMAP_KEY=*** \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4036313 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +.env +!.env*example \ No newline at end of file diff --git a/address.json b/address.json new file mode 100644 index 0000000..4241cee --- /dev/null +++ b/address.json @@ -0,0 +1,6 @@ +[ + { + "from": "北京市朝阳区望京SOHO", + "to": "杭州市西湖区雅仕苑" + } +] \ No newline at end of file diff --git a/distance-results.json b/distance-results.json new file mode 100644 index 0000000..e142433 --- /dev/null +++ b/distance-results.json @@ -0,0 +1,8 @@ +[ + { + "from": "北京市朝阳区望京SOHO", + "to": "杭州市西湖区雅仕苑", + "distanceMeters": 1128950.5753619769, + "distanceKm": "1128.95" + } +] \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..31452c0 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "test-map-distance", + "version": "0.0.2", + "description": "", + "main": "index.js", + "scripts": { + "pub": "ev deploy ./public -k test-map-distance -v 0.0.2 -u -y y" + }, + "keywords": [], + "author": "abearxiong (https://www.xiongxiao.me)", + "license": "MIT", + "packageManager": "pnpm@10.26.0", + "type": "module", + "dependencies": { + "dotenv": "^17.2.3" + }, + "devDependencies": { + "@types/bun": "^1.3.5", + "@types/node": "^25.0.3" + } +} diff --git a/plan/v0.0.1.md b/plan/v0.0.1.md new file mode 100644 index 0000000..525e08f --- /dev/null +++ b/plan/v0.0.1.md @@ -0,0 +1,34 @@ +# 地图两点距离计算 + +## 需求 + +使用高德地图 API,实现输入起点地址和终点地址,计算两者之间的直线距离。 + +## 实现方案 + +1. 使用高德地图**地理编码 API**将地址转换为经纬度坐标 +2. 使用**Haversine 公式**计算两点间的直线距离 + +## API 依赖 + +- 高德地理编码 API: `https://restapi.amap.com/v3/geocode/geo` +- 需要在高德开放平台申请 API Key + +## 输入输出 + +- 输入:起点地址(字符串)、终点地址(字符串) +- 输出:距离(米/公里) + +## 文件结构 + +``` +src/ + index.ts # 主逻辑实现 +``` + + +## 参考链接- 高德开放平台: + +https://console.amap.com/dev/key/app + +创建应用类型,选择 Web 服务,获取 API Key。 \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..0d68bd4 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,56 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + dotenv: + specifier: ^17.2.3 + version: 17.2.3 + devDependencies: + '@types/bun': + specifier: ^1.3.5 + version: 1.3.5 + '@types/node': + specifier: ^25.0.3 + version: 25.0.3 + +packages: + + '@types/bun@1.3.5': + resolution: {integrity: sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w==} + + '@types/node@25.0.3': + resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==} + + bun-types@1.3.5: + resolution: {integrity: sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw==} + + dotenv@17.2.3: + resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} + engines: {node: '>=12'} + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + +snapshots: + + '@types/bun@1.3.5': + dependencies: + bun-types: 1.3.5 + + '@types/node@25.0.3': + dependencies: + undici-types: 7.16.0 + + bun-types@1.3.5: + dependencies: + '@types/node': 25.0.3 + + dotenv@17.2.3: {} + + undici-types@7.16.0: {} diff --git a/public/docs.md b/public/docs.md new file mode 100644 index 0000000..dc68f20 --- /dev/null +++ b/public/docs.md @@ -0,0 +1,109 @@ +# 地图两点距离计算工具 + +## 简介 + +使用高德地图 API,输入起点地址和终点地址,自动计算两者之间的直线距离。 + +## 快速开始 + +### 1. 安装 Deno + +```bash +# Linux/macOS (WSL) +curl -fsSL https://deno.land/install.sh | sh + +# macOS +brew install deno +``` + +### 2. 配置高德 API Key + +**申请步骤:** +1. 访问 [高德开放平台](https://console.amap.com/dev/key/app) +2. 注册/登录账号 +3. 点击「控制台」→「应用管理」→「创建新应用」 +4. 填写应用名称,点击「添加 Key」 +5. 勾选 **Web 服务** 权限 +6. 复制生成的 Key + +**配置环境变量:** + +在项目根目录创建 `.env` 文件: + +```bash +AMAP_KEY=你的高德APIKey +``` + +### 3. 运行程序 + +```bash +deno run -A https://kevisual.xiongxiao.me/root/test-map-distance/index.ts +``` + +### 4. 查看结果 + +程序运行完成后,会在当前目录生成 `distance-results.json` 文件: + +```json +[ + { + "from": "起点地址", + "to": "终点地址", + "distanceMeters": 1120000, + "distanceKm": "1120.00" + } +] +``` + +## 本地开发 + +代码路径:https://git.xiongxiao.me/test/test-map-distance + +### 目录结构 + +``` +test-map-distance/ +├── .env # 环境变量(API Key) +├── address.json # 地址列表配置 +├── distance-results.json # 计算结果(自动生成) +├── plan/ +│ └── v0.0.1.md # 设计文档 +└── public/ + └── docs.md # 使用说明 +``` + +### address.json 格式 + +```json +[ + { "from": "北京市", "to": "上海市" }, + { "from": "广州市", "to": "深圳市" } +] +``` + +## 实现原理 + +1. **地理编码**:使用高德地图 [地理编码 API](https://restapi.amap.com/v3/geocode/geo) 将地址转换为经纬度坐标 +2. **距离计算**:使用 Haversine 公式计算两点间的直线距离 + +```typescript +// Haversine 公式 +const R = 6371000; // 地球半径(米) +const dLat = (to.lat - from.lat) * Math.PI / 180; +const dLng = (to.lng - from.lng) * Math.PI / 180; +const a = Math.sin(dLat/2)² + Math.cos(from.lat) * Math.cos(to.lat) * Math.sin(dLng/2)²; +const distance = R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); +``` + +## 常见问题 + +| 错误 | 原因 | 解决方案 | +|------|------|----------| +| `USERKEY_PLAT_NOMATCH` | API Key 未启用 Web 服务 | 登录高德开放平台,勾选 Web 服务权限 | +| `请在环境变量中设置 AMAP_KEY` | 未配置 .env 文件 | 创建 .env 文件并设置 AMAP_KEY | +| `地址解析失败` | 地址无法被识别 | 使用更详细或更简化的地址 | + +## 参考链接 + +- [高德开放平台](https://lbs.amap.com/) +- [Deno 官方文档](https://deno.com/) diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..6ae1cce --- /dev/null +++ b/public/index.html @@ -0,0 +1,183 @@ + + + + + + 地图距离计算工具 - 使用说明 + + + + + + + +
+
+
+
+
+

地图距离计算工具 - 使用说明

+
+
+
+
+ 加载中... +
+

文档加载中...

+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/public/index.ts b/public/index.ts new file mode 100644 index 0000000..99dd4af --- /dev/null +++ b/public/index.ts @@ -0,0 +1,133 @@ +// deno run -A https://kevisual.xiongxiao.me/root/test-map-distance/index.ts?a=1 + +import path from 'node:path'; +import fs from 'node:fs'; +/** + * 手动读取 .env 文件 + */ +function loadEnv() { + const envPath = path.resolve(process.cwd(), '.env'); + if (!fs.existsSync(envPath)) { + return {}; + } + const content = fs.readFileSync(envPath, 'utf-8'); + const env: Record = {}; + for (const line of content.split('\n')) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#')) continue; + const [key, ...valueParts] = trimmed.split('='); + if (key && valueParts.length > 0) { + env[key.trim()] = valueParts.join('=').trim(); + } + } + return env; +} + +const env = loadEnv(); +const AMAP_KEY = env.AMAP_KEY || ''; + +if (!AMAP_KEY) { + throw new Error('请在环境变量中设置 AMAP_KEY'); +} +/** + * 地理编码:将地址转换为经纬度 + */ +async function geocode(address: string): Promise<{ lng: number; lat: number } | null> { + const url = `https://restapi.amap.com/v3/geocode/geo?address=${encodeURIComponent(address)}&key=${AMAP_KEY}`; + const res = await fetch(url).then(r => r.json()); + + if (res.status === '1' && res.geocodes?.length > 0) { + const [lng, lat] = res.geocodes[0].location.split(',').map(Number); + return { lng, lat }; + } + return null; +} + +/** + * 计算两点间的直线距离(米) + */ +function calculateDistance( + from: { lng: number; lat: number }, + to: { lng: number; lat: number } +): number { + const R = 6371000; // 地球半径(米) + const dLat = (to.lat - from.lat) * Math.PI / 180; + const dLng = (to.lng - from.lng) * Math.PI / 180; + const a = + Math.sin(dLat / 2) ** 2 + + Math.cos(from.lat * Math.PI / 180) * + Math.cos(to.lat * Math.PI / 180) * + Math.sin(dLng / 2) ** 2; + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + return R * c; +} + +/** + * 主函数:计算两个地址间的距离 + */ +async function getDistance(fromAddress: string, toAddress: string) { + const [from, to] = await Promise.all([ + geocode(fromAddress), + geocode(toAddress) + ]); + + if (!from || !to) { + throw new Error('地址解析失败'); + } + + const distance = calculateDistance(from, to); + return { + from, + to, + distanceMeters: distance, + distanceKm: (distance / 1000).toFixed(2) + }; +} + +// 使用示例 +// getDistance('北京市', '上海市').then(console.log); +// const fromAddress = '北京市朝阳区望京SOHO'; +// const toAddress = '杭州市西湖区雅仕苑'; + + +const addressPath = path.resolve(process.cwd(), 'address.json'); + +type Address = { + from: string; + to: string; +} +let addressJson: Address[]; +// 读取 address.json 文件 +const addressData = fs.readFileSync(addressPath, 'utf-8'); +try { + addressJson = JSON.parse(addressData) as Address[]; +} catch (error) { + console.error('无法解析 address.json 文件,请确保其为有效的 JSON 格式'); + process.exit(1); +} +if (!Array.isArray(addressJson)) { + console.error('address.json 文件格式错误,应为地址对象数组'); + process.exit(1); +} + +const results: any[] = []; + +for (const addr of addressJson) { + try { + // 计算距离 + const result = await getDistance(addr.from, addr.to); + results.push({ + from: addr.from, + to: addr.to, + distanceMeters: result.distanceMeters, + distanceKm: result.distanceKm, + }); + console.log(`地址对 (${addr.from} -> ${addr.to}) 的距离为: ${result.distanceKm} 公里`); + } catch (error) { + console.error(`计算地址对 (${addr.from} -> ${addr.to}) 距离时出错:`, error); + } +} +// 将结果写入 distance-results.json 文件 +const resultPath = path.resolve(process.cwd(), 'distance-results.json'); +fs.writeFileSync(resultPath, JSON.stringify(results, null, 2), 'utf-8'); +console.log(`距离结果已保存到 ${resultPath}`); \ No newline at end of file