mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-11 09:41:40 -05:00
Store track distance in user's preferred unit
This commit is contained in:
parent
0d657b9d6e
commit
f33dcdfe21
10 changed files with 56 additions and 51 deletions
|
|
@ -26,7 +26,6 @@ class MapController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def extract_track_ids
|
def extract_track_ids
|
||||||
# Extract track IDs from coordinates (index 8: [lat, lng, battery, altitude, timestamp, velocity, id, country, track_id])
|
|
||||||
@coordinates.map { |coord| coord[8]&.to_i }.compact.uniq.reject(&:zero?)
|
@coordinates.map { |coord| coord[8]&.to_i }.compact.uniq.reject(&:zero?)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -44,15 +43,14 @@ class MapController < ApplicationController
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Convert distance to meters for consistent storage
|
|
||||||
distance_in_meters = case current_user.safe_settings.distance_unit.to_s
|
distance_in_meters = case current_user.safe_settings.distance_unit.to_s
|
||||||
when 'miles', 'mi'
|
when 'mi'
|
||||||
distance * 1609.344 # miles to meters
|
distance * 1609.344 # miles to meters
|
||||||
else
|
else
|
||||||
distance * 1000 # km to meters
|
distance * 1000 # km to meters
|
||||||
end
|
end
|
||||||
|
|
||||||
distance_in_meters.round # Return as integer meters
|
distance_in_meters.round
|
||||||
end
|
end
|
||||||
|
|
||||||
def parsed_start_at
|
def parsed_start_at
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export function createTrackPopupContent(track, distanceUnit) {
|
||||||
<strong>🕐 Start:</strong> ${startTime}<br>
|
<strong>🕐 Start:</strong> ${startTime}<br>
|
||||||
<strong>🏁 End:</strong> ${endTime}<br>
|
<strong>🏁 End:</strong> ${endTime}<br>
|
||||||
<strong>⏱️ Duration:</strong> ${durationFormatted}<br>
|
<strong>⏱️ Duration:</strong> ${durationFormatted}<br>
|
||||||
<strong>📏 Distance:</strong> ${formatDistance(track.distance / 1000, distanceUnit)}<br>
|
<strong>📏 Distance:</strong> ${formatDistance(track.distance, distanceUnit)}<br>
|
||||||
<strong>⚡ Avg Speed:</strong> ${formatSpeed(track.avg_speed, distanceUnit)}<br>
|
<strong>⚡ Avg Speed:</strong> ${formatSpeed(track.avg_speed, distanceUnit)}<br>
|
||||||
<strong>⛰️ Elevation:</strong> +${track.elevation_gain || 0}m / -${track.elevation_loss || 0}m<br>
|
<strong>⛰️ Elevation:</strong> +${track.elevation_gain || 0}m / -${track.elevation_loss || 0}m<br>
|
||||||
<strong>📊 Max Alt:</strong> ${track.elevation_max || 0}m<br>
|
<strong>📊 Max Alt:</strong> ${track.elevation_max || 0}m<br>
|
||||||
|
|
@ -356,8 +356,8 @@ export function toggleTracksVisibility(tracksLayer, map, isVisible) {
|
||||||
// Helper function to filter tracks by criteria
|
// Helper function to filter tracks by criteria
|
||||||
export function filterTracks(tracks, criteria) {
|
export function filterTracks(tracks, criteria) {
|
||||||
return tracks.filter(track => {
|
return tracks.filter(track => {
|
||||||
if (criteria.minDistance && track.distance < criteria.minDistance * 1000) return false;
|
if (criteria.minDistance && track.distance < criteria.minDistance) return false;
|
||||||
if (criteria.maxDistance && track.distance > criteria.maxDistance * 1000) return false;
|
if (criteria.maxDistance && track.distance > criteria.maxDistance) return false;
|
||||||
if (criteria.minDuration && track.duration < criteria.minDuration * 60) return false;
|
if (criteria.minDuration && track.duration < criteria.minDuration * 60) return false;
|
||||||
if (criteria.maxDuration && track.duration > criteria.maxDuration * 60) return false;
|
if (criteria.maxDuration && track.duration > criteria.maxDuration * 60) return false;
|
||||||
if (criteria.startDate && new Date(track.start_at) < new Date(criteria.startDate)) return false;
|
if (criteria.startDate && new Date(track.start_at) < new Date(criteria.startDate)) return false;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class IncrementalTrackGeneratorJob < ApplicationJob
|
class Tracks::IncrementalGeneratorJob < ApplicationJob
|
||||||
queue_as :default
|
queue_as :default
|
||||||
sidekiq_options retry: 3
|
sidekiq_options retry: 3
|
||||||
|
|
||||||
|
|
@ -53,28 +53,14 @@ module Calculateable
|
||||||
end
|
end
|
||||||
|
|
||||||
def convert_distance_for_storage(calculated_distance)
|
def convert_distance_for_storage(calculated_distance)
|
||||||
if track_model?
|
# Store distance in user's preferred unit with 2 decimal places precision
|
||||||
convert_distance_to_meters(calculated_distance)
|
calculated_distance.round(2)
|
||||||
else
|
|
||||||
# For Trip model - store rounded distance in user's preferred unit
|
|
||||||
calculated_distance.round
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def track_model?
|
def track_model?
|
||||||
self.class.name == 'Track'
|
self.class.name == 'Track'
|
||||||
end
|
end
|
||||||
|
|
||||||
def convert_distance_to_meters(calculated_distance)
|
|
||||||
# For Track model - convert to meters for storage (Track expects distance in meters)
|
|
||||||
case user_distance_unit.to_s
|
|
||||||
when 'mi'
|
|
||||||
(calculated_distance * 1609.344).round # miles to meters
|
|
||||||
else
|
|
||||||
(calculated_distance * 1000).round # km to meters
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def save_if_changed!
|
def save_if_changed!
|
||||||
save! if changed?
|
save! if changed?
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -44,19 +44,12 @@ module Tracks::TrackBuilder
|
||||||
Tracks::BuildPath.new(points.map(&:lonlat)).call
|
Tracks::BuildPath.new(points.map(&:lonlat)).call
|
||||||
end
|
end
|
||||||
|
|
||||||
# Calculate track distance in meters for storage
|
# Calculate track distance in user's preferred unit for storage
|
||||||
# @param points [Array<Point>] array of Point objects
|
# @param points [Array<Point>] array of Point objects
|
||||||
# @return [Integer] distance in meters
|
# @return [Float] distance in user's preferred unit with 2 decimal places precision
|
||||||
def calculate_track_distance(points)
|
def calculate_track_distance(points)
|
||||||
distance_in_user_unit = Point.total_distance(points, user.safe_settings.distance_unit || 'km')
|
distance_in_user_unit = Point.total_distance(points, user.safe_settings.distance_unit || 'km')
|
||||||
|
distance_in_user_unit.round(2)
|
||||||
# Convert to meters for storage (Track model expects distance in meters)
|
|
||||||
case user.safe_settings.distance_unit
|
|
||||||
when 'miles', 'mi'
|
|
||||||
(distance_in_user_unit * 1609.344).round # miles to meters
|
|
||||||
else
|
|
||||||
(distance_in_user_unit * 1000).round # km to meters
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Calculate track duration in seconds
|
# Calculate track duration in seconds
|
||||||
|
|
@ -67,11 +60,19 @@ module Tracks::TrackBuilder
|
||||||
end
|
end
|
||||||
|
|
||||||
# Calculate average speed in km/h
|
# Calculate average speed in km/h
|
||||||
# @param distance_meters [Numeric] distance in meters
|
# @param distance_in_user_unit [Numeric] distance in user's preferred unit
|
||||||
# @param duration_seconds [Numeric] duration in seconds
|
# @param duration_seconds [Numeric] duration in seconds
|
||||||
# @return [Float] average speed in km/h
|
# @return [Float] average speed in km/h
|
||||||
def calculate_average_speed(distance_meters, duration_seconds)
|
def calculate_average_speed(distance_in_user_unit, duration_seconds)
|
||||||
return 0.0 if duration_seconds <= 0 || distance_meters <= 0
|
return 0.0 if duration_seconds <= 0 || distance_in_user_unit <= 0
|
||||||
|
|
||||||
|
# Convert distance to meters for speed calculation
|
||||||
|
distance_meters = case user.safe_settings.distance_unit
|
||||||
|
when 'miles', 'mi'
|
||||||
|
distance_in_user_unit * 1609.344 # miles to meters
|
||||||
|
else
|
||||||
|
distance_in_user_unit * 1000 # km to meters
|
||||||
|
end
|
||||||
|
|
||||||
# Speed in meters per second, then convert to km/h for storage
|
# Speed in meters per second, then convert to km/h for storage
|
||||||
speed_mps = distance_meters.to_f / duration_seconds
|
speed_mps = distance_meters.to_f / duration_seconds
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ class CreateTracks < ActiveRecord::Migration[8.0]
|
||||||
t.datetime :end_at, null: false
|
t.datetime :end_at, null: false
|
||||||
t.references :user, null: false, foreign_key: true
|
t.references :user, null: false, foreign_key: true
|
||||||
t.line_string :original_path, null: false
|
t.line_string :original_path, null: false
|
||||||
t.integer :distance
|
t.decimal :distance, precision: 8, scale: 2
|
||||||
t.float :avg_speed
|
t.float :avg_speed
|
||||||
t.integer :duration
|
t.integer :duration
|
||||||
t.integer :elevation_gain
|
t.integer :elevation_gain
|
||||||
|
|
|
||||||
|
|
@ -120,5 +120,25 @@ RSpec.describe Point, type: :model do
|
||||||
expect(point.lat).to eq(2)
|
expect(point.lat).to eq(2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#recalculate_track' do
|
||||||
|
let(:point) { create(:point, track: track) }
|
||||||
|
let(:track) { create(:track) }
|
||||||
|
|
||||||
|
it 'recalculates the track' do
|
||||||
|
expect(track).to receive(:recalculate_path_and_distance!)
|
||||||
|
|
||||||
|
point.update(lonlat: 'POINT(-79.85581250721961 15.854775993302411)')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#trigger_incremental_track_generation' do
|
||||||
|
let(:point) { create(:point, track: track) }
|
||||||
|
let(:track) { create(:track) }
|
||||||
|
|
||||||
|
it 'enqueues Tracks::IncrementalGeneratorJob' do
|
||||||
|
expect { point.trigger_incremental_track_generation }.to have_enqueued_job(Tracks::IncrementalGeneratorJob)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -143,16 +143,16 @@ RSpec.describe Track, type: :model do
|
||||||
track.calculate_distance
|
track.calculate_distance
|
||||||
|
|
||||||
expect(track.distance).to be > 0
|
expect(track.distance).to be > 0
|
||||||
expect(track.distance).to be_a(Integer)
|
expect(track.distance).to be_a(Numeric)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'stores distance in meters for Track model' do
|
it 'stores distance in user preferred unit for Track model' do
|
||||||
allow(user).to receive(:safe_settings).and_return(double(distance_unit: 'km'))
|
allow(user).to receive(:safe_settings).and_return(double(distance_unit: 'km'))
|
||||||
allow(Point).to receive(:total_distance).and_return(1.5) # 1.5 km
|
allow(Point).to receive(:total_distance).and_return(1.5) # 1.5 km
|
||||||
|
|
||||||
track.calculate_distance
|
track.calculate_distance
|
||||||
|
|
||||||
expect(track.distance).to eq(1500) # Should be in meters as integer
|
expect(track.distance).to eq(1.5) # Should be 1.5 km with 2 decimal places precision
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -274,9 +274,9 @@ RSpec.describe Tracks::CreateFromPoints do
|
||||||
allow(Point).to receive(:total_distance).and_return(1.5) # 1.5 km
|
allow(Point).to receive(:total_distance).and_return(1.5) # 1.5 km
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'converts km to meters by default' do
|
it 'stores distance in km by default' do
|
||||||
distance = service.send(:calculate_track_distance, points)
|
distance = service.send(:calculate_track_distance, points)
|
||||||
expect(distance).to eq(1500) # 1.5 km = 1500 meters
|
expect(distance).to eq(1.5) # 1.5 km with 2 decimal places precision
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with miles unit' do
|
context 'with miles unit' do
|
||||||
|
|
@ -284,9 +284,9 @@ RSpec.describe Tracks::CreateFromPoints do
|
||||||
user.update!(settings: user.settings.merge({'maps' => {'distance_unit' => 'miles'}}))
|
user.update!(settings: user.settings.merge({'maps' => {'distance_unit' => 'miles'}}))
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'converts miles to meters' do
|
it 'stores distance in miles' do
|
||||||
distance = service.send(:calculate_track_distance, points)
|
distance = service.send(:calculate_track_distance, points)
|
||||||
expect(distance).to eq(2414) # 1.5 miles ≈ 2414 meters (rounded)
|
expect(distance).to eq(1.5) # 1.5 miles with 2 decimal places precision
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -138,9 +138,9 @@ RSpec.describe Tracks::TrackBuilder do
|
||||||
allow(Point).to receive(:total_distance).and_return(1.5) # 1.5 km
|
allow(Point).to receive(:total_distance).and_return(1.5) # 1.5 km
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'converts km to meters' do
|
it 'stores distance in km' do
|
||||||
result = builder.calculate_track_distance(points)
|
result = builder.calculate_track_distance(points)
|
||||||
expect(result).to eq(1500) # 1.5 km = 1500 meters
|
expect(result).to eq(1.5) # 1.5 km with 2 decimal places precision
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -150,9 +150,9 @@ RSpec.describe Tracks::TrackBuilder do
|
||||||
allow(Point).to receive(:total_distance).and_return(1.0) # 1 mile
|
allow(Point).to receive(:total_distance).and_return(1.0) # 1 mile
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'converts miles to meters' do
|
it 'stores distance in miles' do
|
||||||
result = builder.calculate_track_distance(points)
|
result = builder.calculate_track_distance(points)
|
||||||
expect(result).to eq(1609) # 1 mile ≈ 1609 meters
|
expect(result).to eq(1) # 1 mile
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -162,9 +162,9 @@ RSpec.describe Tracks::TrackBuilder do
|
||||||
allow(Point).to receive(:total_distance).and_return(2.0)
|
allow(Point).to receive(:total_distance).and_return(2.0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'defaults to km and converts to meters' do
|
it 'defaults to km and stores distance in km' do
|
||||||
result = builder.calculate_track_distance(points)
|
result = builder.calculate_track_distance(points)
|
||||||
expect(result).to eq(2000)
|
expect(result).to eq(2.0) # 2.0 km with 2 decimal places precision
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue