summaryrefslogtreecommitdiff
path: root/official_climb
diff options
context:
space:
mode:
Diffstat (limited to 'official_climb')
-rw-r--r--official_climb/matcher.go17
-rw-r--r--official_climb/official_climb_test.go48
2 files changed, 47 insertions, 18 deletions
diff --git a/official_climb/matcher.go b/official_climb/matcher.go
index 59c3e0e..e7b51de 100644
--- a/official_climb/matcher.go
+++ b/official_climb/matcher.go
@@ -7,7 +7,7 @@ import (
"github.com/martinlehoux/biking_home/ride"
)
-func MatchClimb(climb ride.Climb, officialClimbs []OfficialClimb, policy MatchPolicy) (OfficialClimb, bool) {
+func MatchClimb(route ride.Ride, climb ride.Climb, officialClimbs []OfficialClimb, policy MatchPolicy) (OfficialClimb, bool) {
if policy.Validate() != nil {
return OfficialClimb{}, false
}
@@ -15,14 +15,17 @@ func MatchClimb(climb ride.Climb, officialClimbs []OfficialClimb, policy MatchPo
var best OfficialClimb
found := false
for _, official := range officialClimbs {
- startIndex, startDistance := nearestClimbPoint(climb, official.StartCoord)
- endIndex, endDistance := nearestClimbPoint(climb, official.EndCoord)
+ startIndex, startDistance := nearestRoutePoint(route, official.StartCoord)
+ endIndex, endDistance := nearestRoutePoint(route, official.EndCoord)
if startDistance > policy.EndpointRadiusM || endDistance > policy.EndpointRadiusM {
continue
}
if startIndex >= endIndex {
continue
}
+ if endIndex < climb.StartIndex() || startIndex > climb.EndIndex() {
+ continue
+ }
totalDistance := startDistance + endDistance
if totalDistance < bestDistance || (totalDistance == bestDistance && (!found || official.ID < best.ID)) {
best = official
@@ -33,11 +36,11 @@ func MatchClimb(climb ride.Climb, officialClimbs []OfficialClimb, policy MatchPo
return best, found
}
-func nearestClimbPoint(climb ride.Climb, target geodist.Coord) (int, float64) {
- bestIndex := climb.StartIndex()
+func nearestRoutePoint(route ride.Ride, target geodist.Coord) (int, float64) {
+ bestIndex := 0
bestDistance := math.Inf(1)
- for index := climb.StartIndex(); index <= climb.EndIndex(); index++ {
- distance := distanceM(climb.PointCoord(index), target)
+ for index := 0; index < route.Len(); index++ {
+ distance := distanceM(route.Coord(index), target)
if distance < bestDistance {
bestIndex = index
bestDistance = distance
diff --git a/official_climb/official_climb_test.go b/official_climb/official_climb_test.go
index 848216c..f8b0fe3 100644
--- a/official_climb/official_climb_test.go
+++ b/official_climb/official_climb_test.go
@@ -48,11 +48,11 @@ func TestCreateRejectsInvalidOfficialClimb(t *testing.T) {
}
func TestMatchClimbUsesOrderedCoordinates(t *testing.T) {
- climb := testClimb(
+ parsed, climb := testClimb(
geodist.Coord{Lat: 43.1002, Lon: 5.1002},
geodist.Coord{Lat: 43.2002, Lon: 5.2002},
)
- match, found := official_climb.MatchClimb(climb, []official_climb.OfficialClimb{
+ match, found := official_climb.MatchClimb(parsed, climb, []official_climb.OfficialClimb{
{Name: "Same direction", StartCoord: geodist.Coord{Lat: 43.1, Lon: 5.1}, EndCoord: geodist.Coord{Lat: 43.2, Lon: 5.2}},
{Name: "Reverse direction", StartCoord: geodist.Coord{Lat: 43.2, Lon: 5.2}, EndCoord: geodist.Coord{Lat: 43.1, Lon: 5.1}},
}, official_climb.DefaultMatchPolicy())
@@ -62,11 +62,11 @@ func TestMatchClimbUsesOrderedCoordinates(t *testing.T) {
}
func TestMatchClimbRejectsEndpointOutsideRadius(t *testing.T) {
- climb := testClimb(
+ parsed, climb := testClimb(
geodist.Coord{Lat: 43.1, Lon: 5.1},
geodist.Coord{Lat: 43.3, Lon: 5.3},
)
- _, found := official_climb.MatchClimb(climb, []official_climb.OfficialClimb{{
+ _, found := official_climb.MatchClimb(parsed, climb, []official_climb.OfficialClimb{{
Name: "Partial match",
StartCoord: geodist.Coord{Lat: 43.1, Lon: 5.1},
EndCoord: geodist.Coord{Lat: 43.2, Lon: 5.2},
@@ -87,7 +87,7 @@ func TestMatchClimbFindsOfficialEndpointsInsideDetectedClimb(t *testing.T) {
[]time.Time{time.Unix(0, 0), time.Unix(60, 0), time.Unix(120, 0), time.Unix(180, 0)},
)
climb := parsed.ClimbFromIndexes(0, 3)
- matched, found := official_climb.MatchClimb(climb, []official_climb.OfficialClimb{{
+ matched, found := official_climb.MatchClimb(parsed, climb, []official_climb.OfficialClimb{{
Name: "Corrected boundaries",
StartCoord: parsed.Coord(1),
EndCoord: parsed.Coord(2),
@@ -97,6 +97,32 @@ func TestMatchClimbFindsOfficialEndpointsInsideDetectedClimb(t *testing.T) {
assert.Equal(t, "Corrected boundaries", matched.Name)
}
+func TestMatchClimbAllowsOfficialStartBeforeDetectedClimb(t *testing.T) {
+ parsed := ride.FromColumns(
+ []float64{0, 1000, 2000, 3000, 4000},
+ []float64{100, 120, 300, 500, 100},
+ []geodist.Coord{
+ {Lat: 43.100, Lon: 5.100},
+ {Lat: 43.101, Lon: 5.101},
+ {Lat: 43.102, Lon: 5.102},
+ {Lat: 43.103, Lon: 5.103},
+ {Lat: 43.104, Lon: 5.104},
+ },
+ []time.Time{time.Unix(0, 0), time.Unix(60, 0), time.Unix(120, 0), time.Unix(180, 0), time.Unix(240, 0)},
+ )
+ detected := parsed.ClimbFromIndexes(1, 4)
+ official := official_climb.OfficialClimb{
+ Name: "Wider canonical climb",
+ StartCoord: parsed.Coord(0),
+ EndCoord: parsed.Coord(3),
+ }
+
+ matched, found := official_climb.MatchClimb(parsed, detected, []official_climb.OfficialClimb{official}, official_climb.DefaultMatchPolicy())
+
+ require.True(t, found)
+ assert.Equal(t, official.Name, matched.Name)
+}
+
func TestMatchClimbMatchesSameOfficialClimbAcrossRides(t *testing.T) {
official := official_climb.OfficialClimb{
Name: "Shared official climb",
@@ -127,8 +153,8 @@ func TestMatchClimbMatchesSameOfficialClimbAcrossRides(t *testing.T) {
[]time.Time{time.Unix(0, 0), time.Unix(45, 0), time.Unix(90, 0), time.Unix(135, 0), time.Unix(180, 0)},
)
- firstMatch, firstFound := official_climb.MatchClimb(firstRide.ClimbFromIndexes(0, 3), []official_climb.OfficialClimb{official}, official_climb.DefaultMatchPolicy())
- secondMatch, secondFound := official_climb.MatchClimb(secondRide.ClimbFromIndexes(1, 4), []official_climb.OfficialClimb{official}, official_climb.DefaultMatchPolicy())
+ firstMatch, firstFound := official_climb.MatchClimb(firstRide, firstRide.ClimbFromIndexes(0, 3), []official_climb.OfficialClimb{official}, official_climb.DefaultMatchPolicy())
+ secondMatch, secondFound := official_climb.MatchClimb(secondRide, secondRide.ClimbFromIndexes(1, 4), []official_climb.OfficialClimb{official}, official_climb.DefaultMatchPolicy())
require.True(t, firstFound)
require.True(t, secondFound)
@@ -137,11 +163,11 @@ func TestMatchClimbMatchesSameOfficialClimbAcrossRides(t *testing.T) {
}
func TestMatchClimbChoosesNearestCandidate(t *testing.T) {
- climb := testClimb(
+ parsed, climb := testClimb(
geodist.Coord{Lat: 43.1, Lon: 5.1},
geodist.Coord{Lat: 43.2, Lon: 5.2},
)
- match, found := official_climb.MatchClimb(climb, []official_climb.OfficialClimb{
+ match, found := official_climb.MatchClimb(parsed, climb, []official_climb.OfficialClimb{
{ID: 2, Name: "Farther", StartCoord: geodist.Coord{Lat: 43.1005, Lon: 5.1005}, EndCoord: geodist.Coord{Lat: 43.2005, Lon: 5.2005}},
{ID: 1, Name: "Nearest", StartCoord: geodist.Coord{Lat: 43.1001, Lon: 5.1001}, EndCoord: geodist.Coord{Lat: 43.2001, Lon: 5.2001}},
}, official_climb.DefaultMatchPolicy())
@@ -154,12 +180,12 @@ func TestMatchPolicyRejectsInvalidRadius(t *testing.T) {
assert.EqualError(t, (official_climb.MatchPolicy{}).Validate(), "official climb endpoint radius must be greater than zero")
}
-func testClimb(start, end geodist.Coord) ride.Climb {
+func testClimb(start, end geodist.Coord) (ride.Ride, ride.Climb) {
parsed := ride.FromColumns(
[]float64{0, 1000, 2000},
[]float64{100, 150, 200},
[]geodist.Coord{{}, start, end},
[]time.Time{time.Unix(0, 0), time.Unix(60, 0), time.Unix(120, 0)},
)
- return parsed.ClimbFromDist(1000, 2000)
+ return parsed, parsed.ClimbFromDist(1000, 2000)
}