Technology & AI
How to Create Interactive Geospatial Dashboards Using Folium with Heatmaps, Choropleths, Time Animation, Marker Clustering, and Advanced Functionality Plugins

def create_marker_cluster_map():
"""Create a map with marker clustering for large datasets"""
np.random.seed(123)
n_locations = 5000
lats = np.random.uniform(25, 49, n_locations)
lons = np.random.uniform(-125, -65, n_locations)
values = np.random.randint(1, 100, n_locations)
df_markers = pd.DataFrame({
'lat': lats,
'lon': lons,
'value': values
})
m = folium.Map(location=[37.8, -96], zoom_start=4)
marker_cluster = MarkerCluster(
name="Location Cluster",
overlay=True,
control=True
).add_to(m)
for idx, row in df_markers.iterrows():
if row['value'] < 33:
color="green"
elif row['value'] < 66:
color="orange"
else:
color="red"
folium.Marker(
location=[row['lat'], row['lon']],
popup=f"Value: {row['value']}",
tooltip=f"Location {idx}",
icon=folium.Icon(color=color, icon='info-sign')
).add_to(marker_cluster)
folium.LayerControl().add_to(m)
title_html=""'
Marker Clustering Demo
5000 markers - zoom to see individual points
'''
m.get_root().html.add_child(folium.Element(title_html))
return m
def create_time_series_map():
"""Create an animated map showing data changes over time"""
start_date = datetime(2024, 8, 1)
features = []
path = [
[25.0, -70.0], [26.5, -72.0], [28.0, -74.5], [29.5, -76.5],
[31.0, -78.0], [32.5, -79.5], [34.0, -80.5], [35.5, -81.0]
]
for i, (lat, lon) in enumerate(path):
timestamp = start_date + timedelta(hours=i*6)
feature = {
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [lon, lat]
},
'properties': {
'time': timestamp.isoformat(),
'popup': f'Hurricane Position
Time: {timestamp.strftime("%Y-%m-%d %H:%M")}
Category: {min(5, i//2 + 1)}',
'icon': 'circle',
'iconstyle': {
'fillColor': ['yellow', 'orange', 'red', 'darkred', 'purple'][min(4, i//2)],
'fillOpacity': 0.8,
'stroke': 'true',
'radius': 8 + i * 2
}
}
}
features.append(feature)
m = folium.Map(
location=[30.0, -75.0],
zoom_start=5,
tiles="CartoDB Positron"
)
TimestampedGeoJson(
{'type': 'FeatureCollection', 'features': features},
period='PT6H',
add_last_point=True,
auto_play=True,
loop=True,
max_speed=2,
loop_button=True,
date_options="YYYY-MM-DD HH:mm",
time_slider_drag_update=True
).add_to(m)
title_html=""'
Hurricane Path Animation
Simulated hurricane tracking
Use controls below to play/pause
'''
m.get_root().html.add_child(folium.Element(title_html))
return m
def create_interactive_plugins_map():
"""Create a map with multiple interactive plugins"""
m = folium.Map(
location=[40.7128, -74.0060],
zoom_start=12,
tiles="OpenStreetMap"
)
minimap = MiniMap(toggle_display=True)
m.add_child(minimap)
draw = Draw(
export=True,
filename="drawn_shapes.geojson",
position='topleft',
draw_options={
'polyline': True,
'polygon': True,
'circle': True,
'rectangle': True,
'marker': True,
'circlemarker': True
},
edit_options={'edit': True}
)
m.add_child(draw)
Fullscreen(
position='topright',
title="Expand map",
title_cancel="Exit fullscreen",
force_separate_button=True
).add_to(m)
plugins.MeasureControl(
position='bottomleft',
primary_length_unit="kilometers",
secondary_length_unit="miles",
primary_area_unit="sqkilometers",
secondary_area_unit="acres"
).add_to(m)
plugins.MousePosition(
position='bottomright',
separator=" | ",
empty_string='NaN',
lng_first=True,
num_digits=20,
prefix='Coordinates:',
).add_to(m)
plugins.LocateControl(
auto_start=False,
position='topleft'
).add_to(m)
folium.Marker(
[40.7128, -74.0060],
popup='NYC
Try the drawing tools!',
icon=folium.Icon(color="red", icon='info-sign')
).add_to(m)
return m
def create_earthquake_map():
"""Create comprehensive earthquake visualization using real USGS data"""
url="
try:
response = requests.get(url)
earthquake_data = response.json()
print(f"Successfully loaded {len(earthquake_data['features'])} earthquakes")
except Exception as e:
print(f"Error fetching data: {e}")
earthquake_data = {
'features': [
{
'properties': {'mag': 5.2, 'place': 'Sample Location 1', 'time': 1640000000000},
'geometry': {'coordinates': [-122.0, 37.0, 10]}
},
{
'properties': {'mag': 6.1, 'place': 'Sample Location 2', 'time': 1640100000000},
'geometry': {'coordinates': [140.0, 35.0, 20]}
}
]
}
earthquakes = []
for feature in earthquake_data['features']:
props = feature['properties']
coords = feature['geometry']['coordinates']
earthquakes.append({
'lat': coords[1],
'lon': coords[0],
'depth': coords[2],
'magnitude': props.get('mag', 0),
'place': props.get('place', 'Unknown'),
'time': datetime.fromtimestamp(props.get('time', 0) / 1000)
})
df_eq = pd.DataFrame(earthquakes)
print(f"nEarthquake Statistics:")
print(f"Total earthquakes: {len(df_eq)}")
print(f"Magnitude range: {df_eq['magnitude'].min():.1f} - {df_eq['magnitude'].max():.1f}")
print(f"Depth range: {df_eq['depth'].min():.1f} - {df_eq['depth'].max():.1f} km")
m = folium.Map(
location=[20, 0],
zoom_start=2,
tiles="CartoDB dark_matter"
)
minor = folium.FeatureGroup(name="Minor (< 4.0)")
moderate = folium.FeatureGroup(name="Moderate (4.0-5.0)")
strong = folium.FeatureGroup(name="Strong (5.0-6.0)")
major = folium.FeatureGroup(name="Major (≥ 6.0)")
for idx, eq in df_eq.iterrows():
mag = eq['magnitude']
if mag < 4.0:
color="green"
radius = 3
group = minor
elif mag < 5.0:
color="yellow"
radius = 6
group = moderate
elif mag < 6.0:
color="orange"
radius = 9
group = strong
else:
color="red"
radius = 12
group = major
popup_html = f"""
Magnitude {mag:.1f}
Location: {eq['place']}
Depth: {eq['depth']:.1f} km
Time: {eq['time'].strftime('%Y-%m-%d %H:%M:%S')}
Coordinates: {eq['lat']:.4f}, {eq['lon']:.4f}
"""
folium.CircleMarker(
location=[eq['lat'], eq['lon']],
radius=radius,
popup=folium.Popup(popup_html, max_width=270),
tooltip=f"M{mag:.1f} - {eq['place']}",
color=color,
fill=True,
fillColor=color,
fillOpacity=0.7,
weight=2
).add_to(group)
minor.add_to(m)
moderate.add_to(m)
strong.add_to(m)
major.add_to(m)
heat_data = [[row['lat'], row['lon'], row['magnitude']] for idx, row in df_eq.iterrows()]
heatmap = folium.FeatureGroup(name="Density Heatmap", show=False)
HeatMap(
heat_data,
min_opacity=0.3,
radius=15,
blur=20,
gradient={0.4: 'blue', 0.6: 'cyan', 0.7: 'lime', 0.8: 'yellow', 1: 'red'}
).add_to(heatmap)
heatmap.add_to(m)
folium.LayerControl(position='topright', collapsed=False).add_to(m)
legend_html=""'
Earthquake Magnitude
● Minor (< 4.0)
● Moderate (4.0-5.0)
● Strong (5.0-6.0)
● Major (≥ 6.0)
Data: USGS (Past 30 days)
'''
m.get_root().html.add_child(folium.Element(legend_html))
title_html=""'
🌍 Global Earthquake Monitor
Real-time earthquake data (M ≥ 2.5)
Click markers for details | Toggle layers to explore
'''
m.get_root().html.add_child(folium.Element(title_html))
Fullscreen(position='topright').add_to(m)
return m
if __name__ == "__main__":
print("=" * 80)
print("ADVANCED FOLIUM TUTORIAL - ALL EXAMPLES")
print("=" * 80)
print("nGenerating all maps...n")
maps = {
'multi_tile_map': create_multi_tile_map(),
'advanced_markers_map': create_advanced_markers_map(),
'heatmap': create_heatmap(),
'choropleth_map': create_choropleth_map(),
'marker_cluster_map': create_marker_cluster_map(),
'time_series_map': create_time_series_map(),
'interactive_plugins_map': create_interactive_plugins_map(),
'earthquake_map': create_earthquake_map()
}
print("n" + "=" * 80)
print("SAVING MAPS TO HTML FILES")
print("=" * 80)
for name, map_obj in maps.items():
if map_obj is not None:
filename = f"{name}.html"
map_obj.save(filename)
print(f"✓ Saved: {filename}")
else:
print(f"✗ Skipped: {name} (map generation failed)")
print("n" + "=" * 80)
print("ALL MAPS GENERATED SUCCESSFULLY!")
print("=" * 80)
print("nYou can now:")
print("1. Open any HTML file in your browser to view the interactive map")
print("2. Access the map objects in code using the 'maps' dictionary")
print("3. Display maps in Jupyter/Colab by returning the map object")
print("nExample: To display the earthquake map in a notebook, just run:")
print(" maps['earthquake_map']")
print("n" + "=" * 80)


