1
This commit is contained in:
161
api/services/locationService.js
Normal file
161
api/services/locationService.js
Normal file
@@ -0,0 +1,161 @@
|
||||
const axios = require('axios');
|
||||
const config = require('../../config/config');
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 地理位置服务
|
||||
*/
|
||||
class LocationService {
|
||||
/**
|
||||
* 根据经纬度获取地址信息
|
||||
* @param {number} latitude 纬度
|
||||
* @param {number} longitude 经度
|
||||
* @returns {Promise<Object>} 地址信息
|
||||
*/
|
||||
async getAddressByLocation(latitude, longitude) {
|
||||
try {
|
||||
const url = `https://apis.map.qq.com/ws/geocoder/v1/?location=${latitude},${longitude}&key=${config.qq_map_key}`;
|
||||
const response = await axios.get(url);
|
||||
|
||||
if (response.data.status !== 0) {
|
||||
throw new Error('地理位置解析失败, ' + response.data.message);
|
||||
}
|
||||
|
||||
let addressComponent = response.data.result.address_component
|
||||
let formatAddress = this.formatAddressInfo(addressComponent)
|
||||
|
||||
|
||||
|
||||
return formatAddress;
|
||||
|
||||
} catch (error) {
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据地址获取经纬度
|
||||
* @param {string} address 地址
|
||||
* @returns {Promise<Object>} 经纬度信息
|
||||
*/
|
||||
async getLocationByAddress(address) {
|
||||
|
||||
|
||||
const url = `https://apis.map.qq.com/ws/geocoder/v1/?address=${encodeURIComponent(address)}&key=${config.qq_map_key}`;
|
||||
const response = await axios.get(url);
|
||||
|
||||
if (response.data.status !== 0) {
|
||||
throw new Error('地址解析失败, ' + response.data.message);
|
||||
}
|
||||
|
||||
let addressComponent = response.data.result.location
|
||||
return addressComponent;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两点之间的距离(公里)
|
||||
* @param {number} lat1 第一个点的纬度
|
||||
* @param {number} lng1 第一个点的经度
|
||||
* @param {number} lat2 第二个点的纬度
|
||||
* @param {number} lng2 第二个点的经度
|
||||
* @returns {number} 距离(公里)
|
||||
*/
|
||||
calculateDistance(lat1, lng1, lat2, lng2) {
|
||||
const R = 6371; // 地球半径(公里)
|
||||
const dLat = this.toRadians(lat2 - lat1);
|
||||
const dLng = this.toRadians(lng2 - lng1);
|
||||
|
||||
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(this.toRadians(lat1)) * Math.cos(this.toRadians(lat2)) *
|
||||
Math.sin(dLng / 2) * Math.sin(dLng / 2);
|
||||
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
const distance = R * c;
|
||||
|
||||
return Math.round(distance * 100) / 100; // 保留两位小数
|
||||
}
|
||||
|
||||
/**
|
||||
* 角度转弧度
|
||||
* @param {number} degrees 角度
|
||||
* @returns {number} 弧度
|
||||
*/
|
||||
toRadians(degrees) {
|
||||
return degrees * (Math.PI / 180);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化地址信息
|
||||
* @param {Object} addressComponent 地址组件
|
||||
* @returns {Object} 格式化后的地址信息
|
||||
*/
|
||||
formatAddressInfo(addressComponent) {
|
||||
// 处理省份名称,去掉"省"、"市"、"自治区"等后缀
|
||||
let province = addressComponent.province || '';
|
||||
province = province.replace(/省|市|自治区|特别行政区/g, '');
|
||||
|
||||
// 处理城市名称,去掉"市"后缀
|
||||
let city = addressComponent.city || '';
|
||||
city = city.replace(/市/g, '');
|
||||
|
||||
// 处理区县名称
|
||||
let district = addressComponent.district || '';
|
||||
|
||||
// 检查是否为直辖市(北京、上海、天津、重庆)
|
||||
const municipalities = ['北京', '上海', '天津', '重庆'];
|
||||
const isMunicipality = municipalities.includes(province) ||
|
||||
municipalities.some(m => addressComponent.province && addressComponent.province.includes(m));
|
||||
|
||||
// 如果是直辖市,调整省份和城市字段
|
||||
if (isMunicipality) {
|
||||
// 直辖市:province 保持为直辖市名称,city 设置为区名
|
||||
const municipalityName = municipalities.find(m =>
|
||||
province.includes(m) || (addressComponent.province && addressComponent.province.includes(m))
|
||||
) || province;
|
||||
|
||||
province = municipalityName;
|
||||
|
||||
// 特殊处理:浦东新区、滨海新区等特殊区名,保持完整名称
|
||||
if (district.includes('新区') || district.includes('开发区') || district.includes('高新区')) {
|
||||
city = district; // 保持完整的区名作为城市(不去掉后缀)
|
||||
district = ''; // 清空区字段
|
||||
} else {
|
||||
// 普通区名去掉后缀后作为城市
|
||||
city = district.replace(/区|县/g, '');
|
||||
district = ''; // 清空区字段
|
||||
}
|
||||
} else {
|
||||
// 非直辖市,按正常逻辑处理
|
||||
district = district.replace(/区|县/g, '');
|
||||
}
|
||||
|
||||
// 构建简化的地址格式:城市 区县
|
||||
let formatted_address = '';
|
||||
if (city && district) {
|
||||
formatted_address = `${city} ${district}`;
|
||||
} else if (city) {
|
||||
formatted_address = city;
|
||||
} else if (district) {
|
||||
formatted_address = district;
|
||||
} else if (province) {
|
||||
formatted_address = province;
|
||||
}
|
||||
|
||||
return {
|
||||
province: province,
|
||||
city: city,
|
||||
district: district,
|
||||
street: addressComponent.street || '',
|
||||
street_number: addressComponent.street_number || '',
|
||||
formatted_address: formatted_address
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new LocationService();
|
||||
Reference in New Issue
Block a user