diff --git a/pkg/database/route_segment_matching.go b/pkg/database/route_segment_matching.go
index b1997299..389b6634 100644
--- a/pkg/database/route_segment_matching.go
+++ b/pkg/database/route_segment_matching.go
@@ -7,26 +7,26 @@ import (
// MaxDeltaMeter is the maximum distance in meters that a point can be away from
// the route segment
-const MaxDeltaMeter = 20
+const MaxDeltaMeter = 20.0
// MaxTotalDistancePercentage is the maximum percentage of the total distance of
// the route segment that can be exceeded by the total distance matching part of
// the route
-const MaxTotalDistancePercentage = 0.02
+const MaxTotalDistanceFraction = 0.9
// RouteSegmentMatch is a match between a route segment and a workout
type RouteSegmentMatch struct {
Workout *Workout
RouteSegment *RouteSegment
- first, last, end MapPoint // The first and last point of the route
+ first, last MapPoint // The first and last point of the route
+ end MapPoint // The last point of the workout
RouteSegmentID uint `gorm:"primaryKey"` // The ID of the route segment
WorkoutID uint `gorm:"primaryKey"` // The ID of the workout
FirstID, LastID int // The index of the first and last point of the route
Distance float64 // The total distance of the route segment for this workout
Duration time.Duration // The total duration of the route segment for this workout
- Points int // The total number of points of the route segment for this workout
}
func (rsm *RouteSegmentMatch) AverageSpeed() float64 {
@@ -37,14 +37,10 @@ func (rsm *RouteSegmentMatch) AverageSpeed() float64 {
// the first and last point of the route along the route segment
func (rs *RouteSegment) NewRouteSegmentMatch(workout *Workout, p, last int) *RouteSegmentMatch {
rsm := &RouteSegmentMatch{
- RouteSegmentID: rs.ID,
- WorkoutID: workout.ID,
- FirstID: p,
- LastID: last,
-
- first: workout.Data.Details.Points[p],
- last: workout.Data.Details.Points[last],
- end: workout.Data.Details.Points[len(workout.Data.Details.Points)-1],
+ Workout: workout,
+ RouteSegment: rs,
+ FirstID: p,
+ LastID: last,
}
rsm.calculate()
@@ -62,13 +58,19 @@ func (rsm *RouteSegmentMatch) IsBetterThan(current *RouteSegmentMatch) bool {
// within MaxTotalDistancePercentage of the distance of the current route
// segment
func (rsm *RouteSegmentMatch) MatchesDistance(distance float64) bool {
- return math.Abs(1-(rsm.Distance/distance)) < MaxTotalDistancePercentage
+ return math.Abs(rsm.Distance/distance) > MaxTotalDistanceFraction
}
// calculate will calculate the total distance and duration of the route
// segment, and the total number of points of this workout along the route
// segment
func (rsm *RouteSegmentMatch) calculate() {
+ rsm.RouteSegmentID = rsm.RouteSegment.ID
+ rsm.WorkoutID = rsm.Workout.ID
+ rsm.first = rsm.Workout.Data.Details.Points[rsm.FirstID]
+ rsm.last = rsm.Workout.Data.Details.Points[rsm.LastID]
+ rsm.end = rsm.Workout.Data.Details.Points[len(rsm.Workout.Data.Details.Points)-1]
+
if rsm.FirstID <= rsm.LastID {
rsm.Distance = rsm.last.TotalDistance - rsm.first.TotalDistance
rsm.Duration = rsm.last.TotalDuration - rsm.first.TotalDuration
@@ -166,12 +168,16 @@ func (rs *RouteSegment) MatchSegment(workout *Workout, start int, forward bool)
if forward {
cur++
+
+ if cur == segmentLength {
+ return index, true
+ }
} else {
cur--
- }
- if cur%segmentLength == 0 {
- return index, true
+ if cur == 0 {
+ return index, true
+ }
}
if !rs.Circular && index < start {
@@ -190,7 +196,8 @@ func (rs *RouteSegment) StartingPoints(points []MapPoint) []int {
start := rs.Points[0]
for i, p := range points {
- if start.DistanceTo(&p) < MaxDeltaMeter {
+ d := start.DistanceTo(&p)
+ if d < MaxDeltaMeter {
r = append(r, i)
}
}
diff --git a/pkg/database/route_segment_test.go b/pkg/database/route_segment_test.go
new file mode 100644
index 00000000..c6c73b59
--- /dev/null
+++ b/pkg/database/route_segment_test.go
@@ -0,0 +1,108 @@
+package database
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRouteSegment_Parse(t *testing.T) {
+ {
+ rs, err := NewRouteSegment("", "meer.gpx", []byte(meer))
+ assert.NoError(t, err)
+ assert.NotNil(t, rs)
+ assert.Greater(t, rs.TotalDistance, 1800.0)
+ }
+
+ {
+ rs, err := NewRouteSegment("", "finsepiste.gpx", []byte(finsepiste))
+ assert.NoError(t, err)
+ assert.NotNil(t, rs)
+ assert.Greater(t, rs.TotalDistance, 900.0)
+ }
+}
+
+func TestRouteSegment_FindMatches(t *testing.T) {
+ rs, err := NewRouteSegment("", "finsepiste.gpx", []byte(finsepiste))
+ assert.NoError(t, err)
+
+ w1, err := NewWorkout(AnonymousUser(), WorkoutTypeAutoDetect, "", "match.gpx", []byte(track))
+ assert.NoError(t, err)
+ assert.True(t, w1.Type.IsLocation())
+ assert.True(t, w1.HasTracks())
+
+ w2, err := NewWorkout(AnonymousUser(), WorkoutTypeAutoDetect, "", "nomatch.gpx", []byte(GpxSample1))
+ assert.NoError(t, err)
+ assert.True(t, w2.Type.IsLocation())
+ assert.True(t, w2.HasTracks())
+
+ workouts := []*Workout{w1, w2}
+ matches := rs.FindMatches(workouts)
+
+ assert.Len(t, matches, 1)
+ assert.Len(t, matches[0].Workout.Data.Details.Points, 158)
+}
+
+func TestRouteSegment_StartingPoints_NoMatch(t *testing.T) {
+ rs, err := NewRouteSegment("", "finsepiste.gpx", []byte(finsepiste))
+ assert.NoError(t, err)
+
+ w, err := NewWorkout(AnonymousUser(), WorkoutTypeAutoDetect, "", "nomatch.gpx", []byte(GpxSample1))
+ assert.NoError(t, err)
+
+ sp := rs.StartingPoints(w.Data.Details.Points)
+ assert.Empty(t, sp)
+}
+
+func TestRouteSegment_StartingPoints_Match(t *testing.T) {
+ rs, err := NewRouteSegment("", "finsepiste.gpx", []byte(finsepiste))
+ assert.NoError(t, err)
+
+ w, err := NewWorkout(AnonymousUser(), WorkoutTypeAutoDetect, "", "match.gpx", []byte(track))
+ assert.NoError(t, err)
+
+ sp := rs.StartingPoints(w.Data.Details.Points)
+ assert.NotEmpty(t, sp)
+ assert.Greater(t, len(sp), 0)
+
+ for _, p := range sp {
+ assert.Less(t, rs.Points[0].DistanceTo(&w.Data.Details.Points[p]), MaxDeltaMeter)
+ }
+}
+
+func TestRouteSegment_StartingPoints_MatchSegment(t *testing.T) {
+ rs, err := NewRouteSegment("", "finsepiste.gpx", []byte(finsepiste))
+ assert.NoError(t, err)
+
+ w, err := NewWorkout(AnonymousUser(), WorkoutTypeAutoDetect, "", "match.gpx", []byte(track))
+ assert.NoError(t, err)
+
+ sp := rs.StartingPoints(w.Data.Details.Points)
+ assert.NotEmpty(t, sp)
+ assert.Greater(t, len(sp), 0)
+
+ {
+ last, ok := rs.MatchSegment(w, 3, true)
+ assert.Zero(t, last)
+ assert.False(t, ok)
+ }
+
+ {
+ last, ok := rs.MatchSegment(w, 4, true)
+ assert.NotZero(t, last)
+ assert.True(t, ok)
+ }
+}
+
+func TestRouteSegment_Match(t *testing.T) {
+ rs, err := NewRouteSegment("", "finsepiste.gpx", []byte(finsepiste))
+ assert.NoError(t, err)
+
+ w, err := NewWorkout(AnonymousUser(), WorkoutTypeAutoDetect, "", "match.gpx", []byte(track))
+ assert.NoError(t, err)
+
+ rsm := rs.Match(w)
+ assert.NotNil(t, rsm)
+ assert.Greater(t, rsm.Distance, 900.0)
+ assert.True(t, rsm.MatchesDistance(rs.TotalDistance))
+}
diff --git a/pkg/database/segment1_test.go b/pkg/database/segment1_test.go
new file mode 100644
index 00000000..b77c362f
--- /dev/null
+++ b/pkg/database/segment1_test.go
@@ -0,0 +1,77 @@
+package database
+
+//nolint:lll
+const meer = `
+
+
+
+
+Runnermaps route
+
+
+
+
+Rotselaar Het meer
+
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+
+
+
+`
diff --git a/pkg/database/segment2_test.go b/pkg/database/segment2_test.go
new file mode 100644
index 00000000..90f98a37
--- /dev/null
+++ b/pkg/database/segment2_test.go
@@ -0,0 +1,75 @@
+package database
+
+//nolint:lll
+const finsepiste = `
+
+
+
+ route_86636_track
+
+ gpx.studio
+
+
+
+ Runnermaps route
+
+
+
+
+ finsepiste
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
+`
diff --git a/pkg/database/track_test.go b/pkg/database/track_test.go
new file mode 100644
index 00000000..0f721661
--- /dev/null
+++ b/pkg/database/track_test.go
@@ -0,0 +1,654 @@
+package database
+
+const track = `
+
+
+
+ 24 aug. 2024 15:17:28
+
+ gpx.studio
+
+
+
+
+
+ 24 aug. 2024 15:17:28
+ FitoTrack
+ running
+
+
+ 52.4583417160683
+
+
+
+ 52.438713463766405
+
+
+
+ 52.41191358060513
+
+
+
+ 52.43342956309676
+
+
+
+ 52.34359235257779
+
+
+
+ 52.37737585651892
+
+
+
+ 52.44022462884034
+
+
+
+ 52.444188099299325
+
+
+
+ 52.40361007457137
+
+
+
+ 52.39681555378454
+
+
+
+ 52.388510957837276
+
+
+
+ 52.391719663194415
+
+
+
+ 52.43890310871618
+
+
+
+ 52.504582931365455
+
+
+
+ 52.48193507369944
+
+
+
+ 52.51798341790284
+
+
+
+ 52.52062536823767
+
+
+
+ 52.53327054471646
+
+
+
+ 52.63443195654681
+
+
+
+ 52.64084882230435
+
+
+
+ 52.63782867198348
+
+
+
+ 52.627448335766964
+
+
+
+ 52.73710289858086
+
+
+
+ 52.74672846969553
+
+
+
+ 52.76522430186629
+
+
+
+ 52.81674887287634
+
+
+
+ 52.79900835076417
+
+
+
+ 52.74880475492154
+
+
+
+ 52.76314856159704
+
+
+
+ 52.770697847485714
+
+
+
+ 52.78938277964949
+
+
+
+ 52.80655763665284
+
+
+
+ 52.7907037548169
+
+
+
+ 52.84524793119107
+
+
+
+ 52.87544561970251
+
+
+
+ 52.90904002365061
+
+
+
+ 52.92111899006384
+
+
+
+ 52.955091049041236
+
+
+
+ 52.958865964463946
+
+
+
+ 52.91545688940814
+
+
+
+ 52.961507914798766
+
+
+
+ 53.01341014083811
+
+
+
+ 53.043418729356524
+
+
+
+ 53.09267791514754
+
+
+
+ 53.12683852916121
+
+
+
+ 53.12249740367293
+
+
+
+ 53.13363250499129
+
+
+
+ 53.067009362203905
+
+
+
+ 53.020203571711434
+
+
+
+ 53.05757289108225
+
+
+
+ 53.046814899836434
+
+
+
+ 53.01529623615759
+
+
+
+ 53.00869027040703
+
+
+
+ 53.02209021198767
+
+
+
+ 53.002272859692745
+
+
+
+ 52.94357665782332
+
+
+
+ 52.92734566591484
+
+
+
+ 52.899979572688
+
+
+
+ 52.84222723591345
+
+
+
+ 52.80617889171004
+
+
+
+ 52.800328236018075
+
+
+
+ 52.82486327891707
+
+
+
+ 52.82259843865479
+
+
+
+ 52.770319102542906
+
+
+
+ 52.71879507648962
+
+
+
+ 52.7104910254991
+
+
+
+ 52.691995193328346
+
+
+
+ 52.66576152023264
+
+
+
+ 52.635564376677955
+
+
+
+ 52.657457469242125
+
+
+
+ 52.676141856449156
+
+
+
+ 52.676896621551
+
+
+
+ 52.716719336220365
+
+
+
+ 52.722004326803514
+
+
+
+ 52.738424418705016
+
+
+
+ 52.80976579705323
+
+
+
+ 52.85090948689001
+
+
+
+ 52.91583399948069
+
+
+
+ 52.92904538602505
+
+
+
+ 52.989817328163724
+
+
+
+ 53.012465730786495
+
+
+
+ 53.07380388299073
+
+
+
+ 53.15099591703091
+
+
+
+ 53.176474825024776
+
+
+
+ 53.147410101601224
+
+
+
+ 53.13457637008616
+
+
+
+ 53.13608644524659
+
+
+
+ 53.129291924459764
+
+
+
+ 53.10211438626919
+
+
+
+ 53.073238217881915
+
+
+
+ 53.020015561631915
+
+
+
+ 52.99208271338275
+
+
+
+ 53.046060134734596
+
+
+
+ 53.03228253812466
+
+
+
+ 53.030017697862384
+
+
+
+ 52.98415577246478
+
+
+
+ 52.933952721578905
+
+
+
+ 52.910549553854295
+
+
+
+ 52.953958083953346
+
+
+
+ 52.94074669740898
+
+
+
+ 52.95414663898962
+
+
+
+ 52.95433519402589
+
+
+
+ 52.945842043042354
+
+
+
+ 52.891109311631915
+
+
+
+ 52.883182370713946
+
+
+
+ 52.817125437992125
+
+
+
+ 52.73974484891568
+
+
+
+ 52.690674218160936
+
+
+
+ 52.63461996662634
+
+
+
+ 52.59517436202952
+
+
+
+ 52.511754017268075
+
+
+
+ 52.40360952961462
+
+
+
+ 52.29810753725273
+
+
+
+ 52.26715508363945
+
+
+
+ 52.213931882432696
+
+
+
+ 52.16089778121897
+
+
+
+ 52.11409144576975
+
+
+
+ 52.02840626074603
+
+
+
+ 52.055206143907306
+
+
+
+ 52.10635305988806
+
+
+
+ 52.10956122028845
+
+
+
+ 52.07502295124548
+
+
+
+ 52.11918679140312
+
+
+
+ 52.165615471823045
+
+
+
+ 52.21204360728621
+
+
+
+ 52.3535939438515
+
+
+
+ 52.42701160742572
+
+
+
+ 52.480234808632474
+
+
+
+ 52.50967773204207
+
+
+
+ 52.57724419496758
+
+
+
+ 52.61574593446953
+
+
+
+ 52.66651519542098
+
+
+
+ 52.71067903557862
+
+
+
+ 52.71520871610318
+
+
+
+ 52.742197699257474
+
+
+
+ 52.84486918624827
+
+
+
+ 52.879407455291236
+
+
+
+ 52.90337628812466
+
+
+
+ 52.920928800157306
+
+
+
+ 52.886768186143634
+
+
+
+ 52.875632539868526
+
+
+
+ 52.9097931538822
+
+
+
+ 52.905829683423214
+
+
+
+ 52.925080825652564
+
+
+
+ 52.94678536318047
+
+
+
+ 53.04889064010569
+
+
+
+ 52.98623096777728
+
+
+
+ 52.981324177180184
+
+
+
+ 53.096640840649776
+
+
+
+ 53.0813531688794
+
+
+
+ 53.04889118506244
+
+
+
+ 53.037189873678514
+
+
+
+ 52.939048067212276
+
+
+
+ 52.93168733635987
+
+
+
+ 52.928667730995755
+
+
+
+ 52.88129573043772
+
+
+
+ 52.949616958465064
+
+
+
+ 52.96396076514056
+
+
+
+
+
+`
diff --git a/views/route_segments/add.templ b/views/route_segments/add.templ
index c103b4d7..0a72cdd6 100644
--- a/views/route_segments/add.templ
+++ b/views/route_segments/add.templ
@@ -68,9 +68,9 @@ templ Add() {
{ i18n.T(ctx, "Resources") }
{ i18n.T(ctx, "You can find inspiration here:") }
diff --git a/views/route_segments/add_templ.go b/views/route_segments/add_templ.go
index fa4d717b..a66b3b32 100644
--- a/views/route_segments/add_templ.go
+++ b/views/route_segments/add_templ.go
@@ -151,7 +151,7 @@ func Add() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/views/route_segments/list.templ b/views/route_segments/list.templ
index 1f7d09f9..3f81d843 100644
--- a/views/route_segments/list.templ
+++ b/views/route_segments/list.templ
@@ -41,6 +41,11 @@ templ List(segments []*database.RouteSegment) {
@listDetails(s)
+ if s.Dirty {
+
+ @helpers.IconFor("refresh")
+
+ }
@actions(s)
diff --git a/views/route_segments/list_templ.go b/views/route_segments/list_templ.go
index fe2f9ebf..88f14e4f 100644
--- a/views/route_segments/list_templ.go
+++ b/views/route_segments/list_templ.go
@@ -164,7 +164,38 @@ func List(segments []*database.RouteSegment) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " | ")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, " | ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if s.Dirty {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = helpers.IconFor("refresh").Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -172,12 +203,12 @@ func List(segments []*database.RouteSegment) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, " | ")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}