File indexing completed on 2024-07-14 14:25:33

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 }