Introduction
To request a satellite tile image (from Esri World Imagery) centered at a given latitude, longitude, and zoom level, you can use Python.
Step-by-step Python Example
Requirements:
1 | pip install mercantile requests pillow |
Python Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import mercantile import requests from PIL import Image from io import BytesIO # Define your center coordinates and zoom level lat = 37.7749 # Example: San Francisco lon = -122.4194 zoom = 12 # Zoom level (0–19) # Step 1: Convert lat/lon to XYZ tile tile = mercantile.tile(lon, lat, zoom) x, y, z = tile.x, tile.y, tile.z # Step 2: Construct Esri tile URL url = f"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}" # Step 3: Request and display/save the tile image response = requests.get(url) if response.status_code == 200: img = Image.open(BytesIO(response.content)) img.show() # To display img.save(f"esri_tile_z{z}_x{x}_y{y}.png") # To save print(f"Saved tile at zoom {z}, x={x}, y={y}") else: print("Failed to download tile:", response.status_code) |
Tile Size and Coverage
Each tile is:
- 256×256 pixels
- At zoom level:
z=0
: world = 1 tilez=1
: 2×2 tiles- ...
z=19
: \~38cm resolution, millions of tiles
Notes:
- Esri allows usage under fair use and non-commercial limits. For large-scale use, check their terms of service.
- For deep learning, you may want to normalize lat/lon → multiple tiles → stitched image → patch → label.
Improving image resolution
The default Esri tile server (https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}
) provides pre-rendered 256×256 pixel tiles, and the resolution depends on the zoom level.
If the image appears blurry or low-resolution, you can improve it by:
Requesting Higher Zoom Levels**
Each increase in zoom level approximately doubles the spatial resolution. For example:
Zoom | Approx. Resolution |
---|---|
12 | \~38 meters/pixel |
16 | \~2.4 meters/pixel |
19 | \~0.3 meters/pixel |
So, try setting zoom = 18
or zoom = 19
. At those zoom levels, Esri often provides very high-resolution imagery (urban areas especially).
Update line:
1 | zoom = 19 |
But note:
- The image will still be only 256x256 pixels, just representing a smaller real-world area with more detail.
- To get a larger image with high detail, you need to download and stitch multiple tiles.
Stitch Multiple Tiles (e.g., 3x3 or 5x5 Tile Patch)**
Here’s how to get a high-res image centered at a point by stitching multiple high-zoom tiles:
Install:
1 | pip install mercantile requests pillow |
Code to Stitch Tiles:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | import mercantile import requests from PIL import Image from io import BytesIO def get_tile_image(z, x, y): url = f"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}" response = requests.get(url) if response.status_code == 200: return Image.open(BytesIO(response.content)) else: return None def stitch_tiles(lat, lon, zoom=18, tiles_across=3): center_tile = mercantile.tile(lon, lat, zoom) half = tiles_across // 2 stitched = Image.new("RGB", (256 * tiles_across, 256 * tiles_across)) for dx in range(-half, half + 1): for dy in range(-half, half + 1): x = center_tile.x + dx y = center_tile.y + dy img = get_tile_image(zoom, x, y) if img: stitched.paste(img, ((dx + half) * 256, (dy + half) * 256)) else: print(f"Tile {x},{y} failed") return stitched # Example usage: lat, lon = 37.7749, -122.4194 zoom = 18 tiles_across = 3 # for a 768x768 image stitched_image = stitch_tiles(lat, lon, zoom, tiles_across) stitched_image.show() stitched_image.save("stitched_highres_patch.png") |
Result
- You'll get a 768×768 pixel image (or larger, depending on
tiles_across
). - Each pixel will have high spatial resolution, especially at
zoom=18
orzoom=19
. - This is ideal for training neural networks with good geographic detail.
Note: You can reduce the size of the stitched image in your existing code by:
- Converting it to JPEG (or WebP) to save space
- Optionally resizing it before saving
Option 1: Save as JPEG (smaller than PNG)
Just replace your last line:
1 | stitched_image.save("stitched_highres_patch.png") |
With this:
1 | stitched_image.save("stitched_highres_patch.jpg", format="JPEG", quality=85, optimize=True) |
This will compress the image without visibly affecting quality, and reduce storage from \~1–6 MB PNG → \~200–800 KB JPEG.
Option 2: Resize + Save as JPEG (smaller still)
If you don't need full 768×768 resolution, resize before saving:
1 2 | resized = stitched_image.resize((384, 384), Image.LANCZOS) # or 512x512, or whatever you prefer resized.save("stitched_highres_patch.jpg", format="JPEG", quality=85, optimize=True) |
This reduces both dimensions and file size, perfect for ML training data.
Option 3: Save as WebP (best compression)
Requires modern readers, but better compression than JPEG:
1 | stitched_image.save("stitched_highres_patch.webp", format="WEBP", quality=80, method=6) |
Summary:
Method | File Type | Typical Size | Notes |
---|---|---|---|
PNG (your original) | Lossless | 1–6 MB | Big, high quality |
JPEG | Lossy | 200–800 KB | Small, good for ML |
Resized JPEG | Lossy | 50–300 KB | Smaller + faster |
WebP | Lossy or lossless | 30–70% of JPEG | Best compression |
Want Even Better Quality?
If Esri’s tiles still aren't good enough:
- Use Google Static Maps API (limited free use)
- Try Maxar (commercial, via Esri Premium)
- Consider NAIP (1m) via Microsoft Planetary Computer or Google Earth Engine
Acquisition date
When you download a tile from Esri’s World Imagery tile server, the exact acquisition date of the satellite image is not embedded in the tile or URL. These are pre-rendered map tiles stitched together from a global mosaic, and the imagery dates vary by location and zoom level.
What You Can’t Do Directly
You cannot get the acquisition date from:
1 | `https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}` |
The image metadata (it’s just a static PNG with no useful EXIF or metadata)
Use Google Earth Engine or Sentinel/Planet Imagery Instead
If you want precise control over:
- Image acquisition date
- Cloud cover
- Spatial resolution
- Spectral bands
Then use:
- Google Earth Engine (GEE): Query Landsat, Sentinel, MODIS by date
- SentinelHub or Microsoft Planetary Computer: Same idea, all STAC-based imagery
Summary
Source | Acquisition Date Available? | How to Access |
---|---|---|
Esri World Imagery Tiles | ❌ Not directly | Use metadata service + lat/lon |
Esri World Imagery Metadata | ✅ Yes | REST query or ArcGIS tool |
Google Earth Engine | ✅ Yes | Filter imagery by date + cloud cover |
SentinelHub / Planetary Comp. | ✅ Yes | Full access to date, bands, quality |
References
Links | Site |
---|---|
esri website | esri |
Layer: Low Resolution 15m Imagery | server.arcgisonline.com |
World Imagery | arcgis.com |