Commit c2e360a7 authored by Luc Hermitte's avatar Luc Hermitte

ENH: encapsulation of OGR (+loop on layer features)

parent 226779aa
......@@ -25,10 +25,11 @@ ENDIF(NOT OTB_INSTALL_NO_LIBRARIES)
# Note: no txx allowed here
IF(NOT OTB_INSTALL_NO_DEVELOPMENT)
FILE(GLOB __files1 "${CMAKE_CURRENT_SOURCE_DIR}/*.h")
# FILE(GLOB __files2 "${CMAKE_CURRENT_SOURCE_DIR}/*.txx")
FILE(GLOB __files2 "${CMAKE_CURRENT_SOURCE_DIR}/*.txx")
# FILE(GLOB __files3 "${CMAKE_CURRENT_BINARY_DIR}/*.h")
# INSTALL(FILES ${__files1} ${__files2} ${__files3}
INSTALL(FILES ${__files1}
# INSTALL(FILES ${__files1}
INSTALL(FILES ${__files1} ${__files2}
DESTINATION ${OTB_INSTALL_INCLUDE_DIR_CM24}/UtilitiesAdapters/OGRAdapters
COMPONENT Development)
ENDIF(NOT OTB_INSTALL_NO_DEVELOPMENT)
......@@ -199,12 +199,57 @@ OGRLayer* otb::ogr::DataSource::GetLayerUnchecked(size_t i)
return layer_ptr;
}
otb::ogr::Layer otb::ogr::DataSource::GetLayer(std::string const& name)
{
assert(m_DataSource && "Datasource not initialized");
OGRLayer * layer_ptr = m_DataSource->GetLayerByName(name.c_str());
return otb::ogr::Layer(layer_ptr);
}
otb::ogr::Layer otb::ogr::DataSource::GetLayerChecked(std::string const& name)
{
assert(m_DataSource && "Datasource not initialized");
OGRLayer * layer_ptr = m_DataSource->GetLayerByName(name.c_str());
if (!layer_ptr)
{
itkExceptionMacro( << "Cannot fetch any layer named <" << name
<< "> in the OGRDataSource <" << m_DataSource->GetName() << ">.");
}
return otb::ogr::Layer(layer_ptr);
}
int otb::ogr::DataSource::GetLayersCount() const
{
assert(m_DataSource && "Datasource not initialized");
return m_DataSource->GetLayerCount();
}
otb::ogr::Layer otb::ogr::DataSource::ExecuteSQL(
std::string const& statement,
OGRGeometry * poSpatialFilter,
char const* pszDialect)
{
assert(m_DataSource && "Datasource not initialized");
OGRLayer * layer_ptr = m_DataSource->ExecuteSQL(
statement.c_str(), poSpatialFilter, pszDialect);
if (!layer_ptr)
{
#if defined(PREFER_EXCEPTION)
itkExceptionMacro( << "Unexpected error: cannot execute the SQL request <" << statement
<< "> in the OGRDataSource <" << m_DataSource->GetName() << ">.");
#else
// Cannot use the deleter made for result sets obtained from
// OGRDataSource::ExecuteSQL because it checks for non-nullity....
// *sigh*
return otb::ogr::Layer(0);
#endif
}
return otb::ogr::Layer(layer_ptr, m_DataSource);
}
/*===========================================================================*/
/*===============================[ features ]================================*/
/*===========================================================================*/
......@@ -245,3 +290,20 @@ void otb::ogr::DataSource::PrintSelf(
assert(! "Disabled to check if it makes sense...");
}
bool otb::ogr::DataSource::HasCapability(std::string const& capabilityName)
{
assert(m_DataSource && "Datasource not initialized");
return m_DataSource->TestCapability(capabilityName.c_str());
}
void otb::ogr::DataSource::SyncToDisk()
{
assert(m_DataSource && "Datasource not initialized");
const OGRErr res= m_DataSource->SyncToDisk();
if (res != OGRERR_NONE)
{
itkExceptionMacro( << "Cannot flush the pending of the OGRDataSource <"
<< m_DataSource->GetName() << ">.");
}
}
......@@ -32,6 +32,7 @@
class OGRDataSource;
class OGRLayer;
class OGRSpatialReference;
class OGRGeometry;
#include "ogr_core.h" // OGRwkbGeometryType
namespace otb { namespace ogr {
......@@ -149,11 +150,16 @@ namespace otb { namespace ogr {
* an encapsulation of OGR classes. In that particular case, it's an
* encapsulation of \c OGRDataSource.
*
* \note not meant to be inherited
* \note this class has an entity semantics: \em non-copyable, nor \em
* \note Not meant to be inherited
* \note This class has an entity semantics: \em non-copyable, nor \em
* assignable.
* \note \c OGRRegisterAll() is implicitly called on construction
* \internal as the class is not meant to be inherited, no new function is virtual.
* \internal As the class is not meant to be inherited, no new function is virtual.
*
* \note The following function haven't been encapsulated (yet?):
* - \c SetStyleTable() & \c GetStyleTable()
* - \c SetDriver() & \c GetDriver()
* - all functions related to the reference count.
*/
class DataSource : public itk::DataObject
{
......@@ -281,9 +287,13 @@ namespace otb { namespace ogr {
/**
* Creates a new layer.
* \param[in] name name for the layer
* \param poSpatialRef the coordinate system to use for the new layer, or NULL if no coordinate system is available.
* \param[in] eGType the geometry type for the layer. Use wkbUnknown if there are no constraints on the types geometry to be written.
* \param[in] papszOptions a StringList of name=value options. Options are driver specific.
* \param poSpatialRef the coordinate system to use for the new layer,
* or NULL if no coordinate system is available.
* \param[in] eGType the geometry type for the layer. Use wkbUnknown
* if there are no constraints on the types
* geometry to be written.
* \param[in] papszOptions a StringList of name=value options. Options are
* driver specific.
*
* \return a proxy on the \c OGRLayer created.
* \throw itk::ExceptionObject in case the layer cannot be created on the
......@@ -353,7 +363,7 @@ namespace otb { namespace ogr {
/**
* Unchecked Accessor to a given layer.
* \param[in] i index of the layer to access
* \return a reference to the layer requested.
* \return the layer requested.
* \pre <tt>i < GetLayersCount()</tt>, an assertion will abort the program
* otherwise.
* \pre the layer must available, an assertion will abort the program
......@@ -362,11 +372,24 @@ namespace otb { namespace ogr {
* \note Use \c GetLayerUnchecked() if invalid indices are programming
* errors, or if null layers are to be expected.
*/
Layer GetLayer(size_t i);
/**\copydoc otb::ogr::DataSource::GetLayer()
Layer GetLayer(size_t i);
/**\copydoc otb::ogr::DataSource::GetLayer(size_t)
*/
Layer const GetLayer(size_t i) const;
/**
* Unchecked Accessor to a given layer.
* \param[in] name name of the layer to search
* \return the layer requested, possibly a null one.
* \throw None
* \note Use \c GetLayerUnchecked(std::string const&) if you'd rather have
* an exception instead of testing whether the layer obtained is valid.
*/
Layer GetLayer(std::string const& name);
/**\copydoc otb::ogr::DataSource::GetLayer(std::string const&)
*/
Layer const GetLayer(std::string const& name) const;
/**
* Checked Accessor to a given layer.
* \param[in] i index of the layer to access
......@@ -381,6 +404,44 @@ namespace otb { namespace ogr {
/**\copydoc otb::ogr::DataSource::GetLayerChecked()
*/
Layer const GetLayerChecked(size_t i) const;
/**
* Checked Accessor to a given layer.
* \param[in] name name of the layer to search
* \return the layer requested, possibly a null one.
* \throw itk::ExceptionObject if there exist no layer by that name
* \note use \c GetLayer(std::string const&) if you'd rather test the
* obtained layer instead of catching an exception.
*/
Layer GetLayerChecked(std::string const& name);
/**\copydoc otb::ogr::DataSource::GetLayerChecked(std::string const&)
*/
Layer const GetLayerChecked(std::string const& name) const;
/**
* Excecutes the statement..
* \param[in] statement textual description of the SQL statement.
* \param[in] poSpatialFilter \c Geometry representing a spatial filter -- may be null.
* \param[in] pszDialect allows control of the statement dialect. If set to
* NULL, the OGR SQL engine will be used, except for
* RDBMS drivers that will use their dedicated SQL
* engine, unless OGRSQL is explicitely passed as the
* dialect.
* \return a new \c Layer that contains the matching \c Features. In case of
* error, or no matching result sets, a \em null Layer will be returned.
* Check for \¢ Layer's validity before doing anything else.
* \throw None even when there is an error -- OGR can not report errors,
* neither this wrapping.
* \note the returned \c Layer will be automatically collected on its
* destruction ; i.e. unlike OGR API, no need to explicitly call \c
* OGRDataSource::ReleaseResultSet().
* \sa OGRDataSource::ExecuteSQL
*/
Layer ExecuteSQL(
std::string const& statement,
OGRGeometry * poSpatialFilter,
char const* pszDialect);
//@}
......@@ -391,9 +452,25 @@ namespace otb { namespace ogr {
* boolean expression to be used in \c if tests.
* \see <em>Imperfect C++</em>, Matthew Wilson, Addisson-Welsey, par 24.6
*/
operator int boolean ::* () const {
operator int boolean ::* () const
{
return m_DataSource ? &boolean::i : 0;
}
}
/**
* Flushes all changes to disk.
* \throw itd::ExceptionObject in case the flush operation failed.
* \sa OGRDataSource::SyncToDisk
*/
void SyncToDisk();
/**
* Returns whether a capability is avalaible.
* \param[in] capabilityName name of the capability to check.
* \throw None
* \sa OGRDataSource::TestCapability
*/
bool HasCapability(std::string const& capabilityName);
/** Access to raw \c OGRDataSource.
* This function provides an abstraction leak in case deeper control on the
......
......@@ -52,6 +52,13 @@ otb::ogr::Layer const otb::ogr::DataSource::GetLayer(size_t i) const
return const_cast <DataSource*>(this)->GetLayer(i);
}
inline
otb::ogr::Layer const otb::ogr::DataSource::GetLayer(std::string const& name) const
{
return const_cast <DataSource*>(this)->GetLayer(name);
}
inline
otb::ogr::Layer const otb::ogr::DataSource::GetLayerChecked(size_t i) const
{
......
/*=========================================================================
Program: ORFEO Toolbox
Language: C++
Date: $Date$
Version: $Revision$
Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
See OTBCopyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
/*===========================================================================*/
/*===============================[ Includes ]================================*/
/*===========================================================================*/
#include "otbOGRFeatureWrapper.h"
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
#include "ogr_feature.h"
/*===========================================================================*/
/*======================[ Construction / Destruction ]=======================*/
/*===========================================================================*/
otb::ogr::Feature::Feature(OGRFeatureDefn & definition)
: m_Feature(
OGRFeature::CreateFeature(&definition),
boost::bind(&OGRFeature::DestroyFeature, _1))
{
CheckInvariants();
}
otb::ogr::Feature::Feature(OGRFeature * feature)
{
if (feature)
{
m_Feature.reset(feature, boost::bind(&OGRFeature::DestroyFeature, _1));
}
// else default is perfect -> delete null
}
otb::ogr::Feature::~Feature()
{
}
otb::ogr::Feature otb::ogr::Feature::clone() const
{
CheckInvariants();
const Feature res(m_Feature->Clone());
return res;
}
/*===========================================================================*/
/*=================================[ Misc ]==================================*/
/*===========================================================================*/
void otb::ogr::Feature::PrintSelf(std::ostream & os, itk::Indent indent) const
{
CheckInvariants();
os << indent << "+";
os << " " << m_Feature->GetFieldCount() << " fields\n";
}
bool otb::ogr::operator==(otb::ogr::Feature const& lhs, otb::ogr::Feature const& rhs)
{
// special case: they may be null (end() mark)
// OGR is not const correct ...
OGRFeature * l = const_cast<OGRFeature*>(lhs.m_Feature.get());
OGRFeature * r = const_cast<OGRFeature*>(rhs.m_Feature.get());
return
(l == r ) // incl. ==0
||
(l && r && l->Equal(r)) // must be non-null to compare them with Equal
;
}
/*=========================================================================
Program: ORFEO Toolbox
Language: C++
Date: $Date$
Version: $Revision$
Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
See OTBCopyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#ifndef __otbOGRFeatureWrapper_h
#define __otbOGRFeatureWrapper_h
// #include <iosfwd> // std::ostream&
#include <cassert>
#include <boost/shared_ptr.hpp>
#include "itkIndent.h"
class OGRFeature;
class OGRGeometry;
class OGRFeatureDefn;
namespace otb {
namespace ogr {
class Feature;
bool operator==(Feature const& lhs, Feature const& rhs);
/**\ingroup Geometry
* \class Feature
* \invariant <tt>m_Feature != 0</tt>
*/
class Feature
{
public:
// no default constructor
Feature(OGRFeatureDefn & definition);
Feature(OGRFeature * feature);
~Feature();
Feature clone() const;
void PrintSelf(std::ostream &os, itk::Indent indent) const;
OGRFeature const& ogr() const
{
CheckInvariants();
return *m_Feature;
}
OGRFeature & ogr()
{
CheckInvariants();
return *m_Feature;
}
friend bool otb::ogr::operator==(Feature const& lhs, Feature const& rhs);
private:
void CheckInvariants() const
{
assert(m_Feature && "OGRFeature can't be null");
}
boost::shared_ptr<OGRFeature> m_Feature;
};
} // ogr namespace
} // otb namespace
#ifndef OTB_MANUAL_INSTANTIATION
// #include "otbOGRFeatureWrapper.txx"
#endif
#endif // __otbOGRFeatureWrapper_h
......@@ -22,12 +22,12 @@
#include "otbOGRLayerWrapper.h"
#include <cassert>
#include <boost/bind.hpp>
#include <boost/range/algorithm.hpp>
#include "ogrsf_frmts.h" // OGRDataSource & OGRLayer
/*===========================================================================*/
/*==============================[ other stuff ]==============================*/
/*======================[ Construction & Destruction ]=======================*/
/*===========================================================================*/
namespace { // Anonymous namespace
/**\ingroup Geometry
* Deleter for \c boost::shared_ptr<> that doesn't delete.
......@@ -53,12 +53,62 @@ otb::ogr::Layer::Layer(OGRLayer* layer, OGRDataSource* sourceInChargeOfLifeTime)
// OGR always refuses "delete 0". *sigh*
}
/*===========================================================================*/
/*===============================[ Features ]================================*/
/*===========================================================================*/
int otb::ogr::Layer::GetFeatureCount(bool doForceComputation) const
{
assert(m_Layer);
return m_Layer->GetFeatureCount(doForceComputation);
}
otb::ogr::Feature otb::ogr::Layer::GetNextFeature()
{
assert(m_Layer && "OGRLayer not initialized");
return m_Layer->GetNextFeature();
}
otb::ogr::Layer::iterator otb::ogr::Layer::begin()
{
assert(m_Layer && "OGRLayer not initialized");
m_Layer->ResetReading();
return iterator(*this);
}
otb::ogr::Layer::const_iterator otb::ogr::Layer::cbegin() const
{
assert(m_Layer && "OGRLayer not initialized");
m_Layer->ResetReading();
return const_iterator(*const_cast <Layer*>(this));
}
void otb::ogr::Layer::CreateFeature(Feature feature)
{
assert(m_Layer && "OGRLayer not initialized");
m_Layer->CreateFeature(&feature.ogr());
}
void otb::ogr::Layer::DeleteFeature(long nFID)
{
assert(m_Layer && "OGRLayer not initialized");
m_Layer->DeleteFeature(nFID);
}
otb::ogr::Feature otb::ogr::Layer::GetFeature(long nFID)
{
assert(m_Layer && "OGRLayer not initialized");
Feature feat = m_Layer->GetFeature(nFID);
}
void otb::ogr::Layer::SetFeature(Feature feature)
{
assert(m_Layer && "OGRLayer not initialized");
m_Layer->SetFeature(&feature.ogr());
}
/*===========================================================================*/
/*=================================[ Misc ]==================================*/
/*===========================================================================*/
std::string otb::ogr::Layer::GetName() const
{
assert(m_Layer && "null layer");
......@@ -76,8 +126,10 @@ void otb::ogr::Layer::PrintSelf(std::ostream& os, itk::Indent indent) const
os << indent << "+";
if (m_Layer) // in case for odd reason the layer that should exist can't be found
{
os << "Layer <" << m_Layer->GetName() << "\n";
// + for each feature
os << "Layer <" << m_Layer->GetName() << ">\n";
boost::for_each( // for each feature
*this,
boost::bind(&Feature::PrintSelf, _1, boost::ref(os), indent.GetNextIndent()));
}
else
{
......@@ -85,3 +137,21 @@ void otb::ogr::Layer::PrintSelf(std::ostream& os, itk::Indent indent) const
}
}
/*===========================================================================*/
/*============================[ Spatial Filter ]=============================*/
/*===========================================================================*/
OGRGeometry const* otb::ogr::Layer::GetSpatialFilter() const
{
assert(m_Layer && "OGRLayer not initialized");
OGRGeometry* spatialFilter = m_Layer->GetSpatialFilter();
return spatialFilter;
}
void otb::ogr::Layer::SetSpatialFilter(OGRGeometry const* spatialFilter)
{
assert(m_Layer && "OGRLayer not initialized");
// const_cast because OGR is not 100% const-correct
m_Layer->SetSpatialFilter(const_cast <OGRGeometry*>(spatialFilter));
}
/*=========================================================================
Program: ORFEO Toolbox
Language: C++
Date: $Date$
Version: $Revision$
Program: ORFEO Toolbox
Language: C++
Date: $Date$
Version: $Revision$
Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
See OTBCopyright.txt for details.
Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
See OTBCopyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#ifndef __otbLayer_h
......@@ -20,14 +20,19 @@
// #include <iosfwd> // std::ostream&
#include <boost/shared_ptr.hpp>
// #include <boost/iterator/iterator_adaptor.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/utility/enable_if.hpp>
#include "itkIndent.h"
class OGRLayer; // fwd declaration
#include "otbOGRFeatureWrapper.h"
class OGRLayer; // fwd declarations
class OGRDataSource;
class OGRGeometry;
namespace otb { namespace ogr
{
namespace otb { namespace ogr {
/**\ingroup Geometry
* \brief Layer of geometric objets.
* \class Layer
* \brief %Layer of geometric objets.
*
* It provides an encapsulation of OGR classes. In that particular case, it's an
* encapsulation of \c OGRLayer.
......@@ -43,7 +48,7 @@ namespace otb { namespace ogr
* \todo find a way to tell whether the related \c OGRDataSource was released
*/
class Layer
// : public itk::DataObject
// : public itk::DataObject
{
public:
/**\name Standard class typedefs */
......@@ -65,6 +70,9 @@ public:
explicit Layer(OGRLayer* layer);
Layer(OGRLayer* layer, OGRDataSource* sourceInChargeOfLifeTime);
//@}
/**\name Features collection */
//@{
/** Returns the number of elements in the layer.
* \param[in] doForceCompuation indicates whether the size shall be computed
* even so it's expensive to do so.
......@@ -75,12 +83,22 @@ public:
*/
int GetFeatureCount(bool doForceComputation) const;
void CreateFeature(Feature feature);
void DeleteFeature(long nFID);
Feature GetFeature(long nFID);
void SetFeature(Feature feature);
//@}
/** Returns the name given to the layer, if any.
*/
std::string GetName() const;
/** Prints self into stream. */
void PrintSelf(std::ostream& os, itk::Indent indent) const;
/**\copydoc operator int boolean ::* () const
*/
struct boolean{ int i; };
/** Can the layer be used (ie not null).
*
......@@ -88,9 +106,10 @@ public:
* boolean expression to be used in \c if tests.
* @see <em>Imperfect C++</em>, Matthew Wilson, Addisson-Welsey, par 24.6
*/
operator int boolean ::* () const {
operator int boolean ::* () const
{
return m_Layer ? &boolean::i : 0;
}
}
/** Access to raw \c OGRLayer.
* This function provides an abstraction leak in case deeper control on the
......@@ -102,10 +121,72 @@ public:
*/
OGRLayer & ogr();
/**\name Spatial filter property
* \internal the I/O geometry is an undeletable pointer, can be may be null.
* \todo we'll see later if a Geometry capsule is defined, or a
* \c nondeletable<> pointer type.
*/
//@{
OGRGeometry const* GetSpatialFilter() const;
void SetSpatialFilter(OGRGeometry const* spatialFilter);
//@}
/**\name Iteration */
//@{
template <class Value> class feature_iter
: public boost::iterator_facade<feature_iter<Value>, Value, boost::single_pass_traversal_tag>
{
struct enabler {};
public:
feature_iter()
: m_Layer(0), m_Crt(0) {}
explicit feature_iter(otb::ogr::Layer & layer)
: m_Layer(&layer), m_Crt(layer.GetNextFeature()) {}
template <class OtherValue> feature_iter(
feature_iter<OtherValue> const& other,
typename boost::enable_if<boost::is_convertible<OtherValue*,Value*>
, enabler
>::type = enabler()
)
: m_Layer(other.m_Layer), m_Crt(other.m_Crt)
{}
private:
friend class boost::iterator_core_access;
template <class> friend class feature_iter;
template <class OtherValue> bool equal(feature_iter<OtherValue> const& other) const
{ return other.m_Crt == m_Crt; }
void increment()
{
assert(m_Layer && "cannot increment end()");
m_Crt = m_Layer->GetNextFeature();
}
Value & dereference() const
{ return m_Crt; }
otb::ogr::Layer * m_Layer;
otb::ogr::Feature m_Crt;
};
template <class> friend class feature_iter;
typedef feature_iter<Feature > iterator;
typedef feature_iter<Feature const> const_iterator;
const_iterator begin() const { return cbegin(); }
const_iterator end () const { return cend (); }
const_iterator cbegin() const ;
const_iterator cend() const { return iterator(); }
iterator begin() ;
iterator end() { return iterator(); }
//@}
private:
Feature GetNextFeature();
/** Data implementation.
* \internal The actual %layer implementation belongs to the \c otb::Layer object.
* \internal
* The actual %layer implementation belongs to the \c otb::Layer object,
* unless this is the result of \c ExecuteSQL. In that case a deleter is set
* to correctly release the layer.
*/
boost::shared_ptr<OGRLayer> m_Layer;
};
......
......@@ -4,7 +4,10 @@ SET(OGR_TESTS ${CXX_TEST_PATH}/otbOGRTests)
SET(OGRCommon_SRC
otbOGRDataSourceWrapperNew.cxx