File indexing completed on 2024-11-24 04:46:23

0001 /*
0002     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "xmlparser.h"
0008 #include "datatypes.h"
0009 
0010 #include <QIODevice>
0011 #include <QXmlStreamReader>
0012 
0013 using namespace OSM;
0014 
0015 XmlParser::XmlParser(DataSet* dataSet)
0016     : m_dataSet(dataSet)
0017 {
0018     assert(dataSet);
0019 }
0020 
0021 void XmlParser::parse(QIODevice *io)
0022 {
0023     QXmlStreamReader reader(io);
0024     while (!reader.atEnd()) {
0025         const auto token = reader.readNext();
0026         if (token != QXmlStreamReader::StartElement) {
0027             continue;
0028         }
0029 
0030         if (reader.name() == QLatin1StringView("node")) {
0031           parseNode(reader);
0032         } else if (reader.name() == QLatin1StringView("way")) {
0033           parseWay(reader);
0034         } else if (reader.name() == QLatin1StringView("relation")) {
0035           parseRelation(reader);
0036         } else if (reader.name() == QLatin1StringView("remark")) {
0037           m_error = reader.readElementText();
0038           return;
0039         }
0040     }
0041 }
0042 
0043 void XmlParser::parseNode(QXmlStreamReader &reader)
0044 {
0045     Node node;
0046     node.id = reader.attributes().value(QLatin1StringView("id")).toLongLong();
0047     node.coordinate = Coordinate(
0048         reader.attributes().value(QLatin1StringView("lat")).toDouble(),
0049         reader.attributes().value(QLatin1String("lon")).toDouble());
0050 
0051     while (!reader.atEnd() && reader.readNext() != QXmlStreamReader::EndElement) {
0052         if (reader.tokenType() != QXmlStreamReader::StartElement) {
0053             continue;
0054         }
0055         if (reader.name() == QLatin1StringView("tag")) {
0056           parseTag(reader, node);
0057         }
0058         reader.skipCurrentElement();
0059     }
0060 
0061     m_dataSet->addNode(std::move(node));
0062 }
0063 
0064 void XmlParser::parseWay(QXmlStreamReader &reader)
0065 {
0066     Way way;
0067     way.id = reader.attributes().value(QLatin1StringView("id")).toLongLong();
0068 
0069     while (!reader.atEnd() && reader.readNext() != QXmlStreamReader::EndElement) {
0070         if (reader.tokenType() != QXmlStreamReader::StartElement) {
0071             continue;
0072         }
0073         if (reader.name() == QLatin1StringView("nd")) {
0074           OSM::Id node;
0075           node =
0076               reader.attributes().value(QLatin1StringView("ref")).toLongLong();
0077           way.nodes.push_back(node);
0078         } else if (reader.name() == QLatin1StringView("tag")) {
0079           parseTagOrBounds(reader, way);
0080         } else if (reader.name() == QLatin1StringView("bounds")) {
0081           parseBounds(reader, way);
0082         }
0083         reader.skipCurrentElement();
0084     }
0085 
0086     m_dataSet->addWay(std::move(way));
0087 }
0088 
0089 void XmlParser::parseRelation(QXmlStreamReader &reader)
0090 {
0091     Relation rel;
0092     rel.id = reader.attributes().value(QLatin1StringView("id")).toLongLong();
0093 
0094     while (!reader.atEnd() && reader.readNext() != QXmlStreamReader::EndElement) {
0095         if (reader.tokenType() != QXmlStreamReader::StartElement) {
0096             continue;
0097         }
0098         if (reader.name() == QLatin1StringView("tag")) {
0099           parseTagOrBounds(reader, rel);
0100         } else if (reader.name() ==
0101                    QLatin1StringView("bounds")) { // Overpass style bounding box
0102           parseBounds(reader, rel);
0103         } else if (reader.name() == QLatin1StringView("member")) {
0104           Member member;
0105           member.id =
0106               reader.attributes().value(QLatin1StringView("ref")).toLongLong();
0107           const auto type =
0108               reader.attributes().value(QLatin1StringView("type"));
0109           if (type == QLatin1StringView("node")) {
0110             member.type = Type::Node;
0111           } else if (type == QLatin1StringView("way")) {
0112             member.type = Type::Way;
0113           } else {
0114             member.type = Type::Relation;
0115           }
0116           member.role =
0117               reader.attributes()
0118                   .value(QLatin1StringView("role"))
0119                   .toString(); // TODO shared value pool for these values
0120           rel.members.push_back(std::move(member));
0121         }
0122         reader.skipCurrentElement();
0123     }
0124 
0125     m_dataSet->addRelation(std::move(rel));
0126 }
0127 
0128 template <typename T>
0129 void XmlParser::parseTag(QXmlStreamReader &reader, T &elem)
0130 {
0131   OSM::setTagValue(elem,
0132                    reader.attributes().value(QLatin1StringView("k")).toString(),
0133                    reader.attributes().value(QLatin1String("v")).toString());
0134 }
0135 
0136 template <typename T>
0137 void XmlParser::parseTagOrBounds(QXmlStreamReader &reader, T &elem)
0138 {
0139   if (reader.attributes().value(QLatin1StringView("k")) ==
0140       QLatin1String("bBox")) { // osmconvert style bounding box
0141     const auto v = reader.attributes()
0142                        .value(QLatin1StringView("v"))
0143                        .split(QLatin1Char(','));
0144     if (v.size() == 4) {
0145       elem.bbox.min = Coordinate(v[1].toDouble(), v[0].toDouble());
0146       elem.bbox.max = Coordinate(v[3].toDouble(), v[2].toDouble());
0147     }
0148   } else {
0149     parseTag(reader, elem);
0150   }
0151 }
0152 
0153 template<typename T>
0154 void XmlParser::parseBounds(QXmlStreamReader &reader, T &elem)
0155 {
0156     // overpass style bounding box
0157     elem.bbox.min = Coordinate(
0158         reader.attributes().value(QLatin1StringView("minlat")).toDouble(),
0159         reader.attributes().value(QLatin1String("minlon")).toDouble());
0160     elem.bbox.max = Coordinate(
0161         reader.attributes().value(QLatin1StringView("maxlat")).toDouble(),
0162         reader.attributes().value(QLatin1String("maxlon")).toDouble());
0163 }
0164 
0165 QString XmlParser::error() const
0166 {
0167     return m_error;
0168 }