File indexing completed on 2024-05-12 04:42:18
0001 /* 0002 SPDX-FileCopyrightText: 2023 Volker Krause <vkrause@kde.org> 0003 SPDX-License-Identifier: LGPL-2.0-or-later 0004 */ 0005 0006 #include "o5mwriter.h" 0007 #include "o5m.h" 0008 #include "datatypes.h" 0009 0010 #include <QBuffer> 0011 #include <QIODevice> 0012 0013 using namespace OSM; 0014 0015 static void writeByte(uint8_t b, QIODevice *io) 0016 { 0017 io->write(reinterpret_cast<const char*>(&b), 1); 0018 } 0019 0020 static void writeUnsigned(uint64_t n, QIODevice *io) 0021 { 0022 do { 0023 uint8_t b = ((n >> 7) > 0 ? O5M_NUMBER_CONTINUATION : 0) | (n & O5M_NUMBER_MASK); 0024 writeByte(b, io); 0025 n >>= 7; 0026 } while (n > 0); 0027 } 0028 0029 static void writeSigned(int64_t n, QIODevice *io) 0030 { 0031 uint64_t u = n < 0 ? (-n - 1) : n; 0032 u <<= 1; 0033 u |= n < 0 ? O5M_NUMBER_SIGNED_BIT : 0; 0034 writeUnsigned(u, io); 0035 } 0036 0037 static void writeHeader(QIODevice *io) 0038 { 0039 writeByte(O5M_BLOCK_RESET, io); 0040 writeByte(O5M_BLOCK_HEADER, io); 0041 writeByte(4, io); 0042 io->write(O5M_HEADER, 4); 0043 } 0044 0045 static void writeTrailer(QIODevice *io) 0046 { 0047 writeByte(O5M_TRAILER, io); 0048 } 0049 0050 void O5mWriter::writeToIODevice(const OSM::DataSet& dataSet, QIODevice* io) 0051 { 0052 writeHeader(io); 0053 writeNodes(dataSet, io); 0054 writeWays(dataSet, io); 0055 writeRelations(dataSet, io); 0056 writeTrailer(io); 0057 0058 m_stringTable.clear(); 0059 } 0060 0061 void O5mWriter::writeNodes(const OSM::DataSet &dataSet, QIODevice *io) 0062 { 0063 if (dataSet.nodes.empty()) { 0064 return; 0065 } 0066 0067 writeByte(O5M_BLOCK_RESET, io); 0068 m_stringTable.clear(); 0069 0070 OSM::Id prevId = 0; 0071 int64_t prevLat = 900'000'000ll; 0072 int64_t prevLon = 1'800'000'000ll; 0073 0074 QByteArray bufferData; 0075 QBuffer buffer(&bufferData); 0076 for(auto const &node: dataSet.nodes) { 0077 bufferData.clear(); 0078 buffer.open(QIODevice::WriteOnly); 0079 writeByte(O5M_BLOCK_NODE, io); 0080 0081 writeSigned((int64_t)node.id - (int64_t)prevId, &buffer); 0082 prevId = node.id; 0083 0084 writeByte(0x0, &buffer); 0085 writeSigned((int64_t)node.coordinate.longitude - prevLon, &buffer); 0086 prevLon = node.coordinate.longitude; 0087 writeSigned((int64_t)node.coordinate.latitude - prevLat, &buffer); 0088 prevLat = node.coordinate.latitude; 0089 0090 writeTags(node, &buffer); 0091 0092 buffer.close(); 0093 writeUnsigned(bufferData.size(), io); 0094 io->write(bufferData.constData(), bufferData.size()); 0095 } 0096 } 0097 0098 void O5mWriter::writeWays(const OSM::DataSet &dataSet, QIODevice *io) 0099 { 0100 if (dataSet.ways.empty()) { 0101 return; 0102 } 0103 0104 writeByte(O5M_BLOCK_RESET, io); 0105 m_stringTable.clear(); 0106 OSM::Id prevId = 0; 0107 OSM::Id prevNodeId = 0; 0108 0109 QByteArray bufferData; 0110 QBuffer buffer(&bufferData); 0111 QByteArray referencesBufferData; 0112 QBuffer referencesBuffer(&referencesBufferData); 0113 0114 for (auto const &way: dataSet.ways) { 0115 writeByte(O5M_BLOCK_WAY, io); 0116 0117 bufferData.clear(); 0118 buffer.open(QIODevice::WriteOnly); 0119 writeSigned((int64_t)way.id - (int64_t)prevId, &buffer); 0120 prevId = way.id; 0121 writeByte(0x0, &buffer); 0122 0123 referencesBufferData.clear(); 0124 referencesBuffer.open(QIODevice::WriteOnly); 0125 for (const auto &node : way.nodes) { 0126 writeSigned((int64_t)node - (int64_t)prevNodeId, &referencesBuffer); 0127 prevNodeId = node; 0128 } 0129 referencesBuffer.close(); 0130 writeUnsigned(referencesBufferData.size(), &buffer); 0131 buffer.write(referencesBufferData.constData(), referencesBufferData.size()); 0132 writeTags(way, &buffer); 0133 buffer.close(); 0134 writeUnsigned(bufferData.size(), io); 0135 io->write(bufferData.constData(), bufferData.size()); 0136 } 0137 } 0138 0139 void O5mWriter::writeRelations(const OSM::DataSet &dataSet, QIODevice *io) 0140 { 0141 if (dataSet.relations.empty()) { 0142 return; 0143 } 0144 0145 writeByte(O5M_BLOCK_RESET, io); 0146 m_stringTable.clear(); 0147 OSM::Id prevId = 0; 0148 OSM::Id prevMemberId[3] = { 0, 0, 0 }; 0149 0150 QByteArray bufferData; 0151 QBuffer buffer(&bufferData); 0152 QByteArray referencesBufferData; 0153 QBuffer referencesBuffer(&referencesBufferData); 0154 QByteArray role; 0155 0156 for (auto const &relation: dataSet.relations) { 0157 writeByte(O5M_BLOCK_RELATION, io); 0158 0159 bufferData.clear(); 0160 buffer.open(QIODevice::WriteOnly); 0161 0162 writeSigned((int64_t)relation.id - (int64_t)prevId, &buffer); 0163 prevId = relation.id; 0164 writeByte(0x0, &buffer); 0165 0166 referencesBufferData.clear(); 0167 referencesBuffer.open(QIODevice::WriteOnly); 0168 0169 for (const auto &member : relation.members) { 0170 role.clear(); 0171 switch (member.type()) { 0172 case OSM::Type::Node: 0173 writeSigned((int64_t)member.id - (int64_t)prevMemberId[0], &referencesBuffer); 0174 prevMemberId[0] = member.id; 0175 role += (const char)O5M_MEMTYPE_NODE; 0176 role += member.role().name(); 0177 writeStringPair(role.constData(), nullptr, &referencesBuffer); 0178 break; 0179 case OSM::Type::Way: 0180 writeSigned((int64_t)member.id - (int64_t)prevMemberId[1], &referencesBuffer); 0181 prevMemberId[1] = member.id; 0182 role += (const char)O5M_MEMTYPE_WAY; 0183 role += member.role().name(); 0184 writeStringPair(role.constData(), nullptr, &referencesBuffer); 0185 break; 0186 case OSM::Type::Relation: 0187 writeSigned((int64_t)member.id - (int64_t)prevMemberId[2], &referencesBuffer); 0188 prevMemberId[2] = member.id; 0189 role += (const char)O5M_MEMTYPE_RELATION; 0190 role += member.role().name(); 0191 writeStringPair(role.constData(), nullptr, &referencesBuffer); 0192 break; 0193 case OSM::Type::Null: 0194 assert(false); 0195 break; 0196 } 0197 } 0198 0199 referencesBuffer.close(); 0200 writeUnsigned(referencesBufferData.size(), &buffer); 0201 buffer.write(referencesBufferData.constData(), referencesBufferData.size()); 0202 writeTags(relation, &buffer); 0203 buffer.close(); 0204 writeUnsigned(bufferData.size(), io); 0205 io->write(bufferData.constData(), bufferData.size()); 0206 } 0207 } 0208 0209 template <typename T> 0210 void O5mWriter::writeTags(const T &elem, QIODevice *io) 0211 { 0212 for (auto &tag : elem.tags) { 0213 writeStringPair(tag.key.name(), tag.value.constData(), io); 0214 } 0215 } 0216 0217 void O5mWriter::writeStringPair(const char *s1, const char *s2, QIODevice *io) 0218 { 0219 assert(s1); 0220 O5mStringPair p; 0221 p.s1 = s1; 0222 if (s2) { 0223 p.s2 = s2; 0224 } 0225 const auto it = m_stringTable.find(p); 0226 if (it != m_stringTable.end()) { 0227 writeUnsigned(m_stringTable.size() - it->second, io); 0228 } else { 0229 writeByte(0x0, io); 0230 io->write(s1); 0231 writeByte(0x0, io); 0232 if (s2) { 0233 io->write(s2); 0234 writeByte(0x0, io); 0235 } 0236 if ((std::strlen(s1) + (s2 ? std::strlen(s2) : 0) <= O5M_STRING_TABLE_MAXLEN) && (m_stringTable.size() <= O5M_STRING_TABLE_SIZE)) { 0237 m_stringTable[p] = m_stringTable.size(); 0238 } 0239 } 0240 }