File indexing completed on 2023-05-30 09:06:34
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 {"x", "x coordinate of the requested tile", "x"}, 0068 {"y", "y coordinate of the requested tile", "y"}, 0069 {"z", "zoom level of the requested tile", "z"}, 0070 }); 0071 parser.process(app); 0072 0073 // work around MARBLE_ADD_WRITER not working for static builds 0074 #ifdef STATIC_BUILD 0075 GeoDataDocumentWriter::registerWriter(new O5mWriter, QStringLiteral("o5m")); 0076 #endif 0077 0078 TirexBackend backend; 0079 0080 MarbleModel model; 0081 ParsingRunnerManager manager(model.pluginManager()); 0082 auto cacheDirectory = backend.configValue(QStringLiteral("cache-directory")).toString(); 0083 if (cacheDirectory.isEmpty()) { 0084 cacheDirectory = parser.value("cache-directory"); 0085 } 0086 0087 QObject::connect(&backend, &TirexBackend::tileRequested, &app, [&](const TirexMetatileRequest &req) { 0088 // assuming the requested meta tile is a square power of two, we break that down into square power-of-two blocks 0089 // for high zoom levels using few (or even just one block is most efficient), for lower zoom levels we need to use 0090 // more blocks to reduce memory use 0091 // to avoid TileDirectory reloading the same block multiple times, we need to do the below processing in the proper order 0092 int loadZ = req.tile.z; 0093 if (backend.metatileColumns() == backend.metatileRows() && backend.metatileRows() == 8) { 0094 loadZ = req.tile.z - 3; 0095 loadZ = std::max(11, loadZ); 0096 loadZ = std::min(req.tile.z, loadZ); 0097 } 0098 const int blockSize = 1 << (req.tile.z - loadZ); 0099 const int blockColumns = backend.metatileColumns() / blockSize; 0100 const int blockRows = backend.metatileRows() / blockSize; 0101 0102 TileDirectory mapTiles(cacheDirectory, QStringLiteral("planet.osmx"), manager, req.tile.z, loadZ); 0103 TileDirectory landTiles(TileDirectory::Landmass, cacheDirectory, manager, req.tile.z); 0104 0105 QSaveFile f(backend.metatileFileName(req)); 0106 if (!f.open(QFile::WriteOnly)) { 0107 backend.tileError(req, f.errorString()); 0108 return; 0109 } 0110 0111 backend.writeMetatileHeader(&f, req.tile); 0112 for (int blockX = 0; blockX < blockColumns; ++blockX) { 0113 for (int blockY = 0; blockY < blockRows; ++blockY) { 0114 for (int tileX = 0; tileX < blockSize; ++tileX) { 0115 for (int tileY = 0; tileY < blockSize; ++tileY) { 0116 const auto x = blockX * blockSize + tileX; 0117 const auto y = blockY * blockSize + tileY; 0118 0119 auto const tileId = TileId (0, req.tile.z, x + req.tile.x, y + req.tile.y); 0120 using GeoDocPtr = QSharedPointer<GeoDataDocument>; 0121 GeoDocPtr tile1 = GeoDocPtr(mapTiles.clip(tileId.zoomLevel(), tileId.x(), tileId.y())); 0122 TagsFilter::removeAnnotationTags(tile1.data()); 0123 if (tileId.zoomLevel() < 17) { 0124 WayConcatenator concatenator(tile1.data()); 0125 } 0126 NodeReducer nodeReducer(tile1.data(), tileId); 0127 GeoDocPtr tile2 = GeoDocPtr(landTiles.clip(tileId.zoomLevel(), tileId.x(), tileId.y())); 0128 GeoDocPtr combined = GeoDocPtr(mergeDocuments(tile1.data(), tile2.data())); 0129 0130 const auto offset = f.pos(); 0131 if (GeoDataDocumentWriter::write(&f, *combined, QStringLiteral("o5m"))) { 0132 backend.writeMetatileEntry(&f, x * backend.metatileColumns() + y, offset, f.pos() - offset); 0133 } else { 0134 qWarning() << "Could not write the tile " << combined->name(); 0135 } 0136 } 0137 } 0138 } 0139 } 0140 0141 f.commit(); 0142 backend.tileDone(req); 0143 }); 0144 0145 if (parser.isSet("x") && parser.isSet("y") && parser.isSet("z")) { 0146 TirexMetatileRequest req; 0147 req.tile.x = parser.value("x").toInt(); 0148 req.tile.y = parser.value("y").toInt(); 0149 req.tile.z = parser.value("z").toInt(); 0150 emit backend.tileRequested(req); 0151 return 0; 0152 } 0153 0154 return app.exec(); 0155 }