File indexing completed on 2025-03-09 03:37:16
0001 /* 0002 SPDX-FileCopyrightText: 2016 Dennis Nienhüser <nienhueser@kde.org> 0003 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "TirexBackend.h" 0009 0010 #include "GeoDataDocumentWriter.h" 0011 #include "GeoDataPolygon.h" 0012 #include "MarbleModel.h" 0013 #include "NodeReducer.h" 0014 #include "ParsingRunnerManager.h" 0015 #include "TileDirectory.h" 0016 #include "TileId.h" 0017 #include "VectorClipper.h" 0018 #include "WayConcatenator.h" 0019 #ifdef STATIC_BUILD 0020 #include "src/plugins/runner/osm/translators/O5mWriter.h" 0021 #endif 0022 0023 #include <QCommandLineParser> 0024 #include <QCoreApplication> 0025 #include <QDebug> 0026 #include <QFile> 0027 #include <QSaveFile> 0028 #include <QtPlugin> 0029 0030 #ifdef STATIC_BUILD 0031 Q_IMPORT_PLUGIN(OsmPlugin) 0032 Q_IMPORT_PLUGIN(ShpPlugin) 0033 #endif 0034 0035 using namespace Marble; 0036 0037 GeoDataDocument* mergeDocuments(GeoDataDocument* map1, GeoDataDocument* map2) 0038 { 0039 GeoDataDocument* mergedMap = new GeoDataDocument(*map1); 0040 if (!map2) { 0041 return mergedMap; 0042 } 0043 0044 OsmPlacemarkData marbleLand; 0045 marbleLand.addTag("marble_land","landmass"); 0046 for (auto placemark: map2->placemarkList()) { 0047 GeoDataPlacemark* land = new GeoDataPlacemark(*placemark); 0048 if (geodata_cast<GeoDataPolygon>(land->geometry())) { 0049 land->setOsmData(marbleLand); 0050 } 0051 mergedMap->append(land); 0052 } 0053 0054 return mergedMap; 0055 } 0056 0057 int main(int argc, char **argv) 0058 { 0059 setenv("QT_LOGGING_TO_CONSOLE", "0", true); // redirects qDebug to syslog 0060 0061 QCoreApplication app(argc, argv); 0062 0063 // for stand-alone testing only, in normal operation this is entirely controlled via the Tirex command socket 0064 QCommandLineParser parser; 0065 parser.addOptions({ 0066 {{"c", "cache-directory"}, "Directory for temporary data.", "cache"}, 0067 {"source", "source file to load instead of doing an osmx query.", "source"}, 0068 {"x", "x coordinate of the requested tile", "x"}, 0069 {"y", "y coordinate of the requested tile", "y"}, 0070 {"z", "zoom level of the requested tile", "z"}, 0071 }); 0072 parser.process(app); 0073 0074 // work around MARBLE_ADD_WRITER not working for static builds 0075 #ifdef STATIC_BUILD 0076 GeoDataDocumentWriter::registerWriter(new O5mWriter, QStringLiteral("o5m")); 0077 #endif 0078 0079 TirexBackend backend; 0080 0081 MarbleModel model; 0082 ParsingRunnerManager manager(model.pluginManager()); 0083 auto cacheDirectory = backend.configValue(QStringLiteral("cache-directory")).toString(); 0084 if (cacheDirectory.isEmpty()) { 0085 cacheDirectory = parser.value("cache-directory"); 0086 } 0087 0088 const QString sourceFile = parser.value("source"); 0089 0090 QObject::connect(&backend, &TirexBackend::tileRequested, &app, [&](const TirexMetatileRequest &req) { 0091 // assuming the requested meta tile is a square power of two, we break that down into square power-of-two blocks 0092 // for high zoom levels using few (or even just one block is most efficient), for lower zoom levels we need to use 0093 // more blocks to reduce memory use 0094 // to avoid TileDirectory reloading the same block multiple times, we need to do the below processing in the proper order 0095 int loadZ = req.tile.z; 0096 if (backend.metatileColumns() == backend.metatileRows() && backend.metatileRows() == 8) { 0097 loadZ = req.tile.z - 3; 0098 loadZ = std::max(11, loadZ); 0099 loadZ = std::min(req.tile.z, loadZ); 0100 } 0101 const int blockSize = 1 << (req.tile.z - loadZ); 0102 const int blockColumns = backend.metatileColumns() / blockSize; 0103 const int blockRows = backend.metatileRows() / blockSize; 0104 0105 TileDirectory mapTiles(cacheDirectory, sourceFile.isEmpty() ? QStringLiteral("planet.osmx") :sourceFile, manager, req.tile.z, loadZ, sourceFile.isEmpty() ? TileDirectory::OsmxInput : TileDirectory::RawInput); 0106 TileDirectory landTiles(TileDirectory::Landmass, cacheDirectory, manager, req.tile.z); 0107 0108 QSaveFile f(backend.metatileFileName(req)); 0109 if (!f.open(QFile::WriteOnly)) { 0110 backend.tileError(req, f.errorString()); 0111 return; 0112 } 0113 0114 backend.writeMetatileHeader(&f, req.tile); 0115 for (int blockX = 0; blockX < blockColumns; ++blockX) { 0116 for (int blockY = 0; blockY < blockRows; ++blockY) { 0117 for (int tileX = 0; tileX < blockSize; ++tileX) { 0118 for (int tileY = 0; tileY < blockSize; ++tileY) { 0119 const auto x = blockX * blockSize + tileX; 0120 const auto y = blockY * blockSize + tileY; 0121 0122 auto const tileId = TileId (0, req.tile.z, x + req.tile.x, y + req.tile.y); 0123 using GeoDocPtr = QSharedPointer<GeoDataDocument>; 0124 GeoDocPtr tile1 = GeoDocPtr(mapTiles.clip(tileId.zoomLevel(), tileId.x(), tileId.y())); 0125 TagsFilter::removeAnnotationTags(tile1.data()); 0126 if (tileId.zoomLevel() < 17) { 0127 WayConcatenator concatenator(tile1.data()); 0128 NodeReducer nodeReducer(tile1.data(), tileId); 0129 } 0130 GeoDocPtr tile2 = GeoDocPtr(landTiles.clip(tileId.zoomLevel(), tileId.x(), tileId.y())); 0131 GeoDocPtr combined = GeoDocPtr(mergeDocuments(tile1.data(), tile2.data())); 0132 0133 const auto offset = f.pos(); 0134 if (GeoDataDocumentWriter::write(&f, *combined, QStringLiteral("o5m"))) { 0135 backend.writeMetatileEntry(&f, x * backend.metatileColumns() + y, offset, f.pos() - offset); 0136 } else { 0137 qWarning() << "Could not write the tile " << combined->name(); 0138 } 0139 } 0140 } 0141 } 0142 } 0143 0144 f.commit(); 0145 backend.tileDone(req); 0146 }); 0147 0148 if (parser.isSet("x") && parser.isSet("y") && parser.isSet("z")) { 0149 TirexMetatileRequest req; 0150 req.tile.x = parser.value("x").toInt(); 0151 req.tile.y = parser.value("y").toInt(); 0152 req.tile.z = parser.value("z").toInt(); 0153 emit backend.tileRequested(req); 0154 return 0; 0155 } 0156 0157 return app.exec(); 0158 }