Commit f89166bb authored by Luc Hermitte's avatar Luc Hermitte

ENH: OTB-151/GeometriesProjectionFilter -> framework for filtering geometries...

ENH: OTB-151/GeometriesProjectionFilter -> framework for filtering geometries (+inline transformation)
parent a4b4b36f
......@@ -20,39 +20,123 @@
/*===============================[ Includes ]================================*/
/*===========================================================================*/
#include "otbGeometriesSet.h"
#include <cassert>
/*===========================================================================*/
/*==============================[ other stuff ]==============================*/
/*================================[ Helpers ]================================*/
/*===========================================================================*/
template <typename DataType>
inline
DataType & getRef(typename itk::SmartPointer<DataType> const& ds)
{
assert(ds && "unexpected nil datasource");
return *ds;
}
#if 0
template <typename DataType>
inline
DataType const& getRef(typename DataType::ConstPointer ds)
{
assert(ds);
return *ds;
}
#endif
otb::ogr::Layer const& getRef(otb::ogr::Layer const& layer)
{
return layer;
}
otb::ogr::Layer & getRef(otb::ogr::Layer & layer)
{
return layer;
}
/*===========================================================================*/
/*=======================[ Setting and construction ]========================*/
/*===========================================================================*/
otb::GeometriesSet::GeometriesSet()
: m_geometriesSet(otb::ogr::DataSource::New())
{
}
otb::GeometriesSet::GeometriesSet(ogr::DataSource::Pointer datasource)
: m_geometriesSet(datasource)
{
assert(datasource && "unexpected nil datasource");
}
otb::GeometriesSet::GeometriesSet(ogr::Layer layer)
: m_geometriesSet(layer)
{
}
/*virtual*/ otb::GeometriesSet::~GeometriesSet()
/*static*/
otb::GeometriesSet::Pointer otb::GeometriesSet::New(ogr::DataSource::Pointer datasource)
{
Pointer res = new Self(datasource);
res->UnRegister();
return res;
}
/*static*/
otb::GeometriesSet::Pointer otb::GeometriesSet::New(ogr::Layer layer)
{
Pointer res = new Self(layer);
res->UnRegister();
return res;
}
/*virtual*/
otb::GeometriesSet::~GeometriesSet()
{
}
void otb::GeometriesSet::Set(ogr::Layer layer)
{
assert(layer && "unexpected nil layer");
m_geometriesSet = layer;
}
void otb::GeometriesSet::Set(ogr::DataSource::Pointer datasource)
{
assert(datasource && "unexpected nil datasource");
m_geometriesSet = datasource;
}
/*===========================================================================*/
/*=================================[ IsSet ]=================================*/
/*===========================================================================*/
struct IsSetTester : boost::static_visitor<bool>
{
template <typename T>
bool operator()(T const& gs) const
{
return getRef(gs);
}
};
bool otb::GeometriesSet::IsSet() const
{
return this->apply(IsSetTester());
}
/*===========================================================================*/
/*===============================[ Printing ]================================*/
/*===========================================================================*/
struct Printer : boost::static_visitor<>
{
Printer(std::ostream& os, itk::Indent indent)
: m_os(os), m_indent(indent) {}
void operator()(otb::ogr::Layer layer) const
{
assert(layer && "unexpected nil layer...");
layer.PrintSelf(m_os, m_indent);
}
void operator()(otb::ogr::DataSource::Pointer datasource) const
{
assert(datasource && "unexpected nil datasource...");
datasource->Print(m_os, m_indent);
}
private:
......
......@@ -25,6 +25,11 @@
namespace otb
{
/**\ingroup gGeometry
* \class GeometriesSet
* \brief Common ITK type to manipulate any set of geometries: a \c otb::ogr::DataSource, or a \c otb::ogr::Layer.
* \since OTB v 3.14.0
*/
class ITK_EXPORT GeometriesSet : public itk::DataObject
{
public:
......@@ -38,47 +43,85 @@ public:
/**\name Standard macros */
//@{
/** Method for creation through the object factory. */
itkNewMacro(Self);
/** Run-time type information (and related methods). */
itkTypeMacro(GeometriesSet, itk::DataObject);
//@}
/**\name Factory functions */
//@{
/** ITK method for creation through the object factory. */
itkNewMacro(Self);
/// Direct builder from an existing \c DataSource.
static Pointer New(ogr::DataSource::Pointer datasource);
/// Direct builder from an existing \c Layer.
static Pointer New(ogr::Layer layer);
//@}
/**\name Geometries accessors */
//@{
void Set(ogr::Layer layer);
void Set(ogr::DataSource::Pointer datasource);
void Set(ogr::Layer layer);
//@}
/**\name Application of a command to a geometries set, through a visitor
* \note
* As a \c GeometriesSet contains either a \c otb::ogr::DataSource, or a
* \c otb::ogr::Layer, one needs to apply a function on either kind of data.
* This can be done thanks to a specialization of \c boost::static_visitor<>
* that has a const \c operator() that takes either one (or two in case of
* binary visitors) \c DataSource or \c Layer as parameter.
*/
//@{
template <typename Visitor>
void apply(Visitor const& visitor)
typename Visitor::result_type
apply(Visitor const& visitor)
{
boost::apply_visitor(visitor, m_geometriesSet);
return boost::apply_visitor(visitor, m_geometriesSet);
}
template <typename Visitor>
void apply(Visitor const& visitor) const
typename Visitor::result_type
apply(Visitor const& visitor) const
{
boost::apply_visitor(visitor, m_geometriesSet);
return boost::apply_visitor(visitor, m_geometriesSet);
}
template <typename Visitor>
void apply(Visitor const& visitor, Self & visitable)
typename Visitor::result_type
apply(Visitor const& visitor, Self & visitable)
{
boost::apply_visitor(visitor, m_geometriesSet, visitable.m_geometriesSet);
return boost::apply_visitor(visitor, m_geometriesSet, visitable.m_geometriesSet);
}
template <typename Visitor>
void apply(Visitor const& visitor, Self /*const*/& visitable) const
typename Visitor::result_type
apply(Visitor const& visitor, Self /*const*/& visitable) const
{
boost::apply_visitor(visitor, m_geometriesSet, visitable.m_geometriesSet);
return boost::apply_visitor(visitor, m_geometriesSet, visitable.m_geometriesSet);
}
//@}
/** Does it contain a geometries set ?.
*/
bool IsSet() const;
protected:
/** Default constructor. */
/** Default constructor.
* This actual geometries set is an in-memory \c otb::ogr::DataSource.
* It needs to be set to a set of geometries if you expect to serialize it or
* work on a \c Layer.
*
* When using this construction path, you'll likelly need to \c Set() the
* actual geometries set.
*/
GeometriesSet();
/** Init constructor from a \c DataSource. */
GeometriesSet(ogr::DataSource::Pointer datasource);
/** Init constructor from a \c Layer. */
GeometriesSet(ogr::Layer layer);
/** Destructor. */
virtual ~GeometriesSet();
......
......@@ -29,12 +29,14 @@
otb::GeometriesSource::GeometriesSource()
{
Superclass::SetNumberOfRequiredOutputs(1);
// The default behaviour is to prepare a in-memory OGR datasource in case
// filters are piped.
// In the filter is meant to produce a file, use SetOutput, or the New(string)
// function to built the GeometriesSource and filters
ogr::DataSource::Pointer inMemoryDS = ogr::DataSource::New();
Superclass::SetNthOutput(0, inMemoryDS);
// The following line has for side effect to set of DataSource with the wrong
// type as default value instead of a plain nil
// smartpointer... => NO
// Superclass::SetNthOutput(0, 0);
// Moreover, the default, and correct, value will be set in AllocateOutputs if
// nothing is set by then => nothing more to do.
}
/*virtual*/ otb::GeometriesSource::~GeometriesSource()
......@@ -58,3 +60,23 @@ void otb::GeometriesSource::SetOutput(OutputGeometriesType* output, unsigned int
{
Superclass::SetNthOutput(idx, output);
}
/*virtual*/ void otb::GeometriesSource::AllocateOutputs()
{
// The default behaviour is to prepare a in-memory OGR datasource in case
// filters are piped.
// In the filter is meant to produce a file, use SetOutput, or the New(string)
// function to built the GeometriesSource and filters
if (!GetOutput() || !GetOutput()->IsSet())
{
GeometriesSet::Pointer gs = GeometriesSet::New(); // in-memory DataSource
assert(gs);
this->SetOutput(gs);
}
}
/*virtual*/ void otb::GeometriesSource::PrepareOutputs()
{
AllocateOutputs();
Superclass::PrepareOutputs();
}
......@@ -72,16 +72,14 @@ public:
virtual OutputGeometriesType* GetOutput(unsigned int idx);
virtual void SetOutput(OutputGeometriesType* output, unsigned int idx = 0);
virtual void PrepareOutputs();
protected:
GeometriesSource();
virtual ~GeometriesSource();
// Inherited definition is enough
// void PrintSelf(std::ostream& os, itk::Indent indent) const;
// /** Ensures that the output vector data are cleared before processing. */
// virtual void AllocateOutputs();
/** Ensures that the output vector data are cleared before processing. */
virtual void AllocateOutputs();
};
} // end namespace otb
......
......@@ -20,6 +20,7 @@
/*===============================[ Includes ]================================*/
/*===========================================================================*/
#include "otbGeometriesToGeometriesFilter.h"
#include <cassert>
#include "otbGeometriesSet.h"
#include "itkMacro.h"
#include "itkTimeProbe.h"
......@@ -59,24 +60,49 @@ struct ProcessVisitor : boost::static_visitor<>
: m_filter(filter) {}
void operator()(otb::ogr::Layer const& source, otb::ogr::Layer & destination) const
{
std::cout << "G2GF: Process Visitor: L -> L ("<< source.GetName()<<")...\n";
// std::cout << "G2GF: Process Visitor: L -> L ("<< source.GetName()<<")...\n";
m_filter.DoProcessLayer(source, destination);
}
void operator()(otb::ogr::DataSource::Pointer source, otb::ogr::DataSource::Pointer destination) const
{
std::cout << "G2GF: Process Visitor: DS("<<source->ogr().GetName()<<") -> DS("<<source->ogr().GetName()<<") ...\n";
assert(source && "can't filter a nil datasource");
assert(destination && "can't filter to a nil datasource");
// std::cout << "G2GF: Process Visitor: DS("<<source->ogr().GetName()<<") -> DS("<<destination->ogr().GetName()<<") ...\n";
for (otb::ogr::DataSource::const_iterator b = source->begin(), e = source->end()
; b != e
; ++b
)
{
otb::ogr::Layer const& sourceLayer = *b;
assert(sourceLayer && "unexpected nil source layer");
// std::cout << "source layer name:" << sourceLayer.GetName() << "\n";
otb::ogr::Layer destLayer = destination->CreateLayer(
sourceLayer.GetName(), 0, sourceLayer.GetGeomType());
// std::cout << "layer created!\n";
m_filter.DoProcessLayer(sourceLayer, destLayer);
}
}
void operator()(otb::ogr::Layer & inout) const
{
// std::cout << "G2GF: Process Visitor: L -> L ("<< source.GetName()<<")...\n";
m_filter.DoProcessLayer(inout, inout);
}
void operator()(otb::ogr::DataSource::Pointer inout) const
{
assert(inout && "can't filter a nil datasource");
// std::cout << "G2GF: Process Visitor: DS("<<source->ogr().GetName()<<") -> DS("<<destination->ogr().GetName()<<") ...\n";
for (otb::ogr::DataSource::iterator b = inout->begin(), e = inout->end(); b != e; ++b)
{
otb::ogr::Layer layer = *b;
assert(layer && "unexpected nil source layer");
// std::cout << "source layer name:" << sourceLayer.GetName() << "\n";
m_filter.DoProcessLayer(layer, layer);
}
}
template <typename GT1, typename GT2> void operator()(GT1 const&, GT2 &) const
{
assert(!"You shall not mix DataSources and Layers in GeometriesToGeometriesFilter");
......@@ -89,12 +115,21 @@ private:
/*virtual*/ void otb::GeometriesToGeometriesFilter::Process(
InputGeometriesType const& source, OutputGeometriesType & destination)
{
std::cout << "G2GF: Processing ...\n";
// std::cout << "G2GF: Processing ...\n";
// si layer, appelle virt process layer
// si DS, loop et appelle virt process layer
source.apply(ProcessVisitor(*this), destination);
}
/*virtual*/ void otb::GeometriesToGeometriesFilter::Process(
OutputGeometriesType & inout)
{
// std::cout << "G2GF: Processing ...\n";
// si layer, appelle virt process layer
// si DS, loop et appelle virt process layer
inout.apply(ProcessVisitor(*this));
}
/*virtual*/
void otb::GeometriesToGeometriesFilter::GenerateOutputInformation(void )
{
......@@ -111,14 +146,25 @@ void otb::GeometriesToGeometriesFilter::GenerateOutputInformation(void )
/*virtual*/
void otb::GeometriesToGeometriesFilter::GenerateData(void )
{
// this->AllocateOutputs();
OutputGeometriesType::Pointer output = this->GetOutput();
// std::cout << "G2GF::GenerateData\n";
this->AllocateOutputs();
InputGeometriesType::ConstPointer input = this->GetInput();
// assert(input && "Cann't filter to a nil geometries set");
OutputGeometriesType::Pointer output = this->GetOutput();
assert(output && "Cann't filter a nil geometries set");
// Start recursive processing
itk::TimeProbe chrono;
chrono.Start();
this->Process(*input, *output);
if (input)
{
this->Process(*input, *output);
}
else
{
this->Process(*output);
}
chrono.Stop();
otbMsgDevMacro(<< "GeometriesToGeometriesFilter: geometries processed in " << chrono.GetMeanTime() << " seconds.");
}
......@@ -68,8 +68,8 @@ protected:
virtual void GenerateData(void);
private:
void Process(
InputGeometriesType const& source, OutputGeometriesType &destination);
void Process(OutputGeometriesType &inout);
void Process(InputGeometriesType const& source, OutputGeometriesType &destination);
virtual void DoProcessLayer(ogr::Layer const& source, ogr::Layer & destination) const=0;
friend struct ::ProcessVisitor;
};
......@@ -90,6 +90,10 @@ struct TransformationFunctorDispatcher<TransformationFunctor, ogr::Layer>
{
m_functor(in, out);
}
void operator()(ogr::Layer & inout) const
{
m_functor(inout);
}
private:
TransformationFunctor m_functor;
};
......@@ -101,22 +105,38 @@ struct TransformationFunctorDispatcher<TransformationFunctor, OGRGeometry>
typedef typename TransformationFunctor::TransformedElementType TransformedElementType;
BOOST_MPL_ASSERT((boost::is_same<OGRGeometry, TransformedElementType>));
TransformationFunctorDispatcher(TransformationFunctor functor) : m_functor(functor){ }
void operator()(ogr::Layer const& in, ogr::Layer & out) const
{
// std::cout << "Converting layer " << in.GetName() << " -> " << out.GetName() << "\n";
OGRFeatureDefn & defn = out.GetLayerDefn();
for (ogr::Layer::const_iterator b = in.begin(), e = in.end()
; b != e
; ++b
)
for (ogr::Layer::const_iterator b = in.begin(), e = in.end(); b != e; ++b)
{
ogr::Feature const feat = *b;
ogr::UniqueGeometryPtr g = m_functor(feat.GetGeometry());
// TODO: field transformations...
ogr::UniqueGeometryPtr g = m_functor(feat.GetGeometry());
ogr::Feature dest(defn);
dest.SetGeometryDirectly(boost::move(g));
out.CreateFeature(dest);
}
}
void operator()(ogr::Layer & inout) const
{
// std::cout << "Converting layer " << inout.GetName() << "\n";
OGRFeatureDefn & defn = inout.GetLayerDefn();
// NB: We can't iterate with begin()/end() as SetFeature may invalidate the
// iterators depending of the underlying drivers
// => we use start_at(), i.e. SetNextByIndex()
for (int i=0, N=inout.GetFeatureCount(true); i!=N; ++i)
{
ogr::Feature feat = *inout.start_at(i);
// TODO: field transformations...
ogr::UniqueGeometryPtr g = m_functor(feat.GetGeometry());
feat.SetGeometryDirectly(boost::move(g));
inout.SetFeature(feat);
}
}
private:
TransformationFunctor m_functor;
};
......@@ -156,12 +176,17 @@ protected:
/** Destructor. */
virtual ~DefaultGeometriesToGeometriesFilter();
/** Prints self to stream. */
// void PrintSelf(std::ostream& os, itk::Indent indent) const;
virtual void DoProcessLayer(ogr::Layer const& source, ogr::Layer & destination) const
{
m_TransformationFunctor(source, destination); // if TransformedElementType == layer
// std::cout << "DG2GF::DoProcessLayer: L("<<source.GetName()<<") -> L("<<destination.GetName()<<") ...\n";
if (source != destination)
{
m_TransformationFunctor(source, destination); // if TransformedElementType == layer
}
else
{
m_TransformationFunctor(destination); // if TransformedElementType == layer
}
};
private:
TransformationFunctorDispatcherType m_TransformationFunctor;
......
......@@ -162,6 +162,7 @@ otb::ogr::DataSource::New(std::string const& filename, Modes::type mode)
Drivers::Init();
const bool update = mode & Modes::write;
// std::cout << "Opening datasource " << filename << " update=" << update << "\n";
if (itksys::SystemTools::FileExists(filename.c_str()))
{
OGRDataSource * source = OGRSFDriverRegistrar::Open(filename.c_str(), update);
......
......@@ -95,6 +95,7 @@ void otb::ogr::Feature::UncheckedPrintSelf(std::ostream & os, itk::Indent indent
Field const& field = (*this)[i];
field.PrintSelf(os, indent);
}
OGRGeometry const* g = GetGeometry();
}
bool otb::ogr::operator==(otb::ogr::Feature const& lhs, otb::ogr::Feature const& rhs)
......
......@@ -298,6 +298,7 @@ public:
* equal.
*/
friend bool otb::ogr::operator==(Feature const& lhs, Feature const& rhs);
OGRFeature const* addr() const { return m_Feature.get(); }
private:
/**\name Unchecked definitions
* All the definitions that follow do the real work. However, they are not the
......
......@@ -66,7 +66,8 @@ int otb::ogr::Layer::GetFeatureCount(bool doForceComputation) const
otb::ogr::Feature otb::ogr::Layer::GetNextFeature()
{
assert(m_Layer && "OGRLayer not initialized");
return m_Layer->GetNextFeature();
OGRFeature * f = m_Layer->GetNextFeature();
return f;
}
otb::ogr::Layer::iterator otb::ogr::Layer::begin()
......@@ -323,3 +324,9 @@ OGRwkbGeometryType otb::ogr::Layer::GetGeomType() const
return m_Layer->GetGeomType();
#endif
}
bool otb::ogr::operator==(Layer const& lhs, Layer const& rhs)
{
const bool equal = lhs.m_Layer.get() == rhs.m_Layer.get();
return equal;
}
......@@ -153,6 +153,8 @@ public:
* \throw itk::ExceptionObject if the feature can't be set.
* \pre The Layer need to support <em>OLCRandomWrite</em> capability.
* \sa \c OGRLayer::SetFeature()
* \warning Calls to this function may invalidate any feature iterator
* previously obtained depending on the actual \c OGRDriver.
* \internal
* Whilst the \c Feature id is updated, it is not the same feature than the
* one stored in the layer. In other words, \c Feature is still in charge of
......@@ -363,6 +365,8 @@ public:
* existance that were obtained or created with the previous layer definition.
* \throw itk::ExceptionObject if the new field cannot be created
* \sa \c OGRLayer::CreateField()
* \warning Calls to this function may invalidate any feature iterator
* previously obtained depending on the actual \c OGRDriver.
* \todo Move to use \c otb::ogr::FieldDefn
*/
void CreateField(FieldDefn const& field, bool bApproxOK = true);
......@@ -447,6 +451,8 @@ public:
* \sa \c OGRLayer::GetGeomType()
*/
OGRwkbGeometryType GetGeomType() const;
friend bool otb::ogr::operator==(Layer const& lhs, Layer const& rhs);
private:
/**
* Internal encapsulation of \c OGRLayer::GetNextFeature().
......@@ -468,6 +474,12 @@ private:
boost::shared_ptr<OGRLayer> m_Layer;
};
bool operator==(Layer const& lhs, Layer const& rhs);
inline bool operator!=(Layer const& lhs, Layer const& rhs)
{
return ! (lhs == rhs);
}
} } // end namespace otb::ogr
#ifndef OTB_MANUAL_INSTANTIATION
......
......@@ -47,11 +47,13 @@ int main (int argc, char **argv)
if (argc < 2)
{
std::cerr << " inputGeometriesFile [outputGeometriesFile]" << std::endl;
// if outputGeometriesFile == "-", output == stdout
return EXIT_FAILURE;
}
try
{
const bool workingInplace = argc==2;
const bool outputIsStdout = !workingInplace && !strcmp(argv[2], "-");
const std::string inputFile = argv[1];
const std::string outputFile = workingInplace ? argv[1] : argv[2];
......@@ -59,24 +61,38 @@ int main (int argc, char **argv)
otb::ogr::DataSource::Pointer input = otb::ogr::DataSource::New(
inputFile,
workingInplace ? otb::ogr::DataSource::Modes::write : otb::ogr::DataSource::Modes::read);
otb::ogr::DataSource::Pointer output = workingInplace
? input
: otb::ogr::DataSource::New( outputFile, otb::ogr::DataSource::Modes::write);
otb::ogr::DataSource::Pointer output
= workingInplace ? input
: outputIsStdout ? 0
: otb::ogr::DataSource::New( outputFile, otb::ogr::DataSource::Modes::write);
std::cout << "input: " << input -> ogr().GetName() << " should be: " << inputFile << "\n";
std::cout << "output: " << output -> ogr().GetName() << " should be: " << outputFile << "\n";
std::cout << "\n";
if (output)
{