Portal Example Source Files¶
Table of Contents
main.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 "Portal.h"
32#include "PortalPair.h"
33
34#include <tracktable/CommandLineFactories/AssemblerFromCommandLine.h>
35#include <tracktable/CommandLineFactories/PointReaderFromCommandLine.h>
36#include <tracktable/Domain/Terrestrial.h>
37
38#include <boost/timer/timer.hpp>
39
40using TrajectoryT = tracktable::domain::terrestrial::trajectory_type;
41using PointT = typename TrajectoryT::point_type;
42using PointReaderT = tracktable::PointReader<PointT>;
43using PointReaderIteratorT = typename PointReaderT::iterator;
44using AssemblerT = tracktable::AssembleTrajectories<TrajectoryT, PointReaderIteratorT>;
45
46static constexpr auto helpmsg = R"(
47--------------------------------------------------------------------------------
48The portal example takes trajectory data and attempts to find origin/destination
49pairs. It breaks the USA into a grid, identifies what cells are populated by trajectories
50and then refines the grid based on desired parameters. Each level of 'depth' is an
51additional layer of refinement of the original grid. Each level is divided into
52'bin-count' sections in both longitude and latitude. So each the number of cells:
53
54cells = 12*5*bins^(2+depth)
55
56empty cells are dropped but a cell is only empty if no trajectories pass through it
57
58The portal example demonstrates:
59 - Using command line factories to read points and assemble trajectories
60 - Using boost program options to take parameters from command lines(in addition to the factories)
61 - Use of boost::geometry::intersects to test where trajectories overlap regions
62
63Typical use:
64 ./portal-- input=/data/flights.tsv --depth=5 --min-value=12 --min-seperation=10 --bin-count=2
65
66Defaults assume a tab separated file formatted as :
67
68OBJECTID TIMESTAMP LON LAT
69--------------------------------------------------------------------------------)";
70
71int main(int _argc, char* _argv[]) {
72 constexpr auto timerFormat = "\u001b[30;1m %w seconds\u001b[0m\n";
73 // Set log level to reduce unecessary output
74 tracktable::set_log_level(tracktable::log::info);
75 // Create a basic command line option with boost
76 boost::program_options::options_description commandLineOptions;
77 commandLineOptions.add_options()("help", "Print help");
78 // Create command line factories
79 tracktable::PointReaderFromCommandLine<PointT> readerFactory;
80 tracktable::AssemblerFromCommandLine<TrajectoryT> assemblerFactory;
81 // Add options from the factories
82 readerFactory.addOptions(commandLineOptions);
83 assemblerFactory.addOptions(commandLineOptions);
84 /** Boost program options using a variable map to tie everything together.
85 * one parse will have a single variable map. We need to let the factories know
86 * about this variable map so they can pull information out of it */
87 auto vm = std::make_shared<boost::program_options::variables_map>();
88 readerFactory.setVariables(vm);
89 assemblerFactory.setVariables(vm);
90
91 // Portal specific configuration
92 auto seperationDistance = 10.0;
93 auto depth = 5u;
94 auto binSize = 2u;
95 auto minValue = 16u;
96 boost::program_options::options_description portalOptions("Portals");
97 // clang-format off
98 portalOptions.add_options()
99 ("portal-sep", bpo::value(&seperationDistance)->default_value(10), "Set minimum portal separation distance (in lat-lon)")
100 ("depth", bpo::value(&depth)->default_value(5), "Set depth for portal decomposition")
101 ("bin-count", bpo::value(&binSize)->default_value(2), "Portal chopping factor (default is 2)")
102 ("min-value", bpo::value(&minValue)->default_value(16), "Minumum number of portal pairs (default is 16)")
103 ;
104 // clang-format on
105
106 // Parse the command lines, don't forget the 'notify' after
107 try {
108 // We use this try/catch to automatically display help when an unknown option is used
109 boost::program_options::store(
110 boost::program_options::command_line_parser(_argc, _argv).options(commandLineOptions).run(), *vm);
111 boost::program_options::notify(*vm);
112 } catch (boost::program_options::error e) {
113 std::cerr << e.what();
114 std::cerr << helpmsg << "\n\n";
115 std::cerr << commandLineOptions << std::endl;
116 return 1;
117 }
118 /** Parsing will give an error of an incorrect option is used, but it won't
119 * display the help unless we tell it too */
120 if (vm->count("help") != 0) {
121 std::cerr << helpmsg << "\n\n";
122 std::cerr << commandLineOptions << std::endl;
123 return 1;
124 }
125
126 // Create Point Reader and assembler
127 auto pointReader = readerFactory.createPointReader();
128 auto assembler = assemblerFactory.createAssembler(pointReader);
129
130 std::vector<std::shared_ptr<TrajectoryT>> trajectories = {};
131 // This block exists for easy timing of trajectory assembling using the boost auto timer
132 // Note that all feedback to the user is done on std::cerr, this allows us to only
133 // put desired results into std::cout, this make processing output easier.
134 {
135 std::cerr << "Assemble Trajectories" << std::endl;
136 boost::timer::auto_cpu_timer assembleTimer(std::cerr, timerFormat);
137 auto count = 0u;
138 std::cerr << std::right;
139 for (auto tIter = assembler->begin(); tIter != assembler->end(); ++tIter) {
140 if (tracktable::length(*tIter) < 100) {
141 continue;
142 }
143 std::cerr << "\b\b\b\b\b\b\b\b\b\b" << std::setw(10) // Using backspaces for in place counter
144 << count++;
145 trajectories.push_back(std::make_shared<TrajectoryT>(*tIter));
146 }
147 std::cerr << std::left << "\nStarting with " << trajectories.size() << " trajectories" << std::endl;
148 }
149
150 // Create box for the USA
151 PointT lowerLeft(-125.0, 25.0); // lower left of USA
152 PointT upperRight(-65.0, 50.0); // upper right of USA
153 auto USA = std::make_shared<Portal>(boost::geometry::model::box<PointT>(lowerLeft, upperRight));
154 USA->level = 0;
155 PairHeap pairs;
156 pairs.minimumSeperation = seperationDistance;
157 pairs.minimumValue = minValue;
158 pairs.depth = depth;
159 pairs.xDivisions = binSize;
160 pairs.yDivisions = binSize;
161 {
162 std::cerr << "Initializing Pair Heap" << std::endl;
163 boost::timer::auto_cpu_timer initializeTimer(std::cerr, timerFormat);
164 pairs.initialize(trajectories, USA);
165 }
166 {
167 std::cerr << "Finding Portals" << std::endl;
168 boost::timer::auto_cpu_timer findTimer(std::cerr, timerFormat);
169 pairs.find_portals();
170 }
171 return 0;
172}
Portal.h¶
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#ifndef Portal_h
31#define Portal_h
32
33#include <tracktable/Domain/Terrestrial.h>
34
35#include <boost/geometry/geometries/box.hpp>
36#include <boost/geometry/geometries/register/box.hpp>
37
38#include <list>
39#include <memory>
40#include <set>
41
42using TrajectoryT = tracktable::domain::terrestrial::trajectory_type;
43using PointT = typename TrajectoryT::point_type;
44using BoxT = boost::geometry::model::box<PointT>;
45using TrajectoryPtrT = std::shared_ptr<TrajectoryT>;
46
47/**
48 * @brief A portal is simply a box defined by two points
49 * A portal has a set of trajectories that intersect it and the potentail to have a set of 'children' which
50 * are fully contained subdivisions
51 */
52class Portal : public BoxT {
53 public:
54 Portal() = delete;
55 Portal(const BoxT &b) : BoxT(b){};
56
57 /// Sort Portals basic on how many trajectories they contain
58 bool operator<(const Portal &p) const { return (trajectories.size() < p.trajectories.size()); }
59 /** @brief Divides portal into x*y children portals and assign trajectories accordingly
60 * @param _xDivision number of divisions in the x(longitude) direction
61 * @param _yDivisions number of divisions in the y(latitude) direction */
62 void divide(size_t _xDivision, size_t _yDivisions);
63 /** @brief Adds a list of trajectories to the portal
64 * @param _addList */
65 void add_trajectories(std::vector<TrajectoryPtrT> &_addList);
66 /** @brief Will assign the trajectory to this portal (does not check intersection) then adds the
67 * trajectory to all children (based on intersection)
68 * @note if the trajectory does not intersect the top level, it will still be added while not existing
69 * in any of the children (impact unknown)
70 * @param _t The trajectory to add */
71 void add_trajectory(const TrajectoryPtrT &_t);
72 /** @brief Removes a list of trajectories from the portal
73 * @param _removeList */
74 void remove_trajectories(const std::vector<TrajectoryPtrT> &_removeList);
75 /** @brief Removes a specific trajectory from portal and all children
76 * @param _t trajectory to remove */
77 void remove_trajectory(const TrajectoryPtrT &_t);
78
79 public:
80 size_t level; ///< The level this portal is at
81 std::set<TrajectoryPtrT> trajectories; ///< Trajectories that intersect this portal
82 std::list<std::shared_ptr<Portal> > children; ///< Children portals (if any)
83};
84
85/// How we pass portal references around
86using PortalPtrT = std::shared_ptr<Portal>;
87/// Overide default sorting for PortalPtrT
88bool operator<(const PortalPtrT &p1, const PortalPtrT &p2);
89
90BOOST_GEOMETRY_REGISTER_BOX(Portal, PointT, min_corner(), max_corner());
91
92#endif // portal_h
Portal.cpp¶
1#include "Portal.h"
2
3#include <boost/geometry/arithmetic/arithmetic.hpp>
4
5void Portal::divide(size_t _xDivisions, size_t _yDivisions) {
6 /** notes about boost geomtry arithmetic;
7 * the first point is piecwise modified by the second point
8 * to preserve the original point it is necessary to make copies first
9 */
10 auto delta = this->max_corner();
11 boost::geometry::subtract_point(delta, this->min_corner());
12 boost::geometry::divide_point(delta,PointT(_xDivisions,_yDivisions));
13
14 for (auto i = 0u; i < _xDivisions; ++i) {
15 for (auto j = 0u; j < _yDivisions; ++j) {
16 auto ll = this->min_corner();
17 auto d = delta;
18 boost::geometry::multiply_point(d,PointT(i,j));
19 boost::geometry::add_point(ll,d);
20
21 auto ur = ll;
22 boost::geometry::add_point(ur,delta);
23
24 auto p = std::make_shared<Portal>(BoxT(ll, ur));
25 p->level = this->level + 1;
26 // Now, go through all of the trajectories associated with the parent portal
27 // and assign them to the child by testing intersection.
28 for (auto &t : this->trajectories) {
29 if (boost::geometry::intersects(*t, *p)) {
30 p->trajectories.insert(t);
31 }
32 }
33 // only keep the child if it's non empty
34 if (!p->trajectories.empty()) {
35 this->children.push_back(p);
36 }
37 }
38 }
39}
40
41void Portal::add_trajectories(std::vector<TrajectoryPtrT> &_addList) {
42 for (auto &t : _addList) {
43 add_trajectory(t);
44 }
45}
46void Portal::add_trajectory(const TrajectoryPtrT &_t) {
47 trajectories.insert(_t);
48 for (auto &c : children) {
49 if (boost::geometry::intersects(*_t, *c)) {
50 c->add_trajectory(_t);
51 }
52 }
53}
54
55void Portal::remove_trajectories(const std::vector<TrajectoryPtrT> &_removeList) {
56 for (auto &t : _removeList) {
57 remove_trajectory( t);
58 }
59}
60
61void Portal::remove_trajectory(const TrajectoryPtrT &_t) {
62 if (0 == trajectories.erase(_t)) {
63 return; //if it doesn't intersect the parent, it doesn't intersect any of the children
64 }
65 for (auto &c : children) {
66 c->remove_trajectory(_t);
67 }
68}
69
70bool operator<(const PortalPtrT &p1, const PortalPtrT &p2) {
71 return p1->trajectories.size() < p2->trajectories.size();
72}
PortalPair.h¶
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#ifndef PortalPair_h
31#define PortalPair_h
32
33#include "Portal.h"
34
35#include <algorithm>
36#include <queue>
37
38using TrajectoryIterT = TrajectoryT::iterator;
39
40/** Used to trak and rank pairs of portal
41 * The value of a pair represents how many trajectories travel between the two
42 * portals */
43class PortalPair {
44 public:
45 PortalPair(const PortalPtrT &_p1, const PortalPtrT &_p2) {
46 if (_p1->trajectories.size() > _p2->trajectories.size()) {
47 p1 = _p1;
48 p2 = _p2;
49 } else {
50 p1 = _p2;
51 p2 = _p1;
52 }
53 update_value();
54 update_seperation();
55 }
56
57 /// Sort based on custom value
58 bool operator<(const PortalPair &_other) const {
59 return (value * seperation) < (_other.value * _other.seperation);
60 }
61
62 /** compute the value of this pair of portals
63 * The value is equal to the number of trajectories that seem to start in one portal and end
64 * in the other */
65 void update_value();
66 /** @brief updates the seperation between the two portals
67 * No real reason this should ever change with normal use */
68 void update_seperation();
69 /** @brief Determines the 'innermost' points of a trajectory that connect the two portals
70 *
71 * @param trajectory Trajectory in question
72 * @param first_pt determined first point
73 * @param last_pt determined second point
74 */
75 void get_segment(const TrajectoryPtrT &trajectory, TrajectoryIterT &first_pt, TrajectoryIterT &last_pt) const;
76 /** @brief determines if the innermost segment of a trajectory between the two portals constitutes the
77 * bulk of the trajectory, this is the criteria for a trajectory being 'from/to' the pair of portals
78 *
79 * @param _trajectory The trajectory being tested
80 * @param _ecc The deviation allowed
81 * @return true The trajectory is 'from/to' the portals
82 * @return false otherwise
83 */
84 bool is_within_portal_ellipse(const TrajectoryPtrT &_trajectory, const double &_ecc) const;
85
86 public:
87 PortalPtrT p1; ///< First Portal
88 PortalPtrT p2; ///< Second Portal
89 size_t value; ///< value of portal pair
90 double seperation; ///< distance between portals
91 std::vector<TrajectoryPtrT> contributors; ///< trajectories that contribute to the value of the pair
92};
93
94/** Management object for a set of PortalPairs
95 * Based on priority queue which keeps elements sorted
96 * allows for management and refinement of pairs and their respective portals
97 * @note Highly coupled to PortalPair and Portal
98 * @sa PortalPair Portal */
99class PairHeap : public std::priority_queue<PortalPair> {
100 public:
101 /** @brief If possible, subdivides one of the portal in the top ranked pair
102 * Returning false implies a 'best' result has bubbled to the top
103 *
104 * @return true If refinement was done
105 * @return false If refinement was unecessary or impossible*/
106 bool refine_pairs();
107 /** @brief takes a 'refinable' pair and subdivides one them
108 * The pair is then destroyed and a new set of pairs between the undivided portal and the newly created
109 * children of the divided portal. */
110 void refine_top_pair();
111 /** @brief MegaPop() removes the top pair from the list and removes any trajectories connecting that pair
112 * finally the rest of the heap is rescored*/
113 void remove_top_pair();
114 /** @brief Does initial intersection of _trajectories and _top
115 * Does initial subdivision of _top and assigns trajectories accordingly
116 * Creates an initial pair list based on populated divisions
117 *
118 * @param _trajectories trajectories of interest
119 * @param _startingPortal A presumably empty top level portal
120 */
121 void initialize(std::vector<TrajectoryPtrT> &_trajectories, PortalPtrT &_startingPortal);
122 /** @brief goes through the pair heap and corresponding Portal;
123 * recursively peeling off the top pair and refining it down to the depth specified
124 * in the heap.
125 *
126 * Every time the top pair has a level equal to the desired depth, it is recorded
127 * in a kml file and the corresponding trajectories are removed from all portals
128 * in the heirachy
129 */
130 void find_portals();
131
132 public:
133 double minimumSeperation = 10.0; ///< Minimum Seperation for two portals to be considered a viable pair
134 size_t minimumValue = 16u; ///< Minimum value for a pair to be worth keeping
135 size_t xDivisions = 2u; ///< number of subdivisions to use in the x(longitude) direction
136 size_t yDivisions = 2u; ///< number of subdivisions to use in the y(latitude) direction
137 size_t depth = 5u; ///< maximum depth to subdivide to
138 PortalPtrT topPortal; ///< Pointer to top level portal
139};
140
141#endif
PortalPair.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#include "PortalPair.h"
31
32#include <tracktable/RW/KmlOut.h>
33
34#include <boost/geometry/algorithms/intersects.hpp>
35
36void PortalPair::update_value() {
37 value = 0;
38 contributors.clear();
39 std::set_intersection(p1->trajectories.begin(), p1->trajectories.end(), p2->trajectories.begin(),
40 p2->trajectories.end(), std::back_inserter(contributors));
41
42 contributors.erase(
43 std::remove_if(contributors.begin(), contributors.end(),
44 [&](const TrajectoryPtrT _t) {
45 if (is_within_portal_ellipse(_t, 1.01)) {
46 ++value; // cheating by incrementing value from within the lambda
47 return true;
48 }
49 return false;
50 }),
51 contributors.end());
52}
53
54void PortalPair::update_seperation() { seperation = boost::geometry::distance(*p1, *p2); }
55
56bool PortalPair::is_within_portal_ellipse(const TrajectoryPtrT &_trajectory, const double &_ecc) const {
57 TrajectoryT::iterator first_pt, last_pt;
58 get_segment(_trajectory, first_pt, last_pt);
59 TrajectoryT segment(first_pt, last_pt);
60
61 return (!segment.empty() &&
62 (tracktable::length(segment) < _ecc * tracktable::distance(segment.front(), segment.back())));
63}
64
65void PortalPair::get_segment(const TrajectoryPtrT &trajectory, TrajectoryIterT &first_pt,
66 TrajectoryIterT &last_pt) const {
67 // We know that the trajectory intersects both portals because it is in both portals list and they got
68 // added to the list because they intersect
69 unsigned int prev = 0; // Has intersected neither
70 TrajectoryIterT cur_box1, cur_box2;
71
72 if (trajectory->size() < 2)
73 std::cout << "Warning, trajectory->size() = " << trajectory->size() << std::endl;
74 // If the path zigzags in and out of portals, it will remember the two innermost intersections
75 for (auto pt = trajectory->begin(); (pt + 1) != trajectory->end(); ++pt) {
76 auto segment = boost::geometry::model::segment<PointT>(*pt, *(pt + 1));
77 if (boost::geometry::intersects(segment, *p1)) {
78 cur_box1 = pt; // if we were leaving, this was the last point inside the box
79 if (prev == 2) { // means we are entering instead of leaving
80 ++cur_box1; // enter means the other point was the last one inside the box
81 last_pt = cur_box1; // entering the box means the segment is ending here
82 first_pt = cur_box2;
83 }
84 prev = 1;
85 }
86 if (boost::geometry::intersects(segment, *p2)) {
87 cur_box2 = pt;
88 if (prev == 1) { // last intersection was with portal 1
89 ++cur_box2;
90 first_pt = cur_box1;
91 last_pt = cur_box2;
92 }
93 prev = 2;
94 }
95 }
96 if (0 == prev) {
97 std::cout << "Something is very wrong!" << std::endl;
98 }
99}
100
101bool PairHeap::refine_pairs() {
102 if (empty()) return false;
103 if (((top().p1->level >= depth) && (top().p2->level >= depth))) {
104 if (top().seperation > this->minimumSeperation) {
105 return false;
106 }
107 remove_top_pair(); // Consider removing a pair that is too close as a 'refinement'
108 } else {
109 refine_top_pair();
110 }
111 return true;
112}
113
114void PairHeap::refine_top_pair() {
115 // Decompose the first portal by default (it's the largest), or
116 // do the second if the first is already at desired depth
117 auto shrink = top().p1;
118 auto keep = top().p2;
119 if (shrink->level >= depth) {
120 assert(top().p2->level < depth); // check done before call should not allow
121 std::swap(shrink, keep);
122 }
123 pop();
124
125 // If we haven't already created the children in the decomposition, do so
126 /* TODO: Is it possible to have it already divided? if it is, should the pairing below be done? should we
127 * have popped? */
128 if (shrink->children.empty()) {
129 shrink->divide(xDivisions, yDivisions);
130 }
131
132 // Now reassign the pairs, we do not enforce minimum seperation at this time
133 for (auto first = shrink->children.begin(); first != shrink->children.end(); ++first) {
134 PortalPair p(*first, keep);
135 if (p.value >= minimumValue) {
136 push(p);
137 }
138 for (auto second = std::next(first); second != shrink->children.end(); ++second) {
139 PortalPair p2(*first, *second);
140 if (p2.value >= minimumValue) {
141 push(p2);
142 }
143 }
144 }
145}
146
147void PairHeap::remove_top_pair() {
148 // remove the contributors to the top pairs value
149 topPortal->remove_trajectories(top().contributors);
150 // remove the top
151 pop();
152
153 // Rescore and filter the heap NOTE: 'c' comes from the base class
154 c.erase(std::remove_if(c.begin(), c.end(),
155 [&](PortalPair &_p) {
156 _p.update_value();
157 return _p.value < minimumValue;
158 }),
159 c.end());
160 // resort the heap and store it like 'priority_queue' prefers.
161 std::make_heap(c.begin(), c.end());
162}
163
164void PairHeap::initialize(std::vector<TrajectoryPtrT> &_trajectories, PortalPtrT &_startingPortal) {
165 topPortal = _startingPortal;
166 // Initialize the simulation by making one big portal out of the _startingPortal
167 // and then decomposing it.
168
169 for (auto &t : _trajectories) {
170 if (boost::geometry::intersects(*t, *topPortal)) {
171 topPortal->add_trajectory(t);
172 }
173 }
174
175 // Note: we are assuming _startingPortal is the USA and has an aspect ratio
176 // of 12 by 5. Using a different aliquot than that in the command below
177 // will result in non-square portals. Not that there is anything wrong
178 // with that.
179
180 topPortal->divide(12, 5);
181
182 // Now initialize pair list with all of the children...
183 // We do not enforce a separation at this time as it potentials blocks future children with a valid
184 // separation
185 for (auto first = topPortal->children.begin(); first != topPortal->children.end(); ++first) {
186 for (auto second = std::next(first); second != topPortal->children.end(); ++second) {
187 PortalPair p(*first, *second);
188 if (p.value > minimumValue /* && p.seperation > minimumSeperation*/) {
189 push(p);
190 }
191 }
192 }
193}
194
195void writeKmlPortalPair(const PortalPair &pp, const std::string &file_name);
196
197void PairHeap::find_portals() {
198 static int i = 0;
199 while (!empty()) {
200 while (refine_pairs())
201 ;
202 if (!empty()) {
203 std::stringstream filename;
204 filename << "flights" << i << ".kml";
205 writeKmlPortalPair(top(), filename.str());
206 remove_top_pair();
207 ++i;
208 }
209 }
210}
211
212using tracktable::kml;
213void writeKmlPortalPair(const PortalPair &pp, const std::string &file_name) {
214 std::ofstream out(file_name.c_str());
215 out.precision(15);
216 out << kml::header;
217 kml::width(3);
218 kml::write(out, pp.contributors);
219
220 out << kml::style("Portal", "FF0000FF", 1.0);
221 out << kml::startpm();
222 out << kml::startmulti();
223 out << kml::box(pp.p1->min_corner(), pp.p1->max_corner());
224 out << kml::box(pp.p2->min_corner(), pp.p2->max_corner());
225 out << kml::stopmulti();
226 out << kml::stoppm();
227 out << kml::footer;
228}