Map iNaturalist Citizen Science Data#
iNaturalist has thousands of citizen science observations that can be used alongside GLOBE data to better understand mosquito habitats and prevalence. We’ll map out the locations of mosquito predators from iNaturalist and compare them with the mosquito data from GLOBE.
We’ll start with our American Robin observations.
import folium
from folium.plugins import MarkerCluster
from folium.plugins import HeatMap
import pandas as pd
import geopandas as gpd
# ----------------------------
# Load American Robin Data (Predators)
# ----------------------------
df = pd.read_csv("https://raw.githubusercontent.com/geo-di-lab/emerge-lessons/refs/heads/main/docs/data/american_robin.csv")
df["year"] = pd.to_datetime(df["observed_on"], errors="coerce").dt.year
df = df.dropna(subset=["latitude", "longitude"])
# Keep only American Robins
df = df[df["common_name"].str.contains("American Robin", case=False, na=False)]
# ----------------------------
# Load GLOBE Mosquito Data
# ----------------------------
mosquito = gpd.read_file("https://github.com/geo-di-lab/emerge-lessons/raw/refs/heads/main/docs/data/globe_mosquito.zip")
fl = gpd.read_file("https://github.com/geo-di-lab/emerge-lessons/raw/refs/heads/main/docs/data/florida_boundary.geojson")[["geometry"]]
# Clip to Florida boundary
mosquito = gpd.sjoin(mosquito, fl, how="inner", predicate="intersects").drop(columns=["index_right"]).reset_index(drop=True)
# ----------------------------
# Create Base Map (Centered on Florida)
# ----------------------------
m = folium.Map(location=[28.263363, -83.497652],
tiles="CartoDB positron", zoom_start=6)
# ----------------------------
# Add Predator (American Robin) Layer
# ----------------------------
robin_fg = folium.FeatureGroup(name="🟦 American Robin Observations").add_to(m)
robin_cluster = MarkerCluster().add_to(robin_fg)
for _, row in df.iterrows():
popup_text = f"""
<b>Species:</b> {row['common_name']}<br>
<b>Date:</b> {row['observed_on']}<br>
<b>Location:</b> {row['place_guess']}<br>
"""
folium.CircleMarker(
location=[row["latitude"], row["longitude"]],
radius=6,
color="blue",
fill=True,
fill_color="cyan",
fill_opacity=0.8,
popup=folium.Popup(popup_text, max_width=300),
tooltip="American Robin 🐦"
).add_to(robin_cluster)
# ----------------------------
# Add Research-Grade American Robin Observations
# ----------------------------
df_research = df[df['quality_grade'] == 'research']
if not df_research.empty:
research_fg = folium.FeatureGroup(name="🟩 Research-Grade Robins").add_to(m)
research_cluster = MarkerCluster().add_to(research_fg)
for _, row in df_research.iterrows():
popup_text = f"""
<b>Species:</b> {row['common_name']}<br>
<b>Date:</b> {row['observed_on']}<br>
<b>Location:</b> {row['place_guess']}<br>
<b>Quality:</b> {row['quality_grade']}
"""
folium.CircleMarker(
location=[row["latitude"], row["longitude"]],
radius=6,
color="green",
fill=True,
fill_color="lime",
fill_opacity=0.8,
popup=folium.Popup(popup_text, max_width=300),
tooltip="Research-Grade Robin 🟩"
).add_to(research_cluster)
# ----------------------------
# Add Legend (Custom HTML)
# ----------------------------
legend_html = """
<div style="position: fixed;
bottom: 50px; left: 50px; width: 200px;
background-color: white; border:2px solid grey; z-index:9999;
font-size:14px; padding: 10px; border-radius: 8px;">
<b>Legend</b><br>
<span style="color:blue;">●</span> American Robin<br>
<span style="color:green;">●</span> Research Grade Robins<br>
</div>
"""
m.get_root().html.add_child(folium.Element(legend_html))
# ----------------------------
# Layer Control + Display
# ----------------------------
folium.LayerControl(collapsed=False).add_to(m)
display(m)
In this map, we’ll take the American Robin observations from before, still dividing them into research grade and non-research grade, but converting them to points rather than clusters. We’ll also add the mosquito observation points. Toggle observations on and off to see where they might overlay.
# Create new map (fresh)
m = folium.Map(location=[28.263363, -83.497652], tiles="CartoDB positron", zoom_start=6)
# Add robins
robin_fg = folium.FeatureGroup(name="🟦 American Robins").add_to(m)
for _, row in df.iterrows():
folium.CircleMarker(
location=[row.latitude, row.longitude],
radius=6,
color="blue",
fill=True,
fill_color="cyan",
fill_opacity=0.8,
popup=f"{row['common_name']} - {row['place_guess']}"
).add_to(robin_fg)
# Add research-grade robins
df_research = df[df['quality_grade']=='research']
research_fg = folium.FeatureGroup(name="🟩 Research-Grade Robins").add_to(m)
for _, row in df_research.iterrows():
folium.CircleMarker(
location=[row.latitude, row.longitude],
radius=6,
color="green",
fill=True,
fill_color="lime",
fill_opacity=0.8,
popup=f"{row['common_name']} - {row['place_guess']}"
).add_to(research_fg)
# Add mosquitoes (toggleable)
mosquito_fg = folium.FeatureGroup(name="🟥 GLOBE Mosquito Observations").add_to(m)
for _, row in mosquito.iterrows():
folium.CircleMarker(
location=[row.geometry.y, row.geometry.x],
radius=4,
color="darkred",
fill=True,
fill_color="orange",
fill_opacity=0.8,
popup=f"{row.get('MeasuredDate','N/A')} - {row.get('WaterSourceType','N/A')}"
).add_to(mosquito_fg)
# Add LayerControl
folium.LayerControl(collapsed=False).add_to(m)
display(m)
In this next map, we’ll be using heat maps to display these points as a gradient. This will allow you to visualize clusters of robins and mosquitos as color intensity. The darker they are, the more observations that have been recorded there. Mosquitoes will be displayed in red robins in blue, so that when you toggle both heat maps, areas where they have both been observed are purple.
# ----------------------------
# Create Base Map (Centered on Florida)
# ----------------------------
m = folium.Map(location=[28.263363, -83.497652], tiles="CartoDB positron", zoom_start=6)
# ----------------------------
# Add Heatmap for Mosquito Observations
# ----------------------------
mosquito_coords = [[y, x] for y, x in zip(mosquito.geometry.y, mosquito.geometry.x)]
HeatMap(
mosquito_coords,
radius=15,
blur=25,
gradient={0.4: 'orange', 0.7: 'red', 1: 'darkred'},
name="Mosquito Density"
).add_to(m)
# ----------------------------
# Add Heatmap for American Robin Observations
# ----------------------------
robin_coords = [[lat, lon] for lat, lon in zip(df.latitude, df.longitude)]
HeatMap(
robin_coords,
radius=15,
blur=25,
gradient={0.4: 'cyan', 0.7: 'blue', 1: 'darkblue'},
name="American Robin Density"
).add_to(m)
# ----------------------------
# Layer Control + Display
# ----------------------------
folium.LayerControl(collapsed=False).add_to(m)
display(m)