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}