3

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?

bencallis
  • 3,478
  • 4
  • 32
  • 58
  • The provided distance is the only way you can make it more accurate by getting readings more often but you can only measure based on the locations you have. – lorem ipsum Mar 27 '22 at 13:34
  • try having "Kalman Filter" for distance to normalize the result. – Ajay Singh Thakur Mar 28 '22 at 11:00
  • The Kalman filter is less helpful than you'd think, as the values provided have already been run through. – drewster Mar 28 '22 at 16:23
  • The distances provided are really the only way provided to get distances. it's a pain because you got a lot of distances that look small and error/accuracy info that makes it a very wide area. then when you add the distances, you end up with a distance that's much further than it should be. Does that sound about right? You can improve this by recording distances that have moved a minimum amount, or combining points that are near each other in time and physicality. Alternatively, look at points in sequence where their position plus maximum error doesn't overlap with a subsequent one. – drewster Mar 28 '22 at 16:30
  • Thanks for the replies. Other apps are able to do it with the data recorded by my app so I imagine I have all the data I need. I'm still not very clear on if I should have to query HealthKit for distance at certain time intervals. Currently I have a nice looking chart with elevation along the Y axis and time along the bottom but this isn't too useful. – bencallis Mar 29 '22 at 11:17
  • Did you found a solution for this? – Daniel Jun 05 '22 at 11:23
  • @Daniel No, I didn't. In the end, I just stuck with it being elevation over time. While it's not ideal it's good enough for my use case. I would love to change it though if I find out how. – bencallis Jun 10 '22 at 09:45

0 Answers0