icplot 0.0.6__tar.gz → 0.0.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {icplot-0.0.6/src/icplot.egg-info → icplot-0.0.8}/PKG-INFO +4 -2
  2. {icplot-0.0.6 → icplot-0.0.8}/pyproject.toml +5 -3
  3. icplot-0.0.8/src/icplot/color.py +13 -0
  4. {icplot-0.0.6 → icplot-0.0.8}/src/icplot/geometry.py +1 -7
  5. icplot-0.0.8/src/icplot/graph/__init__.py +1 -0
  6. icplot-0.0.8/src/icplot/graph/matplotlib_plot_backend.py +69 -0
  7. icplot-0.0.8/src/icplot/graph/plot.py +163 -0
  8. icplot-0.0.8/src/icplot/graph/series.py +54 -0
  9. {icplot-0.0.6 → icplot-0.0.8/src/icplot.egg-info}/PKG-INFO +4 -2
  10. {icplot-0.0.6 → icplot-0.0.8}/src/icplot.egg-info/SOURCES.txt +6 -0
  11. {icplot-0.0.6 → icplot-0.0.8}/src/icplot.egg-info/requires.txt +3 -1
  12. {icplot-0.0.6 → icplot-0.0.8}/test/test_cairo_interface.py +1 -0
  13. {icplot-0.0.6 → icplot-0.0.8}/test/test_gantt_chart.py +2 -3
  14. {icplot-0.0.6 → icplot-0.0.8}/test/test_image_utils.py +3 -3
  15. icplot-0.0.8/test/test_line_plots.py +23 -0
  16. {icplot-0.0.6 → icplot-0.0.8}/test/test_tex_interface.py +1 -0
  17. {icplot-0.0.6 → icplot-0.0.8}/LICENSE +0 -0
  18. {icplot-0.0.6 → icplot-0.0.8}/README.md +0 -0
  19. {icplot-0.0.6 → icplot-0.0.8}/setup.cfg +0 -0
  20. {icplot-0.0.6 → icplot-0.0.8}/src/icplot/__init__.py +0 -0
  21. {icplot-0.0.6 → icplot-0.0.8}/src/icplot/cairo_interface.py +0 -0
  22. {icplot-0.0.6 → icplot-0.0.8}/src/icplot/gantt.py +0 -0
  23. {icplot-0.0.6 → icplot-0.0.8}/src/icplot/image_utils.py +0 -0
  24. {icplot-0.0.6 → icplot-0.0.8}/src/icplot/main_cli.py +0 -0
  25. {icplot-0.0.6 → icplot-0.0.8}/src/icplot/project_elements.py +0 -0
  26. {icplot-0.0.6 → icplot-0.0.8}/src/icplot/tex_interface.py +0 -0
  27. {icplot-0.0.6 → icplot-0.0.8}/src/icplot.egg-info/dependency_links.txt +0 -0
  28. {icplot-0.0.6 → icplot-0.0.8}/src/icplot.egg-info/entry_points.txt +0 -0
  29. {icplot-0.0.6 → icplot-0.0.8}/src/icplot.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: icplot
3
- Version: 0.0.6
3
+ Version: 0.0.8
4
4
  Summary: Utilities for generating plots and graphics for technical reports.
5
5
  Author-email: "James Grogan, Irish Centre for High End Computing" <james.grogan@ichec.ie>
6
6
  Project-URL: Repository, https://git.ichec.ie/performance/toolshed/icplot
@@ -16,13 +16,15 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: wand
18
18
  Requires-Dist: matplotlib
19
- Requires-Dist: iccore==0.0.8
19
+ Requires-Dist: iccore==0.0.9
20
20
  Provides-Extra: test
21
21
  Requires-Dist: pytest; extra == "test"
22
22
  Requires-Dist: pytest-cov; extra == "test"
23
23
  Requires-Dist: pytest-sugar; extra == "test"
24
24
  Requires-Dist: black; extra == "test"
25
25
  Requires-Dist: mypy; extra == "test"
26
+ Requires-Dist: flake8; extra == "test"
27
+ Requires-Dist: pylint; extra == "test"
26
28
  Provides-Extra: cairo
27
29
  Requires-Dist: pycairo; extra == "cairo"
28
30
  Requires-Dist: CairoSVG; extra == "cairo"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "icplot"
3
- version = "0.0.6"
3
+ version = "0.0.8"
4
4
  authors = [
5
5
  { name="James Grogan, Irish Centre for High End Computing", email="james.grogan@ichec.ie" },
6
6
  ]
@@ -18,7 +18,7 @@ keywords = ["Publishing", "Technical Reports", "Graphics"]
18
18
 
19
19
  dependencies = ["wand",
20
20
  "matplotlib",
21
- "iccore==0.0.8"]
21
+ "iccore==0.0.9"]
22
22
 
23
23
  [project.urls]
24
24
  Repository = "https://git.ichec.ie/performance/toolshed/icplot"
@@ -30,7 +30,9 @@ test = [
30
30
  "pytest-cov",
31
31
  "pytest-sugar",
32
32
  "black",
33
- "mypy"
33
+ "mypy",
34
+ "flake8",
35
+ "pylint"
34
36
  ]
35
37
 
36
38
  cairo = [
@@ -0,0 +1,13 @@
1
+ class Color:
2
+
3
+ def __init__(self, r=1, g=1, b=1, a=1):
4
+ self.r = r
5
+ self.g = g
6
+ self.b = b
7
+ self.a = a
8
+
9
+
10
+ class ColorMap:
11
+
12
+ def __init__(self, label: str):
13
+ self.label = label
@@ -1,10 +1,4 @@
1
- class Color:
2
-
3
- def __init__(self, r=1, g=1, b=1, a=1):
4
- self.r = r
5
- self.g = g
6
- self.b = b
7
- self.a = a
1
+ from .color import Color
8
2
 
9
3
 
10
4
  class Font:
@@ -0,0 +1 @@
1
+ from .plot import * # NOQA
@@ -0,0 +1,69 @@
1
+ from pathlib import Path
2
+ import logging
3
+
4
+ logging.getLogger("matplotlib").setLevel(logging.WARNING)
5
+ import matplotlib # NOQA
6
+
7
+ default_backend = matplotlib.get_backend()
8
+ matplotlib.use("Agg")
9
+ import matplotlib.pyplot as plt # NOQA
10
+ import matplotlib as mpl # NOQA
11
+
12
+ from icplot.color import ColorMap # NOQA
13
+
14
+
15
+ class MatplotlibColorMap(ColorMap):
16
+
17
+ def __init__(self, label: str):
18
+ super().__init__(label)
19
+ self.c_map = mpl.colormaps[label]
20
+ self.cmap_start_offset = 0.25
21
+ self.cmap_scale = 2
22
+
23
+ def get_color(self, cursor: int, values: list):
24
+ """
25
+ Returns a colour based on a cmap and how far across
26
+ the datasets you are
27
+ """
28
+ position = cursor / len(values)
29
+ return self.c_map(self.cmap_start_offset + (position / self.cmap_scale))
30
+
31
+
32
+ class MatplotlibPlotBackend:
33
+
34
+ def __init__(self):
35
+ self.ax = plt.subplot(111)
36
+
37
+ def set_decorations(self, x_label, y_label, title, x_ticks, y_ticks):
38
+ self.ax.legend(loc="upper left")
39
+ if x_label:
40
+ self.ax.set_xlabel(x_label)
41
+ if y_label:
42
+ self.ax.set_ylabel(y_label)
43
+ if title:
44
+ self.ax.set_title(title)
45
+ if x_ticks:
46
+ self.ax.set_xticks(x_ticks)
47
+ if y_ticks:
48
+ self.ax.set_yticks(y_ticks)
49
+
50
+ def plot_line(self, x, y, label, color):
51
+ self.ax.plot(x, y, label=label, color=color)
52
+
53
+ def plot_scatter(self, data, label, color):
54
+ self.ax.plot(data, label=label, color=color)
55
+
56
+ def make_subplot(self, rows, cols, count):
57
+ plt.subplot(rows, cols, count)
58
+
59
+ def plot_image(self, data):
60
+ plt.imshow(data)
61
+ plt.axis("off")
62
+
63
+ def render(self, path: Path | None = None):
64
+ if path:
65
+ plt.savefig(path)
66
+ else:
67
+ plt.switch_backend(default_backend)
68
+ plt.show()
69
+ plt.switch_backend("Agg")
@@ -0,0 +1,163 @@
1
+ """
2
+ This module has content for generating plots
3
+ """
4
+
5
+ import random
6
+ from pathlib import Path
7
+ from typing import cast
8
+ import json
9
+
10
+ from .series import PlotSeries, LinePlotSeries, ScatterPlotSeries, ImageSeries
11
+ from .matplotlib_plot_backend import MatplotlibPlotBackend, MatplotlibColorMap
12
+
13
+
14
+ class Range:
15
+
16
+ def __init__(self, lower, upper, step):
17
+ self.lower = lower
18
+ self.upper = upper
19
+ self.step = step
20
+
21
+ def eval(self):
22
+ return range(self.lower, self.upper, self.step)
23
+
24
+ def serialize(self):
25
+ return {"lower": self.lower, "upper": self.upper, "step": self.step}
26
+
27
+ def __str__(self):
28
+ return json.dumps(self.serialize(), ident=4)
29
+
30
+
31
+ class Plot:
32
+ """
33
+ A generic plot with optional axis ticks
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ title: str = "",
39
+ x_label: str = "",
40
+ y_label: str = "",
41
+ legend_label: str = "",
42
+ ) -> None:
43
+ self.series: list[PlotSeries] = []
44
+ self.title = title
45
+ self.size: tuple | None = None
46
+ self.plot_type = ""
47
+ self.x_label = x_label
48
+ self.y_label = y_label
49
+ self.legend_label = legend_label
50
+ self.x_ticks: Range | None = None
51
+ self.y_ticks: Range | None = None
52
+ self.backend = MatplotlibPlotBackend()
53
+ self.cmap = MatplotlibColorMap("viridis")
54
+
55
+ def set_colour_map(self, cmap):
56
+ self.c_map = MatplotlibColorMap(cmap)
57
+
58
+ def set_x_ticks(self, lower, upper, step):
59
+ self.x_ticks = Range(lower, upper + 1, step)
60
+
61
+ def set_y_ticks(self, lower, upper, step):
62
+ self.y_ticks = Range(lower, upper + 1, step)
63
+
64
+ def set_decorations(self):
65
+
66
+ xtick_range = None
67
+ ytick_range = None
68
+ if self.x_ticks:
69
+ xtick_range = self.x_ticks.eval()
70
+ if self.y_ticks:
71
+ ytick_range = self.y_ticks.eval()
72
+
73
+ self.backend.set_decorations(
74
+ self.x_label,
75
+ self.y_label,
76
+ self.title,
77
+ xtick_range,
78
+ ytick_range,
79
+ )
80
+
81
+ def add_series(self, series):
82
+ self.series.append(series)
83
+
84
+ def plot_series(self, series, label):
85
+ if series.series_type == "line":
86
+ line_series = cast(LinePlotSeries, series)
87
+ self.backend.plot_line(
88
+ line_series.x, line_series.y, label=label, color=series.color
89
+ )
90
+ elif series.series_type == "scatter":
91
+ scatter_series = cast(ScatterPlotSeries, series)
92
+ self.backend.plot_scatter(
93
+ scatter_series.data, label=label, color=series.color
94
+ )
95
+ elif series.series_type == "image":
96
+ image_series = cast(ImageSeries, series)
97
+ self.backend.plot_image(image_series.data)
98
+
99
+ def plot(self, path: Path | None = None):
100
+ for idx, series in enumerate(self.series):
101
+ series.color = self.cmap.get_color(idx, self.series)
102
+
103
+ first = True
104
+ for series in self.series:
105
+ label = series.label
106
+ if first and self.legend_label:
107
+ label = f"{label} {self.legend_label}"
108
+ first = False
109
+ self.plot_series(series, label)
110
+
111
+ self.set_decorations()
112
+ self.backend.render(path)
113
+
114
+ def serialize(self):
115
+ return {
116
+ "series": [s.serialize() for s in self.series],
117
+ "title": self.title,
118
+ "type": self.plot_type,
119
+ "xlabel": self.x_label,
120
+ "ylabel": self.y_label,
121
+ "legend_label": self.legend_label,
122
+ "xticks": self.x_ticks.serialize(),
123
+ "yticks": self.y_ticks.serialize(),
124
+ }
125
+
126
+
127
+ class GridPlot(Plot):
128
+ """
129
+ Make a grid of plots
130
+ """
131
+
132
+ def __init__(
133
+ self, title: str = "", stride: int = 4, size: tuple = (25, 20)
134
+ ) -> None:
135
+ super().__init__(title)
136
+ self.stride = stride
137
+ self.size = size
138
+
139
+ def _subplot(self, rows: int, cols: int, count: int, series):
140
+
141
+ self.backend.make_subplot(rows, cols, count)
142
+ self.plot_series(series, "")
143
+ return count + 1
144
+
145
+ def render(self, data, path: Path, num_samples: int = 0):
146
+ rows = num_samples // self.stride
147
+ cols = num_samples // rows
148
+ count = 1
149
+ if num_samples == 0:
150
+ indices = [random.randint(0, len(data) - 1) for _ in range(num_samples)]
151
+ else:
152
+ indices = list(range(0, len(data)))
153
+
154
+ for index in indices:
155
+ if num_samples > 0 and count == num_samples + 1:
156
+ break
157
+ if isinstance(data[index], list):
158
+ for series in data[index]:
159
+ count = self._subplot(rows, cols, count, series)
160
+ else:
161
+ count = self._subplot(rows, cols, count, data[index])
162
+
163
+ self.backend.render(path)
@@ -0,0 +1,54 @@
1
+ import json
2
+
3
+
4
+ class PlotSeries:
5
+
6
+ def __init__(self, label: str = ""):
7
+ self.label = label
8
+ self.color: list[float] = [0.0, 0.0, 0.0]
9
+ self.series_type = ""
10
+
11
+ def serialize(self):
12
+ return {"label": self.label, "color": self.color, "type": self.series_type}
13
+
14
+ def __str__(self):
15
+ return json.dumps(self.serialize(), indent=4)
16
+
17
+
18
+ class ImageSeries(PlotSeries):
19
+ def __init__(self, data, label: str = "", transform=None):
20
+ super().__init__(label)
21
+ self.data = data
22
+ self.series_type = "image"
23
+ self.transform = transform
24
+
25
+ def serialize(self):
26
+ ret = super().serialize()
27
+ ret["data"] = self.data
28
+ return ret
29
+
30
+
31
+ class LinePlotSeries(PlotSeries):
32
+ def __init__(self, x: list[float], y: list[float], label: str = "") -> None:
33
+ super().__init__(label)
34
+ self.series_type = "line"
35
+ self.x = x
36
+ self.y = y
37
+
38
+ def serialize(self):
39
+ ret = super().serialize()
40
+ ret["x"] = self.x
41
+ ret["y"] = self.y
42
+ return ret
43
+
44
+
45
+ class ScatterPlotSeries(PlotSeries):
46
+ def __init__(self, data, label: str = ""):
47
+ super().__init__(label)
48
+ self.data = data
49
+ self.series_type = "scatter"
50
+
51
+ def serialize(self):
52
+ ret = super().serialize()
53
+ ret["data"] = self.data
54
+ return ret
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: icplot
3
- Version: 0.0.6
3
+ Version: 0.0.8
4
4
  Summary: Utilities for generating plots and graphics for technical reports.
5
5
  Author-email: "James Grogan, Irish Centre for High End Computing" <james.grogan@ichec.ie>
6
6
  Project-URL: Repository, https://git.ichec.ie/performance/toolshed/icplot
@@ -16,13 +16,15 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: wand
18
18
  Requires-Dist: matplotlib
19
- Requires-Dist: iccore==0.0.8
19
+ Requires-Dist: iccore==0.0.9
20
20
  Provides-Extra: test
21
21
  Requires-Dist: pytest; extra == "test"
22
22
  Requires-Dist: pytest-cov; extra == "test"
23
23
  Requires-Dist: pytest-sugar; extra == "test"
24
24
  Requires-Dist: black; extra == "test"
25
25
  Requires-Dist: mypy; extra == "test"
26
+ Requires-Dist: flake8; extra == "test"
27
+ Requires-Dist: pylint; extra == "test"
26
28
  Provides-Extra: cairo
27
29
  Requires-Dist: pycairo; extra == "cairo"
28
30
  Requires-Dist: CairoSVG; extra == "cairo"
@@ -4,6 +4,7 @@ pyproject.toml
4
4
  setup.cfg
5
5
  src/icplot/__init__.py
6
6
  src/icplot/cairo_interface.py
7
+ src/icplot/color.py
7
8
  src/icplot/gantt.py
8
9
  src/icplot/geometry.py
9
10
  src/icplot/image_utils.py
@@ -16,7 +17,12 @@ src/icplot.egg-info/dependency_links.txt
16
17
  src/icplot.egg-info/entry_points.txt
17
18
  src/icplot.egg-info/requires.txt
18
19
  src/icplot.egg-info/top_level.txt
20
+ src/icplot/graph/__init__.py
21
+ src/icplot/graph/matplotlib_plot_backend.py
22
+ src/icplot/graph/plot.py
23
+ src/icplot/graph/series.py
19
24
  test/test_cairo_interface.py
20
25
  test/test_gantt_chart.py
21
26
  test/test_image_utils.py
27
+ test/test_line_plots.py
22
28
  test/test_tex_interface.py
@@ -1,6 +1,6 @@
1
1
  wand
2
2
  matplotlib
3
- iccore==0.0.8
3
+ iccore==0.0.9
4
4
 
5
5
  [cairo]
6
6
  pycairo
@@ -12,3 +12,5 @@ pytest-cov
12
12
  pytest-sugar
13
13
  black
14
14
  mypy
15
+ flake8
16
+ pylint
@@ -1,5 +1,6 @@
1
1
  from icplot.cairo_interface import CairoInterface
2
2
 
3
+
3
4
  def test_cairo_interface():
4
5
 
5
6
  cairo_interface = CairoInterface()
@@ -20,10 +20,9 @@ def test_gantt_chart():
20
20
  milestone1.description = "Description of Milestone 1."
21
21
  milestone1.start_date = datetime.date(2024, 7, 1)
22
22
  milestone1.due_date = datetime.date(2024, 7, 15)
23
-
23
+
24
24
  gantt = GanttChart()
25
- gantt.milestones = [milestone0,
26
- milestone1]
25
+ gantt.milestones = [milestone0, milestone1]
27
26
 
28
27
  output_path = Path(os.getcwd()) / "gantt.svg"
29
28
 
@@ -3,9 +3,11 @@ from pathlib import Path
3
3
 
4
4
  from icplot.image_utils import svg_to_png, svg_to_pdf
5
5
 
6
+
6
7
  def get_test_data_dir():
7
8
  return Path(__file__).parent / "data"
8
-
9
+
10
+
9
11
  def test_convert_svg():
10
12
 
11
13
  source = get_test_data_dir() / "test.svg"
@@ -17,5 +19,3 @@ def test_convert_svg():
17
19
  pdf_out = Path(os.getcwd()) / "out.pdf"
18
20
  svg_to_pdf(source, pdf_out)
19
21
  pdf_out.unlink()
20
-
21
-
@@ -0,0 +1,23 @@
1
+ from pathlib import Path
2
+
3
+ from icplot.graph import Plot, LinePlotSeries
4
+
5
+
6
+ def test_line_plot():
7
+
8
+ plot = Plot(
9
+ title="test", x_label="test_x", y_label="test_y", legend_label="test_legend"
10
+ )
11
+
12
+ data = [([0, 5, 10], [1, 2, 3]), ([0, 5, 10], [3, 6, 9]), ([0, 5, 10], [4, 8, 12])]
13
+
14
+ for idx, [x, y] in enumerate(data):
15
+ plot.add_series(LinePlotSeries(x, y, label=f"Series {idx}"))
16
+
17
+ plot.set_x_ticks(0, 10, 5)
18
+
19
+ output_path = Path() / "output.svg"
20
+ plot.plot(output_path)
21
+
22
+ assert output_path.exists()
23
+ output_path.unlink()
@@ -3,6 +3,7 @@ import shutil
3
3
  from pathlib import Path
4
4
  from icplot.tex_interface import TexInterface
5
5
 
6
+
6
7
  def test_tex_interface():
7
8
 
8
9
  build_dir = Path(os.getcwd()) / "tmp_build"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes