diff --git a/README.md b/README.md index a23672c..0832a6d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # noaaplotter A python package to create fancy plots with NOAA weather data. - - ## Install #### Recommended conda install I recommend to use a fresh conda environment @@ -22,20 +20,29 @@ I recommend to use a fresh conda environment - requests - joblib - tqdm + - geemap + ## Examples ### Download data -#### Option 1: Download via script +#### Option 1 NOAA Daily Summaries: Download via script Download daily summaries (temperature + precipitation) from Kotzebue (or other station) from 1970-01-01 until 2021-12-31 * NOAA API Token is required: https://www.ncdc.noaa.gov/cdo-web/token `python download_data.py -o ./data/kotzebue.csv -sid GHCND:USW00026616 -start 1970-01-01 -end 2021-12-31 -t ` - #### Option 2: Download via browser + #### Option 2 NOAA Daily Summaries: Download via browser CSV files of "daily summaries" ("https://www.ncdc.noaa.gov/cdo-web/search") * Values: metric * File types: csv + + #### Option 3 ERA5 Daily: Download via script +Download daily summaries (temperature + precipitation) from Potsdam (13.05°E, 52.4°N) from 1980-01-01 until 2021-12-31 +* Google Earthengine account is required +* Caution: full dataset may take a few minutes + +`python download_data_ERA5.py -o ./data/potsdam_ERA5.csv -start 1980-01-01 -end 2021-12-31 -lat 52.4 -lon 13.05` ### Daily Mean Temperature and Precipitation values vs. Climate #### Entire year 1 January until 31 December (e.g. 1992) diff --git a/download_data_ERA5.py b/download_data_ERA5.py new file mode 100644 index 0000000..7075d54 --- /dev/null +++ b/download_data_ERA5.py @@ -0,0 +1,98 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Imports +import argparse +import csv +from datetime import datetime +import numpy as np +import os +import pandas as pd +import tqdm +from joblib import delayed, Parallel +from noaaplotter.utils import dl_noaa_api +import ee +import geemap + + +def main(): + """ + Main Function + :return: + """ + ##### Parse arguments ##### + parser = argparse.ArgumentParser(description='Parse arguments.') + + parser.add_argument('-o', dest='output_file', type=str, required=True, + default='data/data.csv', + help='csv file to save results') + + parser.add_argument('-lat', dest='lat', type=float, required=True, + help='Latitude of selected location') + + parser.add_argument('-lon', dest='lon', type=float, required=True, + help='Longitude of selected location') + + parser.add_argument('-loc', dest='loc_name', type=str, required=False, + default='', + help='Location name') + + parser.add_argument('-dt', dest='datatypes', type=list, required=False, default=['TMIN', 'TMAX', 'PRCP', 'SNOW']) + + parser.add_argument('-start', dest='start_date', type=str, required=True, + help='start date of plot ("yyyy-mm-dd")') + + parser.add_argument('-end', dest='end_date', type=str, required=True, + help='end date of plot ("yyyy-mm-dd")') + + args = parser.parse_args() + + # remove file if exists + if os.path.exists(args.output_file): + os.remove(args.output_file) + + ee.Initialize() + + EE_LAYER = 'ECMWF/ERA5/DAILY' + + location = ee.Geometry.Point([args.lon, args.lat]) + + # load ImageCollection + col = ee.ImageCollection(EE_LAYER).filterBounds(location).filterDate(args.start_date, args.end_date) + + # Download data + print("Start downloading daily ERA5 data.") + print("Download may take a while.\n1yr: ~5 seconds\n10yrs: ~35 seconds\n50yrs: ~8 min") + result = geemap.extract_pixel_values(col, region=location) + out_dict = result.getInfo() + + df_gee = pd.DataFrame(data=[out_dict.keys(), out_dict.values()]).T + + # parse dates and values + df_gee['time'] = df_gee[0].apply(lambda x: f'{x[:4]}-{x[4:6]}-{x[6:8]}') + df_gee['feature'] = df_gee[0].apply(lambda x: x[9:]) + df_gee['value'] = df_gee[1] + + df = df_gee.pivot_table(values='value', columns=['feature'], index='time')#.reset_index(drop=False) + + # #### recalculate values + df_new = pd.DataFrame(index=df.index) + + temperature_cols = ['mean_2m_air_temperature', 'minimum_2m_air_temperature', 'maximum_2m_air_temperature', 'dewpoint_2m_temperature'] + precipitation_cols = ['total_precipitation'] + df_joined = df_new.join(df[temperature_cols] - 273.15).join(df[precipitation_cols] *1e3).reset_index(drop=False) + + # Create Output + rename_dict = {'time':'DATE', 'total_precipitation':'PRCP', 'mean_2m_air_temperature':'TAVG', 'maximum_2m_air_temperature':'TMAX', 'minimum_2m_air_temperature':'TMIN'} + df_renamed = df_joined.rename(columns=rename_dict) + df_renamed['NAME'] = '' + df_renamed['STATION'] = '' + df_renamed['SNWD'] = '' + + output_cols = ["STATION","NAME","DATE","PRCP","SNWD","TAVG","TMAX","TMIN"] + df_save = df_renamed[output_cols].astype(str) + + df_save.to_csv(args.output_file, index=False) + + +if __name__ == "__main__": + main() diff --git a/environment.yml b/environment.yml index 09b45a1..ec5ae24 100644 --- a/environment.yml +++ b/environment.yml @@ -11,3 +11,4 @@ dependencies: - requests - joblib - tqdm + - geemap diff --git a/noaaplotter/noaaplotter.py b/noaaplotter/noaaplotter.py index 10dd31c..3a1bac1 100644 --- a/noaaplotter/noaaplotter.py +++ b/noaaplotter/noaaplotter.py @@ -10,6 +10,7 @@ import numpy as np ######################## from matplotlib import pyplot as plt, dates +import matplotlib.dates as mdates from .dataset import NOAAPlotterDailyClimateDataset as DS_daily from .dataset import NOAAPlotterDailySummariesDataset as Dataset @@ -80,7 +81,8 @@ def plot_weather_series(self, start_date, end_date, plot_pmax='auto', plot_snowmax='auto', plot_extrema=True, show_plot=True, show_snow_accumulation=True, save_path=False, - figsize=(9, 6), legend_fontsize='x-small', dpi=300): + figsize=(9, 6), legend_fontsize='x-small', dpi=300, + title=None): """ Plotting Function to show observed vs climate temperatures and snowfall :param dpi: @@ -203,6 +205,8 @@ def plot_weather_series(self, start_date, end_date, ax_t.set_ylim(plot_tmin, plot_tmax) ax_t.set_ylabel('Temperature in °C') ax_t.set_xlabel('Date') + if title: + ax_t.set_title(title) # add legend legend_handle_t = [fb, cm, cm_hi, fill_r, fill_b] @@ -282,6 +286,11 @@ def plot_weather_series(self, start_date, end_date, bbox_to_anchor=(0.5, -0.2)) ax_p.legend(legend_handle_p, legend_text_p, loc='upper left', fontsize=legend_fontsize) + # set locator to monthly + locator = dates.MonthLocator() + ax_t.xaxis.set_major_locator(locator) + ax_p.xaxis.set_major_locator(locator) + fig.tight_layout() # Save Figure diff --git a/plot_daily.py b/plot_daily.py index eec9deb..b4dafd2 100644 --- a/plot_daily.py +++ b/plot_daily.py @@ -70,6 +70,10 @@ def main(): default=[9, 6], help='figure size in inches width x height. 15 10 recommended for 1 year, 30 10 for 2 years ...') + parser.add_argument('-title', dest='title', type=str, required=False, + default=None, + help='Plot title') + args = parser.parse_args() ##### Download from NOAA ##### @@ -91,7 +95,8 @@ def main(): plot_pmax=args.p_range, plot_snowmax=args.s_range, dpi=args.dpi, - figsize=args.figsize) + figsize=args.figsize, + title=args.title) if __name__ == "__main__": main()