File indexing completed on 2024-05-12 04:42:19
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include <KOSMIndoorMap/MapData> 0008 #include <KOSMIndoorMap/MapLoader> 0009 #include <loader/tilecache_p.h> 0010 0011 #include <osm/datatypes.h> 0012 #include <osm/io.h> 0013 0014 #include <QCommandLineParser> 0015 #include <QCoreApplication> 0016 #include <QDebug> 0017 #include <QFile> 0018 #include <QtPlugin> 0019 0020 #if HAVE_OSM_PBF_SUPPORT 0021 Q_IMPORT_PLUGIN(OSM_PbfIOPlugin) 0022 #endif 0023 Q_IMPORT_PLUGIN(OSM_XmlIOPlugin) 0024 0025 using namespace KOSMIndoorMap; 0026 0027 static void filterByBbox(OSM::DataSet &dataSet, OSM::BoundingBox bbox) 0028 { 0029 dataSet.relations.erase(std::remove_if(dataSet.relations.begin(), dataSet.relations.end(), [bbox](const auto &rel) { 0030 return !OSM::intersects(rel.bbox, bbox); 0031 }), dataSet.relations.end()); 0032 dataSet.ways.erase(std::remove_if(dataSet.ways.begin(), dataSet.ways.end(), [bbox](const auto &way) { 0033 return !OSM::intersects(way.bbox, bbox); 0034 }), dataSet.ways.end()); 0035 dataSet.nodes.erase(std::remove_if(dataSet.nodes.begin(), dataSet.nodes.end(), [bbox](const auto &nd) { 0036 return !OSM::contains(bbox, nd.coordinate); 0037 }), dataSet.nodes.end()); 0038 } 0039 0040 template <typename Elem> 0041 static bool containsElement(const std::vector<Elem> &elems, OSM::Id id) 0042 { 0043 const auto it = std::lower_bound(elems.begin(), elems.end(), id, [](const Elem &lhs, OSM::Id rhs) { return lhs.id < rhs; }); 0044 return it != elems.end() && (*it).id == id; 0045 } 0046 0047 static void purgeDanglingReferences(OSM::DataSet &dataSet) 0048 { 0049 for (auto &rel : dataSet.relations) { 0050 rel.members.erase(std::remove_if(rel.members.begin(), rel.members.end(), [&dataSet](const auto &mem) { 0051 switch (mem.type()) { 0052 case OSM::Type::Null: 0053 Q_UNREACHABLE(); 0054 case OSM::Type::Node: 0055 return !containsElement(dataSet.nodes, mem.id); 0056 case OSM::Type::Way: 0057 return !containsElement(dataSet.ways, mem.id); 0058 case OSM::Type::Relation: 0059 return !containsElement(dataSet.relations, mem.id); 0060 } 0061 return false; 0062 }), rel.members.end()); 0063 } 0064 } 0065 0066 int main(int argc, char **argv) 0067 { 0068 QCoreApplication app(argc, argv); 0069 QCommandLineParser parser; 0070 parser.addHelpOption(); 0071 QCommandLineOption bboxOpt({QStringLiteral("b"), QStringLiteral("bbox")}, QStringLiteral("bounding box to download"), QStringLiteral("minlat,minlon,maxlat,maxlon")); 0072 parser.addOption(bboxOpt); 0073 QCommandLineOption clipOpt({QStringLiteral("c"), QStringLiteral("clip")}, QStringLiteral("clip to bounding box")); 0074 parser.addOption(clipOpt); 0075 QCommandLineOption outOpt({QStringLiteral("o"), QStringLiteral("out")}, QStringLiteral("output file"), QStringLiteral("file")); 0076 parser.addOption(outOpt); 0077 QCommandLineOption pointOpt({QStringLiteral("p"), QStringLiteral("point")}, QStringLiteral("download area around point"), QStringLiteral("lat,lon")); 0078 parser.addOption(pointOpt); 0079 QCommandLineOption tileOpt({QStringLiteral("t"), QStringLiteral("tile")}, QStringLiteral("download tile"), QStringLiteral("z/x/y")); 0080 parser.addOption(tileOpt); 0081 parser.process(app); 0082 0083 if ((!parser.isSet(bboxOpt) && !parser.isSet(pointOpt) && !parser.isSet(tileOpt)) || !parser.isSet(outOpt)) { 0084 parser.showHelp(1); 0085 return 1; 0086 } 0087 0088 OSM::BoundingBox bbox; 0089 MapLoader loader; 0090 if (parser.isSet(bboxOpt)) { 0091 const auto coords = QStringView(parser.value(bboxOpt)).split(QLatin1Char(',')); 0092 if (coords.size() == 4) { 0093 bbox.min = OSM::Coordinate(coords[0].toDouble(), coords[1].toDouble()); 0094 bbox.max = OSM::Coordinate(coords[2].toDouble(), coords[3].toDouble()); 0095 } 0096 if (!bbox.isValid()) { 0097 qCritical() << "Invalid bounding box!"; 0098 return 1; 0099 } 0100 } 0101 0102 if (parser.isSet(pointOpt)) { 0103 const auto coords = QStringView(parser.value(pointOpt)).split(QLatin1Char(',')); 0104 if (coords.size() != 2) { 0105 qCritical() << "Invalid coordinate!"; 0106 return 1; 0107 } 0108 OSM::Coordinate coord{coords[0].toDouble(), coords[1].toDouble()}; 0109 loader.loadForCoordinate(coord.latF(), coord.lonF()); 0110 } else if (parser.isSet(tileOpt)) { 0111 const auto coords = QStringView(parser.value(tileOpt)).split(QLatin1Char('/')); 0112 if (coords.size() != 3) { 0113 qCritical() << "Invalid tile!"; 0114 return 1; 0115 } 0116 Tile t(coords[1].toUInt(), coords[2].toUInt(), coords[0].toUInt()); 0117 loader.loadForTile(t); 0118 } else if (parser.isSet(bboxOpt)) { 0119 loader.loadForBoundingBox(bbox); 0120 } 0121 0122 QObject::connect(&loader, &MapLoader::done, &app, &QCoreApplication::quit); 0123 QCoreApplication::exec(); 0124 auto data = loader.takeData(); 0125 0126 if (parser.isSet(clipOpt) && parser.isSet(bboxOpt)) { 0127 filterByBbox(data.dataSet(), bbox); 0128 purgeDanglingReferences(data.dataSet()); 0129 } 0130 0131 QFile f(parser.value(outOpt)); 0132 if (!f.open(QFile::WriteOnly)) { 0133 qCritical() << f.errorString(); 0134 return 1; 0135 } 0136 auto writer = OSM::IO::writerForFileName(f.fileName()); 0137 if (!writer) { 0138 qCritical() << "no file writer for requested format:" << f.fileName(); 0139 return 1; 0140 } 0141 writer->write(data.dataSet(), &f); 0142 return 0; 0143 }