geocode.go 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. package handlers
  2. import (
  3. "encoding/json"
  4. "io"
  5. "net/http"
  6. "net/url"
  7. "strings"
  8. "time"
  9. )
  10. const nominatimURL = "https://nominatim.openstreetmap.org/reverse"
  11. var geocodeClient = &http.Client{Timeout: 5 * time.Second}
  12. type nominatimResponse struct {
  13. DisplayName string `json:"display_name"`
  14. }
  15. func GeocodeReverse(w http.ResponseWriter, r *http.Request) {
  16. lat := r.URL.Query().Get("lat")
  17. lon := r.URL.Query().Get("lon")
  18. if lat == "" || lon == "" {
  19. writeError(w, http.StatusBadRequest, "lat and lon are required", nil)
  20. return
  21. }
  22. u, _ := url.Parse(nominatimURL)
  23. q := u.Query()
  24. q.Set("lat", lat)
  25. q.Set("lon", lon)
  26. q.Set("format", "json")
  27. q.Set("accept-language", "ru")
  28. u.RawQuery = q.Encode()
  29. req, _ := http.NewRequestWithContext(r.Context(), http.MethodGet, u.String(), nil)
  30. req.Header.Set("User-Agent", "Photoplaces/1.0")
  31. resp, err := geocodeClient.Do(req)
  32. if err != nil {
  33. writeError(w, http.StatusBadGateway, "geocoding service unavailable", err)
  34. return
  35. }
  36. defer resp.Body.Close()
  37. if resp.StatusCode != http.StatusOK {
  38. // Тело ответа вышестоящего сервиса наружу не отдаём, только логируем.
  39. io.Copy(io.Discard, resp.Body)
  40. writeJSON(w, http.StatusOK, map[string]interface{}{
  41. "display_name": nil,
  42. })
  43. return
  44. }
  45. var data nominatimResponse
  46. if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
  47. writeError(w, http.StatusInternalServerError, "failed to parse geocoding response", err)
  48. return
  49. }
  50. writeJSON(w, http.StatusOK, map[string]interface{}{
  51. "display_name": reverseAddress(data.DisplayName),
  52. })
  53. }
  54. func reverseAddress(addr string) string {
  55. if addr == "" {
  56. return ""
  57. }
  58. parts := strings.Split(addr, ", ")
  59. for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
  60. parts[i], parts[j] = parts[j], parts[i]
  61. }
  62. return strings.Join(parts, ", ")
  63. }