Skip to content

Commit

Permalink
Modify TimeSeriesMap, SeriesMap and BaseSeriesMap
Browse files Browse the repository at this point in the history
  • Loading branch information
29riyasaxena committed Mar 6, 2024
1 parent d0df31b commit 3bac6c0
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 38 deletions.
74 changes: 59 additions & 15 deletions python/grass/jupyter/baseseriesmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,24 @@


class BaseSeriesMap:
"""
Base class for SeriesMap and TimeSeriesMap
"""

def __init__(
self, width=None, height=None, env=None, use_region=False, saved_region=None
):
"""Creates an instance of the visualizations class.
:param int width: width of map in pixels
:param int height: height of map in pixels
:param str env: environment
:param use_region: if True, use either current or provided saved region,
else derive region from rendered layers
:param saved_region: if name of saved_region is provided,
this region is then used for rendering
"""

# Copy Environment
if env:
self._env = env.copy()
Expand All @@ -21,6 +36,8 @@ def __init__(

self._base_layer_calls = []
self._layers_rendered = False
self._layer_filename_dict = {}
self._date_filename_dict = {}
self._width = width
self._height = height
# Create a temporary directory for our PNG images
Expand All @@ -36,16 +53,24 @@ def cleanup(tmpdir):
weakref.finalize(self, cleanup, self._tmpdir)

# Handle regions in respective classes
self.use_region = use_region
self.saved_region = saved_region

def __getattr__(self, name):
"""
Parse attribute to GRASS display module. Attribute should be in
the form 'd_module_name'. For example, 'd.rast' is called with 'd_rast'.
"""
# Check to make sure format is correct
if not name.startswith("d_"):
raise AttributeError(_("Module must begin with 'd_'"))
# Reformat string
grass_module = name.replace("_", ".")
self.grass_module = name.replace("_", ".")
# Assert module exists
if not shutil.which(grass_module):
raise AttributeError(_("Cannot find GRASS module {}").format(grass_module))
if not shutil.which(self.grass_module):
raise AttributeError(
_("Cannot find GRASS module {}").format(self.grass_module)
)

# Wrapper function is in respective classes

Expand All @@ -66,11 +91,11 @@ def render(self):
# Make base image (background and baselayers)
# Random name needed to avoid potential conflict with layer names
random_name_base = gs.append_random("base", 8) + ".png"
base_file = os.path.join(self._tmpdir.name, random_name_base)
self.base_file = os.path.join(self._tmpdir.name, random_name_base)
img = Map(
width=self._width,
height=self._height,
filename=base_file,
filename=self.base_file,
use_region=True,
env=self._env,
read_file=True,
Expand All @@ -88,12 +113,17 @@ def show(
options=None,
value=None,
description=None,
max=None,
max_value=None,
label=None,
):
"""Create interactive timeline slider.
param str slider_width: width of datetime selection slider
param list options: list of options for the slider
param str value: initial value of the slider
param str description: description of the slider
param int max_value: maximum value of the slider
param bool label: include date/time stamp on each frame
The slider_width parameter sets the width of the slider in the output cell.
It should be formatted as a percentage (%) between 0 and 100 of the cell width
Expand Down Expand Up @@ -127,7 +157,7 @@ def show(
interval=500,
value=0,
min=0,
max=max,
max_value=max_value,
step=1,
description="Press play",
disabled=False,
Expand All @@ -143,13 +173,15 @@ def change_slider(change):
play.observe(change_slider, names="value")

# Display image associated with datetime
def change_image(date):
# Look up layer name for date
filename = self._date_filename_dict[date]
def change_image(parameter, is_date=True):
if is_date:
filename = self._date_filename_dict[parameter]
else:
filename = self._layer_filename_dict[parameter]

with open(filename, "rb") as rfile:
out_img.value = rfile.read()

# Return interact widget with image and slider
widgets.interactive_output(change_image, {label: slider})
layout = widgets.Layout(
width="100%", display="inline-flex", flex_flow="row wrap"
Expand All @@ -167,14 +199,26 @@ def save(
text_size=12,
text_color="gray",
):
"""
Creates a GIF animation of rendered layers.
Text color must be in a format accepted by PIL ImageColor module. For supported
formats, visit:
https://pillow.readthedocs.io/en/stable/reference/ImageColor.html#color-names
param str filename: name of output GIF file
param list save_files: list containing the file paths
param list labels: list of layer labels
param int duration: time to display each frame; milliseconds
param bool label: include date/time stamp on each frame
param str font: font file
param int text_size: size of date/time text
param str text_color: color to use for the text.
"""
# Render images if they have not been already
if not self._layers_rendered:
self.render()

input_files = []
for date in self._dates:
input_files.append(self._date_filename_dict[date])

save_gif(
save_files,
filename,
Expand Down
34 changes: 24 additions & 10 deletions python/grass/jupyter/seriesmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ def __init__(
self._series_length = None
self._calls = []
self._series_added = False
self._layer_filename_dict = {}
self._names = []

# Handle Regions
Expand Down Expand Up @@ -184,24 +183,38 @@ def render(self):
for grass_module, kwargs in self._calls[i]:
img.run(grass_module, **kwargs)

def show(self, slider_width=None):
def show(self, slider_width=None, *args, **kwargs):
"""Create interactive timeline slider.
param str slider_width: width of datetime selection slider
The slider_width parameter sets the width of the slider in the output cell.
It should be formatted as a percentage (%) between 0 and 100 of the cell width
or in pixels (px). Values should be formatted as strings and include the "%"
or "px" suffix. For example, slider_width="80%" or slider_width="500px".
slider_width is passed to ipywidgets in ipywidgets.Layout(width=slider_width).
"""
lookup = list(zip(self._names, range(self._series_length)))
super().show(
slider_width,
return super().show(
slider_width=slider_width,
*args,
**kwargs,
options=lookup,
value=0,
max=self._series_length - 1,
max_value=self._series_length - 1,
label="index",
)

def save(
self,
filename,
*args,
duration=500,
label=True,
font=None,
text_size=12,
text_color="gray",
**kwargs,
):
"""
Creates a GIF animation of rendered layers.
Expand All @@ -217,18 +230,19 @@ def save(
param int text_size: size of date/time text
param str text_color: color to use for the text.
"""

tmp_files = []
for _, file in self._layer_filename_dict.items():
tmp_files.append(file)

super().save(
filename,
tmp_files,
self._names,
return super().save(
filename=filename,
save_files=tmp_files,
labels=self._names,
*args,
duration=duration,
label=label,
font=font,
text_size=text_size,
text_color=text_color,
**kwargs,
)
45 changes: 32 additions & 13 deletions python/grass/jupyter/timeseriesmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ def __init__(
self._layers = None
self._dates = None
self._date_layer_dict = {}
self._date_filename_dict = {}

# Handle Regions
self._region_manager = RegionManagerForTimeSeries(
Expand Down Expand Up @@ -212,6 +211,9 @@ def add_vector_series(self, timeseries, fill_gaps=False):
self._region_manager.set_region_from_timeseries(self.timeseries)

def __getattr__(self, name):
"""Parse attribute to GRASS display module. Attribute should be in
the form 'd_module_name'. For example, 'd.rast' is called with 'd_rast'.
"""
super().__getattr__(name)

def wrapper(**kwargs):
Expand Down Expand Up @@ -334,24 +336,38 @@ def render(self):
# Render image
self._render_layer(layer, filename)

def show(self, slider_width=None):
super().show(
def show(self, slider_width=None, *args, **kwargs):
"""Create interactive timeline slider.
param str slider_width: width of datetime selection slider
The slider_width parameter sets the width of the slider in the output cell.
It should be formatted as a percentage (%) between 0 and 100 of the cell width
or in pixels (px). Values should be formatted as strings and include the "%"
or "px" suffix. For example, slider_width="80%" or slider_width="500px".
slider_width is passed to ipywidgets in ipywidgets.Layout(width=slider_width).
"""
return super().show(
slider_width=slider_width,
*args,
**kwargs,
options=self._dates,
value=self._dates[0],
description=_("Date/Time"),
max=len(self._dates) - 1,
max_value=len(self._dates) - 1,
label="date",
)

def save(
self,
filename,
*args,
duration=500,
label=True,
font="DejaVuSans.ttf",
text_size=12,
text_color="gray",
**kwargs,
):
"""
Creates a GIF animation of rendered layers.
Expand All @@ -372,13 +388,16 @@ def save(
for date in self._dates:
input_files.append(self._date_filename_dict[date])

super().save(
filename,
input_files,
self._dates,
duration,
label,
font,
text_size,
text_color,
return super().save(
filename=filename,
save_files=input_files,
labels=self._dates,
labels=self._names,
*args,
duration=duration,
label=label,
font=font,
text_size=text_size,
text_color=text_color,
**kwargs,
)

0 comments on commit 3bac6c0

Please sign in to comment.