plotly / Plotly.NET

interactive graphing library for .NET programming languages :chart_with_upwards_trend:
https://plotly.net
MIT License
663 stars 88 forks source link

Unable to set certain properties of a legend #378

Closed jgsato closed 1 year ago

jgsato commented 1 year ago

Description

I'm trying to style a stacked bar chart so that the legend is below the chart (as opposed to the default behavior of it being on the right). Some of the styles are being applied correctly while others are not.

Repro steps

legend.SetValue("x", 0.4); // works
legend.SetValue("y", -3); // works
legend.SetValue("xanchor", StyleParam.XAnchorPosition.Center); // doesn't work
legend.SetValue("yanchor", StyleParam.YAnchorPosition.Middle); // doesn't work
legend.SetValue("orientation", StyleParam.Orientation.Horizontal); // doesn't work
legend.SetValue("entrywidth", 0); // doesn't work
legend.SetValue("entrywidthmode", "pixels"); // doesn't work

var layout = new Layout();
layout.SetValue("legend", legend);

// --- code that builds chart --- //
var svgString = stackedBarGraph.WithLayout(layout).ToSVGString(null, 550, 300);

Expected behavior

I expect the legend to be positioned horizontally below the chart.

Actual behavior

The legend is positioned near the correct location but the legend items are still in vertical orientation, either overlapping the chart or reducing the chart size to be unreadable if the "y" value is set to a large negative number.

Related information

kMutagene commented 1 year ago

Hi there,

Which version of Plotly.NET are you using? This is quite important since that also determines which plotly.js version is used. Depending on that it might be that attributes are not supported. entrywidthmode for example is a quite recent addition.

It might be worth upgrading and using Plotly.NET.CSharp which provides native C# bindings.

However for a direct fix for your current setup, it is important to note that lines like this

legend.SetValue("orientation", StyleParam.Orientation.Horizontal)

Will not work because StyleParam types need to be converted first like this

legend.SetValue("orientation", StyleParam.Orientation.Horizontal.Convert());

So for example simply setting the orientation on a chart would look like this (i assumed you're on 3.0.0 for this one):

using Plotly.NET;
using Plotly.NET.LayoutObjects;

var legend = Legend.init();

legend.SetValue("orientation", StyleParam.Orientation.Horizontal.Convert());

var layout = new Layout();
layout.SetValue("legend", legend);

Chart2D.Chart.Line<double, double, string>(
    new double[] { 1, 2, 3, 4 },
    new double[] { 10, 15, 13, 17 },
    ShowLegend: true
).WithLayout(layout)

image

Regarding entrywidthmode, there is not much you can do on older versions other than upgrading to a newer Plotly.NET version or patching the generated html to use a newer plotly.js version (and since you are converting to svg, even that might not be possible)

If using Plotly.NET.CSharp is an option for you, I could add bindings for Chart.WithLegendStyle for easier legend styling using typed arguments

kMutagene commented 1 year ago

Here is a stacked bar chart created with the latest Plotly.NET.CSharp package that i think fits your specifications well:

using Plotly.NET.CSharp;
using Plotly.NET.ImageExport;
using Plotly.NET.LayoutObjects;
using static Plotly.NET.StyleParam;

Chart.Combine(
    new Plotly.NET.GenericChart.GenericChart [] {
        Chart.StackedBar<int, string, string>(
            values: new [] { 1, 2, 3 },
            Keys: new [] { "a", "b", "c" }
        ),
        Chart.StackedBar<int, string, string>(
            values: new [] { 1, 2, 3 },
            Keys: new [] { "a", "b", "c" }
        )
    }
)
    .WithLegendStyle(
        X: 0.5,
        Orientation: Orientation.Horizontal,
        XAnchor: XAnchorPosition.Center,
        EntryWidth: 0,
        EntryWidthMode: EntryWidthMode.Pixels
    )

image

jgsato commented 1 year ago

Great, thank you. I was a bit confused between the two versions (Plotly.NET vs Plotly.NET.CSharp). I've since updated to Plotly.NET.CSharp version 0.10.0 and it appears to be working well with the exception of 1 minor issue.

The legend is now horizontal, but it overlaps the X-axis labels. I've tried setting the "Y" value to a large negative number but it doesn't appear to have any impact.

kMutagene commented 1 year ago

The legend is now horizontal, but it overlaps the X-axis labels. I've tried setting the "Y" value to a large negative number but it doesn't appear to have any impact.

Can you share the code for the chart? If i use Y = -0.4 as specified in your initial example, the legend is far away from the axis:

using Plotly.NET.CSharp;
using Plotly.NET.LayoutObjects;
using static Plotly.NET.StyleParam;

Chart.Combine(
    new Plotly.NET.GenericChart.GenericChart [] {
        Chart.StackedBar<int, string, string>(
            values: new [] { 1, 2, 3 },
            Keys: new [] { "a", "b", "c" }
        ),
        Chart.StackedBar<int, string, string>(
            values: new [] { 1, 2, 3 },
            Keys: new [] { "a", "b", "c" }
        )
    }
)
    .WithLegendStyle(
        X: 0.5,
        Y: -0.4,
        Orientation: Orientation.Horizontal,
        XAnchor: XAnchorPosition.Center,
        YAnchor: YAnchorPosition.Middle,
        EntryWidth: 0,
        EntryWidthMode: EntryWidthMode.Pixels
    )

image

jgsato commented 1 year ago

Thanks. I was setting a value < -1. Updating to -0.5 resolved the issue.