Skip to content

Commit

Permalink
Merge pull request #168 from jovandeginste/geo-switch-lib
Browse files Browse the repository at this point in the history
Use our own reverse geocoding library
  • Loading branch information
jovandeginste committed Jun 7, 2024
2 parents 3a9f489 + 912c96d commit d9b5e13
Show file tree
Hide file tree
Showing 10 changed files with 565 additions and 159 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/glebarez/sqlite v1.11.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2
github.com/google/go-querystring v1.1.0
github.com/jayco/go-emoji-flag v0.0.0-20190810054606-01604da018da
github.com/labstack/echo-jwt/v4 v4.2.0
github.com/labstack/echo/v4 v4.12.0
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,13 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2V
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 h1:yEt5djSYb4iNtmV9iJGVday+i4e9u6Mrn5iP64HH5QM=
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
Expand Down
14 changes: 14 additions & 0 deletions pkg/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package app

import (
"errors"
"fmt"
"io/fs"
"log/slog"
"os"
Expand All @@ -11,6 +12,7 @@ import (
"github.com/cat-dealer/go-rand/v2"
"github.com/fsouza/slognil"
"github.com/jovandeginste/workout-tracker/pkg/database"
"github.com/jovandeginste/workout-tracker/pkg/geocoder"
"github.com/labstack/echo/v4"
"github.com/lmittmann/tint"
"github.com/mattn/go-isatty"
Expand All @@ -28,6 +30,10 @@ type Version struct {
Sha string
}

func (v Version) UserAgent() string {
return fmt.Sprintf("workout-tracker/%s", v.Ref)
}

type App struct {
Version Version
Config database.Config
Expand Down Expand Up @@ -76,6 +82,9 @@ func (a *App) Configure() error {
if err := a.ConfigureDatabase(); err != nil {
return err
}
if err := a.ConfigureGeocoder(); err != nil {
return err
}

if err := a.Config.UpdateFromDatabase(a.db); err != nil {
return err
Expand All @@ -88,6 +97,11 @@ func (a *App) Configure() error {
return nil
}

func (a *App) ConfigureGeocoder() error {
geocoder.SetClient(a.logger, a.Version.UserAgent())
return nil
}

func (a *App) ConfigureDatabase() error {
a.logger.Info("Connecting to the database '" + a.Config.DatabaseDriver + "': " + a.Config.DSN)

Expand Down
33 changes: 11 additions & 22 deletions pkg/database/workouts_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"slices"
"time"

geo "github.com/codingsince1985/geo-golang"
"github.com/codingsince1985/geo-golang/openstreetmap"
"github.com/codingsince1985/geo-golang"
"github.com/jovandeginste/workout-tracker/pkg/geocoder"
"github.com/jovandeginste/workout-tracker/pkg/templatehelpers"
"github.com/labstack/gommon/log"
"github.com/tkrajina/gpxgo/gpx"
Expand Down Expand Up @@ -172,28 +172,17 @@ func (m *MapCenter) Address() *geo.Address {
return nil
}

geocoder := openstreetmap.Geocoder()

log.Infof("OpenStreetMap reverse geocoding {%f, %f}", m.Lat, m.Lng)

for i := range 5 {
address, err := geocoder.ReverseGeocode(m.Lat, m.Lng)
switch err {
case nil:
log.Infof("OpenStreetMap reverse geocoding {%f, %f} result: %s", m.Lat, m.Lng, address.FormattedAddress)
return address
case geo.ErrTimeout:
log.Warnf("OpenStreetMap geocoding timeout, retrying in %d seconds", i)
time.Sleep(time.Duration(i) * time.Second)
default:
log.Errorf("OpenStreetMap geocoding error: %v", err)
return nil
}
r, err := geocoder.Lookup(geocoder.Query{
Lat: m.Lat,
Lon: m.Lng,
Format: "json",
})
if err != nil {
log.Warn("Error performing reverse geocode: ", err)
return nil
}

log.Warnf("Too many OpenStreetMap geocoding timeouts, returning nil")

return nil
return r
}

// allGPXPoints returns the first track segment's points
Expand Down
149 changes: 149 additions & 0 deletions pkg/geocoder/reverse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Inspired by https://github.com/codingsince1985/geo-golang
package geocoder

import (
"cmp"
"encoding/json"
"errors"
"log/slog"
"net/http"
"strings"
"time"

"github.com/codingsince1985/geo-golang"
"github.com/google/go-querystring/query"
)

var (
c *client
ErrClientNotSet = errors.New("geocoder: client not set")
)

const requestInterval = time.Second

type client struct {
url string
client http.Client
logger *slog.Logger
lastRequest time.Time
userAgent string
}

type Query struct {
Lat float64 `url:"lat"`
Lon float64 `url:"lon"`
Format string `url:"format"`
}

type result struct {
PlaceID int `json:"place_id"`
Licence string `json:"licence"`
OsmType string `json:"osm_type"`
OsmID int `json:"osm_id"`
Lat string `json:"lat"`
Lon string `json:"lon"`
PlaceRank int `json:"place_rank"`
Category string `json:"category"`
Type string `json:"type"`
Importance float64 `json:"importance"`
Addresstype string `json:"addresstype"`
DisplayName string `json:"display_name"`
Name string `json:"name"`
Address *address `json:"address"`
Boundingbox []string `json:"boundingbox"`
}

type address struct {
HouseNumber string `json:"house_number"`
Road string `json:"road"`
Pedestrian string `json:"pedestrian"`
Footway string `json:"footway"`
Cycleway string `json:"cycleway"`
Highway string `json:"highway"`
Path string `json:"path"`
Suburb string `json:"suburb"`
City string `json:"city"`
Town string `json:"town"`
Village string `json:"village"`
Hamlet string `json:"hamlet"`
County string `json:"county"`
Country string `json:"country"`
CountryCode string `json:"country_code"`
State string `json:"state"`
StateDistrict string `json:"state_district"`
Postcode string `json:"postcode"`
}

func SetClient(l *slog.Logger, ua string) {
c = &client{
url: "https://nominatim.openstreetmap.org/reverse",
userAgent: ua,
client: http.Client{},
logger: l,
}
}

func Lookup(q Query) (*geo.Address, error) {
if c == nil {
return nil, ErrClientNotSet
}

v, err := query.Values(q)
if err != nil {
return nil, err
}

if !c.lastRequest.IsZero() && time.Since(c.lastRequest) < requestInterval {
c.logger.Warn("Rate limited - waiting " + requestInterval.String())
time.Sleep(requestInterval)
}

req, err := http.NewRequest(http.MethodGet, c.url+"?"+v.Encode(), nil)
if err != nil {
return nil, err
}

req.Header.Set("User-Agent", c.userAgent)

res, err := c.client.Do(req)
if err != nil {
return nil, err
}

defer res.Body.Close()

c.lastRequest = time.Now()
r := result{}

if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
return nil, err
}

return r.ToAddress(), nil
}

func (r result) ToAddress() *geo.Address {
return &geo.Address{
FormattedAddress: r.DisplayName,
HouseNumber: r.Address.HouseNumber,
Street: r.Address.Street(),
Postcode: r.Address.Postcode,
City: r.Address.Locality(),
Suburb: r.Address.Suburb,
State: r.Address.State,
Country: r.Address.Country,
CountryCode: strings.ToUpper(r.Address.CountryCode),
}
}

func (a address) Locality() string {
return cmp.Or(
a.City, a.Town, a.Village, a.Hamlet,
)
}

func (a address) Street() string {
return cmp.Or(
a.Road, a.Pedestrian, a.Path, a.Cycleway, a.Footway, a.Highway,
)
}

This file was deleted.

Loading

0 comments on commit d9b5e13

Please sign in to comment.