UNPKG

iovmap

Version:
161 lines (141 loc) 4.49 kB
/* * 在百度自定义 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) }