NAM-APJATEL-BACKEND/utils/service/geo_service.go

102 lines
2.5 KiB
Go

package service
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)
type GeocodingService interface {
GetAddressFromCoordinates(latitude, longitude float64) (string, error)
}
type nominatimGeocoder struct {
client *http.Client
}
func NewGeocodingService() GeocodingService {
client := &http.Client{
Timeout: 10 * time.Second,
}
return &nominatimGeocoder{
client: client,
}
}
type NominatimResponse struct {
DisplayName string `json:"display_name"`
Error string `json:"error"`
}
func (g *nominatimGeocoder) GetAddressFromCoordinates(latitude, longitude float64) (string, error) {
if latitude < -90 || latitude > 90 || longitude < -180 || longitude > 180 {
errMsg := fmt.Sprintf("Invalid coordinates: lat=%f, lon=%f", latitude, longitude)
return "", fmt.Errorf(errMsg)
}
url := fmt.Sprintf(
"https://nominatim.openstreetmap.org/reverse?format=json&lat=%f&lon=%f&zoom=18&addressdetails=1",
latitude, longitude,
)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Printf("Request creation error: %v", err)
return "", err
}
req.Header.Set("User-Agent", "AssetManagementApp")
resp, err := g.client.Do(req)
if err != nil {
log.Printf("HTTP request error: %v", err)
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Printf("Nominatim returned HTTP %d for lat=%f lon=%f", resp.StatusCode, latitude, longitude)
return "", fmt.Errorf("geocoding service returned status %d", resp.StatusCode)
}
// Read full response body
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("Error reading response body: %v", err)
return "", err
}
if len(bodyBytes) > 0 && bodyBytes[0] == '<' {
log.Printf("Nominatim returned HTML instead of JSON (possible rate limit) for lat=%f lon=%f", latitude, longitude)
return "", fmt.Errorf("geocoding service unavailable (rate limited or network error)")
}
var result NominatimResponse
if err := json.Unmarshal(bodyBytes, &result); err != nil {
log.Printf("JSON unmarshal error: %v", err)
return "", err
}
// Detailed error checking
if result.Error != "" {
log.Printf("Nominatim API Error: %s", result.Error)
return "", fmt.Errorf("geocoding error: %s", result.Error)
}
// Check for empty display name
if result.DisplayName == "" {
log.Printf("No address found for coordinates: %f, %f", latitude, longitude)
return "", fmt.Errorf("no address found for these coordinates")
}
return result.DisplayName, nil
}