99#include < tbb/parallel_for_each.h>
1010#include < tbb/concurrent_set.h>
1111
12+ static constexpr std::time_t SECONDS_IN_MINUTE = 60 ;
13+ static constexpr std::time_t SECONDS_IN_HOUR = 3600 ;
14+
1215namespace dsf ::mdt {
1316 TrajectoryCollection::TrajectoryCollection (
1417 std::unordered_map<
@@ -22,11 +25,12 @@ namespace dsf::mdt {
2225 auto const & lats = std::get<std::vector<double >>(dataframe.at (" lat" ));
2326 auto const & lons = std::get<std::vector<double >>(dataframe.at (" lon" ));
2427
28+ auto const bbox_set =
29+ !(bbox[0 ] == 0.0 && bbox[1 ] == 0.0 && bbox[2 ] == 0.0 && bbox[3 ] == 0.0 );
30+
2531 for (std::size_t i = 0 ; i < uids.size (); ++i) {
2632 auto const point = dsf::geometry::Point (lons[i], lats[i]);
2733 // If bbox is the default-initialized array (all zeros) we treat it as unset.
28- bool bbox_set =
29- !(bbox[0 ] == 0.0 && bbox[1 ] == 0.0 && bbox[2 ] == 0.0 && bbox[3 ] == 0.0 );
3034 if (bbox_set && (point.x () < bbox[0 ] || point.x () > bbox[2 ] ||
3135 point.y () < bbox[1 ] || point.y () > bbox[3 ])) {
3236 continue ;
@@ -86,11 +90,38 @@ namespace dsf::mdt {
8690 tbb::concurrent_set<Id> to_remove;
8791 tbb::concurrent_set<Id> to_split;
8892
93+ // Returns true if speed between two points is below max_speed_kph
94+ auto check_max_speed = [](PointsCluster const & currentCluster,
95+ PointsCluster const & previousCluster,
96+ double const max_speed_kph) {
97+ auto const distance_km = dsf::geometry::haversine_km (currentCluster.centroid (),
98+ previousCluster.centroid ());
99+ auto const current_time =
100+ (currentCluster.lastTimestamp () + currentCluster.firstTimestamp ()) * 0.5 ;
101+ auto const previous_time =
102+ (previousCluster.lastTimestamp () + previousCluster.firstTimestamp ()) * 0.5 ;
103+ if (current_time <= previous_time) {
104+ spdlog::warn (
105+ " Non-increasing timestamps detected. Skipping speed check for these points." );
106+ return true ;
107+ }
108+ auto const speed_kph =
109+ (distance_km * SECONDS_IN_HOUR) / (current_time - previous_time);
110+ return speed_kph <= max_speed_kph;
111+ };
112+ // Returns true if cluster duration is below min_duration_min
113+ auto check_min_duration = [](dsf::mdt::PointsCluster const & cluster,
114+ std::time_t const min_duration_min) {
115+ return cluster.duration () < min_duration_min * SECONDS_IN_MINUTE;
116+ };
117+
89118 tbb::parallel_for_each (
90119 m_trajectories.begin (),
91120 m_trajectories.end (),
92121 [&to_remove,
93122 &to_split,
123+ check_max_speed,
124+ check_min_duration,
94125 min_points_per_trajectory,
95126 cluster_radius_km,
96127 max_speed_kph,
@@ -110,14 +141,21 @@ namespace dsf::mdt {
110141 to_remove.insert (uid);
111142 return ;
112143 }
113- if (!min_duration_min. has_value ()) {
114- return ;
115- }
116- for ( auto const & cluster : trajectory. points ()) {
117- if (cluster. duration () < min_duration_min.value () * 60 ) {
144+ auto const & points{trajectory. points ()};
145+ for (std:: size_t i = 0 ; i < points. size (); ++i) {
146+ auto const & currentCluster = points[i];
147+ if (min_duration_min. has_value () &&
148+ ! check_min_duration (currentCluster, min_duration_min.value ()) ) {
118149 to_split.insert (uid);
119150 return ;
120151 }
152+ if (i > 0 ) {
153+ auto const & previousCluster = points[i - 1 ];
154+ if (!check_max_speed (currentCluster, previousCluster, max_speed_kph)) {
155+ to_split.insert (uid);
156+ return ;
157+ }
158+ }
121159 }
122160 });
123161
@@ -143,16 +181,25 @@ namespace dsf::mdt {
143181 trajectories.clear ();
144182
145183 Trajectory newTrajectory;
146- for (auto const & cluster : originalTrajectory.points ()) {
147- newTrajectory.addCluster (cluster);
148- if (cluster.duration () < min_duration_min.value () * 60 ) {
149- continue ;
184+ auto const & points{originalTrajectory.points ()};
185+ for (size_t i = 0 ; i < points.size (); ++i) {
186+ auto const & currentCluster = points[i];
187+ newTrajectory.addCluster (currentCluster);
188+
189+ bool bShouldSplit = false ;
190+
191+ if (i > 0 ) {
192+ auto const & previousCluster = points[i - 1 ];
193+ bShouldSplit = !check_max_speed (currentCluster, previousCluster, max_speed_kph);
194+ }
195+ if (min_duration_min.has_value () && !bShouldSplit) {
196+ bShouldSplit = !check_min_duration (currentCluster, min_duration_min.value ());
150197 }
151198 // Cluster meets minimum duration - finalize current trajectory and start a new one
152199 if (!newTrajectory.empty ()) {
153200 trajectories.emplace_back (std::move (newTrajectory));
154201 newTrajectory = Trajectory ();
155- newTrajectory.addCluster (cluster );
202+ newTrajectory.addCluster (currentCluster );
156203 }
157204 }
158205 if (newTrajectory.size () > 1 ) {
0 commit comments