@@ -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.
1615type 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.
9183func 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-
423317func 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