A go library for interacting with Stadia Maps, a location data platform.
  • Go 99.6%
  • Nix 0.4%
Find a file
2026-06-19 20:30:49 +00:00
cmd/stadia Default to info-level logging, allow for verbose via -v 2026-06-19 20:24:32 +00:00
doc Phase 4 implementation 2026-06-19 20:07:45 +00:00
.gitignore Embrace zerolog for logging, resolve roadmap 4.1 and 4.2 2026-06-19 18:43:28 +00:00
AGENTS.md Add initial agents documentation 2026-06-19 18:47:23 +00:00
bulk.go Phase 4 implementation 2026-06-19 20:07:45 +00:00
download_area.go Auto-swap inputs if the user accidentially reversed them. 2026-06-19 20:13:04 +00:00
elevation.go Phase 4 implementation 2026-06-19 20:07:45 +00:00
error.go Add initial logic to the library from nidus-sync 2026-06-19 17:13:27 +00:00
example_test.go Phase 4 implementation 2026-06-19 20:07:45 +00:00
export_test.go Add beginnings of testing 2026-06-19 18:23:09 +00:00
flake.lock Add initial logic to the library from nidus-sync 2026-06-19 17:13:27 +00:00
flake.nix initial readme and flake file. 2026-06-19 15:41:29 +00:00
geocode_autocomplete.go Phase 4 implementation 2026-06-19 20:07:45 +00:00
geocode_bygid.go Phase 4 implementation 2026-06-19 20:07:45 +00:00
geocode_raw.go Phase 4 implementation 2026-06-19 20:07:45 +00:00
geocode_structured.go Embrace zerolog for logging, resolve roadmap 4.1 and 4.2 2026-06-19 18:43:28 +00:00
go.mod Add initial logic to the library from nidus-sync 2026-06-19 17:13:27 +00:00
go.sum Add initial logic to the library from nidus-sync 2026-06-19 17:13:27 +00:00
logger.go Add initial logic to the library from nidus-sync 2026-06-19 17:13:27 +00:00
map_tile_raster.go Phase 2 of implementation 2026-06-19 19:53:33 +00:00
raster_types.go Phase 2 of implementation 2026-06-19 19:53:33 +00:00
README.md Add documentation on the new verbose flag 2026-06-19 20:30:49 +00:00
request.go Phase 4 implementation 2026-06-19 20:07:45 +00:00
request_type.go Add initial logic to the library from nidus-sync 2026-06-19 17:13:27 +00:00
response_type.go Phase 4 implementation 2026-06-19 20:07:45 +00:00
reverse_geocode.go Phase 4 implementation 2026-06-19 20:07:45 +00:00
stadia.go Default to info-level logging, allow for verbose via -v 2026-06-19 20:24:32 +00:00
stadia_test.go Phase 3 implementation 2026-06-19 19:59:44 +00:00
static_map.go Phase 2 of implementation 2026-06-19 19:53:33 +00:00
tile_cache.go Phase 2 of implementation 2026-06-19 19:53:33 +00:00
tile_math.go Auto-swap inputs if the user accidentially reversed them. 2026-06-19 20:13:04 +00:00
tile_math_test.go Auto-swap inputs if the user accidentially reversed them. 2026-06-19 20:13:04 +00:00
vector_tile.go Phase 4 implementation 2026-06-19 20:07:45 +00:00

go-stadia

A Go client library for Stadia Maps, a SaaS platform providing geocoding, reverse geocoding, autocomplete, place details, bulk geocoding, and raster map tile services.

Installation

go get source.gleipnir.technology/Gleipnir/go-stadia

Quick Start

package main

import (
    "context"
    "fmt"
    "os"

    "source.gleipnir.technology/Gleipnir/go-stadia"
)

func main() {
    client := stadia.NewStadiaMaps(os.Getenv("STADIA_MAPS_API_KEY"))
    defer client.Close()

    ctx := context.Background()
    resp, err := client.GeocodeRaw(ctx, stadia.RequestGeocodeRaw{
        Text: "1600 Pennsylvania Ave NW, Washington DC",
    })
    if err != nil {
        panic(err)
    }
    for _, feature := range resp.Features {
        fmt.Println(feature.Properties.Label)
    }
}

API Reference

Client

client := stadia.NewStadiaMaps(apiKey)
defer client.Close()

The client is built on resty.dev/v3. Use AddResponseMiddleware to attach custom response middleware (e.g., for logging or caching).

The environment variable STADIA_INSECURE_SKIP_VERIFY (any non-empty value) disables TLS certificate verification — only use in development or proxy environments.

Geocoding

All geocoding methods accept a context.Context and return *GeocodeResponse.

Raw Search — GeocodeRaw

Freeform text search. Calls GET /geocoding/v1/search.

resp, err := client.GeocodeRaw(ctx, stadia.RequestGeocodeRaw{
    Text: "Berlin Hauptbahnhof",
    Size: intPtr(5),
})

Supports boundary rectangle, boundary circle, focus point, language, layers, and sources parameters.

Structured Search — GeocodeStructured

Search by discrete address components. Calls GET /geocoding/v1/search/structured.

resp, err := client.GeocodeStructured(ctx, stadia.RequestGeocodeStructured{
    Address:    strPtr("Põhja pst 27a"),
    Locality:   strPtr("Tallinn"),
    Country:    strPtr("EE"),
})

Fields: Address, Neighbourhood, Borough, Locality, County, Region, PostalCode, Country, plus boundary, focus, layer, and source options.

Autocomplete — GeocodeAutocomplete

Type-ahead / incremental search. Calls GET /geocoding/v2/autocomplete.

resp, err := client.GeocodeAutocomplete(ctx, stadia.RequestGeocodeAutocomplete{
    Text: "1600 Penn",
})

Supports the same boundary, focus, layer, and source options as raw search, plus BoundaryCountry and BoundaryGID.

Reverse Geocode — ReverseGeocode

Find addresses near a point. Calls GET /geocoding/v2/reverse.

resp, err := client.ReverseGeocode(ctx, stadia.RequestReverseGeocode{
    Latitude:  40.7128,
    Longitude: -74.0060,
})

Supports boundary circle radius, boundary country, boundary GID, layers, sources, and result count.

Place Details — GeocodeByGID

Look up one or more features by their Stadia Maps GID. Calls GET /geocoding/v2/place_details.

resp, err := client.GeocodeByGID(ctx, stadia.RequestGeocodeByGID{
    GIDs: []string{"openaddresses:address:us/ca/tulare-addresses-county:fe9dfab3d45c4550"},
})

Bulk Geocoding — BulkGeocode

Send multiple queries in a single HTTP request. Calls POST /geocoding/v1/search/bulk.

requests := []stadia.BulkGeocodeQuery{
    stadia.RequestGeocodeStructured{
        Address:    strPtr("12932 Ave 404"),
        PostalCode: strPtr("93615"),
    },
    stadia.RequestGeocodeRaw{
        Text: "Empire State Building",
    },
}
results, err := client.BulkGeocode(requests)

Only RequestGeocodeStructured and RequestGeocodeRaw implement BulkGeocodeQuery (they have an endpoint() method).

Map Tiles

Fetch raster map tiles with configurable style, format, and resolution. TileRasterRequest returns a *Tile with geographic bounds and image data.

// Primary API — full control over style, format, and retina scale.
tile, err := client.TileRasterRequest(ctx, stadia.RequestTileRaster{
    Z:      16,
    X:      11241,
    Y:      26142,
    Style:  stadia.StyleSatellite,
    Format: stadia.FormatJPEG,
})
// tile.Bounds — geographic extent (North, South, East, West)
// tile.Data  — raw image bytes
// tile.ContentType — e.g. "image/jpeg"

Available raster styles:

Constant Style ID Description
StyleSatellite alidade_satellite Satellite imagery (JPEG only)
StyleSmooth alidade_smooth Clean general-purpose map
StyleSmoothDark alidade_smooth_dark Dark theme
StyleOutdoors outdoors Topographic / outdoors
StyleOSMBright osm_bright OpenStreetMap bright
StyleStamenToner stamen_toner Stamen high-contrast B&W
StyleStamenTonerLite stamen_toner_lite Stamen toner (lite)
StyleStamenTerrain stamen_terrain Stamen terrain shading
StyleStamenWatercolor stamen_watercolor Stamen watercolor art

Available formats: FormatPNG, FormatJPEG, FormatWebP.

Retina tiles (@2x / @3x) are requested via the Retina field:

tile, _ := client.TileRasterRequest(ctx, stadia.RequestTileRaster{
    Z: 16, X: 11241, Y: 26142,
    Style:  stadia.StyleOutdoors,
    Format: stadia.FormatPNG,
    Retina: stadia.Scale2x, // 512×512 px tile
})

Use EnsureSize: true to guarantee 256×256 output — retina tiles are automatically resampled down.

Deprecated: TileRaster(ctx, z, y, x uint) and TileRasterLatLng still work but delegate to the new API with StyleSatellite / FormatJPEG defaults. Prefer TileRasterRequest.

Maximum zoom discovery

MaxAvailableZoom probes for the highest zoom level with imagery at a point. MaxAvailableZoomArea checks multiple sample points and returns the minimum (slightly pessimistic, avoids missing-tile gaps). Results are memoized.

zoom, _ := client.MaxAvailableZoom(ctx, stadia.StyleSatellite, 40.7128, -74.0060)

zoom, _ = client.MaxAvailableZoomArea(ctx, stadia.StyleSatellite,
    36.35021, 36.30616, -119.26937, -119.35014,
)

Coordinate utilities

Function Returns Description
LatLngToTile(z, lat, lng) (row, column uint) GPS → tile coordinates
TileToLatLng(z, x, y) (lat, lng float64) Tile center → GPS
Bounds(z, x, y) TileBounds Geographic extent of a tile
BBoxToTileRange(n, s, e, w, z) (xMin, xMax, yMin, yMax) Bounding box → tile grid range
TileCount(n, s, e, w, z) int Estimated tile count for a bbox

Requests go to tiles.stadiamaps.com rather than the API host.

Area download

DownloadArea streams all tiles covering a bounding box with tunable concurrency, retry with exponential backoff, and optional caching. Use z = 0 to auto-discover the maximum available zoom.

tileCh, errCh := client.DownloadArea(ctx, 36.35021, 36.30616, -119.26937, -119.35014, 0,
    stadia.DownloadAreaConfig{
        Style:       stadia.StyleSatellite,
        Format:      stadia.FormatJPEG,
        Concurrency: 8,
        MaxRetries:  3,
        Cache:       stadia.NewFSStore("./tile_cache"),
    },
)
for tile := range tileCh {
    fmt.Printf("tile %d/%d/%d: %d bytes\n", tile.Bounds.Z, tile.Bounds.X, tile.Bounds.Y, len(tile.Data))
}

Retry behavior: 5xx and 429 responses are retried with jittered exponential backoff. 4xx errors (other than 429) are not retried. Progress is logged every 30 seconds.

Static Maps

StaticMap fetches a single rendered map image for an area — useful for web frontends and overview thumbnails.

tile, err := client.StaticMap(ctx, stadia.RequestStaticMap{
    Latitude: 40.7128, Longitude: -74.0060,
    Zoom:   12,
    Width:  512, Height: 512,
    Style:  stadia.StyleOSMBright,
})

The returned *Tile includes geographic bounds derived from the center, zoom, and image dimensions.

Tile cache

TileCache is a pluggable interface. FSStore stores tiles on the local filesystem at {root}/{style}/{z}/{x}/{y}.{fmt}. Used by DownloadArea to skip already-downloaded tiles (enables resumable downloads).

Elevation tiles

ElevationTile fetches an elevation raster tile and decodes pixel values into heights in meters. Supports both Terrain RGB and Terrarium encodings.

heights, rawPNG, err := client.ElevationTile(ctx, 12, 2345, 1234, stadia.EncodingTerrainRGB)
// heights is a []float64 with 65536 values (256×256 tile)
// rawPNG is the original PNG data

Encoding formulas:

Encoding Formula
EncodingTerrainRGB height = -10000 + ((R×256 + G + B/256) × 0.1)
EncodingTerrarium height = (R×256 + G + B/256) - 32768

Vector tiles

VectorTile fetches a Mapbox Vector Tile (.mvt) with road networks, building footprints, and administrative boundaries as geometry.

data, err := client.VectorTile(ctx, 14, 4567, 2345)
// data is raw MVT bytes — decode with a library like go-mvt

Response Types

All geocoding endpoints return *GeocodeResponse, which wraps a GeoJSON FeatureCollection:

type GeocodeResponse struct {
    BBox     []float64
    Features []GeocodeFeature
    Geocode  GeocodeMeta
    Type     string // "FeatureCollection"
}

GeocodeFeature provides convenience accessor methods that fall back through multiple response formats:

Method Returns Fallback chain
CountryCode() string CountryCodeContext.ISO3166A3WhosOnFirst.Country.Abbreviation
Locality() string LocalityWhosOnFirst.Locality.Name
Number() string AddressComponents.NumberHouseNumber
PostalCode() string PostalCodeAddressComponents.PostalCode
Region() string RegionWhosOnFirst.Region.Name
Street() string StreetAddressComponents.Street

Boundary & Focus Helpers

RequestGeocodeRaw, RequestGeocodeStructured, and RequestGeocodeAutocomplete all implement the RequestGeocode interface:

type RequestGeocode interface {
    SetBoundaryRect(xmin, ymin, xmax, ymax float64)
    SetFocusPoint(x, y float64)
}

They also expose the individual BoundaryRect*, BoundaryCircle*, and FocusPoint* fields directly.

CLI

The cmd/stadia directory contains a unified CLI with subcommands for all API features. Requires STADIA_MAPS_API_KEY in the environment.

export STADIA_MAPS_API_KEY=your-key
go run ./cmd/stadia [-v] <command> [subcommand] [flags]
Global flag Description
-v, --verbose Enable debug-level logging (default: info level)

The -v flag can appear anywhere on the command line (e.g., stadia -v tiles area ... or stadia tiles -v area ...).

Geocoding

Raw search — geocode raw

Freeform text search.

go run ./cmd/stadia geocode raw -query "Berlin Hauptbahnhof" -size 5
Flag Description
-query Search text (required)
-focus-lat, -focus-lng Bias results toward a point (both required if either set)
-boundary-rect-min-lat, -boundary-rect-max-lat Bounding box (all four required if any set)
-boundary-rect-min-lng, -boundary-rect-max-lng
-size Maximum number of results
-lang Language preference

Structured search — geocode structured

Search by discrete address fields.

go run ./cmd/stadia geocode structured \
  -address "12932 Ave 404" \
  -postal-code "93615" \
  -city "Orosi"
Flag Description
-address Street address (required)
-postal-code Postal/ZIP code (required)
-city Locality (optional)
-focus-lat, -focus-lng Bias results toward a point
-boundary-rect-* Bounding box (all four required if any set)

Autocomplete — geocode autocomplete

Type-ahead search with optional boundary and focus point filtering.

go run ./cmd/stadia geocode autocomplete -query "1600 Penn"
Flag Description
-query Search text (required)
-focus-lat, -focus-lng Bias results toward a point (both required if either set)
-boundary-rect-min-lat, -boundary-rect-max-lat Bounding box (all four required if any set)
-boundary-rect-min-lng, -boundary-rect-max-lng
-size Maximum number of results
-lang Language preference

Reverse geocode — geocode reverse

Find addresses at a given latitude/longitude.

go run ./cmd/stadia geocode reverse -lat 40.7128 -lng -74.0060

Place details — geocode by-gid

Look up a single feature by its GID.

go run ./cmd/stadia geocode by-gid -gid "openaddresses:address:us/ca/tulare-addresses-county:fe9dfab3d45c4550"

Bulk geocoding — geocode bulk

Sends two hardcoded structured-geocode requests in a single bulk call. No command-line flags — modify the source to change the queries.

go run ./cmd/stadia geocode bulk

Tiles

Single tile — tiles download

Download a raster tile for a given lat/lng with configurable style and format.

go run ./cmd/stadia tiles download -lat 40.7128 -lng -74.0060 -zoom 16
Flag Description
-lat, -lng Coordinates (required)
-zoom Zoom level (default: 16)
-style Raster style ID (default: alidade_satellite)
-format Output format: png, jpg, webp (default: jpg)
-retina Retina scale: 1, 2, or 3 (default: 1)
-ensure-size Resample retina tiles to 256×256 (default: false)
-output Output file path (default: tile.raw)

Area download — tiles area

Download all tiles covering a bounding box. Use -zoom 0 to auto-discover the maximum available zoom.

go run ./cmd/stadia tiles area \
  -north 36.35021 -south 36.30616 \
  -west -119.35014 -east -119.26937 \
  -zoom 20
Flag Description
-north, -south, -west, -east Bounding box (required)
-zoom Zoom level (0 = auto-discover max available)
-style Raster style ID (default: alidade_satellite)
-format Output format (default: jpg)
-retina Retina scale: 1, 2, or 3 (default: 1)
-ensure-size Resample retina tiles to 256×256 (default: false)
-output-dir Output directory (default: tiles)

Static map — tiles static-map

go run ./cmd/stadia tiles static-map -lat 40.7128 -lng -74.0060 -zoom 12 -width 512 -height 512
Flag Description
-lat, -lng Center coordinates (required)
-zoom Zoom level (default: 12)
-width, -height Image dimensions in pixels (default: 512)
-style Raster style ID (default: alidade_satellite)
-format Output format (default: auto)
-retina Retina scale: 1, 2, or 3 (default: 1)
-output Output file path (default: map.raw)

Elevation — tiles elevation

go run ./cmd/stadia tiles elevation -lat 36.330 -lng -119.300 -zoom 14
Flag Description
-lat, -lng Coordinates (required)
-zoom Zoom level (default: 14)
-encoding Elevation encoding: terrain_rgb or terrarium (default: terrain_rgb)

Vector tile — tiles vector

go run ./cmd/stadia tiles vector -lat 40.7128 -lng -74.0060 -zoom 14
Flag Description
-lat, -lng Coordinates (required)
-zoom Zoom level (default: 14)
-output Output file path (default: tile.mvt)

Development

Nix Flake

nix develop

Dependencies

Go Module

module source.gleipnir.technology/Gleipnir/go-stadia

Requires Go 1.26.3+.

License

Proprietary — Gleipnir Technology.