-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modularize plotting APIs #27
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,36 +8,122 @@ | |
|
||
import plotly.express as px | ||
import plotly.graph_objects as go | ||
from xarray import DataArray | ||
|
||
from atmospheric_explorer.cams_interfaces import ( | ||
EAC4Instance, | ||
InversionOptimisedGreenhouseGas, | ||
) | ||
from atmospheric_explorer.data_transformations import clip_and_concat_countries | ||
from atmospheric_explorer.units_conversion import convert_units_array | ||
|
||
PlotType = Enum("PlotType", ["time_series", "hovmoeller"]) | ||
DatasetName = Enum("DatasetName", ["eac4", "ghg"]) | ||
|
||
|
||
class PlottingInterface(ABC): | ||
"""Generic interface for all plotting APIs""" | ||
class EAC4Parameters: | ||
"""Parameters for EAC4 dataset""" | ||
|
||
_plot_type: PlotType | ||
# pylint: disable=too-few-public-methods | ||
|
||
def __init__( | ||
self: PlottingInterface, | ||
self: EAC4Parameters, | ||
_data_variable: str, | ||
_var_name: str, | ||
_time_period: str, | ||
): | ||
_file_format: str, | ||
_dates_range: str, | ||
_time_values: str, | ||
) -> None: | ||
self.data_variable = _data_variable | ||
self.var_name = _var_name | ||
self.time_period = _time_period | ||
self.file_format = _file_format | ||
self.dates_range = _dates_range | ||
self.time_values = _time_values | ||
|
||
@property | ||
def data_variable(self: PlottingInterface) -> str: | ||
"""Data variable name""" | ||
return self._data_variable | ||
|
||
@data_variable.setter | ||
def data_variable( | ||
self: PlottingInterface, | ||
data_variable_input: str, | ||
class GHGParameters: | ||
"""Parameters for GHG dataset""" | ||
|
||
# pylint: disable=too-few-public-methods | ||
|
||
def __init__( | ||
self: GHGParameters, | ||
_data_variable: str, | ||
_file_format: str, | ||
_quantity: str, | ||
_input_observations: str, | ||
_time_aggregation: str, | ||
_year: list[str], | ||
_month: list[str], | ||
) -> None: | ||
self._data_variable = data_variable_input | ||
self.data_variable = _data_variable | ||
self.file_format = _file_format | ||
self.quantity = _quantity | ||
self.input_observations = _input_observations | ||
self.time_aggregation = _time_aggregation | ||
self.year = _year | ||
self.month = _month | ||
|
||
|
||
class PlottingInterface(ABC): | ||
"""Generic interface for all plotting APIs""" | ||
|
||
_plot_type: PlotType | ||
|
||
def __init__( | ||
self: PlottingInterface, | ||
_dataset_name: DatasetName, | ||
_eac4_parameters: EAC4Parameters, | ||
_ghg_parameters: GHGParameters, | ||
_countries: list[str], | ||
_title: str, | ||
): | ||
self.dataset_name = _dataset_name | ||
self.title = _title | ||
self.countries = _countries | ||
# Qui sotto: puo` avere senso tenere comunque degli argomenti generici, | ||
# che ci sono per entrambi i dataset e possono servire sempre nei plot/dowload dati? | ||
match self.dataset_name: | ||
case DatasetName.eac4: | ||
self.eac4_parameters = _eac4_parameters | ||
self.data_variable = _eac4_parameters.data_variable | ||
self.var_name = _eac4_parameters.var_name | ||
case DatasetName.ghg: | ||
self.ghg_parameters = _ghg_parameters | ||
self.data_variable = _ghg_parameters.data_variable | ||
self.var_name = _ghg_parameters.var_name | ||
|
||
@abstractmethod | ||
def download_data(self: PlottingInterface): | ||
"""Downloads data""" | ||
match self.dataset_name: | ||
case DatasetName.eac4: | ||
assert self.eac4_parameters is not None | ||
data = EAC4Instance( | ||
self.eac4_parameters.data_variable, | ||
"netcdf", | ||
dates_range=self.eac4_parameters.dates_range, | ||
time_values=self.eac4_parameters.time_values, | ||
) | ||
data.download() | ||
case DatasetName.ghg: | ||
assert self.ghg_parameters is not None | ||
data = InversionOptimisedGreenhouseGas( | ||
data_variable=self.ghg_parameters.data_variable, | ||
file_format="zip", | ||
quantity=self.ghg_parameters.quantity, | ||
input_observations=self.ghg_parameters.input_observations, | ||
time_aggregation=self.ghg_parameters.time_aggregation, | ||
year=self.ghg_parameters.year, | ||
month=self.ghg_parameters.month, | ||
) | ||
data.download() | ||
return data.read_dataset() | ||
|
||
@abstractmethod | ||
def transform_data(self: PlottingInterface): | ||
"""Transforms data as needed""" | ||
raise NotImplementedError("Method not implemented") | ||
|
||
@abstractmethod | ||
def plot(self: PlottingInterface): | ||
|
@@ -48,18 +134,75 @@ def plot(self: PlottingInterface): | |
class TimeSeriesPlotInstance(PlottingInterface): | ||
"""Time series plot object""" | ||
|
||
# pylint: disable=too-many-arguments | ||
|
||
_plot_type: PlotType = PlotType.time_series | ||
_data_array: DataArray | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure this is the right type. It becomes a dataframe in line 188 |
||
|
||
def __init__( | ||
self: PlottingInterface, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to use named arguments here |
||
dataset_name: DatasetName, | ||
title: str, | ||
countries: list[str], | ||
resampling: str, | ||
eac4_parameters: EAC4Parameters, | ||
ghg_parameters: GHGParameters, | ||
): | ||
match dataset_name: | ||
case DatasetName.eac4: | ||
super().__init__( | ||
dataset_name, | ||
eac4_parameters, | ||
None, | ||
countries, | ||
title, | ||
) | ||
case DatasetName.ghg: | ||
super().__init__( | ||
dataset_name, | ||
None, | ||
ghg_parameters, | ||
countries, | ||
title, | ||
) | ||
self.resampling = resampling | ||
|
||
def download_data(self: TimeSeriesPlotInstance): | ||
self._data_array = super().download_data() | ||
|
||
def transform_data(self: TimeSeriesPlotInstance): | ||
df_down = self._data_array.rio.write_crs("EPSG:4326") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still have to change variable names here |
||
df_clipped = clip_and_concat_countries(df_down, self.countries).sel( | ||
admin=self.countries[0] | ||
) | ||
df_agg = ( | ||
df_clipped.mean(dim=["latitude", "longitude"]) | ||
.resample(time=self.resampling, restore_coord_dims=True) | ||
.mean(dim="time") | ||
) | ||
reference_value = df_agg.mean(dim="time") | ||
df_converted = convert_units_array(df_agg[self.var_name], self.data_variable) | ||
reference_value = df_converted.mean().values | ||
df_anomalies = df_converted - reference_value | ||
df_anomalies.attrs = df_converted.attrs | ||
self._data_array = df_anomalies | ||
|
||
# def __init__( | ||
# self: PlottingInterface, | ||
# data_variable: str, | ||
# var_name: str, | ||
# time_period: str, | ||
# ): | ||
# super().__init__(data_variable, var_name, time_period) | ||
|
||
def plot( | ||
self: TimeSeriesPlotInstance, | ||
) -> go.Figure: | ||
fig = px.line() | ||
def plot(self: TimeSeriesPlotInstance) -> go.Figure: | ||
fig = px.line( | ||
y=self._data_array.values, | ||
x=self._data_array.coords["time"], | ||
markers="o", | ||
) | ||
fig.update_xaxes(title="Month") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be a variable |
||
fig.update_yaxes(title=self._data_array.attrs["units"]) | ||
fig.update_layout( | ||
title={ | ||
"text": self.title, | ||
"x": 0.45, | ||
"y": 0.95, | ||
"automargin": True, | ||
"yref": "container", | ||
"font": {"size": 19}, | ||
} | ||
) | ||
return fig |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would make sense if we used named args here as well, in the
EAC4Instance
class. I'd also suggest we make these class names more similar (e.g. EAC4Instance and GHGInstance?)