Vanetza
Loading...
Searching...
No Matches
country_data_reader.hpp
1#pragma once
2#include <vanetza/geodesy/country_polygon.hpp>
3#include <vanetza/geodesy/m49_code.hpp>
4#include <cstddef>
5#include <cstdint>
6#include <string>
7
8namespace vanetza
9{
10namespace geodesy
11{
12
14{
15public:
16 static CountryReaderResult success(std::size_t pos)
17 {
18 return CountryReaderResult { pos };
19 }
20
21 static CountryReaderResult failure(std::string msg, std::size_t pos)
22 {
23 return CountryReaderResult { std::move(msg), pos };
24 }
25
26 bool ok() const { return m_success; }
27 bool failed() const { return !ok(); }
28 const std::string& message() const { return m_detail; }
29 std::size_t position() const { return m_position; }
30
31 CountryReaderResult& add_offset(std::size_t offset)
32 {
33 m_position += offset;
34 return *this;
35 }
36
37private:
38 CountryReaderResult(std::size_t pos) : m_position(pos) {}
39 CountryReaderResult(std::string msg, std::size_t pos) :
40 m_success(false), m_position(pos), m_detail(std::move(msg)) {}
41
42 bool m_success = true;
43 std::size_t m_position = 0;
44 std::string m_detail;
45};
46
47namespace detail
48{
49
50/**
51 * Version of the country data binary file format recognised by the reader.
52 */
53static constexpr uint16_t country_data_format_version = 1;
54
55/**
56 * Parse a single WKB geometry blob (Polygon or MultiPolygon) into a CountryPolygon.
57 * Positions in the returned result are relative to the WKB data blob.
58 * \param data pointer to WKB data
59 * \param length size of WKB data in bytes
60 * \param out parsed polygon (output)
61 * \return ok on success, failure(msg, pos) on parse error
62 */
63CountryReaderResult parse_wkb(const uint8_t* data, std::size_t length, CountryPolygon& out);
64
65uint16_t read_u16le(const uint8_t*);
66uint32_t read_u32le(const uint8_t*);
67
68} // namespace detail
69
70/**
71 * Parse a country data binary file (custom framing around OGC WKB payloads).
72 *
73 * File format: version (uint16 LE) | sequence of entries until EOF
74 * Each entry: m49_code (uint16 LE) | wkb_size (uint32 LE) | wkb_data (wkb_size bytes, OGC WKB).
75 *
76 * Positions in the returned result are relative to the start of the input buffer.
77 *
78 * \param data pointer to binary data (may be nullptr if length is 0)
79 * \param length size of data in bytes
80 * \param fn callback invoked for each parsed entry: void(M49Code, CountryPolygon&&)
81 * \return ok on success, failure(msg, pos) on parse error
82 */
83template<typename CallbackFn>
84CountryReaderResult read_country_data(const uint8_t* data, std::size_t length, CallbackFn fn)
85{
86 if (length < sizeof(uint16_t)) {
87 return CountryReaderResult::failure("truncated file header: missing version field", 0);
88 }
89
90 uint16_t version = detail::read_u16le(data);
91 if (version != detail::country_data_format_version) {
92 return CountryReaderResult::failure("unsupported country data format version", 0);
93 }
94
95 std::size_t offset = sizeof(uint16_t);
96 const std::size_t entry_header_size = sizeof(uint16_t) + sizeof(uint32_t);
97
98 while (offset < length) {
99 if (length - offset < entry_header_size) {
100 return CountryReaderResult::failure("truncated entry header", offset);
101 }
102
103 uint16_t m49 = detail::read_u16le(data + offset);
104 offset += sizeof(uint16_t);
105
106 uint32_t wkb_size = detail::read_u32le(data + offset);
107 offset += sizeof(uint32_t);
108
109 if (length - offset < wkb_size) {
110 return CountryReaderResult::failure("invalid WKB payload size exceeding remaining data", offset);
111 }
112
113 CountryPolygon polygon;
114 auto inner = detail::parse_wkb(data + offset, wkb_size, polygon);
115 if (inner.failed()) {
116 inner.add_offset(offset);
117 return inner;
118 }
119
120 fn(M49Code(m49), std::move(polygon));
121 offset += wkb_size;
122 }
123
124 return CountryReaderResult::success(offset);
125}
126
127} // namespace geodesy
128} // namespace vanetza