How to Build Interactive Geospatial Dashboards Using Folium with Heatmaps, Choropleths, Time Animation, Marker Clustering, and Advanced Interactive Plugins

Binance
How to Build Interactive Geospatial Dashboards Using Folium with Heatmaps, Choropleths, Time Animation, Marker Clustering, and Advanced Interactive Plugins


Thank you for reading this post, don't forget to subscribe!
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=””‘
<div style=”position: fixed;
top: 10px; left: 50px; width: 350px; height: 60px;
background-color: white; border:2px solid grey; z-index:9999;
font-size:14px; padding: 10px”>
<h4 style=”margin: 0;”>Marker Clustering Demo</h4>
<p style=”margin: 5px 0 0 0; font-size: 12px;”>5000 markers – zoom to see individual points</p>
</div>
”’
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<br>Time: {timestamp.strftime(“%Y-%m-%d %H:%M”)}<br>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=””‘
<div style=”position: fixed;
top: 10px; left: 50px; width: 300px; height: 80px;
background-color: white; border:2px solid grey; z-index:9999;
font-size:14px; padding: 10px”>
<h4 style=”margin: 0;”>Hurricane Path Animation</h4>
<p style=”margin: 5px 0 0 0; font-size: 12px;”>Simulated hurricane tracking<br>
Use controls below to play/pause</p>
</div>
”’
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='<b>NYC</b><br>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=”https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_month.geojson”

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”””
<div style=”font-family: Arial; width: 250px;”>
<h4 style=”margin: 0; color: {color};”>Magnitude {mag:.1f}</h4>
<hr style=”margin: 5px 0;”>
<p><b>Location:</b> {eq[‘place’]}</p>
<p><b>Depth:</b> {eq[‘depth’]:.1f} km</p>
<p><b>Time:</b> {eq[‘time’].strftime(‘%Y-%m-%d %H:%M:%S’)}</p>
<p><b>Coordinates:</b> {eq[‘lat’]:.4f}, {eq[‘lon’]:.4f}</p>
</div>
“””

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=””‘
<div style=”position: fixed;
bottom: 50px; right: 50px; width: 200px; height: 180px;
background-color: white; border:2px solid grey; z-index:9999;
font-size:14px; padding: 10px; border-radius: 5px;”>
<h4 style=”margin: 0 0 10px 0;”>Earthquake Magnitude</h4>
<p style=”margin: 5px 0;”><span style=”color: green;”>●</span> Minor (< 4.0)</p>
<p style=”margin: 5px 0;”><span style=”color: yellow;”>●</span> Moderate (4.0-5.0)</p>
<p style=”margin: 5px 0;”><span style=”color: orange;”>●</span> Strong (5.0-6.0)</p>
<p style=”margin: 5px 0;”><span style=”color: red;”>●</span> Major (≥ 6.0)</p>
<hr style=”margin: 10px 0;”>
<p style=”margin: 5px 0; font-size: 11px;”>Data: USGS (Past 30 days)</p>
</div>
”’
m.get_root().html.add_child(folium.Element(legend_html))

title_html=””‘
<div style=”position: fixed;
top: 10px; left: 50px; width: 400px; height: 80px;
background-color: rgba(255, 255, 255, 0.95); border:2px solid grey; z-index:9999;
font-size:14px; padding: 10px; border-radius: 5px;”>
<h3 style=”margin: 0;”>🌍 Global Earthquake Monitor</h3>
<p style=”margin: 5px 0 0 0; font-size: 12px;”>
Real-time earthquake data (M ≥ 2.5)<br>
Click markers for details | Toggle layers to explore
</p>
</div>
”’
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)



Source link