tikzplotly 0.1.3__tar.gz → 0.1.4__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.
- {tikzplotly-0.1.3/src/tikzplotly.egg-info → tikzplotly-0.1.4}/PKG-INFO +2 -1
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/pyproject.toml +3 -2
- tikzplotly-0.1.4/src/tests/test_heatmaps.py +206 -0
- tikzplotly-0.1.4/src/tikzplotly/__about__.py +1 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly/_color.py +16 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly/_dataContainer.py +2 -0
- tikzplotly-0.1.4/src/tikzplotly/_heatmap.py +145 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly/_save.py +11 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly/_tex.py +11 -1
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly/_utils.py +20 -1
- {tikzplotly-0.1.3 → tikzplotly-0.1.4/src/tikzplotly.egg-info}/PKG-INFO +2 -1
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly.egg-info/SOURCES.txt +5 -3
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly.egg-info/requires.txt +1 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly.egg-info/top_level.txt +1 -0
- tikzplotly-0.1.3/src/tikzplotly/__about__.py +0 -1
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/LICENSE +0 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/README.md +0 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/setup.cfg +0 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4/src}/tests/test_line_charts.py +0 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4/src}/tests/test_markers.py +0 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly/__init__.py +0 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly/_annotations.py +0 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly/_axis.py +0 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly/_dash.py +0 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly/_data.py +0 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly/_marker.py +0 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly/_scatter.py +0 -0
- {tikzplotly-0.1.3 → tikzplotly-0.1.4}/src/tikzplotly.egg-info/dependency_links.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tikzplotly
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Convert plotly figures to LaTeX / tikz figures
|
|
5
5
|
Author-email: Thomas Saigre <tikzplotly@outlook.fr>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -42,6 +42,7 @@ Description-Content-Type: text/markdown
|
|
|
42
42
|
License-File: LICENSE
|
|
43
43
|
Requires-Dist: numpy
|
|
44
44
|
Requires-Dist: plotly
|
|
45
|
+
Requires-Dist: PIL
|
|
45
46
|
Provides-Extra: dev
|
|
46
47
|
Requires-Dist: pandas; extra == "dev"
|
|
47
48
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tikzplotly"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
description = "Convert plotly figures to LaTeX / tikz figures"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{name = "Thomas Saigre", email = "tikzplotly@outlook.fr"}]
|
|
@@ -25,7 +25,8 @@ classifiers = [
|
|
|
25
25
|
keywords = ["plotly", "latex", "tikz", "figure", "conversion", "graphics"]
|
|
26
26
|
dependencies = [
|
|
27
27
|
"numpy",
|
|
28
|
-
"plotly"
|
|
28
|
+
"plotly",
|
|
29
|
+
"PIL"
|
|
29
30
|
]
|
|
30
31
|
|
|
31
32
|
[project.optional-dependencies]
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# From https://plotly.com/python/heatmaps/
|
|
2
|
+
import os
|
|
3
|
+
import tikzplotly
|
|
4
|
+
|
|
5
|
+
from tikzplotly._tex import tex_create_document, tex_begin_environment, tex_end_environment, tex_end_all_environment
|
|
6
|
+
|
|
7
|
+
import plotly
|
|
8
|
+
import plotly.express as px
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import plotly.graph_objects as go
|
|
11
|
+
import numpy as np
|
|
12
|
+
import datetime
|
|
13
|
+
import numpy as np
|
|
14
|
+
np.random.seed(1)
|
|
15
|
+
|
|
16
|
+
def fig1():
|
|
17
|
+
fig = px.imshow([[1, 20, 30],
|
|
18
|
+
[20, 1, 60],
|
|
19
|
+
[30, 60, 1]])
|
|
20
|
+
# fig.show()
|
|
21
|
+
return fig, "Heatmaps with Plotly Express"
|
|
22
|
+
|
|
23
|
+
def fig2():
|
|
24
|
+
df = px.data.medals_wide(indexed=True)
|
|
25
|
+
fig = px.imshow(df)
|
|
26
|
+
# fig.show()
|
|
27
|
+
return fig, "Heatmaps with Plotly Express 2"
|
|
28
|
+
|
|
29
|
+
def fig3():
|
|
30
|
+
z = [[.1, .3, .5, .7, .9],
|
|
31
|
+
[1, .8, .6, .4, .2],
|
|
32
|
+
[.2, 0, .5, .7, .9],
|
|
33
|
+
[.9, .8, .4, .2, 0],
|
|
34
|
+
[.3, .4, .5, .7, 1]]
|
|
35
|
+
|
|
36
|
+
fig = px.imshow(z, text_auto=True)
|
|
37
|
+
# fig.show()
|
|
38
|
+
return fig, "Displaying Text on Heatmaps"
|
|
39
|
+
|
|
40
|
+
def fig4():
|
|
41
|
+
z = [[.1, .3, .5, .7, .9],
|
|
42
|
+
[1, .8, .6, .4, .2],
|
|
43
|
+
[.2, 0, .5, .7, .9],
|
|
44
|
+
[.9, .8, .4, .2, 0],
|
|
45
|
+
[.3, .4, .5, .7, 1]]
|
|
46
|
+
|
|
47
|
+
fig = px.imshow(z, text_auto=True, aspect="auto")
|
|
48
|
+
# fig.show()
|
|
49
|
+
return fig, "Controlling Aspect Ratio"
|
|
50
|
+
|
|
51
|
+
def fig5():
|
|
52
|
+
data=[[1, 25, 30, 50, 1], [20, 1, 60, 80, 30], [30, 60, 1, 5, 20]]
|
|
53
|
+
fig = px.imshow(data,
|
|
54
|
+
labels=dict(x="Day of Week", y="Time of Day", color="Productivity"),
|
|
55
|
+
x=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
|
|
56
|
+
y=['Morning', 'Afternoon', 'Evening']
|
|
57
|
+
)
|
|
58
|
+
fig.update_xaxes(side="top")
|
|
59
|
+
# fig.show()
|
|
60
|
+
return fig, "Customizing the axes and labels on a heatmap"
|
|
61
|
+
|
|
62
|
+
def fig6():
|
|
63
|
+
|
|
64
|
+
fig = go.Figure(data=go.Heatmap(
|
|
65
|
+
z=[[1, 20, 30],
|
|
66
|
+
[20, 1, 60],
|
|
67
|
+
[30, 60, 1]]))
|
|
68
|
+
# fig.show()
|
|
69
|
+
return fig, "Basic Heatmap with plotly.graph\\_objects"
|
|
70
|
+
|
|
71
|
+
def fig7():
|
|
72
|
+
fig = go.Figure(data=go.Heatmap(
|
|
73
|
+
z=[[1, None, 30, 50, 1], [20, 1, 60, 80, 30], [30, 60, 1, -10, 20]],
|
|
74
|
+
x=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
|
|
75
|
+
y=['Morning', 'Afternoon', 'Evening'],
|
|
76
|
+
hoverongaps = False))
|
|
77
|
+
# fig.show()
|
|
78
|
+
return fig, "Heatmap with Categorical Axis Labels"
|
|
79
|
+
|
|
80
|
+
def fig8():
|
|
81
|
+
import plotly.graph_objects as go
|
|
82
|
+
|
|
83
|
+
# Build the rectangles as a heatmap
|
|
84
|
+
# specify the edges of the heatmap squares
|
|
85
|
+
phi = (1 + np.sqrt(5) )/2. # golden ratio
|
|
86
|
+
xe = [0, 1, 1+(1/(phi**4)), 1+(1/(phi**3)), phi]
|
|
87
|
+
ye = [0, 1/(phi**3), 1/phi**3+1/phi**4, 1/(phi**2), 1]
|
|
88
|
+
|
|
89
|
+
z = [ [13,3,3,5],
|
|
90
|
+
[13,2,1,5],
|
|
91
|
+
[13,10,11,12],
|
|
92
|
+
[13,8,8,8]
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
fig = go.Figure(data=go.Heatmap(
|
|
96
|
+
x = np.sort(xe),
|
|
97
|
+
y = np.sort(ye),
|
|
98
|
+
z = z,
|
|
99
|
+
type = 'heatmap',
|
|
100
|
+
colorscale = 'Viridis'))
|
|
101
|
+
|
|
102
|
+
# Add spiral line plot
|
|
103
|
+
|
|
104
|
+
def spiral(th):
|
|
105
|
+
a = 1.120529
|
|
106
|
+
b = 0.306349
|
|
107
|
+
r = a*np.exp(-b*th)
|
|
108
|
+
return (r*np.cos(th), r*np.sin(th))
|
|
109
|
+
|
|
110
|
+
theta = np.linspace(-np.pi/13,4*np.pi,1000); # angle
|
|
111
|
+
(x,y) = spiral(theta)
|
|
112
|
+
|
|
113
|
+
fig.add_trace(go.Scatter(x= -x+x[0], y= y-y[0],
|
|
114
|
+
line =dict(color='white',width=3)))
|
|
115
|
+
|
|
116
|
+
axis_template = dict(range = [0,1.6], autorange = False,
|
|
117
|
+
showgrid = False, zeroline = False,
|
|
118
|
+
linecolor = 'black', showticklabels = False,
|
|
119
|
+
ticks = '' )
|
|
120
|
+
|
|
121
|
+
fig.update_layout(margin = dict(t=200,r=200,b=200,l=200),
|
|
122
|
+
xaxis = axis_template,
|
|
123
|
+
yaxis = axis_template,
|
|
124
|
+
showlegend = False,
|
|
125
|
+
width = 700, height = 700,
|
|
126
|
+
autosize = False )
|
|
127
|
+
|
|
128
|
+
# fig.show()
|
|
129
|
+
return fig, "Heatmap with Unequal Block Sizes"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def fig9():
|
|
133
|
+
import plotly.graph_objects as go
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
programmers = ['Alex','Nicole','Sara','Etienne','Chelsea','Jody','Marianne']
|
|
137
|
+
|
|
138
|
+
base = datetime.datetime.today()
|
|
139
|
+
dates = base - np.arange(180) * datetime.timedelta(days=1)
|
|
140
|
+
z = np.random.poisson(size=(len(programmers), len(dates)))
|
|
141
|
+
|
|
142
|
+
fig = go.Figure(data=go.Heatmap(
|
|
143
|
+
z=z,
|
|
144
|
+
x=dates,
|
|
145
|
+
y=programmers,
|
|
146
|
+
colorscale='Viridis'))
|
|
147
|
+
|
|
148
|
+
fig.update_layout(
|
|
149
|
+
title='GitHub commits per day',
|
|
150
|
+
xaxis_nticks=36)
|
|
151
|
+
|
|
152
|
+
# fig.show()
|
|
153
|
+
return fig, "Heatmap with Datetime Axis"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def fig11(): # from @JasonGross, see issue https://github.com/thomas-saigre/tikzplotly/issues/6
|
|
157
|
+
data = np.random.rand(10,10)
|
|
158
|
+
fig = px.imshow(data, color_continuous_scale='Viridis', title='Heatmap Example with Plotly Express')
|
|
159
|
+
# fig.show()
|
|
160
|
+
return fig, "Heatmap Example with Plotly Express"
|
|
161
|
+
|
|
162
|
+
if __name__ == "__main__":
|
|
163
|
+
|
|
164
|
+
print("Tikzploty : ", tikzplotly.__version__)
|
|
165
|
+
print("Plotly : ", plotly.__version__)
|
|
166
|
+
print("Test line charts")
|
|
167
|
+
|
|
168
|
+
file_directory = os.path.dirname(os.path.abspath(__file__))
|
|
169
|
+
|
|
170
|
+
functions = [
|
|
171
|
+
("1", fig1),
|
|
172
|
+
("2", fig2),
|
|
173
|
+
# ("3", fig3), # texttemplate not supported
|
|
174
|
+
("4", fig4),
|
|
175
|
+
("5", fig5),
|
|
176
|
+
("6", fig6),
|
|
177
|
+
("7", fig7),
|
|
178
|
+
# ("8", fig8), # Not supported
|
|
179
|
+
("9", fig9),
|
|
180
|
+
("11", fig11)
|
|
181
|
+
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
main_tex_content = tex_create_document(options="twocolumn", compatibility="newest")
|
|
185
|
+
main_tex_content += "\\usepackage[left=1cm, right=1cm, top=1cm, bottom=1cm]{geometry}\n"
|
|
186
|
+
main_tex_content += "\n"
|
|
187
|
+
stack_env = []
|
|
188
|
+
main_tex_content += tex_begin_environment("document", stack_env) + '\n'
|
|
189
|
+
|
|
190
|
+
for i, f in functions:
|
|
191
|
+
print(f"Figure {i}")
|
|
192
|
+
fig, title = f()
|
|
193
|
+
data = fig.data
|
|
194
|
+
save_path = os.path.join(file_directory, "outputs", "test_heatmap", "fig{}.tex".format(i))
|
|
195
|
+
tikzplotly.save(save_path, fig, img_name=os.path.join(file_directory, "outputs", "test_heatmap", "fig{}.png".format(i)))
|
|
196
|
+
main_tex_content += tex_begin_environment("figure", stack_env)
|
|
197
|
+
main_tex_content += " \\input{fig" + str(i) + ".tex}\n"
|
|
198
|
+
main_tex_content += " \\caption{" + title + "}\n"
|
|
199
|
+
main_tex_content += tex_end_environment(stack_env) + '\n'
|
|
200
|
+
|
|
201
|
+
main_tex_content += "\n" + tex_end_all_environment(stack_env)
|
|
202
|
+
|
|
203
|
+
main_tex_path = os.path.join(file_directory, "outputs", "test_heatmap", "main.tex")
|
|
204
|
+
print("Save main tex file : ", main_tex_path)
|
|
205
|
+
with open(main_tex_path, "w") as f:
|
|
206
|
+
f.write(main_tex_content)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.4"
|
|
@@ -54,6 +54,22 @@ def convert_color(color):
|
|
|
54
54
|
warn(f"Color {color} type is not supported yet. Returning the same color.")
|
|
55
55
|
return color, 1
|
|
56
56
|
|
|
57
|
+
def hex2rgb(hex_color):
|
|
58
|
+
"""Convert a hex color to a RGB color.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
hex_color
|
|
63
|
+
hex color string
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
RGB color string
|
|
68
|
+
"""
|
|
69
|
+
hex_color = hex_color.lstrip("#")
|
|
70
|
+
return str(int(hex_color[:2], 16)) + ", " + str(int(hex_color[2:4], 16)) + ", " + str(int(hex_color[4:], 16))
|
|
71
|
+
|
|
72
|
+
DEFAULT_COLORSCALE = ((0.0, '#0d0887'), (0.1111111111111111, '#46039f'), (0.2222222222222222, '#7201a8'), (0.3333333333333333, '#9c179e'), (0.4444444444444444, '#bd3786'), (0.5555555555555556, '#d8576b'), (0.6666666666666666, '#ed7953'), (0.7777777777777778, '#fb9f3a'), (0.8888888888888888, '#fdca26'), (1.0, '#f0f921'))
|
|
57
73
|
|
|
58
74
|
|
|
59
75
|
ALICEBLUE = rgb_str(240, 248, 255)
|
|
@@ -51,6 +51,8 @@ class DataContainer:
|
|
|
51
51
|
tuple (macro_name, y_label), where macro_name is the name of the data in LaTeX and y_label the name of the y data in LaTeX
|
|
52
52
|
"""
|
|
53
53
|
for data in self.data:
|
|
54
|
+
if len(data.x) != len(x):
|
|
55
|
+
continue
|
|
54
56
|
are_equals = data.x == x
|
|
55
57
|
if type(are_equals) == bool:
|
|
56
58
|
if are_equals:
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
import numpy as np
|
|
3
|
+
import io
|
|
4
|
+
from PIL import Image
|
|
5
|
+
from warnings import warn
|
|
6
|
+
from ._tex import tex_addplot, get_tikz_colorscale
|
|
7
|
+
from ._axis import Axis
|
|
8
|
+
from ._color import DEFAULT_COLORSCALE
|
|
9
|
+
from ._utils import get_ticks_str
|
|
10
|
+
|
|
11
|
+
def crop_image(img_data):
|
|
12
|
+
"""Crops an image to the smallest bounding box that contains all non-white pixels.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
img_data
|
|
17
|
+
A PIL Image object.
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
cropped_image : PIL Image
|
|
22
|
+
"""
|
|
23
|
+
image_np = np.array(img_data)
|
|
24
|
+
non_white_rows = np.any(image_np != [255, 255, 255, 255], axis=1)
|
|
25
|
+
non_white_cols = np.any(image_np != [255, 255, 255, 255], axis=0)
|
|
26
|
+
|
|
27
|
+
top, bottom = np.where(non_white_rows)[0][[0, -1]]
|
|
28
|
+
left, right = np.where(non_white_cols)[0][[0, -1]]
|
|
29
|
+
cropped_image = Image.fromarray(image_np[top:bottom+1, left:right+1])
|
|
30
|
+
|
|
31
|
+
return cropped_image
|
|
32
|
+
|
|
33
|
+
def resize_image(img, nb_row, nb_col):
|
|
34
|
+
"""Resizes an image to a given number of rows and columns.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
img
|
|
39
|
+
A PIL Image object.
|
|
40
|
+
nb_row
|
|
41
|
+
The number of rows of the resized image.
|
|
42
|
+
nb_col
|
|
43
|
+
The number of columns of the resized image.
|
|
44
|
+
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
resized_image : PIL Image
|
|
48
|
+
"""
|
|
49
|
+
resized_image = Image.new("RGBA", size = (nb_col, nb_row))
|
|
50
|
+
block_size = (img.width // nb_col, img.height // nb_row)
|
|
51
|
+
|
|
52
|
+
if block_size[0] != img.width / nb_col or block_size[1] != img.height / nb_row:
|
|
53
|
+
warn("png image has not been reduced, see https://github.com/thomas-saigre/tikzplotly/issues/6#issuecomment-2106180586")
|
|
54
|
+
return img
|
|
55
|
+
|
|
56
|
+
for i in range(nb_row):
|
|
57
|
+
for j in range(nb_col):
|
|
58
|
+
color = img.getpixel((j * block_size[0], i * block_size[1]))
|
|
59
|
+
resized_image.putpixel((j, i), color)
|
|
60
|
+
|
|
61
|
+
return resized_image
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def draw_heatmap(data, fig, img_name, axis: Axis):
|
|
65
|
+
"""Draw a heatmap, and return the tikz code.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
data
|
|
70
|
+
data array to be plotted
|
|
71
|
+
fig
|
|
72
|
+
whole plotly figure
|
|
73
|
+
img_name
|
|
74
|
+
name of the image file to be created
|
|
75
|
+
axis
|
|
76
|
+
axis object previously created
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
string of tikz code for the scatter trace
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
code = ""
|
|
84
|
+
|
|
85
|
+
figure_data = np.array(data.z)
|
|
86
|
+
|
|
87
|
+
# We create a new figure with only the data, and export it as a png
|
|
88
|
+
fig_copy = deepcopy(fig)
|
|
89
|
+
fig_copy.update_layout(coloraxis_showscale=False, coloraxis_colorbar=None, xaxis_visible=False, yaxis_visible=False)
|
|
90
|
+
try:
|
|
91
|
+
fig_copy.update_traces(showscale=False)
|
|
92
|
+
except ValueError as e:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
if data.texttemplate is not None:
|
|
96
|
+
warn("Text template is not supported yet.")
|
|
97
|
+
fig_copy.update_traces(texttemplate=None)
|
|
98
|
+
fig_copy.update_layout(title=None)
|
|
99
|
+
|
|
100
|
+
img_bytes = fig_copy.to_image(format="png") # The image created by plotly keeps places around the heatmap
|
|
101
|
+
cropped_image = crop_image(Image.open(io.BytesIO(img_bytes))) # so we crop all the white around the figure
|
|
102
|
+
resized_image = resize_image(cropped_image, *figure_data.shape) # and we resize it so each square is a 1px x 1px square
|
|
103
|
+
resized_image.save(img_name)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
xmin = -0.5
|
|
107
|
+
xmax = -0.5 + figure_data.shape[1]
|
|
108
|
+
ymin = -0.5 + figure_data.shape[0]
|
|
109
|
+
ymax = -0.5 # NB : the axis is reversed
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if not (fig.layout.coloraxis.showscale == False): # If the value is True or None
|
|
113
|
+
axis.add_option("colorbar", None)
|
|
114
|
+
|
|
115
|
+
if (x := data.x) is not None:
|
|
116
|
+
xtick, xticklabels = get_ticks_str(x, fig.layout.xaxis.nticks)
|
|
117
|
+
axis.add_option("xtick", xtick)
|
|
118
|
+
axis.add_option("xticklabels", xticklabels)
|
|
119
|
+
|
|
120
|
+
if (y := data.y) is not None:
|
|
121
|
+
ytick, yticklabels = get_ticks_str(y, fig.layout.yaxis.nticks)
|
|
122
|
+
axis.add_option("ytick", ytick)
|
|
123
|
+
axis.add_option("yticklabels", yticklabels)
|
|
124
|
+
|
|
125
|
+
if (colorscale := data.colorscale) is not None:
|
|
126
|
+
axis.add_option("colormap", get_tikz_colorscale(colorscale))
|
|
127
|
+
elif (colorscale := fig.layout.coloraxis.colorscale) is not None:
|
|
128
|
+
axis.add_option("colormap", get_tikz_colorscale(colorscale))
|
|
129
|
+
elif data.showscale is not False:
|
|
130
|
+
warn("No colorscale found, using default")
|
|
131
|
+
axis.add_option("colormap", get_tikz_colorscale(DEFAULT_COLORSCALE))
|
|
132
|
+
|
|
133
|
+
tmp = np.where(figure_data == None, np.nan, figure_data)
|
|
134
|
+
axis.add_option("point meta max", tmp.max())
|
|
135
|
+
axis.add_option("point meta min", tmp.min())
|
|
136
|
+
axis.add_option("xmin", xmin)
|
|
137
|
+
axis.add_option("xmax", xmax)
|
|
138
|
+
axis.add_option("ymin", ymax)
|
|
139
|
+
axis.add_option("ymax", ymin)
|
|
140
|
+
axis.add_option("tick align", "outside")
|
|
141
|
+
axis.add_option("tick pos", "left")
|
|
142
|
+
|
|
143
|
+
code += tex_addplot(img_name, type="graphics", type_options=f"includegraphics cmd=\pgfimage,xmin={xmin}, xmax={xmax}, ymin={ymin}, ymax={ymax}")
|
|
144
|
+
|
|
145
|
+
return code
|
|
@@ -2,6 +2,7 @@ from pathlib import Path
|
|
|
2
2
|
from .__about__ import __version__
|
|
3
3
|
from ._tex import *
|
|
4
4
|
from ._scatter import draw_scatter2d
|
|
5
|
+
from ._heatmap import draw_heatmap
|
|
5
6
|
from ._axis import Axis
|
|
6
7
|
from ._color import *
|
|
7
8
|
from ._annotations import str_from_annotation
|
|
@@ -15,6 +16,7 @@ def get_tikz_code(
|
|
|
15
16
|
tikz_options = None,
|
|
16
17
|
axis_options = None,
|
|
17
18
|
include_disclamer = True,
|
|
19
|
+
img_name = "heatmap.png",
|
|
18
20
|
):
|
|
19
21
|
"""Get the tikz code of a figure.
|
|
20
22
|
|
|
@@ -28,6 +30,8 @@ def get_tikz_code(
|
|
|
28
30
|
options given to the axis environment, by default None
|
|
29
31
|
include_disclamer, optional
|
|
30
32
|
include a disclamer in the code, by default True
|
|
33
|
+
img_name, optional
|
|
34
|
+
name of the PNG file for heatmap, by default "heatmap.png"
|
|
31
35
|
|
|
32
36
|
Returns
|
|
33
37
|
-------
|
|
@@ -59,6 +63,10 @@ def get_tikz_code(
|
|
|
59
63
|
colors_set.add(convert_color(trace.line.color)[:3])
|
|
60
64
|
if trace.fillcolor is not None:
|
|
61
65
|
colors_set.add(convert_color(trace.fillcolor)[:3])
|
|
66
|
+
|
|
67
|
+
elif trace.type == "heatmap":
|
|
68
|
+
data_str.append( draw_heatmap(trace, fig, img_name, axis) )
|
|
69
|
+
|
|
62
70
|
else:
|
|
63
71
|
warn(f"Trace type {trace.type} is not supported yet.")
|
|
64
72
|
|
|
@@ -110,5 +118,8 @@ def save(filepath: str | Path, *args, encoding: str | None = None, **kwargs):
|
|
|
110
118
|
Additional arguments are passed to the backend.
|
|
111
119
|
"""
|
|
112
120
|
code = get_tikz_code(*args, **kwargs)
|
|
121
|
+
directory = Path(filepath).parent
|
|
122
|
+
if not directory.exists():
|
|
123
|
+
directory.mkdir(parents=True)
|
|
113
124
|
with open(filepath, "w") as fd:
|
|
114
125
|
fd.write(code)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from ._color import hex2rgb
|
|
1
2
|
|
|
2
3
|
def tex_comment(text):
|
|
3
4
|
"""Create a LaTeX comment.
|
|
@@ -190,4 +191,13 @@ def tex_text(text):
|
|
|
190
191
|
"""Convert a string to LaTeX,
|
|
191
192
|
escaping the special characters %, _, &, #, $, {, }, ~.
|
|
192
193
|
"""
|
|
193
|
-
return text.replace("%", "\\%").replace("_", "\\_").replace("&", "\\&").replace("#", "\\#").replace("$", "\\$").replace("{", "\\{").replace("}", "\\}").replace("~", "\\textasciitilde ")
|
|
194
|
+
return text.replace("%", "\\%").replace("_", "\\_").replace("&", "\\&").replace("#", "\\#").replace("$", "\\$").replace("{", "\\{").replace("}", "\\}").replace("~", "\\textasciitilde ")
|
|
195
|
+
|
|
196
|
+
def get_tikz_colorscale(colorscale, name="mycolor"):
|
|
197
|
+
|
|
198
|
+
code = "{" + str(name) + "}{\n"
|
|
199
|
+
for dist, color in colorscale:
|
|
200
|
+
rgb_color = hex2rgb(color)
|
|
201
|
+
code += f" rgb255({dist}cm)=({rgb_color})".replace(" ", "") + ";\n"
|
|
202
|
+
code += "}"
|
|
203
|
+
return code
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import warnings
|
|
3
3
|
from math import floor
|
|
4
|
+
import numpy as np
|
|
4
5
|
|
|
5
6
|
rep_digit = {'0': 'Z', '1': 'O', '2': 'T', '3': 'Th', '4': 'F', '5': 'Fi', '6': 'S', '7': 'Se', '8': 'E', '9': 'N'}
|
|
6
7
|
rep_digit = dict((re.escape(k), v) for k, v in rep_digit.items())
|
|
@@ -110,4 +111,22 @@ def option_dict_to_str(options_dict, sep=" "):
|
|
|
110
111
|
options += f"{key}={value},{sep}"
|
|
111
112
|
if options == "":
|
|
112
113
|
return None
|
|
113
|
-
return options.strip()[:-1]
|
|
114
|
+
return options.strip()[:-1]
|
|
115
|
+
|
|
116
|
+
def get_ticks_str(data, nticks):
|
|
117
|
+
indices = np.arange(len(data))
|
|
118
|
+
if nticks is not None:
|
|
119
|
+
data_ = data[::len(data)//(nticks-1)]
|
|
120
|
+
data = np.append(data_, [data[-1]])
|
|
121
|
+
indices_ = indices[::len(indices)//(nticks-1)]
|
|
122
|
+
indices = np.append(indices_, [indices[-1]])
|
|
123
|
+
|
|
124
|
+
ticks = "{"
|
|
125
|
+
ticklabels = "{"
|
|
126
|
+
for i, val in zip(indices, data):
|
|
127
|
+
ticks += str(i) + ","
|
|
128
|
+
ticklabels += str(val) + ","
|
|
129
|
+
ticks = ticks[:-1] + "}"
|
|
130
|
+
ticklabels = ticklabels[:-1] + "}"
|
|
131
|
+
|
|
132
|
+
return ticks, ticklabels
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tikzplotly
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Convert plotly figures to LaTeX / tikz figures
|
|
5
5
|
Author-email: Thomas Saigre <tikzplotly@outlook.fr>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -42,6 +42,7 @@ Description-Content-Type: text/markdown
|
|
|
42
42
|
License-File: LICENSE
|
|
43
43
|
Requires-Dist: numpy
|
|
44
44
|
Requires-Dist: plotly
|
|
45
|
+
Requires-Dist: PIL
|
|
45
46
|
Provides-Extra: dev
|
|
46
47
|
Requires-Dist: pandas; extra == "dev"
|
|
47
48
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
LICENSE
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
|
+
src/tests/test_heatmaps.py
|
|
5
|
+
src/tests/test_line_charts.py
|
|
6
|
+
src/tests/test_markers.py
|
|
4
7
|
src/tikzplotly/__about__.py
|
|
5
8
|
src/tikzplotly/__init__.py
|
|
6
9
|
src/tikzplotly/_annotations.py
|
|
@@ -9,6 +12,7 @@ src/tikzplotly/_color.py
|
|
|
9
12
|
src/tikzplotly/_dash.py
|
|
10
13
|
src/tikzplotly/_data.py
|
|
11
14
|
src/tikzplotly/_dataContainer.py
|
|
15
|
+
src/tikzplotly/_heatmap.py
|
|
12
16
|
src/tikzplotly/_marker.py
|
|
13
17
|
src/tikzplotly/_save.py
|
|
14
18
|
src/tikzplotly/_scatter.py
|
|
@@ -18,6 +22,4 @@ src/tikzplotly.egg-info/PKG-INFO
|
|
|
18
22
|
src/tikzplotly.egg-info/SOURCES.txt
|
|
19
23
|
src/tikzplotly.egg-info/dependency_links.txt
|
|
20
24
|
src/tikzplotly.egg-info/requires.txt
|
|
21
|
-
src/tikzplotly.egg-info/top_level.txt
|
|
22
|
-
tests/test_line_charts.py
|
|
23
|
-
tests/test_markers.py
|
|
25
|
+
src/tikzplotly.egg-info/top_level.txt
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|