App developed by Aleksandr Kunin
Deutsch translate by Tatiana Holm
Spanish translate by Ekaterina Larina
Logo designed by Julia Makoveeva
def distance a, b rad_per_deg = Math::PI/180 # PI / 180 rkm = 6371 # Радиус земли в километрах rm = rkm * 1000 dlon_rad = (b[1]-a[1]) * rad_per_deg dlat_rad = (b[0]-a[0]) * rad_per_deg lat1_rad, lon1_rad = a.map! {|i| i * rad_per_deg } lat2_rad, lon2_rad = b.map! {|i| i * rad_per_deg } a = Math.sin(dlat_rad/2)**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * Math.sin(dlon_rad/2)**2 c = 2 * Math.asin(Math.sqrt(a)) rm * c # Расстояние в метрах end def process_track_points track_points # Пока не придумал что делать с 5 Гц и 10 Гц файлами - оставляю только первую запись по дате создания track_points.uniq!{ |x| DateTime.strptime(x[:point_created_at], '%Y-%m-%dT%H:%M:%S') } min_h = track_points.min_by{ |x| x[:elevation] }[:elevation] # Уменьшим высоту во всех точках на минимальную. (корректировка относительно уровня земли) track_points.each do |x| x[:elevation] -= min_h end min_h = track_points.min_by{ |x| x[:elevation] }[:elevation] max_h = track_points.max_by{ |x| x[:elevation] }[:elevation] # Расчет дистанции и времени полета fl_time = 0 track_points.each_index do |i| point = track_points[i] point[:distance] = 0 if i == 0 if i > 0 prev_point = track_points.at(i-1) datetime_1 = DateTime.strptime(point[:point_created_at], '%Y-%m-%dT%H:%M:%S') datetime_2 = DateTime.strptime(prev_point[:point_created_at], '%Y-%m-%dT%H:%M:%S') fl_time_diff = (datetime_1 - datetime_2) * 1.days fl_time += fl_time_diff point[:distance] = calc_distance [prev_point[:latitude], prev_point[:longitude]], [point[:latitude], point[:longitude]] point[:h_speed] = point[:distance] / fl_time_diff * 3.6 point[:v_speed] = (prev_point[:elevation] - point[:elevation]) / fl_time_diff * 3.6 end point[:fl_time] = fl_time end # Медианный фильтр для расстояния и высоты track_points.each_index do |i| point = track_points[i] median_start = [0, i-1].max median_end = [track_points.count-1, i+1].min median_points = [track_points[median_start], point, track_points[median_end]] point[:distance] = median_points.map { |x| x[:distance] }.sort[1] point[:elevation] = median_points.map { |x| x[:elevation] }.sort[1] point[:h_speed] = median_points.map { |x| x[:h_speed] || 0 }.sort[1] point[:v_speed] = median_points.map { |x| x[:v_speed] || 0 }.sort[1] end self.ff_start = 0 self.ff_end = fl_time # Развернем массив и найдем точку после достижения максимальной высоты и набору скорости в 25 км/ч track_points.reverse! start_point = track_points.detect { |x| x[:elevation] >= (max_h - 15) } self.ff_start = start_point[:fl_time] if start_point.present? track_points.reverse! start_point = track_points.detect { |x| (x[:fl_time] > self.ff_start && x[:v_speed] > 25) } self.ff_start = start_point[:fl_time] if start_point.present? # Найдем первую точку ниже минимума (предполагаю Земли) + 50 метров end_point = track_points.detect { |x| x[:elevation] < (min_h + 50) } self.ff_end = end_point[:fl_time] if end_point.present? track_points end
function set_chart_data() { var dist_data = [], elev_data = [], heights_data = [], h_speed = [], v_speed = [], gr = [], avg_h_speed = 0, avg_v_speed = 0, avg_gr = 0, min_h_speed = 0, max_h_speed = 0, min_v_speed = 0, max_v_speed = 0, min_gr = 0, max_gr = 0, fl_time = 0, dist = 0, elev = 0; max_val = typeof range_from !== 'undefined' ? range_from : 100000; min_val = typeof range_to !== 'undefined' ? range_to : 0; var isFirst = true, isLast = false; for (var index in charts_data) { var current_point = charts_data[index]; var point = {}; isLast = true; if (current_point.elevation <= max_val && current_point.elevation >= min_val) { point = clone(current_point); // Корректировка выбранного диапазона if (isFirst) { isFirst = false; if (current_point.elevation != max_val && charts_data.hasOwnProperty(index-1)) { point.elevation_diff = max_val - current_point.elevation; var k = point.elevation_diff / current_point.elevation_diff; point.distance = Math.round(current_point.distance * k); point.fl_time = Math.round(current_point.fl_time * k); point.elevation = current_point.elevation; point.v_speed = current_point.v_speed; point.h_speed = current_point.h_speed; } } isLast = false; dist += point.distance; elev += point.elevation_diff; elev_data.push([fl_time, elev]); dist_data.push([fl_time, dist]); heights_data.push([fl_time, point.elevation]); h_speed.push([fl_time, point.h_speed]); v_speed.push([fl_time, point.v_speed]); gr.push([fl_time, point.glrat]); fl_time += point.fl_time; min_h_speed = min_h_speed == 0 || min_h_speed > point.h_speed ? point.h_speed : min_h_speed; max_h_speed = max_h_speed == 0 || max_h_speed < point.h_speed ? point.h_speed : max_h_speed; min_v_speed = min_v_speed == 0 || min_v_speed > point.v_speed ? point.v_speed : min_v_speed; max_v_speed = max_v_speed == 0 || max_v_speed < point.v_speed ? point.v_speed : max_v_speed; min_gr = min_gr == 0 || min_gr > point.glrat ? point.glrat : min_gr; max_gr = max_gr == 0 || max_gr < point.glrat ? point.glrat : max_gr; } if (isLast && elev_data.length > 0) { if (current_point.elevation <= min_val && charts_data.hasOwnProperty(index - 1)) { point = clone(current_point); prev_point = charts_data[index - 1]; point.elevation_diff = prev_point.elevation - min_val; var k = point.elevation_diff / current_point.elevation_diff; point.fl_time = current_point.fl_time * k; point.distance = Math.round(current_point.distance * k); dist += point.distance; elev += point.elevation_diff; elev_data.push([fl_time, elev]); dist_data.push([fl_time, dist]); heights_data.push([fl_time, point.elevation]); h_speed.push([fl_time, point.h_speed]); v_speed.push([fl_time, point.v_speed]); gr.push([fl_time, point.glrat]); } break; } } var ed_chart = $('#elevation_distance_chart').highcharts(); ed_chart.series[0].setData(elev_data); ed_chart.series[1].setData(dist_data); ed_chart.series[2].setData(heights_data); var sp_chart = $('#speeds_chart').highcharts(); sp_chart.series[0].setData(h_speed); sp_chart.series[1].setData(v_speed); var gr_chart = $('#glideratio_chart').highcharts(); gr_chart.series[0].setData(gr); var ad_chart = $('#all_data_chart').highcharts(); ad_chart.series[0].setData(h_speed); ad_chart.series[1].setData(v_speed); ad_chart.series[2].setData(gr); ad_chart.series[3].setData(heights_data); ad_chart.series[4].setData(dist_data); ad_chart.series[5].setData(elev_data); $('#dd_distance').text(dist.toString() + ' м'); $('#dd_elevation').text(elev.toString() + ' м'); $('#dd_fl_time').text(fl_time.toString() + ' с'); $('#p_min_v_speed').text(min_v_speed.toFixed(0) + '...'); $('#p_max_v_speed').text('...' + max_v_speed.toFixed(0)); $('#p_avg_v_speed').text(Math.round(elev / fl_time * 3.6)); $('#p_min_h_speed').text(min_h_speed.toFixed(0) + '...'); $('#p_max_h_speed').text('...' + max_h_speed.toFixed(0)); $('#p_avg_h_speed').text(Math.round(dist / fl_time * 3.6).toString()); $('#p_min_gr').text(min_gr.toFixed(2) + '...'); $('#p_max_gr').text('...' + max_gr.toFixed(2)); $('#p_avg_gr').text((dist / elev).toFixed(2)); };
GPX icon made by Freepik from
www.flaticon.com
is licensed by
CC BY 3.0