File indexing completed on 2025-04-27 10:01:34
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "OsmPbfParser.h" 0008 0009 #ifdef HAVE_PROTOBUF 0010 #include "fileformat.pb.h" 0011 #include "osmformat.pb.h" 0012 #endif 0013 0014 #include <QDebug> 0015 #include <QtEndian> 0016 0017 #include <zlib.h> 0018 0019 #include <cstdint> 0020 #include <cstring> 0021 0022 using namespace Marble; 0023 0024 void OsmPbfParser::parse(const uint8_t *data, std::size_t len) 0025 { 0026 #ifdef HAVE_PROTOBUF 0027 const uint8_t *it = data; 0028 const uint8_t *end = data + len; 0029 while (parseBlob(it, end)); 0030 #endif 0031 } 0032 0033 #ifdef HAVE_PROTOBUF 0034 bool OsmPbfParser::parseBlob(const uint8_t *&it, const uint8_t *end) 0035 { 0036 if (std::distance(it, end) < (int)sizeof(int32_t)) { 0037 return false; 0038 } 0039 int32_t blobHeaderSize = 0; 0040 std::memcpy(&blobHeaderSize, it, sizeof(int32_t)); 0041 blobHeaderSize = qFromBigEndian(blobHeaderSize); 0042 it += sizeof(int32_t); 0043 0044 if (blobHeaderSize < 0 || std::distance(it, end) < blobHeaderSize) { 0045 return false; 0046 } 0047 0048 OSMPBF::BlobHeader blobHeader; 0049 if (!blobHeader.ParseFromArray(it, blobHeaderSize)) { 0050 return false; 0051 } 0052 it += blobHeaderSize; 0053 0054 OSMPBF::Blob blob; 0055 if (std::distance(it, end) < blobHeader.datasize() || !blob.ParseFromArray(it, blobHeader.datasize())) { 0056 return false; 0057 } 0058 0059 const uint8_t *dataBegin = nullptr; 0060 if (blob.has_raw()) { 0061 dataBegin = reinterpret_cast<const uint8_t*>(blob.raw().data()); 0062 } else if (blob.has_zlib_data()) { 0063 m_zlibBuffer.resize(blob.raw_size()); 0064 z_stream zStream; 0065 zStream.next_in = (uint8_t*)blob.zlib_data().data(); 0066 zStream.avail_in = blob.zlib_data().size(); 0067 zStream.next_out = (uint8_t*)m_zlibBuffer.data(); 0068 zStream.avail_out = blob.raw_size(); 0069 zStream.zalloc = nullptr; 0070 zStream.zfree = nullptr; 0071 zStream.opaque = nullptr; 0072 auto result = inflateInit(&zStream); 0073 if (result != Z_OK) { 0074 return false; 0075 } 0076 result = inflate(&zStream, Z_FINISH); 0077 if (result != Z_STREAM_END) { 0078 return false; 0079 } 0080 result = inflateEnd( &zStream ); 0081 dataBegin = reinterpret_cast<const uint8_t*>(m_zlibBuffer.constData()); 0082 } else { 0083 return false; 0084 } 0085 if (std::strcmp(blobHeader.type().c_str(), "OSMData") == 0) { 0086 parsePrimitiveBlock(dataBegin, blob.raw_size()); 0087 } 0088 0089 m_zlibBuffer.clear(); 0090 it += blobHeader.datasize(); 0091 return true; 0092 } 0093 0094 void OsmPbfParser::parsePrimitiveBlock(const uint8_t *data, std::size_t len) 0095 { 0096 OSMPBF::PrimitiveBlock block; 0097 if (!block.ParseFromArray(data, len)) { 0098 return; 0099 } 0100 0101 for (int i = 0; i < block.primitivegroup_size(); ++i) { 0102 const auto &group = block.primitivegroup(i); 0103 0104 if (group.nodes_size()) { 0105 qWarning() << "non-dense nodes - not implemented yet!"; 0106 } else if (group.has_dense()) { 0107 parseDenseNodes(block, group); 0108 } else if (group.ways_size()) { 0109 parseWays(block, group); 0110 } else if (group.relations_size()) { 0111 parseRelations(block, group); 0112 } 0113 } 0114 } 0115 0116 void OsmPbfParser::parseDenseNodes(const OSMPBF::PrimitiveBlock &block, const OSMPBF::PrimitiveGroup &group) 0117 { 0118 int64_t idDelta = 0; 0119 int64_t latDelta = 0; 0120 int64_t lonDelta = 0; 0121 int tagIdx = 0; 0122 0123 const auto dense = group.dense(); 0124 for (int i = 0; i < dense.id_size(); ++i) { 0125 idDelta += dense.id(i); 0126 latDelta += dense.lat(i); 0127 lonDelta += dense.lon(i); 0128 0129 auto &node = m_nodes[idDelta]; 0130 node.osmData().setId(idDelta); 0131 node.setCoordinates(GeoDataCoordinates(lonDelta * 1.0e-7, latDelta * 1.0e-7, 0.0, GeoDataCoordinates::Degree)); 0132 0133 while (tagIdx < dense.keys_vals_size()) { 0134 const auto keyIdx = dense.keys_vals(tagIdx++); 0135 if (keyIdx == 0) { 0136 break; 0137 } 0138 const auto valIdx = dense.keys_vals(tagIdx++); 0139 const QString keyString = *m_stringPool.insert(QString::fromUtf8(block.stringtable().s(keyIdx).data())); 0140 const QString valueString = *m_stringPool.insert(QString::fromUtf8(block.stringtable().s(valIdx).data())); 0141 node.osmData().addTag(keyString, valueString); 0142 } 0143 } 0144 } 0145 0146 void OsmPbfParser::parseWays(const OSMPBF::PrimitiveBlock &block, const OSMPBF::PrimitiveGroup &group) 0147 { 0148 for (int i = 0; i < group.ways_size(); ++i) { 0149 const auto &w = group.ways(i); 0150 auto &way = m_ways[w.id()]; 0151 way.osmData().setId(w.id()); 0152 0153 int64_t idDelta = 0; 0154 for (int j = 0; j < w.refs_size(); ++j) { 0155 idDelta += w.refs(j); 0156 way.addReference(idDelta); 0157 } 0158 0159 for (int j = 0; j < w.keys_size(); ++j) { 0160 const QString keyString = *m_stringPool.insert(QString::fromUtf8(block.stringtable().s(w.keys(j)).data())); 0161 const QString valueString = *m_stringPool.insert(QString::fromUtf8(block.stringtable().s(w.vals(j)).data())); 0162 way.osmData().addTag(keyString, valueString); 0163 } 0164 } 0165 } 0166 0167 void OsmPbfParser::parseRelations(const OSMPBF::PrimitiveBlock &block, const OSMPBF::PrimitiveGroup &group) 0168 { 0169 for (int i = 0; i < group.relations_size(); ++i) { 0170 const auto &r = group.relations(i); 0171 0172 auto &rel = m_relations[r.id()]; 0173 rel.osmData().setId(r.id()); 0174 0175 int64_t idDelta = 0; 0176 for (int j = 0; j < r.memids_size(); ++j) { 0177 idDelta += r.memids(j); 0178 const QString role = *m_stringPool.insert(QString::fromUtf8(block.stringtable().s(r.roles_sid(j)).data())); 0179 QString typeName; 0180 const auto type = r.types(j); 0181 switch (type) { 0182 case OSMPBF::Relation_MemberType_NODE: typeName = QStringLiteral("node"); break; 0183 case OSMPBF::Relation_MemberType_WAY: typeName = QStringLiteral("way"); break; 0184 case OSMPBF::Relation_MemberType_RELATION: typeName = QStringLiteral("relation"); break; 0185 } 0186 rel.addMember(idDelta, role, typeName); 0187 } 0188 0189 for (int j = 0; j < r.keys_size(); ++j) { 0190 const QString keyString = *m_stringPool.insert(QString::fromUtf8(block.stringtable().s(r.keys(j)).data())); 0191 const QString valueString = *m_stringPool.insert(QString::fromUtf8(block.stringtable().s(r.vals(j)).data())); 0192 rel.osmData().addTag(keyString, valueString); 0193 } 0194 } 0195 } 0196 #endif