0
# Place-based Mapping
1
2
Geocoding and mapping functionality for location-based visualizations. These capabilities combine geocoding services with tile downloading to create maps from place names and addresses.
3
4
## Capabilities
5
6
### Place Class
7
8
Geocode locations by name and automatically retrieve corresponding map tiles with integrated plotting capabilities for location-based visualizations.
9
10
```python { .api }
11
class Place:
12
"""
13
Geocode a place by name and get its map.
14
15
Parameters:
16
- search: str - Location to be searched (city, address, landmark, etc.)
17
- zoom: int or None - Zoom level for map detail (auto-calculated if None)
18
- path: str or None - Path to save raster file (no file saved if None)
19
- zoom_adjust: int or None - Adjustment to auto-calculated zoom level
20
- source: TileProvider, str, or None - Tile source (defaults to OpenStreetMap HOT)
21
- geocoder: geopy.geocoders object - Geocoding service (defaults to Nominatim)
22
23
Attributes:
24
- geocode: geopy object - Geocoding result with full location data
25
- s, n, e, w: float - Bounding box edges (south, north, east, west)
26
- im: ndarray - Image array of the retrieved map
27
- bbox: list - Bounding box [minX, minY, maxX, maxY] in lon/lat
28
- bbox_map: tuple - Bounding box in Web Mercator coordinates
29
- place: str - Formatted place name from geocoding result
30
- search: str - Original search string
31
- latitude, longitude: float - Center coordinates of the place
32
- zoom: int - Zoom level used for tile retrieval
33
- n_tiles: int - Number of tiles downloaded for the map
34
"""
35
36
def __init__(self, search, zoom=None, path=None, zoom_adjust=None,
37
source=None, geocoder=None): ...
38
39
def plot(self, ax=None, zoom='auto', interpolation='bilinear',
40
attribution=None):
41
"""
42
Plot the Place object on matplotlib axes.
43
44
Parameters:
45
- ax: AxesSubplot or None - Matplotlib axes (creates new figure if None)
46
- zoom: int or 'auto' - Zoom level (ignored, uses Place's zoom)
47
- interpolation: str - Image interpolation method ('bilinear', 'nearest', etc.)
48
- attribution: str or None - Attribution text (defaults to source attribution)
49
50
Returns:
51
AxesSubplot - Matplotlib axes containing the map
52
"""
53
```
54
55
**Usage Examples:**
56
57
```python
58
import contextily as ctx
59
import matplotlib.pyplot as plt
60
61
# Basic place geocoding and mapping
62
place = ctx.Place("Central Park, New York")
63
print(f"Found: {place.place}")
64
print(f"Coordinates: {place.latitude:.4f}, {place.longitude:.4f}")
65
print(f"Zoom level: {place.zoom}, Tiles: {place.n_tiles}")
66
67
# Display the map
68
place.plot()
69
plt.show()
70
71
# Custom zoom level
72
place_detailed = ctx.Place("Times Square, NYC", zoom=16)
73
place_detailed.plot()
74
plt.show()
75
76
# Save map to file while creating Place
77
place_saved = ctx.Place("Golden Gate Bridge",
78
path="golden_gate.tiff",
79
zoom=14)
80
81
# Use different tile provider
82
place_satellite = ctx.Place("Yellowstone National Park",
83
source=ctx.providers.ESRI.WorldImagery,
84
zoom=10)
85
place_satellite.plot()
86
plt.show()
87
88
# International locations
89
place_international = ctx.Place("Eiffel Tower, Paris, France")
90
print(f"Bbox: {place_international.bbox}") # [lon_min, lat_min, lon_max, lat_max]
91
place_international.plot()
92
plt.show()
93
```
94
95
### Advanced Place Usage
96
97
```python
98
import contextily as ctx
99
import geopy
100
101
# Custom geocoder configuration
102
from geopy.geocoders import Nominatim
103
custom_geocoder = Nominatim(user_agent="my_app_v1.0", timeout=10)
104
105
place = ctx.Place("1600 Pennsylvania Avenue, Washington DC",
106
geocoder=custom_geocoder,
107
zoom=15)
108
109
# Access detailed geocoding information
110
print(f"Full address: {place.geocode.address}")
111
print(f"Raw data: {place.geocode.raw}")
112
113
# Fine-tune zoom level
114
place_adjusted = ctx.Place("Statue of Liberty",
115
zoom_adjust=2) # 2 levels more detailed
116
117
# Plot on existing axes
118
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
119
120
place1 = ctx.Place("San Francisco")
121
place1.plot(ax=ax1)
122
123
place2 = ctx.Place("Los Angeles")
124
place2.plot(ax=ax2)
125
126
plt.suptitle("California Cities")
127
plt.show()
128
```
129
130
### Place Data Access
131
132
```python
133
import contextily as ctx
134
import numpy as np
135
136
# Create place and access data
137
place = ctx.Place("Grand Canyon National Park")
138
139
# Geographic information
140
print(f"Center: ({place.latitude}, {place.longitude})")
141
print(f"Bounding box (lon/lat): {place.bbox}")
142
print(f"Bounding box (Web Mercator): {place.bbox_map}")
143
144
# Image data
145
print(f"Image shape: {place.im.shape}") # (height, width, bands)
146
print(f"Image data type: {place.im.dtype}")
147
148
# Use image data directly
149
import matplotlib.pyplot as plt
150
fig, ax = plt.subplots(figsize=(10, 8))
151
ax.imshow(place.im, extent=place.bbox_map)
152
ax.set_title(place.place)
153
plt.show()
154
155
# Combine with other geospatial data
156
# Convert to same coordinate system as needed
157
extent_4326 = place.bbox # Already in WGS84
158
extent_3857 = place.bbox_map # Web Mercator
159
```
160
161
### Deprecated Plot Function
162
163
```python { .api }
164
def plot_map(place, bbox=None, title=None, ax=None, axis_off=True,
165
latlon=True, attribution=None):
166
"""
167
Plot map of given place (deprecated).
168
169
Parameters:
170
- place: Place instance or ndarray - Place object or image array to plot
171
- bbox: tuple or None - Bounding box for display extent
172
- title: str or None - Plot title
173
- ax: AxesSubplot or None - Matplotlib axes (creates new if None)
174
- axis_off: bool - Whether to turn off axis border and ticks
175
- latlon: bool - Whether bbox is in lat/lon coordinates
176
- attribution: str or None - Attribution text
177
178
Returns:
179
AxesSubplot - Matplotlib axes containing the plot
180
181
Note: This function is deprecated. Use add_basemap or Place.plot instead.
182
"""
183
```
184
185
**Migration from plot_map:**
186
187
```python
188
import contextily as ctx
189
import warnings
190
191
# Old deprecated approach (avoid)
192
place = ctx.Place("Boston")
193
with warnings.catch_warnings():
194
warnings.simplefilter("ignore", DeprecationWarning)
195
ax = ctx.plot_map(place)
196
197
# New recommended approach
198
place = ctx.Place("Boston")
199
ax = place.plot() # Preferred method
200
```
201
202
## Geocoding Customization
203
204
### Alternative Geocoding Services
205
206
```python
207
import contextily as ctx
208
from geopy.geocoders import GoogleV3, Bing, ArcGIS
209
210
# Google Geocoding (requires API key)
211
google_geocoder = GoogleV3(api_key="your_api_key")
212
place_google = ctx.Place("Space Needle", geocoder=google_geocoder)
213
214
# Bing Geocoding (requires API key)
215
bing_geocoder = Bing(api_key="your_api_key")
216
place_bing = ctx.Place("Pike Place Market", geocoder=bing_geocoder)
217
218
# ArcGIS Geocoding (free, rate limited)
219
arcgis_geocoder = ArcGIS(timeout=10)
220
place_arcgis = ctx.Place("Mount Rainier", geocoder=arcgis_geocoder)
221
222
# Compare results
223
places = [place_google, place_bing, place_arcgis]
224
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
225
226
for i, place in enumerate(places):
227
place.plot(ax=axes[i])
228
axes[i].set_title(f"{place.search} via {type(place.geocode.geocoder).__name__}")
229
230
plt.show()
231
```
232
233
### Handling Geocoding Errors
234
235
```python
236
import contextily as ctx
237
from geopy.exc import GeopyError, GeocoderTimedOut
238
239
def safe_place_creation(search_term, **kwargs):
240
"""Create Place with error handling."""
241
try:
242
return ctx.Place(search_term, **kwargs)
243
except GeopyError as e:
244
print(f"Geocoding failed for '{search_term}': {e}")
245
return None
246
except GeocoderTimedOut:
247
print(f"Geocoding timed out for '{search_term}'")
248
return None
249
except Exception as e:
250
print(f"Place creation failed for '{search_term}': {e}")
251
return None
252
253
# Usage with error handling
254
places = []
255
search_terms = [
256
"New York City",
257
"Invalid Location XYZ123", # Will fail
258
"London, England"
259
]
260
261
for term in search_terms:
262
place = safe_place_creation(term, zoom=10)
263
if place:
264
places.append(place)
265
266
# Plot successful results
267
if places:
268
fig, axes = plt.subplots(1, len(places), figsize=(5*len(places), 5))
269
if len(places) == 1:
270
axes = [axes]
271
272
for ax, place in zip(axes, places):
273
place.plot(ax=ax)
274
```
275
276
## Batch Processing
277
278
### Multiple Locations
279
280
```python
281
import contextily as ctx
282
import matplotlib.pyplot as plt
283
284
def create_place_grid(locations, cols=3):
285
"""Create a grid plot of multiple places."""
286
n_places = len(locations)
287
rows = (n_places + cols - 1) // cols
288
289
fig, axes = plt.subplots(rows, cols, figsize=(5*cols, 5*rows))
290
axes = np.array(axes).flatten() if n_places > 1 else [axes]
291
292
places = []
293
for i, location in enumerate(locations):
294
try:
295
place = ctx.Place(location, zoom=12)
296
place.plot(ax=axes[i])
297
places.append(place)
298
except Exception as e:
299
axes[i].text(0.5, 0.5, f"Failed to load\n{location}",
300
ha='center', va='center', transform=axes[i].transAxes)
301
print(f"Failed to create place for {location}: {e}")
302
303
# Hide unused subplots
304
for i in range(len(locations), len(axes)):
305
axes[i].set_visible(False)
306
307
plt.tight_layout()
308
return places
309
310
# Example usage
311
major_cities = [
312
"New York City",
313
"Los Angeles",
314
"Chicago",
315
"Houston",
316
"Phoenix",
317
"Philadelphia"
318
]
319
320
places = create_place_grid(major_cities, cols=3)
321
plt.show()
322
```
323
324
### Route Visualization
325
326
```python
327
import contextily as ctx
328
import matplotlib.pyplot as plt
329
import numpy as np
330
331
def create_route_map(start_location, end_location, zoom=10):
332
"""Create a map showing route between two locations."""
333
334
# Geocode start and end points
335
start_place = ctx.Place(start_location)
336
end_place = ctx.Place(end_location)
337
338
# Calculate combined bounding box
339
all_lons = [start_place.longitude, end_place.longitude]
340
all_lats = [start_place.latitude, end_place.latitude]
341
342
bbox_combined = [
343
min(all_lons) - 0.1, # west
344
min(all_lats) - 0.1, # south
345
max(all_lons) + 0.1, # east
346
max(all_lats) + 0.1 # north
347
]
348
349
# Download map for combined area
350
img, extent = ctx.bounds2img(*bbox_combined, zoom=zoom, ll=True)
351
352
# Create plot
353
fig, ax = plt.subplots(figsize=(12, 8))
354
ax.imshow(img, extent=extent)
355
356
# Convert coordinates to Web Mercator for plotting
357
import mercantile as mt
358
start_x, start_y = mt.xy(start_place.longitude, start_place.latitude)
359
end_x, end_y = mt.xy(end_place.longitude, end_place.latitude)
360
361
# Plot points and line
362
ax.scatter([start_x], [start_y], c='green', s=100, label=start_location, zorder=5)
363
ax.scatter([end_x], [end_y], c='red', s=100, label=end_location, zorder=5)
364
ax.plot([start_x, end_x], [start_y, end_y], 'b--', linewidth=2, alpha=0.7, zorder=4)
365
366
ax.legend()
367
ax.set_title(f"Route: {start_location} to {end_location}")
368
plt.show()
369
370
return start_place, end_place
371
372
# Example usage
373
start, end = create_route_map("San Francisco", "Los Angeles")
374
```
375
376
## Integration with Other Libraries
377
378
### GeoPandas Integration
379
380
```python
381
import contextily as ctx
382
import geopandas as gpd
383
import matplotlib.pyplot as plt
384
385
# Create place and convert to GeoDataFrame
386
place = ctx.Place("Washington DC", zoom=12)
387
388
# Create point geometry for the place center
389
from shapely.geometry import Point
390
point = Point(place.longitude, place.latitude)
391
gdf = gpd.GeoDataFrame([{'name': place.place, 'geometry': point}], crs='EPSG:4326')
392
393
# Convert to Web Mercator for basemap overlay
394
gdf_3857 = gdf.to_crs(epsg=3857)
395
396
# Plot with basemap
397
ax = gdf_3857.plot(figsize=(10, 8), color='red', markersize=100)
398
ctx.add_basemap(ax, source=ctx.providers.OpenStreetMap.HOT)
399
plt.title(f"Location: {place.place}")
400
plt.show()
401
```
402
403
### Folium Integration
404
405
```python
406
import contextily as ctx
407
import folium
408
409
# Geocode location
410
place = ctx.Place("Central Park, NYC")
411
412
# Create folium map centered on the place
413
m = folium.Map(
414
location=[place.latitude, place.longitude],
415
zoom_start=place.zoom
416
)
417
418
# Add marker
419
folium.Marker(
420
[place.latitude, place.longitude],
421
popup=place.place,
422
tooltip=f"Zoom: {place.zoom}, Tiles: {place.n_tiles}"
423
).add_to(m)
424
425
# Add bounding box
426
folium.Rectangle(
427
bounds=[[place.bbox[1], place.bbox[0]], [place.bbox[3], place.bbox[2]]],
428
color='red',
429
fill=False
430
).add_to(m)
431
432
# Display map
433
m.save('place_map.html')
434
```
435
436
## Performance and Caching
437
438
### Optimizing Place Creation
439
440
```python
441
import contextily as ctx
442
443
# Set persistent cache for place images
444
ctx.set_cache_dir('~/contextily_places_cache')
445
446
# Reuse geocoding results
447
class PlaceCache:
448
def __init__(self):
449
self.geocode_cache = {}
450
451
def get_place(self, search, **kwargs):
452
if search in self.geocode_cache:
453
# Reuse geocoding result
454
cached_geocode = self.geocode_cache[search]
455
place = ctx.Place.__new__(ctx.Place)
456
place.geocode = cached_geocode
457
place.search = search
458
# Initialize other attributes...
459
place._get_map()
460
return place
461
else:
462
# Create new place and cache geocoding
463
place = ctx.Place(search, **kwargs)
464
self.geocode_cache[search] = place.geocode
465
return place
466
467
# Usage
468
cache = PlaceCache()
469
place1 = cache.get_place("Paris, France") # First time: geocodes
470
place2 = cache.get_place("Paris, France") # Second time: uses cache
471
```
472
473
### Bulk Location Processing
474
475
```python
476
import contextily as ctx
477
from concurrent.futures import ThreadPoolExecutor
478
import time
479
480
def create_place_safe(location_data):
481
"""Thread-safe place creation."""
482
location, zoom = location_data
483
try:
484
place = ctx.Place(location, zoom=zoom)
485
return place
486
except Exception as e:
487
print(f"Failed to create place for {location}: {e}")
488
return None
489
490
def batch_create_places(locations, max_workers=3):
491
"""Create multiple places in parallel (be respectful of geocoding services)."""
492
location_data = [(loc, 10) for loc in locations]
493
494
places = []
495
with ThreadPoolExecutor(max_workers=max_workers) as executor:
496
results = executor.map(create_place_safe, location_data)
497
places = [place for place in results if place is not None]
498
499
return places
500
501
# Example usage (be mindful of geocoding service rate limits)
502
locations = ["New York", "Boston", "Philadelphia", "Washington DC"]
503
places = batch_create_places(locations, max_workers=2)
504
505
# Plot results
506
if places:
507
fig, axes = plt.subplots(2, 2, figsize=(12, 12))
508
axes = axes.flatten()
509
510
for i, place in enumerate(places[:4]):
511
place.plot(ax=axes[i])
512
513
plt.tight_layout()
514
plt.show()
515
```