summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--official_climb/matcher.go52
-rw-r--r--official_climb/model.go50
-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
-}