How to Cache GeoDataFrames in Prettymaps for Faster Map Generation
Prettymaps provides a built-in file-system cache that stores OpenStreetMap GeoDataFrames as GeoJSON files, eliminating redundant network requests and reducing map generation time from minutes to milliseconds.
Every time you generate a map with prettymaps, the library queries OpenStreetMap (OSM) and converts the response into GeoDataFrames (GDFs) using GeoPandas. Since OSM data changes slowly but styling experiments happen constantly, repeatedly downloading identical geometry wastes bandwidth and blocks your creative flow. The caching mechanism in prettymaps/fetch.py solves this by persisting raw GDFs to disk using deterministic hash-based keys.
How the Caching System Works
The cache implementation lives entirely in prettymaps/fetch.py and operates through three coordinated functions: hash generation, disk I/O, and request integration.
Hash Generation: Creating Deterministic Cache Keys
The cache key combines two hashed components to guarantee uniqueness for every distinct query.
# From prettymaps/fetch.py – write_to_cache (lines 32-38)
perimeter_hash = hash(perimeter["geometry"].to_json())
kwargs_hash = hash(str(layer_kwargs))
hash_str = f"{perimeter_hash}_{kwargs_hash}"
- perimeter_hash: Derived from the geometry JSON of your area of interest (address, bounding box, or polygon)
- kwargs_hash: Captures layer-specific parameters including OSM tags, filters, and query modifiers
This dual-hash approach ensures that moving your map 100 meters east or swapping "building" tags for "highway" tags produces distinct cache entries.
Writing GeoDataFrames to Disk
When fresh OSM data arrives, write_to_cache() serializes the GDF to GeoJSON format with logging suppressed for clean output.
# From prettymaps/fetch.py – lines 41-45
gdf.to_file(cache_path, driver="GeoJSON")
Cached files land in prettymaps_cache/<hash_str>.geojson by default. The GeoJSON driver preserves coordinate reference systems and attribute tables without additional compression overhead.
Reading from Cache Before Network Requests
The read_from_cache() function short-circuits expensive OSM queries when a matching file exists.
# From prettymaps/fetch.py – lines 73-77
if os.path.exists(cache_path):
return gp.read_file(cache_path)
GeoPandas reads the GeoJSON back into a functional GDF instantly, bypassing OSM's Overpass API entirely.
Integration with the Fetch Pipeline
The high-level get_gdfs() entry point coordinates cache lookups through unified_osm_request():
- Generate hash from perimeter + layer kwargs
- Attempt
read_from_cache()— return immediately on hit - On miss: query OSM, receive GDF, call
write_to_cache(), return result
While the current release has cache invocation commented in unified_osm_request() (lines 95-103), the helper functions remain fully functional and can be called directly or enabled with minor modifications.
Legacy Decorator Pattern
A commented cache_geometry decorator (lines 75-111 in fetch.py) demonstrates an alternative approach: wrapping any GDF-returning function to automatically handle cache read/write cycles. This pattern suits custom fetch pipelines or extended OSM query builders.
Performance Benefits of Caching GeoDataFrames
| Benefit | Mechanism | Typical Impact |
|---|---|---|
| Network elimination | Local file reads replace HTTP requests | Seconds to minutes → milliseconds |
| Reproducible builds | Identical inputs produce bit-identical outputs | CI pipelines succeed without connectivity |
| Concurrent safety | Stateless file reads impose no locks | Multi-process batch jobs scale linearly |
| Offline operation | Cache persistence enables disconnected work | Design iterations continue without internet |
Large perimeters with complex building footprints can trigger 30-60 second Overpass timeouts. A cached read completes in under 100ms regardless of geometry complexity.
Practical Code Examples
Basic Cached Workflow
This pattern handles first-time download and subsequent cache hits transparently:
from prettymaps import get_gdfs
layers = {
"streets": {"tags": {"highway": True}},
"building": {"tags": {"building": True}},
"green": {"tags": {"landuse": ["grass", "forest"]}},
}
# First execution: downloads from OSM, writes to cache
gdfs = get_gdfs(
query="Porto, Portugal",
layers_dict=layers,
radius=1500,
logging=True,
)
# Second execution: reads from prettymaps_cache/*.geojson
gdfs = get_gdfs(
query="Porto, Portugal",
layers_dict=layers,
radius=1500,
logging=True,
)
Custom Cache Directory
For shared team caches or managed storage locations:
from prettymaps.fetch import write_to_cache, read_from_cache
from shapely.geometry import Polygon
import geopandas as gpd
perimeter = gpd.GeoDataFrame(
geometry=[Polygon([
(-8.62, 41.15), (-8.62, 41.17),
(-8.60, 41.17), (-8.60, 41.15)
])],
crs="EPSG:4326",
)
layer_gdf = gpd.GeoDataFrame(
geometry=[Polygon([(-8.615, 41.155), (-8.61, 41.155), (-8.61, 41.16)])],
crs="EPSG:4326"
)
# Write to project-specific cache
write_to_cache(
perimeter,
layer_gdf,
{"tags": {"building": True}},
cache_dir="/shared/prettymaps_cache"
)
# Retrieve later or from another machine with access to same path
cached = read_from_cache(
perimeter,
{"tags": {"building": True}},
cache_dir="/shared/prettymaps_cache"
)
Key Source Files
Understanding the cache implementation requires familiarity with these modules:
prettymaps/fetch.py— Core caching logic withwrite_to_cache(),read_from_cache(), andunified_osm_request()according to the marceloprates/prettymaps source codeprettymaps/draw.py— Rendering engine that consumes cached or fresh GDFsprettymaps/utils.py— Supporting decorators including execution timing utilities
Summary
- Cache keys are deterministic hashes combining perimeter geometry and layer parameters, implemented in
prettymaps/fetch.py - GeoJSON serialization provides fast, interoperable storage without external dependencies
- Manual or automatic activation — call helpers directly or integrate with
get_gdfs()pipeline - Three primary use cases: iterative styling, batch map generation, and offline development
Frequently Asked Questions
How do I enable caching in the current prettymaps release?
The cache helper functions in prettymaps/fetch.py are fully implemented but not automatically invoked in recent versions. Import write_to_cache and read_from_cache directly, wrap your fetch calls, or uncomment the cache integration in unified_osm_request() for transparent operation.
What happens if OSM data updates while I have cached files?
Cached GeoDataFrames remain valid indefinitely. The cache has no TTL mechanism — you must manually delete prettymaps_cache/ entries or specific hash-named files to force re-fetch. For rapidly changing features like construction zones, implement timestamp-based cache invalidation or periodic manual clears.
Can multiple Python processes share the same cache directory safely?
Yes. The cache uses simple file existence checks and GeoPandas read operations without file locking. Concurrent reads of identical cache entries succeed without corruption. Simultaneous writes to the same hash (rare in practice due to deterministic keys) follow filesystem atomicity semantics.
Does caching reduce memory usage or only network traffic?
Caching primarily eliminates network I/O. Loaded GeoDataFrames occupy identical memory whether sourced from cache or fresh download. For memory-constrained workflows, consider GeoParquet export or geometry simplification before caching.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →