go-meteologix/geolocation.go

98 lines
3 KiB
Go
Raw Permalink Normal View History

// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
2023-05-12 19:19:44 +02:00
//
// SPDX-License-Identifier: MIT
2023-05-12 12:44:27 +02:00
package meteologix
import (
"encoding/json"
"errors"
"fmt"
"net/url"
"sort"
"strconv"
)
// OSMNominatimURL is the API endpoint URL for the OpenStreetMaps Nominatim API
const OSMNominatimURL = "https://nominatim.openstreetmap.org/search"
// ErrCityNotFound is returned if a requested city was not found in the OSM API
var ErrCityNotFound = errors.New("requested city not found in OSM Nominatim API")
// GeoLocation represent the GPS GeoLocation coordinates of a City
type GeoLocation struct {
// Importance is the OSM computed importance rank
Importance float64 `json:"importance"`
// Latitude represents the GPS Latitude coordinates of the requested City as Float
Latitude float64
// LatitudeString represents the GPS Latitude coordinates of the requested City as string
LatitudeString string `json:"lat"`
// Longitude represents the GPS Longitude coordinates of the requested City as Float
Longitude float64
// LongitudeString represents the GPS Longitude coordinates of the requested City as String
LongitudeString string `json:"lon"`
// Name represents the requested City
Name string `json:"display_name"`
// PlaceID is the OSM Nominatim internal database ID
PlaceID int64 `json:"place_id"`
}
// GetGeoLocationByName returns the GeoLocation with the highest importance based on
2023-05-12 12:44:27 +02:00
// the given City name
//
// This method makes use of the OSM Nominatim API
func (c *Client) GetGeoLocationByName(ci string) (GeoLocation, error) {
ga, err := c.GetGeoLocationsByName(ci)
if err != nil || len(ga) < 1 {
return GeoLocation{}, err
}
return ga[0], nil
2023-05-12 12:44:27 +02:00
}
// GetGeoLocationsByName returns a slice of GeoLocation based on the requested City name
2023-05-12 12:44:27 +02:00
// The returned slice will be sorted by Importance of the results with the highest
// importance as first entry
//
// This method makes use of the OSM Nominatim API
func (c *Client) GetGeoLocationsByName(ci string) ([]GeoLocation, error) {
2023-05-12 12:44:27 +02:00
ga := make([]GeoLocation, 0)
u, err := url.Parse(OSMNominatimURL)
if err != nil {
return ga, fmt.Errorf("failed to parse OSM Nominatim URL: %w", err)
}
uq := u.Query()
uq.Add("format", "json")
uq.Add("q", ci)
u.RawQuery = uq.Encode()
r, err := c.httpClient.Get(u.String())
2023-05-12 12:44:27 +02:00
if err != nil {
return ga, fmt.Errorf("OSM Nominatim API request failed: %w", err)
}
var la []GeoLocation
if err := json.Unmarshal(r, &la); err != nil {
return ga, fmt.Errorf("failed to unmarshal API response JSON: %w", err)
}
if len(la) < 1 {
return ga, ErrCityNotFound
}
for _, l := range la {
lat, err := strconv.ParseFloat(l.LatitudeString, 64)
if err != nil {
return ga, fmt.Errorf("failed to convert latitude string to float value: %w", err)
}
lon, err := strconv.ParseFloat(l.LongitudeString, 64)
if err != nil {
return ga, fmt.Errorf("failed to convert longitude string to float value: %w", err)
}
l.Latitude = lat
l.Longitude = lon
ga = append(ga, l)
}
sort.SliceStable(ga, func(i, j int) bool { return ga[i].Importance > ga[j].Importance })
return ga, nil
}