#include "GoogleTile.h" #include #include #include #include #include #include using std::string; namespace fs = std::filesystem; #define M_PI 3.1415926 googleTile::googleTile() { } googleTile::~googleTile() { } void googleTile::ExportGeoPng(cv::Mat _pan, TileInfo info, std::string dir) { // 创建目录 if (!fs::exists(dir)) { fs::create_directories(dir); } // 保存全景图 string png_path = dir + "/" + info.tileName + ".png"; cv::imwrite(png_path, _pan); // 写入kml string kml_path = dir + "/" + info.tileName + ".kml"; vector taskTiles; taskTiles.push_back(info); WriteKml(taskTiles, kml_path); } void googleTile::ExportTileSet(cv::Mat _pan, vector taskTilesVec, std::string dir,std::string fileString) { int panWidth = _pan.cols; int panHeight = _pan.rows; int zoom = taskTilesVec[0].ind.z; for (size_t i = 0; i < taskTilesVec.size(); i++) { TileInfo tile = taskTilesVec[i]; TileBox tilebox = tile.boxLatLon; RECT32S tileBoxInPan = tile.boxInPan; int x1 = tileBoxInPan.x; int x2 = tileBoxInPan.x + tileBoxInPan.w; int y1 = tileBoxInPan.y; int y2 = tileBoxInPan.y + tileBoxInPan.h; // 有不完整瓦片,不保存 if (x1 < 0 || y1 < 0 || x2 > panWidth - 1 || y2 > panHeight - 1) { continue; } // 确保坐标在图像范围内 x1 = std::max(0, std::min(x1, panWidth - 1)); y1 = std::max(0, std::min(y1, panHeight - 1)); x2 = std::max(0, std::min(x2, panWidth - 1)); y2 = std::max(0, std::min(y2, panHeight - 1)); // 提取瓦片 cv::Rect tileRect(x1, y1, x2 - x1, y2 - y1); cv::Mat tileImg = _pan(tileRect); // 标准web瓦片尺寸 if (tileImg.cols != 256 || tileImg.rows != 256) { cv::resize(tileImg, tileImg, cv::Size(256, 256)); } // 生成文件名 std::string tileDir = dir + "/" + std::to_string(zoom); std::string fileName = tileDir + "/" + std::to_string(tile.ind.x) + "_" + std::to_string(tile.ind.y) + ".png"; // 创建目录 if (!fs::exists(tileDir)) { fs::create_directories(tileDir); } // 保存瓦片为图像文件 cv::imwrite(fileName, tileImg); } // 输出KML WriteKml(taskTilesVec,dir + "/" + fileString + "_z" + std::to_string(zoom) + ".kml"); } void googleTile::ExportOneTile(cv::Mat _pan, TileInfo tile, std::string dir, std::string fileString) { int panWidth = _pan.cols; int panHeight = _pan.rows; int zoom = tile.ind.z; TileBox tilebox = tile.boxLatLon; RECT32S tileBoxInPan = tile.boxInPan; int x1 = tileBoxInPan.x; int x2 = tileBoxInPan.x + tileBoxInPan.w; int y1 = tileBoxInPan.y; int y2 = tileBoxInPan.y + tileBoxInPan.h; // 有不完整瓦片,不保存 if (x1 < 0 || y1 < 0 || x2 > panWidth - 1 || y2 > panHeight - 1) { return; } // 确保坐标在图像范围内 x1 = std::max(0, std::min(x1, panWidth - 1)); y1 = std::max(0, std::min(y1, panHeight - 1)); x2 = std::max(0, std::min(x2, panWidth - 1)); y2 = std::max(0, std::min(y2, panHeight - 1)); // 提取瓦片 cv::Rect tileRect(x1, y1, x2 - x1, y2 - y1); cv::Mat tileImg = _pan(tileRect); // 标准web瓦片尺寸 if (tileImg.cols != 256 || tileImg.rows != 256) { cv::resize(tileImg, tileImg, cv::Size(256, 256)); } // 生成文件名 std::string tileDir = dir + "/" + std::to_string(zoom); std::string fileName = tileDir + "/" + std::to_string(tile.ind.x) + "_" + std::to_string(tile.ind.y) + ".png"; std::string kmlPath = tileDir + "/" + std::to_string(tile.ind.x) + "_" + std::to_string(tile.ind.y) + ".kml"; tile.href = std::to_string(tile.ind.x) + "_" + std::to_string(tile.ind.y) + ".png"; // 创建目录 if (!fs::exists(tileDir)) { fs::create_directories(tileDir); } // 保存瓦片为图像文件 cv::imwrite(fileName, tileImg); vector info; info.push_back(tile); WriteKml(info, kmlPath); } vector googleTile::CalcTileOverlayVec(TileInfo areaInfo,int panWidth,int panHeight) { int zoom = areaInfo.ind.z; TileBox panBox = areaInfo.boxLatLon; // 计算四至的瓦片编号 TileIndex WN = LatLonToTile(panBox.north, panBox.west, zoom); TileIndex WS = LatLonToTile(panBox.south, panBox.west, zoom); TileIndex EN = LatLonToTile(panBox.north, panBox.east, zoom); TileIndex ES = LatLonToTile(panBox.south, panBox.east, zoom); int xStart = WN.x; int xEnd = EN.x; int yStart = EN.y; int yEnd = ES.y; vector taskTiles; for (size_t i = yStart; i < yEnd; i++) { for (size_t j = xStart; j < xEnd; j++) { TileIndex id = { j,i,zoom }; TileBox tilebox = GetTileBox(id); // 瓦片不完整覆盖则不输出 if (tilebox.west < panBox.west|| panBox.north < tilebox.north || tilebox.east > panBox.east || panBox.south > tilebox.south) { continue; } // 计算瓦片在全景图中的像素位置 RECT32S rect = { 0 }; rect.x = (tilebox.west - panBox.west) / (panBox.east - panBox.west) * panWidth; rect.y = (panBox.north - tilebox.north) / (panBox.north - panBox.south) * panHeight; rect.w = (tilebox.east - panBox.west) / (panBox.east - panBox.west) * panWidth - rect.x; rect.h = (panBox.north - tilebox.south) / (panBox.north - panBox.south) * panHeight - rect.y; // 填充 TileInfo info = { 0 }; info.boxLatLon = tilebox; info.ind = id; info.boxInPan = rect; // 补充瓦片信息 info.tileName = std::to_string(j) + "_" + std::to_string(i) + "_" + std::to_string(zoom); info.href = std::to_string(zoom) + "/" + std::to_string(j) + "_" + std::to_string(i) + ".png"; taskTiles.push_back(info); } } return taskTiles; } int googleTile::ZoomLevel(float mp) { int nLev = 0; // 计算单位瓦片的实际覆盖 int meters_cover = TILE_SIZE * 1.0/mp; int earthLen = 40075017; nLev = std::floor(std::log2(earthLen / meters_cover)) + 1; return nLev; } TileIndex googleTile::LatLonToTile(double latitude, double longitude, int zoomLevel) { // 将纬度转换为墨卡托投影 double latRad = latitude * M_PI / 180.0; double sinLat = sin(latRad); // 计算经度的瓦片编号(0-2^zoomLevel) int xTile = static_cast((longitude + 180.0) / 360.0 * pow(2.0, zoomLevel)); // 计算纬度的瓦片编号(0-2^zoomLevel) int yTile = static_cast((1.0 - log((1.0 + sinLat) / (1.0 - sinLat)) / (2.0 * M_PI)) / 2.0 * pow(2.0, zoomLevel)); return TileIndex{ xTile, yTile, zoomLevel }; } // 将瓦片编号转换为经纬度 void TileToLatLon(int x, int y, int z, double& lat, double& lon) { double n = std::pow(2.0, z); lon = x / n * 360.0 - 180.0; double lat_rad = std::atan(std::sinh(M_PI * (1 - 2 * y / n))); lat = lat_rad * 180.0 / M_PI; } TileBox googleTile::GetTileBox(TileIndex ind) { TileBox box = {0}; // 计算瓦片的西北角经纬度 TileToLatLon(ind.x, ind.y, ind.z, box.north, box.west); // 计算瓦片的东南角经纬度 TileToLatLon(ind.x + 1, ind.y + 1, ind.z, box.south, box.east); return box; } void googleTile::WriteKml(vector tiles, string filePath) { // Open the KML file for writing std::ofstream kml_file(filePath); if (!kml_file.is_open()) { std::cerr << "Failed to open KML file for writing: " << filePath << std::endl; return; } // Write the KML header kml_file << "" << std::endl; kml_file << "" << std::endl; kml_file << "" << std::endl; // Loop through each tile and write a GroundOverlay element for (const auto& tile : tiles) { kml_file << "" << std::endl; kml_file << " " << tile.tileName << "" << std::endl; kml_file << " " << std::endl; kml_file << " " << tile.href << "" << std::endl; kml_file << " " << std::endl; kml_file << " " << std::endl; kml_file << " " << std::fixed << std::setprecision(6) << tile.boxLatLon.north << "" << std::endl; kml_file << " " << std::fixed << std::setprecision(6) << tile.boxLatLon.south << "" << std::endl; kml_file << " " << std::fixed << std::setprecision(6) << tile.boxLatLon.east << "" << std::endl; kml_file << " " << std::fixed << std::setprecision(6) << tile.boxLatLon.west << "" << std::endl; kml_file << " " << std::endl; kml_file << "" << std::endl; } // Write the KML footer kml_file << "" << std::endl; kml_file << "" << std::endl; // Close the file kml_file.close(); }