mirror of
https://github.com/acaloiaro/roam-location
synced 2026-07-21 02:09:50 +00:00
feat: log GPS fixes every poll, geocode only on significant movement
Remove the in-memory cache from geocode/nominatim.go (cacheTTL, cacheMu, cachedLoc, cacheExpiry) so CityCenter always makes a live request when called. Call frequency is now controlled by the caller rather than a time-based TTL. In the poll goroutine, log the raw GPS coordinates on every cycle. Track the last geocoded position and only call CityCenter on the first fix or when the device has moved more than 5 miles (haversine). WebDAV snapshots are written every poll cycle using the cached city centroid with fresh weather data.
This commit is contained in:
parent
fe5fb08fb9
commit
1c8ab19426
2 changed files with 38 additions and 39 deletions
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -29,27 +28,12 @@ type nominatimResponse struct {
|
|||
} `json:"address"`
|
||||
}
|
||||
|
||||
const cacheTTL = 24 * time.Hour
|
||||
|
||||
var (
|
||||
httpClient = &http.Client{Timeout: 10 * time.Second}
|
||||
cacheMu sync.Mutex
|
||||
cachedLoc Location
|
||||
cacheExpiry time.Time
|
||||
)
|
||||
var httpClient = &http.Client{Timeout: 10 * time.Second}
|
||||
|
||||
// CityCenter reverse-geocodes lat/lon to the centroid of the matched city.
|
||||
// Results are cached in memory for 24 hours to avoid hammering Nominatim.
|
||||
// zoom=10 asks Nominatim for city-level granularity; the returned coordinates
|
||||
// are the centroid of that administrative boundary, not the input coordinates.
|
||||
func CityCenter(ctx context.Context, lat, lon float64) (Location, error) {
|
||||
cacheMu.Lock()
|
||||
defer cacheMu.Unlock()
|
||||
|
||||
if time.Now().Before(cacheExpiry) {
|
||||
return cachedLoc, nil
|
||||
}
|
||||
|
||||
url := fmt.Sprintf(
|
||||
"https://nominatim.openstreetmap.org/reverse?lat=%f&lon=%f&format=json&zoom=10",
|
||||
lat, lon,
|
||||
|
|
@ -100,7 +84,5 @@ func CityCenter(ctx context.Context, lat, lon float64) (Location, error) {
|
|||
name += a.State
|
||||
}
|
||||
|
||||
cachedLoc = Location{Lat: resLat, Lon: resLon, Name: name}
|
||||
cacheExpiry = time.Now().Add(cacheTTL)
|
||||
return cachedLoc, nil
|
||||
return Location{Lat: resLat, Lon: resLon, Name: name}, nil
|
||||
}
|
||||
|
|
|
|||
55
main.go
55
main.go
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
|
@ -48,6 +49,15 @@ func (c *cache) get() (lat, lon float64, name string, ok bool) {
|
|||
return c.lat, c.lon, c.name, c.ready
|
||||
}
|
||||
|
||||
func milesApart(lat1, lon1, lat2, lon2 float64) float64 {
|
||||
const r = 3958.8
|
||||
lat1r, lat2r := lat1*math.Pi/180, lat2*math.Pi/180
|
||||
dlat := (lat2 - lat1) * math.Pi / 180
|
||||
dlon := (lon2 - lon1) * math.Pi / 180
|
||||
a := math.Sin(dlat/2)*math.Sin(dlat/2) + math.Cos(lat1r)*math.Cos(lat2r)*math.Sin(dlon/2)*math.Sin(dlon/2)
|
||||
return r * 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
||||
}
|
||||
|
||||
func pollInterval() time.Duration {
|
||||
if s := os.Getenv("POLL_INTERVAL_SECONDS"); s != "" {
|
||||
if n, err := strconv.Atoi(s); err == nil && n > 0 {
|
||||
|
|
@ -64,30 +74,37 @@ func main() {
|
|||
wdClient := webdav.NewClientFromEnv()
|
||||
|
||||
go func() {
|
||||
const geocodeTriggerMiles = 5.0
|
||||
var geocodedLat, geocodedLon float64
|
||||
var currentCity *geocode.Location
|
||||
for {
|
||||
lat, lon, err := gpsd.GetLocation(context.Background())
|
||||
if err != nil {
|
||||
log.Printf("location poll error: %v", err)
|
||||
} else {
|
||||
city, err := geocode.CityCenter(context.Background(), lat, lon)
|
||||
if err != nil {
|
||||
log.Printf("geocode error: %v", err)
|
||||
} else {
|
||||
loc.set(city.Lat, city.Lon, city.Name)
|
||||
log.Printf("location updated: %.6f, %.6f (%s)", city.Lat, city.Lon, city.Name)
|
||||
|
||||
if wdClient != nil {
|
||||
snap := snapshot{
|
||||
RecordedAt: time.Now().UTC(),
|
||||
Location: &locationEntry{Lat: city.Lat, Lon: city.Lon, Name: city.Name},
|
||||
}
|
||||
if data, ok := wCache.Get(); ok {
|
||||
snap.Weather = &data
|
||||
snap.Conditions = weather.Infer(data, city.Lat, city.Lon)
|
||||
}
|
||||
if err := wdClient.Append(snap); err != nil {
|
||||
log.Printf("webdav append error: %v", err)
|
||||
}
|
||||
log.Printf("gps location: %.6f, %.6f", lat, lon)
|
||||
if currentCity == nil || milesApart(lat, lon, geocodedLat, geocodedLon) > geocodeTriggerMiles {
|
||||
city, err := geocode.CityCenter(context.Background(), lat, lon)
|
||||
if err != nil {
|
||||
log.Printf("geocode error: %v", err)
|
||||
} else {
|
||||
geocodedLat, geocodedLon = lat, lon
|
||||
currentCity = &city
|
||||
loc.set(city.Lat, city.Lon, city.Name)
|
||||
log.Printf("location updated: %.6f, %.6f (%s)", city.Lat, city.Lon, city.Name)
|
||||
}
|
||||
}
|
||||
if currentCity != nil && wdClient != nil {
|
||||
snap := snapshot{
|
||||
RecordedAt: time.Now().UTC(),
|
||||
Location: &locationEntry{Lat: currentCity.Lat, Lon: currentCity.Lon, Name: currentCity.Name},
|
||||
}
|
||||
if data, ok := wCache.Get(); ok {
|
||||
snap.Weather = &data
|
||||
snap.Conditions = weather.Infer(data, currentCity.Lat, currentCity.Lon)
|
||||
}
|
||||
if err := wdClient.Append(snap); err != nil {
|
||||
log.Printf("webdav append error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue