TileCutter.js
4.08 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
/*
* @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);
// console.warn(`瓦片编号: X(${tileStartX} -> ${tileEndX}), Y(${tileStartY} -> ${tileEndY})`);
const cols = tileEndX - tileStartX + 1;
const rows = tileEndY - tileStartY + 1;
const tileWidth = imgWidth / cols;
const tileHeight = imgHeight / rows;
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 x = 0; x < cols; x++) {
for (let y = 0; y < rows; y++) {
ctx.clearRect(0, 0, tileSize, tileSize);
ctx.drawImage(
image,
x * tileWidth, y * tileHeight, tileWidth, tileHeight, // 源图像区域
0, 0, tileSize, tileSize // 目标画布区域
);
canvas.toBlob((blob) => {
if (!blob) {
console.error("瓦片转换失败!");
return;
}
const tileX = tileStartX + x;
const tileY = tileStartY + y;
// console.warn(`保存瓦片: ${tileX}_${tileY}_${zoomLevel}.png`);
// saveTile(blob, `${tileX}_${tileY}_${zoomLevel}.png`);
// 获取当前北京时间(UTC+8)
const beijingTime = dayjs().add(8, "hour").toDate();
// 使用 JSZip 将每个瓦片添加到压缩包中
zip.file(`${tileX}_${tileY}_${zoomLevel}.png`, blob, { date: beijingTime });
tileIndex++;
// 如果所有瓦片都处理完,生成并下载压缩包
if (tileIndex === cols * rows) {
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`);
});
}