feat:新增地图模块,用于查找附近设备场地
This commit is contained in:
+78
-90
@@ -1,10 +1,10 @@
|
||||
// 高德静态地图API工具类
|
||||
const AMAP_KEY = '4c513a688938fd89b88b296e867f66ec'
|
||||
// 腾讯静态地图API工具类
|
||||
const QQMAP_KEY = 'RO5BZ-ECZ63-7US3C-RT5QW-TIDZE-2FF35'
|
||||
|
||||
class StaticMapUtil {
|
||||
constructor() {
|
||||
this.key = AMAP_KEY
|
||||
this.baseUrl = 'https://restapi.amap.com/v3/staticmap'
|
||||
this.key = QQMAP_KEY
|
||||
this.baseUrl = 'https://apis.map.qq.com/ws/staticmap/v2/'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -14,54 +14,40 @@ class StaticMapUtil {
|
||||
*/
|
||||
generateMapUrl(options = {}) {
|
||||
const defaultOptions = {
|
||||
location: '116.397128,39.916527', // 默认中心点(北京)
|
||||
zoom: 13, // 缩放级别
|
||||
size: '750*500', // 图片尺寸
|
||||
scale: 2, // 高清显示
|
||||
markers: [], // 标记点
|
||||
labels: [], // 文字标注
|
||||
paths: [], // 路径
|
||||
traffic: 0, // 交通路况 0-不显示 1-显示
|
||||
format: 'png' // 图片格式
|
||||
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 += `&location=${config.location}`
|
||||
url += `¢er=${config.center}`
|
||||
url += `&zoom=${config.zoom}`
|
||||
url += `&size=${config.size}`
|
||||
url += `&scale=${config.scale}`
|
||||
url += `&traffic=${config.traffic}`
|
||||
url += `&format=${config.format}`
|
||||
|
||||
// 添加标记点
|
||||
|
||||
// 腾讯地图添加标记点
|
||||
if (config.markers && config.markers.length > 0) {
|
||||
const markersStr = config.markers.map(marker => {
|
||||
let markerStr = ''
|
||||
if (marker.size) markerStr += `size:${marker.size}|`
|
||||
if (marker.color) markerStr += `color:${marker.color}|`
|
||||
if (marker.label) markerStr += `label:${marker.label}|`
|
||||
markerStr += `${marker.longitude},${marker.latitude}`
|
||||
return markerStr
|
||||
}).join('|')
|
||||
url += `&markers=${encodeURIComponent(markersStr)}`
|
||||
config.markers.forEach((marker, index) => {
|
||||
url += `&markers=size:${marker.size || 'medium'}|color:${marker.color || '0x2196F3'}|label:${marker.label || ' '}|${marker.latitude},${marker.longitude}`
|
||||
})
|
||||
}
|
||||
|
||||
// 添加文字标注
|
||||
if (config.labels && config.labels.length > 0) {
|
||||
const labelsStr = config.labels.map(label => {
|
||||
let labelStr = ''
|
||||
if (label.content) labelStr += `content:${label.content}|`
|
||||
if (label.font) labelStr += `font:${label.font}|`
|
||||
if (label.bold) labelStr += `bold:${label.bold}|`
|
||||
if (label.fontSize) labelStr += `fontSize:${label.fontSize}|`
|
||||
if (label.fontColor) labelStr += `fontColor:${label.fontColor}|`
|
||||
if (label.background) labelStr += `background:${label.background}|`
|
||||
labelStr += `${label.longitude},${label.latitude}`
|
||||
return labelStr
|
||||
// 添加路径
|
||||
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 += `&labels=${encodeURIComponent(labelsStr)}`
|
||||
url += `&path=${encodeURIComponent(pathsStr)}`
|
||||
}
|
||||
|
||||
return url
|
||||
@@ -86,13 +72,13 @@ class StaticMapUtil {
|
||||
const markers = positions.map((position, index) => ({
|
||||
longitude: parseFloat(position.longitude),
|
||||
latitude: parseFloat(position.latitude),
|
||||
size: 'mid',
|
||||
color: position.status === 'online' ? 'green' : 'red',
|
||||
size: 'medium',
|
||||
color: position.status === 'online' ? '0x4CAF50' : '0xF44336',
|
||||
label: String.fromCharCode(65 + (index % 26)) // A, B, C...
|
||||
}))
|
||||
|
||||
return this.generateMapUrl({
|
||||
location: `${center.longitude},${center.latitude}`,
|
||||
center: `${center.latitude},${center.longitude}`,
|
||||
zoom: zoom,
|
||||
markers: markers,
|
||||
...options
|
||||
@@ -114,14 +100,23 @@ class StaticMapUtil {
|
||||
return { longitude: 116.397128, latitude: 39.916527 }
|
||||
}
|
||||
|
||||
const sum = validPositions.reduce((acc, pos) => ({
|
||||
longitude: acc.longitude + parseFloat(pos.longitude),
|
||||
latitude: acc.latitude + parseFloat(pos.latitude)
|
||||
}), { longitude: 0, latitude: 0 })
|
||||
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 {
|
||||
longitude: (sum.longitude / validPositions.length).toFixed(6),
|
||||
latitude: (sum.latitude / validPositions.length).toFixed(6)
|
||||
latitude: sumLat / validPositions.length,
|
||||
longitude: sumLng / validPositions.length
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,63 +127,56 @@ class StaticMapUtil {
|
||||
* @returns {Number} 缩放级别
|
||||
*/
|
||||
calculateOptimalZoom(positions, center) {
|
||||
if (positions.length <= 1) return 15
|
||||
if (!positions || positions.length <= 1) {
|
||||
return 13 // 默认缩放级别
|
||||
}
|
||||
|
||||
const validPositions = positions.filter(p => p.longitude && p.latitude)
|
||||
if (validPositions.length <= 1) return 15
|
||||
if (validPositions.length <= 1) {
|
||||
return 13
|
||||
}
|
||||
|
||||
// 计算最大距离
|
||||
let maxDistance = 0
|
||||
validPositions.forEach(pos => {
|
||||
const distance = this.getDistance(
|
||||
validPositions.forEach(position => {
|
||||
const distance = this.calculateHaversineDistance(
|
||||
center.latitude, center.longitude,
|
||||
parseFloat(pos.latitude), parseFloat(pos.longitude)
|
||||
parseFloat(position.latitude), parseFloat(position.longitude)
|
||||
)
|
||||
maxDistance = Math.max(maxDistance, distance)
|
||||
})
|
||||
|
||||
// 根据最大距离确定缩放级别
|
||||
if (maxDistance < 1) return 16 // 1km内
|
||||
if (maxDistance < 2) return 15 // 2km内
|
||||
if (maxDistance < 5) return 14 // 5km内
|
||||
if (maxDistance < 10) return 13 // 10km内
|
||||
if (maxDistance < 20) return 12 // 20km内
|
||||
if (maxDistance < 50) return 11 // 50km内
|
||||
return 10 // 50km以上
|
||||
// 基于距离计算缩放级别
|
||||
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 纬度1
|
||||
* @param {Number} lng1 经度1
|
||||
* @param {Number} lat2 纬度2
|
||||
* @param {Number} lng2 经度2
|
||||
* @param {Number} lat1 第一点纬度
|
||||
* @param {Number} lng1 第一点经度
|
||||
* @param {Number} lat2 第二点纬度
|
||||
* @param {Number} lng2 第二点经度
|
||||
* @returns {Number} 距离(公里)
|
||||
*/
|
||||
getDistance(lat1, lng1, lat2, lng2) {
|
||||
const radLat1 = lat1 * Math.PI / 180.0
|
||||
const radLat2 = lat2 * Math.PI / 180.0
|
||||
const a = radLat1 - radLat2
|
||||
const b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0
|
||||
let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)))
|
||||
s = s * 6378.137
|
||||
s = Math.round(s * 10000) / 10000
|
||||
return s
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载地图图片
|
||||
* @param {String} mapUrl 地图URL
|
||||
* @returns {Promise} 图片加载Promise
|
||||
*/
|
||||
preloadMapImage(mapUrl) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image()
|
||||
img.onload = () => resolve(mapUrl)
|
||||
img.onerror = reject
|
||||
img.src = mapUrl
|
||||
})
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
export default new StaticMapUtil()
|
||||
// 导出实例
|
||||
module.exports = new StaticMapUtil()
|
||||
Reference in New Issue
Block a user