geocode.go 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. package handlers
  2. import (
  3. "encoding/json"
  4. "io"
  5. "net/http"
  6. "net/url"
  7. "time"
  8. )
  9. const nominatimURL = "https://nominatim.openstreetmap.org/reverse"
  10. var geocodeClient = &http.Client{Timeout: 5 * time.Second}
  11. type nominatimResponse struct {
  12. DisplayName string `json:"display_name"`
  13. }
  14. func GeocodeReverse(w http.ResponseWriter, r *http.Request) {
  15. lat := r.URL.Query().Get("lat")
  16. lon := r.URL.Query().Get("lon")
  17. if lat == "" || lon == "" {
  18. writeError(w, http.StatusBadRequest, "lat and lon are required", nil)
  19. return
  20. }
  21. u, _ := url.Parse(nominatimURL)
  22. q := u.Query()
  23. q.Set("lat", lat)
  24. q.Set("lon", lon)
  25. q.Set("format", "json")
  26. q.Set("accept-language", "ru")
  27. u.RawQuery = q.Encode()
  28. req, _ := http.NewRequestWithContext(r.Context(), http.MethodGet, u.String(), nil)
  29. req.Header.Set("User-Agent", "Photoplaces/1.0")
  30. resp, err := geocodeClient.Do(req)
  31. if err != nil {
  32. writeError(w, http.StatusBadGateway, "geocoding service unavailable", err)
  33. return
  34. }
  35. defer resp.Body.Close()
  36. if resp.StatusCode != http.StatusOK {
  37. body, _ := io.ReadAll(resp.Body)
  38. writeJSON(w, http.StatusOK, map[string]interface{}{
  39. "display_name": nil,
  40. "raw_error": string(body),
  41. })
  42. return
  43. }
  44. var data nominatimResponse
  45. if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
  46. writeError(w, http.StatusInternalServerError, "failed to parse geocoding response", err)
  47. return
  48. }
  49. writeJSON(w, http.StatusOK, map[string]interface{}{
  50. "display_name": data.DisplayName,
  51. })
  52. }