diff --git a/app/controllers/map_controller.rb b/app/controllers/map_controller.rb
index 4bb8994d..99cb98e8 100644
--- a/app/controllers/map_controller.rb
+++ b/app/controllers/map_controller.rb
@@ -26,7 +26,6 @@ class MapController < ApplicationController
end
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?)
end
@@ -44,15 +43,14 @@ class MapController < ApplicationController
)
end
- # Convert distance to meters for consistent storage
distance_in_meters = case current_user.safe_settings.distance_unit.to_s
- when 'miles', 'mi'
+ when 'mi'
distance * 1609.344 # miles to meters
else
distance * 1000 # km to meters
end
- distance_in_meters.round # Return as integer meters
+ distance_in_meters.round
end
def parsed_start_at
diff --git a/app/javascript/maps/tracks.js b/app/javascript/maps/tracks.js
index 53355c1c..ffda6a35 100644
--- a/app/javascript/maps/tracks.js
+++ b/app/javascript/maps/tracks.js
@@ -30,7 +30,7 @@ export function createTrackPopupContent(track, distanceUnit) {
🕐 Start: ${startTime}
🏁 End: ${endTime}
⏱️ Duration: ${durationFormatted}
- 📏 Distance: ${formatDistance(track.distance / 1000, distanceUnit)}
+ 📏 Distance: ${formatDistance(track.distance, distanceUnit)}
⚡ Avg Speed: ${formatSpeed(track.avg_speed, distanceUnit)}
⛰️ Elevation: +${track.elevation_gain || 0}m / -${track.elevation_loss || 0}m
📊 Max Alt: ${track.elevation_max || 0}m
@@ -356,8 +356,8 @@ export function toggleTracksVisibility(tracksLayer, map, isVisible) {
// Helper function to filter tracks by criteria
export function filterTracks(tracks, criteria) {
return tracks.filter(track => {
- if (criteria.minDistance && track.distance < criteria.minDistance * 1000) return false;
- if (criteria.maxDistance && track.distance > criteria.maxDistance * 1000) return false;
+ if (criteria.minDistance && track.distance < criteria.minDistance) return false;
+ if (criteria.maxDistance && track.distance > criteria.maxDistance) return false;
if (criteria.minDuration && track.duration < criteria.minDuration * 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;
diff --git a/app/jobs/incremental_track_generator_job.rb b/app/jobs/tracks/incremental_generator_job.rb
similarity index 94%
rename from app/jobs/incremental_track_generator_job.rb
rename to app/jobs/tracks/incremental_generator_job.rb
index 37dfea6f..837f6a7f 100644
--- a/app/jobs/incremental_track_generator_job.rb
+++ b/app/jobs/tracks/incremental_generator_job.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class IncrementalTrackGeneratorJob < ApplicationJob
+class Tracks::IncrementalGeneratorJob < ApplicationJob
queue_as :default
sidekiq_options retry: 3
diff --git a/app/models/concerns/calculateable.rb b/app/models/concerns/calculateable.rb
index c612ef6c..45450e82 100644
--- a/app/models/concerns/calculateable.rb
+++ b/app/models/concerns/calculateable.rb
@@ -53,28 +53,14 @@ module Calculateable
end
def convert_distance_for_storage(calculated_distance)
- if track_model?
- convert_distance_to_meters(calculated_distance)
- else
- # For Trip model - store rounded distance in user's preferred unit
- calculated_distance.round
- end
+ # Store distance in user's preferred unit with 2 decimal places precision
+ calculated_distance.round(2)
end
def track_model?
self.class.name == 'Track'
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!
save! if changed?
end
diff --git a/app/services/tracks/track_builder.rb b/app/services/tracks/track_builder.rb
index 25262456..f62f7603 100644
--- a/app/services/tracks/track_builder.rb
+++ b/app/services/tracks/track_builder.rb
@@ -44,19 +44,12 @@ module Tracks::TrackBuilder
Tracks::BuildPath.new(points.map(&:lonlat)).call
end
- # Calculate track distance in meters for storage
+ # Calculate track distance in user's preferred unit for storage
# @param points [Array] 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)
distance_in_user_unit = Point.total_distance(points, user.safe_settings.distance_unit || 'km')
-
- # 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
+ distance_in_user_unit.round(2)
end
# Calculate track duration in seconds
@@ -67,11 +60,19 @@ module Tracks::TrackBuilder
end
# 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
# @return [Float] average speed in km/h
- def calculate_average_speed(distance_meters, duration_seconds)
- return 0.0 if duration_seconds <= 0 || distance_meters <= 0
+ def calculate_average_speed(distance_in_user_unit, duration_seconds)
+ 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_mps = distance_meters.to_f / duration_seconds
diff --git a/db/migrate/20250703193656_create_tracks.rb b/db/migrate/20250703193656_create_tracks.rb
index b89b42dc..e595b827 100644
--- a/db/migrate/20250703193656_create_tracks.rb
+++ b/db/migrate/20250703193656_create_tracks.rb
@@ -5,7 +5,7 @@ class CreateTracks < ActiveRecord::Migration[8.0]
t.datetime :end_at, null: false
t.references :user, null: false, foreign_key: true
t.line_string :original_path, null: false
- t.integer :distance
+ t.decimal :distance, precision: 8, scale: 2
t.float :avg_speed
t.integer :duration
t.integer :elevation_gain
diff --git a/spec/models/point_spec.rb b/spec/models/point_spec.rb
index 076dd218..4fa59cfb 100644
--- a/spec/models/point_spec.rb
+++ b/spec/models/point_spec.rb
@@ -120,5 +120,25 @@ RSpec.describe Point, type: :model do
expect(point.lat).to eq(2)
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
diff --git a/spec/models/track_spec.rb b/spec/models/track_spec.rb
index 91e821f5..474a51a4 100644
--- a/spec/models/track_spec.rb
+++ b/spec/models/track_spec.rb
@@ -143,16 +143,16 @@ RSpec.describe Track, type: :model do
track.calculate_distance
expect(track.distance).to be > 0
- expect(track.distance).to be_a(Integer)
+ expect(track.distance).to be_a(Numeric)
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(Point).to receive(:total_distance).and_return(1.5) # 1.5 km
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
diff --git a/spec/services/tracks/create_from_points_spec.rb b/spec/services/tracks/create_from_points_spec.rb
index df9a3352..b3a66afc 100644
--- a/spec/services/tracks/create_from_points_spec.rb
+++ b/spec/services/tracks/create_from_points_spec.rb
@@ -274,9 +274,9 @@ RSpec.describe Tracks::CreateFromPoints do
allow(Point).to receive(:total_distance).and_return(1.5) # 1.5 km
end
- it 'converts km to meters by default' do
+ it 'stores distance in km by default' do
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
context 'with miles unit' do
@@ -284,9 +284,9 @@ RSpec.describe Tracks::CreateFromPoints do
user.update!(settings: user.settings.merge({'maps' => {'distance_unit' => 'miles'}}))
end
- it 'converts miles to meters' do
+ it 'stores distance in miles' do
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
diff --git a/spec/services/tracks/track_builder_spec.rb b/spec/services/tracks/track_builder_spec.rb
index b97b5c48..ca1c47b9 100644
--- a/spec/services/tracks/track_builder_spec.rb
+++ b/spec/services/tracks/track_builder_spec.rb
@@ -138,9 +138,9 @@ RSpec.describe Tracks::TrackBuilder do
allow(Point).to receive(:total_distance).and_return(1.5) # 1.5 km
end
- it 'converts km to meters' do
+ it 'stores distance in km' do
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
@@ -150,9 +150,9 @@ RSpec.describe Tracks::TrackBuilder do
allow(Point).to receive(:total_distance).and_return(1.0) # 1 mile
end
- it 'converts miles to meters' do
+ it 'stores distance in miles' do
result = builder.calculate_track_distance(points)
- expect(result).to eq(1609) # 1 mile ≈ 1609 meters
+ expect(result).to eq(1) # 1 mile
end
end
@@ -162,9 +162,9 @@ RSpec.describe Tracks::TrackBuilder do
allow(Point).to receive(:total_distance).and_return(2.0)
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)
- expect(result).to eq(2000)
+ expect(result).to eq(2.0) # 2.0 km with 2 decimal places precision
end
end
end