KOSMIndoorMap

xmlparser.cpp
1/*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "xmlparser.h"
8#include "datatypes.h"
9
10#include <QDebug>
11#include <QIODevice>
12#include <QXmlStreamReader>
13
14#include <cmath>
15
16using namespace OSM;
17
18XmlParser::XmlParser(DataSet* dataSet)
19 : AbstractReader(dataSet)
20{
21}
22
23void XmlParser::readFromIODevice(QIODevice *io)
24{
25 QXmlStreamReader reader(io);
26 while (!reader.atEnd() && !reader.hasError()) {
27 const auto token = reader.readNext();
28 if (token != QXmlStreamReader::StartElement) {
29 continue;
30 }
31
32 if (reader.name() == QLatin1String("node")) {
33 addNode(parseNode(reader));
34 } else if (reader.name() == QLatin1String("way")) {
35 addWay(parseWay(reader));
36 } else if (reader.name() == QLatin1String("relation")) {
37 addRelation(parseRelation(reader));
38 } else if (reader.name() == QLatin1String("remark")) {
39 m_error = reader.readElementText();
40 return;
41 }
42 }
43
44 if (reader.hasError()) {
45 m_error = reader.errorString();
46 }
47}
48
49// parse double coordinate value without actually doing floating point computations
50// this avoids any loss in precision we can other get heret
51uint32_t parseCoordinateValue(QStringView s, int offset)
52{
53 const auto idx = s.indexOf(QLatin1Char('.'));
54 if (idx < 0) {
55 return s.toUInt() * 10'000'000;
56 }
57 uint32_t result = (uint32_t)(s.left(idx).toInt() + offset) * 10'000'000;
58 const auto decimals = s.mid(idx + 1);
59 if (decimals.size() >= 7) {
60 result += decimals.left(7).toUInt();
61 } else {
62 result += decimals.toUInt() * std::pow(10, 7 - decimals.size());
63 }
64 return result;
65}
66
67OSM::Node XmlParser::parseNode(QXmlStreamReader &reader) const
68{
69 Node node;
70 node.id = reader.attributes().value(QLatin1String("id")).toLongLong();
71 node.coordinate = Coordinate(parseCoordinateValue(reader.attributes().value(QLatin1String("lat")), 90), parseCoordinateValue(reader.attributes().value(QLatin1String("lon")), 180));
72
73 while (!reader.atEnd() && reader.readNext() != QXmlStreamReader::EndElement) {
75 continue;
76 }
77 if (reader.name() == QLatin1String("tag")) {
78 parseTag(reader, node);
79 }
80 reader.skipCurrentElement();
81 }
82
83 return node;
84}
85
86OSM::Way XmlParser::parseWay(QXmlStreamReader &reader) const
87{
88 Way way;
89 way.id = reader.attributes().value(QLatin1String("id")).toLongLong();
90
91 while (!reader.atEnd() && reader.readNext() != QXmlStreamReader::EndElement) {
93 continue;
94 }
95 if (reader.name() == QLatin1String("nd")) {
96 OSM::Id node;
97 node = reader.attributes().value(QLatin1String("ref")).toLongLong();
98 way.nodes.push_back(node);
99 } else if (reader.name() == QLatin1String("tag")) {
100 parseTagOrBounds(reader, way);
101 } else if (reader.name() == QLatin1String("bounds")) {
102 parseBounds(reader, way);
103 }
104 reader.skipCurrentElement();
105 }
106
107 return way;
108}
109
110OSM::Relation XmlParser::parseRelation(QXmlStreamReader &reader) const
111{
112 Relation rel;
113 rel.id = reader.attributes().value(QLatin1String("id")).toLongLong();
114
115 while (!reader.atEnd() && reader.readNext() != QXmlStreamReader::EndElement) {
117 continue;
118 }
119 if (reader.name() == QLatin1String("tag")) {
120 parseTagOrBounds(reader, rel);
121 } else if (reader.name() == QLatin1String("bounds")) { // Overpass style bounding box
122 parseBounds(reader, rel);
123 } else if (reader.name() == QLatin1String("member")) {
124 Member member;
125 member.id = reader.attributes().value(QLatin1String("ref")).toLongLong();
126 const auto type = reader.attributes().value(QLatin1String("type"));
127 if (type == QLatin1String("node")) {
128 member.setType(Type::Node);
129 } else if (type == QLatin1String("way")) {
130 member.setType(Type::Way);
131 } else {
132 member.setType(Type::Relation);
133 }
134 member.setRole(m_dataSet->makeRole(reader.attributes().value(QLatin1String("role")).toUtf8().constData(), OSM::StringMemory::Transient));
135 rel.members.push_back(std::move(member));
136 }
137 reader.skipCurrentElement();
138 }
139
140 return rel;
141}
142
143template <typename T>
144void XmlParser::parseTag(QXmlStreamReader &reader, T &elem) const
145{
146 const auto key = m_dataSet->makeTagKey(reader.attributes().value(QLatin1String("k")).toString().toUtf8().constData(), OSM::StringMemory::Transient);
147 OSM::setTagValue(elem, key, reader.attributes().value(QLatin1String("v")).toUtf8());
148}
149
150template <typename T>
151void XmlParser::parseTagOrBounds(QXmlStreamReader &reader, T &elem) const
152{
153 if (reader.attributes().value(QLatin1String("k")) == QLatin1String("bBox")) { // osmconvert style bounding box
154 const auto v = reader.attributes().value(QLatin1String("v")).split(QLatin1Char(','));
155 if (v.size() == 4) {
156 elem.bbox.min = Coordinate(v[1].toDouble(), v[0].toDouble());
157 elem.bbox.max = Coordinate(v[3].toDouble(), v[2].toDouble());
158 }
159 } else {
160 parseTag(reader, elem);
161 }
162}
163
164template<typename T>
165void XmlParser::parseBounds(QXmlStreamReader &reader, T &elem) const
166{
167 // overpass style bounding box
168 elem.bbox.min = Coordinate(reader.attributes().value(QLatin1String("minlat")).toDouble(), reader.attributes().value(QLatin1String("minlon")).toDouble());
169 elem.bbox.max = Coordinate(reader.attributes().value(QLatin1String("maxlat")).toDouble(), reader.attributes().value(QLatin1String("maxlon")).toDouble());
170}
Abstract base class for OSM file format readers.
void addNode(OSM::Node &&node)
Add read elements to the merge buffer if set, or the dataset otherwise.
A set of nodes, ways and relations.
Definition datatypes.h:343
TagKey makeTagKey(const char *keyName, StringMemory keyMemOpt=StringMemory::Transient)
Create a tag key for the given tag name.
Definition datatypes.cpp:28
Role makeRole(const char *roleName, StringMemory memOpt=StringMemory::Transient)
Creates a role name key.
Definition datatypes.cpp:33
A member in a relation.
Definition datatypes.h:285
An OSM node.
Definition datatypes.h:204
An OSM relation.
Definition datatypes.h:314
An OSM way.
Definition datatypes.h:231
char * toString(const EngineQuery &query)
Low-level types and functions to work with raw OSM data as efficiently as possible.
void setTagValue(Elem &elem, TagKey key, QByteArray &&value)
Inserts a new tag, or updates an existing one.
Definition datatypes.h:491
int64_t Id
OSM element identifier.
Definition datatypes.h:30
QStringView left(qsizetype length) const const
QStringView mid(qsizetype start, qsizetype length) const const
qsizetype indexOf(QChar c, qsizetype from, Qt::CaseSensitivity cs) const const
QList< QStringView > split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
qlonglong toLongLong(bool *ok, int base) const const
uint toUInt(bool *ok, int base) const const
QStringView value(QAnyStringView namespaceUri, QAnyStringView name) const const
bool atEnd() const const
QXmlStreamAttributes attributes() const const
QStringView name() const const
TokenType readNext()
void skipCurrentElement()
TokenType tokenType() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:57:47 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.