OpenSWMM Engine  6.0.0-alpha.3
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.3)
Loading...
Searching...
No Matches
GpkgGeometry.hpp
Go to the documentation of this file.
1
18
19#ifndef OPENSWMM_GPKG_GEOMETRY_HPP
20#define OPENSWMM_GPKG_GEOMETRY_HPP
21
22#include <vector>
23#include <cstdint>
24#include <cstring>
25#include <stdexcept>
26
27namespace openswmm::gpkg {
28
29// WKB geometry type codes (ISO 13249)
30enum WkbType : uint32_t {
35};
36
37// Byte order
38constexpr uint8_t WKB_LITTLE_ENDIAN = 1;
39
40// GeoPackage header magic
41constexpr uint8_t GP_MAGIC_1 = 0x47; // 'G'
42constexpr uint8_t GP_MAGIC_2 = 0x50; // 'P'
43
44// ============================================================================
45// Encoding helpers
46// ============================================================================
47
48namespace detail {
49
50inline void append_u8(std::vector<uint8_t>& buf, uint8_t v) {
51 buf.push_back(v);
52}
53
54inline void append_u32(std::vector<uint8_t>& buf, uint32_t v) {
55 buf.insert(buf.end(), reinterpret_cast<const uint8_t*>(&v),
56 reinterpret_cast<const uint8_t*>(&v) + 4);
57}
58
59inline void append_f64(std::vector<uint8_t>& buf, double v) {
60 buf.insert(buf.end(), reinterpret_cast<const uint8_t*>(&v),
61 reinterpret_cast<const uint8_t*>(&v) + 8);
62}
63
64// GP header with envelope type 1 (min/max XY = 32 bytes envelope)
65inline void write_gp_header(std::vector<uint8_t>& buf, int32_t srs_id,
66 double min_x, double min_y, double max_x, double max_y) {
69 append_u8(buf, 0x00); // version 0
70 // flags: byte_order=1 (LE), envelope_type=1 (XY), empty=0
71 // bits: [empty:1][envelope:3][byte_order:1] -> 0b0_001_1 = 0x03 (LE) with envelope type 1
72 uint8_t flags = 0x00;
73 flags |= WKB_LITTLE_ENDIAN; // bit 0: byte order (1=LE)
74 flags |= (1 << 1); // bits 1-3: envelope type 1 (XY only)
75 append_u8(buf, flags);
76 // SRS ID
77 buf.insert(buf.end(), reinterpret_cast<const uint8_t*>(&srs_id),
78 reinterpret_cast<const uint8_t*>(&srs_id) + 4);
79 // Envelope (32 bytes for type 1: min_x, max_x, min_y, max_y)
80 append_f64(buf, min_x);
81 append_f64(buf, max_x);
82 append_f64(buf, min_y);
83 append_f64(buf, max_y);
84}
85
86// GP header with no envelope (empty geometry)
87inline void write_gp_header_empty(std::vector<uint8_t>& buf, int32_t srs_id) {
90 append_u8(buf, 0x00);
91 uint8_t flags = WKB_LITTLE_ENDIAN | (1 << 4); // empty flag set
92 append_u8(buf, flags);
93 buf.insert(buf.end(), reinterpret_cast<const uint8_t*>(&srs_id),
94 reinterpret_cast<const uint8_t*>(&srs_id) + 4);
95}
96
97} // namespace detail
98
99// ============================================================================
100// Encode functions
101// ============================================================================
102
106inline std::vector<uint8_t> encode_point(double x, double y, int32_t srs_id) {
107 std::vector<uint8_t> buf;
108 buf.reserve(8 + 32 + 21); // header + envelope + WKB point
109 detail::write_gp_header(buf, srs_id, x, y, x, y);
110 // WKB POINT
113 detail::append_f64(buf, x);
114 detail::append_f64(buf, y);
115 return buf;
116}
117
123inline std::vector<uint8_t> encode_linestring(const std::vector<double>& xs,
124 const std::vector<double>& ys,
125 int32_t srs_id) {
126 if (xs.empty()) {
127 std::vector<uint8_t> buf;
131 detail::append_u32(buf, 0);
132 return buf;
133 }
134 double min_x = xs[0], max_x = xs[0], min_y = ys[0], max_y = ys[0];
135 for (size_t i = 1; i < xs.size(); ++i) {
136 min_x = std::min(min_x, xs[i]);
137 max_x = std::max(max_x, xs[i]);
138 min_y = std::min(min_y, ys[i]);
139 max_y = std::max(max_y, ys[i]);
140 }
141 std::vector<uint8_t> buf;
142 buf.reserve(8 + 32 + 9 + xs.size() * 16);
143 detail::write_gp_header(buf, srs_id, min_x, min_y, max_x, max_y);
146 detail::append_u32(buf, static_cast<uint32_t>(xs.size()));
147 for (size_t i = 0; i < xs.size(); ++i) {
148 detail::append_f64(buf, xs[i]);
149 detail::append_f64(buf, ys[i]);
150 }
151 return buf;
152}
153
159inline std::vector<uint8_t> encode_multipolygon(const std::vector<double>& xs,
160 const std::vector<double>& ys,
161 int32_t srs_id) {
162 if (xs.empty()) {
163 std::vector<uint8_t> buf;
167 detail::append_u32(buf, 0);
168 return buf;
169 }
170 // Ensure ring closure
171 bool closed = (xs.front() == xs.back() && ys.front() == ys.back());
172 uint32_t n_pts = static_cast<uint32_t>(xs.size()) + (closed ? 0 : 1);
173
174 double min_x = xs[0], max_x = xs[0], min_y = ys[0], max_y = ys[0];
175 for (size_t i = 1; i < xs.size(); ++i) {
176 min_x = std::min(min_x, xs[i]);
177 max_x = std::max(max_x, xs[i]);
178 min_y = std::min(min_y, ys[i]);
179 max_y = std::max(max_y, ys[i]);
180 }
181
182 std::vector<uint8_t> buf;
183 buf.reserve(8 + 32 + 50 + n_pts * 16);
184 detail::write_gp_header(buf, srs_id, min_x, min_y, max_x, max_y);
185
186 // WKB MULTIPOLYGON
189 detail::append_u32(buf, 1); // 1 polygon
190
191 // WKB POLYGON
194 detail::append_u32(buf, 1); // 1 ring
195 detail::append_u32(buf, n_pts);
196 for (size_t i = 0; i < xs.size(); ++i) {
197 detail::append_f64(buf, xs[i]);
198 detail::append_f64(buf, ys[i]);
199 }
200 if (!closed) {
201 detail::append_f64(buf, xs.front());
202 detail::append_f64(buf, ys.front());
203 }
204 return buf;
205}
206
216inline std::vector<uint8_t> encode_polygon(const std::vector<double>& xs,
217 const std::vector<double>& ys,
218 int32_t srs_id) {
219 if (xs.empty()) {
220 std::vector<uint8_t> buf;
224 detail::append_u32(buf, 0);
225 return buf;
226 }
227 // Ensure ring closure
228 bool closed = (xs.front() == xs.back() && ys.front() == ys.back());
229 uint32_t n_pts = static_cast<uint32_t>(xs.size()) + (closed ? 0 : 1);
230
231 double min_x = xs[0], max_x = xs[0], min_y = ys[0], max_y = ys[0];
232 for (size_t i = 1; i < xs.size(); ++i) {
233 min_x = std::min(min_x, xs[i]);
234 max_x = std::max(max_x, xs[i]);
235 min_y = std::min(min_y, ys[i]);
236 max_y = std::max(max_y, ys[i]);
237 }
238
239 std::vector<uint8_t> buf;
240 buf.reserve(8 + 32 + 30 + n_pts * 16);
241 detail::write_gp_header(buf, srs_id, min_x, min_y, max_x, max_y);
242
243 // WKB POLYGON
246 detail::append_u32(buf, 1); // 1 ring
247 detail::append_u32(buf, n_pts);
248 for (size_t i = 0; i < xs.size(); ++i) {
249 detail::append_f64(buf, xs[i]);
250 detail::append_f64(buf, ys[i]);
251 }
252 if (!closed) {
253 detail::append_f64(buf, xs.front());
254 detail::append_f64(buf, ys.front());
255 }
256 return buf;
257}
258
259// ============================================================================
260// Decode structures
261// ============================================================================
262
264 double x = 0.0, y = 0.0;
265 int32_t srs_id = 0;
266};
267
269 std::vector<double> xs, ys;
270 int32_t srs_id = 0;
271};
272
274 // Outer ring of the first polygon only (sufficient for SWMM subcatchments)
275 std::vector<double> xs, ys;
276 int32_t srs_id = 0;
277};
278
279// ============================================================================
280// Decode functions
281// ============================================================================
282
283namespace detail {
284
285inline size_t parse_gp_header(const uint8_t* data, size_t size, int32_t& srs_id) {
286 if (size < 8) throw std::runtime_error("GeoPackage binary too short");
287 if (data[0] != GP_MAGIC_1 || data[1] != GP_MAGIC_2)
288 throw std::runtime_error("Invalid GeoPackage binary magic");
289 uint8_t flags = data[3];
290 std::memcpy(&srs_id, data + 4, 4);
291 // Determine envelope size from flags bits 1-3
292 int envelope_type = (flags >> 1) & 0x07;
293 size_t envelope_size = 0;
294 switch (envelope_type) {
295 case 0: envelope_size = 0; break;
296 case 1: envelope_size = 32; break; // XY
297 case 2: envelope_size = 48; break; // XYZ
298 case 3: envelope_size = 48; break; // XYM
299 case 4: envelope_size = 64; break; // XYZM
300 default: throw std::runtime_error("Unknown envelope type");
301 }
302 return 8 + envelope_size; // offset to WKB payload
303}
304
305template<typename T>
306inline T read_val(const uint8_t* data, size_t& offset) {
307 T val;
308 std::memcpy(&val, data + offset, sizeof(T));
309 offset += sizeof(T);
310 return val;
311}
312
313} // namespace detail
314
315inline DecodedPoint decode_point(const std::vector<uint8_t>& blob) {
316 DecodedPoint pt;
317 size_t offset = detail::parse_gp_header(blob.data(), blob.size(), pt.srs_id);
318 offset += 1; // byte order
319 uint32_t type = detail::read_val<uint32_t>(blob.data(), offset);
320 if (type != WKB_POINT) throw std::runtime_error("Expected WKB POINT");
321 pt.x = detail::read_val<double>(blob.data(), offset);
322 pt.y = detail::read_val<double>(blob.data(), offset);
323 return pt;
324}
325
326inline DecodedLinestring decode_linestring(const std::vector<uint8_t>& blob) {
328 size_t offset = detail::parse_gp_header(blob.data(), blob.size(), ls.srs_id);
329 offset += 1; // byte order
330 uint32_t type = detail::read_val<uint32_t>(blob.data(), offset);
331 if (type != WKB_LINESTRING) throw std::runtime_error("Expected WKB LINESTRING");
332 uint32_t n = detail::read_val<uint32_t>(blob.data(), offset);
333 ls.xs.resize(n);
334 ls.ys.resize(n);
335 for (uint32_t i = 0; i < n; ++i) {
336 ls.xs[i] = detail::read_val<double>(blob.data(), offset);
337 ls.ys[i] = detail::read_val<double>(blob.data(), offset);
338 }
339 return ls;
340}
341
342inline DecodedMultipolygon decode_multipolygon(const std::vector<uint8_t>& blob) {
344 size_t offset = detail::parse_gp_header(blob.data(), blob.size(), mp.srs_id);
345 offset += 1; // byte order
346 uint32_t type = detail::read_val<uint32_t>(blob.data(), offset);
347 if (type != WKB_MULTIPOLYGON) throw std::runtime_error("Expected WKB MULTIPOLYGON");
348 uint32_t n_polys = detail::read_val<uint32_t>(blob.data(), offset);
349 if (n_polys == 0) return mp;
350 // Read first polygon
351 offset += 1; // polygon byte order
352 uint32_t poly_type = detail::read_val<uint32_t>(blob.data(), offset);
353 (void)poly_type;
354 uint32_t n_rings = detail::read_val<uint32_t>(blob.data(), offset);
355 if (n_rings == 0) return mp;
356 // Read first ring (outer ring)
357 uint32_t n_pts = detail::read_val<uint32_t>(blob.data(), offset);
358 mp.xs.resize(n_pts);
359 mp.ys.resize(n_pts);
360 for (uint32_t i = 0; i < n_pts; ++i) {
361 mp.xs[i] = detail::read_val<double>(blob.data(), offset);
362 mp.ys[i] = detail::read_val<double>(blob.data(), offset);
363 }
364 return mp;
365}
366
367} // namespace openswmm::gpkg
368
369#endif // OPENSWMM_GPKG_GEOMETRY_HPP
Definition GpkgGeometry.hpp:48
void append_u32(std::vector< uint8_t > &buf, uint32_t v)
Definition GpkgGeometry.hpp:54
void write_gp_header_empty(std::vector< uint8_t > &buf, int32_t srs_id)
Definition GpkgGeometry.hpp:87
size_t parse_gp_header(const uint8_t *data, size_t size, int32_t &srs_id)
Definition GpkgGeometry.hpp:285
T read_val(const uint8_t *data, size_t &offset)
Definition GpkgGeometry.hpp:306
void append_u8(std::vector< uint8_t > &buf, uint8_t v)
Definition GpkgGeometry.hpp:50
void append_f64(std::vector< uint8_t > &buf, double v)
Definition GpkgGeometry.hpp:59
void write_gp_header(std::vector< uint8_t > &buf, int32_t srs_id, double min_x, double min_y, double max_x, double max_y)
Definition GpkgGeometry.hpp:65
Definition ExternalContentReader.cpp:33
std::vector< uint8_t > encode_point(double x, double y, int32_t srs_id)
Encode a POINT geometry in GeoPackage Binary format.
Definition GpkgGeometry.hpp:106
DecodedMultipolygon decode_multipolygon(const std::vector< uint8_t > &blob)
Definition GpkgGeometry.hpp:342
DecodedPoint decode_point(const std::vector< uint8_t > &blob)
Definition GpkgGeometry.hpp:315
std::vector< uint8_t > encode_polygon(const std::vector< double > &xs, const std::vector< double > &ys, int32_t srs_id)
Encode a POLYGON geometry (single ring) in GeoPackage Binary format.
Definition GpkgGeometry.hpp:216
constexpr uint8_t WKB_LITTLE_ENDIAN
Definition GpkgGeometry.hpp:38
DecodedLinestring decode_linestring(const std::vector< uint8_t > &blob)
Definition GpkgGeometry.hpp:326
std::vector< uint8_t > encode_linestring(const std::vector< double > &xs, const std::vector< double > &ys, int32_t srs_id)
Encode a LINESTRING geometry in GeoPackage Binary format.
Definition GpkgGeometry.hpp:123
constexpr uint8_t GP_MAGIC_2
Definition GpkgGeometry.hpp:42
WkbType
Definition GpkgGeometry.hpp:30
@ WKB_POINT
Definition GpkgGeometry.hpp:31
@ WKB_POLYGON
Definition GpkgGeometry.hpp:33
@ WKB_MULTIPOLYGON
Definition GpkgGeometry.hpp:34
@ WKB_LINESTRING
Definition GpkgGeometry.hpp:32
constexpr uint8_t GP_MAGIC_1
Definition GpkgGeometry.hpp:41
std::vector< uint8_t > encode_multipolygon(const std::vector< double > &xs, const std::vector< double > &ys, int32_t srs_id)
Encode a MULTIPOLYGON geometry (single polygon, single ring) in GeoPackage Binary format.
Definition GpkgGeometry.hpp:159
double * y
Definition odesolve.c:28
Definition GpkgGeometry.hpp:268
std::vector< double > xs
Definition GpkgGeometry.hpp:269
std::vector< double > ys
Definition GpkgGeometry.hpp:269
int32_t srs_id
Definition GpkgGeometry.hpp:270
Definition GpkgGeometry.hpp:273
int32_t srs_id
Definition GpkgGeometry.hpp:276
std::vector< double > ys
Definition GpkgGeometry.hpp:275
std::vector< double > xs
Definition GpkgGeometry.hpp:275
Definition GpkgGeometry.hpp:263
double y
Definition GpkgGeometry.hpp:264
int32_t srs_id
Definition GpkgGeometry.hpp:265
double x
Definition GpkgGeometry.hpp:264