Skip to content

Commit

Permalink
Merge pull request #35 from azogue/feature/new-zones
Browse files Browse the repository at this point in the history
✨ More kinds of chart zones + CSS for SVG styling
  • Loading branch information
azogue authored Jun 12, 2023
2 parents fbbb1f0 + cc3671f commit 78d66a3
Show file tree
Hide file tree
Showing 16 changed files with 1,249 additions and 112 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.9.0] - ✨ More kinds of chart zones + CSS for SVG styling - 2023-06-12

Define new enclosed areas in chart between constant RH lines and constant volume or enthalpy values,
and enable full potencial for SVG customization by including CSS and/or `<defs>` inside it.

##### Changes

- ✨ Add new kind of overlay **zones 'enthalpy-rh' and 'volume-rh'**, to define chart areas delimited between 2 constant enthalpy or volume lines, and 2 constant humidity lines
- 🎨 Add `chart.remove_zones()` method, like the existent `.remove_legend()` and `.remove_annotations()`, to clear matplotlib current Axes from each kind of annotated objects
- 🐛 Fix artists registry not mirroring objects in plot, by setting a new one each time `chart.plot()` is invoked
- ♻️ Set gid for 'chart_legend_background' item if frameon is enabled
- ✨ Pass **optional CSS and SVG <defs>** to `chart.make_svg()`, to include extra-styling inside the produced SVG 🌈
- ✨ Add method `chart.plot_over_saturated_zone()` to create a filled patch in the over-saturated zone of the psychrochart, to colorize that area
- 🦄 tests: Add dynamic effects and CSS rules to splash chart for a show-off of SVG styling capabilities
- 🚀 env: Bump version and update CHANGELOG

## [0.8.0] - ✨ Lazy generation of chart data, 'Artist' registry, and better SVG export - 2023-06-11

Now all chart-data parameters, including the overlayed zones, are included in the `ChartConfig`,
Expand Down
3 changes: 2 additions & 1 deletion psychrochart/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""A library to make psychrometric charts and overlay information in them."""
from psychrochart.chart import PsychroChart
from psychrochart.models.annots import ChartAnnots
from psychrochart.models.config import ChartConfig, ChartZones
from psychrochart.models.config import ChartConfig, ChartZone, ChartZones
from psychrochart.models.curves import PsychroCurve, PsychroCurves
from psychrochart.models.parsers import load_config, load_zones
from psychrochart.models.styles import (
Expand All @@ -15,6 +15,7 @@
"ChartAnnots",
"ChartConfig",
"ChartZones",
"ChartZone",
"load_config",
"load_zones",
"PsychroChart",
Expand Down
67 changes: 50 additions & 17 deletions psychrochart/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
make_constant_dry_bulb_v_line,
make_saturation_line,
)
from psychrochart.chartzones import make_over_saturated_zone
from psychrochart.models.annots import ChartAnnots
from psychrochart.models.config import ChartConfig, ChartZones
from psychrochart.models.curves import PsychroChartModel
from psychrochart.models.curves import PsychroChartModel, PsychroCurve
from psychrochart.models.parsers import (
ConvexGroupTuple,
load_extra_annots,
Expand All @@ -41,7 +42,7 @@
get_pressure_pa,
update_psychrochart_data,
)
from psychrochart.util import mod_color
from psychrochart.util import add_styling_to_svg, mod_color


def _select_fig_canvas(
Expand Down Expand Up @@ -308,6 +309,26 @@ def plot_vertical_dry_bulb_temp_line(
self._artists.annotations,
)

def plot_over_saturated_zone(
self, color_fill: str | list[float] = "#0C92F6FF"
) -> PsychroCurve | None:
"""Add a colored zone in chart to fill the over-saturated space."""
# ensure chart is plotted
current_ax = self.axes
if (
curve_sat_zone := make_over_saturated_zone(
self.saturation,
dbt_min=self.config.dbt_min,
dbt_max=self.config.dbt_max,
w_min=self.config.w_min,
w_max=self.config.w_max,
color_fill=color_fill,
)
) is not None:
plot_curve(curve_sat_zone, current_ax)
self._artists.zones.update()
return curve_sat_zone

def plot_legend(
self,
loc: str = "upper left",
Expand All @@ -320,24 +341,25 @@ def plot_legend(
**params,
) -> None:
"""Append a legend to the psychrochart plot."""
reg_artist(
"chart_legend",
self.axes.legend(
loc=loc,
markerscale=markerscale,
frameon=frameon,
edgecolor=edgecolor,
fontsize=fontsize,
fancybox=fancybox,
labelspacing=labelspacing,
**params,
),
self._artists.layout,
legend = self.axes.legend(
loc=loc,
markerscale=markerscale,
frameon=frameon,
edgecolor=edgecolor,
fontsize=fontsize,
fancybox=fancybox,
labelspacing=labelspacing,
**params,
)
if frameon:
legend.get_frame().set_gid("chart_legend_background")
reg_artist("chart_legend", legend, self._artists.layout)

def plot(self, ax: Axes | None = None) -> Axes:
"""Plot the psychrochart and return the matplotlib Axes instance."""
self.process_chart()
# instantiate a new artist registry for the new plot
self._artists = ChartRegistry()
if ax is not None:
self._fig = ax.get_figure()
else:
Expand All @@ -357,6 +379,12 @@ def plot(self, ax: Axes | None = None) -> Axes:
plot_chart(self, self._axes, self._artists)
return self._axes

def remove_zones(self) -> None:
"""Remove the zones in the chart to reuse it."""
for patch in self._artists.zones.values():
patch.remove()
self._artists.zones = {}

def remove_annotations(self) -> None:
"""Remove the annotations made in the chart to reuse it."""
for line in self._artists.annotations.values():
Expand Down Expand Up @@ -388,12 +416,17 @@ def save(
canvas_use(self._fig).print_figure(path_dest, **params)
gc.collect()

def make_svg(self, **params) -> str:
def make_svg(
self,
css_styles: str | Path | None = None,
svg_definitions: str | None = None,
**params,
) -> str:
"""Generate chart as SVG and return as text."""
svg_io = StringIO()
self.save(svg_io, canvas_cls=FigureCanvasSVG, **params)
svg_io.seek(0)
return svg_io.read()
return add_styling_to_svg(svg_io.read(), css_styles, svg_definitions)

def close_fig(self) -> None:
"""Close the figure plot."""
Expand Down
Loading

0 comments on commit 78d66a3

Please sign in to comment.