File indexing completed on 2025-01-05 03:59:29

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