You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
4.6 KiB

/**
* 天地图 H5 封装(仅浏览器端)
*/
import { getDefaultMarkerUrl, normalizeMarkerIconUrl } from '@/common/h5-asset.js'
const API_BASE = 'https://api.tianditu.gov.cn/api?v=4.0'
let scriptPromise = null
export function loadTiandituScript(tk) {
if (typeof window === 'undefined') {
return Promise.reject(new Error('非浏览器环境'))
}
if (window.T && window.T.Map) {
return Promise.resolve(window.T)
}
if (scriptPromise) return scriptPromise
scriptPromise = new Promise((resolve, reject) => {
const existed = document.querySelector('script[data-tianditu-api]')
if (existed) {
existed.addEventListener('load', () => resolve(window.T))
existed.addEventListener('error', reject)
return
}
const script = document.createElement('script')
script.type = 'text/javascript'
script.src = `${API_BASE}&tk=${tk}`
script.setAttribute('data-tianditu-api', '1')
script.onload = () => {
if (window.T) resolve(window.T)
else reject(new Error('天地图 API 加载失败'))
}
script.onerror = () => reject(new Error('天地图脚本加载失败'))
document.head.appendChild(script)
})
return scriptPromise
}
export class TiandituMapHelper {
constructor(options) {
this.containerId = options.containerId
this.tk = options.tk
this.onMarkerClick = options.onMarkerClick || (() => {})
this.map = null
this.pointMarkers = []
this.userMarker = null
this._defaultIconUrl = getDefaultMarkerUrl()
}
async init(lng, lat, zoom) {
await loadTiandituScript(this.tk)
await new Promise((r) => {
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(r)
else setTimeout(r, 50)
})
const el = document.getElementById(this.containerId)
if (!el) throw new Error('地图容器不存在: ' + this.containerId)
const T = window.T
this.map = new T.Map(this.containerId)
this.map.centerAndZoom(new T.LngLat(lng, lat), zoom)
if (typeof this.map.enableScrollWheelZoom === 'function') {
this.map.enableScrollWheelZoom()
}
await new Promise((r) => setTimeout(r, 200))
if (typeof this.map.checkResize === 'function') {
this.map.checkResize()
}
return this.map
}
clearPointMarkers() {
if (!this.map) return
this.pointMarkers.forEach((m) => {
try {
this.map.removeOverLay(m)
} catch (e) {}
})
this.pointMarkers = []
}
resolveIconUrl(rawUrl) {
const iconUrl = normalizeMarkerIconUrl(rawUrl || this._defaultIconUrl)
if (!/^https?:\/\//i.test(iconUrl)) {
return this._defaultIconUrl
}
return iconUrl
}
async setPointMarkers(list) {
if (!this.map || !window.T) return
const T = window.T
this.clearPointMarkers()
if (!list || !list.length) return
for (let i = 0; i < list.length; i++) {
const item = list[i]
const lng = parseFloat(item.lng)
const lat = parseFloat(item.lat)
if (isNaN(lng) || isNaN(lat)) continue
const iconUrl = this.resolveIconUrl(item.iconUrl)
const point = new T.LngLat(lng, lat)
const MARKER_SIZE = 44
const icon = new T.Icon({
iconUrl,
iconSize: new T.Point(MARKER_SIZE, MARKER_SIZE),
iconAnchor: new T.Point(MARKER_SIZE / 2, MARKER_SIZE)
})
const marker = new T.Marker(point, { icon })
if (typeof marker.setZIndexOffset === 'function') {
marker.setZIndexOffset(1000)
}
const pointId = item.id
marker.addEventListener('click', () => {
this.onMarkerClick({ markerId: pointId })
})
this.map.addOverLay(marker)
this.pointMarkers.push(marker)
}
if (typeof this.map.checkResize === 'function') {
setTimeout(() => this.map.checkResize(), 150)
}
}
setUserMarker(lng, lat) {
if (!this.map || lng == null || lat == null) return
const T = window.T
const point = new T.LngLat(lng, lat)
if (this.userMarker) {
try {
this.map.removeOverLay(this.userMarker)
} catch (e) {}
this.userMarker = null
}
this.userMarker = new T.Circle(point, 60, {
color: '#1791fc',
weight: 2,
opacity: 0.9,
fillColor: '#1791fc',
fillOpacity: 0.25,
lineStyle: 'solid'
})
this.map.addOverLay(this.userMarker)
}
centerAndZoom(lng, lat, zoom) {
if (!this.map) return
this.map.centerAndZoom(new T.LngLat(lng, lat), zoom)
}
panTo(lng, lat) {
if (!this.map) return
this.map.panTo(new T.LngLat(lng, lat))
}
getZoom() {
return this.map ? this.map.getZoom() : null
}
destroy() {
this.clearPointMarkers()
if (this.userMarker && this.map) {
try {
this.map.removeOverLay(this.userMarker)
} catch (e) {}
}
this.userMarker = null
this.map = null
}
}
export function buildTiandituNavUrl(wgsLng, wgsLat, name) {
const n = encodeURIComponent(name || '目的地')
return `https://map.tianditu.gov.cn/?l=${wgsLng}&lat=${wgsLat}&name=${n}`
}