Yield map from KBS LTER harvest data
Precision Agriculture
Example from the compendium of canonical charts
Python Code
"""Precision Agriculture — Yield map from KBS LTER harvest data."""
from pathlib import Path
# ── Palette + theme (matching the Plotly Studio gallery these charts ship in) ──
VIOLET, TEAL, GREEN, PINK, ORANGE = "#845EEE", "#52B3D0", "#55B685", "#DA5597", "#E9A23B"
PRIMARY, SECONDARY = VIOLET, TEAL
COLORWAY = [VIOLET, TEAL, GREEN, PINK, ORANGE]
BG, TEXT, GRID, MUTED = "#ffffff", "#1c2024", "#d9d9e0", "#60646c"
FONT = "Inter, -apple-system, BlinkMacSystemFont, sans-serif"
COLORSCALE = [[0, "rgba(132, 94, 238, 0.05)"], [1, "rgba(132, 94, 238, 0.9)"]]
def apply_theme(fig):
"""Light gallery theme: white background, Inter font, soft gridlines."""
fig.update_layout(
paper_bgcolor=BG, plot_bgcolor=BG, colorway=COLORWAY,
font=dict(family=FONT, color=TEXT, size=12),
legend=dict(font=dict(color=TEXT)),
hoverlabel=dict(bgcolor="#f0f0f3", font=dict(color=TEXT, family=FONT), bordercolor=GRID),
)
fig.update_xaxes(gridcolor=GRID, linecolor=GRID, zerolinecolor=GRID)
fig.update_yaxes(gridcolor=GRID, linecolor=GRID, zerolinecolor=GRID)
def fetch_csv(url, **kwargs):
import io
import pandas as pd
import requests
r = requests.get(url, timeout=60)
r.raise_for_status()
return pd.read_csv(io.StringIO(r.text), **kwargs)
def fetch_json(url):
import requests
r = requests.get(url, timeout=60)
r.raise_for_status()
return r.json()
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import io, requests, warnings
URL = "https://lter.kbs.msu.edu/datatables/80.csv"
LON_C, LAT_C = -85.37, 42.39
def fetch_real():
warnings.filterwarnings("ignore")
requests.packages.urllib3.disable_warnings()
r = requests.get(URL, verify=False, timeout=60)
r.raise_for_status()
df = pd.read_csv(io.StringIO(r.text), comment="#", low_memory=False)
df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
return df
def make_synthetic():
RNG = np.random.default_rng(3)
n = 5000
lons = LON_C + RNG.uniform(-0.003, 0.003, n)
lats = LAT_C + RNG.uniform(-0.002, 0.002, n)
base = 45 + 8 * (lats - LAT_C) / 0.002 + 6 * (lons - LON_C) / 0.003
yields = np.clip(base + RNG.normal(0, 4, n), 20, 75)
return pd.DataFrame({"longitude": lons, "latitude": lats, "yield_bu_ac": yields})
def generate():
print("building yield map …")
df = None
try:
raw = fetch_real()
col_map = {}
for c in raw.columns:
if "lat" in c: col_map["latitude"] = c
if "lon" in c or "lng" in c: col_map["longitude"] = c
if len(col_map) == 2:
raw = raw.rename(columns={v: k for k, v in col_map.items()})
if "latitude" in raw.columns and "longitude" in raw.columns:
flow_cols = [c for c in raw.columns
if "flow" in c or "yield" in c or "crop" in c]
if flow_cols:
raw["yield_bu_ac"] = pd.to_numeric(raw[flow_cols[0]], errors="coerce")
raw = raw.dropna(subset=["latitude", "longitude", "yield_bu_ac"])
raw = raw[raw["yield_bu_ac"] > 0]
if len(raw) > 100:
if len(raw) > 5000:
raw = raw.sample(5000, random_state=42)
df = raw[["latitude", "longitude", "yield_bu_ac"]]
print(f" real: {len(df)} points")
except Exception as e:
print(f" fetch failed ({e})")
if df is None:
df = make_synthetic()
print(f" synthetic: {len(df)} points")
center_lat = float(df["latitude"].mean())
center_lon = float(df["longitude"].mean())
q02 = float(df["yield_bu_ac"].quantile(0.02))
q98 = float(df["yield_bu_ac"].quantile(0.98))
fig = go.Figure(go.Scattermapbox(
lat=df["latitude"],
lon=df["longitude"],
mode="markers",
marker=dict(
size=3,
color=df["yield_bu_ac"],
colorscale="YlOrRd",
cmin=q02,
cmax=q98,
colorbar=dict(title="Yield (bu/ac)", thickness=14),
opacity=0.9,
),
hovertemplate="Lon: %{lon:.4f}<br>Lat: %{lat:.4f}<br>Yield: %{marker.color:.1f} bu/ac<extra></extra>",
))
fig.update_layout(
mapbox=dict(
style="white-bg",
layers=[{
"below": "traces",
"sourcetype": "raster",
"sourceattribution": "Tiles © Esri",
"source": ["https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"],
}],
center=dict(lat=center_lat, lon=center_lon),
zoom=16,
),
margin=dict(t=0, b=0, l=0, r=0),
)
apply_theme(fig)
return fig
fig = generate()
fig.show()
Made with Plotly