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