Skip to content

Commit 526d31c

Browse files
committed
added drawing of legend shapes (lines, markers)
1 parent ad2b540 commit 526d31c

File tree

1 file changed

+115
-18
lines changed

1 file changed

+115
-18
lines changed

Diff for: packages/python/plotly/plotly/matplotlylib/renderer.py

+115-18
Original file line numberDiff line numberDiff line change
@@ -312,10 +312,84 @@ def draw_bar(self, coll):
312312
"assuming data redundancy, not plotting."
313313
)
314314

315+
def draw_legend_shapes(self, mode, shape, **props):
316+
"""Create a shape that matches lines or markers in legends.
317+
318+
Main issue is that path for circles do not render, so we have to use 'circle'
319+
instead of 'path'.
320+
"""
321+
for single_mode in mode.split("+"):
322+
x = props["data"][0][0]
323+
y = props["data"][0][1]
324+
if single_mode == "markers" and props.get("markerstyle"):
325+
size = shape.pop("size", 6)
326+
symbol = shape.pop("symbol")
327+
# aligning to "center"
328+
x0 = 0
329+
y0 = 0
330+
x1 = size
331+
y1 = size
332+
markerpath = props["markerstyle"].get("markerpath")
333+
if markerpath is None and symbol != "circle":
334+
self.msg += "not sure how to handle this marker without a valid path\n"
335+
return
336+
# marker path to SVG path conversion
337+
path = ' '.join([f"{a} {t[0]},{t[1]}" for a, t in zip(markerpath[1], markerpath[0])])
338+
339+
if symbol == "circle":
340+
# symbols like . and o in matplotlib, use circle
341+
# plotly also maps many other markers to circle, such as 1,8 and p
342+
path = None
343+
shape_type = "circle"
344+
x0 = -size / 2
345+
y0 = size / 2
346+
x1 = size / 2
347+
y1 = size + size / 2
348+
else:
349+
# triangles, star etc
350+
shape_type = "path"
351+
legend_shape = go.layout.Shape(
352+
type=shape_type,
353+
xref="paper",
354+
yref="paper",
355+
x0=x0,
356+
y0=y0,
357+
x1=x1,
358+
y1=y1,
359+
xsizemode="pixel",
360+
ysizemode="pixel",
361+
xanchor=x,
362+
yanchor=y,
363+
path=path,
364+
**shape
365+
)
366+
367+
elif single_mode == "lines":
368+
mode = "line"
369+
x1 = props["data"][1][0]
370+
y1 = props["data"][1][1]
371+
372+
legend_shape = go.layout.Shape(
373+
type=mode,
374+
xref="paper",
375+
yref="paper",
376+
x0=x,
377+
y0=y+0.02,
378+
x1=x1,
379+
y1=y1+0.02,
380+
**shape
381+
)
382+
else:
383+
self.msg += "not sure how to handle this element\n"
384+
return
385+
self.plotly_fig.add_shape(legend_shape)
386+
self.msg += " Heck yeah, I drew that shape\n"
387+
315388
def draw_marked_line(self, **props):
316389
"""Create a data dict for a line obj.
317390
318-
This will draw 'lines', 'markers', or 'lines+markers'.
391+
This will draw 'lines', 'markers', or 'lines+markers'. For legend elements,
392+
this will use layout.shapes, so they can be positioned with paper refs.
319393
320394
props.keys() -- [
321395
'coordinates', ('data', 'axes', 'figure', or 'display')
@@ -346,7 +420,7 @@ def draw_marked_line(self, **props):
346420
347421
"""
348422
self.msg += " Attempting to draw a line "
349-
line, marker = {}, {}
423+
line, marker, shape = {}, {}, {}
350424
if props["linestyle"] and props["markerstyle"]:
351425
self.msg += "... with both lines+markers\n"
352426
mode = "lines+markers"
@@ -361,23 +435,43 @@ def draw_marked_line(self, **props):
361435
props["linestyle"]["color"], props["linestyle"]["alpha"]
362436
)
363437

364-
# print(mpltools.convert_dash(props['linestyle']['dasharray']))
365-
line = go.scatter.Line(
366-
color=color,
367-
width=props["linestyle"]["linewidth"],
368-
dash=mpltools.convert_dash(props["linestyle"]["dasharray"]),
369-
)
438+
if props["coordinates"] == "data":
439+
line = go.scatter.Line(
440+
color=color,
441+
width=props["linestyle"]["linewidth"],
442+
dash=mpltools.convert_dash(props["linestyle"]["dasharray"]),
443+
)
444+
else:
445+
shape=dict(
446+
line = dict(
447+
color=color,
448+
width=props["linestyle"]["linewidth"],
449+
dash=mpltools.convert_dash(props["linestyle"]["dasharray"])
450+
)
451+
)
370452
if props["markerstyle"]:
371-
marker = go.scatter.Marker(
372-
opacity=props["markerstyle"]["alpha"],
373-
color=props["markerstyle"]["facecolor"],
374-
symbol=mpltools.convert_symbol(props["markerstyle"]["marker"]),
375-
size=props["markerstyle"]["markersize"],
376-
line=dict(
377-
color=props["markerstyle"]["edgecolor"],
378-
width=props["markerstyle"]["edgewidth"],
379-
),
380-
)
453+
if props["coordinates"] == "data":
454+
marker = go.scatter.Marker(
455+
opacity=props["markerstyle"]["alpha"],
456+
color=props["markerstyle"]["facecolor"],
457+
symbol=mpltools.convert_symbol(props["markerstyle"]["marker"]),
458+
size=props["markerstyle"]["markersize"],
459+
line=dict(
460+
color=props["markerstyle"]["edgecolor"],
461+
width=props["markerstyle"]["edgewidth"],
462+
),
463+
)
464+
else:
465+
shape = dict(
466+
opacity=props["markerstyle"]["alpha"],
467+
fillcolor=props["markerstyle"]["facecolor"],
468+
symbol=mpltools.convert_symbol(props["markerstyle"]["marker"]),
469+
size=props["markerstyle"]["markersize"],
470+
line=dict(
471+
color=props["markerstyle"]["edgecolor"],
472+
width=props["markerstyle"]["edgewidth"],
473+
),
474+
)
381475
if props["coordinates"] == "data":
382476
marked_line = go.Scatter(
383477
mode=mode,
@@ -404,6 +498,9 @@ def draw_marked_line(self, **props):
404498
)
405499
self.plotly_fig.add_trace(marked_line),
406500
self.msg += " Heck yeah, I drew that line\n"
501+
elif props["coordinates"] == "axes":
502+
# dealing with legend graphical elements
503+
self.draw_legend_shapes(mode=mode,shape=shape, **props)
407504
else:
408505
self.msg += " Line didn't have 'data' coordinates, " "not drawing\n"
409506
warnings.warn(

0 commit comments

Comments
 (0)