Hover the line to read an event. Click any year for a full dossier.
""" Policy vs Deforestation Explorer — built with gr.Server Uses gr.Server for API endpoints (with MCP tool support) and a custom HTML frontend with Chart.js for interactive visualization. """ import json import urllib.request from gradio import Server from fastapi.responses import HTMLResponse, JSONResponse app = Server() COUNTRIES = { "Brazil": "BRA", "Indonesia": "IDN", "DR Congo": "COD", "Colombia": "COL", "Bolivia": "BOL", "Malaysia": "MYS", "India": "IND", "Mexico": "MEX", "Peru": "PER", "Australia": "AUS", "Canada": "CAN", "Russia": "RUS", "China": "CHN", "United States": "USA", "Nigeria": "NGA", "Myanmar": "MMR", "Tanzania": "TZA", "Paraguay": "PRY", "Madagascar": "MDG", "Cameroon": "CMR", } POLICY_EVENTS = { "Brazil": [ {"year": 2004, "text": "PPCDAm action plan launched", "url": "https://en.wikipedia.org/wiki/Deforestation_in_Brazil"}, {"year": 2006, "text": "Soy Moratorium signed", "url": "https://en.wikipedia.org/wiki/Amazon_Soy_Moratorium"}, {"year": 2008, "text": "Amazon Fund established", "url": "https://en.wikipedia.org/wiki/Amazon_Fund"}, {"year": 2012, "text": "New Forest Code enacted", "url": "https://en.wikipedia.org/wiki/Deforestation_in_Brazil"}, {"year": 2019, "text": "Enforcement weakened under Bolsonaro", "url": "https://en.wikipedia.org/wiki/Deforestation_in_Brazil"}, {"year": 2023, "text": "Zero deforestation pledge renewed", "url": "https://en.wikipedia.org/wiki/Deforestation_in_Brazil"}, ], "Indonesia": [ {"year": 2002, "text": "Illegal Logging Decree", "url": "https://en.wikipedia.org/wiki/Deforestation_in_Indonesia"}, {"year": 2011, "text": "Forest moratorium on concessions", "url": "https://en.wikipedia.org/wiki/Deforestation_in_Indonesia"}, {"year": 2014, "text": "One Map Policy", "url": "https://en.wikipedia.org/wiki/Joko_Widodo"}, {"year": 2018, "text": "Moratorium extended permanently", "url": "https://en.wikipedia.org/wiki/Deforestation_in_Indonesia"}, {"year": 2023, "text": "FOLU Net Sink 2030 strategy", "url": "https://en.wikipedia.org/wiki/Climate_change_in_Indonesia"}, ], "DR Congo": [ {"year": 2002, "text": "Forest Code enacted", "url": "https://en.wikipedia.org/wiki/Deforestation_in_the_Democratic_Republic_of_the_Congo"}, {"year": 2014, "text": "REDD+ national strategy", "url": "https://en.wikipedia.org/wiki/REDD_and_REDD%2B"}, {"year": 2022, "text": "Logging moratorium lifted", "url": "https://en.wikipedia.org/wiki/Deforestation_in_the_Democratic_Republic_of_the_Congo"}, ], "Colombia": [ {"year": 2010, "text": "REDD+ strategy launched", "url": "https://en.wikipedia.org/wiki/REDD_and_REDD%2B"}, {"year": 2016, "text": "FARC peace deal", "url": "https://en.wikipedia.org/wiki/Colombian_peace_process"}, {"year": 2018, "text": "Deforestation Control Council", "url": "https://en.wikipedia.org/wiki/Deforestation_in_Colombia"}, {"year": 2023, "text": "Amazon pact signed", "url": "https://en.wikipedia.org/wiki/Amazon_Cooperation_Treaty_Organization"}, ], "India": [ {"year": 2006, "text": "Forest Rights Act", "url": "https://en.wikipedia.org/wiki/Forest_Rights_Act,_2006"}, {"year": 2014, "text": "Green India Mission", "url": "https://en.wikipedia.org/wiki/Government_of_India"}, {"year": 2019, "text": "Compensatory Afforestation Fund", "url": "https://en.wikipedia.org/wiki/Compensatory_Afforestation_Fund_Act,_2016"}, ], "Malaysia": [ {"year": 2010, "text": "50% forest cover pledge", "url": "https://en.wikipedia.org/wiki/Deforestation_in_Malaysia"}, {"year": 2017, "text": "MSPO mandatory certification", "url": "https://en.wikipedia.org/wiki/Malaysian_Sustainable_Palm_Oil"}, ], "China": [ {"year": 1998, "text": "Natural Forest Protection Program", "url": "https://en.wikipedia.org/wiki/Reforestation_in_China"}, {"year": 2003, "text": "Grain-to-Green expanded", "url": "https://en.wikipedia.org/wiki/Reforestation_in_China"}, {"year": 2016, "text": "13th Five-Year Plan forestry targets", "url": "https://en.wikipedia.org/wiki/13th_Five-Year_Plan_(China)"}, ], "Mexico": [ {"year": 2001, "text": "ProÁrbol reforestation", "url": "https://en.wikipedia.org/wiki/CONAFOR"}, {"year": 2012, "text": "Climate Change Law", "url": "https://en.wikipedia.org/wiki/Climate_change_in_Mexico"}, {"year": 2020, "text": "Sembrando Vida program", "url": "https://en.wikipedia.org/wiki/Andr%C3%A9s_Manuel_L%C3%B3pez_Obrador"}, ], "Canada": [ {"year": 2010, "text": "Boreal Forest Agreement", "url": "https://en.wikipedia.org/wiki/Boreal_forest_of_Canada"}, {"year": 2021, "text": "2 Billion Trees program", "url": "https://www.canada.ca/en/campaign/2-billion-trees.html"}, ], "Australia": [ {"year": 2000, "text": "Regional Forest Agreements", "url": "https://en.wikipedia.org/wiki/Regional_Forest_Agreement"}, {"year": 2012, "text": "Carbon farming legislation", "url": "https://en.wikipedia.org/wiki/Climate_change_in_Australia"}, {"year": 2022, "text": "Nature Repair Market Act", "url": "https://en.wikipedia.org/wiki/Climate_change_in_Australia"}, ], "United States": [ {"year": 2001, "text": "Healthy Forests Initiative", "url": "https://en.wikipedia.org/wiki/Healthy_Forests_Initiative"}, {"year": 2008, "text": "Lacey Act amended to ban illegal timber", "url": "https://en.wikipedia.org/wiki/Lacey_Act_of_1900"}, {"year": 2022, "text": "Inflation Reduction Act — $5B for forests", "url": "https://en.wikipedia.org/wiki/Inflation_Reduction_Act"}, ], "Russia": [ {"year": 1997, "text": "First Russian Forest Code", "url": "https://en.wikipedia.org/wiki/Forestry_in_Russia"}, {"year": 2006, "text": "New Forest Code — privatisation of management", "url": "https://en.wikipedia.org/wiki/Forestry_in_Russia"}, {"year": 2020, "text": "Forest protection reform after record fires", "url": "https://en.wikipedia.org/wiki/Forestry_in_Russia"}, ], "Bolivia": [ {"year": 1996, "text": "Forestry Law 1700 enacted", "url": "https://en.wikipedia.org/wiki/Deforestation"}, {"year": 2012, "text": "Mother Earth Law", "url": "https://en.wikipedia.org/wiki/Law_of_the_Rights_of_Mother_Earth"}, ], "Peru": [ {"year": 2011, "text": "Forest and Wildlife Law 29763", "url": "https://en.wikipedia.org/wiki/Deforestation_in_Peru"}, {"year": 2014, "text": "Joint Declaration with Norway & Germany", "url": "https://en.wikipedia.org/wiki/Deforestation_in_Peru"}, ], "Paraguay": [ {"year": 2004, "text": "Zero Deforestation Law (East region)", "url": "https://en.wikipedia.org/wiki/Deforestation"}, ], "Madagascar": [ {"year": 2003, "text": "Durban Vision — triple protected areas", "url": "https://en.wikipedia.org/wiki/Environment_of_Madagascar"}, {"year": 2015, "text": "National REDD+ strategy", "url": "https://en.wikipedia.org/wiki/REDD_and_REDD%2B"}, ], "Cameroon": [ {"year": 1994, "text": "Forest Law 94/01 enacted", "url": "https://en.wikipedia.org/wiki/Forestry_in_Cameroon"}, {"year": 2010, "text": "CAFI partnership initiated", "url": "https://en.wikipedia.org/wiki/Forestry_in_Cameroon"}, ], "Nigeria": [ {"year": 1999, "text": "Federal Forestry Policy", "url": "https://en.wikipedia.org/wiki/Forestry_in_Nigeria"}, {"year": 2017, "text": "Great Green Wall commitment", "url": "https://en.wikipedia.org/wiki/Great_Green_Wall_(Africa)"}, ], "Myanmar": [ {"year": 1992, "text": "Forest Law passed", "url": "https://en.wikipedia.org/wiki/Deforestation"}, {"year": 2014, "text": "Raw log export ban", "url": "https://en.wikipedia.org/wiki/Deforestation"}, ], "Tanzania": [ {"year": 2002, "text": "Forest Act enacted", "url": "https://en.wikipedia.org/wiki/Environmental_issues_in_Tanzania"}, {"year": 2015, "text": "National REDD+ strategy", "url": "https://en.wikipedia.org/wiki/REDD_and_REDD%2B"}, ], } CLIMATE_EVENTS = { "Australia": [ {"year": 2002, "text": "Millennium Drought intensifies", "url": "https://en.wikipedia.org/wiki/2000s_Australian_drought"}, {"year": 2009, "text": "Black Saturday bushfires (430k ha)", "url": "https://en.wikipedia.org/wiki/Black_Saturday_bushfires"}, {"year": 2020, "text": "Black Summer fires (18.6M ha)", "url": "https://en.wikipedia.org/wiki/2019%E2%80%9320_Australian_bushfire_season"}, ], "Indonesia": [ {"year": 1997, "text": "Southeast Asian haze from peat fires", "url": "https://en.wikipedia.org/wiki/1997_Southeast_Asian_haze"}, {"year": 2015, "text": "Peat fires burn 2.6M ha", "url": "https://en.wikipedia.org/wiki/2015_Southeast_Asian_haze"}, {"year": 2019, "text": "Major fire season, 1.6M ha burned", "url": "https://en.wikipedia.org/wiki/2019_Southeast_Asian_haze"}, ], "Brazil": [ {"year": 2019, "text": "Amazon fires spike 84%", "url": "https://en.wikipedia.org/wiki/2019_Amazon_rainforest_wildfires"}, {"year": 2020, "text": "Pantanal wetland fires (4M ha)", "url": "https://en.wikipedia.org/wiki/2020_Pantanal_wildfires"}, ], "Canada": [ {"year": 2005, "text": "Mountain pine beetle peak (18M ha)", "url": "https://en.wikipedia.org/wiki/Mountain_pine_beetle"}, {"year": 2016, "text": "Fort McMurray fire", "url": "https://en.wikipedia.org/wiki/2016_Fort_McMurray_wildfire"}, {"year": 2023, "text": "Record fire season (18.5M ha)", "url": "https://en.wikipedia.org/wiki/2023_Canadian_wildfires"}, ], "Russia": [ {"year": 2010, "text": "Moscow smog — wildfires kill 56k", "url": "https://en.wikipedia.org/wiki/2010_Russian_wildfires"}, {"year": 2019, "text": "Siberian fires (4M ha)", "url": "https://en.wikipedia.org/wiki/2019_Siberia_wildfires"}, {"year": 2021, "text": "Record Siberian fires (18.8M ha)", "url": "https://en.wikipedia.org/wiki/2021_Russia_wildfires"}, ], "China": [ {"year": 1998, "text": "Yangtze floods kill 4,000+", "url": "https://en.wikipedia.org/wiki/1998_China_floods"}, ], "United States": [ {"year": 2018, "text": "Camp Fire destroys Paradise", "url": "https://en.wikipedia.org/wiki/Camp_Fire_(2018)"}, {"year": 2020, "text": "Record fire season (4.2M acres)", "url": "https://en.wikipedia.org/wiki/2020_California_wildfires"}, {"year": 2023, "text": "Maui fires & Canadian smoke crisis", "url": "https://en.wikipedia.org/wiki/2023_Hawaii_wildfires"}, ], "Colombia": [ {"year": 2017, "text": "Deforestation spikes in post-FARC areas", "url": "https://en.wikipedia.org/wiki/Deforestation_in_Colombia"}, ], "Mexico": [ {"year": 2011, "text": "Worst drought in 70 years", "url": "https://en.wikipedia.org/wiki/Climate_change_in_Mexico"}, ], "Bolivia": [ {"year": 2019, "text": "Chiquitano fires burn 2.3M ha", "url": "https://en.wikipedia.org/wiki/2019_Amazon_rainforest_wildfires"}, ], "Madagascar": [ {"year": 2020, "text": "Severe drought in southern regions", "url": "https://en.wikipedia.org/wiki/Environment_of_Madagascar"}, ], "DR Congo": [ {"year": 2003, "text": "Second Congo War ends", "url": "https://en.wikipedia.org/wiki/Second_Congo_War"}, ], } _cache = {} HISTORICAL_CONTEXT = { "Brazil": { range(1990, 1996): "Rapid expansion of cattle ranching and soy farming in the Amazon after economic stabilisation.", range(1996, 2005): "Peak deforestation era — illegal logging, land speculation, and weak enforcement. Arc of deforestation expanded.", range(2005, 2013): "PPCDAm enforcement + satellite monitoring (DETER) + soy/beef moratoriums drove sharp decline in clearing rates.", range(2013, 2019): "Deforestation crept back up as budget cuts weakened IBAMA enforcement and new Forest Code allowed amnesty for past clearing.", range(2019, 2023): "Environmental enforcement dismantled, IBAMA fines dropped 70%. Amazon tipping point warnings from scientists.", }, "Indonesia": { range(1990, 2000): "Suharto-era logging concessions and transmigration programs accelerated forest loss, especially in Sumatra and Kalimantan.", range(2000, 2005): "Post-Suharto decentralisation gave district heads power to issue logging/plantation permits, often corruptly.", range(2005, 2012): "Palm oil boom — Indonesia became world's largest producer. Peatland drainage caused massive fires (2006, 2009).", range(2012, 2018): "2015 fires burned 2.6M hectares, caused $16B damage. Led to peat restoration agency and stronger moratorium.", range(2018, 2023): "Deforestation rates declined significantly. Palm oil export ban (2022) temporarily reduced pressure.", }, "Australia": { range(1990, 2000): "Broadscale land clearing for agriculture, especially in Queensland. Woody vegetation loss peaked mid-1990s.", range(2000, 2007): "Millennium drought (2001-2009) devastated forests. Queensland banned broadscale clearing in 2006.", range(2007, 2013): "Black Saturday bushfires (2009) destroyed 430,000 hectares. Drought continued to stress forests.", range(2013, 2020): "Forest recovery after drought broke. But 2019-20 Black Summer fires burned 18.6M hectares — worst fire season on record.", range(2020, 2023): "La Niña rains aided recovery. New environmental laws and carbon farming incentives.", }, "DR Congo": { range(1990, 2002): "Civil wars (1996-2003) disrupted industrial logging but subsistence clearing continued.", range(2002, 2015): "Population growth drove smallholder agriculture expansion — the primary deforestation driver. Charcoal demand surged.", range(2015, 2023): "Artisanal mining and cocoa expansion increased. DRC has lowest governance capacity of major forest nations.", }, "Colombia": { range(1990, 2016): "FARC conflict paradoxically protected forests — armed groups controlled access to remote areas.", range(2016, 2020): "Post-peace deal: deforestation spiked 44% as land grabbers moved into former FARC territory.", range(2020, 2023): "Government crackdown on deforestation. Amazon pact signed with Brazil and other nations.", }, "India": { range(1990, 2005): "Forest cover data contested — government counts plantations as forest. Native forest loss continued.", range(2005, 2015): "Massive afforestation programs (Green India Mission) increased total tree cover, though primary forest still declined.", range(2015, 2023): "Forest Rights Act empowered tribal communities. Compensatory afforestation fund reached $6B.", }, "China": { range(1990, 2000): "1998 Yangtze floods killed 4,000+ — blamed on upstream deforestation. Triggered logging ban.", range(2000, 2010): "Grain-to-Green: world's largest reforestation program. Paid 120M farmers to convert cropland to forest.", range(2010, 2023): "China became net reforester. But imports shifted deforestation to SE Asia and Africa.", }, "Malaysia": { range(1990, 2005): "Rapid palm oil expansion, especially in Sabah and Sarawak. Malaysia became 2nd largest producer.", range(2005, 2015): "International pressure over orangutan habitat. RSPO certification introduced but adoption slow.", range(2015, 2023): "MSPO mandatory certification. Pledged 50% forest cover but definition includes oil palm.", }, "Mexico": { range(1990, 2005): "NAFTA (1994) shifted agriculture — some marginal farmland abandoned and reforested, but Lacandón jungle clearing continued.", range(2005, 2015): "Drug cartel activity in forests (avocado, poppy cultivation) drove illegal clearing in Michoacán and Guerrero.", range(2015, 2023): "Sembrando Vida program controversially paid farmers to plant trees — but some cut existing forest to qualify.", }, "Canada": { range(1990, 2005): "Forestry industry dominated — clearcut logging in British Columbia and boreal regions.", range(2005, 2015): "Mountain pine beetle epidemic killed 18M hectares of BC forest — largest insect blight in North American history.", range(2015, 2023): "Wildfires intensified with climate change. 2023 was worst fire season ever — 18.5M hectares burned.", }, "Russia": { range(1990, 2000): "Post-Soviet collapse reduced industrial logging but also enforcement. Illegal logging surged.", range(2000, 2010): "Siberian wildfires increased dramatically. 2010 fires caused Moscow smog crisis.", range(2010, 2023): "Permafrost thaw and wildfires became primary forest loss drivers. 2021: record 18.8M hectares burned.", }, "United States": { range(1990, 2005): "Net forest area roughly stable. Urban sprawl consumed some forest, offset by farmland reversion in East.", range(2005, 2015): "Western wildfires intensified — bark beetle outbreaks weakened millions of hectares. 2012 was record fire year.", range(2015, 2023): "Paradise fire (2018), record 2020 season (4.2M acres in CA/OR/WA). Climate-driven megafires now the norm.", }, } def _fetch_wb(country_code: str, indicator: str) -> list[dict]: cache_key = f"{country_code}:{indicator}" if cache_key in _cache: return _cache[cache_key] url = ( f"https://api.worldbank.org/v2/country/{country_code}" f"/indicator/{indicator}?format=json&per_page=50&date=1990:2022" ) for attempt in range(3): try: resp = urllib.request.urlopen(url, timeout=30) data = json.loads(resp.read()) if len(data) < 2 or not data[1]: _cache[cache_key] = [] return [] results = [ {"year": int(d["date"]), "value": round(d["value"], 3)} for d in data[1] if d["value"] is not None ] results.sort(key=lambda x: x["year"]) _cache[cache_key] = results return results except Exception: if attempt == 2: return [] import time time.sleep(1) @app.mcp.tool(name="get_forest_data") @app.api(name="get_forest_data") def get_forest_data(country: str) -> dict: """Get forest area (% of land) time series for a country. Returns yearly data from World Bank.""" code = COUNTRIES.get(country) if not code: return {"error": f"Unknown country. Available: {', '.join(COUNTRIES.keys())}"} forest = _fetch_wb(code, "AG.LND.FRST.ZS") governance = _fetch_wb(code, "RL.EST") policies = POLICY_EVENTS.get(country, []) climate = CLIMATE_EVENTS.get(country, []) summary = {} if forest: first, last = forest[0], forest[-1] change = last["value"] - first["value"] years = last["year"] - first["year"] summary = { "start_year": first["year"], "end_year": last["year"], "start_pct": first["value"], "end_pct": last["value"], "change_pct": round(change, 3), "annual_rate": round(change / years, 4) if years else 0, } return { "country": country, "forest": forest, "governance": governance, "policies": policies, "climate": climate, "summary": summary, } @app.mcp.tool(name="compare_countries") @app.api(name="compare_countries") def compare_countries(country_a: str, country_b: str) -> dict: """Compare forest cover trends between two countries.""" a = get_forest_data(country_a) b = get_forest_data(country_b) return {"country_a": a, "country_b": b} @app.mcp.tool(name="explain_spike") @app.api(name="explain_spike") def explain_spike(country: str, year: int) -> dict: """Explain what happened to forest cover around a specific year. Identifies rate changes and nearby policy events.""" code = COUNTRIES.get(country) if not code: return {"error": f"Unknown country."} forest = _fetch_wb(code, "AG.LND.FRST.ZS") if not forest: return {"error": "No data available."} policies = POLICY_EVENTS.get(country, []) nearby_policies = [p for p in policies if abs(p["year"] - year) <= 3] climate = CLIMATE_EVENTS.get(country, []) nearby_climate = [c for c in climate if abs(c["year"] - year) <= 3] window = [f for f in forest if abs(f["year"] - year) <= 5] window.sort(key=lambda x: x["year"]) point = next((f for f in forest if f["year"] == year), None) prev = next((f for f in forest if f["year"] == year - 1), None) nxt = next((f for f in forest if f["year"] == year + 1), None) before = [f for f in forest if year - 5 <= f["year"] < year] after = [f for f in forest if year < f["year"] <= year + 5] rate_before = None rate_after = None if len(before) >= 2: rate_before = round((before[-1]["value"] - before[0]["value"]) / (before[-1]["year"] - before[0]["year"]), 4) if len(after) >= 2: rate_after = round((after[-1]["value"] - after[0]["value"]) / (after[-1]["year"] - after[0]["year"]), 4) yoy_change = None if point and prev: yoy_change = round(point["value"] - prev["value"], 3) trend = "stable" if rate_before is not None and rate_after is not None: if rate_after > rate_before + 0.01: trend = "recovery" elif rate_after < rate_before - 0.01: trend = "acceleration" if yoy_change is not None: if yoy_change > 0.05: trend = "sharp increase" elif yoy_change < -0.05: trend = "sharp decline" context = None country_ctx = HISTORICAL_CONTEXT.get(country, {}) for year_range, text in country_ctx.items(): if year in year_range: context = text break return { "country": country, "year": year, "forest_pct": point["value"] if point else None, "yoy_change": yoy_change, "trend": trend, "rate_5yr_before": rate_before, "rate_5yr_after": rate_after, "nearby_policies": nearby_policies, "nearby_climate": nearby_climate, "context": context, "window": window, } @app.mcp.tool(name="list_countries") @app.api(name="list_countries") def list_countries() -> list[str]: """List all available countries.""" return list(COUNTRIES.keys()) @app.get("/", response_class=HTMLResponse) async def homepage(): countries_json = json.dumps(list(COUNTRIES.keys())) return f"""