{"id":486,"date":"2026-02-28T07:56:00","date_gmt":"2026-02-27T23:56:00","guid":{"rendered":"https:\/\/connectword.dpdns.org\/?p=486"},"modified":"2026-02-28T07:56:00","modified_gmt":"2026-02-27T23:56:00","slug":"how-to-build-interactive-geospatial-dashboards-using-folium-with-heatmaps-choropleths-time-animation-marker-clustering-and-advanced-interactive-plugins","status":"publish","type":"post","link":"https:\/\/connectword.dpdns.org\/?p=486","title":{"rendered":"How to Build Interactive Geospatial Dashboards Using Folium with Heatmaps, Choropleths, Time Animation, Marker Clustering, and Advanced Interactive Plugins"},"content":{"rendered":"<p>In this <a href=\"https:\/\/github.com\/python-visualization\/folium\"><strong>Folium<\/strong><\/a> tutorial, we build a complete set of interactive maps that run in Colab or any local Python setup. We explore multiple basemap styles, design rich markers with HTML popups, and visualize spatial density using heatmaps. We also create region-level choropleth maps from GeoJSON, scale to thousands of points using marker clustering, and animate time-based movement with a timestamped layer. Finally, we combine real-world USGS earthquake data with layered magnitude buckets, density heatmaps, legends, and fullscreen controls to produce a practical, dashboard-like global monitor.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">import folium\nfrom folium import plugins\nfrom folium.plugins import HeatMap, MarkerCluster, TimestampedGeoJson, MiniMap, Draw, Fullscreen\nimport pandas as pd\nimport numpy as np\nimport json\nimport requests\nfrom datetime import datetime, timedelta\nimport branca.colormap as cm\n\n\nprint(f\"Folium version: {folium.__version__}\")\nprint(\"All imports successful!n\")<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We import all required libraries, such as Folium, Pandas, NumPy, Requests, and Folium plugins to prepare our geospatial environment. We initialize the mapping workflow by confirming the Folium version and ensuring that all dependencies load successfully. This setup establishes the technical foundation for building interactive maps, processing data, and integrating external geospatial sources.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">def create_multi_tile_map():\n   \"\"\"Create a map with multiple tile layers\"\"\"\n   m = folium.Map(\n       location=[40.7128, -74.0060],\n       zoom_start=12,\n       tiles='OpenStreetMap'\n   )\n  \n   folium.TileLayer('cartodbpositron', name='CartoDB Positron').add_to(m)\n   folium.TileLayer('cartodbdark_matter', name='CartoDB Dark Matter').add_to(m)\n  \n   folium.TileLayer(\n       tiles='https:\/\/tiles.stadiamaps.com\/tiles\/stamen_terrain\/{z}\/{x}\/{y}.png',\n       attr='Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL',\n       name='Terrain'\n   ).add_to(m)\n  \n   folium.TileLayer(\n       tiles='https:\/\/tiles.stadiamaps.com\/tiles\/stamen_toner\/{z}\/{x}\/{y}.png',\n       attr='Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL',\n       name='Toner'\n   ).add_to(m)\n  \n   folium.TileLayer(\n       tiles='https:\/\/tiles.stadiamaps.com\/tiles\/stamen_watercolor\/{z}\/{x}\/{y}.jpg',\n       attr='Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under ODbL',\n       name='Watercolor'\n   ).add_to(m)\n  \n   folium.LayerControl().add_to(m)\n  \n   return m<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We create a multi-layer base map and configure multiple tile providers to enable different visual styles. We add terrain, dark mode, toner, and watercolor layers so we can switch perspectives based on the analytical requirements. By including a layer control panel, we can dynamically toggle map styles and explore spatial data more effectively.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">def create_advanced_markers_map():\n   \"\"\"Create map with custom markers and HTML popups\"\"\"\n   landmarks = [\n       {'name': 'Statue of Liberty', 'lat': 40.6892, 'lon': -74.0445, 'type': 'monument', 'visitors': 4500000},\n       {'name': 'Empire State Building', 'lat': 40.7484, 'lon': -73.9857, 'type': 'building', 'visitors': 4000000},\n       {'name': 'Central Park', 'lat': 40.7829, 'lon': -73.9654, 'type': 'park', 'visitors': 42000000},\n       {'name': 'Brooklyn Bridge', 'lat': 40.7061, 'lon': -73.9969, 'type': 'bridge', 'visitors': 4000000},\n       {'name': 'Times Square', 'lat': 40.7580, 'lon': -73.9855, 'type': 'plaza', 'visitors': 50000000}\n   ]\n  \n   m = folium.Map(location=[40.7128, -74.0060], zoom_start=12)\n  \n   icon_colors = {\n       'monument': 'red',\n       'building': 'blue',\n       'park': 'green',\n       'bridge': 'orange',\n       'plaza': 'purple'\n   }\n  \n   icon_symbols = {\n       'monument': 'star',\n       'building': 'home',\n       'park': 'tree',\n       'bridge': 'road',\n       'plaza': 'shopping-cart'\n   }\n  \n   for landmark in landmarks:\n       html = f\"\"\"\n       &lt;div style=\"font-family: Arial; width: 200px;\"&gt;\n           &lt;h4 style=\"color: {icon_colors[landmark['type']]};\"&gt;{landmark['name']}&lt;\/h4&gt;\n           &lt;hr style=\"margin: 5px 0;\"&gt;\n           &lt;p&gt;&lt;b&gt;Type:&lt;\/b&gt; {landmark['type'].title()}&lt;\/p&gt;\n           &lt;p&gt;&lt;b&gt;Annual Visitors:&lt;\/b&gt; {landmark['visitors']:,}&lt;\/p&gt;\n           &lt;img src=\"https:\/\/via.placeholder.com\/180x100?text={landmark['name'].replace(' ', '+')}\"\n                style=\"width: 100%; border-radius: 5px;\"&gt;\n       &lt;\/div&gt;\n       \"\"\"\n      \n       iframe = folium.IFrame(html, width=220, height=250)\n       popup = folium.Popup(iframe, max_width=220)\n      \n       folium.Marker(\n           location=[landmark['lat'], landmark['lon']],\n           popup=popup,\n           tooltip=landmark['name'],\n           icon=folium.Icon(\n               color=icon_colors[landmark['type']],\n               icon=icon_symbols[landmark['type']],\n               prefix='fa'\n           )\n       ).add_to(m)\n  \n   folium.CircleMarker(\n       location=[40.7128, -74.0060],\n       radius=20,\n       popup='NYC Center',\n       color='#3186cc',\n       fill=True,\n       fillColor='#3186cc',\n       fillOpacity=0.2\n   ).add_to(m)\n  \n   return m<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We build a map with advanced markers and rich HTML popups to represent real-world landmarks. We customize marker icons, colors, and symbols by location type to enhance visual clarity and semantic meaning. By embedding structured HTML content inside popups, we present detailed contextual information directly within the interactive map.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">def create_heatmap():\n   \"\"\"Create a heatmap showing data density\"\"\"\n   np.random.seed(42)\n   n_incidents = 1000\n  \n   crime_data = []\n   hotspots = [\n       [40.7580, -73.9855],\n       [40.7484, -73.9857],\n       [40.7128, -74.0060],\n   ]\n  \n   for _ in range(n_incidents):\n       hotspot = hotspots[np.random.choice(len(hotspots))]\n       lat = hotspot[0] + np.random.normal(0, 0.02)\n       lon = hotspot[1] + np.random.normal(0, 0.02)\n       intensity = np.random.uniform(0.3, 1.0)\n       crime_data.append([lat, lon, intensity])\n  \n   m = folium.Map(location=[40.7128, -74.0060], zoom_start=12)\n  \n   HeatMap(\n       crime_data,\n       min_opacity=0.2,\n       max_zoom=18,\n       max_val=1.0,\n       radius=15,\n       blur=25,\n       gradient={\n           0.0: 'blue',\n           0.3: 'lime',\n           0.5: 'yellow',\n           0.7: 'orange',\n           1.0: 'red'\n       }\n   ).add_to(m)\n  \n   title_html = '''\n                &lt;div style=\"position: fixed;\n                            top: 10px; left: 50px; width: 300px; height: 60px;\n                            background-color: white; border:2px solid grey; z-index:9999;\n                            font-size:16px; padding: 10px\"&gt;\n                &lt;h4 style=\"margin: 0;\"&gt;NYC Crime Density Heatmap&lt;\/h4&gt;\n                &lt;p style=\"margin: 5px 0 0 0; font-size: 12px;\"&gt;Simulated incident data&lt;\/p&gt;\n                &lt;\/div&gt;\n                '''\n   m.get_root().html.add_child(folium.Element(title_html))\n  \n   return m<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We generate synthetic spatial data and use a heatmap to visualize density patterns across geographic locations. We simulate clustered coordinates and apply gradient-based intensity visualization to reveal spatial concentration trends. By overlaying this density layer on the map, we gain insight into how events distribute across regions.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">def create_choropleth_map():\n   \"\"\"Create a choropleth map showing data across regions\"\"\"\n   us_states_url = 'https:\/\/raw.githubusercontent.com\/python-visualization\/folium\/master\/examples\/data\/us-states.json'\n  \n   try:\n       us_states = requests.get(us_states_url).json()\n   except:\n       print(\"Warning: Could not fetch GeoJSON data. Using offline sample.\")\n       return None\n  \n   state_data = {\n       'Alabama': 5.1, 'Alaska': 6.3, 'Arizona': 4.7, 'Arkansas': 3.8, 'California': 5.3,\n       'Colorado': 3.9, 'Connecticut': 4.3, 'Delaware': 4.1, 'Florida': 3.6, 'Georgia': 4.0,\n       'Hawaii': 2.8, 'Idaho': 2.9, 'Illinois': 5.0, 'Indiana': 3.5, 'Iowa': 3.1,\n       'Kansas': 3.3, 'Kentucky': 4.3, 'Louisiana': 4.6, 'Maine': 3.2, 'Maryland': 4.0,\n       'Massachusetts': 3.6, 'Michigan': 4.3, 'Minnesota': 3.2, 'Mississippi': 5.2,\n       'Missouri': 3.7, 'Montana': 3.5, 'Nebraska': 2.9, 'Nevada': 4.8, 'New Hampshire': 2.7,\n       'New Jersey': 4.2, 'New Mexico': 5.0, 'New York': 4.5, 'North Carolina': 4.0,\n       'North Dakota': 2.6, 'Ohio': 4.2, 'Oklahoma': 3.4, 'Oregon': 4.2, 'Pennsylvania': 4.4,\n       'Rhode Island': 4.0, 'South Carolina': 3.5, 'South Dakota': 2.9, 'Tennessee': 3.6,\n       'Texas': 4.0, 'Utah': 2.8, 'Vermont': 2.8, 'Virginia': 3.3, 'Washington': 4.6,\n       'West Virginia': 5.1, 'Wisconsin': 3.4, 'Wyoming': 3.6\n   }\n  \n   df = pd.DataFrame(list(state_data.items()), columns=['State', 'Unemployment'])\n  \n   m = folium.Map(location=[37.8, -96], zoom_start=4)\n  \n   folium.Choropleth(\n       geo_data=us_states,\n       name='choropleth',\n       data=df,\n       columns=['State', 'Unemployment'],\n       key_on='feature.properties.name',\n       fill_color='YlOrRd',\n       fill_opacity=0.7,\n       line_opacity=0.5,\n       legend_name='Unemployment Rate (%)'\n   ).add_to(m)\n  \n   style_function = lambda x: {'fillColor': '#ffffff',\n                               'color':'#000000',\n                               'fillOpacity': 0.1,\n                               'weight': 0.1}\n   highlight_function = lambda x: {'fillColor': '#000000',\n                                   'color':'#000000',\n                                   'fillOpacity': 0.50,\n                                   'weight': 0.1}\n  \n   NIL = folium.features.GeoJson(\n       us_states,\n       style_function=style_function,\n       control=False,\n       highlight_function=highlight_function,\n       tooltip=folium.features.GeoJsonTooltip(\n           fields=['name'],\n           aliases=['State:'],\n           style=(\"background-color: white; color: #333333; font-family: arial; font-size: 12px; padding: 10px;\")\n       )\n   )\n   m.add_child(NIL)\n   m.keep_in_front(NIL)\n  \n   folium.LayerControl().add_to(m)\n  \n   return m<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We create a choropleth map by combining GeoJSON boundary data with structured numerical attributes. We map unemployment rates to geographic regions and use color gradients to visually represent statistical differences. By enabling hover interactions and tooltips, we can explore region-specific data directly within the map interface.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">def create_marker_cluster_map():\n   \"\"\"Create a map with marker clustering for large datasets\"\"\"\n   np.random.seed(123)\n   n_locations = 5000\n  \n   lats = np.random.uniform(25, 49, n_locations)\n   lons = np.random.uniform(-125, -65, n_locations)\n   values = np.random.randint(1, 100, n_locations)\n  \n   df_markers = pd.DataFrame({\n       'lat': lats,\n       'lon': lons,\n       'value': values\n   })\n  \n   m = folium.Map(location=[37.8, -96], zoom_start=4)\n  \n   marker_cluster = MarkerCluster(\n       name='Location Cluster',\n       overlay=True,\n       control=True\n   ).add_to(m)\n  \n   for idx, row in df_markers.iterrows():\n       if row['value'] &lt; 33:\n           color = 'green'\n       elif row['value'] &lt; 66:\n           color = 'orange'\n       else:\n           color = 'red'\n      \n       folium.Marker(\n           location=[row['lat'], row['lon']],\n           popup=f\"Value: {row['value']}\",\n           tooltip=f\"Location {idx}\",\n           icon=folium.Icon(color=color, icon='info-sign')\n       ).add_to(marker_cluster)\n  \n   folium.LayerControl().add_to(m)\n  \n   title_html = '''\n                &lt;div style=\"position: fixed;\n                            top: 10px; left: 50px; width: 350px; height: 60px;\n                            background-color: white; border:2px solid grey; z-index:9999;\n                            font-size:14px; padding: 10px\"&gt;\n                &lt;h4 style=\"margin: 0;\"&gt;Marker Clustering Demo&lt;\/h4&gt;\n                &lt;p style=\"margin: 5px 0 0 0; font-size: 12px;\"&gt;5000 markers - zoom to see individual points&lt;\/p&gt;\n                &lt;\/div&gt;\n                '''\n   m.get_root().html.add_child(folium.Element(title_html))\n  \n   return m\n\n\ndef create_time_series_map():\n   \"\"\"Create an animated map showing data changes over time\"\"\"\n   start_date = datetime(2024, 8, 1)\n   features = []\n  \n   path = [\n       [25.0, -70.0], [26.5, -72.0], [28.0, -74.5], [29.5, -76.5],\n       [31.0, -78.0], [32.5, -79.5], [34.0, -80.5], [35.5, -81.0]\n   ]\n  \n   for i, (lat, lon) in enumerate(path):\n       timestamp = start_date + timedelta(hours=i*6)\n      \n       feature = {\n           'type': 'Feature',\n           'geometry': {\n               'type': 'Point',\n               'coordinates': [lon, lat]\n           },\n           'properties': {\n               'time': timestamp.isoformat(),\n               'popup': f'Hurricane Position&lt;br&gt;Time: {timestamp.strftime(\"%Y-%m-%d %H:%M\")}&lt;br&gt;Category: {min(5, i\/\/2 + 1)}',\n               'icon': 'circle',\n               'iconstyle': {\n                   'fillColor': ['yellow', 'orange', 'red', 'darkred', 'purple'][min(4, i\/\/2)],\n                   'fillOpacity': 0.8,\n                   'stroke': 'true',\n                   'radius': 8 + i * 2\n               }\n           }\n       }\n       features.append(feature)\n  \n   m = folium.Map(\n       location=[30.0, -75.0],\n       zoom_start=5,\n       tiles='CartoDB Positron'\n   )\n  \n   TimestampedGeoJson(\n       {'type': 'FeatureCollection', 'features': features},\n       period='PT6H',\n       add_last_point=True,\n       auto_play=True,\n       loop=True,\n       max_speed=2,\n       loop_button=True,\n       date_options='YYYY-MM-DD HH:mm',\n       time_slider_drag_update=True\n   ).add_to(m)\n  \n   title_html = '''\n                &lt;div style=\"position: fixed;\n                            top: 10px; left: 50px; width: 300px; height: 80px;\n                            background-color: white; border:2px solid grey; z-index:9999;\n                            font-size:14px; padding: 10px\"&gt;\n                &lt;h4 style=\"margin: 0;\"&gt;Hurricane Path Animation&lt;\/h4&gt;\n                &lt;p style=\"margin: 5px 0 0 0; font-size: 12px;\"&gt;Simulated hurricane tracking&lt;br&gt;\n                Use controls below to play\/pause&lt;\/p&gt;\n                &lt;\/div&gt;\n                '''\n   m.get_root().html.add_child(folium.Element(title_html))\n  \n   return m\n\n\ndef create_interactive_plugins_map():\n   \"\"\"Create a map with multiple interactive plugins\"\"\"\n   m = folium.Map(\n       location=[40.7128, -74.0060],\n       zoom_start=12,\n       tiles='OpenStreetMap'\n   )\n  \n   minimap = MiniMap(toggle_display=True)\n   m.add_child(minimap)\n  \n   draw = Draw(\n       export=True,\n       filename='drawn_shapes.geojson',\n       position='topleft',\n       draw_options={\n           'polyline': True,\n           'polygon': True,\n           'circle': True,\n           'rectangle': True,\n           'marker': True,\n           'circlemarker': True\n       },\n       edit_options={'edit': True}\n   )\n   m.add_child(draw)\n  \n   Fullscreen(\n       position='topright',\n       title='Expand map',\n       title_cancel='Exit fullscreen',\n       force_separate_button=True\n   ).add_to(m)\n  \n   plugins.MeasureControl(\n       position='bottomleft',\n       primary_length_unit='kilometers',\n       secondary_length_unit='miles',\n       primary_area_unit='sqkilometers',\n       secondary_area_unit='acres'\n   ).add_to(m)\n  \n   plugins.MousePosition(\n       position='bottomright',\n       separator=' | ',\n       empty_string='NaN',\n       lng_first=True,\n       num_digits=20,\n       prefix='Coordinates:',\n   ).add_to(m)\n  \n   plugins.LocateControl(\n       auto_start=False,\n       position='topleft'\n   ).add_to(m)\n  \n   folium.Marker(\n       [40.7128, -74.0060],\n       popup='&lt;b&gt;NYC&lt;\/b&gt;&lt;br&gt;Try the drawing tools!',\n       icon=folium.Icon(color='red', icon='info-sign')\n   ).add_to(m)\n  \n   return m\n\n\ndef create_earthquake_map():\n   \"\"\"Create comprehensive earthquake visualization using real USGS data\"\"\"\n   url = 'https:\/\/earthquake.usgs.gov\/earthquakes\/feed\/v1.0\/summary\/2.5_month.geojson'\n  \n   try:\n       response = requests.get(url)\n       earthquake_data = response.json()\n       print(f\"Successfully loaded {len(earthquake_data['features'])} earthquakes\")\n   except Exception as e:\n       print(f\"Error fetching data: {e}\")\n       earthquake_data = {\n           'features': [\n               {\n                   'properties': {'mag': 5.2, 'place': 'Sample Location 1', 'time': 1640000000000},\n                   'geometry': {'coordinates': [-122.0, 37.0, 10]}\n               },\n               {\n                   'properties': {'mag': 6.1, 'place': 'Sample Location 2', 'time': 1640100000000},\n                   'geometry': {'coordinates': [140.0, 35.0, 20]}\n               }\n           ]\n       }\n  \n   earthquakes = []\n   for feature in earthquake_data['features']:\n       props = feature['properties']\n       coords = feature['geometry']['coordinates']\n      \n       earthquakes.append({\n           'lat': coords[1],\n           'lon': coords[0],\n           'depth': coords[2],\n           'magnitude': props.get('mag', 0),\n           'place': props.get('place', 'Unknown'),\n           'time': datetime.fromtimestamp(props.get('time', 0) \/ 1000)\n       })\n  \n   df_eq = pd.DataFrame(earthquakes)\n  \n   print(f\"nEarthquake Statistics:\")\n   print(f\"Total earthquakes: {len(df_eq)}\")\n   print(f\"Magnitude range: {df_eq['magnitude'].min():.1f} - {df_eq['magnitude'].max():.1f}\")\n   print(f\"Depth range: {df_eq['depth'].min():.1f} - {df_eq['depth'].max():.1f} km\")\n  \n   m = folium.Map(\n       location=[20, 0],\n       zoom_start=2,\n       tiles='CartoDB dark_matter'\n   )\n  \n   minor = folium.FeatureGroup(name='Minor (&lt; 4.0)')\n   moderate = folium.FeatureGroup(name='Moderate (4.0-5.0)')\n   strong = folium.FeatureGroup(name='Strong (5.0-6.0)')\n   major = folium.FeatureGroup(name='Major (\u2265 6.0)')\n  \n   for idx, eq in df_eq.iterrows():\n       mag = eq['magnitude']\n      \n       if mag &lt; 4.0:\n           color = 'green'\n           radius = 3\n           group = minor\n       elif mag &lt; 5.0:\n           color = 'yellow'\n           radius = 6\n           group = moderate\n       elif mag &lt; 6.0:\n           color = 'orange'\n           radius = 9\n           group = strong\n       else:\n           color = 'red'\n           radius = 12\n           group = major\n      \n       popup_html = f\"\"\"\n       &lt;div style=\"font-family: Arial; width: 250px;\"&gt;\n           &lt;h4 style=\"margin: 0; color: {color};\"&gt;Magnitude {mag:.1f}&lt;\/h4&gt;\n           &lt;hr style=\"margin: 5px 0;\"&gt;\n           &lt;p&gt;&lt;b&gt;Location:&lt;\/b&gt; {eq['place']}&lt;\/p&gt;\n           &lt;p&gt;&lt;b&gt;Depth:&lt;\/b&gt; {eq['depth']:.1f} km&lt;\/p&gt;\n           &lt;p&gt;&lt;b&gt;Time:&lt;\/b&gt; {eq['time'].strftime('%Y-%m-%d %H:%M:%S')}&lt;\/p&gt;\n           &lt;p&gt;&lt;b&gt;Coordinates:&lt;\/b&gt; {eq['lat']:.4f}, {eq['lon']:.4f}&lt;\/p&gt;\n       &lt;\/div&gt;\n       \"\"\"\n      \n       folium.CircleMarker(\n           location=[eq['lat'], eq['lon']],\n           radius=radius,\n           popup=folium.Popup(popup_html, max_width=270),\n           tooltip=f\"M{mag:.1f} - {eq['place']}\",\n           color=color,\n           fill=True,\n           fillColor=color,\n           fillOpacity=0.7,\n           weight=2\n       ).add_to(group)\n  \n   minor.add_to(m)\n   moderate.add_to(m)\n   strong.add_to(m)\n   major.add_to(m)\n  \n   heat_data = [[row['lat'], row['lon'], row['magnitude']] for idx, row in df_eq.iterrows()]\n   heatmap = folium.FeatureGroup(name='Density Heatmap', show=False)\n   HeatMap(\n       heat_data,\n       min_opacity=0.3,\n       radius=15,\n       blur=20,\n       gradient={0.4: 'blue', 0.6: 'cyan', 0.7: 'lime', 0.8: 'yellow', 1: 'red'}\n   ).add_to(heatmap)\n   heatmap.add_to(m)\n  \n   folium.LayerControl(position='topright', collapsed=False).add_to(m)\n  \n   legend_html = '''\n   &lt;div style=\"position: fixed;\n               bottom: 50px; right: 50px; width: 200px; height: 180px;\n               background-color: white; border:2px solid grey; z-index:9999;\n               font-size:14px; padding: 10px; border-radius: 5px;\"&gt;\n       &lt;h4 style=\"margin: 0 0 10px 0;\"&gt;Earthquake Magnitude&lt;\/h4&gt;\n       &lt;p style=\"margin: 5px 0;\"&gt;&lt;span style=\"color: green;\"&gt;\u25cf&lt;\/span&gt; Minor (&lt; 4.0)&lt;\/p&gt;\n       &lt;p style=\"margin: 5px 0;\"&gt;&lt;span style=\"color: yellow;\"&gt;\u25cf&lt;\/span&gt; Moderate (4.0-5.0)&lt;\/p&gt;\n       &lt;p style=\"margin: 5px 0;\"&gt;&lt;span style=\"color: orange;\"&gt;\u25cf&lt;\/span&gt; Strong (5.0-6.0)&lt;\/p&gt;\n       &lt;p style=\"margin: 5px 0;\"&gt;&lt;span style=\"color: red;\"&gt;\u25cf&lt;\/span&gt; Major (\u2265 6.0)&lt;\/p&gt;\n       &lt;hr style=\"margin: 10px 0;\"&gt;\n       &lt;p style=\"margin: 5px 0; font-size: 11px;\"&gt;Data: USGS (Past 30 days)&lt;\/p&gt;\n   &lt;\/div&gt;\n   '''\n   m.get_root().html.add_child(folium.Element(legend_html))\n  \n   title_html = '''\n   &lt;div style=\"position: fixed;\n               top: 10px; left: 50px; width: 400px; height: 80px;\n               background-color: rgba(255, 255, 255, 0.95); border:2px solid grey; z-index:9999;\n               font-size:14px; padding: 10px; border-radius: 5px;\"&gt;\n       &lt;h3 style=\"margin: 0;\"&gt;<img decoding=\"async\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/17.0.2\/72x72\/1f30d.png\" alt=\"\ud83c\udf0d\" class=\"wp-smiley\" \/> Global Earthquake Monitor&lt;\/h3&gt;\n       &lt;p style=\"margin: 5px 0 0 0; font-size: 12px;\"&gt;\n           Real-time earthquake data (M \u2265 2.5)&lt;br&gt;\n           Click markers for details | Toggle layers to explore\n       &lt;\/p&gt;\n   &lt;\/div&gt;\n   '''\n   m.get_root().html.add_child(folium.Element(title_html))\n  \n   Fullscreen(position='topright').add_to(m)\n  \n   return m\n\n\nif __name__ == \"__main__\":\n   print(\"=\" * 80)\n   print(\"ADVANCED FOLIUM TUTORIAL - ALL EXAMPLES\")\n   print(\"=\" * 80)\n   print(\"nGenerating all maps...n\")\n  \n   maps = {\n       'multi_tile_map': create_multi_tile_map(),\n       'advanced_markers_map': create_advanced_markers_map(),\n       'heatmap': create_heatmap(),\n       'choropleth_map': create_choropleth_map(),\n       'marker_cluster_map': create_marker_cluster_map(),\n       'time_series_map': create_time_series_map(),\n       'interactive_plugins_map': create_interactive_plugins_map(),\n       'earthquake_map': create_earthquake_map()\n   }\n  \n   print(\"n\" + \"=\" * 80)\n   print(\"SAVING MAPS TO HTML FILES\")\n   print(\"=\" * 80)\n  \n   for name, map_obj in maps.items():\n       if map_obj is not None:\n           filename = f\"{name}.html\"\n           map_obj.save(filename)\n           print(f\"\u2713 Saved: {filename}\")\n       else:\n           print(f\"\u2717 Skipped: {name} (map generation failed)\")\n  \n   print(\"n\" + \"=\" * 80)\n   print(\"ALL MAPS GENERATED SUCCESSFULLY!\")\n   print(\"=\" * 80)\n   print(\"nYou can now:\")\n   print(\"1. Open any HTML file in your browser to view the interactive map\")\n   print(\"2. Access the map objects in code using the 'maps' dictionary\")\n   print(\"3. Display maps in Jupyter\/Colab by returning the map object\")\n   print(\"nExample: To display the earthquake map in a notebook, just run:\")\n   print(\"  maps['earthquake_map']\")\n   print(\"n\" + \"=\" * 80)<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We implement advanced geospatial features, including marker clustering, time-series animation, interactive plugins, and real-world earthquake visualization. We integrate live USGS data, categorize earthquakes by magnitude, and enhance interpretation using feature groups, heatmaps, and custom legends. By combining interactivity, animation, and real-data integration, we build a comprehensive, production-ready geospatial visualization system.<\/p>\n<p>In conclusion, we have a reusable Folium \u201ctoolbox\u201d that covers both core mapping patterns and production-style interactivity. We generated and saved multiple maps as standalone HTML files, making them easy to share, embed, or iterate on without extra infrastructure. We also demonstrated how to move from synthetic demo data to live public feeds while keeping the visualization structured with layers, tooltips, pop-ups, and controls. Also, we can confidently extend the script by plugging in our own datasets, adding new GeoJSON boundaries, enriching popups with charts or images, and turning Folium maps into lightweight geospatial applications.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n<p>Check out the\u00a0<strong><a href=\"https:\/\/github.com\/Marktechpost\/AI-Tutorial-Codes-Included\/blob\/main\/Data%20Science\/folium_interactive_geospatial_visualization_Marktechpost.ipynb\" target=\"_blank\" rel=\"noreferrer noopener\">Full Codes here<\/a><\/strong>\u00a0<strong>.\u00a0<\/strong>Also,\u00a0feel free to follow us on\u00a0<strong><a href=\"https:\/\/x.com\/intent\/follow?screen_name=marktechpost\" target=\"_blank\" rel=\"noreferrer noopener\"><mark>Twitter<\/mark><\/a><\/strong>\u00a0and don\u2019t forget to join our\u00a0<strong><a href=\"https:\/\/www.reddit.com\/r\/machinelearningnews\/\" target=\"_blank\" rel=\"noreferrer noopener\">120k+ ML SubReddit<\/a><\/strong>\u00a0and Subscribe to\u00a0<strong><a href=\"https:\/\/www.aidevsignals.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">our Newsletter<\/a><\/strong>. Wait! are you on telegram?\u00a0<strong><a href=\"https:\/\/t.me\/machinelearningresearchnews\" target=\"_blank\" rel=\"noreferrer noopener\">now you can join us on telegram as well.<\/a><\/strong><\/p>\n<p>The post <a href=\"https:\/\/www.marktechpost.com\/2026\/02\/27\/how-to-build-interactive-geospatial-dashboards-using-folium-with-heatmaps-choropleths-time-animation-marker-clustering-and-advanced-interactive-plugins\/\">How to Build Interactive Geospatial Dashboards Using Folium with Heatmaps, Choropleths, Time Animation, Marker Clustering, and Advanced Interactive Plugins<\/a> appeared first on <a href=\"https:\/\/www.marktechpost.com\/\">MarkTechPost<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>In this Folium tutorial, we bu&hellip;<\/p>\n","protected":false},"author":1,"featured_media":29,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-486","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/posts\/486","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=486"}],"version-history":[{"count":0,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/posts\/486\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/media\/29"}],"wp:attachment":[{"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=486"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=486"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=486"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}