diff --git a/download_phenocam.py b/download_phenocam.py index 2e21733..f352539 100644 --- a/download_phenocam.py +++ b/download_phenocam.py @@ -1,7 +1,10 @@ +import csv +import json import requests from pathlib import Path from datetime import datetime from concurrent.futures import ThreadPoolExecutor, as_completed +from io import StringIO PHENOCAM_API = "https://phenocam.nau.edu/api" @@ -144,3 +147,70 @@ def download_phenocam(season, site_position, site_name, date_range=None): print("[PhenoCam] Completed") + +def download_phenocam_greenness(season, site_position, site_name, date_range=None): + """Fetch greenness-index time series from PhenoCam API.""" + datetime_range = date_range or f"{season}-01-01/{season}-12-31" + output_file = Path(f"data/{site_name}/{season}/raw/phenocam/timeseries.json") + output_file.parent.mkdir(parents=True, exist_ok=True) + + start_date, end_date = datetime_range.split("/") + start_dt = datetime.strptime(start_date, "%Y-%m-%d") + end_dt = datetime.strptime(end_date, "%Y-%m-%d") + + print(f"[PhenoCam-GI] Fetching greenness-index time series: {site_name}, {season}") + + # Get ROIs for site (paginate through results) + try: + url = f"{PHENOCAM_API}/roilists/" + params = {"site": site_name} + rois = [] + while url: + r = requests.get(url, params=params, timeout=30) + r.raise_for_status() + data = r.json() + rois.extend([roi for roi in data.get("results", []) if roi["site"] == site_name]) + url = data.get("next") + params = None + if len(rois) > 0: + break + if not rois: + print(f"[PhenoCam-GI] No ROIs found for site '{site_name}'") + return + csv_url = rois[0].get("one_day_summary") + if not csv_url: + print(f"[PhenoCam-GI] No CSV data URL found for ROI") + return + except requests.exceptions.RequestException as e: + print(f"[PhenoCam-GI] Error fetching ROIs: {e}") + return + + # Fetch CSV data + try: + csv_r = requests.get(csv_url, timeout=30) + csv_r.raise_for_status() + lines = [l for l in csv_r.text.split('\n') if l and not l.startswith('#')] + reader = csv.DictReader(lines) + timeseries = [] + for row in reader: + try: + date_str = row.get("date") + if not date_str: + continue + date = datetime.strptime(date_str, "%Y-%m-%d") + if start_dt <= date <= end_dt: + gcc = row.get("gcc_mean") + if gcc and gcc != "NA": + timeseries.append({"date": date.isoformat(), "greenness_index": float(gcc)}) + except (ValueError, KeyError): + continue + except requests.exceptions.RequestException as e: + print(f"[PhenoCam-GI] Error fetching CSV: {e}") + return + + timeseries.sort(key=lambda x: x["date"]) + with open(output_file, "w") as f: + json.dump(timeseries, f, indent=2) + + print(f"[PhenoCam-GI] Saved: {output_file} ({len(timeseries)} entries)") + diff --git a/run.py b/run.py index 37bf7ac..d4b74d8 100644 --- a/run.py +++ b/run.py @@ -8,7 +8,7 @@ from ndvi import ( ) from download_s2 import download_s2 from download_s3 import download_s3 -from download_phenocam import download_phenocam +from download_phenocam import download_phenocam, download_phenocam_greenness from clouds import detect_clouds @@ -18,6 +18,7 @@ def run_pipeline(season, site_position, site_name): # download_s2(season, site_position, site_name) # download_s3(season, site_position, site_name) # download_phenocam(season, site_position, site_name) + download_phenocam_greenness(season, site_position, site_name) # print(f"Generating NDVI for raw data: {site_name}, {season}") # generate_ndvi_raw(season, site_position, site_name) @@ -34,10 +35,10 @@ def run_pipeline(season, site_position, site_name): #run_efast(season, site_position, site_name) #print(f"Post-processing data: {site_name}, {season}") - process_cropped(season, site_position, site_name) + #process_cropped(season, site_position, site_name) #print(f"Generating NDVI for final outputs: {site_name}, {season}") - generate_ndvi_post_process(season, site_position, site_name) - create_ndvi_timeseries_post_process(season, site_position, site_name) + #generate_ndvi_post_process(season, site_position, site_name) + #create_ndvi_timeseries_post_process(season, site_position, site_name) except Exception as e: print(f"Error: {e}") diff --git a/webapp/index.html b/webapp/index.html index 396e1c9..49574f0 100644 --- a/webapp/index.html +++ b/webapp/index.html @@ -18,6 +18,7 @@ .phenocam-date { font-size: 11px; margin-top: 5px; color: #999; } .phenocam-image { width: 100%; height: 200px; object-fit: contain; border: 1px solid #ccc; } .sitemap { height: 200px; border: 1px solid #ccc; margin-top: 32px; } + #greennesstimeseries { margin-top: 0; } #dateSlider { width: 100%; } #dateDisplay { text-align: center; margin: 10px 0; font-size: 18px; } .maps { display: flex; gap: 20px; } @@ -42,12 +43,14 @@