diff options
Diffstat (limited to 'official_climb')
| -rw-r--r-- | official_climb/matcher.go | 52 | ||||
| -rw-r--r-- | official_climb/model.go | 50 | ||||
| -rw-r--r-- | official_climb/repository.go (renamed from official_climb/official_climb.go) | 87 |
3 files changed, 102 insertions, 87 deletions
diff --git a/official_climb/matcher.go b/official_climb/matcher.go new file mode 100644 index 0000000..59c3e0e --- /dev/null +++ b/official_climb/matcher.go @@ -0,0 +1,52 @@ +package official_climb + +import ( + "math" + + "github.com/jftuga/geodist" + "github.com/martinlehoux/biking_home/ride" +) + +func MatchClimb(climb ride.Climb, officialClimbs []OfficialClimb, policy MatchPolicy) (OfficialClimb, bool) { + if policy.Validate() != nil { + return OfficialClimb{}, false + } + bestDistance := math.Inf(1) + var best OfficialClimb + found := false + for _, official := range officialClimbs { + startIndex, startDistance := nearestClimbPoint(climb, official.StartCoord) + endIndex, endDistance := nearestClimbPoint(climb, official.EndCoord) + if startDistance > policy.EndpointRadiusM || endDistance > policy.EndpointRadiusM { + continue + } + if startIndex >= endIndex { + continue + } + totalDistance := startDistance + endDistance + if totalDistance < bestDistance || (totalDistance == bestDistance && (!found || official.ID < best.ID)) { + best = official + bestDistance = totalDistance + found = true + } + } + return best, found +} + +func nearestClimbPoint(climb ride.Climb, target geodist.Coord) (int, float64) { + bestIndex := climb.StartIndex() + bestDistance := math.Inf(1) + for index := climb.StartIndex(); index <= climb.EndIndex(); index++ { + distance := distanceM(climb.PointCoord(index), target) + if distance < bestDistance { + bestIndex = index + bestDistance = distance + } + } + return bestIndex, bestDistance +} + +func distanceM(a, b geodist.Coord) float64 { + _, distanceKm := geodist.HaversineDistance(a, b) + return distanceKm * 1000 +} diff --git a/official_climb/model.go b/official_climb/model.go new file mode 100644 index 0000000..ae66f46 --- /dev/null +++ b/official_climb/model.go @@ -0,0 +1,50 @@ +package official_climb + +import ( + "fmt" + "math" + "strings" + "time" + + "github.com/jftuga/geodist" +) + +const DefaultMatchRadiusM = 100.0 + +type OfficialClimb struct { + ID int64 + Name string + StartCoord geodist.Coord + EndCoord geodist.Coord + CreatedAt time.Time + UpdatedAt time.Time +} + +type MatchPolicy struct { + EndpointRadiusM float64 +} + +func DefaultMatchPolicy() MatchPolicy { + return MatchPolicy{EndpointRadiusM: DefaultMatchRadiusM} +} + +func (policy MatchPolicy) Validate() error { + if math.IsNaN(policy.EndpointRadiusM) || math.IsInf(policy.EndpointRadiusM, 0) || policy.EndpointRadiusM <= 0 { + return fmt.Errorf("official climb endpoint radius must be greater than zero") + } + return nil +} + +func (climb OfficialClimb) validate() error { + if strings.TrimSpace(climb.Name) == "" { + return fmt.Errorf("official climb name is required") + } + if !validCoord(climb.StartCoord) || !validCoord(climb.EndCoord) { + return fmt.Errorf("official climb coordinates are invalid") + } + return nil +} + +func validCoord(coord geodist.Coord) bool { + return coord.Lat >= -90 && coord.Lat <= 90 && coord.Lon >= -180 && coord.Lon <= 180 +} diff --git a/official_climb/official_climb.go b/official_climb/repository.go index ab98633..1feb6a9 100644 --- a/official_climb/official_climb.go +++ b/official_climb/repository.go @@ -3,54 +3,11 @@ package official_climb import ( "database/sql" "fmt" - "math" - "strings" "time" "github.com/jftuga/geodist" - "github.com/martinlehoux/biking_home/ride" ) -const DefaultMatchRadiusM = 100.0 - -type OfficialClimb struct { - ID int64 - Name string - StartCoord geodist.Coord - EndCoord geodist.Coord - CreatedAt time.Time - UpdatedAt time.Time -} - -type MatchPolicy struct { - EndpointRadiusM float64 -} - -func DefaultMatchPolicy() MatchPolicy { - return MatchPolicy{EndpointRadiusM: DefaultMatchRadiusM} -} - -func (policy MatchPolicy) Validate() error { - if math.IsNaN(policy.EndpointRadiusM) || math.IsInf(policy.EndpointRadiusM, 0) || policy.EndpointRadiusM <= 0 { - return fmt.Errorf("official climb endpoint radius must be greater than zero") - } - return nil -} - -func (climb OfficialClimb) validate() error { - if strings.TrimSpace(climb.Name) == "" { - return fmt.Errorf("official climb name is required") - } - if !validCoord(climb.StartCoord) || !validCoord(climb.EndCoord) { - return fmt.Errorf("official climb coordinates are invalid") - } - return nil -} - -func validCoord(coord geodist.Coord) bool { - return coord.Lat >= -90 && coord.Lat <= 90 && coord.Lon >= -180 && coord.Lon <= 180 -} - const columns = "id, name, start_latitude, start_longitude, end_latitude, end_longitude, created_at, updated_at" func Create(db *sql.DB, climb OfficialClimb) (OfficialClimb, error) { @@ -138,47 +95,3 @@ func scan(s scanner) (OfficialClimb, error) { } return climb, nil } - -func MatchClimb(climb ride.Climb, officialClimbs []OfficialClimb, policy MatchPolicy) (OfficialClimb, bool) { - if policy.Validate() != nil { - return OfficialClimb{}, false - } - bestDistance := math.Inf(1) - var best OfficialClimb - found := false - for _, official := range officialClimbs { - startIndex, startDistance := nearestClimbPoint(climb, official.StartCoord) - endIndex, endDistance := nearestClimbPoint(climb, official.EndCoord) - if startDistance > policy.EndpointRadiusM || endDistance > policy.EndpointRadiusM { - continue - } - if startIndex >= endIndex { - continue - } - totalDistance := startDistance + endDistance - if totalDistance < bestDistance || (totalDistance == bestDistance && (!found || official.ID < best.ID)) { - best = official - bestDistance = totalDistance - found = true - } - } - return best, found -} - -func nearestClimbPoint(climb ride.Climb, target geodist.Coord) (int, float64) { - bestIndex := climb.StartIndex() - bestDistance := math.Inf(1) - for index := climb.StartIndex(); index <= climb.EndIndex(); index++ { - distance := distanceM(climb.PointCoord(index), target) - if distance < bestDistance { - bestIndex = index - bestDistance = distance - } - } - return bestIndex, bestDistance -} - -func distanceM(a, b geodist.Coord) float64 { - _, distanceKm := geodist.HaversineDistance(a, b) - return distanceKm * 1000 -} |