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