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")
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")
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")
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
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}")
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}")
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
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()}")
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
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!