Skip to content

Commit a9db2cd

Browse files
author
Greg
committed
Demo: serve only the featured trip
The synthetic commute/errand world around the fictional centre is gone; the demo dataset is exactly the embedded road trip, so the map holds only the trip corridor and the KPIs describe the trip itself. README GIF re-recorded accordingly.
1 parent 9732366 commit a9db2cd

2 files changed

Lines changed: 5 additions & 128 deletions

File tree

docs/demo.gif

-3.78 MB
Loading

src/demo.go

Lines changed: 5 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@ import (
55
_ "embed"
66
"encoding/json"
77
"math"
8-
"math/rand"
98
"sort"
109
"strconv"
1110
"time"
1211
)
1312

14-
// demoStore generates deterministic synthetic data around a fictional area so
15-
// the app runs and renders with no database. No real locations are used.
13+
// demoStore serves the embedded featured road trip so the app runs and
14+
// renders with no database.
1615
type demoStore struct {
1716
drives []demoDrive
1817
charges []demoCharge
@@ -79,100 +78,11 @@ type demoTrip struct {
7978
} `json:"charges"`
8079
}
8180

82-
// Centre of the synthetic world (a neutral land point used only for demo
83-
// geometry, not a real address).
84-
const demoLat, demoLon = 50.06, 19.94
85-
86-
type demoPlace struct {
87-
name string
88-
lat, lng float64
89-
}
90-
81+
// The demo dataset is exactly the embedded featured trip — nothing else, so
82+
// the map holds only the trip's corridor.
9183
func newDemoStore(units string) *demoStore {
92-
rng := rand.New(rand.NewSource(42))
9384
s := &demoStore{units: units}
94-
now := time.Now()
95-
96-
home := demoPlace{"Home", demoLat, demoLon}
97-
work := demoPlace{"Work", demoLat + 0.05, demoLon + 0.08}
98-
gym := demoPlace{"Gym", demoLat + 0.02, demoLon - 0.06}
99-
lake := demoPlace{"Lakeside", demoLat - 0.4, demoLon + 0.55}
100-
hills := demoPlace{"Hillcrest", demoLat + 0.5, demoLon - 0.45}
101-
superNorth := demoPlace{"Supercharger Northgate", demoLat + 0.24, demoLon + 0.3}
102-
hotel := demoPlace{"Riverside Hotel", lake.lat + 0.01, lake.lng + 0.01}
103-
104-
nextDrive, nextCharge := 1000, 2000
105-
addDrive := func(day int, hour int, from, to demoPlace) {
106-
start := now.AddDate(0, 0, -day).Truncate(time.Hour).Add(time.Duration(hour) * time.Hour)
107-
coords := wander(from.lng, from.lat, to.lng, to.lat, 24, rng)
108-
km := pathKm(coords)
109-
durMin := int(km / (40 + rng.Float64()*30) * 60)
110-
if durMin < 8 {
111-
durMin = 8
112-
}
113-
speedMax := 0.0
114-
for _, c := range coords {
115-
if c[2] > speedMax {
116-
speedMax = c[2]
117-
}
118-
}
119-
s0 := 45 + rng.Intn(50)
120-
s1 := s0 - int(km*0.18)
121-
if s1 < 5 {
122-
s1 = 5
123-
}
124-
s.drives = append(s.drives, demoDrive{
125-
id: nextDrive, start: start, km: round1(km), durMin: durMin,
126-
speedMax: math.Round(speedMax), from: from.name, to: to.name,
127-
s0: s0, s1: s1, kwh: round1(km * 0.16), coords: coords,
128-
})
129-
nextDrive++
130-
}
131-
addCharge := func(day, hour int, p demoPlace, category string, kwh float64, durMin int) {
132-
start := now.AddDate(0, 0, -day).Truncate(time.Hour).Add(time.Duration(hour) * time.Hour)
133-
s0 := 18 + rng.Intn(40)
134-
s1 := s0 + int(kwh/75*100)
135-
if s1 > 100 {
136-
s1 = 100
137-
}
138-
peakKw := 11.0
139-
if category == "supercharger" {
140-
peakKw = math.Round(120 + rng.Float64()*70)
141-
}
142-
title := p.name
143-
if category == "home" {
144-
title = p.name
145-
} else if category == "destination" {
146-
title = p.name + " (destination)"
147-
}
148-
s.charges = append(s.charges, demoCharge{
149-
id: nextCharge, start: start, kwh: round1(kwh), durMin: durMin,
150-
s0: s0, s1: s1, peakKw: peakKw, category: category, title: title,
151-
pt: []float64{p.lng, p.lat},
152-
})
153-
nextCharge++
154-
}
155-
156-
// Commutes and errands over ~90 days, home charging every few days.
157-
spots := []demoPlace{work, gym, superNorth}
158-
for day := 2; day < 90; day += 2 {
159-
to := spots[rng.Intn(2)]
160-
addDrive(day, 8, home, to)
161-
addDrive(day, 17, to, home)
162-
if day%6 == 0 {
163-
addCharge(day, 20, home, "home", 12+rng.Float64()*38, 90+rng.Intn(300))
164-
}
165-
}
166-
// A few longer weekend trips with supercharger and destination stops.
167-
for _, day := range []int{7, 21, 49, 77} {
168-
addDrive(day, 9, home, superNorth)
169-
addCharge(day, 10, superNorth, "supercharger", 30+rng.Float64()*25, 18+rng.Intn(18))
170-
addDrive(day, 11, superNorth, lake)
171-
addCharge(day, 19, hotel, "destination", 35+rng.Float64()*15, 480)
172-
addDrive(day-1, 10, lake, hills)
173-
addDrive(day-1, 16, hills, home)
174-
}
175-
s.addFeaturedTrip(now)
85+
s.addFeaturedTrip(time.Now())
17686
return s
17787
}
17888

@@ -404,37 +314,4 @@ func (s *demoStore) Detail(ctx context.Context, id string) (*Detail, error) {
404314
return nil, nil
405315
}
406316

407-
// wander draws a jittered polyline from start to end with a plausible speed
408-
// profile: slow near the endpoints, faster mid-route.
409-
func wander(x0, y0, x1, y1 float64, n int, rng *rand.Rand) [][]float64 {
410-
out := make([][]float64, 0, n+1)
411-
base := 60 + rng.Float64()*60
412-
for i := 0; i <= n; i++ {
413-
t := float64(i) / float64(n)
414-
jx := (rng.Float64() - 0.5) * 0.01
415-
jy := (rng.Float64() - 0.5) * 0.01
416-
ramp := math.Min(1, math.Min(float64(i), float64(n-i))/4)
417-
speed := math.Round(25 + (base-25)*ramp*(0.75+0.25*math.Sin(float64(i)*0.7)))
418-
out = append(out, []float64{x0 + (x1-x0)*t + jx, y0 + (y1-y0)*t + jy, speed})
419-
}
420-
return out
421-
}
422-
423317
func round1(f float64) float64 { return math.Round(f*10) / 10 }
424-
425-
func pathKm(c [][]float64) float64 {
426-
var km float64
427-
for i := 1; i < len(c); i++ {
428-
km += haversine(c[i-1][1], c[i-1][0], c[i][1], c[i][0])
429-
}
430-
return km
431-
}
432-
433-
func haversine(lat1, lon1, lat2, lon2 float64) float64 {
434-
const R = 6371.0
435-
dLat := (lat2 - lat1) * math.Pi / 180
436-
dLon := (lon2 - lon1) * math.Pi / 180
437-
a := math.Sin(dLat/2)*math.Sin(dLat/2) +
438-
math.Cos(lat1*math.Pi/180)*math.Cos(lat2*math.Pi/180)*math.Sin(dLon/2)*math.Sin(dLon/2)
439-
return R * 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
440-
}

0 commit comments

Comments
 (0)