Skip to main content

Download satellite map tiles from the command line or as a Python library

Project description

geodot

geodot downloads satellite map tiles and can be used as a CLI or as a library from Python, JavaScript, and Rust.

Tiles are saved as:

{out}/tiles/{z}/{x}/{y}.jpg
{out}/manifest.json
{out}/index.html

Install

cargo install geodot
npm install -g @geodot/cli
npm install @geodot/lib
pip install geodot

Run without installing globally:

npx -y @geodot/cli -x 37.6504907 -y 55.7303 -z 18 -c 1 -r 1
uvx geodot -x 37.6504907 -y 55.7303 -z 18 -c 1 -r 1
cargo install geodot && geodot -x 37.6504907 -y 55.7303 -z 18 -c 1 -r 1

During local development:

python -m pip install -e '.[test]'
npm test
cargo test --manifest-path rust/Cargo.toml

Lint and format checks:

python -m pip install -e '.[test,dev]'
ruff format --check python
ruff check python

npm install
npm run format:check
npm run lint

cargo fmt --manifest-path rust/Cargo.toml -- --check
cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings

CLI

geodot -x 37.6504907 -y 55.7303 -z 18 -c 3 -r 3 -o data -j 16

geodot -x 37.6504907 -y 55.7303 --x2 37.652 --y2 55.7297 -z 18 -o data

geodot -p "37.6504,55.7304;37.6520,55.7304;37.6520,55.7297;37.6504,55.7297" -z 18 -o data

geodot --geojson area.geojson -z 18 -o data

geodot --geojson https://example.com/area.geojson -z 18 -o data
Flag Default Description
-x, --lon 37.6504907 Top-left longitude
-y, --lat 55.7303 Top-left latitude
--x2, --bottom-right-lon none Bottom-right longitude
--y2, --bottom-right-lat none Bottom-right latitude
-p, --polygon none Closed area as lon,lat;lon,lat;lon,lat
-g, --geojson none GeoJSON Polygon, Feature, or FeatureCollection file path or URL
-z, --zoom 18 Zoom level
-c, --cols 3 Tile columns to the right of the top-left tile
-r, --rows 3 Tile rows downward from the top-left tile
-o, --out data Output directory
-j, --jobs 16 Concurrent downloads
--no-manifest off Do not write manifest.json
--no-demo off Do not write index.html

Output

For -o data, a 3 by 3 download at zoom 18 writes files like this:

data/
├── manifest.json
├── index.html
└── tiles/
    └── 18/
        └── 158488/
            └── 81979.jpg

JPEG bytes are written directly from the tile server without re-compression.

Run geodot demo to inspect the downloaded tiles as a MapLibre raster overlay on a satellite base map at their tile coordinates and zoom. The demo serves {out}/index.html and reads tiles from {out}/tiles/{z}/{x}/{y}.jpg; it does not depend on manifest.json. Use the corner opacity control to compare the overlay against the base map. Zooming is disabled because the output folder only contains the downloaded zoom level.

Do not open index.html with file://; browsers block local tile loading from file origins. Serve the output folder instead:

geodot demo

Then open http://127.0.0.1:8000/. Use geodot demo -o other-dir for a different output folder. Pass --no-manifest when you only want tiles and the demo, or --no-demo when you only want tiles and manifest.json.

manifest.json contains:

{
  "center": { "x": 158488, "y": 81979, "z": 18 },
  "tiles": [
    {
      "tile": { "x": 158488, "y": 81979, "z": 18 },
      "bounds": {
        "lat_min": 55.730012,
        "lon_min": 37.650146,
        "lat_max": 55.730793,
        "lon_max": 37.651520
      },
      "path": "data/tiles/18/158488/81979.jpg",
      "bytes": 12345
    }
  ],
  "failed": []
}

Selection Modes

geodot supports three tile selection modes:

  1. Grid mode: -x/-y selects the top-left tile, then cols/rows expands right and down.
  2. Rectangle mode: -x/-y is the top-left geographic coordinate and --x2/--y2 is the bottom-right geographic coordinate.
  3. Polygon mode: -p/--polygon specifies a closed area as semicolon-separated lon,lat pairs. The closing edge is implicit.

Polygon downloads include tiles whose center or corners fall inside the polygon, plus tiles containing polygon vertices.

GeoJSON input uses the first Polygon geometry found in a Polygon, MultiPolygon, Feature, or FeatureCollection and uses its exterior ring as the download polygon.

Python API

from geodot import Coordinate, DownloadOptions, download, latlon_to_tile, tile_bounds, tile_grid, tile_grid_between, tile_grid_for_polygon

tile = latlon_to_tile(55.7303, 37.6504907, 18)
bounds = tile_bounds(tile)
tiles = tile_grid(55.7303, 37.6504907, zoom=18, cols=3, rows=3)
rectangle = tile_grid_between(55.7303, 37.6504907, 55.7297, 37.652, zoom=18)
polygon = tile_grid_for_polygon([
    Coordinate(lon=37.6504, lat=55.7304),
    Coordinate(lon=37.6520, lat=55.7304),
    Coordinate(lon=37.6520, lat=55.7297),
    Coordinate(lon=37.6504, lat=55.7297),
], zoom=18)

report = download(DownloadOptions(
    lat=55.7303,
    lon=37.6504907,
    bottom_right_lat=55.7297,
    bottom_right_lon=37.652,
    zoom=18,
    cols=3,
    rows=3,
    out="data",
    jobs=16,
    geojson=None,
    no_manifest=False,
    no_demo=False,
))

JavaScript API

import { download, latlonToTile, tileBounds, tileGrid, tileGridBetween, tileGridForPolygon } from '@geodot/lib';

const tile = latlonToTile(55.7303, 37.6504907, 18);
const bounds = tileBounds(tile);
const tiles = tileGrid(55.7303, 37.6504907, 18, 3, 3);
const rectangle = tileGridBetween(55.7303, 37.6504907, 55.7297, 37.652, 18);
const polygon = tileGridForPolygon([
  { lon: 37.6504, lat: 55.7304 },
  { lon: 37.6520, lat: 55.7304 },
  { lon: 37.6520, lat: 55.7297 },
  { lon: 37.6504, lat: 55.7297 },
], 18);

const report = await download({
  lat: 55.7303,
  lon: 37.6504907,
  bottomRightLat: 55.7297,
  bottomRightLon: 37.652,
  zoom: 18,
  cols: 3,
  rows: 3,
  out: 'data',
  jobs: 16,
  geojson: undefined,
  noManifest: false,
  noDemo: false,
});

Rust API

use geodot::{download, latlon_to_tile, tile_bounds, tile_grid, tile_grid_between, tile_grid_for_polygon, Coordinate, DownloadOptions};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let tile = latlon_to_tile(55.7303, 37.6504907, 18);
    let bounds = tile_bounds(tile);
    let tiles = tile_grid(55.7303, 37.6504907, 18, 3, 3);
    let rectangle = tile_grid_between(55.7303, 37.6504907, 55.7297, 37.652, 18);
    let polygon = tile_grid_for_polygon(&[
        Coordinate { lon: 37.6504, lat: 55.7304 },
        Coordinate { lon: 37.6520, lat: 55.7304 },
        Coordinate { lon: 37.6520, lat: 55.7297 },
        Coordinate { lon: 37.6504, lat: 55.7297 },
    ], 18);

    let report = download(DownloadOptions {
        lat: 55.7303,
        lon: 37.6504907,
        bottom_right_lat: Some(55.7297),
        bottom_right_lon: Some(37.652),
        polygon: Vec::new(),
        zoom: 18,
        cols: 3,
        rows: 3,
        out: "data".into(),
        jobs: 16,
        geojson: None,
        tile_url_template: None,
        no_manifest: false,
        no_demo: false,
    })
    .await?;

    Ok(())
}

Tile Math

Given tile { z: 18, x: 158488, y: 81979 }:

n = 2^z
lon_min = x / n * 360 - 180
lon_max = (x + 1) / n * 360 - 180
lat_max = atan(sinh(pi * (1 - 2y/n))) * 180/pi
lat_min = atan(sinh(pi * (1 - 2(y+1)/n))) * 180/pi

Tile bounds are returned as [lat_min, lon_min, lat_max, lon_max] fields.

Approximate resolution:

Zoom m/px Tile covers
18 0.34 86 x 86 m
16 1.36 347 x 347 m
14 5.45 1.4 x 1.4 km
10 86 22 x 22 km
8 345 88 x 88 km

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

geodot-0.1.6.tar.gz (59.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

geodot-0.1.6-py3-none-any.whl (12.9 kB view details)

Uploaded Python 3

File details

Details for the file geodot-0.1.6.tar.gz.

File metadata

  • Download URL: geodot-0.1.6.tar.gz
  • Upload date:
  • Size: 59.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for geodot-0.1.6.tar.gz
Algorithm Hash digest
SHA256 99d12d7a984c9b48e8fa48a201f378d68de0fd1ffd5620f8abbf494f9095dfcd
MD5 5af6aa0913abba168a983677eabca570
BLAKE2b-256 501c8ca87208f3de8be20768aaa298a4635af4d5f979535ae95931de0892b2c5

See more details on using hashes here.

Provenance

The following attestation bundles were made for geodot-0.1.6.tar.gz:

Publisher: publish.yml on ArgusPano/geodot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file geodot-0.1.6-py3-none-any.whl.

File metadata

  • Download URL: geodot-0.1.6-py3-none-any.whl
  • Upload date:
  • Size: 12.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for geodot-0.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 fe89d6c8e8e31d1580dc1b524475a55442e9a3904b317de579390f7d5c2eda06
MD5 f371a3bbe45ca220ff6af2996c83e187
BLAKE2b-256 f5f21d622612cd762171750aa3114f1f5ccf26a399e59ea9ace43cd4ad7d5b0c

See more details on using hashes here.

Provenance

The following attestation bundles were made for geodot-0.1.6-py3-none-any.whl:

Publisher: publish.yml on ArgusPano/geodot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page