478 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			478 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #ifndef GDALCPP_HPP
 | |
| #define GDALCPP_HPP
 | |
| 
 | |
| /*
 | |
| 
 | |
| C++11 wrapper classes for GDAL/OGR.
 | |
| 
 | |
| Version 1.1.1
 | |
| 
 | |
| https://github.com/joto/gdalcpp
 | |
| 
 | |
| Copyright 2015 Jochen Topf <jochen@topf.org>
 | |
| 
 | |
| Boost Software License - Version 1.0 - August 17th, 2003
 | |
| 
 | |
| Permission is hereby granted, free of charge, to any person or organization
 | |
| obtaining a copy of the software and accompanying documentation covered by
 | |
| this license (the "Software") to use, reproduce, display, distribute,
 | |
| execute, and transmit the Software, and to prepare derivative works of the
 | |
| Software, and to permit third-parties to whom the Software is furnished to
 | |
| do so, all subject to the following:
 | |
| 
 | |
| The copyright notices in the Software and this entire statement, including
 | |
| the above license grant, this restriction and the following disclaimer,
 | |
| must be included in all copies of the Software, in whole or in part, and
 | |
| all derivative works of the Software, unless such copies or derivative
 | |
| works are solely in the form of machine-executable object code generated by
 | |
| a source language processor.
 | |
| 
 | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 | |
| SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 | |
| FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 | |
| ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | |
| DEALINGS IN THE SOFTWARE.
 | |
| 
 | |
| */
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <memory>
 | |
| #include <stdexcept>
 | |
| #include <string>
 | |
| #include <vector>
 | |
| 
 | |
| #include <gdal_priv.h>
 | |
| #include <gdal_version.h>
 | |
| #include <ogr_api.h>
 | |
| #include <ogrsf_frmts.h>
 | |
| 
 | |
| namespace gdalcpp {
 | |
| 
 | |
| #if GDAL_VERSION_MAJOR >= 2
 | |
|     using gdal_driver_type = GDALDriver;
 | |
|     using gdal_dataset_type = GDALDataset;
 | |
| #else
 | |
|     using gdal_driver_type = OGRSFDriver;
 | |
|     using gdal_dataset_type = OGRDataSource;
 | |
| #endif
 | |
| 
 | |
|     /**
 | |
|      * Exception thrown for all errors in this class.
 | |
|      */
 | |
|     class gdal_error : public std::runtime_error {
 | |
| 
 | |
|         std::string m_driver;
 | |
|         std::string m_dataset;
 | |
|         std::string m_layer;
 | |
|         std::string m_field;
 | |
|         OGRErr m_error;
 | |
| 
 | |
|     public:
 | |
| 
 | |
|         gdal_error(const std::string& message,
 | |
|                    OGRErr error,
 | |
|                    const std::string& driver = "",
 | |
|                    const std::string& dataset = "",
 | |
|                    const std::string& layer = "",
 | |
|                    const std::string& field = "") :
 | |
|             std::runtime_error(message),
 | |
|             m_driver(driver),
 | |
|             m_dataset(dataset),
 | |
|             m_layer(layer),
 | |
|             m_field(field),
 | |
|             m_error(error) {
 | |
|         }
 | |
| 
 | |
|         const std::string& driver() const {
 | |
|             return m_driver;
 | |
|         }
 | |
| 
 | |
|         const std::string& dataset() const {
 | |
|             return m_dataset;
 | |
|         }
 | |
| 
 | |
|         const std::string& layer() const {
 | |
|             return m_layer;
 | |
|         }
 | |
| 
 | |
|         const std::string& field() const {
 | |
|             return m_field;
 | |
|         }
 | |
| 
 | |
|         OGRErr error() const {
 | |
|             return m_error;
 | |
|         }
 | |
| 
 | |
|     }; // class gdal_error
 | |
| 
 | |
|     namespace detail {
 | |
| 
 | |
|         struct init_wrapper {
 | |
| #if GDAL_VERSION_MAJOR >= 2
 | |
|             init_wrapper() { GDALAllRegister(); }
 | |
| #else
 | |
|             init_wrapper() { OGRRegisterAll(); }
 | |
|             ~init_wrapper() { OGRCleanupAll(); }
 | |
| #endif
 | |
|         };
 | |
| 
 | |
|         struct init_library {
 | |
|             init_library() {
 | |
|                 static init_wrapper iw;
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         class Driver : private init_library {
 | |
| 
 | |
|             gdal_driver_type* m_driver;
 | |
| 
 | |
|         public:
 | |
| 
 | |
|             Driver(const std::string& driver_name) :
 | |
|                 init_library(),
 | |
| #if GDAL_VERSION_MAJOR >= 2
 | |
|                 m_driver(GetGDALDriverManager()->GetDriverByName(driver_name.c_str())) {
 | |
| #else
 | |
|                 m_driver(OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str())) {
 | |
| #endif
 | |
|                 if (!m_driver) {
 | |
|                     throw gdal_error(std::string("unknown driver: '") + driver_name + "'", OGRERR_NONE, driver_name);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             gdal_driver_type& get() const {
 | |
|                 return *m_driver;
 | |
|             }
 | |
| 
 | |
|         }; // struct Driver
 | |
| 
 | |
|         struct Options {
 | |
| 
 | |
|             std::vector<std::string> m_options;
 | |
|             std::unique_ptr<const char*[]> m_ptrs;
 | |
| 
 | |
|             Options(const std::vector<std::string>& options) :
 | |
|                 m_options(options),
 | |
|                 m_ptrs(new const char*[options.size()+1]) {
 | |
|                 std::transform(m_options.begin(), m_options.end(), m_ptrs.get(), [&](const std::string& s) {
 | |
|                     return s.data();
 | |
|                 });
 | |
|                 m_ptrs[options.size()] = nullptr;
 | |
|             }
 | |
| 
 | |
|             char** get() const {
 | |
|                 return const_cast<char**>(m_ptrs.get());
 | |
|             }
 | |
| 
 | |
|         }; // struct Options
 | |
| 
 | |
|     } // namespace detail
 | |
| 
 | |
|     class SRS {
 | |
| 
 | |
|         OGRSpatialReference m_spatial_reference;
 | |
| 
 | |
|     public:
 | |
| 
 | |
|         SRS() :
 | |
|             m_spatial_reference() {
 | |
|             auto result = m_spatial_reference.SetWellKnownGeogCS("WGS84");
 | |
|             if (result != OGRERR_NONE) {
 | |
|                 throw gdal_error(std::string("can not initialize spatial reference system WGS84"), result);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         explicit SRS(int epsg) :
 | |
|             m_spatial_reference() {
 | |
|             auto result = m_spatial_reference.importFromEPSG(epsg);
 | |
|             if (result != OGRERR_NONE) {
 | |
|                 throw gdal_error(std::string("can not initialize spatial reference system for EPSG:") + std::to_string(epsg), result);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         explicit SRS(const char* name) :
 | |
|             m_spatial_reference() {
 | |
|             auto result = m_spatial_reference.importFromProj4(name);
 | |
|             if (result != OGRERR_NONE) {
 | |
|                 throw gdal_error(std::string("can not initialize spatial reference system '") + name + "'", result);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         explicit SRS(const std::string& name) :
 | |
|             m_spatial_reference() {
 | |
|             auto result = m_spatial_reference.importFromProj4(name.c_str());
 | |
|             if (result != OGRERR_NONE) {
 | |
|                 throw gdal_error(std::string("can not initialize spatial reference system '") + name + "'", result);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         explicit SRS(const OGRSpatialReference& spatial_reference) :
 | |
|             m_spatial_reference(spatial_reference) {
 | |
|         }
 | |
| 
 | |
|         OGRSpatialReference& get() {
 | |
|             return m_spatial_reference;
 | |
|         }
 | |
| 
 | |
|         const OGRSpatialReference& get() const {
 | |
|             return m_spatial_reference;
 | |
|         }
 | |
| 
 | |
|     }; // class SRS
 | |
| 
 | |
|     class Dataset {
 | |
| 
 | |
|         struct gdal_dataset_deleter {
 | |
| 
 | |
|             void operator()(gdal_dataset_type* ds) {
 | |
| #if GDAL_VERSION_MAJOR >= 2
 | |
|                 GDALClose(ds);
 | |
| #else
 | |
|                 OGRDataSource::DestroyDataSource(ds);
 | |
| #endif
 | |
|             }
 | |
| 
 | |
|         }; // struct gdal_dataset_deleter
 | |
| 
 | |
|         std::string m_driver_name;
 | |
|         std::string m_dataset_name;
 | |
|         detail::Options m_options;
 | |
|         SRS m_srs;
 | |
|         std::unique_ptr<gdal_dataset_type, gdal_dataset_deleter> m_dataset;
 | |
|         uint64_t m_edit_count = 0;
 | |
|         uint64_t m_max_edit_count = 0;
 | |
| 
 | |
|     public:
 | |
| 
 | |
|         Dataset(const std::string& driver_name, const std::string& dataset_name, const SRS& srs = SRS{}, const std::vector<std::string>& options = {}) :
 | |
|             m_driver_name(driver_name),
 | |
|             m_dataset_name(dataset_name),
 | |
|             m_options(options),
 | |
|             m_srs(srs),
 | |
| #if GDAL_VERSION_MAJOR >= 2
 | |
|             m_dataset(detail::Driver(driver_name).get().Create(dataset_name.c_str(), 0, 0, 0, GDT_Unknown, m_options.get())) {
 | |
| #else
 | |
|             m_dataset(detail::Driver(driver_name).get().CreateDataSource(dataset_name.c_str(), m_options.get())) {
 | |
| #endif
 | |
|             if (!m_dataset) {
 | |
|                 throw gdal_error(std::string("failed to create dataset '") + dataset_name + "'", OGRERR_NONE, driver_name, dataset_name);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         ~Dataset() {
 | |
|             try {
 | |
|                 if (m_edit_count > 0) {
 | |
|                     commit_transaction();
 | |
|                 }
 | |
|             } catch (...) {
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         const std::string& driver_name() const {
 | |
|             return m_driver_name;
 | |
|         }
 | |
| 
 | |
|         const std::string& dataset_name() const {
 | |
|             return m_dataset_name;
 | |
|         }
 | |
| 
 | |
|         gdal_dataset_type& get() const {
 | |
|             return *m_dataset;
 | |
|         }
 | |
| 
 | |
|         SRS& srs() {
 | |
|             return m_srs;
 | |
|         }
 | |
| 
 | |
|         void exec(const char* sql) {
 | |
|             auto result = m_dataset->ExecuteSQL(sql, nullptr, nullptr);
 | |
|             if (result) {
 | |
|                 m_dataset->ReleaseResultSet(result);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void exec(const std::string& sql) {
 | |
|             exec(sql.c_str());
 | |
|         }
 | |
| 
 | |
|         Dataset& start_transaction() {
 | |
| #if GDAL_VERSION_MAJOR >= 2
 | |
|             m_dataset->StartTransaction();
 | |
| #else
 | |
|             OGRLayer* layer = m_dataset->GetLayer(0);
 | |
|             if (layer) {
 | |
|                 layer->StartTransaction();
 | |
|             }
 | |
| #endif
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         Dataset& commit_transaction() {
 | |
| #if GDAL_VERSION_MAJOR >= 2
 | |
|             m_dataset->CommitTransaction();
 | |
| #else
 | |
|             OGRLayer* layer = m_dataset->GetLayer(0);
 | |
|             if (layer) {
 | |
|                 layer->CommitTransaction();
 | |
|             }
 | |
| #endif
 | |
|             m_edit_count = 0;
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         void prepare_edit() {
 | |
|             if (m_max_edit_count != 0 && m_edit_count == 0) {
 | |
|                 start_transaction();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void finalize_edit() {
 | |
|             if (m_max_edit_count != 0 && ++m_edit_count > m_max_edit_count) {
 | |
|                 commit_transaction();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         Dataset& enable_auto_transactions(uint64_t edits = 100000) {
 | |
|             m_max_edit_count = edits;
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         Dataset& disable_auto_transactions() {
 | |
|             if (m_max_edit_count != 0 && m_edit_count > 0) {
 | |
|                 commit_transaction();
 | |
|             }
 | |
|             m_max_edit_count = 0;
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|     }; // class Dataset
 | |
| 
 | |
|     class Layer {
 | |
| 
 | |
|         detail::Options m_options;
 | |
|         Dataset& m_dataset;
 | |
|         OGRLayer* m_layer;
 | |
| 
 | |
|     public:
 | |
| 
 | |
|         Layer(Dataset& dataset, const std::string& layer_name, OGRwkbGeometryType type, const std::vector<std::string>& options = {}) :
 | |
|             m_options(options),
 | |
|             m_dataset(dataset),
 | |
|             m_layer(dataset.get().CreateLayer(layer_name.c_str(), &dataset.srs().get(), type, m_options.get())) {
 | |
|             if (!m_layer) {
 | |
|                 throw gdal_error(std::string("failed to create layer '") + layer_name + "'", OGRERR_NONE,
 | |
|                     dataset.driver_name(), dataset.dataset_name(), layer_name);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         OGRLayer& get() {
 | |
|             return *m_layer;
 | |
|         }
 | |
| 
 | |
|         const OGRLayer& get() const {
 | |
|             return *m_layer;
 | |
|         }
 | |
| 
 | |
|         Dataset& dataset() const {
 | |
|             return m_dataset;
 | |
|         }
 | |
| 
 | |
|         const char* name() const {
 | |
|             return m_layer->GetName();
 | |
|         }
 | |
| 
 | |
|         Layer& add_field(const std::string& field_name, OGRFieldType type, int width, int precision=0) {
 | |
|             OGRFieldDefn field(field_name.c_str(), type);
 | |
|             field.SetWidth(width);
 | |
|             field.SetPrecision(precision);
 | |
| 
 | |
|             if (m_layer->CreateField(&field) != OGRERR_NONE) {
 | |
|                 throw gdal_error(std::string("failed to create field '") + field_name + "' in layer '" + name() + "'", OGRERR_NONE,
 | |
|                     m_dataset.driver_name(), m_dataset.dataset_name(), name(), field_name);
 | |
|             }
 | |
| 
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         void create_feature(OGRFeature* feature) {
 | |
|             dataset().prepare_edit();
 | |
|             OGRErr result = m_layer->CreateFeature(feature);
 | |
|             if (result != OGRERR_NONE) {
 | |
|                 throw gdal_error(std::string("creating feature in layer '") + name() + "' failed", result, dataset().driver_name(), dataset().dataset_name());
 | |
|             }
 | |
|             dataset().finalize_edit();
 | |
|         }
 | |
| 
 | |
|         Layer& start_transaction() {
 | |
| #if GDAL_VERSION_MAJOR < 2
 | |
|             OGRErr result = m_layer->StartTransaction();
 | |
|             if (result != OGRERR_NONE) {
 | |
|                 throw gdal_error(std::string("starting transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name());
 | |
|             }
 | |
| #endif
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         Layer& commit_transaction() {
 | |
| #if GDAL_VERSION_MAJOR < 2
 | |
|             OGRErr result = m_layer->CommitTransaction();
 | |
|             if (result != OGRERR_NONE) {
 | |
|                 throw gdal_error(std::string("committing transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name());
 | |
|             }
 | |
| #endif
 | |
|             return *this;
 | |
|          }
 | |
| 
 | |
|     }; // class Layer
 | |
| 
 | |
|     class Feature {
 | |
| 
 | |
|         struct ogr_feature_deleter {
 | |
| 
 | |
|             void operator()(OGRFeature* feature) {
 | |
|                  OGRFeature::DestroyFeature(feature);
 | |
|             }
 | |
| 
 | |
|         }; // struct ogr_feature_deleter
 | |
| 
 | |
|         Layer& m_layer;
 | |
|         std::unique_ptr<OGRFeature, ogr_feature_deleter> m_feature;
 | |
| 
 | |
|     public:
 | |
| 
 | |
|         Feature(Layer& layer, std::unique_ptr<OGRGeometry>&& geometry) :
 | |
|             m_layer(layer),
 | |
|             m_feature(OGRFeature::CreateFeature(m_layer.get().GetLayerDefn())) {
 | |
|             if (!m_feature) {
 | |
|                 throw std::bad_alloc();
 | |
|             }
 | |
|             OGRErr result = m_feature->SetGeometryDirectly(geometry.release());
 | |
|             if (result != OGRERR_NONE) {
 | |
|                 throw gdal_error(std::string("setting feature geometry in layer '") + m_layer.name() + "' failed", result, m_layer.dataset().driver_name(), m_layer.dataset().dataset_name());
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void add_to_layer() {
 | |
|             m_layer.create_feature(m_feature.get());
 | |
|         }
 | |
| 
 | |
|         template <class T>
 | |
|         Feature& set_field(int n, T&& arg) {
 | |
|             m_feature->SetField(n, std::forward<T>(arg));
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|         template <class T>
 | |
|         Feature& set_field(const char* name, T&& arg) {
 | |
|             m_feature->SetField(name, std::forward<T>(arg));
 | |
|             return *this;
 | |
|         }
 | |
| 
 | |
|     }; // class Feature
 | |
| 
 | |
| } // namespace gdalcpp
 | |
| 
 | |
| #endif // GDALCPP_HPP
 |