mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-11 01:31:39 -05:00
83 lines
2.3 KiB
Ruby
83 lines
2.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Visits
|
|
# Merges consecutive visits that are likely part of the same stay
|
|
class Merger
|
|
MAXIMUM_VISIT_GAP = 30.minutes
|
|
SIGNIFICANT_MOVEMENT_THRESHOLD = 50 # meters
|
|
|
|
attr_reader :points
|
|
|
|
def initialize(points)
|
|
@points = points
|
|
end
|
|
|
|
def merge_visits(visits)
|
|
return visits if visits.empty?
|
|
|
|
merged = []
|
|
current_merged = visits.first
|
|
|
|
visits[1..].each do |visit|
|
|
if can_merge_visits?(current_merged, visit)
|
|
# Merge the visits
|
|
current_merged[:end_time] = visit[:end_time]
|
|
current_merged[:points].concat(visit[:points])
|
|
else
|
|
merged << current_merged
|
|
current_merged = visit
|
|
end
|
|
end
|
|
|
|
merged << current_merged
|
|
merged
|
|
end
|
|
|
|
private
|
|
|
|
def can_merge_visits?(first_visit, second_visit)
|
|
return false unless same_location?(first_visit, second_visit)
|
|
return false if gap_too_large?(first_visit, second_visit)
|
|
return false if significant_movement_between?(first_visit, second_visit)
|
|
|
|
true
|
|
end
|
|
|
|
def same_location?(first_visit, second_visit)
|
|
distance = Geocoder::Calculations.distance_between(
|
|
[first_visit[:center_lat], first_visit[:center_lon]],
|
|
[second_visit[:center_lat], second_visit[:center_lon]],
|
|
units: :km
|
|
)
|
|
|
|
# Convert to meters and check if within threshold
|
|
(distance * 1000) <= SIGNIFICANT_MOVEMENT_THRESHOLD
|
|
end
|
|
|
|
def gap_too_large?(first_visit, second_visit)
|
|
gap = second_visit[:start_time] - first_visit[:end_time]
|
|
gap > MAXIMUM_VISIT_GAP
|
|
end
|
|
|
|
def significant_movement_between?(first_visit, second_visit)
|
|
# Get points between the two visits
|
|
between_points = points.where(
|
|
timestamp: (first_visit[:end_time] + 1)..(second_visit[:start_time] - 1)
|
|
)
|
|
|
|
return false if between_points.empty?
|
|
|
|
visit_center = [first_visit[:center_lat], first_visit[:center_lon]]
|
|
max_distance = between_points.map do |point|
|
|
Geocoder::Calculations.distance_between(
|
|
visit_center,
|
|
[point.lat, point.lon],
|
|
units: :km
|
|
)
|
|
end.max
|
|
|
|
# Convert to meters and check if exceeds threshold
|
|
(max_distance * 1000) > SIGNIFICANT_MOVEMENT_THRESHOLD
|
|
end
|
|
end
|
|
end
|