Commit fda9f6f7 authored by Julien Michel's avatar Julien Michel

Merge branch 'enhance-field-parameters' into develop

parents bf3dc3bc 7f0f5849
......@@ -23,6 +23,16 @@
#include "otbConfigure.h"
#include "itkMacro.h"
#if defined(_MSC_VER)
#pragma warning ( push )
#pragma warning ( disable: 4251 )
#include "ogr_core.h" // OGR enums
#pragma warning ( pop )
#else
#include "ogr_core.h" // OGR enums
#endif
#ifdef OTB_USE_GDAL_20
class GDALDataset;
class GDALDriver;
......@@ -40,6 +50,12 @@ namespace ogr
namespace version_proxy
{
/**
* With Gdal >= 2.0, this function will test equality between type and
* OFTInteger64 enum. Otherwise, it returns false.
*/
OTBGdalAdapters_EXPORT bool IsOFTInteger64(OGRFieldType type);
/**
* This namespace holds proxy functions hiding interface changes in gdal 2.0
*
......
......@@ -17,6 +17,8 @@
=========================================================================*/
#include "otbOGRVersionProxy.h"
#include "itkMacro.h"
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
......@@ -33,6 +35,12 @@ namespace ogr
namespace version_proxy
{
OTBGdalAdapters_EXPORT bool IsOFTInteger64(OGRFieldType itkNotUsed(type))
{
return false;
}
GDALDatasetType * Open(const char * filename, bool readOnly)
{
return OGRSFDriverRegistrar::Open(filename,!readOnly);
......
......@@ -34,6 +34,11 @@ namespace ogr
namespace version_proxy
{
OTBGdalAdapters_EXPORT bool IsOFTInteger64(OGRFieldType type)
{
return type == OFTInteger64;
}
GDALDatasetType * Open(const char * filename, bool readOnly)
{
return (GDALDatasetType *)GDALOpenEx(filename, (readOnly? GDAL_OF_READONLY : GDAL_OF_UPDATE) | GDAL_OF_VECTOR,NULL,NULL,NULL);
......
......@@ -30,6 +30,11 @@ namespace otb
{
namespace Wrapper
{
/** Utility function to negate std::isalnum */
bool IsNotAlphaNum(char c)
{
return !std::isalnum(c);
}
class ComputeConfusionMatrix : public Application
{
......@@ -108,12 +113,10 @@ private:
AddParameter(ParameterType_InputFilename,"ref.vector.in","Input reference vector data");
SetParameterDescription("ref.vector.in", "Input vector data of the ground truth");
AddParameter(ParameterType_String,"ref.vector.field","Field name");
AddParameter(ParameterType_ListView,"ref.vector.field","Field name");
SetParameterDescription("ref.vector.field","Field name containing the label values");
SetParameterString("ref.vector.field","Class");
MandatoryOff("ref.vector.field");
DisableParameter("ref.vector.field");
SetListViewSingleSelectionMode("ref.vector.field",true);
AddParameter(ParameterType_Int,"nodatalabel","Value for nodata pixels");
SetParameterDescription("nodatalabel", "Label for the NoData class. Such input pixels will be discarded from the "
"ground truth and from the input classification map. By default, 'nodatalabel = 0'.");
......@@ -134,7 +137,32 @@ private:
void DoUpdateParameters() ITK_OVERRIDE
{
// Nothing to do here : all parameters are independent
if ( HasValue("ref.vector.in") )
{
std::string vectorFile = GetParameterString("ref.vector.in");
ogr::DataSource::Pointer ogrDS =
ogr::DataSource::New(vectorFile, ogr::DataSource::Modes::Read);
ogr::Layer layer = ogrDS->GetLayer(0);
ogr::Feature feature = layer.ogr().GetNextFeature();
ClearChoices("ref.vector.field");
for(int iField=0; iField<feature.ogr().GetFieldCount(); iField++)
{
std::string key, item = feature.ogr().GetFieldDefnRef(iField)->GetNameRef();
key = item;
std::string::iterator end = std::remove_if(key.begin(),key.end(),IsNotAlphaNum);
std::transform(key.begin(), end, key.begin(), tolower);
OGRFieldType fieldType = feature.ogr().GetFieldDefnRef(iField)->GetType();
if(fieldType == OFTString || fieldType == OFTInteger || ogr::version_proxy::IsOFTInteger64(fieldType))
{
std::string tmpKey="ref.vector.field."+key.substr(0, end - key.begin());
AddChoice(tmpKey,item);
}
}
}
}
std::string LogConfusionMatrix(MapOfClassesType* mapOfClasses, ConfusionMatrixType* matrix)
......@@ -228,8 +256,18 @@ private:
else
{
ogrRef = otb::ogr::DataSource::New(GetParameterString("ref.vector.in"), otb::ogr::DataSource::Modes::Read);
field = this->GetParameterString("ref.vector.field");
// Get field name
std::vector<int> selectedCFieldIdx = GetSelectedItems("ref.vector.field");
if(selectedCFieldIdx.empty())
{
otbAppLogFATAL(<<"No field has been selected for data labelling!");
}
std::vector<std::string> cFieldNames = GetChoiceNames("ref.vector.field");
field = cFieldNames[selectedCFieldIdx.front()];
rasterizeReference->AddOGRDataSource(ogrRef);
rasterizeReference->SetOutputParametersFromImage(input);
rasterizeReference->SetBackgroundValue(nodata);
......
......@@ -29,6 +29,12 @@ namespace otb
namespace Wrapper
{
/** Utility function to negate std::isalnum */
bool IsNotAlphaNum(char c)
{
return !std::isalnum(c);
}
class PolygonClassStatistics : public Application
{
public:
......@@ -96,10 +102,9 @@ private:
AddParameter(ParameterType_OutputFilename, "out", "Output Statistics");
SetParameterDescription("out","Output file to store statistics (XML format)");
AddParameter(ParameterType_String, "field", "Field Name");
AddParameter(ParameterType_ListView, "field", "Field Name");
SetParameterDescription("field","Name of the field carrying the class name in the input vectors.");
MandatoryOff("field");
SetParameterString("field", "class");
SetListViewSingleSelectionMode("field",true);
AddParameter(ParameterType_Int, "layer", "Layer Index");
SetParameterDescription("layer", "Layer index to read in the input vector file.");
......@@ -119,14 +124,49 @@ private:
void DoUpdateParameters() ITK_OVERRIDE
{
// Nothing to do
if ( HasValue("vec") )
{
std::string vectorFile = GetParameterString("vec");
ogr::DataSource::Pointer ogrDS =
ogr::DataSource::New(vectorFile, ogr::DataSource::Modes::Read);
ogr::Layer layer = ogrDS->GetLayer(this->GetParameterInt("layer"));
ogr::Feature feature = layer.ogr().GetNextFeature();
ClearChoices("field");
for(int iField=0; iField<feature.ogr().GetFieldCount(); iField++)
{
std::string key, item = feature.ogr().GetFieldDefnRef(iField)->GetNameRef();
key = item;
std::string::iterator end = std::remove_if(key.begin(),key.end(),IsNotAlphaNum);
std::transform(key.begin(), end, key.begin(), tolower);
OGRFieldType fieldType = feature.ogr().GetFieldDefnRef(iField)->GetType();
if(fieldType == OFTString || fieldType == OFTInteger || ogr::version_proxy::IsOFTInteger64(fieldType))
{
std::string tmpKey="field."+key.substr(0, end - key.begin());
AddChoice(tmpKey,item);
}
}
}
}
void DoExecute() ITK_OVERRIDE
{
otb::ogr::DataSource::Pointer vectors =
otb::ogr::DataSource::New(this->GetParameterString("vec"));
std::string fieldName = this->GetParameterString("field");
// Retrieve the field name
std::vector<int> selectedCFieldIdx = GetSelectedItems("field");
if(selectedCFieldIdx.empty())
{
otbAppLogFATAL(<<"No field has been selected for data labelling!");
}
std::vector<std::string> cFieldNames = GetChoiceNames("field");
std::string fieldName = cFieldNames[selectedCFieldIdx.front()];
otb::Wrapper::ElevationParametersHandler::SetupDEMHandlerFromElevationParameters(this,"elev");
......
......@@ -24,6 +24,11 @@ namespace otb
{
namespace Wrapper
{
/** Utility function to negate std::isalnum */
bool IsNotAlphaNum(char c)
{
return !std::isalnum(c);
}
class SampleExtraction : public Application
{
......@@ -89,12 +94,10 @@ private:
AddParameter(ParameterType_StringList, "outfield.list.names", "Output field names");
SetParameterDescription("outfield.list.names","Full list of output field names.");
AddParameter(ParameterType_String, "field", "Field Name");
SetParameterDescription("field","Name of the field carrying the class"
"name in the input vectors. This field is copied to output.");
MandatoryOff("field");
SetParameterString("field", "class");
AddParameter(ParameterType_ListView, "field", "Field Name");
SetParameterDescription("field","Name of the field carrying the class name in the input vectors.");
SetListViewSingleSelectionMode("field",true);
AddParameter(ParameterType_Int, "layer", "Layer Index");
SetParameterDescription("layer", "Layer index to read in the input vector file.");
MandatoryOff("layer");
......@@ -113,7 +116,32 @@ private:
void DoUpdateParameters()
{
// Nothing to do
if ( HasValue("vec") )
{
std::string vectorFile = GetParameterString("vec");
ogr::DataSource::Pointer ogrDS =
ogr::DataSource::New(vectorFile, ogr::DataSource::Modes::Read);
ogr::Layer layer = ogrDS->GetLayer(this->GetParameterInt("layer"));
ogr::Feature feature = layer.ogr().GetNextFeature();
ClearChoices("field");
for(int iField=0; iField<feature.ogr().GetFieldCount(); iField++)
{
std::string key, item = feature.ogr().GetFieldDefnRef(iField)->GetNameRef();
key = item;
std::string::iterator end = std::remove_if(key.begin(),key.end(),IsNotAlphaNum);
std::transform(key.begin(), end, key.begin(), tolower);
OGRFieldType fieldType = feature.ogr().GetFieldDefnRef(iField)->GetType();
if(fieldType == OFTString || fieldType == OFTInteger || ogr::version_proxy::IsOFTInteger64(fieldType))
{
std::string tmpKey="field."+key.substr(0, end - key.begin());
AddChoice(tmpKey,item);
}
}
}
}
void DoExecute()
......@@ -134,6 +162,17 @@ private:
output = vectors;
}
// Retrieve the field name
std::vector<int> selectedCFieldIdx = GetSelectedItems("field");
if(selectedCFieldIdx.empty())
{
otbAppLogFATAL(<<"No field has been selected for data labelling!");
}
std::vector<std::string> cFieldNames = GetChoiceNames("field");
std::string fieldName = cFieldNames[selectedCFieldIdx.front()];
std::vector<std::string> nameList;
std::string namePrefix("");
if (this->GetParameterString("outfield").compare("prefix") == 0)
......@@ -155,7 +194,7 @@ private:
filter->SetLayerIndex(this->GetParameterInt("layer"));
filter->SetSamplePositions(vectors);
filter->SetOutputSamples(output);
filter->SetClassFieldName(this->GetParameterString("field"));
filter->SetClassFieldName(fieldName);
filter->SetOutputFieldPrefix(namePrefix);
filter->SetOutputFieldNames(nameList);
filter->GetStreamer()->SetAutomaticAdaptativeStreaming(GetParameterInt("ram"));
......
......@@ -30,6 +30,12 @@ namespace otb
namespace Wrapper
{
/** Utility function to negate std::isalnum */
bool IsNotAlphaNum(char c)
{
return !std::isalnum(c);
}
class SampleSelection : public Application
{
public:
......@@ -195,10 +201,9 @@ private:
// Default strategy : smallest
SetParameterString("strategy","smallest");
AddParameter(ParameterType_String, "field", "Field Name");
AddParameter(ParameterType_ListView, "field", "Field Name");
SetParameterDescription("field","Name of the field carrying the class name in the input vectors.");
MandatoryOff("field");
SetParameterString("field", "class");
SetListViewSingleSelectionMode("field",true);
AddParameter(ParameterType_Int, "layer", "Layer Index");
SetParameterDescription("layer", "Layer index to read in the input vector file.");
......@@ -221,6 +226,32 @@ private:
void DoUpdateParameters()
{
if ( HasValue("vec") )
{
std::string vectorFile = GetParameterString("vec");
ogr::DataSource::Pointer ogrDS =
ogr::DataSource::New(vectorFile, ogr::DataSource::Modes::Read);
ogr::Layer layer = ogrDS->GetLayer(this->GetParameterInt("layer"));
ogr::Feature feature = layer.ogr().GetNextFeature();
ClearChoices("field");
for(int iField=0; iField<feature.ogr().GetFieldCount(); iField++)
{
std::string key, item = feature.ogr().GetFieldDefnRef(iField)->GetNameRef();
key = item;
std::string::iterator end = std::remove_if(key.begin(),key.end(),IsNotAlphaNum);
std::transform(key.begin(), end, key.begin(), tolower);
OGRFieldType fieldType = feature.ogr().GetFieldDefnRef(iField)->GetType();
if(fieldType == OFTString || fieldType == OFTInteger || ogr::version_proxy::IsOFTInteger64(fieldType))
{
std::string tmpKey="field."+key.substr(0, end - key.begin());
AddChoice(tmpKey,item);
}
}
}
}
void DoExecute()
......@@ -235,6 +266,17 @@ private:
// Setup ram
m_Periodic->GetStreamer()->SetAutomaticAdaptativeStreaming(GetParameterInt("ram"));
m_Random->GetStreamer()->SetAutomaticAdaptativeStreaming(GetParameterInt("ram"));
// Get field name
std::vector<int> selectedCFieldIdx = GetSelectedItems("field");
if(selectedCFieldIdx.empty())
{
otbAppLogFATAL(<<"No field has been selected for data labelling!");
}
std::vector<std::string> cFieldNames = GetChoiceNames("field");
std::string fieldName = cFieldNames[selectedCFieldIdx.front()];
m_ReaderStat->SetFileName(this->GetParameterString("instats"));
ClassCountMapType classCount = m_ReaderStat->GetStatisticMapByName<ClassCountMapType>("samplesPerClass");
......@@ -377,7 +419,7 @@ private:
m_Periodic->SetInput(this->GetParameterImage("in"));
m_Periodic->SetOGRData(reprojVector);
m_Periodic->SetOutputPositionContainerAndRates(outputSamples, rates);
m_Periodic->SetFieldName(this->GetParameterString("field"));
m_Periodic->SetFieldName(fieldName);
m_Periodic->SetLayerIndex(this->GetParameterInt("layer"));
m_Periodic->SetSamplerParameters(param);
if (IsParameterEnabled("mask") && HasValue("mask"))
......@@ -395,7 +437,7 @@ private:
m_Random->SetInput(this->GetParameterImage("in"));
m_Random->SetOGRData(reprojVector);
m_Random->SetOutputPositionContainerAndRates(outputSamples, rates);
m_Random->SetFieldName(this->GetParameterString("field"));
m_Random->SetFieldName(fieldName);
m_Random->SetLayerIndex(this->GetParameterInt("layer"));
if (IsParameterEnabled("mask") && HasValue("mask"))
{
......
......@@ -109,11 +109,11 @@ private:
AddParameter(ParameterType_ListView, "feat", "Field names for training features.");
SetParameterDescription("feat","List of field names in the input vector data to be used as features for training.");
AddParameter(ParameterType_String,"cfield","Field containing the class id for supervision");
AddParameter(ParameterType_ListView,"cfield","Field containing the class id for supervision");
SetParameterDescription("cfield","Field containing the class id for supervision. "
"Only geometries with this field available will be taken into account.");
SetParameterString("cfield","class");
SetListViewSingleSelectionMode("cfield",true);
AddParameter(ParameterType_Int, "layer", "Layer Index");
SetParameterDescription("layer", "Index of the layer to use in the input vector file.");
MandatoryOff("layer");
......@@ -155,14 +155,27 @@ private:
ogr::Feature feature = layer.ogr().GetNextFeature();
ClearChoices("feat");
ClearChoices("cfield");
for(int iField=0; iField<feature.ogr().GetFieldCount(); iField++)
{
std::string key, item = feature.ogr().GetFieldDefnRef(iField)->GetNameRef();
key = item;
std::string::iterator end = std::remove_if(key.begin(),key.end(),IsNotAlphaNum);
std::transform(key.begin(), end, key.begin(), tolower);
key="feat."+key.substr(0, end - key.begin());
AddChoice(key,item);
OGRFieldType fieldType = feature.ogr().GetFieldDefnRef(iField)->GetType();
if(fieldType == OFTInteger || ogr::version_proxy::IsOFTInteger64(fieldType) || fieldType == OFTReal)
{
std::string tmpKey="feat."+key.substr(0, end - key.begin());
AddChoice(tmpKey,item);
}
if(fieldType == OFTString || fieldType == OFTInteger || ogr::version_proxy::IsOFTInteger64(fieldType))
{
std::string tmpKey="cfield."+key.substr(0, end - key.begin());
AddChoice(tmpKey,item);
}
}
}
}
......@@ -248,13 +261,29 @@ void DoExecute()
// Prepare selected field names (their position may change between two inputs)
std::vector<int> selectedIdx = GetSelectedItems("feat");
std::vector<int> selectedCFieldIdx = GetSelectedItems("cfield");
if(selectedIdx.empty())
{
otbAppLogFATAL(<<"No features have been selected to train the classifier on!");
}
if(selectedCFieldIdx.empty())
{
otbAppLogFATAL(<<"No field has been selected for data labelling!");
}
const unsigned int nbFeatures = selectedIdx.size();
std::vector<std::string> fieldNames = GetChoiceNames("feat");
std::vector<std::string> cFieldNames = GetChoiceNames("cfield");
std::vector<std::string> selectedNames(nbFeatures);
for (unsigned int i=0 ; i<nbFeatures ; i++)
{
selectedNames[i] = fieldNames[selectedIdx[i]];
}
std::string selectedCFieldName = cFieldNames[selectedCFieldIdx.front()];
std::vector<int> featureFieldIndex(nbFeatures, -1);
int cFieldIndex = -1;
......@@ -307,9 +336,9 @@ void DoExecute()
// Check all needed fields are present :
// - check class field
cFieldIndex = feature.ogr().GetFieldIndex(GetParameterString("cfield").c_str());
cFieldIndex = feature.ogr().GetFieldIndex(selectedCFieldName.c_str());
if (cFieldIndex < 0)
otbAppLogFATAL("The field name for class label ("<<GetParameterString("cfield")
otbAppLogFATAL("The field name for class label ("<<selectedCFieldName
<<") has not been found in the input vector file! Choices are "<< availableFields);
// - check feature fields
for (unsigned int i=0 ; i<nbFeatures ; i++)
......
......@@ -901,7 +901,7 @@ otb_test_application(NAME apTvClSampleExtraction
APP SampleExtraction
OPTIONS -in ${INPUTDATA}/Classification/QB_1_ortho.tif
-vec ${INPUTDATA}/Classification/apTvClSampleSelectionOut.sqlite
-field Class
-field class
-out ${TEMP}/apTvClSampleExtractionOut.sqlite
VALID --compare-ogr ${NOTOL}
${OTBAPP_BASELINE_FILES}/apTvClSampleExtractionOut.sqlite
......
......@@ -299,6 +299,16 @@ public:
*/
void SetMaximumParameterFloatValue(std::string parameter, float value);
/**
* Enable single selection mode for list view if status in true
* (default is false).
*
* Can be called for types:
* \li ParameterType_ListView
*/
void SetListViewSingleSelectionMode(std::string parameter, bool status);
/* Set a string value
*
* Can be called for types :
......
......@@ -50,6 +50,10 @@ public:
/** RTTI support */
itkTypeMacro(ListViewParameter, Parameter);
itkSetMacro(SingleSelection,bool);
itkGetMacro(SingleSelection,bool);
itkBooleanMacro(SingleSelection);
/** Add a value to the choice */
void AddChoice( std::string choicekey, std::string choiceName );
......@@ -164,6 +168,7 @@ protected:
std::vector<int> m_SelectedItems;
std::vector<std::string> m_SelectedKeys;
std::vector<std::string> m_SelectedNames;
bool m_SingleSelection;
private:
ListViewParameter(const ListViewParameter &); //purposely not implemented
......
......@@ -735,6 +735,20 @@ void Application::SetMaximumParameterFloatValue(std::string parameter, float val
}
void Application::SetListViewSingleSelectionMode(std::string parameter, bool status)
{
Parameter* param = GetParameterByKey(parameter);
if (dynamic_cast<ListViewParameter*>(param))
{
ListViewParameter* paramListView = dynamic_cast<ListViewParameter*>(param);
paramListView->SetSingleSelection(status);
}
else
itkExceptionMacro(<<parameter << "parameter can't be casted to ListView");
}
void Application::SetParameterString(std::string parameter, std::string value)
{
......
......@@ -23,7 +23,12 @@ namespace Wrapper
{
ListViewParameter::ListViewParameter()
: m_CurrentChoice(0)
: m_ChoiceList(),
m_CurrentChoice(0),
m_SelectedItems(),
m_SelectedKeys(),
m_SelectedNames(),
m_SingleSelection(false)
{
}
......@@ -151,6 +156,12 @@ ListViewParameter::SetSelectedNames(std::vector<std::string> selectedNames)
{
std::vector<int> selectedItems;
std::vector<std::string> names = this->GetChoiceNames();
if(m_SingleSelection && selectedNames.size() > 1)
{
itkExceptionMacro(<<"Single selection mode is on, but there are "<<selectedNames.size()<<" selected items");
}
for(unsigned int i=0; i<selectedNames.size(); i++)
{
const std::string selectedName = selectedNames[i];
......@@ -187,7 +198,13 @@ ListViewParameter::SetSelectedKeys(std::vector<std::string> selectedKeys)
{
std::vector<int> selectedItems;
std::vector<std::string> keys = this->GetChoiceKeys();
for(unsigned int i=0; i<selectedKeys.size(); i++)
if(m_SingleSelection && m_SelectedKeys.size() > 1)
{
itkExceptionMacro(<<"Single selection mode is on, but there are "<<m_SelectedKeys.size()<<" selected items");
}
for(unsigned int i=0; i<m_SelectedKeys.size(); i++)
{
const std::string selectedKey = selectedKeys[i];
unsigned int j(0);
......
......@@ -141,37 +141,27 @@ ParameterGroup::AddChoice(std::string paramKey, std::string paramName)
void
ParameterGroup::ClearChoices(std::string paramKey)
{
ParameterKey pKey( paramKey );
// Split the parameter name
std::vector<std::string> splitKey = pKey.Split();
std::string parentkey;
Parameter::Pointer parentParam;
Parameter * param = GetParameterByKey(paramKey);
if (splitKey.size() > 1)
{
parentkey = pKey.GetRoot();
parentParam = GetParameterByKey(parentkey);
}
else
if(!param)
{
parentParam = GetParameterByKey(splitKey[0]);
itkExceptionMacro("Parameter "<<paramKey<<" not found");
}
// param must be a choice, a listBox or this is an error
ListViewParameter* paramAsChoice = dynamic_cast<ListViewParameter*>(param);
// parentParam must be a choice, a listBox or this is an error
ListViewParameter* listBoxParentAsChoice = dynamic_cast<ListViewParameter*>(parentParam.GetPointer());
if (listBoxParentAsChoice)
if (paramAsChoice)
{
listBoxParentAsChoice->ClearChoices();
paramAsChoice->ClearChoices();
}
else
{
itkExceptionMacro(<<parentkey << " is not a ListView");
itkExceptionMacro(<<paramKey << " is not a ListView");
}
}
void ParameterGroup::AddOutXMLParameter()
{
Parameter::Pointer tmpParam;
......@@ -216,34 +206,25 @@ void ParameterGroup::AddInXMLParameter()
std::vector<int>
ParameterGroup::GetSelectedItems(std::string paramKey)
{
std::vector<int> selectedItems;
ParameterKey pKey( paramKey );
// Split the parameter name
std::vector<std::string> splitKey = pKey.Split();
Parameter * param = GetParameterByKey(paramKey);
std::string parentkey;
Parameter::Pointer parentParam;
if (splitKey.size() > 1)
if(!param)
{
parentkey = pKey.GetRoot();
parentParam = GetParameterByKey(parentkey);
itkExceptionMacro("Parameter "<<paramKey<<" not found");
}
else