runarium 0.1.0

Generate animated videos from GPS running/cycling data with real-time statistics
Documentation
use std::fs::File;

use anyhow::Result;
use fitparser::{profile::MesgNum, Value};

use crate::{
  types::fit_data::{LapData, RouteData},
  utils::converter::{semicircles_to_degrees, speed_to_pace},
};

pub fn fit_reader(file_path: &str) -> Result<(RouteData, LapData)> {
  let mut paces = Vec::new();
  let mut gps_points = Vec::new();
  let mut distances = Vec::new();

  let mut avg_heart_rate = Vec::new();
  let mut enhanced_avg_speed = Vec::new();
  let mut avg_step_length = Vec::new();

  let mut fp = File::open(file_path)?;

  for data in fitparser::from_reader(&mut fp)? {
    match data.kind() {
      MesgNum::Lap => {
        let mut hr = None;
        let mut speed = None;
        let mut length = None;

        for field in data.fields() {
          match (field.name(), field.value()) {
            ("avg_heart_rate", Value::UInt8(v)) => hr = Some(*v),
            ("enhanced_avg_speed", Value::Float64(v)) => {
              speed = Some(speed_to_pace(*v as f32))
            }
            ("avg_step_length", Value::Float64(v)) => length = Some(*v),
            _ => {}
          }
        }

        if let (Some(hr), Some(speed), Some(length)) = (hr, speed, length) {
          avg_heart_rate.push(hr);
          enhanced_avg_speed.push(speed);
          avg_step_length.push(length);
        }
      }

      MesgNum::Record => {
        let mut lat = None;
        let mut lon = None;
        let mut pace = None;

        for field in data.fields() {
          match (field.name(), field.value()) {
            ("enhanced_speed", Value::Float32(v)) => {
              pace = Some(speed_to_pace(*v))
            }
            ("enhanced_speed", Value::Float64(v)) => {
              pace = Some(speed_to_pace(*v as f32))
            }
            ("position_lat", Value::SInt32(v)) => {
              lat = Some(semicircles_to_degrees(*v))
            }
            ("position_long", Value::SInt32(v)) => {
              lon = Some(semicircles_to_degrees(*v))
            }
            ("distance", Value::Float64(v)) => distances.push(*v),
            _ => {}
          }
        }

        if let (Some(lat), Some(lon), Some(pace)) = (lat, lon, pace) {
          gps_points.push((lat, lon));
          paces.push(pace);
        }
      }

      _ => {}
    }
  }

  Ok((
    RouteData {
      paces,
      gps_points,
      distances,
    },
    LapData {
      avg_heart_rate,
      enhanced_avg_speed,
      avg_step_length,
    },
  ))
}