diff --git a/apps/pdal.cpp b/apps/pdal.cpp index d22c018c06..fdb7691989 100644 --- a/apps/pdal.cpp +++ b/apps/pdal.cpp @@ -246,11 +246,11 @@ int main(int argc, char* argv[]) // Dispatch execution to the kernel, passing all remaining args if (isValidKernel) { - int count(argc - 1); // remove the 1st argument - const char** args = const_cast(&argv[1]); + int count(argc - 2); // remove 'pdal' and the kernel name + argv += 2; void *kernel = PluginManager::createObject(fullname); std::unique_ptr app(static_cast(kernel)); - return app->run(count, args, command); + return app->run(count, const_cast(argv), command); } // Otherwise, process the remaining args to see if they are supported diff --git a/include/pdal/Kernel.hpp b/include/pdal/Kernel.hpp index 7b12ec69e8..d2211f43fa 100644 --- a/include/pdal/Kernel.hpp +++ b/include/pdal/Kernel.hpp @@ -34,8 +34,6 @@ #pragma once -#include -#include #ifdef PDAL_COMPILER_MSVC # pragma warning(push) @@ -53,6 +51,9 @@ #include #include +#include +#include + namespace po = boost::program_options; namespace pdal @@ -91,8 +92,7 @@ class PDAL_DLL Kernel Stage& makeWriter(const std::string& outputFile, Stage& parent); public: - // implement this, with calls to addOptionSet() - virtual void addSwitches() {} + virtual void addSwitches(ProgramArgs& args) {} // implement this, to do sanity checking of cmd line // will throw if the user gave us bad options @@ -104,7 +104,6 @@ class PDAL_DLL Kernel void addSwitchSet(po::options_description* options); void addHiddenSwitchSet(po::options_description* options); - void addPositionalSwitch(const char* name, int max_count); void setCommonOptions(Options &options); void setProgressShellCommand(std::vector const& command) @@ -150,22 +149,19 @@ class PDAL_DLL Kernel bool argumentSpecified(const std::string& name); bool m_usestdin; - int m_argc; - const char** m_argv; Log m_log; private: int innerRun(); - void parseSwitches(); void outputHelp(); void outputVersion(); - void addBasicSwitchSet(); + void addBasicSwitches(ProgramArgs& args); void collectExtraOptions(); - int do_switches(); - int do_startup(); - int do_execution(); - int do_shutdown(); + void doSwitches(int argc, const char *argv[], ProgramArgs& args); + int doStartup(); + int doExecution(); + int doShutdown(); static bool test_parseOption(std::string o, std::string& stage, std::string& option, std::string& value); @@ -173,7 +169,7 @@ class PDAL_DLL Kernel bool m_isDebug; uint32_t m_verboseLevel; bool m_showHelp; - std::string m_showOptions; + bool m_showOptions; bool m_showVersion; bool m_showTime; std::string m_appName; diff --git a/include/pdal/util/ProgramArgs.hpp b/include/pdal/util/ProgramArgs.hpp index 9918e10549..9ce54a9323 100644 --- a/include/pdal/util/ProgramArgs.hpp +++ b/include/pdal/util/ProgramArgs.hpp @@ -23,6 +23,7 @@ #pragma once #include +#include #include @@ -38,14 +39,27 @@ class arg_error std::string m_error; }; -class BaseArg +class Arg { protected: - BaseArg(const std::string& longname, const std::string& shortname, + Arg(const std::string& longname, const std::string& shortname, const std::string& description) : m_longname(longname), - m_shortname(shortname), m_description(description), m_set(false) + m_shortname(shortname), m_description(description), m_set(false), + m_hidden(false), m_positional(false) {} +public: + void setHidden(bool hidden = true) + { m_hidden = true; } + virtual void setPositional() + { m_positional = true; } + bool set() const + { return m_set; } + bool positional() const + { return m_positional; } + std::string name() const + { return m_longname; } + public: virtual bool needsValue() const { return true; } @@ -58,15 +72,17 @@ class BaseArg std::string m_description; std::string m_rawVal; bool m_set; + bool m_hidden; + bool m_positional; }; template -class Arg : public BaseArg +class TArg : public Arg { public: - Arg(const std::string& longname, const std::string& shortname, + TArg(const std::string& longname, const std::string& shortname, const std::string& description, T& variable, T def) : - BaseArg(longname, shortname, description), m_var(variable), + Arg(longname, shortname, description), m_var(variable), m_defaultVal(def) { m_var = m_defaultVal; } @@ -93,6 +109,7 @@ class Arg : public BaseArg { m_var = m_defaultVal; m_set = false; + m_hidden = false; } private: @@ -101,12 +118,12 @@ class Arg : public BaseArg }; template <> -class Arg : public BaseArg +class TArg : public Arg { public: - Arg(const std::string& longname, const std::string& shortname, + TArg(const std::string& longname, const std::string& shortname, const std::string& description, bool& variable, bool def) : - BaseArg(longname, shortname, description), m_val(variable), + Arg(longname, shortname, description), m_val(variable), m_defaultVal(def) {} @@ -128,6 +145,13 @@ class Arg : public BaseArg { m_val = m_defaultVal; m_set = false; + m_hidden = false; + } + virtual void setPositional() + { + std::ostringstream oss; + oss << "Boolean argument '" << m_longname << "' can't be positional."; + throw arg_error(oss.str()); } private: @@ -138,14 +162,14 @@ class Arg : public BaseArg class ProgramArgs { public: - void add(const std::string& name, const std::string description, + Arg *add(const std::string& name, const std::string description, std::string& var, std::string def) { - add(name, description, var, def); + return add(name, description, var, def); } template - void add(const std::string& name, const std::string description, T& var, + Arg *add(const std::string& name, const std::string description, T& var, T def = T()) { // Arg names must be specified as "longname[,shortname]" where @@ -161,12 +185,13 @@ class ProgramArgs if (s.size() == 1) s.push_back(""); - BaseArg *arg = new Arg(s[0], s[1], description, var, def); + Arg *arg = new TArg(s[0], s[1], description, var, def); if (s[0].size()) m_longargs[s[0]] = arg; if (s[1].size()) m_shortargs[s[1]] = arg; - m_args.push_back(std::unique_ptr(arg)); + m_args.push_back(std::unique_ptr(arg)); + return arg; } void parse(int argc, char *argv[]) @@ -188,6 +213,25 @@ class ProgramArgs std::string value((i != s.size() - 1) ? s[i + 1] : "-"); i += parseArg(arg, value); } + + // Go through things on the positional list looking for matches. + for (auto ai = m_args.begin(); ai != m_args.end(); ++ai) + { + Arg *arg = ai->get(); + if (arg->positional() && !arg->set()) + { + if (m_positional.empty()) + { + std::ostringstream oss; + + oss << "Missing value for positional argument '" << + arg->name() << "'."; + throw arg_error(oss.str()); + } + arg->setValue(m_positional.front()); + m_positional.pop(); + } + } } void reset() @@ -197,7 +241,7 @@ class ProgramArgs } private: - BaseArg *findLongArg(const std::string& s) + Arg *findLongArg(const std::string& s) { auto si = m_longargs.find(s); if (si != m_longargs.end()) @@ -205,7 +249,7 @@ class ProgramArgs return NULL; } - BaseArg *findShortArg(char c) + Arg *findShortArg(char c) { std::string s(1, c); auto si = m_shortargs.find(s); @@ -216,11 +260,11 @@ class ProgramArgs int parseArg(std::string& arg, std::string value) { - if (arg.size() > 2 && arg[0] == '-' && arg[1] == '-') + if (arg.size() > 1 && arg[0] == '-' && arg[1] == '-') return parseLongArg(arg, value); - else if (arg.size() > 1 && arg[0] == '-') + else if (arg.size() && arg[0] == '-') return parseShortArg(arg, value); - m_unrecognized.push_back(arg); + m_positional.push(arg); return 1; } @@ -228,6 +272,9 @@ class ProgramArgs { bool attachedValue = false; + if (name.size() == 2) + throw arg_error("No argument found following '--'."); + name = name.substr(2); std::size_t pos = name.find_first_of("="); @@ -240,7 +287,7 @@ class ProgramArgs attachedValue = true; } } - BaseArg *arg = findLongArg(name); + Arg *arg = findLongArg(name); if (!arg) { std::ostringstream oss; @@ -272,7 +319,7 @@ class ProgramArgs if (name.size() == 1) throw arg_error("No argument found following '-'."); - BaseArg *arg = findShortArg(name[1]); + Arg *arg = findShortArg(name[1]); if (!arg) { std::ostringstream oss; @@ -306,10 +353,13 @@ class ProgramArgs return cnt; } - std::vector> m_args; - std::map m_shortargs; - std::map m_longargs; - std::vector m_unrecognized; + std::vector> m_args; + std::map m_shortargs; + std::map m_longargs; + + // Contains remaining arguments after positional argument have been + // processed. + std::queue m_positional; }; } // namespace pdal diff --git a/kernels/delta/DeltaKernel.cpp b/kernels/delta/DeltaKernel.cpp index 28c61ad29c..47072766c4 100644 --- a/kernels/delta/DeltaKernel.cpp +++ b/kernels/delta/DeltaKernel.cpp @@ -52,39 +52,20 @@ DeltaKernel::DeltaKernel() : m_3d(true), m_detail(false), m_allDims(false) {} -void DeltaKernel::addSwitches() +void DeltaKernel::addSwitches(ProgramArgs& args) { - namespace po = boost::program_options; - - po::options_description* file_options = - new po::options_description("file options"); - - file_options->add_options() - ("source", po::value(&m_sourceFile), - "source file name") - ("candidate", po::value(&m_candidateFile), - "candidate file name") - ("output", po::value(&m_outputFile), - "output file name") - ("2d", po::value(&m_3d)->zero_tokens()->implicit_value(false), - "only 2D comparisons/indexing") - ("detail", - po::value(&m_detail)->zero_tokens()->implicit_value(true), - "Output deltas per-point") - ("alldims", - po::value(&m_allDims)->zero_tokens()->implicit_value(true), - "Compute diffs for all dimensions (not just X,Y,Z)"); - addSwitchSet(file_options); - - po::options_description* processing_options = - new po::options_description("processing options"); - - processing_options->add_options(); - addSwitchSet(processing_options); - - addPositionalSwitch("source", 1); - addPositionalSwitch("candidate", 2); - addPositionalSwitch("output", 3); + Arg *arg; + + arg = args.add("source", "source file name", m_sourceFile); + arg->setPositional(); + arg = args.add("candidate", "candidate file name", m_candidateFile); + arg->setPositional(); + arg = args.add("output", "output file name", m_outputFile); + arg->setPositional(); + args.add("2d", "only 2D comparisons/indexing", m_3d, true); + args.add("detail", "Output deltas per-point", m_detail); + args.add("alldims", "Compute diffs for all dimensions (not just X,Y,Z)", + m_allDims); } diff --git a/kernels/delta/DeltaKernel.hpp b/kernels/delta/DeltaKernel.hpp index 499cde08cf..6fd29d5447 100644 --- a/kernels/delta/DeltaKernel.hpp +++ b/kernels/delta/DeltaKernel.hpp @@ -75,7 +75,7 @@ class PDAL_DLL DeltaKernel : public Kernel private: DeltaKernel(); - void addSwitches(); + void addSwitches(ProgramArgs& args); PointViewPtr loadSet(const std::string& filename, PointTable& table); MetadataNode dump(PointViewPtr& srcView, PointViewPtr& candView, KD3Index& index, DimIndexMap& dims); diff --git a/kernels/diff/DiffKernel.cpp b/kernels/diff/DiffKernel.cpp index 04d03fd5e3..39dda8baef 100644 --- a/kernels/diff/DiffKernel.cpp +++ b/kernels/diff/DiffKernel.cpp @@ -66,29 +66,13 @@ void DiffKernel::validateSwitches() } -void DiffKernel::addSwitches() +void DiffKernel::addSwitches(ProgramArgs& args) { - namespace po = boost::program_options; - - po::options_description* file_options = - new po::options_description("file options"); - - file_options->add_options() - ("source", po::value(&m_sourceFile), "source file name") - ("candidate", po::value(&m_candidateFile), - "candidate file name") - ; - - addSwitchSet(file_options); - po::options_description* processing_options = - new po::options_description("processing options"); - - processing_options->add_options(); - - addSwitchSet(processing_options); - - addPositionalSwitch("source", 1); - addPositionalSwitch("candidate", 2); + Arg *arg; + arg = args.add("source", "Source filename", m_sourceFile); + arg->setPositional(); + arg = args.add("candidate", "Candidate filename", m_candidateFile); + arg->setPositional(); } diff --git a/kernels/diff/DiffKernel.hpp b/kernels/diff/DiffKernel.hpp index f2e55c7866..1bce21193a 100644 --- a/kernels/diff/DiffKernel.hpp +++ b/kernels/diff/DiffKernel.hpp @@ -58,7 +58,7 @@ class PDAL_DLL DiffKernel : public Kernel private: - void addSwitches(); // overrride + virtual void addSwitches(ProgramArgs& args); void validateSwitches(); // overrride void checkPoints(const PointView& source_data, diff --git a/kernels/info/InfoKernel.cpp b/kernels/info/InfoKernel.cpp index 191ace7f00..b8b672fb2a 100644 --- a/kernels/info/InfoKernel.cpp +++ b/kernels/info/InfoKernel.cpp @@ -122,64 +122,34 @@ void InfoKernel::validateSwitches() } -void InfoKernel::addSwitches() +void InfoKernel::addSwitches(ProgramArgs& args) { - namespace po = boost::program_options; - - po::options_description* file_options = - new po::options_description("file options"); - - file_options->add_options() - ("input,i", po::value(&m_inputFile)->default_value(""), - "input file name") - ; - - addSwitchSet(file_options); - - po::options_description* processing_options = - new po::options_description("processing options"); - - processing_options->add_options() - ("all", - po::value(&m_showAll)->zero_tokens()->implicit_value(true), - "dump statistics, schema and metadata") - ("point,p", po::value(&m_pointIndexes), "point to dump") - ("query", po::value< std::string>(&m_queryPoint), + Arg *arg; + + arg = args.add("input,i", "input file name", m_inputFile); + arg->setPositional(); + args.add("all", "dump statistics, schema and metadata", m_showAll); + args.add("point,p", "point to dump\n--point=\"1-5,10,100-200\"", + m_pointIndexes); + args.add("query", "Return points in order of distance from the specified " "location (2D or 3D)\n" - "--query Xcoord,Ycoord[,Zcoord][/count]") - ("stats", - po::value(&m_showStats)->zero_tokens()->implicit_value(true), - "dump stats on all points (reads entire dataset)") - ("boundary", - po::value(&m_boundary)->zero_tokens()->implicit_value(true), - "compute a hexagonal hull/boundary of dataset") - ("dimensions", po::value(&m_dimensions), - "dimensions on which to compute statistics") - ("schema", - po::value(&m_showSchema)->zero_tokens()->implicit_value(true), - "dump the schema") - ("pipeline-serialization", - po::value(&m_pipelineFile)->default_value(""), "") - ("summary", - po::value(&m_showSummary)->zero_tokens()->implicit_value(true), - "dump summary of the info") - ("metadata", - po::value(&m_showMetadata)->zero_tokens()->implicit_value(true), - "dump file metadata info") - ; - - po::options_description* hidden = - new po::options_description("Hidden options"); - hidden->add_options() - ("pointcloudschema", - po::value(&m_PointCloudSchemaOutput), - "dump PointCloudSchema XML output") - ; - - addSwitchSet(processing_options); - addHiddenSwitchSet(hidden); - addPositionalSwitch("input", 1); + "--query Xcoord,Ycoord[,Zcoord][/count]", + m_queryPoint); + args.add("stats", "dump stats on all points (reads entire dataset)", + m_showStats); + args.add("boundary", "compute a hexagonal hull/boundary of dataset", + m_boundary); + args.add("dimensions", "dimensions on which to compute statistics", + m_dimensions); + args.add("schema", "dump the schema", m_showSchema); + args.add("pipeline-serialization", "Output file for pipeline serialization", + m_pipelineFile); + args.add("summary", "dump summary of the info", m_showSummary); + args.add("metadata", "dump file metadata info", m_showMetadata); + arg = args.add("pointcloudschema", "dump PointCloudSchema XML output", + m_PointCloudSchemaOutput); + arg->setHidden(); } // Support for parsing point numbers. Points can be specified singly or as diff --git a/kernels/info/InfoKernel.hpp b/kernels/info/InfoKernel.hpp index 38acf8d4cc..456b9d96bf 100644 --- a/kernels/info/InfoKernel.hpp +++ b/kernels/info/InfoKernel.hpp @@ -74,7 +74,7 @@ class PDAL_DLL InfoKernel : public Kernel private: InfoKernel(); - void addSwitches(); // overrride + void addSwitches(ProgramArgs& args); void validateSwitches(); // overrride void dump(MetadataNode& root); diff --git a/kernels/merge/MergeKernel.cpp b/kernels/merge/MergeKernel.cpp index 7cf15458d5..00f662ee7d 100644 --- a/kernels/merge/MergeKernel.cpp +++ b/kernels/merge/MergeKernel.cpp @@ -56,18 +56,13 @@ std::string MergeKernel::getName() const } -void MergeKernel::addSwitches() +void MergeKernel::addSwitches(ProgramArgs& args) { - po::options_description* file_options = - new po::options_description("file options"); +// args.add("files,f", "input/output files", m_files); - file_options->add_options() - ("files,f", po::value(&m_files)->multitoken(), - "input/output files") - ; - - addSwitchSet(file_options); + /** addPositionalSwitch("files", 10000); + **/ } diff --git a/kernels/merge/MergeKernel.hpp b/kernels/merge/MergeKernel.hpp index a1ea3adbed..61d8832ae6 100644 --- a/kernels/merge/MergeKernel.hpp +++ b/kernels/merge/MergeKernel.hpp @@ -51,7 +51,7 @@ class PDAL_DLL MergeKernel : public Kernel int execute(); private: - void addSwitches(); + void addSwitches(ProgramArgs& args); void validateSwitches(); StringList m_files; diff --git a/kernels/pipeline/PipelineKernel.cpp b/kernels/pipeline/PipelineKernel.cpp index a6744b531c..9a91e99000 100644 --- a/kernels/pipeline/PipelineKernel.cpp +++ b/kernels/pipeline/PipelineKernel.cpp @@ -68,38 +68,24 @@ void PipelineKernel::validateSwitches() } -void PipelineKernel::addSwitches() +void PipelineKernel::addSwitches(ProgramArgs& args) { - po::options_description* file_options = - new po::options_description("file options"); - - file_options->add_options() - ("input,i", po::value(&m_inputFile)->default_value(""), - "input file name") - ("pipeline-serialization", - po::value(&m_pipelineFile)->default_value(""), "") - ("validate", - po::value(&m_validate)->zero_tokens()->implicit_value(true), - "Validate the pipeline (including serialization), but do not " - "execute writing of points") - ("progress", po::value(&m_progressFile), - "Name of file or FIFO to which stages should write progress " - "information. The file/FIFO must exist. PDAL will not create " - "the progress file.") - ; - - addSwitchSet(file_options); - addPositionalSwitch("input", 1); - - po::options_description* hidden = - new po::options_description("Hidden options"); - hidden->add_options() - ("pointcloudschema", - po::value(&m_PointCloudSchemaOutput), - "dump PointCloudSchema XML output") - ; - - addHiddenSwitchSet(hidden); + Arg *arg; + + arg = args.add("input,i", "input file name", m_inputFile); + arg->setPositional(); + args.add("pipeline-serialization", "Output file for pipeline serialization", + m_pipelineFile); + args.add("validate", "Validate the pipeline (including serialization), " + "but do not write points", m_validate); + args.add("progress", + "Name of file or FIFO to which stages should write progress " + "information. The file/FIFO must exist. PDAL will not create " + "the progress file.", + m_progressFile); + arg = args.add("pointcloudschema", "dump PointCloudSchema XML output", + m_PointCloudSchemaOutput); + arg->setHidden(); } int PipelineKernel::execute() diff --git a/kernels/pipeline/PipelineKernel.hpp b/kernels/pipeline/PipelineKernel.hpp index 3ad5165e15..f694b879ba 100644 --- a/kernels/pipeline/PipelineKernel.hpp +++ b/kernels/pipeline/PipelineKernel.hpp @@ -56,7 +56,7 @@ class PDAL_DLL PipelineKernel : public Kernel private: PipelineKernel(); - void addSwitches(); + void addSwitches(ProgramArgs& args); void validateSwitches(); std::string m_inputFile; diff --git a/kernels/random/RandomKernel.cpp b/kernels/random/RandomKernel.cpp index f6ecd5b117..7554f53339 100644 --- a/kernels/random/RandomKernel.cpp +++ b/kernels/random/RandomKernel.cpp @@ -63,23 +63,24 @@ void RandomKernel::validateSwitches() } -void RandomKernel::addSwitches() +void RandomKernel::addSwitches(ProgramArgs& args) { - po::options_description* file_options = new po::options_description("file options"); - - file_options->add_options() - ("output,o", po::value(&m_outputFile)->default_value(""), "output file name") - ("compress,z", po::value(&m_bCompress)->zero_tokens()->implicit_value(true), "Compress output data (if supported by output format)") - ("count", po::value(&m_numPointsToWrite)->default_value(0), "How many points should we write?") - ("bounds", po::value(&m_bounds), "Extent (in XYZ to clip output to)") - ("mean", po::value< std::string >(&m_means), "A comma-separated or quoted, space-separated list of means (normal mode): \n--mean 0.0,0.0,0.0\n--mean \"0.0 0.0 0.0\"") - ("stdev", po::value< std::string >(&m_stdevs), "A comma-separated or quoted, space-separated list of standard deviations (normal mode): \n--stdev 0.0,0.0,0.0\n--stdev \"0.0 0.0 0.0\"") - ("distribution", po::value(&m_distribution)->default_value("uniform"), "Distribution (uniform / normal)") - ; - - addSwitchSet(file_options); - - addPositionalSwitch("output", 1); + Arg *arg; + + arg = args.add("output,o", "Output file name", m_outputFile); + arg->setPositional(); + args.add("compress,z", + "Compress output data (if supported by output format)", m_bCompress); + args.add("count", "How many points should we write?", m_numPointsToWrite); + args.add("bounds", "Extent (in XYZ to clip output to)", m_bounds); + args.add("mean", "A comma-separated or quoted, space-separated list " + "of means (normal mode): \n--mean 0.0,0.0,0.0\n--mean \"0.0 0.0 0.0\"", + m_means); + args.add("stdev", "A comma-separated or quoted, space-separated list " + "of standard deviations (normal mode): \n" + "--stdev 0.0,0.0,0.0\n--stdev \"0.0 0.0 0.0\"", m_stdevs); + args.add("distribution", "Distribution (uniform / normal)", m_distribution, + "uniform"); } diff --git a/kernels/random/RandomKernel.hpp b/kernels/random/RandomKernel.hpp index 0683328843..75c3192d91 100644 --- a/kernels/random/RandomKernel.hpp +++ b/kernels/random/RandomKernel.hpp @@ -60,7 +60,7 @@ class PDAL_DLL RandomKernel : public Kernel private: RandomKernel(); - void addSwitches(); + void addSwitches(ProgramArgs& arg); void validateSwitches(); Stage& makeReader(Options readerOptions); diff --git a/kernels/sort/SortKernel.cpp b/kernels/sort/SortKernel.cpp index 531c04f353..1da7e259d9 100644 --- a/kernels/sort/SortKernel.cpp +++ b/kernels/sort/SortKernel.cpp @@ -69,27 +69,19 @@ void SortKernel::validateSwitches() } -void SortKernel::addSwitches() +void SortKernel::addSwitches(ProgramArgs& args) { - po::options_description* file_options = - new po::options_description("file options"); - - file_options->add_options() - ("input,i", po::value(&m_inputFile)->default_value(""), - "input file name") - ("output,o", po::value(&m_outputFile)->default_value(""), - "output file name") - ("compress,z", - po::value(&m_bCompress)->zero_tokens()->implicit_value(true), - "Compress output data (if supported by output format)") - ("metadata,m", - po::value< bool >(&m_bForwardMetadata)->implicit_value(true), - "Forward metadata (VLRs, header entries, etc) from previous stages") - ; - - addSwitchSet(file_options); - addPositionalSwitch("input", 1); - addPositionalSwitch("output", 1); + Arg *arg; + + arg = args.add("input,i", "Input filename", m_inputFile); + arg->setPositional(); + arg = args.add("output,o", "Output filename", m_outputFile); + arg->setPositional(); + args.add("compress,z", + "Compress output data (if supported by output format)", m_bCompress); + args.add("metadata,m", + "Forward metadata (VLRs, header entries, etc) from previous stages", + m_bForwardMetadata); } diff --git a/kernels/sort/SortKernel.hpp b/kernels/sort/SortKernel.hpp index 53a14a6f75..498e004609 100644 --- a/kernels/sort/SortKernel.hpp +++ b/kernels/sort/SortKernel.hpp @@ -52,7 +52,7 @@ class PDAL_DLL SortKernel : public Kernel private: SortKernel(); - void addSwitches(); + void addSwitches(ProgramArgs& args); void validateSwitches(); Stage& makeReader(Options readerOptions); diff --git a/kernels/split/SplitKernel.cpp b/kernels/split/SplitKernel.cpp index ed739cb458..91caaaaded 100644 --- a/kernels/split/SplitKernel.cpp +++ b/kernels/split/SplitKernel.cpp @@ -59,29 +59,20 @@ std::string SplitKernel::getName() const double m_xOrigin; double m_yOrigin; -void SplitKernel::addSwitches() +void SplitKernel::addSwitches(ProgramArgs& args) { - po::options_description* file_options = - new po::options_description("file options"); - - file_options->add_options() - ("length", po::value(&m_length)->default_value(0.0), - "Edge length for splitter cells") - ("capacity", po::value(&m_capacity)->default_value(0), - "Point capacity of chipper cells") - ("origin_x", po::value(&m_xOrigin)->default_value(std::numeric_limits::quiet_NaN()), - "Origin in X axis for splitter cells") - ("origin_y", po::value(&m_yOrigin)->default_value(std::numeric_limits::quiet_NaN()), - "Origin in Y axis for splitter cells") - ("input,i", po::value(&m_inputFile)->default_value(""), - "input file name") - ("output,o", po::value(&m_outputFile)->default_value(""), - "output file name") - ; - - addSwitchSet(file_options); - addPositionalSwitch("input", 1); - addPositionalSwitch("output", 1); + Arg *arg; + + arg = args.add("length", "Edge length for splitter cells", m_length, 0.0); + arg->setPositional(); + arg = args.add("capacity", "Point capacity of chipper cells", m_capacity); + arg->setPositional(); + args.add("origin_x", "Origin in X axis for splitter cells", m_xOrigin, + std::numeric_limits::quiet_NaN()); + args.add("origin_y", "Origin in Y axis for splitter cells", m_yOrigin, + std::numeric_limits::quiet_NaN()); + args.add("input,i", "Input filename", m_inputFile); + args.add("output,o", "Output filename", m_outputFile); } diff --git a/kernels/split/SplitKernel.hpp b/kernels/split/SplitKernel.hpp index ae66292453..b8093e6901 100644 --- a/kernels/split/SplitKernel.hpp +++ b/kernels/split/SplitKernel.hpp @@ -51,7 +51,7 @@ class PDAL_DLL SplitKernel : public Kernel int execute(); private: - void addSwitches(); + void addSwitches(ProgramArgs& args); void validateSwitches(); std::string m_inputFile; diff --git a/kernels/tindex/TIndexKernel.cpp b/kernels/tindex/TIndexKernel.cpp index 913fcb3235..a5d4641f60 100644 --- a/kernels/tindex/TIndexKernel.cpp +++ b/kernels/tindex/TIndexKernel.cpp @@ -92,59 +92,39 @@ TIndexKernel::TIndexKernel() } -void TIndexKernel::addSwitches() +void TIndexKernel::addSwitches(ProgramArgs& args) { - namespace po = boost::program_options; - - po::options_description* file_options = - new po::options_description("file options"); - - file_options->add_options() - ("tindex", po::value(&m_idxFilename), - "OGR-readable/writeable tile index output") - ("filespec", po::value(&m_filespec), - "Build: Pattern of files to index. Merge: Output filename") - ("fast_boundary", po::value(&m_fastBoundary)-> - zero_tokens()->implicit_value(true), - "use extent instead of exact boundary") - ("lyr_name", po::value(&m_layerName), - "OGR layer name to write into datasource") - ("tindex_name", po::value(&m_tileIndexColumnName)-> - default_value("location"), "Tile index column name") - ("driver,f", po::value(&m_driverName)-> - default_value("ESRI Shapefile"), "OGR driver name to use ") - ("t_srs", po::value(&m_tgtSrsString)-> - default_value("EPSG:4326"), "Target SRS of tile index") - ("a_srs", po::value(&m_assignSrsString)-> - default_value("EPSG:4326"), - "Assign SRS of tile with no SRS to this value") - ("bounds", po::value(&m_bounds), - "Extent (in XYZ) to clip output to") - ("polygon", po::value(&m_wkt), - "Well-known text of polygon to clip output") - ("write_absolute_path", po::value(&m_absPath)-> - default_value(false), - "Write absolute rather than relative file paths") - ("merge", "Whether we're merging the entries in a tindex file.") - ; - - addSwitchSet(file_options); - po::options_description* processing_options = - new po::options_description("processing options"); - - processing_options->add_options(); - - addSwitchSet(processing_options); - - addPositionalSwitch("tindex", 1); - addPositionalSwitch("filespec", 1); + Arg *arg; + + arg = args.add("tindex", "OGR-readable/writeable tile index output", + m_idxFilename); + arg->setPositional(); + arg = args.add("filespec", "Build: Pattern of files to index. " + "Merge: Output filename", m_filespec); + arg->setPositional(); + args.add("fast_boundary", "Use extent instead of exact boundary", + m_fastBoundary); + args.add("lyr_name", "OGR layer name to write into datasource", + m_layerName); + args.add("tindex_name", "Tile index column name", m_tileIndexColumnName, + "location"); + args.add("driver,f", "OGR driver name to use ", m_driverName, + "ESRI Shapefile"); + args.add("t_srs", "Target SRS of tile index", m_tgtSrsString, + "EPSG:4326"); + args.add("a_srs", "Assign SRS of tile with no SRS to this value", + m_assignSrsString, "EPSG:4326"); + args.add("bounds", "Extent (in XYZ) to clip output to", m_bounds); + args.add("polygon", "Well-known text of polygon to clip output", m_wkt); + args.add("write_absolute_path", + "Write absolute rather than relative file paths", m_absPath); + args.add("merge", "Whether we're merging the entries in a tindex file.", + m_merge); } void TIndexKernel::validateSwitches() { - m_merge = argumentExists("merge"); - if (m_idxFilename.empty()) throw pdal_error("No index filename provided."); diff --git a/kernels/tindex/TIndexKernel.hpp b/kernels/tindex/TIndexKernel.hpp index 944e7b1380..7dadadfe63 100644 --- a/kernels/tindex/TIndexKernel.hpp +++ b/kernels/tindex/TIndexKernel.hpp @@ -75,7 +75,7 @@ class PDAL_DLL TIndexKernel : public Kernel private: TIndexKernel(); - void addSwitches(); // overrride + virtual void addSwitches(ProgramArgs& args); void validateSwitches(); // overrride StringList glob(std::string& path); diff --git a/kernels/translate/TranslateKernel.cpp b/kernels/translate/TranslateKernel.cpp index d67214afbc..d9492559eb 100644 --- a/kernels/translate/TranslateKernel.cpp +++ b/kernels/translate/TranslateKernel.cpp @@ -86,32 +86,24 @@ void TranslateKernel::validateSwitches() throw app_usage_error("--output/-o required"); } -void TranslateKernel::addSwitches() +void TranslateKernel::addSwitches(ProgramArgs& args) { - po::options_description* file_options = - new po::options_description("file options"); - - file_options->add_options() - ("input,i", po::value(&m_inputFile)->default_value(""), - "input file name") - ("output,o", po::value(&m_outputFile)->default_value(""), - "output file name") - ("pipeline,p", po::value(&m_pipelineOutput)->default_value(""), - "pipeline output") - ("reader,r", po::value(&m_readerType)->default_value(""), - "reader type") - ("filter,f", - po::value >(&m_filterType)->multitoken(), - "filter type") - ("writer,w", po::value(&m_writerType)->default_value(""), - "writer type") - ; - - addSwitchSet(file_options); - - addPositionalSwitch("input", 1); - addPositionalSwitch("output", 1); + Arg *arg; + + arg = args.add("input,i", "Input filename", m_inputFile); + arg->setPositional(); + arg = args.add("output,o", "Output filename", m_outputFile); + arg->setPositional(); + args.add("pipeline,p", "Pipeline output", m_pipelineOutput); + args.add("reader,r", "Reader type", m_readerType); + //TODO - Accept multiple filters. +// args.add("filter,f", "Filter type", m_filterType); + args.add("writer,w", "Writer type", m_writerType); + + //ABELL - Think this should go away. + /** addPositionalSwitch("filter", -1); + **/ } int TranslateKernel::execute() diff --git a/kernels/translate/TranslateKernel.hpp b/kernels/translate/TranslateKernel.hpp index 366533629f..1bc89da013 100644 --- a/kernels/translate/TranslateKernel.hpp +++ b/kernels/translate/TranslateKernel.hpp @@ -59,7 +59,7 @@ class PDAL_DLL TranslateKernel : public Kernel private: TranslateKernel(); - void addSwitches(); + virtual void addSwitches(ProgramArgs& args); void validateSwitches(); std::string m_inputFile; diff --git a/plugins/cpd/kernel/Cpd.cpp b/plugins/cpd/kernel/Cpd.cpp index 585c17922c..063828f7ca 100644 --- a/plugins/cpd/kernel/Cpd.cpp +++ b/plugins/cpd/kernel/Cpd.cpp @@ -63,57 +63,56 @@ void CpdKernel::validateSwitches() } -void CpdKernel::addSwitches() +void CpdKernel::addSwitches(ProgramArgs& args) { using namespace cpd; - po::options_description* file_options = - new po::options_description("file options"); - - file_options->add_options() - ("filex,x", po::value(&m_filex)->default_value(""), - "input file containing the source points") - ("filey,y", po::value(&m_filey)->default_value(""), - "input file containg target points, i.e. the points that will be registered") - ("output,o", po::value(&m_output)->default_value(""), - "output file name") - ("tolerance,t", po::value(&m_tolerance)->default_value(DefaultTolerance), - "tolerance criterium") - ("max-iterations,m", po::value(&m_max_it)->default_value(DefaultMaxIterations), - "maximum number of iterations allowed") - ("outliers,O", po::value(&m_outliers)->default_value(DefaultOutliers), - "the weight of noise and outliers") - ("fgt,f", po::value(&m_fgt)->default_value(DefaultFgt), - "use a fast gauss transform (less accurate but faster)") - ("epsilon,e", po::value(&m_epsilon)->default_value(DefaultEpsilon), - "tolerance level for the fast gauss transform") - ("beta,b", po::value(&m_beta)->default_value(DefaultBeta), - "std of gaussian filter (Green's function, used for nonrigid registrations only)") - ("lambda,l", po::value(&m_lambda)->default_value(DefaultLambda), - "regularization weight (used for nonrigid registrations only)") - ("numeig,n", po::value(&m_numeig)->default_value(DefaultNumeig), - "number of eigenvectors/eigenvalues to find for nonrigid_lowrank registrations (if zero, default to N ^ (1/2) where N is the number of points in Y)") - ("bounds", po::value(&m_bounds), - "Extent (in XYZ) to clip output to") - ("auto-z-exaggeration", po::value(&m_auto_z_exaggeration)->default_value(false), - "Use the domain of the XY dimensions to automatically exaggerate the Z dimensions") - ("auto-z-exaggeration-ratio", po::value(&m_auto_z_exaggeration_ratio)->default_value(5.0 / 8.0), - "The scaling ratio for the Z-exaggeration. Z's range will be scaled to this ratio of the extent of the smallest XY extent.") - ("chipped", po::value(&m_chipped)->default_value(false), - "Run chipped registration") - ("chip-capacity", po::value(&m_chip_capacity)->default_value(8000), - "The maximum number of points in each chip (before buffer)") - ("chip-buffer", po::value(&m_chip_buffer)->default_value(50), - "The width of the buffer around each chip") - ("sigma2", po::value(&m_sigma2)->default_value(DefaultSigma2), - "The starting sigma2 value. To improve CPD runs, set to a bit more than you expect the average motion to be") - ; - - addSwitchSet(file_options); - - addPositionalSwitch("filex", 1); - addPositionalSwitch("filey", 1); - addPositionalSwitch("output", 1); + Arg *arg; + + arg = args.add("filex,x", "input file containing the source points", + m_filex); + arg->setPositional(); + arg = args.add("filey,y", "input file containg target points, " + "i.e. the points that will be registered", m_filey); + arg->setPositional(); + arg = args.add("output,o", "output file name", m_output); + arg->setPositional(); + args.add("tolerance,t", "tolerance criterium", m_tolerance, + DefaultTolerance); + args.add("max-iterations,m", "maximum number of iterations allowed", + m_max_it, DefaultMaxIterations); + args.add("outliers,O", "the weight of noise and outliers", + m_outliers, DefaultOutliers); + args.add("fgt,f", "use a fast gauss transform (less accurate but faster)", + m_fgt, DefaultFgt); + args.add("epsilon,e", "tolerance level for the fast gauss transform", + m_epsilon, DefaultEpsilon); + args.add("beta,b", "std of gaussian filter (Green's function, used " + "for nonrigid registrations only)", m_beta, DefaultBeta); + args.add("lambda,l", "regularization weight (used for nonrigid " + "registrations only)", m_lambda, DefaultLambda); + args.add("numeig,n", "number of eigenvectors/eigenvalues to find for " + "nonrigid_lowrank registrations (if zero, default to N ^ (1/2) " + "where N is the number of points in Y)", + m_numeig, DefaultNumeig); + args.add("bounds", "Extent (in XYZ) to clip output to", m_bounds); + args.add("auto-z-exaggeration", + "Use the domain of the XY dimensions to automatically " + "exaggerate the Z dimensions", + m_auto_z_exaggeration); + args.add("auto-z-exaggeration-ratio", + "The scaling ratio for the Z-exaggeration. Z's range will " + "be scaled to this ratio of the extent of the smallest XY extent.", + m_auto_z_exaggeration_ratio, 5.0 / 8.0); + args.add("chipped", "Run chipped registration", m_chipped); + args.add("chip-capacity", "The maximum number of points in each " + "chip (before buffer)", m_chip_capacity, 8000); + args.add("chip-buffer", "The width of the buffer around each chip", + m_chip_buffer, 50.0); + args.add("sigma2", + "The starting sigma2 value. To improve CPD runs, set to a bit " + "more than you expect the average motion to be", + m_sigma2, DefaultSigma2); } diff --git a/plugins/cpd/kernel/Cpd.hpp b/plugins/cpd/kernel/Cpd.hpp index f1fc5acbd5..85d83e8163 100644 --- a/plugins/cpd/kernel/Cpd.hpp +++ b/plugins/cpd/kernel/Cpd.hpp @@ -53,7 +53,7 @@ class PDAL_DLL CpdKernel : public Kernel private: CpdKernel() {}; - void addSwitches(); + virtual void addSwitches(ProgramArgs& args); void validateSwitches(); PointViewPtr readFile(const std::string& filename, PointTableRef table, arma::mat& mat); diff --git a/plugins/pcl/kernel/GroundKernel.cpp b/plugins/pcl/kernel/GroundKernel.cpp index 824573be94..37d2a2ece6 100644 --- a/plugins/pcl/kernel/GroundKernel.cpp +++ b/plugins/pcl/kernel/GroundKernel.cpp @@ -87,27 +87,23 @@ void GroundKernel::validateSwitches() } } -void GroundKernel::addSwitches() +void GroundKernel::addSwitches(ProgramArgs& args) { - po::options_description* file_options = new po::options_description("file options"); - - file_options->add_options() - ("input,i", po::value(&m_inputFile)->default_value(""), "input file name") - ("output,o", po::value(&m_outputFile)->default_value(""), "output file name") - ("max_window_size", po::value(&m_maxWindowSize)->default_value(33), "max window size") - ("slope", po::value(&m_slope)->default_value(1), "slope") - ("max_distance", po::value(&m_maxDistance)->default_value(2.5), "max distance") - ("initial_distance", po::value(&m_initialDistance)->default_value(0.15, "0.15"), "initial distance") - ("cell_size", po::value(&m_cellSize)->default_value(1), "cell size") - ("classify", po::bool_switch(&m_classify), "apply classification labels?") - ("extract", po::bool_switch(&m_extract), "extract ground returns?") - ("approximate,a", po::bool_switch(&m_approximate), "use approximate algorithm? (much faster)") - ; - - addSwitchSet(file_options); - - addPositionalSwitch("input", 1); - addPositionalSwitch("output", 1); + Arg *arg; + + arg = args.add("input,i", "Input filename", m_inputFile); + arg->setPositional(); + arg = args.add("output,o", "Output filename", m_outputFile); + arg->setPositional(); + args.add("max_window_size", "Max window size, m_maxWindowSize, 33); + args.add("slope", "Slope", m_slope, 1); + args.add("max_distance", "Max distance", m_maxDistance, 2.5); + args.add("initial_distance", "Initial distance", m_initialDistance, .15); + args.add("cell_size", "Cell size", m_cellSize, 1); + args.add("classify", "Apply classification labels?", m_classify); + args.add("extract", "extract ground returns?", m_extract); + args.add("approximate,a", "use approximate algorithm? (much faster)", + m_approximate); } int GroundKernel::execute() diff --git a/plugins/pcl/kernel/GroundKernel.hpp b/plugins/pcl/kernel/GroundKernel.hpp index 29fba7f1cb..cf51e78631 100644 --- a/plugins/pcl/kernel/GroundKernel.hpp +++ b/plugins/pcl/kernel/GroundKernel.hpp @@ -58,7 +58,7 @@ class PDAL_DLL GroundKernel : public Kernel private: GroundKernel(); - void addSwitches(); + virtual void addSwitches(ProgramArgs& args); void validateSwitches(); std::shared_ptr makeReader(Options readerOptions); diff --git a/plugins/pcl/kernel/HeightAboveGroundKernel.cpp b/plugins/pcl/kernel/HeightAboveGroundKernel.cpp index 25e9c8fc0e..767ef3b12d 100644 --- a/plugins/pcl/kernel/HeightAboveGroundKernel.cpp +++ b/plugins/pcl/kernel/HeightAboveGroundKernel.cpp @@ -79,20 +79,18 @@ void HeightAboveGroundKernel::validateSwitches() // should probably also verify that there is a classification dimension if --use_classification == true } -void HeightAboveGroundKernel::addSwitches() +void HeightAboveGroundKernel::addSwitches(ProgramArgs& args) { - po::options_description* options = new po::options_description("file options"); - options->add_options() - ("input,i", po::value(&m_input_file)->default_value(""), "input file name") - ("ground,g", po::value(&m_ground_file)->default_value(""), "ground file name") - ("output,o", po::value(&m_output_file)->default_value(""), "output file name") - ("use_classification,c", po::bool_switch(&m_use_classification)->default_value(false), "use existing classification labels?") - ; - - addSwitchSet(options); - addPositionalSwitch("input", 1); - addPositionalSwitch("ground", 1); - addPositionalSwitch("output", 1); + Arg *arg; + + arg = args.add("input,i", "Input filename", m_input_file); + arg->setPositional(); + arg = args.add("ground,g", "Ground filename", m_ground_file); + arg->setPositional(); + arg = args.add("output,o", "Output filename", m_output_file); + arg->setPositional(); + args.add("use_classification,c", "Use existing classification labels?", + m_use_classification); } int HeightAboveGroundKernel::execute() diff --git a/plugins/pcl/kernel/HeightAboveGroundKernel.hpp b/plugins/pcl/kernel/HeightAboveGroundKernel.hpp index b56c3657c7..be5142fee6 100644 --- a/plugins/pcl/kernel/HeightAboveGroundKernel.hpp +++ b/plugins/pcl/kernel/HeightAboveGroundKernel.hpp @@ -54,7 +54,7 @@ class HeightAboveGroundKernel : public Kernel private: void validateSwitches(); - void addSwitches(); + virtual void addSwitches(ProgramArgs& args); bool m_use_classification; diff --git a/plugins/pcl/kernel/PCLKernel.cpp b/plugins/pcl/kernel/PCLKernel.cpp index 9d44821714..b021dfd819 100644 --- a/plugins/pcl/kernel/PCLKernel.cpp +++ b/plugins/pcl/kernel/PCLKernel.cpp @@ -69,31 +69,22 @@ void PCLKernel::validateSwitches() } -void PCLKernel::addSwitches() +void PCLKernel::addSwitches(ProgramArgs& args) { - po::options_description* file_options = - new po::options_description("file options"); - - file_options->add_options() - ("input,i", po::value(&m_inputFile)->default_value(""), - "input file name") - ("output,o", po::value(&m_outputFile)->default_value(""), - "output file name") - ("pcl,p", po::value(&m_pclFile)->default_value(""), - "pcl file name") - ("compress,z", - po::value(&m_bCompress)->zero_tokens()->implicit_value(true), - "Compress output data (if supported by output format)") - ("metadata,m", - po::value< bool >(&m_bForwardMetadata)->implicit_value(true), - "Forward metadata (VLRs, header entries, etc) from previous stages") - ; - - addSwitchSet(file_options); - - addPositionalSwitch("input", 1); - addPositionalSwitch("output", 1); - addPositionalSwitch("pcl", 1); + Arg *arg; + + arg = args.add("input,i", "Input filename", m_inputFile); + arg->setPositional(); + args.add("output,o", "Output filename", m_outputFile); + arg = arg->setPositional(); + args.add("pcl,p", "PCL filename", m_pclFile); + arg = arg->setPositional(); + args.add("compress,z", + "Compress output data (if supported by output format)", + m_bCompress); + args.add("metadata,m", + "Forward metadata (VLRs, header entries, etc) from previous stages", + m_bForwardMetadata); } diff --git a/plugins/pcl/kernel/PCLKernel.hpp b/plugins/pcl/kernel/PCLKernel.hpp index bb13f09221..64bb608328 100644 --- a/plugins/pcl/kernel/PCLKernel.hpp +++ b/plugins/pcl/kernel/PCLKernel.hpp @@ -49,7 +49,7 @@ class PDAL_DLL PCLKernel : public Kernel private: PCLKernel(); - void addSwitches(); + virtual void addSwitches(ProgramArgs& args); void validateSwitches(); std::unique_ptr m_manager; diff --git a/plugins/pcl/kernel/SmoothKernel.cpp b/plugins/pcl/kernel/SmoothKernel.cpp index 346aec6b9d..d1f0bc16cf 100644 --- a/plugins/pcl/kernel/SmoothKernel.cpp +++ b/plugins/pcl/kernel/SmoothKernel.cpp @@ -68,20 +68,14 @@ void SmoothKernel::validateSwitches() } -void SmoothKernel::addSwitches() +void SmoothKernel::addSwitches(ProgramArgs& args) { - po::options_description* file_options = new po::options_description("file options"); + Arg *arg; - file_options->add_options() - ("input,i", po::value(&m_inputFile)->default_value(""), "input file name") - ("output,o", po::value(&m_outputFile)->default_value(""), "output file name") -// ("compress,z", po::value(&m_bCompress)->zero_tokens()->implicit_value(true), "Compress output data (if supported by output format)") - ; - - addSwitchSet(file_options); - - addPositionalSwitch("input", 1); - addPositionalSwitch("output", 1); + arg = args.add("input,i", "Input filename", m_inputFile); + arg->setPositional(); + arg = args.add("output,o", "Output filename", m_outputFile); + arg->setPositional(); } diff --git a/plugins/pcl/kernel/SmoothKernel.hpp b/plugins/pcl/kernel/SmoothKernel.hpp index 7aa7313b5a..ee8c3722d4 100644 --- a/plugins/pcl/kernel/SmoothKernel.hpp +++ b/plugins/pcl/kernel/SmoothKernel.hpp @@ -52,7 +52,7 @@ class PDAL_DLL SmoothKernel : public Kernel private: SmoothKernel() {}; - void addSwitches(); + virtual void addSwitches(ProgramArgs& args); void validateSwitches(); std::shared_ptr makeReader(Options readerOptions); diff --git a/plugins/pcl/kernel/ViewKernel.cpp b/plugins/pcl/kernel/ViewKernel.cpp index 2d38a06e0e..a72572cd09 100644 --- a/plugins/pcl/kernel/ViewKernel.cpp +++ b/plugins/pcl/kernel/ViewKernel.cpp @@ -140,20 +140,16 @@ void ViewKernel::validateSwitches() } -void ViewKernel::addSwitches() +void ViewKernel::addSwitches(ProgramArgs& args) { - po::options_description* file_options = new po::options_description("file options"); + Arg *arg; - file_options->add_options() - ("input,i", po::value(&m_inputFile)->default_value(""), "input file name") - ("point,p", po::value(&m_pointIndexes), "point to dump") - ; - - addSwitchSet(file_options); - - addPositionalSwitch("input", 1); + arg = args.add("input,i", "Input filename", m_inputFile); + arg->setPositional(); + args.add("point,p", "Point to dump", m_pointIndexes); } + int ViewKernel::execute() { Options readerOptions; diff --git a/plugins/pcl/kernel/ViewKernel.hpp b/plugins/pcl/kernel/ViewKernel.hpp index 0334a6357a..05e2776fa9 100644 --- a/plugins/pcl/kernel/ViewKernel.hpp +++ b/plugins/pcl/kernel/ViewKernel.hpp @@ -52,7 +52,7 @@ class PDAL_DLL ViewKernel : public Kernel private: ViewKernel(); - void addSwitches(); + virtual void addSwitches(ProgramArgs& args); void validateSwitches(); std::shared_ptr makeReader(Options readerOptions); diff --git a/src/Kernel.cpp b/src/Kernel.cpp index 26472776d4..397321301a 100644 --- a/src/Kernel.cpp +++ b/src/Kernel.cpp @@ -60,6 +60,72 @@ namespace po = boost::program_options; namespace pdal { +namespace +{ + +bool parseOption(std::string o, std::string& stage, std::string& option, + std::string& value) +{ + if (o.size() < 2) + return false; + if (o[0] != '-' || o[1] != '-') + return false; + + o = o.substr(2); + + // Options are stage_type.stage_name.option_name + // stage_type is always lowercase stage_names start with lowercase and + // then are lowercase or digits. Option names start with lowercase and + // then contain lowercase, digits or underscore. + + // This awfulness is to work around the multiply-defined islower. Seems + // a bit better than the cast solution. + auto islc = [](char c) + { return std::islower(c); }; + auto islcOrDigit = [](char c) + { return std::islower(c) || std::isdigit(c); }; + + std::string::size_type pos = 0; + std::string::size_type count = 0; + + // Get stage_type. + count = Utils::extract(o, pos, islc); + pos += count; + + std::string stage_type = o.substr(0, pos); + if (stage_type != "readers" && stage_type != "writers" && + stage_type != "filters") + return false; + if (o[pos++] != '.') + return false; + + // Get stage_name. + count = Utils::extract(o, pos, islcOrDigit); + if (std::isdigit(o[pos])) + return false; + pos += count; + stage = o.substr(0, pos); + if (o[pos++] != '.') + return false; + + // Get option name. + std::string::size_type optionStart = pos; + count = Option::parse(o, pos); + pos += count; + option = o.substr(optionStart, count); + + if (o[pos++] != '=') + return false; + + // The command-line parser takes care of quotes around an argument + // value and such. May want to do something to handle escaped characters? + value = o.substr(pos); + return true; +} + +} // unnamed namespace + + Kernel::Kernel() : m_usestdin(false) , m_log("pdal", "stderr") @@ -93,34 +159,44 @@ std::ostream& operator<<(std::ostream& ostr, const Kernel& kernel) } -int Kernel::do_switches() +void Kernel::doSwitches(int argc, const char *argv[], ProgramArgs& args) { - try + StringList stringArgs; + + // Scan the argument vector for extra stage options. Pull them out and + // stick them in the list. Let the ProgramArgs handle everything else. + // NOTE: This depends on the format being "option=value" rather than + // "option value". This is what we've always expected, so no problem, + // but it would be better to be more flexible. + for (int i = 0; i < argc; ++i) { - // add -h, -v, etc - addBasicSwitchSet(); - - // add the options for the derived application - addSwitches(); + std::string stageName, opName, value; - // parse the command line - parseSwitches(); + if (parseOption(argv[i], stageName, opName, value)) + { + Option op(opName, value); + m_extraStageOptions[stageName].add(op); + } + else + stringArgs.push_back(argv[i]); } - catch (std::exception const& e) + + try { - Utils::printError(e.what()); - return 1; + // add -h, -v, etc + addBasicSwitches(args); + // add the options for the derived application + addSwitches(args); + args.parse(stringArgs); } - catch (...) + catch (arg_error& e) { - Utils::printError("Caught unknown exception handling switches"); - return 1; + throw pdal_error(e.m_error); } - return 0; } -int Kernel::do_startup() +int Kernel::doStartup() { try { @@ -142,9 +218,8 @@ int Kernel::do_startup() } -int Kernel::do_execution() +int Kernel::doExecution() { - if (m_reportDebug) { std::cout << getPDALDebugInformation() << std::endl; @@ -183,7 +258,7 @@ int Kernel::do_execution() } -int Kernel::do_shutdown() +int Kernel::doShutdown() { try { @@ -206,26 +281,24 @@ int Kernel::do_shutdown() // this just wraps ALL the code in total catch block -int Kernel::run(int argc, const char* argv[], const std::string& appName) +int Kernel::run(int argc, char const * argv[], const std::string& appName) { - m_argc = argc; - m_argv = argv; m_appName = appName; - int switches_status = do_switches(); - if (switches_status) - return switches_status; + ProgramArgs args; - int startup_status = do_startup(); + doSwitches(argc, argv, args); + + int startup_status = doStartup(); if (startup_status) return startup_status; - int execution_status = do_execution(); + int execution_status = doExecution(); // note we will try to shutdown cleanly even if we got an error condition // in the execution phase - int shutdown_status = do_shutdown(); + int shutdown_status = doShutdown(); if (execution_status) return execution_status; @@ -234,67 +307,6 @@ int Kernel::run(int argc, const char* argv[], const std::string& appName) } -namespace -{ - -bool parseOption(std::string o, std::string& stage, std::string& option, - std::string& value) -{ - if (o.size() < 2) - return false; - if (o[0] != '-' || o[1] != '-') - return false; - - o = o.substr(2); - - // Options are stage_type.stage_name.option_name - // stage_type is always lowercase stage_names start with lowercase and - // then are lowercase or digits. Option names start with lowercase and - // then contain lowercase, digits or underscore. - - // This awfulness is to work around the multiply-defined islower. Seems - // a bit better than the cast solution. - auto islc = [](char c) - { return std::islower(c); }; - auto islcOrDigit = [](char c) - { return std::islower(c) || std::isdigit(c); }; - - std::string::size_type pos = 0; - std::string::size_type count = 0; - - // Get stage_type. - count = Utils::extract(o, pos, islc); - pos += count; - if (o[pos++] != '.') - return false; - - // Get stage_name. - count = Utils::extract(o, pos, islcOrDigit); - if (std::isdigit(o[pos])) - return false; - pos += count; - stage = o.substr(0, pos); - if (o[pos++] != '.') - return false; - - // Get option name. - std::string::size_type optionStart = pos; - count = Option::parse(o, pos); - pos += count; - option = o.substr(optionStart, count); - - if (o[pos++] != '=') - return false; - - // The command-line parser takes care of quotes around an argument - // value and such. May want to do something to handle escaped characters? - value = o.substr(pos); - return true; -} - -} // unnamed namespace - - void Kernel::collectExtraOptions() { for (const auto& o : m_extra_options) @@ -334,10 +346,8 @@ int Kernel::innerRun() return 0; } - if (!m_showOptions.empty()) - { + if (m_showOptions) return 0; - } try { @@ -518,12 +528,6 @@ void Kernel::setCommonOptions(Options &options) } -void Kernel::addPositionalSwitch(const char* name, int max_count) -{ - m_positionalOptions.add(name, max_count); -} - - void Kernel::outputHelp() { outputVersion(); @@ -547,19 +551,19 @@ void Kernel::outputVersion() } -void Kernel::addBasicSwitchSet() +void Kernel::addBasicSwitches(ProgramArgs& args) { - ProgramArgs args; - args.add("help,h", "Print help message", m_showHelp); - args.add("options", "Show available options for a driver", - m_showOptions, "all"); + args.add("options", "Show available options for a driver", m_showOptions); args.add("debug,d", "Enable debug mode", m_isDebug); args.add("report-debug", "Report PDAL compilation DEBUG status", m_reportDebug, true); + args.add("developer-debug", + "Enable developer debug (don't trap exceptions)", m_hardCoreDebug); args.add("label", "A string to label the process with", m_label); args.add("verbose,v", "Set verbose message level", m_verboseLevel, 0u); args.add("version", "Show version info", m_showVersion); + args.add("visualize", "Visualize result", m_visualize); args.add("stdin,s", "Read pipeline XML from stdin", m_usestdin); /** @@ -574,83 +578,6 @@ void Kernel::addBasicSwitchSet() "A comma-separated or quoted, space-separated list of offsets to " "set on the output file: \n--offset 0,0,0\n--offset " "\"1234 5678 91011\"", m_offsets); - - po::options_description* basic_options = - new po::options_description("basic options"); - - /** - basic_options->add_options() - ("help,h", - po::value(&m_showHelp)->zero_tokens()->implicit_value(true), - "Print help message") - ("options", po::value(&m_showOptions)->implicit_value("all"), - "Show available options for a driver") - ("debug,d", - po::value(&m_isDebug)->zero_tokens()->implicit_value(true), - "Enable debug mode") - ("report-debug", - po::value(&m_reportDebug)->zero_tokens()->implicit_value(true), - "Report PDAL compilation DEBUG status") - ("developer-debug", - po::value(&m_hardCoreDebug)->zero_tokens()->implicit_value(true), - "Enable developer debug mode (don't trap exceptions so segfaults " - "are thrown)") - ("label", - po::value(&m_label)->default_value(""), - "A string to label the process with") - ("verbose,v", po::value(&m_verboseLevel)->default_value(0), - "Set verbose message level") - ("version", - po::value(&m_showVersion)->zero_tokens()->implicit_value(true), - "Show version info") - ("visualize", - po::value(&m_visualize)->zero_tokens()->implicit_value(true), - "Visualize result") - ("stdin,s", - po::value(&m_usestdin)->zero_tokens()->implicit_value(true), - "Read pipeline XML from stdin") - ("heartbeat", - po::value< std::vector >(&m_heartbeat_shell_command), - "Shell command to run for every progress heartbeat") - ("scale", po::value< std::string >(&m_scales), - "A comma-separated or quoted, space-separated list of scales to " - "set on the output file: \n--scale 0.1,0.1,0.00001\n--scale \"" - "0.1 0.1 0.00001\"") - ("offset", po::value< std::string >(&m_offsets), - "A comma-separated or quoted, space-separated list of offsets to " - "set on the output file: \n--offset 0,0,0\n--offset " - "\"1234 5678 91011\"") - ; - - addSwitchSet(basic_options); - **/ -} - - -void Kernel::parseSwitches() -{ - po::options_description options; - - for (auto const& iter : m_public_options) - options.add(*iter); - for (auto const& iter : m_hidden_options) - options.add(*iter); - - try - { - auto parsed = po::command_line_parser(m_argc, m_argv). - options(options).allow_unregistered(). - positional(m_positionalOptions).run(); - m_extra_options = po::collect_unrecognized(parsed.options, - po::include_positional); - - po::store(parsed, m_variablesMap); - } - catch (boost::program_options::unknown_option e) - { - throw app_usage_error("unknown option: " + e.get_option_name()); - } - po::notify(m_variablesMap); } diff --git a/test/unit/ProgramArgsTest.cpp b/test/unit/ProgramArgsTest.cpp index b5dd4bd548..37e117ac0c 100644 --- a/test/unit/ProgramArgsTest.cpp +++ b/test/unit/ProgramArgsTest.cpp @@ -213,3 +213,46 @@ TEST(ProgramArgsTest, t4) // No program option provided. HANDLE_EXCEPTION(args.add("", "Foo description", m_foo, "foo")); } + +TEST(ProgramArgsTest, positional) +{ + ProgramArgs args; + Arg *arg; + + std::string m_foo; + int m_bar; + bool m_baz; + + arg = args.add("foo,f", "Foo description", m_foo, "foo"); + arg->setPositional(); + arg = args.add("bar", "Foo description", m_bar, 23); + arg->setPositional(); + arg = args.add("baz,z", "Foo description", m_baz); + EXPECT_THROW(arg->setPositional(), arg_error); + + // Go through exceptions procedurally. + + StringList s = toStringList("--foo Foo -z 55"); + args.parse(s); + EXPECT_EQ(m_foo, "Foo"); + EXPECT_EQ(m_bar, 55); + EXPECT_EQ(m_baz, true); + + args.reset(); + + s = toStringList("-z Flub 66"); + args.parse(s); + EXPECT_EQ(m_foo, "Flub"); + EXPECT_EQ(m_bar, 66); + EXPECT_EQ(m_baz, true); + + args.reset(); + + s = toStringList("Flub 66"); + args.parse(s); + EXPECT_EQ(m_foo, "Flub"); + EXPECT_EQ(m_bar, 66); + EXPECT_EQ(m_baz, false); + + +}