Commit 0b3202d2 authored by Luc Hermitte's avatar Luc Hermitte

ENH: encapsulation of OGR (+add/del/copy layer)

parent a2306c95
......@@ -122,36 +122,86 @@ otb::ogr::DataSource::New(OGRDataSource * source)
/*===========================================================================*/
/*================================[ layers ]=================================*/
/*===========================================================================*/
OGRLayer& otb::ogr::DataSource::GetLayerChecked(size_t i)
otb::ogr::Layer otb::ogr::DataSource::CreateLayer(
std::string const& name,
OGRSpatialReference * poSpatialRef/* = NULL */,
OGRwkbGeometryType eGType/* = wkbUnknown */,
char ** papszOptions/* = NULL */)
{
assert(m_DataSource && "Datasource not initialized");
OGRLayer * ol = m_DataSource->CreateLayer(
name.c_str(), poSpatialRef, eGType, papszOptions);
if (!ol)
{
itkGenericExceptionMacro(<< "Failed to create the layer <"<<name
<< "> in the OGRDataSource file " << m_DataSource->GetName());
}
Layer l(ol);
return l;
}
otb::ogr::Layer otb::ogr::DataSource::CopyLayer(
Layer & srcLayer,
std::string const& newName,
char ** papszOptions/* = NULL */)
{
assert(m_DataSource && "Datasource not initialized");
OGRLayer * l0 = &srcLayer.ogr();
OGRLayer * ol = m_DataSource->CopyLayer(l0, newName.c_str(), papszOptions);
if (!ol)
{
itkGenericExceptionMacro(<< "Failed to copy the layer <"
<< srcLayer.GetName() << "> into the new layer <" <<newName
<< "> in the OGRDataSource file " << m_DataSource->GetName());
}
Layer l(ol);
return l;
}
void otb::ogr::DataSource::DeleteLayer(size_t i)
{
const int nb_layers = GetLayersCount();
if (int(i) >= nb_layers)
{
itkExceptionMacro(<< "Cannot fetch " << i << "th layer in the OGRDataSource as it contains only "
<< nb_layers << "layers.");
itkExceptionMacro(<< "Cannot delete " << i << "th layer in the OGRDataSource <"
<< m_DataSource->GetName() << "> as it contains only " << nb_layers << "layers.");
}
OGRLayer * layer_ptr = m_DataSource->GetLayer(int(i));
if (!layer_ptr)
const OGRErr err = m_DataSource->DeleteLayer(int(i));
if (err != OGRERR_NONE)
{
itkExceptionMacro( << "Unexpected error: cannot fetch " << i << "th layer in the OGRDataSource.");
itkExceptionMacro(<< "Cannot delete " << i << "th layer in the OGRDataSource <"
<< m_DataSource->GetName() << ">.");
}
return *layer_ptr;
}
OGRLayer const& otb::ogr::DataSource::GetLayerChecked(size_t i) const
otb::ogr::Layer otb::ogr::DataSource::GetLayerChecked(size_t i)
{
return const_cast <DataSource*>(this)->GetLayerChecked(i);
const int nb_layers = GetLayersCount();
if (int(i) >= nb_layers)
{
itkExceptionMacro(<< "Cannot fetch " << i << "th layer in the OGRDataSource <"
<< m_DataSource->GetName() << "> as it contains only " << nb_layers << "layers.");
}
OGRLayer * layer_ptr = m_DataSource->GetLayer(int(i));
if (!layer_ptr)
{
itkExceptionMacro( << "Unexpected error: cannot fetch " << i << "th layer in the OGRDataSource <"
<< m_DataSource->GetName() << ">.");
}
return otb::ogr::Layer(layer_ptr);
}
OGRLayer* otb::ogr::DataSource::GetLayerUnchecked(size_t i)
{
assert(m_DataSource && "Datasource not initialized");
OGRLayer * layer_ptr = m_DataSource->GetLayer(int(i));
return layer_ptr;
}
int otb::ogr::DataSource::GetLayersCount() const
{
assert(m_DataSource);
assert(m_DataSource && "Datasource not initialized");
return m_DataSource->GetLayerCount();
}
......@@ -163,7 +213,7 @@ namespace { // Anonymous namespace
{
AccuLayersSizes(bool doForceComputation)
: m_doForceComputation(doForceComputation) { }
int operator()(OGRLayer& layer, int accumulated) const
int operator()(otb::ogr::Layer const& layer, int accumulated) const
{
const int loc_size = layer.GetFeatureCount(m_doForceComputation);
return loc_size < 0 ? loc_size : loc_size+accumulated;
......@@ -186,10 +236,12 @@ int otb::ogr::DataSource::Size(bool doForceComputation) const
void otb::ogr::DataSource::PrintSelf(
std::ostream& os, itk::Indent indent) const
{
// ForEachLayer(boost::bind(&OGRLayer::PrintSelf, _1, indent++));
assert(m_DataSource && "Datasource not initialized");
ForEachLayer(boost::bind(&Layer::PrintSelf, _1, boost::ref(os), indent.GetNextIndent()));
}
/*virtual*/ void otb::ogr::DataSource::Graft(const itk::DataObject * data)
{
assert(! "Disabled to check if it makes sense...");
}
......@@ -19,7 +19,7 @@
#define __otbOGRDataSourceWrapper_h
#include <string>
#include <cassert>
#include "itkVector.h"
#include "itkPoint.h"
......@@ -27,9 +27,12 @@
#include "itkMacro.h" // itkNewMacro
#include "itkObjectFactory.h" // that should have been included by itkMacro.h
// class OGRDataSource;
#include "ogrsf_frmts.h" // OGRDataSource
#include "otbOGRLayerWrapper.h"
class OGRDataSource;
class OGRLayer;
class OGRSpatialReference;
#include "ogr_core.h" // OGRwkbGeometryType
namespace otb { namespace ogr {
/**\ingroup Geometry
......@@ -228,23 +231,10 @@ namespace otb { namespace ogr {
*
* \throw itk::ExceptionObject in case one layer can't be accessed.
* \throw * whatever the functor \c may throw.
* \note the functor is expected to receive an \c OGRLayer by reference.
* \note the functor is expected to receive an \c ogr::Layer by reference.
* \sa std::for_each
*/
template <class Functor> void ForEachLayer(Functor f)
{
assert(m_DataSource && "OGRDataSource not initialized");
const int nbLayers = this->GetLayersCount();
for (int i=0; i!=nbLayers; ++i)
{
OGRLayer * l = m_DataSource->GetLayer(i);
if (!l)
{
itkExceptionMacro(<< "Failed to fetch "<< i <<"th layer from OGRDataSource");
}
f(*l);
}
}
template <class Functor> void ForEachLayer(Functor f) const;
/**
* Accumulates the result of a functor on all layers.
......@@ -253,24 +243,10 @@ namespace otb { namespace ogr {
* \return the result accumulated (with +)
* \throw itk::ExceptionObject in case one layer can't be accessed.
* \throw * whatever the functor \c may throw.
* \note the functor is expected to receive an \c OGRLayer by reference.
* \note the functor is expected to receive an \c ogr::Layer by reference.
* \sa std::accumulate
*/
template <class Functor, typename V> V AccumulateOnLayers(Functor f, V v0) const
{
assert(m_DataSource && "OGRDataSource not initialized");
const int nbLayers = this->GetLayersCount();
for (int i=0; i!=nbLayers; ++i)
{
OGRLayer * l = m_DataSource->GetLayer(i);
if (!l)
{
itkExceptionMacro(<< "Failed to fetch "<< i <<"th layer from OGRDataSource");
}
v0 = f(*l, v0);
}
return v0;
}
template <class Functor, typename V> V AccumulateOnLayers(Functor f, V v0) const;
/** Returns the number of elements in the Data Source.
* \param[in] doForceComputation indicates whether the size shall be
......@@ -300,6 +276,72 @@ namespace otb { namespace ogr {
*/
void Reset(OGRDataSource * source);
/**\name Layers modification */
//@{
/**
* 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.
*
* \return a proxy on the \c OGRLayer created.
* \throw itk::ExceptionObject in case the layer cannot be created on the
* data source.
*
* \note a \em proxy-class is returned instead of a plain \c OGRLayer is
* order to encapsulate all lifetime management of the \c OGRLayer obtained
* (i.e. never to be destroyed). If you want to delete a layer obtained
* with \c CreateLayer, you must use \c DeleteLayer.
* \note the \c papszOptions parameter may later become a \c
* std::vector<std::string>
* \sa OGRDataSource::CreateLayer
*/
Layer CreateLayer(
std::string const& name,
OGRSpatialReference * poSpatialRef = NULL,
OGRwkbGeometryType eGType = wkbUnknown,
char ** papszOptions = NULL);
/**
* Deletes the i-th layer from the data source.
* \param[in] i layer index
*
* \throw it::ExceptionObject in case the index is out of range
* \throw it::ExceptionObject if the layer cannot be deleted from the data
* source.
*
* \pre the data source must support the delete operation
* \pre the index \c i must be in range [0, GetLayersCount())
* \sa OGRDataSource::DeleteLayer
*/
void DeleteLayer(size_t i);
/**
* Copies a layer.
* \param[in] srcLayer Source layer to copy. It may come from another \c
* DataSource.
* \param[in] newName Name of the new layer
* \param[in] papszOptions Creation options
*
* \return a proxy on the \c OGRLayer created.
* \throw itk::ExceptionObject in case the layer cannot be created on the
* data source.
*
* \note a \em proxy-class is returned instead of a plain \c OGRLayer is
* order to encapsulate all lifetime management of the \c OGRLayer obtained
* (i.e. never to be destroyed). If you want to delete a layer obtained
* with \c CreateLayer, you must use \c DeleteLayer.
* \note the \c papszOptions parameter may later become a \c
* std::vector<std::string>
* \sa OGRDataSource::CopyLayer
*/
Layer CopyLayer(
Layer & srcLayer,
std::string const& newName,
char ** papszOptions = NULL);
//@}
/**\name Layers access
*\note as the following accessors are not inlined, they aren't optimized.
*/
......@@ -320,19 +362,10 @@ namespace otb { namespace ogr {
* \note Use \c GetLayerUnchecked() if invalid indices are programming
* errors, or if null layers are to be expected.
*/
OGRLayer& GetLayer(size_t i)
{
assert(int(i) < GetLayersCount());
OGRLayer * layer_ptr = GetLayerUnchecked(i);
assert(layer_ptr);
return *layer_ptr;
}
Layer GetLayer(size_t i);
/**\copydoc otb::ogr::DataSource::GetLayer()
*/
OGRLayer const& GetLayer(size_t i) const
{
return const_cast <DataSource*>(this)->GetLayer(i);
}
Layer const GetLayer(size_t i) const;
/**
* Checked Accessor to a given layer.
......@@ -344,10 +377,10 @@ namespace otb { namespace ogr {
* to be programming errors.
* \throw None
*/
OGRLayer& GetLayerChecked(size_t i);
Layer GetLayerChecked(size_t i);
/**\copydoc otb::ogr::DataSource::GetLayerChecked()
*/
OGRLayer const& GetLayerChecked(size_t i) const;
Layer const GetLayerChecked(size_t i) const;
//@}
......@@ -362,6 +395,16 @@ namespace otb { namespace ogr {
return m_DataSource ? &boolean::i : 0;
}
/** Access to raw \c OGRDataSource.
* This function provides an abstraction leak in case deeper control on the
* underlying \c OGRDataSource is required.
* \pre the underlying \c OGRDataSource must be valid, i.e.
* <tt>m_DataSource != 0</tt>, an assertion is fired otherwise.
* \warning you must under no circonstance try to delete the \c OGRDataSource
* obtained this way.
*/
OGRDataSource & ogr();
protected:
/** Default constructor.
* The actual \c OGRDataSource is using the <em>in-memory</em> \c
......@@ -407,7 +450,7 @@ namespace otb { namespace ogr {
} } // end namespace otb::ogr
#ifndef OTB_MANUAL_INSTANTIATION
// #include "otbOGRDataSourceWrapper.txx"
#include "otbOGRDataSourceWrapper.txx"
#endif
#endif // __otbOGRDataSourceWrapper_h
/*=========================================================================
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.
=========================================================================*/
/**\ingroup Geometry
* \file otbOGRDataSourceWrapper.txx
* \internal this file is meant to be included by otbOGRDataSourceWrapper.h
*/
#ifndef __otbOGRDataSourceWrapper_txx
#define __otbOGRDataSourceWrapper_txx
/*===========================================================================*/
/*===============================[ Includes ]================================*/
/*===========================================================================*/
#include <cassert>
#include "itkMacro.h" // itk::ExceptionObject
#include "ogrsf_frmts.h" // OGRDataSource & OGRLayer
/*===========================================================================*/
/*==============================[ other stuff ]==============================*/
/*===========================================================================*/
// This implementation is inline so assert() will be expanded according to the
// compilation mode of the client code.
inline
otb::ogr::Layer otb::ogr::DataSource::GetLayer(size_t i)
{
assert(int(i) < GetLayersCount());
OGRLayer * layer_ptr = GetLayerUnchecked(i);
assert(layer_ptr);
return otb::ogr::Layer(layer_ptr);
}
inline
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::GetLayerChecked(size_t i) const
{
return const_cast <DataSource*>(this)->GetLayerChecked(i);
}
template <class Functor, typename V>
inline
V otb::ogr::DataSource::AccumulateOnLayers(Functor f, V v0) const
{
assert(m_DataSource && "OGRDataSource not initialized");
const int nbLayers = this->GetLayersCount();
for (int i=0; i!=nbLayers; ++i)
{
const Layer l = GetLayer(i);
if (!l)
{
itkExceptionMacro(<< "Failed to fetch "<< i <<"th layer from OGRDataSource");
}
v0 = f(l, v0);
}
return v0;
}
template <class Functor>
inline
void otb::ogr::DataSource::ForEachLayer(Functor f) const
{
assert(m_DataSource && "OGRDataSource not initialized");
const int nbLayers = this->GetLayersCount();
for (int i=0; i!=nbLayers; ++i)
{
Layer l = GetLayer(i);
if (!l)
{
itkExceptionMacro(<< "Failed to fetch "<< i <<"th layer from OGRDataSource");
}
f(l);
}
}
#endif // __otbOGRDataSourceWrapper_txx
inline
OGRDataSource & otb::ogr::DataSource::ogr()
{
assert(m_DataSource && "OGRDataSource not initialized");
return *m_DataSource;
}
/*=========================================================================
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 "otbOGRLayerWrapper.h"
#include <cassert>
#include <boost/bind.hpp>
#include "ogrsf_frmts.h" // OGRDataSource & OGRLayer
/*===========================================================================*/
/*==============================[ other stuff ]==============================*/
/*===========================================================================*/
namespace { // Anonymous namespace
/**\ingroup Geometry
* Deleter for \c boost::shared_ptr<> that doesn't delete.
* This is required for \c OGRLayer s that belong to \c OGRDataSource.
* \internal
*/
struct LeaveAloneDeleter
{
void operator()(OGRLayer*) const {}
};
} // Anonymous namespace
otb::ogr::Layer::Layer(OGRLayer* layer)
: m_Layer(layer, LeaveAloneDeleter())
{
}
otb::ogr::Layer::Layer(OGRLayer* layer, OGRDataSource* sourceInChargeOfLifeTime)
: m_Layer(layer, boost::bind(&OGRDataSource::ReleaseResultSet, sourceInChargeOfLifeTime, _1))
{
assert(layer && "A null OGRlayer cannot belong to an OGRDataSource" );
// OGR always refuses "delete 0". *sigh*
}
int otb::ogr::Layer::GetFeatureCount(bool doForceComputation) const
{
assert(m_Layer);
return m_Layer->GetFeatureCount(doForceComputation);
}
std::string otb::ogr::Layer::GetName() const
{
assert(m_Layer && "null layer");
return m_Layer->GetName();
}
OGRLayer & otb::ogr::Layer::ogr()
{
assert(m_Layer && "OGRLayer not initialized");
return *m_Layer;
}
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
}
else
{
os << "null Layer\n";
}
}
/*=========================================================================
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 __otbLayer_h
#define __otbLayer_h
// #include <iosfwd> // std::ostream&
#include <boost/shared_ptr.hpp>
#include "itkIndent.h"
class OGRLayer; // fwd declaration
class OGRDataSource;
namespace otb { namespace ogr
{
/**\ingroup Geometry
* \brief Layer of geometric objets.
*
* It provides an encapsulation of OGR classes. In that particular case, it's an
* encapsulation of \c OGRLayer.
*
* \note this class is a proxy class on top of an \c OGRLayer.
* \note It can be copied.
* \note when created from a \c otb::ogr::DataSource::ExecuteSQL, it will
* automatically manage the release of the underlying \c OGRLayer.
* \note the default constructor is disabled on purpose
* \note the destructor automatically generated does everything that is
* expected.
*
* \todo find a way to tell whether the related \c OGRDataSource was released
*/
class Layer
// : public itk::DataObject
{
public:
/**\name Standard class typedefs */
//@{
typedef Layer Self;
// typedef itk::DataObject Superclass;
// typedef itk::SmartPointer<Self> Pointer;
// typedef itk::SmartPointer<const Self> ConstPointer;
//@}
/**\name Standard macros */
//@{
// itkNewMacro(Self);
// itkTypeMacro(Layer, DataObject);
//@}
/**\name Construction */
//@{
explicit Layer(OGRLayer* layer);
Layer(OGRLayer* layer, OGRDataSource* sourceInChargeOfLifeTime);
//@}
/** 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.
*
* \return the number of features in the layer, -1 if count is unknown
* \throw None
* \sa OGRLayer::GetFeatureCount
*/
int GetFeatureCount(bool doForceComputation) const;
std::string GetName() const;
/** Prints self into stream. */
void PrintSelf(std::ostream& os, itk::Indent indent) const;
struct boolean{ int i;};
/** Can the layer be used (ie not null).
*
* Hack to provide a boolean operator that is convertible only to a
* 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 {
return m_Layer ? &boolean::i : 0;
}
/** Access to raw \c OGRLayer.
* This function provides an abstraction leak in case deeper control on the
* underlying \c OGRLayer is required.
* \pre the underlying \c OGRLayer must be valid, i.e.
* <tt>m_Layer != 0</tt>, an assertion is fired otherwise.
* \warning you must under no circonstance try to delete the \c OGRLayer
* obtained this way.
*/
OGRLayer & ogr();
private:
/** Data implementation.
* \internal The actual %layer implementation belongs to the \c otb::Layer object.
*/
boost::shared_ptr<OGRLayer> m_Layer;
};
} } // end namespace otb::ogr
#ifndef OTB_MANUAL_INSTANTIATION
// #include "otbLayer.txx"
#endif
#endif // __otbLayer_h
......@@ -27,31 +27,19 @@
using namespace otb;
#if 0
#if defined(BOOST_TEST_DYN_LINK)
#error BOOST_TEST_DYN_LINK defined
#else
#error BOOST_TEST_DYN_LINK not defined
#endif
#if defined(BOOST_TEST_MAIN)
#error BOOST_TEST_MAIN defined
#else
#error BOOST_TEST_MAIN not defined
#endif
#if defined(BOOST_TEST_NO_MAIN)
#error BOOST_TEST_NO_MAIN defined
#else
#error BOOST_TEST_NO_MAIN not defined
#endif
#endif
/**\ingroup Geometry
* \file otbOGRDataSourceWrapperNew.cxx
*
*\todo test: CopyLayer from one datasource to another
*\todo test: read datasource
*\todo test: write datasource
*/
/*===========================================================================*/
/*==============================[ other stuff ]==============================*/
/*===========================================================================*/
BOOST_AUTO_TEST_CASE(OGRDataSource_new)
BOOST_AUTO_TEST_CASE(OGRDataSource_new_empty)
{
ogr::DataSource::Pointer ds = ogr::DataSource::New();
BOOST_ASSERT(ds);
......@@ -60,3 +48,37 @@ BOOST_AUTO_TEST_CASE(OGRDataSource_new)
BOOST_CHECK_EQUAL(ds->Size(true), 0);
BOOST_CHECK_THROW(ds->GetLayerChecked(0), itk::ExceptionObject);
}
const std::string k_one = "one";
const std::string k_two = "two";
BOOST_AUTO_TEST_CASE(OGRDataSource_mem_add_n_del_layer)
{
ogr::DataSource::Pointer ds = ogr::DataSource::New();
BOOST_ASSERT(ds);
ogr::Layer l = ds -> CreateLayer(k_one);
BOOST_CHECK_EQUAL(ds->GetLayersCount(), 1);
BOOST_CHECK_EQUAL(l.GetFeatureCount(false), 0);
BOOST_CHECK_EQUAL(l.GetFeatureCount(true), 0);
ogr::Layer l0 = ds->GetLayerChecked(0);
BOOST_CHECK_THROW(ds->GetLayerChecked(1), itk::ExceptionObject);
BOOST_CHECK_EQUAL(l.GetName(), k_one);
BOOST_CHECK_EQUAL(l0.GetName(), k_one);
ogr::Layer l2 = ds -> CreateLayer(k_two);
BOOST_CHECK_EQUAL(ds->GetLayersCount(), 2);
BOOST_CHECK_EQUAL(ds->Size(false), 0);
BOOST_CHECK_EQUAL(ds->Size(true), 0);
BOOST_CHECK_EQUAL(ds->GetLayer(0).GetName(), k_one);
BOOST_CHECK_EQUAL(ds->GetLayer(1).GetName(), k_two);
BOOST_CHECK_THROW(ds->GetLayerChecked(2), itk::ExceptionObject);
ds->DeleteLayer(0);
BOOST_CHECK_EQUAL(ds->GetLayersCount(), 1);
BOOST_CHECK_EQUAL(ds->GetLayer(0).GetName(), k_two);
BOOST_CHECK_THROW(ds->GetLayerChecked(1), itk::ExceptionObject);
BOOST_CHECK_EQUAL(ds->Size(false), 0);
BOOST_CHECK_EQUAL(ds->Size(true), 0);