Return error when fetching GeoLocations fails or the result is empty. This ensures that an error is properly returned and handled when either an error occurs during the API call or no locations are found for the given city name.
98 lines
3 KiB
Go
98 lines
3 KiB
Go
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
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
|
|
// 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
|
|
}
|
|
|
|
// GetGeoLocationsByName returns a slice of GeoLocation based on the requested City name
|
|
// 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) {
|
|
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())
|
|
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
|
|
}
|