diff --git a/public/api/ecoregion_harvest_summary.json b/public/api/ecoregion_harvest_summary.json new file mode 100644 index 0000000..08e4199 --- /dev/null +++ b/public/api/ecoregion_harvest_summary.json @@ -0,0 +1 @@ +{"meta": {"source": "conus_render ph_*.tif harvest probability (TreeMap-based, ~3.1km Albers) zonal mean by EPA L3 ecoregion", "n_ecoregions": 74, "fields": ["p_harvest_any", "p_harvest_clearcut", "p_harvest_partial"], "note": "Per-EPA-Level-III-ecoregion mean harvest probability. P(any), P(stand-replacement), P(partial)."}, "ecoregions": {"7.1.8": {"name": "Coast Range", "l1": "MARINE WEST COAST FOREST", "npix": 88, "p_harvest_any": 0.6855, "p_harvest_clearcut": 0.6664, "p_harvest_partial": 0.4093}, "10.1.2": {"name": "Columbia Plateau", "l1": "NORTH AMERICAN DESERTS", "npix": 237, "p_harvest_any": 0.7404, "p_harvest_clearcut": 0.6895, "p_harvest_partial": 0.6791}, "6.2.9": {"name": "Blue Mountains", "l1": "NORTHWESTERN FORESTED MOUNTAINS", "npix": 1995, "p_harvest_any": 0.8558, "p_harvest_clearcut": 0.7079, "p_harvest_partial": 0.5734}, "10.1.8": {"name": "Snake River Plain", "l1": "NORTH AMERICAN DESERTS", "npix": 52, "p_harvest_any": 0.8771, "p_harvest_clearcut": 0.6755, "p_harvest_partial": 0.6054}, "10.1.5": {"name": "Central Basin and Range", "l1": "NORTH AMERICAN DESERTS", "npix": 376, "p_harvest_any": 0.8527, "p_harvest_clearcut": 0.6661, "p_harvest_partial": 0.649}, "6.2.3": {"name": "Columbia Mountains/Northern Rockies", "l1": "NORTHWESTERN FORESTED MOUNTAINS", "npix": 5482, "p_harvest_any": 0.891, "p_harvest_clearcut": 0.7376, "p_harvest_partial": 0.6727}, "6.2.15": {"name": "Idaho Batholith", "l1": "NORTHWESTERN FORESTED MOUNTAINS", "npix": 3474, "p_harvest_any": 0.8972, "p_harvest_clearcut": 0.8046, "p_harvest_partial": 0.3389}, "6.2.10": {"name": "Middle Rockies", "l1": "NORTHWESTERN FORESTED MOUNTAINS", "npix": 3376, "p_harvest_any": 0.8746, "p_harvest_clearcut": 0.6936, "p_harvest_partial": 0.5571}, "10.1.4": {"name": "Wyoming Basin", "l1": "NORTH AMERICAN DESERTS", "npix": 83, "p_harvest_any": 0.8839, "p_harvest_clearcut": 0.6822, "p_harvest_partial": 0.6237}, "6.2.13": {"name": "Wasatch and Uinta Mountains", "l1": "NORTHWESTERN FORESTED MOUNTAINS", "npix": 766, "p_harvest_any": 0.8777, "p_harvest_clearcut": 0.7222, "p_harvest_partial": 0.5169}, "7.1.7": {"name": "Strait of Georgia/Puget Lowland", "l1": "MARINE WEST COAST FOREST", "npix": 29, "p_harvest_any": 0.9203, "p_harvest_clearcut": 0.7488, "p_harvest_partial": 0.6828}, "10.1.6": {"name": "Colorado Plateaus", "l1": "NORTH AMERICAN DESERTS", "npix": 68, "p_harvest_any": 0.8909, "p_harvest_clearcut": 0.7392, "p_harvest_partial": 0.563}, "6.2.14": {"name": "Southern Rockies", "l1": "NORTHWESTERN FORESTED MOUNTAINS", "npix": 375, "p_harvest_any": 0.8932, "p_harvest_clearcut": 0.7705, "p_harvest_partial": 0.4755}, "9.4.2": {"name": "Central Great Plains", "l1": "GREAT PLAINS", "npix": 4589, "p_harvest_any": 0.7917, "p_harvest_clearcut": 0.7176, "p_harvest_partial": 0.4446}, "9.4.4": {"name": "Flint Hills", "l1": "GREAT PLAINS", "npix": 1677, "p_harvest_any": 0.7666, "p_harvest_clearcut": 0.7038, "p_harvest_partial": 0.4813}, "9.4.5": {"name": "Cross Timbers", "l1": "GREAT PLAINS", "npix": 5828, "p_harvest_any": 0.8277, "p_harvest_clearcut": 0.7258, "p_harvest_partial": 0.443}, "9.4.6": {"name": "Edwards Plateau", "l1": "GREAT PLAINS", "npix": 102, "p_harvest_any": 0.815, "p_harvest_clearcut": 0.7086, "p_harvest_partial": 0.3777}, "9.6.1": {"name": "Southern Texas Plains/Interior Plains and Hills with Xerophytic Shrub and Oak Forest", "l1": "GREAT PLAINS", "npix": 54, "p_harvest_any": 0.8578, "p_harvest_clearcut": 0.7951, "p_harvest_partial": 0.3}, "9.4.7": {"name": "Texas Blackland Prairies", "l1": "GREAT PLAINS", "npix": 2774, "p_harvest_any": 0.8338, "p_harvest_clearcut": 0.7447, "p_harvest_partial": 0.4065}, "8.3.8": {"name": "East Central Texas Plains", "l1": "EASTERN TEMPERATE FORESTS", "npix": 2789, "p_harvest_any": 0.834, "p_harvest_clearcut": 0.7589, "p_harvest_partial": 0.4062}, "9.5.1": {"name": "Western Gulf Coastal Plain", "l1": "GREAT PLAINS", "npix": 3788, "p_harvest_any": 0.8427, "p_harvest_clearcut": 0.7639, "p_harvest_partial": 0.3785}, "8.3.7": {"name": "South Central Plains", "l1": "EASTERN TEMPERATE FORESTS", "npix": 15297, "p_harvest_any": 0.8493, "p_harvest_clearcut": 0.769, "p_harvest_partial": 0.4243}, "8.4.8": {"name": "Ouachita Mountains", "l1": "EASTERN TEMPERATE FORESTS", "npix": 2702, "p_harvest_any": 0.841, "p_harvest_clearcut": 0.7618, "p_harvest_partial": 0.4422}, "8.4.7": {"name": "Arkansas Valley", "l1": "EASTERN TEMPERATE FORESTS", "npix": 2850, "p_harvest_any": 0.8362, "p_harvest_clearcut": 0.7444, "p_harvest_partial": 0.4451}, "8.4.6": {"name": "Boston Mountains", "l1": "EASTERN TEMPERATE FORESTS", "npix": 1433, "p_harvest_any": 0.8057, "p_harvest_clearcut": 0.742, "p_harvest_partial": 0.4419}, "8.4.5": {"name": "Ozark Highlands", "l1": "EASTERN TEMPERATE FORESTS", "npix": 9059, "p_harvest_any": 0.7961, "p_harvest_clearcut": 0.7167, "p_harvest_partial": 0.4513}, "6.2.7": {"name": "Cascades", "l1": "NORTHWESTERN FORESTED MOUNTAINS", "npix": 1161, "p_harvest_any": 0.8422, "p_harvest_clearcut": 0.7169, "p_harvest_partial": 0.5839}, "9.2.4": {"name": "Central Irregular Plains", "l1": "GREAT PLAINS", "npix": 8270, "p_harvest_any": 0.8148, "p_harvest_clearcut": 0.7092, "p_harvest_partial": 0.503}, "6.2.4": {"name": "Canadian Rockies", "l1": "NORTHWESTERN FORESTED MOUNTAINS", "npix": 1038, "p_harvest_any": 0.8973, "p_harvest_clearcut": 0.7032, "p_harvest_partial": 0.66}, "9.3.1": {"name": "Northwestern Glaciated Plains", "l1": "GREAT PLAINS", "npix": 298, "p_harvest_any": 0.8563, "p_harvest_clearcut": 0.6851, "p_harvest_partial": 0.5549}, "9.3.3": {"name": "Northwestern Great Plains", "l1": "GREAT PLAINS", "npix": 239, "p_harvest_any": 0.8825, "p_harvest_clearcut": 0.6672, "p_harvest_partial": 0.6462}, "8.3.4": {"name": "Piedmont", "l1": "EASTERN TEMPERATE FORESTS", "npix": 16733, "p_harvest_any": 0.8306, "p_harvest_clearcut": 0.7636, "p_harvest_partial": 0.4697}, "9.2.1": {"name": "Aspen Parkland/Northern Glaciated Plains", "l1": "GREAT PLAINS", "npix": 4548, "p_harvest_any": 0.8847, "p_harvest_clearcut": 0.6826, "p_harvest_partial": 0.6471}, "9.2.3": {"name": "Western Corn Belt Plains", "l1": "GREAT PLAINS", "npix": 20716, "p_harvest_any": 0.8587, "p_harvest_clearcut": 0.7203, "p_harvest_partial": 0.6268}, "9.2.2": {"name": "Lake Manitoba and Lake Agassiz Plain", "l1": "GREAT PLAINS", "npix": 1407, "p_harvest_any": 0.8934, "p_harvest_clearcut": 0.6899, "p_harvest_partial": 0.6577}, "5.2.2": {"name": "Northern Minnesota Wetlands", "l1": "NORTHERN FORESTS", "npix": 35, "p_harvest_any": 0.8999, "p_harvest_clearcut": 0.7103, "p_harvest_partial": 0.6537}, "6.2.12": {"name": "Sierra Nevada", "l1": "NORTHWESTERN FORESTED MOUNTAINS", "npix": 1589, "p_harvest_any": 0.8563, "p_harvest_clearcut": 0.7299, "p_harvest_partial": 0.4779}, "5.2.1": {"name": "Northern Lakes and Forests", "l1": "NORTHERN FORESTS", "npix": 14416, "p_harvest_any": 0.8949, "p_harvest_clearcut": 0.7292, "p_harvest_partial": 0.653}, "8.1.4": {"name": "North Central Hardwood Forests", "l1": "EASTERN TEMPERATE FORESTS", "npix": 8798, "p_harvest_any": 0.8987, "p_harvest_clearcut": 0.7313, "p_harvest_partial": 0.6516}, "8.1.5": {"name": "Driftless Area", "l1": "EASTERN TEMPERATE FORESTS", "npix": 4774, "p_harvest_any": 0.8901, "p_harvest_clearcut": 0.7237, "p_harvest_partial": 0.6192}, "8.2.1": {"name": "Southeastern Wisconsin Till Plains", "l1": "EASTERN TEMPERATE FORESTS", "npix": 3153, "p_harvest_any": 0.8965, "p_harvest_clearcut": 0.7276, "p_harvest_partial": 0.6377}, "8.2.3": {"name": "Central Corn Belt Plains", "l1": "EASTERN TEMPERATE FORESTS", "npix": 5177, "p_harvest_any": 0.8476, "p_harvest_clearcut": 0.7049, "p_harvest_partial": 0.6213}, "8.2.4": {"name": "Eastern Corn Belt Plains", "l1": "EASTERN TEMPERATE FORESTS", "npix": 6581, "p_harvest_any": 0.8161, "p_harvest_clearcut": 0.7128, "p_harvest_partial": 0.5754}, "8.1.6": {"name": "Southern Michigan/Northern Indiana Drift Plains", "l1": "EASTERN TEMPERATE FORESTS", "npix": 5367, "p_harvest_any": 0.8784, "p_harvest_clearcut": 0.7273, "p_harvest_partial": 0.631}, "8.2.2": {"name": "Huron/Erie Lake Plains", "l1": "EASTERN TEMPERATE FORESTS", "npix": 3141, "p_harvest_any": 0.8925, "p_harvest_clearcut": 0.7286, "p_harvest_partial": 0.6327}, "5.3.1": {"name": "Northern Appalachian and Atlantic Maritime Highlands", "l1": "NORTHERN FORESTS", "npix": 8678, "p_harvest_any": 0.8751, "p_harvest_clearcut": 0.7145, "p_harvest_partial": 0.636}, "8.1.7": {"name": "Northeastern Coastal Zone", "l1": "EASTERN TEMPERATE FORESTS", "npix": 4088, "p_harvest_any": 0.8314, "p_harvest_clearcut": 0.6941, "p_harvest_partial": 0.6348}, "11.1.1": {"name": "California Coastal Sage, Chaparral, and Oak Woodlands", "l1": "MEDITERRANEAN CALIFORNIA", "npix": 1254, "p_harvest_any": 0.8298, "p_harvest_clearcut": 0.7104, "p_harvest_partial": 0.415}, "8.1.3": {"name": "Northern Allegheny Plateau", "l1": "EASTERN TEMPERATE FORESTS", "npix": 4686, "p_harvest_any": 0.8555, "p_harvest_clearcut": 0.7255, "p_harvest_partial": 0.6347}, "8.1.10": {"name": "Erie Drift Plain", "l1": "EASTERN TEMPERATE FORESTS", "npix": 3090, "p_harvest_any": 0.792, "p_harvest_clearcut": 0.6875, "p_harvest_partial": 0.6268}, "5.3.3": {"name": "North Central Appalachians", "l1": "NORTHERN FORESTS", "npix": 2652, "p_harvest_any": 0.8505, "p_harvest_clearcut": 0.71, "p_harvest_partial": 0.6362}, "8.5.1": {"name": "Middle Atlantic Coastal Plain", "l1": "EASTERN TEMPERATE FORESTS", "npix": 7539, "p_harvest_any": 0.8651, "p_harvest_clearcut": 0.7681, "p_harvest_partial": 0.4681}, "8.3.1": {"name": "Northern Piedmont", "l1": "EASTERN TEMPERATE FORESTS", "npix": 2904, "p_harvest_any": 0.807, "p_harvest_clearcut": 0.6964, "p_harvest_partial": 0.4704}, "8.3.5": {"name": "Southeastern Plains", "l1": "EASTERN TEMPERATE FORESTS", "npix": 33192, "p_harvest_any": 0.8554, "p_harvest_clearcut": 0.7682, "p_harvest_partial": 0.4411}, "8.4.4": {"name": "Blue Ridge", "l1": "EASTERN TEMPERATE FORESTS", "npix": 4678, "p_harvest_any": 0.8474, "p_harvest_clearcut": 0.7787, "p_harvest_partial": 0.4121}, "8.4.1": {"name": "Ridge and Valley", "l1": "EASTERN TEMPERATE FORESTS", "npix": 11353, "p_harvest_any": 0.8237, "p_harvest_clearcut": 0.7367, "p_harvest_partial": 0.4904}, "8.4.9": {"name": "Southwestern Appalachians", "l1": "EASTERN TEMPERATE FORESTS", "npix": 3825, "p_harvest_any": 0.8398, "p_harvest_clearcut": 0.7628, "p_harvest_partial": 0.4007}, "8.4.2": {"name": "Central Appalachians", "l1": "EASTERN TEMPERATE FORESTS", "npix": 6150, "p_harvest_any": 0.8249, "p_harvest_clearcut": 0.7352, "p_harvest_partial": 0.4817}, "11.1.2": {"name": "Central California Valley", "l1": "MEDITERRANEAN CALIFORNIA", "npix": 13, "p_harvest_any": 0.8438, "p_harvest_clearcut": 0.7218, "p_harvest_partial": 0.4405}, "8.4.3": {"name": "Western Allegheny Plateau", "l1": "EASTERN TEMPERATE FORESTS", "npix": 7825, "p_harvest_any": 0.8212, "p_harvest_clearcut": 0.7226, "p_harvest_partial": 0.5033}, "8.3.3": {"name": "Interior Plateau", "l1": "EASTERN TEMPERATE FORESTS", "npix": 12006, "p_harvest_any": 0.8399, "p_harvest_clearcut": 0.7461, "p_harvest_partial": 0.4433}, "8.3.2": {"name": "Interior River Valleys and Hills", "l1": "EASTERN TEMPERATE FORESTS", "npix": 7524, "p_harvest_any": 0.7914, "p_harvest_clearcut": 0.7141, "p_harvest_partial": 0.5064}, "8.5.2": {"name": "Mississippi Alluvial Plain", "l1": "EASTERN TEMPERATE FORESTS", "npix": 10875, "p_harvest_any": 0.8505, "p_harvest_clearcut": 0.7645, "p_harvest_partial": 0.4141}, "8.3.6": {"name": "Mississippi Valley Loess Plains", "l1": "EASTERN TEMPERATE FORESTS", "npix": 5232, "p_harvest_any": 0.8519, "p_harvest_clearcut": 0.7706, "p_harvest_partial": 0.4112}, "8.5.3": {"name": "Southern Coastal Plain", "l1": "EASTERN TEMPERATE FORESTS", "npix": 14281, "p_harvest_any": 0.8407, "p_harvest_clearcut": 0.778, "p_harvest_partial": 0.3827}, "15.4.1": {"name": "Southern Florida Coastal Plain", "l1": "TROPICAL WET FORESTS", "npix": 2127, "p_harvest_any": 0.8369, "p_harvest_clearcut": 0.7807, "p_harvest_partial": 0.3188}, "6.2.5": {"name": "North Cascades", "l1": "NORTHWESTERN FORESTED MOUNTAINS", "npix": 1072, "p_harvest_any": 0.8669, "p_harvest_clearcut": 0.6875, "p_harvest_partial": 0.649}, "6.2.11": {"name": "Klamath Mountains", "l1": "NORTHWESTERN FORESTED MOUNTAINS", "npix": 125, "p_harvest_any": 0.9018, "p_harvest_clearcut": 0.8752, "p_harvest_partial": 0.1314}, "11.1.3": {"name": "Southern and Baja California Pine-Oak Mountains", "l1": "MEDITERRANEAN CALIFORNIA", "npix": 6, "p_harvest_any": 0.7345, "p_harvest_clearcut": 0.6701, "p_harvest_partial": 0.1953}, "10.1.3": {"name": "Northern Basin and Range", "l1": "NORTH AMERICAN DESERTS", "npix": 672, "p_harvest_any": 0.8724, "p_harvest_clearcut": 0.664, "p_harvest_partial": 0.6511}, "8.1.8": {"name": "Acadian Plains and Hills", "l1": "EASTERN TEMPERATE FORESTS", "npix": 3578, "p_harvest_any": 0.8829, "p_harvest_clearcut": 0.7008, "p_harvest_partial": 0.6514}, "8.1.1": {"name": "Eastern Great Lakes Lowlands", "l1": "EASTERN TEMPERATE FORESTS", "npix": 4094, "p_harvest_any": 0.8857, "p_harvest_clearcut": 0.7243, "p_harvest_partial": 0.6443}, "8.5.4": {"name": "Atlantic Coastal Pine Barrens", "l1": "EASTERN TEMPERATE FORESTS", "npix": 1139, "p_harvest_any": 0.8296, "p_harvest_clearcut": 0.7213, "p_harvest_partial": 0.5137}, "6.2.8": {"name": "Eastern Cascades Slopes and Foothills", "l1": "NORTHWESTERN FORESTED MOUNTAINS", "npix": 1100, "p_harvest_any": 0.8505, "p_harvest_clearcut": 0.7409, "p_harvest_partial": 0.5059}}} \ No newline at end of file diff --git a/scripts/build_ecoregion_harvest_summary.py b/scripts/build_ecoregion_harvest_summary.py new file mode 100644 index 0000000..ceddd74 --- /dev/null +++ b/scripts/build_ecoregion_harvest_summary.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +"""Zonal-mean the CONUS harvest-probability rasters by EPA Level III ecoregion. + +Inputs (on Cardinal, ~/conus_render/): ph_any.tif, ph_clearcut.tif, +ph_partial.tif — single-band float32 P(harvest) at ~3.1 km Albers, plus the +EPA L3 polygons (public/geo/us_eco_l3_features.geojson, WGS84). +Output: public/api/ecoregion_harvest_summary.json (mean P by L3 code). + +Run: python3 scripts/build_ecoregion_harvest_summary.py (needs rasterio, pyproj) +The .tif inputs are not committed (each ~6.8 MB); pull from Cardinal first. +""" +import json, numpy as np, rasterio, sys +from rasterio.features import rasterize +from pyproj import Transformer +ALBERS="+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=37.5 +lon_0=-96 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs" +GEO=sys.argv[1] if len(sys.argv)>1 else "public/geo/us_eco_l3_features.geojson" +tr=Transformer.from_crs("EPSG:4326",ALBERS,always_xy=True) +def reproj(g): + def rc(c): return [rc(x) if isinstance(x[0],(list,tuple)) else list(tr.transform(x[0],x[1])) for x in c] + return {"type":g["type"],"coordinates":rc(g["coordinates"])} +gj=json.load(open(GEO)); feats=gj["features"]; codes={}; meta={} +for f in feats: + p=f["properties"]; code=p.get("NA_L3CODE") + if code is None: continue + if code not in codes: codes[code]=len(codes)+1; meta[code]={"name":p.get("NA_L3NAME"),"l1":p.get("NA_L1NAME")} +shapes=[(reproj(f["geometry"]),codes[f["properties"]["NA_L3CODE"]]) for f in feats + if f["properties"].get("NA_L3CODE") and f.get("geometry")] +with rasterio.open("ph_any.tif") as r: transform=r.transform; H,W=r.height,r.width +zone=rasterize(shapes,out_shape=(H,W),transform=transform,fill=0,dtype="int32"); K=len(codes)+1 +fields={"p_harvest_any":"ph_any.tif","p_harvest_clearcut":"ph_clearcut.tif","p_harvest_partial":"ph_partial.tif"} +band={} +for key,fn in fields.items(): + with rasterio.open(fn) as r: a=r.read(1).astype("float64"); nod=r.nodata + valid=np.isfinite(a)&(zone>0) + if nod is not None: valid&=(a!=nod) + band[key]=(np.bincount(zone[valid],weights=a[valid],minlength=K),np.bincount(zone[valid],minlength=K)) +inv={v:k for k,v in codes.items()}; out={} +for i in range(1,K): + if band["p_harvest_any"][1][i]<5: continue + code=inv[i]; rec={"name":meta[code]["name"],"l1":meta[code]["l1"],"npix":int(band["p_harvest_any"][1][i])} + for key in fields: + s,cc=band[key]; rec[key]=round(float(s[i]/cc[i]),4) if cc[i]>0 else None + out[code]=rec +json.dump({"meta":{"source":"conus_render ph_*.tif harvest probability (TreeMap-based, ~3.1km Albers) zonal mean by EPA L3 ecoregion","n_ecoregions":len(out),"fields":list(fields.keys()),"note":"Per-EPA-Level-III-ecoregion mean harvest probability. P(any), P(stand-replacement), P(partial)."},"ecoregions":out},open("public/api/ecoregion_harvest_summary.json","w")) +print("wrote", len(out), "ecoregions") diff --git a/src/App.jsx b/src/App.jsx index add1dee..f869c95 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -7,6 +7,7 @@ import StumpagePanel from "./StumpagePanel.jsx"; import LandisStratified from "./LandisStratified.jsx"; import LandownerYields from "./LandownerYields.jsx"; import FaustmannRotation from "./FaustmannRotation.jsx"; +import EcoregionHarvest from "./EcoregionHarvest.jsx"; import AOIReport from "./AOIReport.jsx"; import { findFeature, agbAtAge, polygonCentroid, polygonAreaM2, pointInGeometry } from "./geo.js"; import { ownershipComposition, riskSummary, forestFraction, forestTypeDiversity, rampRelative, rampValues, median, percentile } from "./rasterSample.js"; @@ -471,6 +472,7 @@ export default function App(){ const [landis,setLandis] = useState(null); const [landowner,setLandowner] = useState(null); const [faustmann,setFaustmann] = useState(null); + const [ecoHarvest,setEcoHarvest] = useState(null); // v1.3 map/AOI tools const [ecoOn,setEcoOn] = useState(false); const [ecoGeo,setEcoGeo] = useState(null); @@ -499,6 +501,7 @@ export default function App(){ j("api/landis_stratified.json").then(setLandis).catch(()=>{}); j("api/landowner_yields.json").then(setLandowner).catch(()=>{}); j("api/faustmann_rotation.json").then(setFaustmann).catch(()=>{}); + j("api/ecoregion_harvest_summary.json").then(setEcoHarvest).catch(()=>{}); geo.features.forEach(ft=>{ const st=ft.properties.state; const c=s[st]; ft.properties.engines = c ? c.engines : 0; ft.properties.hasSeries = (c && c.has_series) ? 1 : 0; @@ -1403,12 +1406,13 @@ export default function App(){ {(!aoi || researchOpen) &&
{[["engines","Engine compare"],["rd","RD trend"],["divergence","Engine spread"], ["stumpage","Stumpage"],["landis","LANDIS stratified"], - ["landowner","Landowner yields"],["faustmann","Faustmann rotation"]].map(([k,lbl])=>{ + ["landowner","Landowner yields"],["faustmann","Faustmann rotation"],["ecoharvest","Ecoregion harvest"]].map(([k,lbl])=>{ const disabled = (k==="divergence" && !divergence) || (k==="stumpage" && !(stumpage && stumpage.series && stumpage.series[sel])) || (k==="landis" && !(landis && landis[sel])) || (k==="landowner" && !(landowner && landowner[sel])) || (k==="faustmann" && !(faustmann && faustmann[sel])) + || (k==="ecoharvest" && !ecoHarvest) || ((k==="engines"||k==="rd") && !series); return ; @@ -1426,6 +1430,7 @@ export default function App(){ {(!aoi || researchOpen) && tab==="landis" && } {(!aoi || researchOpen) && tab==="landowner" && } {(!aoi || researchOpen) && tab==="faustmann" && } + {(!aoi || researchOpen) && tab==="ecoharvest" && } {(!aoi || researchOpen) && (tab==="engines"||tab==="rd") && (<> {LANDIS_STATES.includes(sel) && (
diff --git a/src/EcoregionHarvest.jsx b/src/EcoregionHarvest.jsx new file mode 100644 index 0000000..6a40a86 --- /dev/null +++ b/src/EcoregionHarvest.jsx @@ -0,0 +1,77 @@ +// Ecoregion harvest-probability summary: per-EPA-Level-III-ecoregion mean +// harvest probability (any / stand-replacement / partial), zonal-averaged from +// the CONUS harvest-probability rasters. A sortable, searchable table — the +// "summarize by ecoregion" companion to the per-pixel raster overlays. +import { useMemo, useState } from "react"; + +const COLS = [ + ["p_harvest_any","P(any harvest)"], + ["p_harvest_clearcut","P(stand replacement)"], + ["p_harvest_partial","P(partial)"], +]; +// blue(low)->amber->red(high) cell shade for a 0..1 probability +const shade = v => { + if(v==null) return "transparent"; + const t=Math.max(0,Math.min(1,v)); + const stops=[[0.3,[47,98,158]],[0.6,[202,161,90]],[0.95,[224,90,90]]]; + let a=stops[0],b=stops[stops.length-1]; + for(let i=1;iMath.round(ca+f*(b[1][k]-ca))); + return `rgba(${c[0]},${c[1]},${c[2]},0.55)`; +}; + +export default function EcoregionHarvest({ data }){ + const [sortKey,setSortKey]=useState("p_harvest_any"); + const [asc,setAsc]=useState(false); + const [q,setQ]=useState(""); + const rows=useMemo(()=>{ + if(!data || !data.ecoregions) return []; + let r=Object.entries(data.ecoregions).map(([code,v])=>({code,...v})); + if(q){ const s=q.toLowerCase(); r=r.filter(x=>(x.name||"").toLowerCase().includes(s)||(x.l1||"").toLowerCase().includes(s)||x.code.includes(s)); } + r.sort((a,b)=>{ const av=a[sortKey],bv=b[sortKey]; + if(typeof av==="string") return asc?av.localeCompare(bv):bv.localeCompare(av); + return asc?(av-bv):(bv-av); }); + return r; + },[data,sortKey,asc,q]); + + if(!data) return
Ecoregion harvest summary not loaded.
; + const th=(key,lbl)=>( + { if(sortKey===key) setAsc(!asc); else { setSortKey(key); setAsc(false);} }} + style={{padding:"3px 7px",textAlign:key==="name"||key==="l1"?"left":"right",cursor:"pointer",whiteSpace:"nowrap"}} + title="click to sort">{lbl}{sortKey===key?(asc?" ▲":" ▼"):""}); + + return ( +
+
+ Harvest probability by ecoregion + {rows.length} EPA Level III ecoregions · mean P(harvest) · {data.meta && data.meta.source && data.meta.source.includes("3.1km")?"~3.1 km zonal mean":"zonal mean"} +
+ setQ(e.target.value)} placeholder="filter by ecoregion or biome…" + style={{width:"min(320px,90%)",padding:"4px 8px",marginBottom:6,fontSize:12, + background:"var(--panel,#172029)",color:"var(--fg,#e8eef2)",border:"1px solid var(--line,#2a3a47)",borderRadius:5}}/> +
+ + + {th("code","L3")}{th("name","Ecoregion")}{th("l1","Biome (L1)")} + {COLS.map(([k,l])=>th(k,l))} + + + {rows.map(r=>( + + + + + {COLS.map(([k])=>( + ))} + ))} + +
{r.code}{r.name}{(r.l1||"").toLowerCase().replace(/\b\w/g,c=>c.toUpperCase())} + {r[k]!=null?r[k].toFixed(2):"—"}
+
+
+ Mean modeled harvest probability per EPA Level III ecoregion, zonal-averaged from the CONUS harvest-probability rasters. P(any) is the chance a forested pixel is harvested in the window; the stand-replacement vs partial split shows the silvicultural character — high stand-replacement with low partial means clearcut-dominated regions, the reverse means selection/partial systems. Click a column to sort. +
+
+ ); +}