From 16865fa9d34ab74bfff12d3207df161bc00023e0 Mon Sep 17 00:00:00 2001 From: J Guthrie Date: Thu, 26 Jul 2018 20:47:34 +0100 Subject: [PATCH 1/2] Added distance of traces to model and view - Migrated a new column (trace-length) - Added 'Distance' row in trace view - Calculates distance bwteen each point using haversine formula --- app/models/trace.rb | 8 +++-- app/views/traces/view.html.erb | 3 ++ config/locales/en.yml | 1 + .../20180726180947_add_length_to_trace.rb | 9 ++++++ lib/gpx.rb | 29 +++++++++++++++++++ 5 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20180726180947_add_length_to_trace.rb diff --git a/app/models/trace.rb b/app/models/trace.rb index 214b0b6470..b151c359ad 100644 --- a/app/models/trace.rb +++ b/app/models/trace.rb @@ -2,17 +2,18 @@ # # Table name: gpx_files # -# id :integer not null, primary key -# user_id :integer not null +# id :bigint(8) not null, primary key +# user_id :bigint(8) not null # visible :boolean default(TRUE), not null # name :string default(""), not null -# size :integer +# size :bigint(8) # latitude :float # longitude :float # timestamp :datetime not null # description :string default(""), not null # inserted :boolean not null # visibility :enum default("public"), not null +# length :bigint(8) # # Indexes # @@ -319,6 +320,7 @@ def import self.large_picture = gpx.picture(min_lat, min_lon, max_lat, max_lon, gpx.actual_points) self.icon_picture = gpx.icon(min_lat, min_lon, max_lat, max_lon) self.size = gpx.actual_points + self.length = gpx.length self.inserted = true save! end diff --git a/app/views/traces/view.html.erb b/app/views/traces/view.html.erb index 648160c2f6..569d060884 100644 --- a/app/views/traces/view.html.erb +++ b/app/views/traces/view.html.erb @@ -27,6 +27,9 @@ <%= t '.start_coordinates' %>
<%= @trace.latitude %>; <%= @trace.longitude %>
(<%=link_to t('.map'), :controller => 'site', :action => 'index', :mlat => @trace.latitude, :mlon => @trace.longitude, :anchor => "map=14/#{@trace.latitude}/#{@trace.longitude}" %> / <%=link_to t('.edit'), :controller => 'site', :action => 'edit', :gpx=> @trace.id, :anchor => "map=14/#{@trace.latitude}/#{@trace.longitude}" %>) + + <%= t '.distance' %> + <%= @trace.length ? "#{(@trace.length / 1000.0).round(2)}km" : "N/A" %> <% end %> <%= t '.owner' %> diff --git a/config/locales/en.yml b/config/locales/en.yml index a403d29592..3760fd3d70 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1732,6 +1732,7 @@ en: uploaded: "Uploaded:" points: "Points:" start_coordinates: "Start coordinate:" + distance: "Distance:" map: "map" edit: "edit" owner: "Owner:" diff --git a/db/migrate/20180726180947_add_length_to_trace.rb b/db/migrate/20180726180947_add_length_to_trace.rb new file mode 100644 index 0000000000..4e8b3652ad --- /dev/null +++ b/db/migrate/20180726180947_add_length_to_trace.rb @@ -0,0 +1,9 @@ +class AddLengthToTrace < ActiveRecord::Migration[5.2] + def up + add_column "gpx_files", "length", :bigint + end + + def down + remove_column "gpx_files", "length" + end +end diff --git a/lib/gpx.rb b/lib/gpx.rb index ee9d53afaa..abc6507962 100644 --- a/lib/gpx.rb +++ b/lib/gpx.rb @@ -7,6 +7,7 @@ class File attr_reader :possible_points attr_reader :actual_points attr_reader :tracksegs + attr_reader :length def initialize(file) @file = file @@ -16,12 +17,14 @@ def points @possible_points = 0 @actual_points = 0 @tracksegs = 0 + @length = 0 @file.rewind reader = XML::Reader.io(@file) point = nil + last_point = nil while reader.read if reader.node_type == XML::Reader::TYPE_ELEMENT @@ -38,6 +41,12 @@ def points point.altitude ||= 0 yield point @actual_points += 1 + + if last_point + @length += dist_between(point, last_point) + end + + last_point = point elsif reader.name == "trkseg" @tracksegs += 1 end @@ -146,6 +155,26 @@ def icon(min_lat, min_lon, max_lat, max_lon) image.to_blob end + + private + def dist_between(p1, p2) + earth_radius = 6371 + + lat_dist = (p1.latitude - p2.latitude) * Math::PI / 180 + lon_dist = (p1.longitude - p2.longitude) * Math::PI / 180 + + a = Math.sin(lat_dist / 2) * Math.sin(lat_dist / 2) + Math.cos(p1.latitude * Math::PI / 180) * Math.cos(p2.latitude * Math::PI / 180) * Math.sin(lon_dist / 2) * Math.sin(lon_dist / 2) + + c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) + + distance = earth_radius * c * 1000 + + height = p1.altitude - p2.altitude + + distance = (distance ** 2) + (height ** 2) + + return Math.sqrt(distance) + end end TrkPt = Struct.new(:segment, :latitude, :longitude, :altitude, :timestamp) do From a29f391befc3179941aaf3a03a30ce6e6fbda477 Mon Sep 17 00:00:00 2001 From: J Guthrie Date: Thu, 26 Jul 2018 21:14:48 +0100 Subject: [PATCH 2/2] Make changes to satisfy rubocop --- lib/gpx.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/gpx.rb b/lib/gpx.rb index abc6507962..516f09b1e2 100644 --- a/lib/gpx.rb +++ b/lib/gpx.rb @@ -42,10 +42,8 @@ def points yield point @actual_points += 1 - if last_point - @length += dist_between(point, last_point) - end - + @length += dist_between(point, last_point) if last_point + last_point = point elsif reader.name == "trkseg" @tracksegs += 1 @@ -157,6 +155,7 @@ def icon(min_lat, min_lon, max_lat, max_lon) end private + def dist_between(p1, p2) earth_radius = 6371 @@ -171,9 +170,9 @@ def dist_between(p1, p2) height = p1.altitude - p2.altitude - distance = (distance ** 2) + (height ** 2) + distance = (distance**2) + (height**2) - return Math.sqrt(distance) + Math.sqrt(distance) end end