KOSMIndoorMap

o5mparser.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 "o5mparser.h"
8#include "o5m.h"
9#include "datatypes.h"
10#include "datasetmergebuffer.h"
11
12#include <QDebug>
13
14#include <cstdlib>
15#include <cstring>
16
17using namespace OSM;
18
19O5mParser::O5mParser(DataSet *dataSet)
20 : AbstractReader(dataSet)
21{
22 m_stringLookupTable.resize(O5M_STRING_TABLE_SIZE);
23}
24
25void O5mParser::readFromData(const uint8_t* data, std::size_t len)
26{
27 std::fill(m_stringLookupTable.begin(), m_stringLookupTable.end(), nullptr);
28 resetDeltaCodingState();
29
30 const auto endIt = data + len;
31 for (auto it = data; it < endIt - 1;) {
32 const auto blockType = (*it);
33 if (blockType == O5M_BLOCK_RESET) {
34 resetDeltaCodingState();
35 ++it;
36 continue;
37 }
38
39 auto blockSize = readUnsigned(++it, endIt);
40 if (blockSize >= (uint64_t)(endIt - it)) {
41 qWarning() << "premature end of file, or blocksize too large" << (endIt - it) << blockType << blockSize;
42 break;
43 }
44 switch (blockType) {
45 case O5M_BLOCK_HEADER:
46 if (blockSize != 4 || std::strncmp(reinterpret_cast<const char*>(it), O5M_HEADER, 4) != 0) {
47 qWarning() << "Invalid file header";
48 return;
49 }
50 break;
51 case O5M_BLOCK_BOUNDING_BOX:
52 case O5M_BLOCK_TIMESTAMP:
53 // not of interest at the moment
54 break;
55 case O5M_BLOCK_NODE:
56 readNode(it, it + blockSize);
57 break;
58 case O5M_BLOCK_WAY:
59 readWay(it, it + blockSize);
60 break;
61 case O5M_BLOCK_RELATION:
62 readRelation(it, it + blockSize);
63 break;
64 default:
65 qDebug() << "unhandled o5m block type:" << (it - data) << blockType << blockSize;
66 }
67
68 it += blockSize;
69 }
70}
71
72uint64_t O5mParser::readUnsigned(const uint8_t *&it, const uint8_t *endIt) const
73{
74 uint64_t result = 0;
75 int i = 0;
76 for (; it < endIt && ((*it) & O5M_NUMBER_CONTINUATION); ++it, ++i) {
77 result |= ((*it) & O5M_NUMBER_MASK) << (i * 7);
78 }
79 result |= ((uint64_t)(*it++) & O5M_NUMBER_MASK) << (i * 7);
80 return result;
81}
82
83int64_t O5mParser::readSigned(const uint8_t *&it, const uint8_t *endIt) const
84{
85 const uint64_t u = readUnsigned(it, endIt);
86 return (u & O5M_NUMBER_SIGNED_BIT) ? (-(u >> 1) -1) : (u >> 1);
87}
88
89template <typename T>
90T O5mParser::readDelta(const uint8_t *&it, const uint8_t *endIt, T &deltaState)
91{
92 deltaState += (T)readSigned(it, endIt);
93 return deltaState;
94}
95
96const char* O5mParser::readString(const uint8_t *&it, const uint8_t *endIt)
97{
98 auto ref = readUnsigned(it, endIt);
99 if (ref) {
100 return m_stringLookupTable[(m_stringLookupPosition + O5M_STRING_TABLE_SIZE - ref) % O5M_STRING_TABLE_SIZE];
101 } else {
102 const auto s = reinterpret_cast<const char*>(it);
103 const auto len = std::strlen(s);
104 if (len <= O5M_STRING_TABLE_MAXLEN) {
105 m_stringLookupTable[m_stringLookupPosition] = s;
106 m_stringLookupPosition = (m_stringLookupPosition + 1) % O5M_STRING_TABLE_SIZE;
107 }
108 it += len + 1;
109 return s;
110 }
111}
112
113std::pair<const char*, const char*> O5mParser::readStringPair(const uint8_t *&it, const uint8_t *endIt)
114{
115 auto ref = readUnsigned(it, endIt);
116 if (ref) {
117 const auto s = m_stringLookupTable[(m_stringLookupPosition + O5M_STRING_TABLE_SIZE - ref) % O5M_STRING_TABLE_SIZE];
118 if (!s) {
119 return {};
120 }
121 const auto len1 = std::strlen(s);
122 return std::make_pair(s, s + len1 + 1);
123 } else {
124 const auto s = reinterpret_cast<const char*>(it);
125 const auto len1 = std::strlen(s);
126 const auto len2 = std::strlen(s + len1 + 1);
127
128 if (len1 + len2 <= O5M_STRING_TABLE_MAXLEN) {
129 m_stringLookupTable[m_stringLookupPosition] = s;
130 m_stringLookupPosition = (m_stringLookupPosition + 1) % O5M_STRING_TABLE_SIZE;
131 }
132
133 it += len1 + len2 + 2;
134 return std::make_pair(s, s + len1 + 1);
135 }
136}
137
138void O5mParser::skipVersionInformation(const uint8_t *&it, const uint8_t *end)
139{
140 if (it >= end) { return; }
141 const auto version = readUnsigned(it, end);
142 if (version > 0) {
143 qWarning() << "skipping changeset data not implemented yet!";
144 // timestamp (seconds since 1970, signed, delta-coded)
145 // author information – only if timestamp is not 0:
146 // changeset (signed, delta-coded)
147 // uid, user (string pair)
148 it = end;
149 }
150}
151
152template<typename Elem>
153void O5mParser::readTagOrBbox(Elem &e, const uint8_t *&it, const uint8_t *endIt)
154{
155 const auto tagData = readStringPair(it, endIt);
156 if (!tagData.first) {
157 return;
158 }
159 if (std::strcmp(tagData.first, "bBox") == 0) {
160 char *next = nullptr;
161 const auto lon1 = std::strtod(tagData.second, &next);
162 ++next;
163 const auto lat1 = std::strtod(next, &next);
164 ++next;
165 const auto lon2 = std::strtod(next, &next);
166 ++next;
167 const auto lat2 = std::strtod(next, &next);
168 e.bbox = OSM::BoundingBox(OSM::Coordinate(lat1, lon1), OSM::Coordinate(lat2, lon2));
169 return;
170 }
171
172 OSM::Tag tag;
173 tag.key = m_dataSet->makeTagKey(tagData.first, OSM::StringMemory::Transient); // TODO make use of mmap'ed data for this
174 tag.value = QByteArray(tagData.second);
175 e.tags.push_back(std::move(tag));
176}
177
178void O5mParser::readNode(const uint8_t *begin, const uint8_t *end)
179{
180 OSM::Node node;
181
182 auto it = begin;
183 node.id = readDelta(it, end, m_nodeIdDelta);
184 skipVersionInformation(it, end);
185 if (it >= end) { return; }
186
187 node.coordinate.longitude = (int64_t)readDelta(it, end, m_lonDelta) + 1'800'000'000ll;
188 node.coordinate.latitude = (int64_t)readDelta(it, end, m_latDelata) + 900'000'000ll;
189
190 while (it < end) {
191 OSM::Tag tag;
192 const auto tagData = readStringPair(it, end);
193 if (tagData.first) {
194 tag.key = m_dataSet->makeTagKey(tagData.first, OSM::StringMemory::Transient); // TODO use the fact this is mmap'ed data here
195 tag.value = QByteArray(tagData.second);
196 node.tags.push_back(std::move(tag));
197 }
198 }
199 std::sort(node.tags.begin(), node.tags.end());
200
201 addNode(std::move(node));
202}
203
204void O5mParser::readWay(const uint8_t *begin, const uint8_t *end)
205{
206 OSM::Way way;
207
208 auto it = begin;
209 way.id = readDelta(it, end, m_wayIdDelta);
210 skipVersionInformation(it, end);
211 if (it >= end) { return; }
212
213 const auto nodesBlockSize = readUnsigned(it, end);
214 if (it + nodesBlockSize > end) { return; }
215
216 const auto nodesBlockEnd = it + nodesBlockSize;
217 while(it < nodesBlockEnd) {
218 way.nodes.push_back(readDelta(it, end, m_wayNodeIdDelta));
219 }
220
221 while (it < end) {
222 readTagOrBbox(way, it, end);
223 }
224 std::sort(way.tags.begin(), way.tags.end());
225
226 addWay(std::move(way));
227}
228
229void O5mParser::readRelation(const uint8_t *begin, const uint8_t *end)
230{
231 OSM::Relation rel;
232
233 auto it = begin;
234 rel.id = readDelta(it, end, m_relIdDelta);
235 skipVersionInformation(it, end);
236 if (it >= end) { return; }
237
238 const auto relBlockSize = readUnsigned(it, end);
239 if (it + relBlockSize > end) { return; }
240
241 const auto relBlockEnd = it + relBlockSize;
242 while (it < relBlockEnd) {
243 const int64_t memId = readSigned(it, end);
244 OSM::Member mem;
245 const auto typeAndRole = readString(it, end);
246 switch (typeAndRole[0]) {
247 case O5M_MEMTYPE_NODE:
248 mem.id = m_relNodeMemberIdDelta += memId;
249 mem.setType(OSM::Type::Node);
250 break;
251 case O5M_MEMTYPE_WAY:
252 mem.id = m_relWayMemberIdDelta += memId;
253 mem.setType(OSM::Type::Way);
254 break;
255 case O5M_MEMTYPE_RELATION:
256 mem.id = m_relRelMemberIdDelta += memId;
257 mem.setType(OSM::Type::Relation);
258 break;
259 }
260 mem.setRole(m_dataSet->makeRole(typeAndRole + 1, OSM::StringMemory::Transient));
261
262 rel.members.push_back(std::move(mem));
263 }
264
265
266
267 while (it < end) {
268 readTagOrBbox(rel, it, end);
269 }
270 std::sort(rel.tags.begin(), rel.tags.end());
271
272 addRelation(std::move(rel));
273}
274
275void O5mParser::resetDeltaCodingState()
276{
277 m_nodeIdDelta = 0;
278 m_latDelata = 0;
279 m_lonDelta = 0;
280
281 m_wayIdDelta = 0;
282 m_wayNodeIdDelta = 0;
283
284 m_relIdDelta = 0;
285 m_relNodeMemberIdDelta = 0;
286 m_relWayMemberIdDelta = 0;
287 m_relRelMemberIdDelta = 0;
288}
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.
Bounding box, ie.
Definition datatypes.h:95
Coordinate, stored as 1e7 * degree to avoid floating point precision issues, and offset to unsigned v...
Definition datatypes.h:37
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 node.
Definition datatypes.h:204
An OSM relation.
Definition datatypes.h:311
An OSM element tag.
Definition datatypes.h:182
An OSM way.
Definition datatypes.h:231
KDB_EXPORT KDbVersionInfo version()
const QList< QKeySequence > & begin()
const QList< QKeySequence > & next()
const QList< QKeySequence > & end()
Low-level types and functions to work with raw OSM data as efficiently as possible.
Common declarations for O5M file format I/O.
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.