Filter Example Source Files

filter_by_time.cpp

  1/*
  2 * Copyright (c) 2014-2023 National Technology and Engineering
  3 * Solutions of Sandia, LLC. Under the terms of Contract DE-NA0003525
  4 * with National Technology and Engineering Solutions of Sandia, LLC,
  5 * the U.S. Government retains certain rights in this software.
  6 *
  7 * Redistribution and use in source and binary forms, with or without
  8 * modification, are permitted provided that the following conditions
  9 * are met:
 10 *
 11 * 1. Redistributions of source code must retain the above copyright
 12 * notice, this list of conditions and the following disclaimer.
 13 *
 14 * 2. Redistributions in binary form must reproduce the above copyright
 15 * notice, this list of conditions and the following disclaimer in the
 16 * documentation and/or other materials provided with the distribution.
 17 *
 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 22 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 29 */
 30
 31#include <tracktable/CommandLineFactories/PointReaderFromCommandLine.h>
 32#include <tracktable/Domain/Terrestrial.h>
 33#include <tracktable/RW/PointWriter.h>
 34
 35#include <boost/iterator/filter_iterator.hpp>
 36#include <boost/timer/timer.hpp>
 37
 38#include <fstream>
 39#include <string>
 40
 41using TrajectoryT = tracktable::domain::terrestrial::trajectory_type;
 42using PointT = typename TrajectoryT::point_type;
 43using PointReaderT = tracktable::PointReader<PointT>;
 44using PointWriterT = tracktable::PointWriter;
 45using PointReaderIteratorT = typename PointReaderT::iterator;
 46
 47namespace bpo = boost::program_options;
 48
 49/** Function object
 50 * Created with two timestamps
 51 * Callable with a point argument
 52 * returns true if the point is between the two timestamps
 53 **/
 54template <typename pointT>
 55struct DateBetween {
 56 public:
 57  DateBetween(tracktable::Timestamp const& _startTime, tracktable::Timestamp const& _endTime)
 58      : startTime(_startTime), endTime(_endTime) {}
 59  DateBetween() = delete;
 60
 61  bool operator()(pointT const& _point)  // const& not copied (reference), not alterable
 62  {
 63    return (_point.timestamp() >= this->startTime && _point.timestamp() <= this->endTime);
 64  }
 65
 66 private:
 67  const tracktable::Timestamp startTime;
 68  const tracktable::Timestamp endTime;
 69};
 70
 71static constexpr auto helpmsg = R"(
 72--------------------------------------------------------------------------------
 73This program takes an input file of points and filters for points that fall within
 74two given timestamps
 75
 76The filter_time example demonstrates:
 77    - Using command line factories to read points
 78    - Using 'required' options
 79    - Using a function object to filter those points
 80    - Using a point writer to output those points.
 81
 82Typical use:
 83    ./filter_time --input=/data/flights.tsv --output=/results/filtered.tsv --start=2013-07-10-00:00:05 --stop=2013-07-10-00:01:05
 84
 85Defaults assume a tab separated points file formatted as :
 86
 87OBJECTID TIMESTAMP LON LAT
 88--------------------------------------------------------------------------------)";
 89
 90int main(int _argc, char* _argv[]) {
 91  // Set log level to reduce unecessary output
 92  tracktable::set_log_level(tracktable::log::info);
 93  // Create a basic command line option with boost
 94  boost::program_options::options_description commandLineOptions("Options");
 95  commandLineOptions.add_options()("help", "Print help");
 96
 97  // Create command line factories
 98  tracktable::PointReaderFromCommandLine<PointT> readerFactory;
 99  // Add options from the factories
100  readerFactory.addOptions(commandLineOptions);
101
102  // And a command line option for output
103  commandLineOptions.add_options()("output", bpo::value<std::string>()->default_value("-"),
104                                   "file to write to (use '-' for stdout), overridden by 'separate-kmls'");
105
106  /** Boost program options using a variable map to tie everything together.
107   * one parse will have a single variable map. We need to let the factories know
108   * about this variable map so they can pull information out of it */
109  auto vm = std::make_shared<boost::program_options::variables_map>();
110  readerFactory.setVariables(vm);
111
112  // And a command line option for output
113  commandLineOptions.add_options()("output", bpo::value<std::string>()->default_value("-"),
114                                   "file to write to (use '-' for stdout), overridden by 'separate-kmls'");
115
116  // Add command line option for start and stop timestamps, noticed they are 'required'
117  // clang-format off
118  std::string startString("");
119  std::string stopString("");
120  commandLineOptions.add_options()
121      ("start", bpo::value<std::string>(&startString)->required()," timestamp to start at")\
122      ("stop", bpo::value<std::string>(&stopString)->required(), "timestamp to stop at")
123  ;
124  // clang-format on
125  // Parse the command lines, don't forget the 'notify' after
126  try {
127    // We use this try/catch to automatically display help when an unknown option is used
128    boost::program_options::store(
129        boost::program_options::command_line_parser(_argc, _argv).options(commandLineOptions).run(), *vm);
130    boost::program_options::notify(*vm);
131  } catch (boost::program_options::error e) {
132    std::cerr << e.what();
133    std::cerr << helpmsg << "\n\n";
134    std::cerr << commandLineOptions << std::endl;
135    return 1;
136  }
137  /** Parsing will give an error of an incorrect option is used, but it won't
138   * display the help unless we tell it too */
139  if (vm->count("help") != 0) {
140    std::cerr << helpmsg << "\n\n";
141    std::cerr << commandLineOptions << std::endl;
142    return 1;
143  }
144  // Attempt to parse time from the command line
145  tracktable::Timestamp startTime(tracktable::time_from_string(startString));
146  tracktable::Timestamp endTime(tracktable::time_from_string(stopString));
147
148  // Create Point Reader
149  auto pointReader = readerFactory.createPointReader();
150
151  // Using an oldstyle pointer here in order to be able to swap out for file if needed
152  std::ostream* out = &std::cout;
153
154  // Check the output file argument, if it is default '-', then use std::cout, otherwise try to open a file
155  auto filename = (*vm)["output"].as<std::string>();
156  if ("-" != filename) {
157    out = new std::ofstream(filename);
158    if (!out->good()) {
159      std::cerr << "\n\nCould not open " << filename << std::endl;
160      return 1;
161    }
162    std::cerr << "Writing to: " << filename << std::endl;
163  } else {
164    std::cerr << "Writing to: standard out" << std::endl;
165  }
166  PointWriterT pointWriter;
167  // Match input format as much as possible
168  pointWriter.set_field_delimiter(pointReader->field_delimiter());
169  pointWriter.set_timestamp_format(pointReader->timestamp_format());
170  pointWriter.set_null_value(pointReader->null_value());
171
172  pointWriter.set_output(*out);
173
174  std::cerr << " Filtered to include only updates between " << startString << " and " << stopString << "."
175            << std::endl;
176
177  DateBetween<PointT> dateFilter(startTime, endTime);
178
179  // Use Boost filter iterator to process points as a stream and skip those that
180  // don't match our filter criteria (between two timestamps)
181  using FilterIteratorT = boost::filter_iterator<DateBetween<PointT>, PointReaderIteratorT>;
182  FilterIteratorT start(dateFilter, pointReader->begin());
183  FilterIteratorT finish(dateFilter, pointReader->end());
184  {
185    boost::timer::auto_cpu_timer filterTimer(std::cerr);
186    pointWriter.write(start, finish);
187  }
188  std::cerr << "Done" << std::endl;
189  return 0;
190}