主要思路:
- 根据坐标 获取 mapbox 瓦片范围
- 每张瓦片 256 像素计算出 地图容器的 宽和高
- 新建 mapbox 实例 等待地图加载完
- 调用getCanvas 获取 base64 并下载
const renderLoading = () => {
const loadingContainer = document.createElement('div');
loadingContainer.classList.add(styles['download-loading'])
document.body.appendChild(loadingContainer);
loadingContainer.style = {
position: 'absolute',
top: '0',
left: '0',
right: '0',
bottom: "0"
}
ReactDOM.render(<Loading style={{ display: 'block' }} />, loadingContainer);
}
const removeLoading = () => {
const loadingContainer = document.getElementsByClassName(styles['download-loading']);
document.body.removeChild(loadingContainer[0]);
}
const latLonToTile = (lat, lon, zoom) => {
const n = Math.pow(2, zoom);
const xTile = Math.floor((lon + 180) / 360 * n);
const yTile = Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * n);
return { x: xTile, y: yTile };
};
const downloadImage = (dataUrl, filename) => {
const link = document.createElement('a');
link.href = dataUrl;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
removeLoading()
};
const handleDownloadMap = async (feature) => {
Message.info("正在下载地图, 请您耐心等待 2~3 分钟!")
const zoom = 18;
const tileSize = 256;
try {
renderLoading()
const coordinates = feature.geometry.coordinates[0];
const swLng = Math.min(...coordinates.map(pt => pt[0]));
const swLat = Math.min(...coordinates.map(pt => pt[1]));
const neLng = Math.max(...coordinates.map(pt => pt[0]));
const neLat = Math.max(...coordinates.map(pt => pt[1]));
// 计算中心点的经度
const centerLng = (swLng + neLng) / 2;
// 计算中心点的纬度
const centerLat = (swLat + neLat) / 2;
// 计算瓦片坐标范围
const swTile = latLonToTile(swLat, swLng, zoom);
const neTile = latLonToTile(neLat, neLng, zoom);
// 计算瓦片的行列索引
const startX = swTile.x;
const startY = swTile.y;
const endX = neTile.x;
const endY = neTile.y;
// 获取瓦片数量
const tileWidthCount = Math.abs(endX - startX) + 1; // 横向瓦片数量
const tileHeightCount = Math.abs(endY - startY) + 1; // 纵向瓦片数量
// 计算宽高(瓦片尺寸为 256x256 像素)
const width = tileWidthCount * tileSize;
const height = tileHeightCount * tileSize;
const NewMap = () => {
return <Map
onStyleLoad={(map) => {
// mapRef.current = map;
Message.success("地图下载成功")
const canvas = map.getCanvas()
if (canvas) {
setTimeout(() => {
const dataUrl = canvas.toDataURL('image/png');
downloadImage(dataUrl, 'map.png');
const downloadMap = document.getElementsByClassName('download-map');
document.body.removeChild(downloadMap[0]);
}, 1000);
}
}}
opt={{
center: [centerLng, centerLat],
zoom: [zoom]
}}
style={{ width, height }}
>
</Map>
}
const container = document.createElement('div');
container.classList.add('download-map')
container.style.visibility = 'hidden';
document.body.appendChild(container);
ReactDOM.render(<NewMap />, container);
container.style.display = "none"
} catch (error) {
const elements = document.getElementsByClassName('download-map');
Message.error("下载失败,请刷新页面重试!");
document.body.removeChild(elements[0]);
}
};