import os
import requests
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from dotenv import load_dotenv
load_dotenv(dotenv_path="../.env")
load_dotenv(dotenv_path="../.Renviron")
API_KEY = os.getenv("GJD_API_KEY")
BASE_URL = "https://api.greenjurisdictions.org/api/v1/dataPlaces/false/true/false"
HEADERS = {
"X-API-TOKEN": API_KEY,
"Accept": "application/json",
"Content-Type": "application/json",
}UC02: Deforestation in Acre, Brazil
Querying and visualizing deforestation data using PRODES as source
Objective
In this use case we shift our focus from forest cover to deforestation. We will query annual deforestation data for Acre, Brazil from 2010 to 2019, using data from PRODES — the official Brazilian deforestation monitoring system operated by INPE.
This example also demonstrates an important aspect of the GJD API: filtering by data source, since multiple sources may report on the same topic for the same jurisdiction.
Step 1: Import Libraries
Step 2: Query Parameters
| Parameter | Value | Meaning |
|---|---|---|
ID_Topic |
11 |
Deforestation |
ID_Countries |
["BR"] |
Brazil |
ID_Jurisdictions |
["BR-AC"] |
Acre |
ID_years |
["2010", ..., "2019"] |
Years 2010–2019 |
ID_sources |
[12] |
PRODES Amazonia, Brazil |
Why filter by source? The GJD API may return data from multiple sources for the same topic (PRODES, Hansen/UMD, MapBiomas). Each uses different methodologies. By specifying
ID_sources=[12], we ensure we only get PRODES data.
Step 3: Send the Request (PRODES only)
years = [str(y) for y in range(2010, 2020)]
params_prodes = {
"ID_Topic": 11,
"ID_Countries": '["BR"]',
"ID_Jurisdictions": '["BR-AC"]',
"ID_Municipalities": '[]',
"ID_years": str(years).replace("'", '"'),
"ID_sources": '[12]',
}
response = requests.get(BASE_URL, params=params_prodes, headers=HEADERS)
print(f"HTTP status: {response.status_code}")
data = response.json()
print(f"Message: {data['message']}")
print(f"Total records: {data['data']['total_data']}")Step 4: Parse into a DataFrame
df = pd.DataFrame(data["data"]["data"])
df["value"] = df["value"].astype(float)
df["year"] = df["year"].astype(int)
df = df[["year", "jurisdiction", "value", "unit", "source"]].sort_values("year")
dfStep 5: Bar Chart — PRODES Deforestation
fig, ax = plt.subplots(figsize=(10, 5))
bars = ax.bar(df["year"].astype(str), df["value"] / 1e3, color="#c0392b", width=0.7)
for bar, val in zip(bars, df["value"]):
ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.3,
f"{val:,.0f}", ha="center", va="bottom", fontsize=8)
ax.set_title("Annual Deforestation in Acre, Brazil (2010–2019)", fontweight="bold", fontsize=14)
ax.set_xlabel("Year")
ax.set_ylabel("Deforestation (thousand hectares)")
ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, _: f"{x:.0f}K"))
ax.spines[["top", "right"]].set_visible(False)
plt.figtext(0.5, -0.02, "Source: PRODES Amazonia (INPE) — via GJD API", ha="center", fontsize=9, color="gray")
plt.tight_layout()
plt.show()Step 6: Comparing Data Sources
To illustrate why source filtering matters, let’s make the same request without the ID_sources filter.
params_all = {
"ID_Topic": 11,
"ID_Countries": '["BR"]',
"ID_Jurisdictions": '["BR-AC"]',
"ID_Municipalities": '[]',
"ID_years": str(years).replace("'", '"'),
}
response_all = requests.get(BASE_URL, params=params_all, headers=HEADERS)
data_all = response_all.json()
df_all = pd.DataFrame(data_all["data"]["data"])
df_all["value"] = df_all["value"].astype(float)
df_all["year"] = df_all["year"].astype(int)
df_all = df_all[["year", "jurisdiction", "value", "unit", "source"]].sort_values(["year", "source"])
print(f"Records with all sources: {len(df_all)}")
print(f"Sources found: {df_all['source'].unique().tolist()}")source_colors = {
"PRODES Amazonia, Brazil": "#c0392b",
"Hansen et al./UMD/Google/USGS/NASA, post-processing by EII": "#2980b9",
"MapBiomas - Brasil": "#27ae60",
}
sources = df_all["source"].unique()
n_sources = len(sources)
year_labels = sorted(df_all["year"].unique())
x = np.arange(len(year_labels))
total_width = 0.75
bar_width = total_width / n_sources
fig, ax = plt.subplots(figsize=(12, 6))
for i, src in enumerate(sources):
subset = df_all[df_all["source"] == src].set_index("year").reindex(year_labels)
offset = (i - n_sources / 2 + 0.5) * bar_width
color = source_colors.get(src, f"C{i}")
label = src if len(src) < 30 else src[:27] + "..."
ax.bar(x + offset, subset["value"] / 1e3, bar_width, label=label, color=color)
ax.set_xticks(x)
ax.set_xticklabels([str(y) for y in year_labels])
ax.set_title("Deforestation in Acre: Comparing Data Sources (2010–2019)", fontweight="bold", fontsize=14)
ax.set_xlabel("Year")
ax.set_ylabel("Deforestation (thousand hectares)")
ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, _: f"{x:.0f}K"))
ax.legend(loc="upper left", fontsize=8)
ax.spines[["top", "right"]].set_visible(False)
plt.figtext(0.5, -0.02, "Three sources report different values for the same jurisdiction and topic", ha="center", fontsize=9, color="gray")
plt.tight_layout()
plt.show()Key Takeaway: Different data sources use different methodologies (satellite imagery resolution, classification algorithms, reference periods). Always be explicit about which source you are using in your analysis and document your choice.
Summary
In this use case we learned how to:
- Query deforestation data (Topic 11) from the GJD API using
requests. - Filter by a specific data source (PRODES) to ensure consistency.
- Visualize annual deforestation as a bar chart with
matplotlib. - Compare multiple data sources for the same jurisdiction to understand methodological differences.
Next Steps
- Combine deforestation and forest cover data to calculate deforestation rates.
- Compare deforestation across multiple jurisdictions in the Brazilian Amazon.
- Explore how deforestation trends relate to specific policy interventions.