Context geo-tiles in Python for retrieving and working with basemap tiles from the internet
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Geocoding and mapping functionality for location-based visualizations. These capabilities combine geocoding services with tile downloading to create maps from place names and addresses.
Geocode locations by name and automatically retrieve corresponding map tiles with integrated plotting capabilities for location-based visualizations.
class Place:
"""
Geocode a place by name and get its map.
Parameters:
- search: str - Location to be searched (city, address, landmark, etc.)
- zoom: int or None - Zoom level for map detail (auto-calculated if None)
- path: str or None - Path to save raster file (no file saved if None)
- zoom_adjust: int or None - Adjustment to auto-calculated zoom level
- source: TileProvider, str, or None - Tile source (defaults to OpenStreetMap HOT)
- geocoder: geopy.geocoders object - Geocoding service (defaults to Nominatim)
Attributes:
- geocode: geopy object - Geocoding result with full location data
- s, n, e, w: float - Bounding box edges (south, north, east, west)
- im: ndarray - Image array of the retrieved map
- bbox: list - Bounding box [minX, minY, maxX, maxY] in lon/lat
- bbox_map: tuple - Bounding box in Web Mercator coordinates
- place: str - Formatted place name from geocoding result
- search: str - Original search string
- latitude, longitude: float - Center coordinates of the place
- zoom: int - Zoom level used for tile retrieval
- n_tiles: int - Number of tiles downloaded for the map
"""
def __init__(self, search, zoom=None, path=None, zoom_adjust=None,
source=None, geocoder=None): ...
def plot(self, ax=None, zoom='auto', interpolation='bilinear',
attribution=None):
"""
Plot the Place object on matplotlib axes.
Parameters:
- ax: AxesSubplot or None - Matplotlib axes (creates new figure if None)
- zoom: int or 'auto' - Zoom level (ignored, uses Place's zoom)
- interpolation: str - Image interpolation method ('bilinear', 'nearest', etc.)
- attribution: str or None - Attribution text (defaults to source attribution)
Returns:
AxesSubplot - Matplotlib axes containing the map
"""Usage Examples:
import contextily as ctx
import matplotlib.pyplot as plt
# Basic place geocoding and mapping
place = ctx.Place("Central Park, New York")
print(f"Found: {place.place}")
print(f"Coordinates: {place.latitude:.4f}, {place.longitude:.4f}")
print(f"Zoom level: {place.zoom}, Tiles: {place.n_tiles}")
# Display the map
place.plot()
plt.show()
# Custom zoom level
place_detailed = ctx.Place("Times Square, NYC", zoom=16)
place_detailed.plot()
plt.show()
# Save map to file while creating Place
place_saved = ctx.Place("Golden Gate Bridge",
path="golden_gate.tiff",
zoom=14)
# Use different tile provider
place_satellite = ctx.Place("Yellowstone National Park",
source=ctx.providers.ESRI.WorldImagery,
zoom=10)
place_satellite.plot()
plt.show()
# International locations
place_international = ctx.Place("Eiffel Tower, Paris, France")
print(f"Bbox: {place_international.bbox}") # [lon_min, lat_min, lon_max, lat_max]
place_international.plot()
plt.show()import contextily as ctx
import geopy
# Custom geocoder configuration
from geopy.geocoders import Nominatim
custom_geocoder = Nominatim(user_agent="my_app_v1.0", timeout=10)
place = ctx.Place("1600 Pennsylvania Avenue, Washington DC",
geocoder=custom_geocoder,
zoom=15)
# Access detailed geocoding information
print(f"Full address: {place.geocode.address}")
print(f"Raw data: {place.geocode.raw}")
# Fine-tune zoom level
place_adjusted = ctx.Place("Statue of Liberty",
zoom_adjust=2) # 2 levels more detailed
# Plot on existing axes
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
place1 = ctx.Place("San Francisco")
place1.plot(ax=ax1)
place2 = ctx.Place("Los Angeles")
place2.plot(ax=ax2)
plt.suptitle("California Cities")
plt.show()import contextily as ctx
import numpy as np
# Create place and access data
place = ctx.Place("Grand Canyon National Park")
# Geographic information
print(f"Center: ({place.latitude}, {place.longitude})")
print(f"Bounding box (lon/lat): {place.bbox}")
print(f"Bounding box (Web Mercator): {place.bbox_map}")
# Image data
print(f"Image shape: {place.im.shape}") # (height, width, bands)
print(f"Image data type: {place.im.dtype}")
# Use image data directly
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10, 8))
ax.imshow(place.im, extent=place.bbox_map)
ax.set_title(place.place)
plt.show()
# Combine with other geospatial data
# Convert to same coordinate system as needed
extent_4326 = place.bbox # Already in WGS84
extent_3857 = place.bbox_map # Web Mercatordef plot_map(place, bbox=None, title=None, ax=None, axis_off=True,
latlon=True, attribution=None):
"""
Plot map of given place (deprecated).
Parameters:
- place: Place instance or ndarray - Place object or image array to plot
- bbox: tuple or None - Bounding box for display extent
- title: str or None - Plot title
- ax: AxesSubplot or None - Matplotlib axes (creates new if None)
- axis_off: bool - Whether to turn off axis border and ticks
- latlon: bool - Whether bbox is in lat/lon coordinates
- attribution: str or None - Attribution text
Returns:
AxesSubplot - Matplotlib axes containing the plot
Note: This function is deprecated. Use add_basemap or Place.plot instead.
"""Migration from plot_map:
import contextily as ctx
import warnings
# Old deprecated approach (avoid)
place = ctx.Place("Boston")
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
ax = ctx.plot_map(place)
# New recommended approach
place = ctx.Place("Boston")
ax = place.plot() # Preferred methodimport contextily as ctx
from geopy.geocoders import GoogleV3, Bing, ArcGIS
# Google Geocoding (requires API key)
google_geocoder = GoogleV3(api_key="your_api_key")
place_google = ctx.Place("Space Needle", geocoder=google_geocoder)
# Bing Geocoding (requires API key)
bing_geocoder = Bing(api_key="your_api_key")
place_bing = ctx.Place("Pike Place Market", geocoder=bing_geocoder)
# ArcGIS Geocoding (free, rate limited)
arcgis_geocoder = ArcGIS(timeout=10)
place_arcgis = ctx.Place("Mount Rainier", geocoder=arcgis_geocoder)
# Compare results
places = [place_google, place_bing, place_arcgis]
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
for i, place in enumerate(places):
place.plot(ax=axes[i])
axes[i].set_title(f"{place.search} via {type(place.geocode.geocoder).__name__}")
plt.show()import contextily as ctx
from geopy.exc import GeopyError, GeocoderTimedOut
def safe_place_creation(search_term, **kwargs):
"""Create Place with error handling."""
try:
return ctx.Place(search_term, **kwargs)
except GeopyError as e:
print(f"Geocoding failed for '{search_term}': {e}")
return None
except GeocoderTimedOut:
print(f"Geocoding timed out for '{search_term}'")
return None
except Exception as e:
print(f"Place creation failed for '{search_term}': {e}")
return None
# Usage with error handling
places = []
search_terms = [
"New York City",
"Invalid Location XYZ123", # Will fail
"London, England"
]
for term in search_terms:
place = safe_place_creation(term, zoom=10)
if place:
places.append(place)
# Plot successful results
if places:
fig, axes = plt.subplots(1, len(places), figsize=(5*len(places), 5))
if len(places) == 1:
axes = [axes]
for ax, place in zip(axes, places):
place.plot(ax=ax)import contextily as ctx
import matplotlib.pyplot as plt
def create_place_grid(locations, cols=3):
"""Create a grid plot of multiple places."""
n_places = len(locations)
rows = (n_places + cols - 1) // cols
fig, axes = plt.subplots(rows, cols, figsize=(5*cols, 5*rows))
axes = np.array(axes).flatten() if n_places > 1 else [axes]
places = []
for i, location in enumerate(locations):
try:
place = ctx.Place(location, zoom=12)
place.plot(ax=axes[i])
places.append(place)
except Exception as e:
axes[i].text(0.5, 0.5, f"Failed to load\n{location}",
ha='center', va='center', transform=axes[i].transAxes)
print(f"Failed to create place for {location}: {e}")
# Hide unused subplots
for i in range(len(locations), len(axes)):
axes[i].set_visible(False)
plt.tight_layout()
return places
# Example usage
major_cities = [
"New York City",
"Los Angeles",
"Chicago",
"Houston",
"Phoenix",
"Philadelphia"
]
places = create_place_grid(major_cities, cols=3)
plt.show()import contextily as ctx
import matplotlib.pyplot as plt
import numpy as np
def create_route_map(start_location, end_location, zoom=10):
"""Create a map showing route between two locations."""
# Geocode start and end points
start_place = ctx.Place(start_location)
end_place = ctx.Place(end_location)
# Calculate combined bounding box
all_lons = [start_place.longitude, end_place.longitude]
all_lats = [start_place.latitude, end_place.latitude]
bbox_combined = [
min(all_lons) - 0.1, # west
min(all_lats) - 0.1, # south
max(all_lons) + 0.1, # east
max(all_lats) + 0.1 # north
]
# Download map for combined area
img, extent = ctx.bounds2img(*bbox_combined, zoom=zoom, ll=True)
# Create plot
fig, ax = plt.subplots(figsize=(12, 8))
ax.imshow(img, extent=extent)
# Convert coordinates to Web Mercator for plotting
import mercantile as mt
start_x, start_y = mt.xy(start_place.longitude, start_place.latitude)
end_x, end_y = mt.xy(end_place.longitude, end_place.latitude)
# Plot points and line
ax.scatter([start_x], [start_y], c='green', s=100, label=start_location, zorder=5)
ax.scatter([end_x], [end_y], c='red', s=100, label=end_location, zorder=5)
ax.plot([start_x, end_x], [start_y, end_y], 'b--', linewidth=2, alpha=0.7, zorder=4)
ax.legend()
ax.set_title(f"Route: {start_location} to {end_location}")
plt.show()
return start_place, end_place
# Example usage
start, end = create_route_map("San Francisco", "Los Angeles")import contextily as ctx
import geopandas as gpd
import matplotlib.pyplot as plt
# Create place and convert to GeoDataFrame
place = ctx.Place("Washington DC", zoom=12)
# Create point geometry for the place center
from shapely.geometry import Point
point = Point(place.longitude, place.latitude)
gdf = gpd.GeoDataFrame([{'name': place.place, 'geometry': point}], crs='EPSG:4326')
# Convert to Web Mercator for basemap overlay
gdf_3857 = gdf.to_crs(epsg=3857)
# Plot with basemap
ax = gdf_3857.plot(figsize=(10, 8), color='red', markersize=100)
ctx.add_basemap(ax, source=ctx.providers.OpenStreetMap.HOT)
plt.title(f"Location: {place.place}")
plt.show()import contextily as ctx
import folium
# Geocode location
place = ctx.Place("Central Park, NYC")
# Create folium map centered on the place
m = folium.Map(
location=[place.latitude, place.longitude],
zoom_start=place.zoom
)
# Add marker
folium.Marker(
[place.latitude, place.longitude],
popup=place.place,
tooltip=f"Zoom: {place.zoom}, Tiles: {place.n_tiles}"
).add_to(m)
# Add bounding box
folium.Rectangle(
bounds=[[place.bbox[1], place.bbox[0]], [place.bbox[3], place.bbox[2]]],
color='red',
fill=False
).add_to(m)
# Display map
m.save('place_map.html')import contextily as ctx
# Set persistent cache for place images
ctx.set_cache_dir('~/contextily_places_cache')
# Reuse geocoding results
class PlaceCache:
def __init__(self):
self.geocode_cache = {}
def get_place(self, search, **kwargs):
if search in self.geocode_cache:
# Reuse geocoding result
cached_geocode = self.geocode_cache[search]
place = ctx.Place.__new__(ctx.Place)
place.geocode = cached_geocode
place.search = search
# Initialize other attributes...
place._get_map()
return place
else:
# Create new place and cache geocoding
place = ctx.Place(search, **kwargs)
self.geocode_cache[search] = place.geocode
return place
# Usage
cache = PlaceCache()
place1 = cache.get_place("Paris, France") # First time: geocodes
place2 = cache.get_place("Paris, France") # Second time: uses cacheimport contextily as ctx
from concurrent.futures import ThreadPoolExecutor
import time
def create_place_safe(location_data):
"""Thread-safe place creation."""
location, zoom = location_data
try:
place = ctx.Place(location, zoom=zoom)
return place
except Exception as e:
print(f"Failed to create place for {location}: {e}")
return None
def batch_create_places(locations, max_workers=3):
"""Create multiple places in parallel (be respectful of geocoding services)."""
location_data = [(loc, 10) for loc in locations]
places = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
results = executor.map(create_place_safe, location_data)
places = [place for place in results if place is not None]
return places
# Example usage (be mindful of geocoding service rate limits)
locations = ["New York", "Boston", "Philadelphia", "Washington DC"]
places = batch_create_places(locations, max_workers=2)
# Plot results
if places:
fig, axes = plt.subplots(2, 2, figsize=(12, 12))
axes = axes.flatten()
for i, place in enumerate(places[:4]):
place.plot(ax=axes[i])
plt.tight_layout()
plt.show()Install with Tessl CLI
npx tessl i tessl/pypi-contextily