518 lines
16 KiB
C++
518 lines
16 KiB
C++
#ifndef GDALCPP_HPP
|
|
#define GDALCPP_HPP
|
|
|
|
/*
|
|
|
|
C++11 wrapper classes for GDAL/OGR.
|
|
|
|
Version 1.2.0
|
|
|
|
https://github.com/joto/gdalcpp
|
|
|
|
Copyright 2015-2018 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 <gdal_priv.h>
|
|
#include <gdal_version.h>
|
|
#include <ogr_api.h>
|
|
#include <ogrsf_frmts.h>
|
|
|
|
#include <cstdint>
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
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 noexcept {
|
|
return m_driver;
|
|
}
|
|
|
|
const std::string& dataset() const noexcept {
|
|
return m_dataset;
|
|
}
|
|
|
|
const std::string& layer() const noexcept {
|
|
return m_layer;
|
|
}
|
|
|
|
const std::string& field() const noexcept {
|
|
return m_field;
|
|
}
|
|
|
|
OGRErr error() const noexcept {
|
|
return m_error;
|
|
}
|
|
|
|
}; // class gdal_error
|
|
|
|
namespace detail {
|
|
|
|
struct init_wrapper {
|
|
#if GDAL_VERSION_MAJOR >= 2
|
|
init_wrapper() noexcept {
|
|
GDALAllRegister();
|
|
}
|
|
#else
|
|
init_wrapper() noexcept {
|
|
OGRRegisterAll();
|
|
}
|
|
~init_wrapper() noexcept {
|
|
OGRCleanupAll();
|
|
}
|
|
#endif
|
|
}; // struct init_wrapper
|
|
|
|
struct init_library {
|
|
|
|
init_library() {
|
|
static init_wrapper iw;
|
|
}
|
|
|
|
}; // struct init_library
|
|
|
|
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 noexcept {
|
|
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 noexcept {
|
|
return const_cast<char**>(m_ptrs.get());
|
|
}
|
|
|
|
}; // struct Options
|
|
|
|
} // namespace detail
|
|
|
|
class SRS {
|
|
|
|
OGRSpatialReference m_spatial_reference;
|
|
|
|
public:
|
|
|
|
SRS() :
|
|
m_spatial_reference() {
|
|
const 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() {
|
|
const 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() {
|
|
const 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() {
|
|
const 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() noexcept {
|
|
return m_spatial_reference;
|
|
}
|
|
|
|
const OGRSpatialReference& get() const noexcept {
|
|
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() noexcept {
|
|
try {
|
|
if (m_edit_count > 0) {
|
|
commit_transaction();
|
|
}
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
const std::string& driver_name() const noexcept {
|
|
return m_driver_name;
|
|
}
|
|
|
|
const std::string& dataset_name() const noexcept {
|
|
return m_dataset_name;
|
|
}
|
|
|
|
gdal_dataset_type& get() const noexcept {
|
|
return *m_dataset;
|
|
}
|
|
|
|
SRS& srs() noexcept {
|
|
return m_srs;
|
|
}
|
|
|
|
void exec(const char* sql) {
|
|
const 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) noexcept {
|
|
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() noexcept {
|
|
return *m_layer;
|
|
}
|
|
|
|
const OGRLayer& get() const noexcept {
|
|
return *m_layer;
|
|
}
|
|
|
|
Dataset& dataset() const noexcept {
|
|
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();
|
|
const auto 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
|
|
const auto 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
|
|
const auto 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{};
|
|
}
|
|
const auto 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 <typename T>
|
|
Feature& set_field(int n, T&& arg) {
|
|
m_feature->SetField(n, std::forward<T>(arg));
|
|
return *this;
|
|
}
|
|
|
|
template <typename 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
|