firefly-cpp / sport-activities-features

A minimalistic toolbox for extracting features from sports activity files written in Python

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Hill visualization creates overlapping hills

KoprivcLuka opened this issue · comments

It would appear detected hills can overlap. After creation of visuals of hill climbs using the algorithm, I've noticed weird behavior when processing the data further - my bug seems to appear from hills being allowed to overlap, which I don't know if is the desired behavior. Here is the issue visualized in google earth -
image
There are actually 4 dots in the picture
image
Here are the two hills on top of each other
image
This is an outtake of my exported and processed XML file
image
The problem isn't export related as I can see the values in my debugger. I'm attaching the problematic file bellow

02_25_2019_10_51_26_trackMap.zip

Thanks, @KoprivcLuka. Since you are now fresh with this issue, can you please look at the algorithm and try to find a part of the algorithm that may raise this issue? Algorithms are straightforward for now, but control parameters may have an influence.

I also include @luckyLukac and @alenrajsp to check this issue and find a solution.

After analyzing the algorithm I think I've found the issue. In the method, once a hill is identified, the I index is not set and double checks the data. I've implemented an exampled of a quick and dirty fix locally which seems to work

def identify_hills(self) -> None:
        """
        Method for identifying hills and extracting
        total ascent and descent from data.\n
        Note:
            [WIP]
            Algorithm is still in its preliminary stage.
        """
        differences = []
        for i in range(1, len(self.altitudes)):
            if not type(self.altitudes[i]) is float and type(self.altitudes[i]) is float:
                continue
            differences.append(self.altitudes[i] - self.altitudes[i - 1])
        self.total_ascent = sum(x for x in differences if x > 0)
        self.total_descent = sum(-x for x in differences if x < 0)

        hill_segment = []
        hill_segment_ascent = 0.0

        i = 0
        while i < len(differences): # Replaced this with a while loop
            total_ascent = 0.0
            selected_IDs = []
            selected_IDs.append(i)
            descent_counter = 0

            for j in range(i + 1, len(differences)):
                NEXT = differences[j]
                if NEXT >= 0.0:
                    total_ascent = total_ascent + NEXT
                    selected_IDs.append(j)

                else:
                    if len(selected_IDs) == 1:
                        break
                    else:
                        selected_IDs.append(j)
                        descent_counter = descent_counter + 1

                if descent_counter == 10:
                    selected_IDs = selected_IDs[
                                   : len(selected_IDs) - descent_counter
                                   ]
                    break

            if self.return_hill(total_ascent):
                if len(hill_segment) < 3:  # Nothing happens...
                    hill_segment = selected_IDs
                    hill_segment_ascent = total_ascent
                else:
                    length_of_intersection = len(
                        set(hill_segment).intersection(selected_IDs)
                    )
                    calculation = float(
                        float(length_of_intersection)
                        / float(len(hill_segment))
                    )
                    if calculation < 0.1:  # if less than 10% of nodes repeat

                        avg_grade = None

                        is_a_list = isinstance(self.distances, numpy.ndarray) or isinstance(self.distances, list)
                        hill_segment_grade = None
                        if is_a_list and len(self.distances) == len(self.altitudes):
                            end_distance = self.distances[hill_segment[-1]]
                            start_distance = self.distances[hill_segment[0]]
                            hill_segment_distance = end_distance - start_distance
                            hill_segment_grade = self.__calculate_hill_grade(hill_segment_distance, hill_segment_ascent)

                        self.identified_hills.append(
                            StoredSegments(hill_segment, hill_segment_ascent, hill_segment_grade)
                        )
                        i = hill_segment[-1] # Set i to last segment 
                        hill_segment = []
                        hill_segment_ascent = 0.0

            i = i + 1 # Increment i as it is now in a while loop

After visualizing the new hills, overlaying hills (red section is old algorithm) no longer exist. However, out of 22 identified hills, 1 no longer qualifies as one due to a height difference bellow threshold.
image

At this point I would also suggest the descent / end of hill part be looked at. I believe the algorithm would work better if instead of looking at amount of points - which are not really good data as they can be very far or very close, depending on recorded data - it would be based on elevation instead (let's assume elevation data is accurate 😄 ) - If I put this in a sentence - "My hills is a hill as long as I haven't lost more than xy elevation during a climb at once.". This should result in what I believe cyclist consider a realistic hill climb. As is, under correct conditions, a short plateau can break a climb, like pictured bellow
image

@KoprivcLuka, thanks. I agree with you. Please make corrections and submit a PR. I will then release a new version.

Thanks for all the hard work!

@KoprivcLuka, if I remember correctly, you have already improved the hill detection algorithm currently residing within this package. Can you pull request changes? Are there any specific challenges?

@firefly-cpp I've only made the quick changes described above. I haven't had time to work on this more, as I'm VERY busy working on my project and have sort of pivoted away from hills for now and am trying to scale my algorithms to work on entire activities. I'll hopefully email you this month for a check-up :)

@KoprivcLuka, thanks for the update. Anyway, please incorporate even minor changes in the current project since the existing algorithms for hill detection are essential, and enhancement may help improve it.