OpenSWMM Engine  6.0.0-alpha.1
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.1)
Loading...
Searching...
No Matches
GpkgGeometry.hpp
Go to the documentation of this file.
1
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
207// ============================================================================
208// Decode structures
209// ============================================================================
210
212 double x = 0.0, y = 0.0;
213 int32_t srs_id = 0;
214};
215
217 std::vector<double> xs, ys;
218 int32_t srs_id = 0;
219};
220
222 // Outer ring of the first polygon only (sufficient for SWMM subcatchments)
223 std::vector<double> xs, ys;
224 int32_t srs_id = 0;
225};
226
227// ============================================================================
228// Decode functions
229// ============================================================================
230
231namespace detail {
232
233inline size_t parse_gp_header(const uint8_t* data, size_t size, int32_t& srs_id) {
234 if (size < 8) throw std::runtime_error("GeoPackage binary too short");
235 if (data[0] != GP_MAGIC_1 || data[1] != GP_MAGIC_2)
236 throw std::runtime_error("Invalid GeoPackage binary magic");
237 uint8_t flags = data[3];
238 std::memcpy(&srs_id, data + 4, 4);
239 // Determine envelope size from flags bits 1-3
240 int envelope_type = (flags >> 1) & 0x07;
241 size_t envelope_size = 0;
242 switch (envelope_type) {
243 case 0: envelope_size = 0; break;
244 case 1: envelope_size = 32; break; // XY
245 case 2: envelope_size = 48; break; // XYZ
246 case 3: envelope_size = 48; break; // XYM
247 case 4: envelope_size = 64; break; // XYZM
248 default: throw std::runtime_error("Unknown envelope type");
249 }
250 return 8 + envelope_size; // offset to WKB payload
251}
252
253template<typename T>
254inline T read_val(const uint8_t* data, size_t& offset) {
255 T val;
256 std::memcpy(&val, data + offset, sizeof(T));
257 offset += sizeof(T);
258 return val;
259}
260
261} // namespace detail
262
263inline DecodedPoint decode_point(const std::vector<uint8_t>& blob) {
264 DecodedPoint pt;
265 size_t offset = detail::parse_gp_header(blob.data(), blob.size(), pt.srs_id);
266 offset += 1; // byte order
267 uint32_t type = detail::read_val<uint32_t>(blob.data(), offset);
268 if (type != WKB_POINT) throw std::runtime_error("Expected WKB POINT");
269 pt.x = detail::read_val<double>(blob.data(), offset);
270 pt.y = detail::read_val<double>(blob.data(), offset);
271 return pt;
272}
273
274inline DecodedLinestring decode_linestring(const std::vector<uint8_t>& blob) {
276 size_t offset = detail::parse_gp_header(blob.data(), blob.size(), ls.srs_id);
277 offset += 1; // byte order
278 uint32_t type = detail::read_val<uint32_t>(blob.data(), offset);
279 if (type != WKB_LINESTRING) throw std::runtime_error("Expected WKB LINESTRING");
280 uint32_t n = detail::read_val<uint32_t>(blob.data(), offset);
281 ls.xs.resize(n);
282 ls.ys.resize(n);
283 for (uint32_t i = 0; i < n; ++i) {
284 ls.xs[i] = detail::read_val<double>(blob.data(), offset);
285 ls.ys[i] = detail::read_val<double>(blob.data(), offset);
286 }
287 return ls;
288}
289
290inline DecodedMultipolygon decode_multipolygon(const std::vector<uint8_t>& blob) {
292 size_t offset = detail::parse_gp_header(blob.data(), blob.size(), mp.srs_id);
293 offset += 1; // byte order
294 uint32_t type = detail::read_val<uint32_t>(blob.data(), offset);
295 if (type != WKB_MULTIPOLYGON) throw std::runtime_error("Expected WKB MULTIPOLYGON");
296 uint32_t n_polys = detail::read_val<uint32_t>(blob.data(), offset);
297 if (n_polys == 0) return mp;
298 // Read first polygon
299 offset += 1; // polygon byte order
300 uint32_t poly_type = detail::read_val<uint32_t>(blob.data(), offset);
301 (void)poly_type;
302 uint32_t n_rings = detail::read_val<uint32_t>(blob.data(), offset);
303 if (n_rings == 0) return mp;
304 // Read first ring (outer ring)
305 uint32_t n_pts = detail::read_val<uint32_t>(blob.data(), offset);
306 mp.xs.resize(n_pts);
307 mp.ys.resize(n_pts);
308 for (uint32_t i = 0; i < n_pts; ++i) {
309 mp.xs[i] = detail::read_val<double>(blob.data(), offset);
310 mp.ys[i] = detail::read_val<double>(blob.data(), offset);
311 }
312 return mp;
313}
314
315} // namespace openswmm::gpkg
316
317#endif // OPENSWMM_GPKG_GEOMETRY_HPP
int size
Definition XSectBatch.cpp:567
const double * data
Definition XSectBatch.cpp:567
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:233
T read_val(const uint8_t *data, size_t &offset)
Definition GpkgGeometry.hpp:254
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 GeoPackageInputPlugin.cpp:15
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:290
DecodedPoint decode_point(const std::vector< uint8_t > &blob)
Definition GpkgGeometry.hpp:263
constexpr uint8_t WKB_LITTLE_ENDIAN
Definition GpkgGeometry.hpp:38
DecodedLinestring decode_linestring(const std::vector< uint8_t > &blob)
Definition GpkgGeometry.hpp:274
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:216
std::vector< double > xs
Definition GpkgGeometry.hpp:217
std::vector< double > ys
Definition GpkgGeometry.hpp:217
int32_t srs_id
Definition GpkgGeometry.hpp:218
Definition GpkgGeometry.hpp:221
int32_t srs_id
Definition GpkgGeometry.hpp:224
std::vector< double > ys
Definition GpkgGeometry.hpp:223
std::vector< double > xs
Definition GpkgGeometry.hpp:223
Definition GpkgGeometry.hpp:211
double y
Definition GpkgGeometry.hpp:212
int32_t srs_id
Definition GpkgGeometry.hpp:213
double x
Definition GpkgGeometry.hpp:212