Showing
1 changed file
with
44 additions
and
28 deletions
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-01-22 11:45:30 | 2 | * @Date: 2025-01-22 11:45:30 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-02-08 10:24:25 | 4 | + * @LastEditTime: 2025-02-24 13:21:58 |
| 5 | * @FilePath: /map-demo/src/utils/TileCutter.js | 5 | * @FilePath: /map-demo/src/utils/TileCutter.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| ... | @@ -21,27 +21,38 @@ export function TileCutter(imageURL, bounds, zoomLevel) { | ... | @@ -21,27 +21,38 @@ export function TileCutter(imageURL, bounds, zoomLevel) { |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | +/** | ||
| 25 | + * 将图片切割成瓦片并打包下载 | ||
| 26 | + * @param {HTMLImageElement} image - 要切割的图片元素 | ||
| 27 | + * @param {L.LatLngBounds} bounds - 图片边界范围,包含西南角和东北角的经纬度 | ||
| 28 | + * @param {number} zoomLevel - 地图缩放级别 | ||
| 29 | + */ | ||
| 24 | function sliceImageToTiles(image, bounds, zoomLevel) { | 30 | function sliceImageToTiles(image, bounds, zoomLevel) { |
| 31 | + // 创建画布及上下文 | ||
| 25 | const canvas = document.createElement("canvas"); | 32 | const canvas = document.createElement("canvas"); |
| 26 | const ctx = canvas.getContext("2d"); | 33 | const ctx = canvas.getContext("2d"); |
| 27 | 34 | ||
| 28 | - const imgWidth = image.width; | 35 | + // 获取原始图片尺寸 |
| 29 | - const imgHeight = image.height; | 36 | + const imgWidth = image.width; // 图片原始宽度 |
| 37 | + const imgHeight = image.height; // 图片原始高度 | ||
| 30 | 38 | ||
| 31 | - const southWest = bounds.getSouthWest(); | 39 | + // 获取边界经纬度 |
| 32 | - const northEast = bounds.getNorthEast(); | 40 | + const southWest = bounds.getSouthWest(); // 西南角坐标 |
| 41 | + const northEast = bounds.getNorthEast(); // 东北角坐标 | ||
| 33 | 42 | ||
| 34 | - const lonStart = southWest.lng; | 43 | + // 提取边界经纬度值 |
| 35 | - const latStart = southWest.lat; | 44 | + const lonStart = southWest.lng; // 起始经度 |
| 36 | - const lonEnd = northEast.lng; | 45 | + const latStart = southWest.lat; // 起始纬度 |
| 37 | - const latEnd = northEast.lat; | 46 | + const lonEnd = northEast.lng; // 结束经度 |
| 47 | + const latEnd = northEast.lat; // 结束纬度 | ||
| 38 | 48 | ||
| 39 | - let tileStartX = lonToTileX(lonStart, zoomLevel); | 49 | + // 计算瓦片坐标范围 |
| 40 | - let tileEndX = lonToTileX(lonEnd, zoomLevel); | 50 | + let tileStartX = lonToTileX(lonStart, zoomLevel); // 起始瓦片X坐标 |
| 41 | - let tileStartY = latToTileY(latEnd, zoomLevel); // 取 latEnd 作为起点 | 51 | + let tileEndX = lonToTileX(lonEnd, zoomLevel); // 结束瓦片X坐标 |
| 42 | - let tileEndY = latToTileY(latStart, zoomLevel); // 取 latStart 作为终点 | 52 | + let tileStartY = latToTileY(latEnd, zoomLevel); // 起始瓦片Y坐标 |
| 53 | + let tileEndY = latToTileY(latStart, zoomLevel); // 结束瓦片Y坐标 | ||
| 43 | 54 | ||
| 44 | - // 确保 tileStartX <= tileEndX,tileStartY <= tileEndY | 55 | + // 确保瓦片坐标范围正确(起始值小于结束值) |
| 45 | tileStartX = Math.min(tileStartX, tileEndX); | 56 | tileStartX = Math.min(tileStartX, tileEndX); |
| 46 | tileEndX = Math.max(tileStartX, tileEndX); | 57 | tileEndX = Math.max(tileStartX, tileEndX); |
| 47 | tileStartY = Math.min(tileStartY, tileEndY); | 58 | tileStartY = Math.min(tileStartY, tileEndY); |
| ... | @@ -49,9 +60,11 @@ function sliceImageToTiles(image, bounds, zoomLevel) { | ... | @@ -49,9 +60,11 @@ function sliceImageToTiles(image, bounds, zoomLevel) { |
| 49 | 60 | ||
| 50 | const scaleFactor = 2; // 调高分辨率倍率 | 61 | const scaleFactor = 2; // 调高分辨率倍率 |
| 51 | 62 | ||
| 63 | + // 设置画布尺寸 | ||
| 52 | canvas.width = tileSize * scaleFactor; | 64 | canvas.width = tileSize * scaleFactor; |
| 53 | canvas.height = tileSize * scaleFactor; | 65 | canvas.height = tileSize * scaleFactor; |
| 54 | 66 | ||
| 67 | + // 设置画布缩放 | ||
| 55 | ctx.scale(scaleFactor, scaleFactor); | 68 | ctx.scale(scaleFactor, scaleFactor); |
| 56 | 69 | ||
| 57 | const zip = new JSZip(); // 创建一个 JSZip 实例,用来打包所有瓦片 | 70 | const zip = new JSZip(); // 创建一个 JSZip 实例,用来打包所有瓦片 |
| ... | @@ -62,26 +75,28 @@ function sliceImageToTiles(image, bounds, zoomLevel) { | ... | @@ -62,26 +75,28 @@ function sliceImageToTiles(image, bounds, zoomLevel) { |
| 62 | for (let tileX = tileStartX; tileX <= tileEndX; tileX++) { | 75 | for (let tileX = tileStartX; tileX <= tileEndX; tileX++) { |
| 63 | for (let tileY = tileStartY; tileY <= tileEndY; tileY++) { | 76 | for (let tileY = tileStartY; tileY <= tileEndY; tileY++) { |
| 64 | // 计算当前瓦片的经纬度范围 | 77 | // 计算当前瓦片的经纬度范围 |
| 65 | - const tileLonStart = tileX * 360 / Math.pow(2, zoomLevel) - 180; | 78 | + const tileLonStart = tileX * 360 / Math.pow(2, zoomLevel) - 180; // 瓦片起始经度 |
| 66 | - const tileLonEnd = (tileX + 1) * 360 / Math.pow(2, zoomLevel) - 180; | 79 | + const tileLonEnd = (tileX + 1) * 360 / Math.pow(2, zoomLevel) - 180; // 瓦片结束经度 |
| 67 | - const tileLatStart = Math.atan(Math.sinh(Math.PI * (1 - 2 * (tileY + 1) / Math.pow(2, zoomLevel)))) * 180 / Math.PI; | 80 | + const tileLatStart = Math.atan(Math.sinh(Math.PI * (1 - 2 * (tileY + 1) / Math.pow(2, zoomLevel)))) * 180 / Math.PI; // 瓦片起始纬度 |
| 68 | - const tileLatEnd = Math.atan(Math.sinh(Math.PI * (1 - 2 * tileY / Math.pow(2, zoomLevel)))) * 180 / Math.PI; | 81 | + const tileLatEnd = Math.atan(Math.sinh(Math.PI * (1 - 2 * tileY / Math.pow(2, zoomLevel)))) * 180 / Math.PI; // 瓦片结束纬度 |
| 69 | - | 82 | + |
| 70 | - // 计算图片在当前瓦片中的位置和尺寸 | 83 | + // 计算图片在当前瓦片中的位置和尺寸(像素坐标) |
| 71 | - const tileImgX = (lonStart - tileLonStart) / (tileLonEnd - tileLonStart) * tileSize; | 84 | + const tileImgX = (lonStart - tileLonStart) / (tileLonEnd - tileLonStart) * tileSize; // 图片在瓦片中的X坐标 |
| 72 | - const tileImgY = (tileLatEnd - latEnd) / (tileLatEnd - tileLatStart) * tileSize; | 85 | + const tileImgY = (tileLatEnd - latEnd) / (tileLatEnd - tileLatStart) * tileSize; // 图片在瓦片中的Y坐标 |
| 73 | - const tileImgWidth = (lonEnd - lonStart) / (tileLonEnd - tileLonStart) * tileSize; | 86 | + const tileImgWidth = (lonEnd - lonStart) / (tileLonEnd - tileLonStart) * tileSize; // 图片在瓦片中的宽度 |
| 74 | - const tileImgHeight = (latEnd - latStart) / (tileLatEnd - tileLatStart) * tileSize; | 87 | + const tileImgHeight = (latEnd - latStart) / (tileLatEnd - tileLatStart) * tileSize; // 图片在瓦片中的高度 |
| 75 | - | 88 | + |
| 89 | + // 清空画布 | ||
| 76 | ctx.clearRect(0, 0, tileSize * scaleFactor, tileSize * scaleFactor); | 90 | ctx.clearRect(0, 0, tileSize * scaleFactor, tileSize * scaleFactor); |
| 77 | 91 | ||
| 78 | - // 根据图片在瓦片中的实际位置和尺寸绘制 | 92 | + // 绘制图片到画布 |
| 79 | ctx.drawImage( | 93 | ctx.drawImage( |
| 80 | image, | 94 | image, |
| 81 | 0, 0, imgWidth, imgHeight, // 源图像区域(使用完整图片) | 95 | 0, 0, imgWidth, imgHeight, // 源图像区域(使用完整图片) |
| 82 | tileImgX, tileImgY, tileImgWidth, tileImgHeight // 目标画布区域(保持实际位置和比例) | 96 | tileImgX, tileImgY, tileImgWidth, tileImgHeight // 目标画布区域(保持实际位置和比例) |
| 83 | ); | 97 | ); |
| 84 | 98 | ||
| 99 | + // 将画布内容转换为Blob对象 | ||
| 85 | canvas.toBlob((blob) => { | 100 | canvas.toBlob((blob) => { |
| 86 | if (!blob) { | 101 | if (!blob) { |
| 87 | console.error("瓦片转换失败!"); | 102 | console.error("瓦片转换失败!"); |
| ... | @@ -104,6 +119,7 @@ function sliceImageToTiles(image, bounds, zoomLevel) { | ... | @@ -104,6 +119,7 @@ function sliceImageToTiles(image, bounds, zoomLevel) { |
| 104 | } | 119 | } |
| 105 | } | 120 | } |
| 106 | } | 121 | } |
| 122 | + | ||
| 107 | // 经纬度转换为瓦片坐标 | 123 | // 经纬度转换为瓦片坐标 |
| 108 | function lonToTileX(lon, zoom) { | 124 | function lonToTileX(lon, zoom) { |
| 109 | return Math.floor(((lon + 180) / 360) * Math.pow(2, zoom)); | 125 | return Math.floor(((lon + 180) / 360) * Math.pow(2, zoom)); |
| ... | @@ -127,7 +143,7 @@ function saveTile(blob, filename) { | ... | @@ -127,7 +143,7 @@ function saveTile(blob, filename) { |
| 127 | // 生成并下载压缩包 | 143 | // 生成并下载压缩包 |
| 128 | function generateAndDownloadZip(zip, zoomLevel) { | 144 | function generateAndDownloadZip(zip, zoomLevel) { |
| 129 | zip.generateAsync({ type: "blob" }).then((content) => { | 145 | zip.generateAsync({ type: "blob" }).then((content) => { |
| 130 | - // 使用 FileSaver.js 下载压缩包 | 146 | + // 使用 FileSaver.js 下载压缩包 |
| 131 | - saveAs(content, `${zoomLevel}级瓦片切片包.zip`); | 147 | + saveAs(content, `${zoomLevel}级瓦片切片包.zip`); |
| 132 | }); | 148 | }); |
| 133 | } | 149 | } | ... | ... |
-
Please register or login to post a comment