Here’s what I found Folium We build interactive maps in this tutorial that work with Colab and any Python local setup. Heatmaps are used to visualize spatial density, we explore different basemap styles and design HTML popups for richer markers. Then, we create region-level maps using GeoJSON. We scale up to thousands of locations with marker clustering and use a timestamped overlay. We combine USGS real-world earthquake data, layered heatmaps and legends with fullscreen controls, legends, and layered magnitude buckets to create a dashboard-like global monitoring tool.
import folium
From folium Import Plugins
HeatMap, MarkerCluster (TimestampedGeoJson), MiniMap, Draw or Fullscreen can be imported from folium.plugins.
import pandas as pd
Numpy can be imported as np
Import json
Import requests
Datetime can be imported as timedelta
Import branca.colormap in cm
print(f"Folium version: {folium.__version__}")
print("All imports successful!n")
Import all libraries required, including Folium, Pandas NumPy Requests, Folium plug-ins and Requests. By confirming that the Folium is the latest version and that all dependencies are loaded, we initialize our mapping workflow. This set-up establishes a technical foundation to build interactive maps and integrate external geospatial data sources.
def create_multi_tile_map():
"""Create a map with multiple tile layers"""
m = folium.Map(
location=[40.7128, -74.0060],
zoom_start=12,
tiles="OpenStreetMap"
)
folium.TileLayer('cartodbpositron', name="CartoDB Positron").add_to(m)
folium.TileLayer('cartodbdark_matter', name="CartoDB Dark Matter").add_to(m)
folium.TileLayer(
tiles="https://tiles.stadiamaps.com/tiles/stamen_terrain/{z}/{x}/{y}.png",
attr="Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL",
name="Terrain"
).add_to(m)
folium.TileLayer(
tiles="https://tiles.stadiamaps.com/tiles/stamen_toner/{z}/{x}/{y}.png",
attr="Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL",
name="Toner"
).add_to(m)
folium.TileLayer(
tiles="https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg",
attr="Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL",
name="Watercolor"
).add_to(m)
folium.LayerControl().add_to(m)
Return m
Create a base map with multiple layers and set up different tile providers for different visual styles. To switch between different perspectives, we add layers for terrain, darkness, toners, and watercolors. We include a panel to control the layer so we can toggle between map styles dynamically and better explore spatial information.
def create_advanced_markers_map():
"""Create map with custom markers and HTML popups"""
"Landmarks" = [
{'name': 'Statue of Liberty', 'lat': 40.6892, 'lon': -74.0445, 'type': 'monument', 'visitors': 4500000},
{'name': 'Empire State Building', 'lat': 40.7484, 'lon': -73.9857, 'type': 'building', 'visitors': 4000000},
{'name': 'Central Park', 'lat': 40.7829, 'lon': -73.9654, 'type': 'park', 'visitors': 42000000},
{'name': 'Brooklyn Bridge', 'lat': 40.7061, 'lon': -73.9969, 'type': 'bridge', 'visitors': 4000000},
{'name': 'Times Square', 'lat': 40.7580, 'lon': -73.9855, 'type': 'plaza', 'visitors': 50000000}
]
m = folium.Map(location=[40.7128, -74.0060], zoom_start=12)
icon_colors = {
'monument': 'red',
'building': 'blue',
'park': 'green',
'bridge': 'orange',
'plaza': 'purple'
}
icon_symbols = {
'monument': 'star',
'building': 'home',
'park': 'tree',
'bridge': 'road',
'plaza': 'shopping-cart'
}
For landmark in landmarks
HTML = F"""
{landmark['name']}
Type: {landmark['type'].title()}
Visitor Numbers: {landmark['visitors']:,}
"""
iframe=folium.IFrame (html, width=220 and height=250).
popup = folium.Popup(iframe, max_width=220)
folium.Marker(
location=[landmark['lat']The landmark['lon']],
popup=popup,
tooltip=landmark['name'],
icon=folium.Icon(
color=icon_colors[landmark['type']],
icon=icon_symbols[landmark['type']],
prefix='fa'
)
).add_to(m)
folium.CircleMarker(
location=[40.7128, -74.0060],
radius=20,
popup='NYC Center',
color="#3186cc",
fill=True,
fillColor="#3186cc",
fillOpacity=0.2
).add_to(m)
Return m
Our map is built with advanced marker and rich HTML popups that represent real world landmarks. To improve the visual clarity of the map and its semantic content, we customize markers, symbols, colors, etc. based on location type. We embed structured HTML into popups to present context-specific information within the interactive map.
Def Create_Heatmap():
"""Create a heatmap showing data density"""
np.random.seed(42)
n_incidents = 1000
crime_data = []
Hotspots = [
[40.7580, -73.9855],
[40.7484, -73.9857],
[40.7128, -74.0060],
]
For _, in range (n_incidents),:
The hotspot is also known as the hotspot[np.random.choice(len(hotspots))]
Lat = Hotspot[0] + np.random.normal(0, 0.02)
lon = hotspot[1] + np.random.normal(0, 0.02)
intensity = np.random.uniform(0.3, 1.0)
crime_data.append([lat, lon, intensity])
m = folium.Map(location=[40.7128, -74.0060], zoom_start=12)
HeatMap(
crime_data,
min_opacity=0.2,
max_zoom=18,
max_val=1.0,
radius=15,
blur=25,
gradient={
0.0: 'blue',
0.3: 'lime',
0.5: 'yellow',
0.7: 'orange',
1.0: 'red'
}
).add_to(m)
title_html=""'
Heatmap of NYC Crime Density
Simulation of incident data
'''
m.get_root().html.add_child(folium.Element(title_html))
Return m
We use synthetic spatial data to generate heatmaps that visualize patterns in density. In order to uncover spatial concentration patterns, we use gradient-based visualization of intensity and clustered coordinates. This density layer is superimposed on the map to reveal how different events are distributed across regions.
def create_choropleth_map():
"""Create a choropleth map showing data across regions"""
us_states_url="https://raw.githubusercontent.com/python-visualization/folium/master/examples/data/us-states.json"
try:
us_states = requests.get(us_states_url).json()
except:
print("Warning: Could not fetch GeoJSON data. Using offline sample.")
Return No
state_data = {
'Alabama': 5.1, 'Alaska': 6.3, 'Arizona': 4.7, 'Arkansas': 3.8, 'California': 5.3,
'Colorado': 3.9, 'Connecticut': 4.3, 'Delaware': 4.1, 'Florida': 3.6, 'Georgia': 4.0,
'Hawaii': 2.8, 'Idaho': 2.9, 'Illinois': 5.0, 'Indiana': 3.5, 'Iowa': 3.1,
'Kansas': 3.3, 'Kentucky': 4.3, 'Louisiana': 4.6, 'Maine': 3.2, 'Maryland': 4.0,
'Massachusetts': 3.6, 'Michigan': 4.3, 'Minnesota': 3.2, 'Mississippi': 5.2,
'Missouri': 3.7, 'Montana': 3.5, 'Nebraska': 2.9, 'Nevada': 4.8, 'New Hampshire': 2.7,
'New Jersey': 4.2, 'New Mexico': 5.0, 'New York': 4.5, 'North Carolina': 4.0,
'North Dakota': 2.6, 'Ohio': 4.2, 'Oklahoma': 3.4, 'Oregon': 4.2, 'Pennsylvania': 4.4,
'Rhode Island': 4.0, 'South Carolina': 3.5, 'South Dakota': 2.9, 'Tennessee': 3.6,
'Texas': 4.0, 'Utah': 2.8, 'Vermont': 2.8, 'Virginia': 3.3, 'Washington': 4.6,
'West Virginia': 5.1, 'Wisconsin': 3.4, 'Wyoming': 3.6
}
df = pd.DataFrame(list(state_data.items()), columns=['State', 'Unemployment'])
m = folium.Map(location=[37.8, -96], zoom_start=4)
folium.Choropleth(
geo_data=us_states,
name="choropleth",
data=df,
columns=['State', 'Unemployment'],
key_on='feature.properties.name',
fill_color="YlOrRd",
fill_opacity=0.7,
line_opacity=0.5,
legend_name="Unemployment Rate (%)"
).add_to(m)
style_function = lambda x: {Style_function = Lambda x 'fillColor: #fffffff,
'color':'#000000',
'fillOpacity': 0.1,
'weight': 0.1}
highlight_function = lambda x: {highlight_function = lambda'x': "fillColor: #000000'
'color':'#000000',
'fillOpacity': 0.50,
'weight': 0.1}
NIL = folium.features.GeoJson(
us_states,
style_function=style_function,
control=False,
highlight_function=highlight_function,
tooltip=folium.features.GeoJsonTooltip(
fields=['name'],
aliases=['State:'],
style=("background-color: white; color: #333333; font-family: arial; font-size: 12px; padding: 10px;")
)
)
m.add_child(NIL)
m.keep_in_front(NIL)
folium.LayerControl().add_to(m)
Return m
GeoJSON boundary information is combined with structured numerical attribute to create a Choropleth Map. Mapping unemployment rates onto geographic regions, we use gradients of color to show statistical differences. We can use tooltips and hover interactions to view data for specific regions directly on the map.
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 = random.randint(1), 100, location(n)
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)
Row in df_markers.iterrows for the idx():
If row['value']
Marker Clustering Demo
Zoom in to view individual markers
”’
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]
]
If i is (lat or lon) then enumerate the path.Meaning of feature:
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
Hurricane tracking simulation
You can play/pause the video by using the buttons below.
”’
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
Draw with the tools you have!’,
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”)
Only Exceptions as follows:
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]}
}
]
}
Seismological earthquakes []
For feature in earthquake_data[‘features’]:
Props = feature[‘properties’]
Coords = feature[‘geometry’][‘coordinates’]
earthquakes.append({
“Lat”: coordinates[1],
Coords[0],
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 (
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’]The 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’]The row number in df_eq.iterrows is ] for the idx.()]
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
● Modest (4.0 to 5.0)
● Strong (5.0-6.0).
● Major (≥ 6.0)
USGS data (last 30 days).
”’
m.get_root().html.add_child(folium.Element(legend_html))
title_html=””‘
🌍 Global Earthquake Monitor
Real-time earthquake data (M ≥ 2.5)
You can click on markers to see more details.| Toggle layers to explore
”’
m.get_root().html.add_child(folium.Element(title_html))
Fullscreen(position=’topright’).add_to(m)
Return m
If the __name__ equalsMaps = “__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)
Name map_obj when using maps.items():
If map_obj does not equal None, then:
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)

