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

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
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
-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

Output

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

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

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

Tile images are always 256x256 pixels. geodot does not currently support other tile sizes.

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.

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,
))

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,
});

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,
        tile_url_template: None,
    })
    .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.1.tar.gz (41.9 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.1-py3-none-any.whl (7.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: geodot-0.1.1.tar.gz
  • Upload date:
  • Size: 41.9 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.1.tar.gz
Algorithm Hash digest
SHA256 50182af09e2b7a869d24a03a1456d19a6b9ee7920d921d04df966386c63891d5
MD5 4bedfbf5fc8e10b143fec44c024e7828
BLAKE2b-256 c38b35723cdab5ed586ec7181fe485e51953cea607cc1de090894559bda0ac4c

See more details on using hashes here.

Provenance

The following attestation bundles were made for geodot-0.1.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: geodot-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 7.7 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 93977e85ced7445f77f8ded57f28463e920e3e9045e5d55d9f1d62361df75b00
MD5 13d52f8d9d5dd2ea9ea8e12cf0f0721d
BLAKE2b-256 308cb74c8e681812772a06a2943682576916c8a77c88b2246eb3cc303803c2e4

See more details on using hashes here.

Provenance

The following attestation bundles were made for geodot-0.1.1-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