iovmap
Version:
a map sdk base on leaflet
161 lines (141 loc) • 4.49 kB
JavaScript
/*
* 在百度自定义 CRS 上叠加 EPSG:3857/900913 瓦片。
* 关键 GridLayer:格子定位交给百度 CRS,格子内容绘制 WebMercator 影像。
*/
import L from 'leaflet'
const MAX_LATITUDE = 85.0511287798
function clampLat(lat) {
return Math.max(Math.min(lat, MAX_LATITUDE), -MAX_LATITUDE)
}
function lon2tile(lon, zoom) {
return Math.floor(((lon + 180) / 360) * Math.pow(2, zoom))
}
function lat2tile(lat, zoom) {
const rad = (clampLat(lat) * Math.PI) / 180
return Math.floor(
((1 - Math.log(Math.tan(rad) + 1 / Math.cos(rad)) / Math.PI) / 2) * Math.pow(2, zoom),
)
}
function tile2lon(x, zoom) {
return (x / Math.pow(2, zoom)) * 360 - 180
}
function tile2lat(y, zoom) {
const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, zoom)
return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))
}
export const MercatorTileLayer = L.GridLayer.extend({
options: {
// nginx 源瓦片是否为 TMS(Y 轴反向)
sourceTms: false,
maxZoom: 18,
minZoom: 0,
maxNativeZoom: 18,
opacity: 1,
zIndex: 450,
tileSize: 256,
pane: 'overlayPane',
},
initialize(url, options) {
L.setOptions(this, options)
this._url = url
},
_getSourceTileUrl(z, x, y) {
let ty = y
if (this.options.sourceTms) {
ty = Math.pow(2, z) - 1 - y
}
return this._url
.replace(/\{z\}/gi, String(z))
.replace(/\{x\}/gi, String(x))
.replace(/\{y\}/gi, String(ty))
},
_getWmZoom(mapZoom) {
const maxNative = this.options.maxNativeZoom != null ? this.options.maxNativeZoom : 18
const minZoom = this.options.minZoom != null ? this.options.minZoom : 0
// 百度缩放级别通常比 WebMercator 大 1
let z = Math.round(mapZoom) - 1
if (!isFinite(z)) {
z = 14
}
return Math.max(minZoom, Math.min(maxNative, z))
},
createTile(coords, done) {
const canvas = L.DomUtil.create('canvas', 'leaflet-tile')
const size = this.getTileSize()
canvas.width = size.x
canvas.height = size.y
const ctx = canvas.getContext('2d')
const that = this
let tileBounds
try {
tileBounds = this._tileCoordsToBounds(coords)
} catch (e) {
setTimeout(function () {
done(e, canvas)
}, 0)
return canvas
}
const sw = tileBounds.getSouthWest()
const ne = tileBounds.getNorthEast()
const lngMin = Math.min(sw.lng, ne.lng)
const lngMax = Math.max(sw.lng, ne.lng)
const latMin = Math.min(sw.lat, ne.lat)
const latMax = Math.max(sw.lat, ne.lat)
const wmZ = this._getWmZoom(coords.z)
const n = Math.pow(2, wmZ)
const xMin = lon2tile(lngMin, wmZ)
const xMax = lon2tile(lngMax, wmZ)
const yMin = lat2tile(latMax, wmZ)
const yMax = lat2tile(latMin, wmZ)
let pending = 0
let settled = 0
function finish() {
settled++
if (settled >= pending) {
done(null, canvas)
}
}
for (let x = xMin; x <= xMax; x++) {
for (let y = yMin; y <= yMax; y++) {
if (x < 0 || y < 0 || x >= n || y >= n) {
continue
}
pending++
;(function (tx, ty) {
const img = new Image()
img.onload = function () {
try {
const west = tile2lon(tx, wmZ)
const east = tile2lon(tx + 1, wmZ)
const north = tile2lat(ty, wmZ)
const south = tile2lat(ty + 1, wmZ)
const dx = lngMax - lngMin || 1
const dy = latMax - latMin || 1
const bx0 = ((west - lngMin) / dx) * size.x
const bx1 = ((east - lngMin) / dx) * size.x
const by0 = ((latMax - north) / dy) * size.y
const by1 = ((latMax - south) / dy) * size.y
ctx.drawImage(img, bx0, by0, Math.max(1, bx1 - bx0), Math.max(1, by1 - by0))
} catch (err) {
/* ignore */
}
finish()
}
img.onerror = function () {
finish()
}
img.src = that._getSourceTileUrl(wmZ, tx, ty)
})(x, y)
}
}
if (pending === 0) {
setTimeout(function () {
done(null, canvas)
}, 0)
}
return canvas
},
})
export function mercatorTileLayer(url, options) {
return new MercatorTileLayer(url, options)
}