KOSMIndoorMap

oscparser.cpp
1/*
2 SPDX-FileCopyrightText: 2024 Volker Krause <vkrause@kde.org>
3 SPDX-License-Identifier: LGPL-2.0-or-later
4*/
5
6#include "oscparser.h"
7#include "datatypes.h"
8
9#include <QDebug>
10#include <QIODevice>
11#include <QXmlStreamReader>
12
13#include <cmath>
14
15using namespace Qt::Literals::StringLiterals;
16using namespace OSM;
17
18OscParser::OscParser(DataSet* dataSet)
19 : XmlParser(dataSet)
20{
21}
22
23void OscParser::readFromIODevice(QIODevice *io)
24{
25 QXmlStreamReader reader(io);
26 while (!reader.atEnd() && !reader.hasError()) {
27 reader.readNext();
28 if (reader.tokenType() != QXmlStreamReader::StartElement) {
29 continue;
30 }
31 if (reader.name() == "create"_L1) {
32 parseCreate(reader);
33 } else if (reader.name() == "modify"_L1) {
34 parseModify(reader);
35 } else if (reader.name() == "delete"_L1){
36 parseDelete(reader);
37 }
38 }
39
40 if (reader.hasError()) {
41 m_error = reader.errorString();
42 }
43}
44
45template <typename T>
46void OscParser::assignNewId(T &elem, std::unordered_map<OSM::Id, OSM::Id> &idMap)
47{
48 const auto newId = m_dataSet->nextInternalId();
49 idMap[elem.id] = newId;
50 elem.id = newId;
51}
52
53OSM::Id OscParser::mapId(OSM::Id id, const std::unordered_map<OSM::Id, OSM::Id> &idMap)
54{
55 auto it = idMap.find(id);
56 return it == idMap.end() ? id : (*it).second;
57}
58
59void OscParser::mapNodeIds(OSM::Way &way) const
60{
61 for (auto &id : way.nodes) {
62 id = mapId(id, m_nodeIdMap);
63 }
64}
65
66void OscParser::mapMemberIds(OSM::Relation &rel) const
67{
68 for (auto &member : rel.members) {
69 switch (member.type()) {
70 case OSM::Type::Null:
71 break;
72 case OSM::Type::Node:
73 member.id = mapId(member.id, m_nodeIdMap);
74 break;
75 case OSM::Type::Way:
76 member.id = mapId(member.id, m_wayIdMap);
77 break;
78 case OSM::Type::Relation:
79 member.id = mapId(member.id, m_relIdMap);
80 break;
81 }
82 }
83}
84
85void OscParser::parseCreate(QXmlStreamReader &reader)
86{
87 while (!reader.atEnd() && !reader.hasError()) {
88 reader.readNext();
89 if (reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == "create"_L1) {
90 return;
91 }
93 continue;
94 }
95 if (reader.name() == "node"_L1) {
96 auto node = parseNode(reader);
97 assignNewId(node, m_nodeIdMap);
98 addNode(std::move(node));
99 } else if (reader.name() == "way"_L1) {
100 auto way = parseWay(reader);
101 assignNewId(way, m_wayIdMap);
102 mapNodeIds(way);
103 addWay(std::move(way));
104 } else if (reader.name() == "releation"_L1) {
105 auto rel = parseRelation(reader);
106 assignNewId(rel, m_relIdMap);
107 mapMemberIds(rel);
108 addRelation(std::move(rel));
109 } else {
110 reader.skipCurrentElement();
111 }
112 }
113}
114
115void OscParser::parseModify(QXmlStreamReader &reader)
116{
117 while (!reader.atEnd() && !reader.hasError()) {
118 reader.readNext();
119 if (reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == "modify"_L1) {
120 return;
121 }
123 continue;
124 }
125 if (reader.name() == "node"_L1) {
126 auto modifiedNode = parseNode(reader);
127 if (const auto it = std::lower_bound(m_dataSet->nodes.begin(), m_dataSet->nodes.end(), modifiedNode.id); it != m_dataSet->nodes.end() && (*it).id == modifiedNode.id) {
128 if (modifiedNode.coordinate.isValid()) {
129 (*it).coordinate = modifiedNode.coordinate;
130 }
131 if (!modifiedNode.tags.empty()) {
132 (*it).tags = std::move(modifiedNode.tags);
133 }
134 } else {
135 qDebug() << "modified node not in data set:" << modifiedNode.url();
136 }
137 } else if (reader.name() == "way"_L1) {
138 auto modifiedWay = parseWay(reader);
139 if (const auto it = std::lower_bound(m_dataSet->ways.begin(), m_dataSet->ways.end(), modifiedWay.id); it != m_dataSet->ways.end() && (*it).id == modifiedWay.id) {
140 if (!modifiedWay.tags.empty()) {
141 (*it).tags = std::move(modifiedWay.tags);
142 }
143 if (!modifiedWay.nodes.empty()) {
144 mapNodeIds(modifiedWay);
145 (*it).nodes = std::move(modifiedWay.nodes);
146 }
147 } else {
148 qDebug() << "modified way not in data set:" << modifiedWay.url();
149 }
150 } else if (reader.name() == "releation"_L1) {
151 auto modifiedRel = parseRelation(reader);
152 if (const auto it = std::lower_bound(m_dataSet->relations.begin(), m_dataSet->relations.end(), modifiedRel.id); it != m_dataSet->relations.end() && (*it).id == modifiedRel.id) {
153 if (!modifiedRel.tags.empty()) {
154 (*it).tags = std::move(modifiedRel.tags);
155 }
156 if (!modifiedRel.members.empty()) {
157 mapMemberIds(modifiedRel);
158 (*it).members = std::move(modifiedRel.members);
159 }
160 } else {
161 qDebug() << "modified relation not in data set:" << modifiedRel.url();
162 }
163 } else {
164 reader.skipCurrentElement();
165 }
166 }
167}
168
169void OscParser::parseDelete(QXmlStreamReader &reader)
170{
171 // we don't actually delete but just drop all tags
172 // this avoids having to deal with broken referential integrity
173 // but nevertheless results in the deleted element having not effect anymore
174 while (!reader.atEnd() && !reader.hasError()) {
175 reader.readNext();
176 if (reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == "modify"_L1) {
177 return;
178 }
180 continue;
181 }
182 if (reader.name() == "node"_L1) {
183 const auto node = parseNode(reader);
184 if (const auto it = std::lower_bound(m_dataSet->nodes.begin(), m_dataSet->nodes.end(), node.id); it != m_dataSet->nodes.end() && (*it).id == node.id) {
185 (*it).tags.clear();
186 } else {
187 qDebug() << "deleted node not in data set:" << node.url();
188 }
189 } else if (reader.name() == "way"_L1) {
190 const auto way = parseWay(reader);
191 if (const auto it = std::lower_bound(m_dataSet->ways.begin(), m_dataSet->ways.end(), way.id); it != m_dataSet->ways.end() && (*it).id == way.id) {
192 (*it).tags.clear();
193 } else {
194 qDebug() << "modified way not in data set:" << way.url();
195 }
196 } else if (reader.name() == "releation"_L1) {
197 const auto rel = parseRelation(reader);
198 if (const auto it = std::lower_bound(m_dataSet->relations.begin(), m_dataSet->relations.end(), rel.id); it != m_dataSet->relations.end() && (*it).id == rel.id) {
199 (*it).tags.clear();
200 } else {
201 qDebug() << "modified relation not in data set:" << rel.url();
202 }
203 } else {
204 reader.skipCurrentElement();
205 }
206 }
207}
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:346
Id nextInternalId() const
Create a unique id for internal use (ie.
An OSM relation.
Definition datatypes.h:316
An OSM way.
Definition datatypes.h:232
Low-level types and functions to work with raw OSM data as efficiently as possible.
int64_t Id
OSM element identifier.
Definition datatypes.h:30
bool atEnd() const const
bool hasError() 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 Mon Nov 18 2024 12:17:55 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.