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 parseNode(reader);
34 } else if (reader.name() == QLatin1String("way")) {
35 parseWay(reader);
36 } else if (reader.name() == QLatin1String("relation")) {
37 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
67void XmlParser::parseNode(QXmlStreamReader &reader)
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 addNode(std::move(node));
84}
85
86void XmlParser::parseWay(QXmlStreamReader &reader)
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 addWay(std::move(way));
108}
109
110void XmlParser::parseRelation(QXmlStreamReader &reader)
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 addRelation(std::move(rel));
141}
142
143template <typename T>
144void XmlParser::parseTag(QXmlStreamReader &reader, T &elem)
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)
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)
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:340
TagKey makeTagKey(const char *keyName, StringMemory keyMemOpt=StringMemory::Transient)
Create a tag key for the given tag name.
Definition datatypes.cpp:17
Role makeRole(const char *roleName, StringMemory memOpt=StringMemory::Transient)
Creates a role name key.
Definition datatypes.cpp:22
A member in a relation.
Definition datatypes.h:282
An OSM relation.
Definition datatypes.h:311
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:484
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 Tue Mar 26 2024 11:20:03 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.