Quetzal-CoaTL
The Coalescence Template Library
Loading...
Searching...
No Matches
gdalcpp.hpp
1#ifndef GDALCPP_HPP
2#define GDALCPP_HPP
3
4/*
5
6C++11 wrapper classes for GDAL/OGR.
7
8Version 1.1.1
9
10https://github.com/joto/gdalcpp
11
12Copyright 2015 Jochen Topf <jochen@topf.org>
13
14Boost Software License - Version 1.0 - August 17th, 2003
15
16Permission is hereby granted, free of charge, to any person or organization
17obtaining a copy of the software and accompanying documentation covered by
18this license (the "Software") to use, reproduce, display, distribute,
19execute, and transmit the Software, and to prepare derivative works of the
20Software, and to permit third-parties to whom the Software is furnished to
21do so, all subject to the following:
22
23The copyright notices in the Software and this entire statement, including
24the above license grant, this restriction and the following disclaimer,
25must be included in all copies of the Software, in whole or in part, and
26all derivative works of the Software, unless such copies or derivative
27works are solely in the form of machine-executable object code generated by
28a source language processor.
29
30THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
33SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
34FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
35ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
36DEALINGS IN THE SOFTWARE.
37
38*/
39
40#include <algorithm>
41#include <memory>
42#include <stdexcept>
43#include <string>
44#include <vector>
45
46#include <gdal_priv.h>
47#include <gdal_version.h>
48#include <ogr_api.h>
49#include <ogrsf_frmts.h>
50
51#include "assert.h"
52
53namespace quetzal::geography::gdalcpp
54{
55
56using gdal_driver_type = GDALDriver;
57using gdal_dataset_type = GDALDataset;
58
62class gdal_error : public std::runtime_error
63{
64
65 std::string m_driver;
66 std::string m_dataset;
67 std::string m_layer;
68 std::string m_field;
69 OGRErr m_error;
70
71 public:
72 gdal_error(const std::string &message, OGRErr error, const std::string &driver = "",
73 const std::string &dataset = "", const std::string &layer = "", const std::string &field = "")
74 : std::runtime_error(message), m_driver(driver), m_dataset(dataset), m_layer(layer), m_field(field),
75 m_error(error)
76 {
77 }
78
79 const std::string &driver() const
80 {
81 return m_driver;
82 }
83
84 const std::string &dataset() const
85 {
86 return m_dataset;
87 }
88
89 const std::string &layer() const
90 {
91 return m_layer;
92 }
93
94 const std::string &field() const
95 {
96 return m_field;
97 }
98
99 OGRErr error() const
100 {
101 return m_error;
102 }
103
104}; // class gdal_error
105
106namespace detail
107{
108
110{
112 {
113 GDALAllRegister();
114 }
115};
116
118{
120 {
121 static init_wrapper iw;
122 }
123};
124
125class Driver : private init_library
126{
127
128 gdal_driver_type *m_driver;
129
130 public:
131 Driver(const std::string &driver_name)
132 : init_library(), m_driver(GetGDALDriverManager()->GetDriverByName(driver_name.c_str()))
133 {
134 if (!m_driver)
135 {
136 throw gdal_error(std::string("unknown driver: '") + driver_name + "'", OGRERR_NONE, driver_name);
137 }
138 }
139
140 gdal_driver_type &get() const
141 {
142 return *m_driver;
143 }
144
145}; // struct Driver
146
148{
149
150 std::vector<std::string> m_options;
151 std::unique_ptr<const char *[]> m_ptrs;
152
153 Options(const std::vector<std::string> &options) : m_options(options), m_ptrs(new const char *[options.size() + 1])
154 {
155 std::transform(m_options.begin(), m_options.end(), m_ptrs.get(),
156 [&](const std::string &s) { return s.data(); });
157 m_ptrs[options.size()] = nullptr;
158 }
159
160 char **get() const
161 {
162 return const_cast<char **>(m_ptrs.get());
163 }
164
165}; // struct Options
166
167} // namespace detail
168
169class SRS
170{
171
172 OGRSpatialReference m_spatial_reference;
173
174 public:
175 SRS() : m_spatial_reference()
176 {
177 auto result = m_spatial_reference.SetWellKnownGeogCS("WGS84");
178 if (result != OGRERR_NONE)
179 {
180 throw gdal_error(std::string("can not initialize spatial reference system WGS84"), result);
181 }
182 }
183
184 explicit SRS(int epsg) : m_spatial_reference()
185 {
186 auto result = m_spatial_reference.importFromEPSG(epsg);
187 if (result != OGRERR_NONE)
188 {
189 throw gdal_error(
190 std::string("can not initialize spatial reference system for EPSG:") + std::to_string(epsg), result);
191 }
192 }
193
194 explicit SRS(const char *name) : m_spatial_reference()
195 {
196 auto result = m_spatial_reference.importFromProj4(name);
197 if (result != OGRERR_NONE)
198 {
199 throw gdal_error(std::string("can not initialize spatial reference system '") + name + "'", result);
200 }
201 }
202
203 explicit SRS(const std::string &name) : m_spatial_reference()
204 {
205 auto result = m_spatial_reference.importFromProj4(name.c_str());
206 if (result != OGRERR_NONE)
207 {
208 throw gdal_error(std::string("can not initialize spatial reference system '") + name + "'", result);
209 }
210 }
211
212 explicit SRS(const OGRSpatialReference &spatial_reference) : m_spatial_reference(spatial_reference)
213 {
214 }
215
216 OGRSpatialReference &get()
217 {
218 return m_spatial_reference;
219 }
220
221 const OGRSpatialReference &get() const
222 {
223 return m_spatial_reference;
224 }
225
226}; // class SRS
227
229{
230
231 struct gdal_dataset_deleter
232 {
233
234 void operator()(gdal_dataset_type *ds)
235 {
236 GDALClose(ds);
237 }
238
239 }; // struct gdal_dataset_deleter
240
241 GDALDataset *raster_read_only(const std::string &dataset_name)
242 {
243 GDALAllRegister();
244 return static_cast<GDALDataset *>(GDALOpen(dataset_name.c_str(), GA_ReadOnly));
245 }
246
247 std::string m_driver_name;
248 std::string m_dataset_name;
249 detail::Options m_options;
250 SRS m_srs;
251 mutable std::unique_ptr<gdal_dataset_type, gdal_dataset_deleter> m_dataset;
252 uint64_t m_edit_count = 0;
253 uint64_t m_max_edit_count = 0;
254
255 public:
257 Dataset(const std::string &driver_name, const std::string &dataset_name, const SRS &srs = SRS{},
258 const std::vector<std::string> &options = {})
259 : m_driver_name(driver_name), m_dataset_name(dataset_name), m_options(options), m_srs(srs),
260 m_dataset(
261 detail::Driver(driver_name).get().Create(dataset_name.c_str(), 0, 0, 0, GDT_Unknown, m_options.get()))
262 {
263
264 if (!m_dataset)
265 {
266 throw gdal_error(std::string("failed to create dataset '") + dataset_name + "'", OGRERR_NONE, driver_name,
267 dataset_name);
268 }
269 }
270
272 Dataset(const std::string &dataset_name, const SRS &srs = SRS{}, const std::vector<std::string> &options = {})
273 : m_driver_name(), m_dataset_name(dataset_name), m_options(options), m_srs(srs),
274 m_dataset(raster_read_only(dataset_name))
275 {
276 if (!m_dataset)
277 {
278 throw gdal_error(std::string("failed to create dataset '") + dataset_name + "'", OGRERR_NONE, m_driver_name,
279 dataset_name);
280 }
281 m_driver_name = std::string(m_dataset->GetDriverName());
282 }
283
284 ~Dataset()
285 {
286 try
287 {
288 if (m_edit_count > 0)
289 {
290 commit_transaction();
291 }
292 }
293 catch (...)
294 {
295 }
296 }
297
299 unsigned int width() const
300 {
301 return m_dataset->GetRasterXSize();
302 }
303
305 unsigned int height() const
306 {
307 return m_dataset->GetRasterYSize();
308 }
309
311 unsigned int depth() const
312 {
313 return m_dataset->GetRasterCount();
314 }
315
317 auto &band(unsigned int i) const
318 {
319 assert(i >= 1 and i <= depth());
320 return *(m_dataset->GetRasterBand(i));
321 }
322
325 {
326 std::vector<double> v;
327 double g[6];
328 if (m_dataset->GetGeoTransform(g) == CE_None)
329 {
330 v.assign(std::begin(g), std::end(g));
331 }
332 else
333 {
334 throw gdal_error(std::string("failed to get affine transformation coefficients in dataset '") +
335 m_dataset_name + "'",
336 OGRERR_NONE, m_driver_name, m_dataset_name);
337 }
338 return v;
339 }
340
341 const std::string &driver_name() const
342 {
343 return m_driver_name;
344 }
345
346 const std::string &dataset_name() const
347 {
348 return m_dataset_name;
349 }
350
351 gdal_dataset_type &get() const
352 {
353 return *m_dataset;
354 }
355
356 SRS &srs()
357 {
358 return m_srs;
359 }
360
361 void exec(const char *sql)
362 {
363 auto result = m_dataset->ExecuteSQL(sql, nullptr, nullptr);
364 if (result)
365 {
366 m_dataset->ReleaseResultSet(result);
367 }
368 }
369
370 void exec(const std::string &sql)
371 {
372 exec(sql.c_str());
373 }
374
375 Dataset &start_transaction()
376 {
377 m_dataset->StartTransaction();
378 return *this;
379 }
380
381 Dataset &commit_transaction()
382 {
383 m_dataset->CommitTransaction();
384
385 m_edit_count = 0;
386 return *this;
387 }
388
389 void prepare_edit()
390 {
391 if (m_max_edit_count != 0 && m_edit_count == 0)
392 {
393 start_transaction();
394 }
395 }
396
397 void finalize_edit()
398 {
399 if (m_max_edit_count != 0 && ++m_edit_count > m_max_edit_count)
400 {
401 commit_transaction();
402 }
403 }
404
405 Dataset &enable_auto_transactions(uint64_t edits = 100000)
406 {
407 m_max_edit_count = edits;
408 return *this;
409 }
410
411 Dataset &disable_auto_transactions()
412 {
413 if (m_max_edit_count != 0 && m_edit_count > 0)
414 {
415 commit_transaction();
416 }
417 m_max_edit_count = 0;
418 return *this;
419 }
420}; // class Dataset
421
422class Layer
423{
424
425 detail::Options m_options;
426 Dataset &m_dataset;
427 OGRLayer *m_layer;
428
429 public:
430 Layer(Dataset &dataset, const std::string &layer_name, OGRwkbGeometryType type,
431 const std::vector<std::string> &options = {})
432 : m_options(options), m_dataset(dataset),
433 m_layer(dataset.get().CreateLayer(layer_name.c_str(), &dataset.srs().get(), type, m_options.get()))
434 {
435 if (!m_layer)
436 {
437 throw gdal_error(std::string("failed to create layer '") + layer_name + "'", OGRERR_NONE,
438 dataset.driver_name(), dataset.dataset_name(), layer_name);
439 }
440 }
441
442 OGRLayer &get()
443 {
444 return *m_layer;
445 }
446
447 const OGRLayer &get() const
448 {
449 return *m_layer;
450 }
451
452 Dataset &dataset() const
453 {
454 return m_dataset;
455 }
456
457 const char *name() const
458 {
459 return m_layer->GetName();
460 }
461
462 Layer &add_field(const std::string &field_name, OGRFieldType type, int width, int precision = 0)
463 {
464 OGRFieldDefn field(field_name.c_str(), type);
465 field.SetWidth(width);
466 field.SetPrecision(precision);
467
468 if (m_layer->CreateField(&field) != OGRERR_NONE)
469 {
470 throw gdal_error(std::string("failed to create field '") + field_name + "' in layer '" + name() + "'",
471 OGRERR_NONE, m_dataset.driver_name(), m_dataset.dataset_name(), name(), field_name);
472 }
473
474 return *this;
475 }
476
477 void create_feature(OGRFeature *feature)
478 {
479 dataset().prepare_edit();
480 OGRErr result = m_layer->CreateFeature(feature);
481 if (result != OGRERR_NONE)
482 {
483 throw gdal_error(std::string("creating feature in layer '") + name() + "' failed", result,
484 dataset().driver_name(), dataset().dataset_name());
485 }
486 dataset().finalize_edit();
487 }
488
489 Layer &start_transaction()
490 {
491 return *this;
492 }
493
494 Layer &commit_transaction()
495 {
496 return *this;
497 }
498
499}; // class Layer
500
502{
503
504 struct ogr_feature_deleter
505 {
506
507 void operator()(OGRFeature *feature)
508 {
509 OGRFeature::DestroyFeature(feature);
510 }
511
512 }; // struct ogr_feature_deleter
513
514 Layer &m_layer;
515 std::unique_ptr<OGRFeature, ogr_feature_deleter> m_feature;
516
517 public:
518 Feature(Layer &layer, std::unique_ptr<OGRGeometry> &&geometry)
519 : m_layer(layer), m_feature(OGRFeature::CreateFeature(m_layer.get().GetLayerDefn()))
520 {
521 if (!m_feature)
522 {
523 throw std::bad_alloc();
524 }
525 OGRErr result = m_feature->SetGeometryDirectly(geometry.release());
526 if (result != OGRERR_NONE)
527 {
528 throw gdal_error(std::string("setting feature geometry in layer '") + m_layer.name() + "' failed", result,
529 m_layer.dataset().driver_name(), m_layer.dataset().dataset_name());
530 }
531 }
532
533 void add_to_layer()
534 {
535 m_layer.create_feature(m_feature.get());
536 }
537
538 template <class T> Feature &set_field(int n, T &&arg)
539 {
540 m_feature->SetField(n, std::forward<T>(arg));
541 return *this;
542 }
543
544 template <class T> Feature &set_field(const char *name, T &&arg)
545 {
546 m_feature->SetField(name, std::forward<T>(arg));
547 return *this;
548 }
549
550}; // class Feature
551
552} // namespace quetzal::geography::gdalcpp
553
554#endif // GDALCPP_HPP
Definition gdalcpp.hpp:229
auto & band(unsigned int i) const
Fetch a band object for a dataset, zeroth-based numbering.
Definition gdalcpp.hpp:317
unsigned int depth() const
Fetch the number of raster bands on this dataset.
Definition gdalcpp.hpp:311
auto affine_transformation_coefficients()
Fetch the affine transformation coefficients.
Definition gdalcpp.hpp:324
Dataset(const std::string &driver_name, const std::string &dataset_name, const SRS &srs=SRS{}, const std::vector< std::string > &options={})
Vector constructor.
Definition gdalcpp.hpp:257
unsigned int height() const
Fetch raster height in pixels.
Definition gdalcpp.hpp:305
unsigned int width() const
Fetch raster width in pixels.
Definition gdalcpp.hpp:299
Dataset(const std::string &dataset_name, const SRS &srs=SRS{}, const std::vector< std::string > &options={})
Raster constructor.
Definition gdalcpp.hpp:272
Definition gdalcpp.hpp:502
Definition gdalcpp.hpp:423
Definition gdalcpp.hpp:170