TileCutter.js
4.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
* @Date: 2025-01-22 11:45:30
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-02-08 10:24:25
* @FilePath: /map-demo/src/utils/TileCutter.js
* @Description: 文件描述
*/
import JSZip from "jszip";
import { saveAs } from "file-saver";
import dayjs from "dayjs";
const tileSize = 512;
export function TileCutter(imageURL, bounds, zoomLevel) {
const img = new Image();
img.crossOrigin = "Anonymous"; // 避免跨域问题
img.src = imageURL;
img.onload = () => {
sliceImageToTiles(img, bounds, zoomLevel);
};
}
function sliceImageToTiles(image, bounds, zoomLevel) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const imgWidth = image.width;
const imgHeight = image.height;
const southWest = bounds.getSouthWest();
const northEast = bounds.getNorthEast();
const lonStart = southWest.lng;
const latStart = southWest.lat;
const lonEnd = northEast.lng;
const latEnd = northEast.lat;
let tileStartX = lonToTileX(lonStart, zoomLevel);
let tileEndX = lonToTileX(lonEnd, zoomLevel);
let tileStartY = latToTileY(latEnd, zoomLevel); // 取 latEnd 作为起点
let tileEndY = latToTileY(latStart, zoomLevel); // 取 latStart 作为终点
// 确保 tileStartX <= tileEndX,tileStartY <= tileEndY
tileStartX = Math.min(tileStartX, tileEndX);
tileEndX = Math.max(tileStartX, tileEndX);
tileStartY = Math.min(tileStartY, tileEndY);
tileEndY = Math.max(tileStartY, tileEndY);
const scaleFactor = 2; // 调高分辨率倍率
canvas.width = tileSize * scaleFactor;
canvas.height = tileSize * scaleFactor;
ctx.scale(scaleFactor, scaleFactor);
const zip = new JSZip(); // 创建一个 JSZip 实例,用来打包所有瓦片
let tileIndex = 0; // 瓦片索引,用来给每个瓦片命名
// 计算每个瓦片的经纬度范围
for (let tileX = tileStartX; tileX <= tileEndX; tileX++) {
for (let tileY = tileStartY; tileY <= tileEndY; tileY++) {
// 计算当前瓦片的经纬度范围
const tileLonStart = tileX * 360 / Math.pow(2, zoomLevel) - 180;
const tileLonEnd = (tileX + 1) * 360 / Math.pow(2, zoomLevel) - 180;
const tileLatStart = Math.atan(Math.sinh(Math.PI * (1 - 2 * (tileY + 1) / Math.pow(2, zoomLevel)))) * 180 / Math.PI;
const tileLatEnd = Math.atan(Math.sinh(Math.PI * (1 - 2 * tileY / Math.pow(2, zoomLevel)))) * 180 / Math.PI;
// 计算图片在当前瓦片中的位置和尺寸
const tileImgX = (lonStart - tileLonStart) / (tileLonEnd - tileLonStart) * tileSize;
const tileImgY = (tileLatEnd - latEnd) / (tileLatEnd - tileLatStart) * tileSize;
const tileImgWidth = (lonEnd - lonStart) / (tileLonEnd - tileLonStart) * tileSize;
const tileImgHeight = (latEnd - latStart) / (tileLatEnd - tileLatStart) * tileSize;
ctx.clearRect(0, 0, tileSize * scaleFactor, tileSize * scaleFactor);
// 根据图片在瓦片中的实际位置和尺寸绘制
ctx.drawImage(
image,
0, 0, imgWidth, imgHeight, // 源图像区域(使用完整图片)
tileImgX, tileImgY, tileImgWidth, tileImgHeight // 目标画布区域(保持实际位置和比例)
);
canvas.toBlob((blob) => {
if (!blob) {
console.error("瓦片转换失败!");
return;
}
// 获取当前北京时间(UTC+8)
const beijingTime = dayjs().add(8, "hour").toDate();
// 使用 JSZip 将每个瓦片添加到压缩包中
zip.file(`${tileX}_${tileY}_${zoomLevel}.png`, blob, { date: beijingTime });
tileIndex++;
// 如果所有瓦片都处理完,生成并下载压缩包
const totalTiles = (tileEndX - tileStartX + 1) * (tileEndY - tileStartY + 1);
if (tileIndex === totalTiles) {
generateAndDownloadZip(zip, zoomLevel);
}
}, "image/png", 1.0);
}
}
}
// 经纬度转换为瓦片坐标
function lonToTileX(lon, zoom) {
return Math.floor(((lon + 180) / 360) * Math.pow(2, zoom));
}
function latToTileY(lat, zoom) {
return Math.floor(
((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2) * Math.pow(2, zoom)
);
}
function saveTile(blob, filename) {
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// 生成并下载压缩包
function generateAndDownloadZip(zip, zoomLevel) {
zip.generateAsync({ type: "blob" }).then((content) => {
// 使用 FileSaver.js 下载压缩包
saveAs(content, `${zoomLevel}级瓦片切片包.zip`);
});
}