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
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}`
|
|
}
|