Save Draw Data Example¶
This notebook demonstrates the new save_draw_data()
method in AnyMap, which allows you to save drawn features to various file formats supported by GeoPandas.
Installation Requirements¶
The save_draw_data
method requires GeoPandas to be installed:
pip install geopandas
Basic Usage¶
First, let's create a map and add a draw control:
from anymap.maplibre import MapLibreMap
import tempfile
import os
# Create a map centered on New York City
m = MapLibreMap(center=[-74.0, 40.7], zoom=10)
m.add_basemap("OpenStreetMap.Mapnik")
# Add a draw control so users can draw features
m.add_draw_control(
position="top-left",
controls={"point": True, "line_string": True, "polygon": True, "trash": True},
)
m
Adding Sample Data¶
Let's add some sample features for demonstration:
# Add some sample drawn features
sample_features = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {"name": "Central Park", "type": "park"},
"geometry": {"type": "Point", "coordinates": [-73.9665, 40.7812]},
},
{
"type": "Feature",
"properties": {"name": "Brooklyn Bridge", "type": "landmark"},
"geometry": {"type": "Point", "coordinates": [-73.9969, 40.7061]},
},
{
"type": "Feature",
"properties": {"name": "Manhattan Area", "type": "area"},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-74.02, 40.70],
[-73.93, 40.70],
[-73.93, 40.80],
[-74.02, 40.80],
[-74.02, 40.70],
]
],
},
},
],
}
# Load the sample data into the draw control
m.load_draw_data(sample_features)
print(f"Loaded {len(m.get_draw_data()['features'])} features")
Loaded 3 features
Saving to Different Formats¶
Now let's demonstrate saving the drawn data to various formats:
Save to GeoJSON¶
GeoJSON is the most common format for web mapping and supports all geometry types:
# Save to GeoJSON format
geojson_path = "/tmp/drawn_features.geojson"
m.save_draw_data(geojson_path)
print(f"✓ Saved to GeoJSON: {geojson_path}")
print(f"File size: {os.path.getsize(geojson_path)} bytes")
✓ Saved to GeoJSON: /tmp/drawn_features.geojson File size: 702 bytes
Save to GeoPackage¶
GeoPackage is a modern, standards-based format that supports mixed geometry types:
# Save to GeoPackage format
gpkg_path = "/tmp/drawn_features.gpkg"
m.save_draw_data(gpkg_path)
print(f"✓ Saved to GeoPackage: {gpkg_path}")
print(f"File size: {os.path.getsize(gpkg_path)} bytes")
✓ Saved to GeoPackage: /tmp/drawn_features.gpkg File size: 98304 bytes
Save to Shapefile (Points Only)¶
Shapefiles require all features to have the same geometry type. Let's create a map with only points:
# Create a new map with only point features
m_points = MapLibreMap(center=[-74.0, 40.7], zoom=10)
m_points.add_basemap("OpenStreetMap.Mapnik")
m_points.add_draw_control()
# Add only point features
point_features = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {"name": "Central Park", "type": "park"},
"geometry": {"type": "Point", "coordinates": [-73.9665, 40.7812]},
},
{
"type": "Feature",
"properties": {"name": "Brooklyn Bridge", "type": "landmark"},
"geometry": {"type": "Point", "coordinates": [-73.9969, 40.7061]},
},
],
}
m_points.load_draw_data(point_features)
# Now save to shapefile
shp_path = "/tmp/drawn_points.shp"
m_points.save_draw_data(shp_path)
print(f"✓ Saved points to Shapefile: {shp_path}")
print(
f"Files created: {[f for f in os.listdir('/tmp') if f.startswith('drawn_points.')]}"
)
m_points
✓ Saved points to Shapefile: /tmp/drawn_points.shp Files created: ['drawn_points.cpg', 'drawn_points.shp', 'drawn_points.prj', 'drawn_points.shx', 'drawn_points.dbf']
Error Handling¶
The save_draw_data
method provides helpful error messages for common issues:
Mixed Geometry Types in Shapefile¶
Let's see what happens when we try to save mixed geometry types to a shapefile:
# Try to save mixed geometry types to shapefile
try:
m.save_draw_data("/tmp/mixed_geometries.shp")
except ValueError as e:
print(f"Expected error: {e}")
Expected error: Cannot save mixed geometry types ['Point', 'Polygon'] to shapefile. Use GeoJSON (.geojson) or GeoPackage (.gpkg) format instead.
No Features to Save¶
Let's see what happens when there are no drawn features:
# Create a map with no drawn features
empty_map = MapLibreMap(center=[0, 0], zoom=2)
try:
empty_map.save_draw_data("/tmp/empty.geojson")
except ValueError as e:
print(f"Expected error: {e}")
Expected error: No drawn features to save
Loading Saved Data¶
You can load the saved data back into a map or use it with other geospatial tools:
import json
# Create a new map and load the saved GeoJSON data
m_new = MapLibreMap(center=[-74.0, 40.7], zoom=10)
m_new.add_basemap("OpenStreetMap.Mapnik")
m_new.add_draw_control()
# Load the saved data
with open(geojson_path, "r") as f:
saved_data = json.load(f)
m_new.load_draw_data(saved_data)
print(f"Loaded {len(m_new.get_draw_data()['features'])} features from saved file")
m_new
Loaded 3 features from saved file
Working with GeoPandas¶
Since the save method uses GeoPandas, you can also work directly with the data:
import geopandas as gpd
# Read the saved GeoJSON with GeoPandas
gdf = gpd.read_file(geojson_path)
print("GeoPandas DataFrame:")
print(gdf)
print(f"\nCRS: {gdf.crs}")
print(f"Geometry types: {gdf.geometry.geom_type.unique()}")
GeoPandas DataFrame: name type \ 0 Central Park park 1 Brooklyn Bridge landmark 2 Manhattan Area area geometry 0 POINT (-73.9665 40.7812) 1 POINT (-73.9969 40.7061) 2 POLYGON ((-74.02 40.7, -73.93 40.7, -73.93 40.... CRS: EPSG:4326 Geometry types: ['Point' 'Polygon']
Supported Formats¶
The save_draw_data
method supports all formats that GeoPandas can write to, including:
- GeoJSON (
.geojson
) - Best for web applications, supports all geometry types - GeoPackage (
.gpkg
) - Modern SQLite-based format, supports all geometry types - Shapefile (
.shp
) - Traditional GIS format, requires same geometry type for all features - KML (
.kml
) - Google Earth format - GML (
.gml
) - OGC Geography Markup Language - CSV (
.csv
) - Only for point data without geometry
You can also specify the driver explicitly:
m.save_draw_data("output.gpkg", driver="GPKG")
m.save_draw_data("output.shp", driver="ESRI Shapefile")
Cleanup¶
Clean up the temporary files:
# Clean up temporary files
import glob
for pattern in ["/tmp/drawn_features.*", "/tmp/drawn_points.*"]:
for file in glob.glob(pattern):
try:
os.remove(file)
print(f"Removed {file}")
except:
pass
Removed /tmp/drawn_features.gpkg Removed /tmp/drawn_features.geojson Removed /tmp/drawn_points.cpg Removed /tmp/drawn_points.shp Removed /tmp/drawn_points.prj Removed /tmp/drawn_points.shx Removed /tmp/drawn_points.dbf
Summary¶
The save_draw_data()
method provides a simple way to export drawn features from AnyMap to various geospatial file formats:
- Easy to use: Just call
map.save_draw_data("filename.geojson")
- Multiple formats: Supports all GeoPandas-compatible formats
- Error handling: Provides helpful error messages for common issues
- Format detection: Automatically detects format from file extension
- CRS handling: Automatically sets EPSG:4326 if no CRS is present
This makes it easy to integrate drawn features with other geospatial workflows and tools!