// 腾讯静态地图API工具类 const QQMAP_KEY = 'RO5BZ-ECZ63-7US3C-RT5QW-TIDZE-2FF35' class StaticMapUtil { constructor() { this.key = QQMAP_KEY this.baseUrl = 'https://apis.map.qq.com/ws/staticmap/v2/' } /** * 生成静态地图URL * @param {Object} options 地图配置选项 * @returns {String} 静态地图URL */ generateMapUrl(options = {}) { const defaultOptions = { center: '39.916527,116.397128', // 默认中心点(北京) zoom: 13, // 缩放级别 size: '750*500', // 图片尺寸 scale: 2, // 高清显示 markers: [], // 标记点 labels: [], // 文字标注 paths: [], // 路径 traffic: 0, // 交通路况 0-不显示 1-显示 format: 'png' // 图片格式 } const config = { ...defaultOptions, ...options } let url = `${this.baseUrl}?key=${this.key}` url += `¢er=${config.center}` url += `&zoom=${config.zoom}` url += `&size=${config.size}` url += `&scale=${config.scale}` // 腾讯地图添加标记点 if (config.markers && config.markers.length > 0) { config.markers.forEach((marker, index) => { url += `&markers=size:${marker.size || 'medium'}|color:${marker.color || '0x2196F3'}|label:${marker.label || ' '}|${marker.latitude},${marker.longitude}` }) } // 添加路径 if (config.paths && config.paths.length > 0) { const pathsStr = config.paths.map(path => { let pathStr = `color:${path.color || '0xFF0000'}|weight:${path.weight || 5}|` pathStr += path.points.map(point => `${point.latitude},${point.longitude}`).join(';') return pathStr }).join('|') url += `&path=${encodeURIComponent(pathsStr)}` } return url } /** * 为场地列表生成静态地图URL * @param {Array} positions 场地列表 * @param {Object} options 额外选项 * @returns {String} 静态地图URL */ generatePositionsMapUrl(positions = [], options = {}) { if (!positions || positions.length === 0) { return this.generateMapUrl(options) } // 计算地图中心点和缩放级别 const center = this.calculateCenter(positions) const zoom = this.calculateOptimalZoom(positions, center) // 生成标记点 const markers = positions.map((position, index) => ({ longitude: parseFloat(position.longitude), latitude: parseFloat(position.latitude), size: 'medium', color: position.status === 'online' ? '0x4CAF50' : '0xF44336', label: String.fromCharCode(65 + (index % 26)) // A, B, C... })) return this.generateMapUrl({ center: `${center.latitude},${center.longitude}`, zoom: zoom, markers: markers, ...options }) } /** * 计算多个点的中心位置 * @param {Array} positions 位置数组 * @returns {Object} 中心点坐标 */ calculateCenter(positions) { if (positions.length === 0) { return { longitude: 116.397128, latitude: 39.916527 } } const validPositions = positions.filter(p => p.longitude && p.latitude) if (validPositions.length === 0) { return { longitude: 116.397128, latitude: 39.916527 } } if (validPositions.length === 1) { return { longitude: parseFloat(validPositions[0].longitude), latitude: parseFloat(validPositions[0].latitude) } } let sumLat = 0 let sumLng = 0 validPositions.forEach(position => { sumLat += parseFloat(position.latitude) sumLng += parseFloat(position.longitude) }) return { latitude: sumLat / validPositions.length, longitude: sumLng / validPositions.length } } /** * 计算最佳缩放级别 * @param {Array} positions 位置数组 * @param {Object} center 中心点 * @returns {Number} 缩放级别 */ calculateOptimalZoom(positions, center) { if (!positions || positions.length <= 1) { return 13 // 默认缩放级别 } const validPositions = positions.filter(p => p.longitude && p.latitude) if (validPositions.length <= 1) { return 13 } // 计算最大距离 let maxDistance = 0 validPositions.forEach(position => { const distance = this.calculateHaversineDistance( center.latitude, center.longitude, parseFloat(position.latitude), parseFloat(position.longitude) ) maxDistance = Math.max(maxDistance, distance) }) // 基于距离计算缩放级别 if (maxDistance > 10) return 10 // 10km以上 if (maxDistance > 5) return 11 // 5-10km if (maxDistance > 2) return 12 // 2-5km if (maxDistance > 1) return 13 // 1-2km if (maxDistance > 0.5) return 14 // 500m-1km if (maxDistance > 0.2) return 15 // 200-500m return 16 // 200m以内 } /** * 计算两点间距离(公里) * @param {Number} lat1 第一点纬度 * @param {Number} lng1 第一点经度 * @param {Number} lat2 第二点纬度 * @param {Number} lng2 第二点经度 * @returns {Number} 距离(公里) */ calculateHaversineDistance(lat1, lng1, lat2, lng2) { const R = 6371 // 地球半径(公里) 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 } } // 导出实例 module.exports = new StaticMapUtil()