struct RouteSample { /// The x-coordinate in the map image (left to right) in the range [0,1] var x: Double /// The y-coordinate in the map image (top to bottom) in the range [0,1] var y: Double /// The timestamp of the sample in the range [0,1] var time: Double /// The distance of the sample in the range [0,1] var distance: Double /// The elevation of the sample in the range [0,1] var elevation: Double /// The speed of the sample in the range [0,1] var speed: Double /// The pace of the sample in the range [0,1] var pace: Double /// The heart rate of the sample in the range [0,1] var hr: Double /// The active energy rate of the sample in the range [0,1] var energy: Double? } extension RouteSample: Codable { func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(x.rounded(decimals: 4), forKey: .x) try container.encode(y.rounded(decimals: 4), forKey: .y) try container.encode(time.rounded(decimals: 4), forKey: .time) try container.encode(distance.rounded(decimals: 4), forKey: .distance) try container.encode(elevation.rounded(decimals: 4), forKey: .elevation) try container.encode(speed.rounded(decimals: 4), forKey: .speed) try container.encode(pace.rounded(decimals: 4), forKey: .pace) try container.encode(hr.rounded(decimals: 4), forKey: .hr) try container.encodeIfPresent(energy?.rounded(decimals: 4), forKey: .energy) } } extension RouteSample: Identifiable { var id: Double { x } } extension RouteSample { static var zero: RouteSample { .init(x: 0, y: 0, time: 0, distance: 0, elevation: 0, speed: 0, pace: 0, hr: 0, energy: nil) } } extension Collection where Element == RouteSample { var averageSample: RouteSample { guard var average = first else { return .zero } var energySamples = average.energy == nil ? 0 : 1 for sample in dropFirst() { average.x += sample.x average.y += sample.y average.time += sample.time average.distance += sample.distance average.elevation += sample.elevation average.speed += sample.speed average.pace += sample.pace average.hr += sample.hr if let energy = sample.energy { average.energy = (average.energy ?? 0) + energy energySamples += 1 } } let scale = 1 / Double(count) average.x *= scale average.y *= scale average.time *= scale average.distance *= scale average.elevation *= scale average.speed *= scale average.pace *= scale average.hr *= scale if let energy = average.energy, energySamples > 0 { average.energy = energy / Double(energySamples) } return average } }