Files
WXProgram/miniprogram/services/mapService.ts
2025-10-19 23:38:54 +08:00

453 lines
14 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 地图服务 - 处理地图相关的功能,包括定位、路线规划、地理编码等
import { AMapRegeoResponse, RoutePlanResult, SearchResult } from '../types';
/**
* 地图服务类
* 提供地图相关功能,包括定位、路线规划、地理编码等
*/
class MapService {
// 引入高德地图小程序SDK
private amapFile: any;
// 高德地图API密钥
private readonly MAP_KEY: string;
// 昆明中心点坐标
private readonly KUNMING_CENTER: { longitude: number; latitude: number };
// 高德地图实例
private amapInstance: any;
/**
* 构造函数,初始化地图实例
*/
constructor() {
this.amapFile = require('../libs/amap-wx.js');
this.MAP_KEY = '1fc5cafd570d9fbfd14c39359d41823d';
this.KUNMING_CENTER = {
longitude: 102.833722,
latitude: 24.880095
};
this.amapInstance = new this.amapFile.AMapWX({ key: this.MAP_KEY });
}
/**
* 获取用户位置信息
* @returns 用户位置坐标
*/
async getLocation(): Promise<{ longitude: number; latitude: number }> {
return new Promise((resolve, reject) => {
console.log('开始调用高德地图SDK获取位置...');
// 添加超时机制
const timeout = setTimeout(() => {
console.error('高德地图SDK调用超时使用模拟位置');
resolve({
longitude: 102.7123,
latitude: 25.0409
});
}, 10000); // 10秒超时
try {
// 高德地图SDK的getWxLocation方法需要两个参数配置对象和回调函数
this.amapInstance.getWxLocation({
type: 'gcj02', // 使用国测局坐标系
success: (res: { longitude: number; latitude: number }) => {
clearTimeout(timeout);
console.log('高德地图SDK定位成功:', res);
resolve(res);
},
fail: (err: any) => {
clearTimeout(timeout);
console.error('高德地图SDK定位失败:', err);
// 如果高德SDK失败尝试使用微信原生API
console.log('尝试使用微信原生getLocation API...');
wx.getLocation({
type: 'gcj02',
success: (wxRes) => {
console.log('微信原生API定位成功:', wxRes);
resolve({
longitude: wxRes.longitude,
latitude: wxRes.latitude
});
},
fail: (wxErr) => {
console.error('微信原生API定位失败:', wxErr);
// 所有方法都失败,使用模拟位置
console.log('所有定位方法失败,使用模拟位置');
resolve({
longitude: 102.7123,
latitude: 25.0409
});
}
});
}
}, (locationString: string) => {
// 这是高德地图SDK的回调函数参数是格式化的位置字符串
console.log('高德地图SDK格式化位置:', locationString);
// 如果格式化位置回调被调用说明SDK已经获取到位置
// 尝试从格式化字符串中解析经纬度
if (locationString) {
const coords = locationString.split(',');
if (coords.length === 2) {
const longitude = parseFloat(coords[0]);
const latitude = parseFloat(coords[1]);
if (!isNaN(longitude) && !isNaN(latitude)) {
clearTimeout(timeout);
console.log('从格式化位置解析成功,使用解析的位置:', { longitude, latitude });
resolve({ longitude, latitude });
return;
}
}
}
// 如果无法解析继续等待success回调
console.log('格式化位置回调被调用但无法解析位置继续等待success回调...');
});
} catch (error) {
clearTimeout(timeout);
console.error('高德地图SDK调用异常:', error);
reject(error);
}
});
}
/**
* 获取当前位置信息(兼容格式)
* @returns 当前位置的详细信息
*/
async getCurrentLocation(): Promise<{
latitude: number;
longitude: number;
accuracy?: number;
speed?: number;
altitude?: number;
}> {
const location = await this.getLocation();
return {
latitude: location.latitude,
longitude: location.longitude,
accuracy: 50,
speed: 0,
altitude: 1891
};
}
/**
* 获取位置详细信息(逆地理编码)
* @param longitude 经度
* @param latitude 纬度
* @returns 逆地理编码结果
*/
async getLocationInfo(longitude: number, latitude: number): Promise<AMapRegeoResponse> {
return new Promise((resolve, reject) => {
this.amapInstance.getRegeo({
location: `${longitude.toFixed(6)},${latitude.toFixed(6)}`,
success: (res: AMapRegeoResponse) => {
console.log('逆地理编码成功:', res);
resolve(res);
},
fail: (err: any) => {
console.log('逆地理编码失败:', err);
reject(err);
}
});
});
}
/**
* 获取昆明中心点坐标
* @returns 昆明中心点坐标
*/
getKunmingCenter(): { longitude: number; latitude: number } {
return this.KUNMING_CENTER;
}
/**
* 搜索地点
* @param keyword 搜索关键词
* @param city 城市名称
* @returns 搜索结果列表
*/
async searchPoi(keyword: string, city: string): Promise<SearchResult[]> {
return new Promise((resolve, reject) => {
this.amapInstance.getPoiAround({
querykeywords: keyword,
city: city,
radius: 50000, // 搜索半径50公里
offset: 20, // 返回20条结果
success: (res: any) => {
console.log('搜索POI成功:', res);
if (res.pois && res.pois.length > 0) {
const results: SearchResult[] = res.pois.map((poi: any) => ({
id: poi.id,
name: poi.name,
address: poi.address,
longitude: parseFloat(poi.location.split(',')[0]),
latitude: parseFloat(poi.location.split(',')[1]),
phone: poi.tel || ''
}));
resolve(results);
} else {
resolve([]);
}
},
fail: (err: any) => {
console.error('搜索POI失败:', err);
reject(err);
}
});
});
}
/**
* 规划驾车路线
* @param origin 起点坐标字符串 (经度,纬度)
* @param destination 终点坐标字符串 (经度,纬度)
* @returns 路线规划结果
*/
async getDrivingRoute(origin: string, destination: string): Promise<RoutePlanResult> {
return new Promise((resolve, reject) => {
this.amapInstance.getDrivingRoute({
origin,
destination,
strategy: '0', // 推荐路线策略
showtraffic: false, // 不显示交通状况
success: (res: any) => {
console.log('高德地图路线规划API调用成功');
if (res.paths && res.paths.length > 0) {
const path = res.paths[0];
const result: RoutePlanResult = {
polyline: path.polyline || '',
distance: path.distance || 0,
duration: path.duration || 0
};
console.log(`路线规划完成: 距离${result.distance}米, 预计${result.duration}`);
resolve(result);
} else {
resolve({
polyline: '',
distance: 0,
duration: 0
});
}
},
fail: (err: any) => {
console.error('高德地图路线规划API调用失败:', err);
reject(err);
}
});
});
}
/**
* 计算两点之间的距离(米)
* @param lat1 第一个点的纬度
* @param lng1 第一个点的经度
* @param lat2 第二个点的纬度
* @param lng2 第二个点的经度
* @returns 两点之间的距离(米)
*/
calculateDistance(
lat1: number,
lng1: number,
lat2: number,
lng2: number
): number {
const R = 6371000; // 地球半径(米)
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLng = (lng2 - lng1) * Math.PI / 180;
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLng / 2) * Math.sin(dLng / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
/**
* 获取路线规划(通用版本)
* @param origin 起点坐标
* @param destination 终点坐标
* @returns 路线规划结果
*/
async getRoute(
origin: { latitude: number; longitude: number },
destination: { latitude: number; longitude: number }
): Promise<{
distance: number; // 距离(米)
duration: number; // 时间(秒)
polyline: { latitude: number; longitude: number }[];
}> {
// 对于真实模式,目前只支持驾车路线
const originStr = `${origin.longitude},${origin.latitude}`;
const destStr = `${destination.longitude},${destination.latitude}`;
const result = await this.getDrivingRoute(originStr, destStr);
// 转换polyline格式
const polylinePoints: { latitude: number; longitude: number }[] = [];
if (result.polyline) {
const points = result.polyline.split(';');
for (const point of points) {
const [lng, lat] = point.split(',');
if (lng && lat) {
polylinePoints.push({
latitude: parseFloat(lat),
longitude: parseFloat(lng)
});
}
}
}
return {
distance: result.distance,
duration: result.duration,
polyline: polylinePoints
};
}
/**
* 地理编码 - 地址转坐标
* @param address 地址字符串
* @returns 坐标信息
*/
async geocode(address: string): Promise<{
latitude: number;
longitude: number;
formattedAddress: string;
}> {
// 对于真实模式使用搜索API来模拟地理编码
const results = await this.searchPoi(address, '昆明');
if (results.length > 0) {
return {
latitude: results[0].latitude,
longitude: results[0].longitude,
formattedAddress: results[0].address
};
}
// 如果没有找到结果,返回默认位置
return {
latitude: 25.0409,
longitude: 102.7123,
formattedAddress: '云南省昆明市'
};
}
/**
* 逆地理编码 - 坐标转地址
* @param latitude 纬度
* @param longitude 经度
* @returns 地址信息
*/
async reverseGeocode(latitude: number, longitude: number): Promise<{
formattedAddress: string;
country: string;
province: string;
city: string;
district: string;
street: string;
}> {
const result = await this.getLocationInfo(longitude, latitude);
return {
// 修复不再使用不存在的formatted_address字段而是根据已有的addressComponent字段构建格式化地址
formattedAddress: `${(result.regeocode && result.regeocode.addressComponent && result.regeocode.addressComponent.province) || '云南省'}${(result.regeocode && result.regeocode.addressComponent && result.regeocode.addressComponent.city) || '昆明市'}${(result.regeocode && result.regeocode.addressComponent && result.regeocode.addressComponent.district) || '五华区'}` || '未知地址',
country: '中国',
province: (result.regeocode && result.regeocode.addressComponent && result.regeocode.addressComponent.province) || '云南省',
city: (result.regeocode && result.regeocode.addressComponent && result.regeocode.addressComponent.city) || '昆明市',
district: (result.regeocode && result.regeocode.addressComponent && result.regeocode.addressComponent.district) || '五华区',
street: (result.regeocode && result.regeocode.addressComponent && result.regeocode.addressComponent.township) || ''
};
}
/**
* 获取附近的地点
* @param latitude 纬度
* @param longitude 经度
* @param radius 搜索半径
* @param type 地点类型
* @returns 附近地点列表
*/
async getNearbyPlaces(
latitude: number,
longitude: number,
radius: number,
type?: string
): Promise<Array<{
id: string;
name: string;
latitude: number;
longitude: number;
address: string;
distance: number;
}>> {
// 对于真实模式使用搜索API
const keyword = type ? type : '';
const results = await this.searchPoi(keyword, '昆明');
return results.map(place => ({
id: place.id,
name: place.name,
latitude: place.latitude,
longitude: place.longitude,
address: place.address,
distance: this.calculateDistance(latitude, longitude, place.latitude, place.longitude)
})).filter(place => place.distance <= radius);
}
/**
* 格式化路线距离
* @param distance 距离(米)
* @returns 格式化后的距离字符串
*/
formatRouteDistance(distance: number): string {
if (distance < 1000) {
return `${distance}`;
} else {
return `${(distance / 1000).toFixed(1)}公里`;
}
}
/**
* 格式化路线时间
* @param duration 时间(秒)
* @returns 格式化后的时间字符串
*/
formatRouteDuration(duration: number): string {
if (duration < 60) {
return `${Math.round(duration)}`;
} else if (duration < 3600) {
return `${Math.round(duration / 60)} 分钟`;
} else {
const hours = Math.floor(duration / 3600);
const minutes = Math.round((duration % 3600) / 60);
return `${hours}小时${minutes}分钟`;
}
}
}
/**
* 地图服务单例实例
* 导出供应用程序全局使用
*/
export default new MapService();