How to get the distance traveled at a certain location in a HKWorkout

377 views Asked by At

Goal

I am currently trying to get the data to plot an elevation chart for a HKWorkout. I want the X-axis to represent distance and the Y-axis to represent elevation. This sort of graph is fairly common and acts as a sort of cross section of a workout route.

This is a rough mockup of the sort of chart I am hoping to plot.

enter image description here

Progress

I have successfully queried the WorkoutRoute for a given HKWorkout and I am able to get all of the recorded CLLocations in the route.

    func getWorkoutRoute(for workout: HKWorkout) async -> [CLLocation]? {
        let byWorkout = HKQuery.predicateForObjects(from: workout)
        
        let samples = try? await withCheckedThrowingContinuation { (continuation: CheckedContinuation<[HKSample], Error>) in
            store.execute(HKAnchoredObjectQuery(type: HKSeriesType.workoutRoute(), predicate: byWorkout, anchor: nil, limit: HKObjectQueryNoLimit, resultsHandler: { (query, samples, deletedObjects, anchor, error) in
                if let hasError = error {
                    continuation.resume(throwing: hasError); return
                }
                
                guard let samples = samples else { return }
                
                continuation.resume(returning: samples)
            }))
        }
        
        guard let route = (samples as? [HKWorkoutRoute])?.first else { return nil }

        let locations = try? await withCheckedThrowingContinuation { (continuation: CheckedContinuation<[CLLocation], Error>) in
            var allLocations = [CLLocation]() // built up over time as and when HK tells us
            store.execute(HKWorkoutRouteQuery(route: route) { (query, locationsOrNil, done, errorOrNil) in
                // This block may be called multiple times.
                if let error = errorOrNil {
                    continuation.resume(throwing: error); return
                }
                
                guard let locations = locationsOrNil else {
                    fatalError("Invalid State: This can only fail if there was an error.")
                }
                allLocations += locations
                    
                if done {
                    continuation.resume(returning: allLocations)
                }
            })
        }
        return locations
    }

Problem

Within each CLLocation I am able to access the elevation (so that's the y-axis sorted) but I am struggling to find out how to get the distance traveled for the given location.

CLLocation does has a method to get the distance between two locations but this is calculated as the crow flies so will not give accurate values. The locations also include a timestamp. The HKWorkout objects should include all the data I need to acutely get the distance I'm just not sure how.

Other health apps such as HealthFit are able to show get the data I am looking for so I know it must be possible.

enter image description here

Can anyone point me in the right direction?

0

There are 0 answers