File indexing completed on 2025-03-09 03:37:16
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2016 David Kolozsvari <freedawson@gmail.com> 0004 // SPDX-FileCopyrightText: 2016 Dennis Nienhüser <nienhueser@kde.org> 0005 // 0006 0007 #include "GeoDataDocumentWriter.h" 0008 #include "MarbleModel.h" 0009 #include "ParsingRunnerManager.h" 0010 #include "GeoDataGeometry.h" 0011 #include "GeoDataPlacemark.h" 0012 #include "GeoDataPolygon.h" 0013 #include "GeoDataLatLonAltBox.h" 0014 #include "TileId.h" 0015 #include "MarbleDirs.h" 0016 #ifdef STATIC_BUILD 0017 #include "src/plugins/runner/osm/translators/O5mWriter.h" 0018 #endif 0019 0020 #include <QApplication> 0021 #include <QCommandLineParser> 0022 #include <QDebug> 0023 #include <QFileInfo> 0024 #include <QDir> 0025 #include <QString> 0026 #include <QUrl> 0027 0028 #include "VectorClipper.h" 0029 #include "NodeReducer.h" 0030 #include "TileIterator.h" 0031 #include "TileDirectory.h" 0032 #include "SpellChecker.h" 0033 0034 #ifdef STATIC_BUILD 0035 #include <QtPlugin> 0036 Q_IMPORT_PLUGIN(OsmPlugin) 0037 Q_IMPORT_PLUGIN(ShpPlugin) 0038 #endif 0039 0040 #include <iostream> 0041 0042 using namespace Marble; 0043 0044 static QString tileFileName(const QCommandLineParser &parser, int x, int y, int zoomLevel) 0045 { 0046 QString const extension = parser.value("extension"); 0047 QString const output = parser.isSet("development") ? QString("%1/maps/earth/vectorosm-dev").arg(MarbleDirs::localPath()) : parser.value("output"); 0048 QString const outputDir = QString("%1/%2/%3").arg(output).arg(zoomLevel).arg(x); 0049 QString const outputFile = QString("%1/%2.%3").arg(outputDir).arg(y).arg(extension); 0050 return outputFile; 0051 } 0052 0053 bool writeTile(GeoDataDocument* tile, const QString &outputFile) 0054 { 0055 QDir().mkpath(QFileInfo(outputFile).path()); 0056 if (!GeoDataDocumentWriter::write(outputFile, *tile)) { 0057 qWarning() << "Could not write the file " << outputFile; 0058 return false; 0059 } 0060 return true; 0061 } 0062 0063 int main(int argc, char *argv[]) 0064 { 0065 QCoreApplication app(argc, argv); 0066 0067 QCoreApplication::setApplicationName("marble-vectorosm-tilecreator"); 0068 QCoreApplication::setApplicationVersion("0.1"); 0069 0070 QCommandLineParser parser; 0071 parser.setApplicationDescription("A tool for Marble, which is used to reduce the details of osm maps."); 0072 parser.addHelpOption(); 0073 parser.addVersionOption(); 0074 parser.addPositionalArgument("input", "The input .osm or .shp file."); 0075 0076 parser.addOptions({ 0077 {{"t", "osmconvert"}, "Tile data using osmconvert."}, 0078 {"conflict-resolution", "How to deal with existing tiles: overwrite, skip or merge", "mode", "overwrite"}, 0079 {{"c", "cache-directory"}, "Directory for temporary data.", "cache", "cache"}, 0080 {{"s", "spellcheck"}, "Use this geonames.org cities file for spell-checking city names", "spellcheck"}, 0081 {"verbose", "Increase amount of shell output information"}, 0082 {{"d", "development"}, "Use local development vector osm map theme as output storage"}, 0083 {{"z", "zoom-level"}, "Zoom level according to which OSM information has to be processed.", "levels", "11,13,15,17"}, 0084 {{"o", "output"}, "Output file or directory", "output", QString("%1/maps/earth/vectorosm").arg(MarbleDirs::localPath())}, 0085 {{"e", "extension"}, "Output file type: o5m (default), osm or kml", "file extension", "o5m"} 0086 }); 0087 0088 // Process the actual command line arguments given by the user 0089 parser.process(app); 0090 0091 const QStringList args = parser.positionalArguments(); 0092 if (args.isEmpty()) { 0093 parser.showHelp(); 0094 return 0; 0095 } 0096 // input is args.at(0), output is args.at(1) 0097 0098 QString const extension = parser.value("extension"); 0099 QString inputFileName = args.at(0); 0100 auto const levels = parser.value("zoom-level").split(','); 0101 QVector<unsigned int> zoomLevels; 0102 int maxZoomLevel = 0; 0103 for(auto const &level: levels) { 0104 int const zoomLevel = level.toInt(); 0105 maxZoomLevel = qMax(zoomLevel, maxZoomLevel); 0106 zoomLevels << zoomLevel; 0107 } 0108 0109 if (zoomLevels.isEmpty()) { 0110 parser.showHelp(1); 0111 return 1; 0112 } 0113 0114 // work around MARBLE_ADD_WRITER not working for static builds 0115 #ifdef STATIC_BUILD 0116 GeoDataDocumentWriter::registerWriter(new O5mWriter, QStringLiteral("o5m")); 0117 #endif 0118 0119 bool const overwriteTiles = parser.value("conflict-resolution") == "overwrite"; 0120 0121 MarbleModel model; 0122 ParsingRunnerManager manager(model.pluginManager()); 0123 QString const cacheDirectory = parser.value("cache-directory"); 0124 QDir().mkpath(cacheDirectory); 0125 if (!QFileInfo(cacheDirectory).isWritable()) { 0126 qWarning() << "Cannot write to cache directory" << cacheDirectory; 0127 parser.showHelp(1); 0128 } 0129 0130 if (*zoomLevels.cbegin() <= 9) { 0131 auto map = TileDirectory::open(inputFileName, manager); 0132 VectorClipper processor(map.data(), maxZoomLevel); 0133 GeoDataLatLonBox world(85.0, -85.0, 180.0, -180.0, GeoDataCoordinates::Degree); 0134 if (parser.isSet("spellcheck")) { 0135 SpellChecker spellChecker(parser.value("spellcheck")); 0136 spellChecker.setVerbose(parser.isSet("verbose")); 0137 spellChecker.correctPlaceLabels(map.data()->placemarkList()); 0138 } 0139 for(auto zoomLevel: zoomLevels) { 0140 TileIterator iter(world, zoomLevel); 0141 qint64 count = 0; 0142 qint64 const total = iter.total(); 0143 for(auto const &tileId: iter) { 0144 ++count; 0145 QString const filename = tileFileName(parser, tileId.x(), tileId.y(), zoomLevel); 0146 if (!overwriteTiles && QFileInfo(filename).exists()) { 0147 continue; 0148 } 0149 GeoDataDocument* tile = processor.clipTo(zoomLevel, tileId.x(), tileId.y()); 0150 if (!tile->isEmpty()) { 0151 NodeReducer nodeReducer(tile, TileId(0, zoomLevel, tileId.x(), tileId.y())); 0152 if (!writeTile(tile, filename)) { 0153 return 4; 0154 } 0155 TileDirectory::printProgress(count / double(total)); 0156 std::cout << " Tile " << count << "/" << total << " (" << tile->name().toStdString() << ") done."; 0157 double const reduction = nodeReducer.removedNodes() / qMax(1.0, double(nodeReducer.remainingNodes() + nodeReducer.removedNodes())); 0158 std::cout << " Node reduction: " << qRound(reduction * 100.0) << "%"; 0159 } else { 0160 TileDirectory::printProgress(count / double(total)); 0161 std::cout << " Skipping empty tile " << count << "/" << total << " (" << tile->name().toStdString() << ")."; 0162 } 0163 std::cout << std::string(20, ' ') << '\r'; 0164 std::cout.flush(); 0165 delete tile; 0166 } 0167 } 0168 } else { 0169 qWarning() << "high zoom level tiles cannot be created with this tool!"; 0170 return 1; 0171 } 0172 0173 return 0; 0174 }