Skip to content

Commit

Permalink
Allow staged tags to be referenced as pipeline command options.
Browse files Browse the repository at this point in the history
  • Loading branch information
abellgithub committed Jan 30, 2017
1 parent 6d7170d commit d1da72c
Show file tree
Hide file tree
Showing 15 changed files with 337 additions and 100 deletions.
26 changes: 26 additions & 0 deletions doc/apps/pipeline.rst
Expand Up @@ -38,4 +38,30 @@ The ``pipeline`` command is used to execute :ref:`pipeline` JSON. See
$ pdal pipeline translate.json --writers.las.filename=output.laz \
--readers.las.filename=input.las

Option substitution can also refer to the tag of an individual stage.
This can be done by using the syntax --stage.<tagname>.<option>. This
allows options to be set on individual stages, even if there are multiple
stages of the same type. For example, if a pipeline contained two LAS
readers with tags ``las1`` and ``las2`` respectively, the following
command would allow assignment of different filenames to each stage:

::

{
"pipeline" : [
{
"tag" : "las1",
"type" : "readers.las"
},
{
"tag" : "las2",
"type" : "readers.las"
},
"placeholder.laz"
]
}

$ pdal pipeline translate.json --writers.las.filename=output.laz \
--stage.las1.filename=file1.las --stage.las2.filename=file2.las

Options specified by tag names override options specified by stage types.
6 changes: 6 additions & 0 deletions kernels/PipelineKernel.cpp
Expand Up @@ -65,6 +65,12 @@ void PipelineKernel::validateSwitches(ProgramArgs& args)
}


bool PipelineKernel::isStagePrefix(const std::string& stage)
{
return Kernel::isStagePrefix(stage) || stage == "stage";
}


void PipelineKernel::addSwitches(ProgramArgs& args)
{
args.add("input,i", "input file name", m_inputFile).setOptionalPositional();
Expand Down
1 change: 1 addition & 0 deletions kernels/PipelineKernel.hpp
Expand Up @@ -58,6 +58,7 @@ class PDAL_DLL PipelineKernel : public Kernel
PipelineKernel();
void addSwitches(ProgramArgs& args);
void validateSwitches(ProgramArgs& args);
virtual bool isStagePrefix(const std::string& stage);

std::string m_inputFile;
std::string m_pipelineFile;
Expand Down
17 changes: 16 additions & 1 deletion pdal/Options.cpp
Expand Up @@ -58,7 +58,6 @@ void Option::toMetadata(MetadataNode& parent) const

}

//---------------------------------------------------------------------------

bool Option::nameValid(const std::string& name, bool reportError)
{
Expand All @@ -74,13 +73,22 @@ bool Option::nameValid(const std::string& name, bool reportError)
}


//---------------------------------------------------------------------------


void Options::add(const Option& option)
{
assert(Option::nameValid(option.getName(), true));
m_options.insert({ option.getName(), option });
}


void Options::add(const Options& o)
{
m_options.insert(o.m_options.begin(), o.m_options.end());
}


void Options::addConditional(const Option& option)
{
assert(Option::nameValid(option.getName(), true));
Expand All @@ -89,6 +97,13 @@ void Options::addConditional(const Option& option)
}


void Options::addConditional(const Options& options)
{
for (auto& o : options.m_options)
addConditional(o.second);
}


void Options::remove(const Option& option)
{
m_options.erase(option.getName());
Expand Down
2 changes: 2 additions & 0 deletions pdal/Options.hpp
Expand Up @@ -129,7 +129,9 @@ class PDAL_DLL Options
{ add(opt); }

void add(const Option& option);
void add(const Options& options);
void addConditional(const Option& option);
void addConditional(const Options& option);

// if option name not present, just returns
void remove(const Option& option);
Expand Down
157 changes: 104 additions & 53 deletions pdal/PipelineManager.cpp
Expand Up @@ -39,6 +39,10 @@

#include "private/PipelineReaderXML.hpp"

#if defined(PDAL_COMPILER_CLANG) || defined(PDAL_COMPILER_GCC)
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif

namespace pdal
{

Expand Down Expand Up @@ -93,7 +97,14 @@ void PipelineManager::readPipeline(const std::string& filename)
{
Utils::closeFile(m_input);
m_input = Utils::openFile(filename);
readPipeline(*m_input);
try
{
readPipeline(*m_input);
}
catch (const pdal_error& err)
{
throw pdal_error(filename + ": " + err.what());
}
}
}

Expand Down Expand Up @@ -154,7 +165,8 @@ void PipelineManager::validateStageOptions() const
const std::string& stageName = si.first;
auto it = std::find_if(m_stages.begin(), m_stages.end(),
[stageName](Stage *s)
{ return (s->getName() == stageName); });
{ return (s->getName() == stageName ||
"stage." + s->tag() == stageName); });

// If the option stage name matches no created stage, then error.
if (it == m_stages.end())
Expand Down Expand Up @@ -230,112 +242,141 @@ MetadataNode PipelineManager::getMetadata() const
return output;
}


Stage& PipelineManager::makeReader(const std::string& inputFile,
std::string driver)
{
static Options nullOpts;
StageCreationOptions ops { inputFile, driver };

return makeReader(inputFile, driver, nullOpts);
return makeReader(ops);
}


Stage& PipelineManager::makeReader(const std::string& inputFile,
std::string driver, Options options)
{
if (driver.empty())
StageCreationOptions ops { inputFile, driver, nullptr, options };

return makeReader(ops);
}


Stage& PipelineManager::makeReader(StageCreationOptions& o)
{
if (o.m_driver.empty())
{
driver = StageFactory::inferReaderDriver(inputFile);
if (driver.empty())
o.m_driver = StageFactory::inferReaderDriver(o.m_filename);
if (o.m_driver.empty())
throw pdal_error("Cannot determine reader for input file: " +
inputFile);
o.m_filename);
}
if (!inputFile.empty())
options.replace("filename", inputFile);
if (!o.m_filename.empty())
o.m_options.replace("filename", o.m_filename);

Stage& reader = addReader(driver);
setOptions(reader, options);
Stage& reader = addReader(o.m_driver);
reader.setTag(o.m_tag);
setOptions(reader, o.m_options);
return reader;
}


Stage& PipelineManager::makeFilter(const std::string& driver)
{
static Options nullOps;
StageCreationOptions ops { "", driver };

Stage& filter = addFilter(driver);
setOptions(filter, nullOps);
return filter;
return makeFilter(ops);
}


Stage& PipelineManager::makeFilter(const std::string& driver, Options options)
{
Stage& filter = addFilter(driver);
setOptions(filter, options);
return filter;
StageCreationOptions ops { "", driver, nullptr, options };

return makeFilter(ops);
}


Stage& PipelineManager::makeFilter(const std::string& driver, Stage& parent)
{
static Options nullOps;
StageCreationOptions ops { "", driver, &parent };

return makeFilter(driver, parent, nullOps);
return makeFilter(ops);
}


Stage& PipelineManager::makeFilter(const std::string& driver, Stage& parent,
Options options)
{
Stage& filter = addFilter(driver);
setOptions(filter, options);
filter.setInput(parent);
StageCreationOptions ops { "", driver, &parent, options };

return makeFilter(ops);
}


Stage& PipelineManager::makeFilter(StageCreationOptions& o)
{
Stage& filter = addFilter(o.m_driver);
filter.setTag(o.m_tag);
setOptions(filter, o.m_options);
if (o.m_parent)
filter.setInput(*o.m_parent);
return filter;
}


Stage& PipelineManager::makeWriter(const std::string& outputFile,
std::string driver)
{
static Options nullOps;
StageCreationOptions ops { outputFile, driver };

return makeWriter(outputFile, driver, nullOps);
return makeWriter(ops);
}


Stage& PipelineManager::makeWriter(const std::string& outputFile,
std::string driver, Options options)
std::string driver, Stage& parent)
{
if (driver.empty())
{
driver = StageFactory::inferWriterDriver(outputFile);
if (driver.empty())
throw pdal_error("Cannot determine writer for output file: " +
outputFile);
}
StageCreationOptions ops { outputFile, driver, &parent };

if (!outputFile.empty())
options.replace("filename", outputFile);

auto& writer = addWriter(driver);
setOptions(writer, options);
return writer;
return makeWriter(ops);
}


Stage& PipelineManager::makeWriter(const std::string& outputFile,
std::string driver, Stage& parent)
std::string driver, Stage& parent, Options options)
{
static Options nullOps;
StageCreationOptions ops { outputFile, driver, &parent, options };

return makeWriter(outputFile, driver, parent, nullOps);
return makeWriter(ops);
}


Stage& PipelineManager::makeWriter(const std::string& outputFile,
std::string driver, Stage& parent, Options options)
std::string driver, Options options)
{
StageCreationOptions ops { outputFile, driver, nullptr, options };

return makeWriter(ops);
}


Stage& PipelineManager::makeWriter(StageCreationOptions& o)
{
Stage& writer = makeWriter(outputFile, driver, options);
writer.setInput(parent);
if (o.m_driver.empty())
{
o.m_driver = StageFactory::inferWriterDriver(o.m_filename);
if (o.m_driver.empty())
throw pdal_error("Cannot determine writer for output file: " +
o.m_filename);
}

if (!o.m_filename.empty())
o.m_options.replace("filename", o.m_filename);

auto& writer = addWriter(o.m_driver);
writer.setTag(o.m_tag);
setOptions(writer, o.m_options);
if (o.m_parent)
writer.setInput(*o.m_parent);
return writer;
}

Expand All @@ -351,20 +392,30 @@ void PipelineManager::setOptions(Stage& stage, const Options& addOps)
stage.addOptions(addOps);

// Apply options provided on the command line, overriding others.
Options& ops = stageOptions(stage);
Options ops = stageOptions(stage);
stage.removeOptions(ops);
stage.addOptions(ops);
}


Options& PipelineManager::stageOptions(Stage& stage)
Options PipelineManager::stageOptions(Stage& stage)
{
static Options nullOpts;
Options opts;

std::string tag = stage.tag();
if (tag.size())
{
tag = "stage." + tag;
auto oi = m_stageOptions.find(tag);
if (oi != m_stageOptions.end())
opts.add(oi->second);
}
// Tag-based options options override stagename-based options, so
// we call addConditional.
auto oi = m_stageOptions.find(stage.getName());
if (oi == m_stageOptions.end())
return nullOpts;
return oi->second;
if (oi != m_stageOptions.end())
opts.addConditional(oi->second);
return opts;
}

} // namespace pdal

0 comments on commit d1da72c

Please sign in to comment.