feat: Add nominatim

Signed-off-by: Tim Hårek Andreassen <tim@harek.no>
This commit is contained in:
Tim Hårek Andreassen 2024-09-25 22:09:12 +02:00
parent 012bd95cbb
commit dd25c4e6cb
No known key found for this signature in database
GPG key ID: E59C7734F0E10EB5
3 changed files with 182 additions and 0 deletions

View file

@ -0,0 +1,86 @@
package nominatim
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
)
type Nominatim struct {
siteName string
}
func New(siteName string) (*Nominatim, error) {
if siteName == "" {
return nil, fmt.Errorf("`siteName` must be defined.")
}
return &Nominatim{
siteName: siteName,
}, nil
}
func (n *Nominatim) Search(q string) (*SearchResults, error) {
url := fmt.Sprintf("https://nominatim.openstreetmap.org/search?q=%s&format=jsonv2", q)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("User-Agent", n.siteName)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%s", resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
result := SearchResults{}
err = json.Unmarshal(body, &result)
if err != nil {
return nil, err
}
return &result, nil
}
func (n *Nominatim) Lookup(q string) (*Coordinates, error) {
r, err := n.Search(q)
if err != nil {
return nil, err
}
if len(*r) == 0 {
return nil, fmt.Errorf("no result")
}
firstResult := (*r)[0]
lon, err := strconv.ParseFloat(*firstResult.Longitude, 64)
if err != nil {
return nil, err
}
lat, err := strconv.ParseFloat(*firstResult.Latitude, 64)
if err != nil {
return nil, err
}
return &Coordinates{
Latitude: lat,
Longitude: lon,
}, nil
}

View file

@ -0,0 +1,57 @@
package nominatim
import (
"strconv"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewClient(t *testing.T) {
assert := assert.New(t)
c, err := New("my siteName")
assert.NoError(err)
assert.Equal("my siteName", c.siteName)
_, err = New("")
assert.Error(err)
}
func TestSearch(t *testing.T) {
assert := assert.New(t)
c, err := New("my siteName")
assert.NoError(err)
r, err := c.Search("bergen")
assert.NoError(err)
assert.NotEmpty(r)
firstResult := (*r)[0]
lon, err := strconv.ParseFloat(*firstResult.Longitude, 64)
assert.NoError(err)
lat, err := strconv.ParseFloat(*firstResult.Latitude, 64)
assert.NoError(err)
assert.Equal(5.3259192, lon)
assert.Equal(60.3943055, lat)
assert.Nil(firstResult.Address)
}
func TestLookup(t *testing.T) {
assert := assert.New(t)
c, err := New("my siteName")
assert.NoError(err)
r, err := c.Lookup("bergen")
assert.NoError(err)
assert.NotEmpty(r)
assert.Equal(5.3259192, r.Longitude)
assert.Equal(60.3943055, r.Latitude)
_, err = c.Lookup("")
assert.Error(err)
}

View file

@ -0,0 +1,39 @@
package nominatim
type Address struct {
ISO3166 string `json:"ISO3166-2-lvl4"`
Borough string `json:"borough"`
City string `json:"city"`
Country string `json:"country"`
CountryCode string `json:"country_code"`
Neighbourhood string `json:"neighbourhood"`
Postcode string `json:"postcode"`
Road string `json:"road"`
Shop string `json:"shop"`
Suburb string `json:"suburb"`
}
type SearchResult struct {
Address *Address `json:"address"`
AddressType *string `json:"addresstype"`
BoundingBox *[]string `json:"boundingbox"`
Category *string `json:"category"`
DisplayName *string `json:"display_name"`
Importance *float64 `json:"importance"`
Latitude *string `json:"lat"`
Longitude *string `json:"lon"`
Licence *string `json:"licence"`
Name *string `json:"name"`
OsmId *int `json:"osm_id"`
OsmType *string `json:"osm_type"`
PlaceId *int `json:"place_id"`
PlaceRank *int `json:"place_rank"`
Type *string `json:"type"`
}
type SearchResults = []SearchResult
type Coordinates struct {
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
}