133 lines
3.7 KiB
TypeScript
133 lines
3.7 KiB
TypeScript
// 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<string, string> = {};
|
|
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}`); |