iovmap
Version:
a map sdk base on leaflet
419 lines (344 loc) • 10.4 kB
JavaScript
import L from 'leaflet'
import './Leaflet.CustomLayer.js'
import AMapLoader from '@amap/amap-jsapi-loader'
const AMAP_STYLES = {
normal: 'amap://styles/normal',
dark: 'amap://styles/dark',
light: 'amap://styles/light',
whitesmoke: 'amap://styles/whitesmoke',
fresh: 'amap://styles/fresh',
grey: 'amap://styles/grey',
graffiti: 'amap://styles/graffiti',
macaron: 'amap://styles/macaron',
blue: 'amap://styles/blue',
darkblue: 'amap://styles/darkblue',
wine: 'amap://styles/wine',
}
const DEFAULT_CONFIG = {
minZoom: 3,
maxZoom: 20,
padding: 0,
opacity: 1,
visible: true,
zIndex: 100,
corrdType: 'gcj02',
viewMode: '2D',
mapType: 'roadmap',
mapStyle: 'normal',
apiKey: '1fac5a5127fbe35e0219f295a506309b',
securityConfig: {
securityJsCode: '171de1c8624b802ac3c9e53c691721a9',
},
amapConfig: {
zoomEnable: true,
dragEnable: true,
resizeEnable: false,
animateEnable: false,
jogEnable: false,
doubleClickZoom: false,
keyboardEnable: false,
scrollWheel: true,
},
}
let amapSdkPromise = null
function mergeOptions(options) {
const merged = Object.assign({}, DEFAULT_CONFIG, options)
merged.securityConfig = Object.assign({}, DEFAULT_CONFIG.securityConfig, options && options.securityConfig)
merged.amapConfig = Object.assign({}, DEFAULT_CONFIG.amapConfig, options && options.amapConfig)
merged.mapStyle = (options && options.mapStyle) || merged.amapConfig.mapStyle || DEFAULT_CONFIG.mapStyle
delete merged.amapConfig.mapStyle
return merged
}
function convertMapStyleName(style) {
return style in AMAP_STYLES ? AMAP_STYLES[style] : style
}
function convertCenter(latlng, corrdType) {
if (!latlng || !L.coordConver) {
return latlng
}
if (corrdType === 'gcj02') {
const center = L.coordConver().gps84_To_gcj02(latlng.lng, latlng.lat)
return L.latLng(center.lat, center.lng)
}
if (corrdType === 'bd09') {
const center = L.coordConver().gps84_To_bd09(latlng.lng, latlng.lat)
return L.latLng(center.lat, center.lng)
}
return latlng
}
function normalizeZoomRange(minZoom, maxZoom) {
const min = Number(minZoom)
const max = Number(maxZoom)
const normalizedMin = Number.isFinite(min) ? min : DEFAULT_CONFIG.minZoom
const normalizedMax = Number.isFinite(max) ? max : DEFAULT_CONFIG.maxZoom
if (normalizedMin > normalizedMax) {
return [normalizedMax, normalizedMin]
}
return [normalizedMin, normalizedMax]
}
const AmapLayer = L.CustomLayer.extend({
options: DEFAULT_CONFIG,
initialize(options) {
const mergedOptions = mergeOptions(options)
const container = document.createElement('div')
const host = document.createElement('div')
container.className = 'leaflet-amap-layer'
container.style.position = 'absolute'
container.style.pointerEvents = 'auto'
container.style.background = 'transparent'
container.style.overflow = 'hidden'
host.className = 'leaflet-amap-layer-host'
host.style.position = 'absolute'
host.style.left = '0'
host.style.top = '0'
host.style.width = '100%'
host.style.height = '100%'
host.style.pointerEvents = 'auto'
host.style.background = 'transparent'
container.appendChild(host)
this._amap = null
this._amapHost = host
this._isLoaded = false
this._loadPromise = null
this._mapStyle = mergedOptions.mapStyle
this._amapCenter = null
this._previousMapMinZoom = null
this._previousMapMaxZoom = null
L.CustomLayer.prototype.initialize.call(
this,
Object.assign({}, mergedOptions, {
container,
}),
)
this.on('layer-render', this._handleLayerRender, this)
this.on('layer-mounted', this._handleLayerMounted, this)
},
onAdd(map) {
L.CustomLayer.prototype.onAdd.call(this, map)
this._applyLeafletZoomRange(map)
this._loadAndCreateMap()
},
onRemove(map) {
this._destroyAmap()
this._restoreLeafletZoomRange(map)
L.CustomLayer.prototype.onRemove.call(this, map)
},
setVisible(visible) {
this.options.visible = visible
if (visible) {
this.show()
} else {
this.hide()
}
return this
},
show() {
L.CustomLayer.prototype.show.call(this)
if (this._amap) {
this._handleLayerRender()
}
return this
},
hide() {
L.CustomLayer.prototype.hide.call(this)
return this
},
setMapStyle(style) {
this._mapStyle = style
this.options.mapStyle = style
if (this._amap && this.options.mapType === 'roadmap') {
this._amap.setMapStyle(convertMapStyleName(style))
}
return this
},
getMapStyle() {
return this._mapStyle
},
getAvailableStyles() {
return AMAP_STYLES
},
getAmapInstance() {
return this._amap
},
getCustomLayer() {
return this
},
destroy() {
if (this._map) {
this.remove()
} else {
this._destroyAmap()
if (this._container) {
L.DomUtil.remove(this._container)
this._container = null
}
}
return this
},
isLayerLoaded() {
return this._isLoaded
},
_convertCenter(center) {
return convertCenter(center, this.options.corrdType)
},
_updateTransform(_center, zoom) {
L.CustomLayer.prototype._updateTransform.call(this, _center, zoom)
},
__update() {
L.CustomLayer.prototype.__update.call(this)
this._amapCenter = this._convertCenter(this._center)
},
_loadAndCreateMap() {
if (this._loadPromise) {
return this._loadPromise
}
this._loadPromise = this._loadAmapSDK().then(() => {
if (!this._container || !this._amapHost || !this._map || this._amap) {
return
}
const amapConfig = Object.assign({}, this.options.amapConfig)
if (this.options.mapType === 'roadmap' && this.options.mapStyle) {
amapConfig.mapStyle = convertMapStyleName(this.options.mapStyle)
}
const zooms = normalizeZoomRange(this.options.minZoom, this.options.maxZoom)
const center = this._convertCenter(this._map.getCenter())
const mapOptions = Object.assign({}, amapConfig, {
viewMode: this.options.viewMode,
zoom: this._limitZoom(this._map.getZoom()),
zooms,
center: [center.lng, center.lat],
zoomEnable: true,
dragEnable: true,
resizeEnable: false,
animateEnable: false,
jogEnable: false,
doubleClickZoom: false,
keyboardEnable: false,
scrollWheel: true,
})
const layers = this._createAmapLayers(window.AMap)
if (layers.length) {
mapOptions.layers = layers
}
this._syncContainerSize()
this._amap = new window.AMap.Map(this._amapHost, mapOptions)
this._amap.setStatus({
zoomEnable: true,
dragEnable: true,
animateEnable: false,
jogEnable: false,
doubleClickZoom: false,
keyboardEnable: false,
scrollWheel: true,
})
this._isLoaded = true
this._handleLayerRender()
})
return this._loadPromise
},
_loadAmapSDK() {
if (window.AMap) {
return Promise.resolve(window.AMap)
}
if (!amapSdkPromise) {
window._AMapSecurityConfig = this.options.securityConfig || {}
amapSdkPromise = AMapLoader.load({
key: this.options.apiKey,
version: '2.0',
plugins: [],
})
}
return amapSdkPromise
},
_createAmapLayers(AMap) {
switch (this.options.mapType) {
case 'satellite':
return [new AMap.TileLayer.Satellite()]
case 'roadnet':
return [new AMap.TileLayer.RoadNet()]
case 'hybrid':
return [new AMap.TileLayer.Satellite(), new AMap.TileLayer.RoadNet()]
default:
return []
}
},
_handleLayerMounted() {
this._syncContainerSize()
},
_handleLayerRender() {
if (!this._amap || !this._zoomVisible) {
return
}
this._syncContainerSize()
const zoom = this._zoom != null ? this._zoom : this._map.getZoom()
const center = this._amapCenter || this._convertCenter(this._center || this._map.getCenter())
this._amap.resize()
this._amap.setZoomAndCenter(this._limitZoom(zoom), [center.lng, center.lat], true)
},
_limitZoom(zoom) {
const zooms = normalizeZoomRange(this.options.minZoom, this.options.maxZoom)
return Math.max(zooms[0], Math.min(zooms[1], zoom))
},
_applyLeafletZoomRange(map) {
if (!map) {
return
}
const zooms = normalizeZoomRange(this.options.minZoom, this.options.maxZoom)
this._previousMapMinZoom = map.options.minZoom
this._previousMapMaxZoom = map.options.maxZoom
if (map.setMinZoom) {
map.setMinZoom(zooms[0])
} else {
map.options.minZoom = zooms[0]
}
if (map.setMaxZoom) {
map.setMaxZoom(zooms[1])
} else {
map.options.maxZoom = zooms[1]
}
if (map.getZoom() < zooms[0] || map.getZoom() > zooms[1]) {
map.setZoom(this._limitZoom(map.getZoom()))
}
},
_restoreLeafletZoomRange(map) {
if (!map) {
return
}
if (map.setMinZoom) {
map.setMinZoom(this._previousMapMinZoom)
} else {
map.options.minZoom = this._previousMapMinZoom
}
if (map.setMaxZoom) {
map.setMaxZoom(this._previousMapMaxZoom)
} else {
map.options.maxZoom = this._previousMapMaxZoom
}
this._previousMapMinZoom = null
this._previousMapMaxZoom = null
},
_syncContainerSize() {
if (!this._map || !this._container || !this._amapHost) {
return
}
const size = this._map.getSize()
this._container.style.width = `${size.x}px`
this._container.style.height = `${size.y}px`
this._container.style.padding = '0'
this._amapHost.style.width = `${size.x}px`
this._amapHost.style.height = `${size.y}px`
},
_destroyAmap() {
if (this._amap) {
this._amap.destroy()
this._amap = null
}
this._amapCenter = null
this._isLoaded = false
this._loadPromise = null
},
})
function createAmapLayer(options) {
return new AmapLayer(options)
}
export { AmapLayer, createAmapLayer, AMAP_STYLES }