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
× Something went wrong with that request. Please try again.