Add working ability to get stadia tiles directly

This commit is contained in:
Eli Ribble 2026-04-16 20:37:49 +00:00
parent 163b0f9edc
commit d03c12ffb6
No known key found for this signature in database
9 changed files with 149 additions and 11 deletions

View file

@ -37,7 +37,7 @@ func (s *StadiaMaps) BulkGeocode(requests []BulkGeocodeQuery) ([]BulkGeocodeResp
resp, err := s.client.R().
SetBody(body).
SetContentType("application/json").
SetPathParam("urlBase", s.urlBase).
SetPathParam("urlBase", s.urlBaseApi).
SetQueryParam("api_key", s.APIKey).
SetError(&api_error).
SetResult(&results).

View file

@ -0,0 +1,55 @@
package main
import (
"context"
"flag"
"log"
"os"
"github.com/Gleipnir-Technology/nidus-sync/stadia"
)
func main() {
// Define command-line flags
lat := flag.Float64("lat", 0, "The latitude of the tile")
lng := flag.Float64("lng", 0, "The longitude of the tile")
zoom := flag.Uint("zoom", 16, "The zoom level")
// Parse the flags
flag.Parse()
if *lat == 0 {
log.Println("Error: you must specify -lat")
flag.Usage()
os.Exit(1)
}
if *lng == 0 {
log.Println("Error: you must specify -lng")
flag.Usage()
os.Exit(1)
}
key := os.Getenv("STADIA_MAPS_API_KEY")
if key == "" {
log.Println("STADIA_MAPS_API_KEY is empty")
os.Exit(1)
}
client := stadia.NewStadiaMaps(key)
ctx := context.Background()
req := stadia.RequestTileRaster{
Latitude: *lat,
Longitude: *lng,
Zoom: *zoom,
}
data, err := client.TileRaster(ctx, req)
if err != nil {
log.Printf("err: %v\n", err)
os.Exit(2)
}
err = os.WriteFile("tile.raw", data, 0666)
if err != nil {
log.Printf("err: %v\n", err)
os.Exit(2)
}
log.Printf("wrote tile.raw")
}

View file

@ -58,7 +58,7 @@ func (s *StadiaMaps) GeocodeAutocomplete(ctx context.Context, req RequestGeocode
SetQueryParamsFromValues(query).
SetContext(ctx).
SetResult(&result).
SetPathParam("urlBase", s.urlBase).
SetPathParam("urlBase", s.urlBaseApi).
SetQueryParam("api_key", s.APIKey).
Get("https://{urlBase}/geocoding/v2/autocomplete")
if err != nil {

View file

@ -27,7 +27,7 @@ func (s *StadiaMaps) GeocodeByGID(ctx context.Context, req RequestGeocodeByGID)
SetQueryParamsFromValues(query).
SetContext(ctx).
SetResult(&result).
SetPathParam("urlBase", s.urlBase).
SetPathParam("urlBase", s.urlBaseApi).
SetQueryParam("api_key", s.APIKey).
Get("https://{urlBase}/geocoding/v2/place_details")
if err != nil {

View file

@ -58,7 +58,7 @@ func (s *StadiaMaps) GeocodeRaw(ctx context.Context, req RequestGeocodeRaw) (*Ge
SetQueryParamsFromValues(query).
SetContext(ctx).
SetResult(&result).
SetPathParam("urlBase", s.urlBase).
SetPathParam("urlBase", s.urlBaseApi).
SetQueryParam("api_key", s.APIKey).
Get("https://{urlBase}/geocoding/v1/search")
if err != nil {

View file

@ -71,7 +71,7 @@ func (s *StadiaMaps) GeocodeStructured(ctx context.Context, req RequestGeocodeSt
SetQueryParamsFromValues(query).
SetContext(ctx).
SetResult(&result).
SetPathParam("urlBase", s.urlBase).
SetPathParam("urlBase", s.urlBaseApi).
SetQueryParam("api_key", s.APIKey).
Get("https://{urlBase}/geocoding/v1/search/structured")
if err != nil {

81
stadia/map_tile_raster.go Normal file
View file

@ -0,0 +1,81 @@
package stadia
import (
"context"
"fmt"
"math"
"strconv"
"github.com/rs/zerolog/log"
)
type RequestTileRaster struct {
Latitude float64
Longitude float64
//Style string
Zoom uint
}
func (s *StadiaMaps) TileRaster(ctx context.Context, req RequestTileRaster) ([]byte, error) {
// https://docs.stadiamaps.com/raster/
//url := "https://{urlBase}/tiles/{style}/{z}/{x}/{y}{r}.png"
//url := "https://{urlBase}/data/imagery/{z}/{x}/{y}{r}.png"
url := "https://{urlBase}/tiles/alidade_satellite/{z}/{x}/{y}.jpg"
y, x := LatLngToTile(req.Zoom, req.Latitude, req.Longitude)
//var api_error Error
resp, err := s.client.R().
SetContext(ctx).
//SetPathParam("style", req.Style).
//SetPathParam("r", "").
SetPathParam("x", strconv.Itoa(int(x))).
SetPathParam("y", strconv.Itoa(int(y))).
SetPathParam("z", strconv.Itoa(int(req.Zoom))).
SetPathParam("urlBase", s.urlBaseTiles).
SetQueryParam("api_key", s.APIKey).
Get(url)
if err != nil {
return nil, fmt.Errorf("autocomplete get: %w", err)
}
if !resp.IsSuccess() {
return nil, parseError(resp)
}
content_type := resp.Header().Get("Content-Type")
log.Debug().Str("content_type", content_type).Send()
return resp.Bytes(), nil
}
// LatLngToTile converts GPS coordinates to ArcGIS tile coordinates
func LatLngToTile(level uint, lat, lng float64) (row, column uint) {
// Get number of tiles per dimension at this zoom level
numTiles := math.Pow(2, float64(level))
// Convert longitude to tile column
// Range: -180 to 180 degrees maps to 0 to numTiles
column = uint(math.Floor((lng + 180.0) / 360.0 * numTiles))
// Convert latitude to tile row using Mercator projection
// First convert lat to radians
latRad := lat * math.Pi / 180.0
// Apply Mercator projection formula
// This maps latitude from -85.0511 to 85.0511 degrees to 0 to numTiles
mercatorY := 0.5 - math.Log(math.Tan(latRad)+1/math.Cos(latRad))/(2*math.Pi)
row = uint(math.Floor(mercatorY * numTiles))
// Ensure values are within valid range
if column < 0 {
column = 0
} else if column >= uint(numTiles) {
column = uint(numTiles) - 1
}
if row < 0 {
row = 0
} else if row >= uint(numTiles) {
row = uint(numTiles) - 1
}
return row, column
}

View file

@ -35,7 +35,7 @@ func (s *StadiaMaps) ReverseGeocode(ctx context.Context, req RequestReverseGeoco
SetQueryParamsFromValues(query).
SetContext(ctx).
SetResult(&result).
SetPathParam("urlBase", s.urlBase).
SetPathParam("urlBase", s.urlBaseApi).
SetQueryParam("api_key", s.APIKey).
Get("https://{urlBase}/geocoding/v2/reverse")
if err != nil {

View file

@ -10,8 +10,9 @@ import (
type StadiaMaps struct {
APIKey string
client *resty.Client
urlBase string
client *resty.Client
urlBaseApi string
urlBaseTiles string
}
func NewStadiaMaps(api_key string) *StadiaMaps {
@ -26,9 +27,10 @@ func NewStadiaMaps(api_key string) *StadiaMaps {
})
}
return &StadiaMaps{
APIKey: api_key,
client: r,
urlBase: "api.stadiamaps.com",
APIKey: api_key,
client: r,
urlBaseApi: "api.stadiamaps.com",
urlBaseTiles: "tiles.stadiamaps.com",
}
}