SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, and now iOS Charting & Android Chart Components

1
0

I am trying to use the Box plot type to render a gantt-style chart. The intention is to set the Median, lowerQuartile and Min all to the start time values, and the upperQuartile and Max to the End times.

The trouble, as I think I have found, is that the model doesn’t support DateTime as the type for the YAxis values. Using the Box Chart example in the demo suite for inspiration, I have two very stripped down charts on my page:

 <s:SciChartSurface x:Name="ganttChart" BorderThickness="0" Padding="0" Grid.Row="0">
      <s:SciChartSurface.RenderableSeries>
           <s:FastBoxPlotRenderableSeries x:Name="ganttSeries" />
      </s:SciChartSurface.RenderableSeries>
      <s:SciChartSurface.XAxis>
           <s:NumericAxis/>
      </s:SciChartSurface.XAxis>
      <s:SciChartSurface.YAxis>
           <s:DateTimeAxis/>
      </s:SciChartSurface.YAxis>
 </s:SciChartSurface>

 <s:SciChartSurface x:Name="sciChart" Grid.Column="1" BorderThickness="0" Padding="0" Grid.Row="1">
      <s:SciChartSurface.RenderableSeries>
           <s:FastBoxPlotRenderableSeries x:Name="boxSeries" />
      </s:SciChartSurface.RenderableSeries>
      <s:SciChartSurface.XAxis>
           <s:DateTimeAxis />
      </s:SciChartSurface.XAxis>
      <s:SciChartSurface.YAxis>
           <s:NumericAxis />
      </s:SciChartSurface.YAxis>
 </s:SciChartSurface>

I then populate them appropriately:

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        var ganttDataSeries = new BoxPlotDataSeries<double, DateTime>();
        var ganttData = GetGanttPlotData().ToArray();

        ganttSeries.DataSeries = ganttDataSeries;
        ganttDataSeries.Append(ganttData.Select(x => x.X),
                    ganttData.Select(x => x.Median),
                    ganttData.Select(x => x.Minimum),
                    ganttData.Select(x => x.LowerQuartile),
                    ganttData.Select(x => x.UpperQuartile),
                    ganttData.Select(x => x.Maximum));

        var boxData = GetBoxPlotData().ToArray(); // this is the same implementation as the demo 
        var boxDataSeries = new BoxPlotDataSeries<DateTime, double>();
        boxDataSeries.Append(boxData.Select(x => x.Date),
            boxData.Select(x => x.Median),
            boxData.Select(x => x.Minimum),
            boxData.Select(x => x.LowerQuartile),
            boxData.Select(x => x.UpperQuartile),
            boxData.Select(x => x.Maximum));

        boxSeries.DataSeries = boxDataSeries;
    }

    private IEnumerable<GanttPoint> GetGanttPlotData()
    {
        var xValues = Enumerable.Range(1, 10).ToArray();

        var random = new Random();

        for (int i = 0; i < 10; i++)
        {
            DateTime med = new DateTime(2016, 1, 1).AddDays(random.Next(31, 330));
            DateTime min = med.AddDays(-1 * random.Next(30));
            DateTime max = med.AddDays(random.Next(30));
            DateTime lower = min.AddDays((med - min).TotalDays * random.NextDouble());
            DateTime upper = max.AddDays(-1 * ((max - med).TotalDays * random.NextDouble()));

            Console.WriteLine($"Median:{med} Min:{min} Max:{max} Lower:{lower} Upper:{upper}");
            yield return new GanttPoint(xValues[i], min, lower, med, upper, max);
        }
    }

    private struct GanttPoint
    {
        public readonly double X;
        public readonly DateTime Minimum;
        public readonly DateTime LowerQuartile;
        public readonly DateTime Median;
        public readonly DateTime UpperQuartile;
        public readonly DateTime Maximum;

        public GanttPoint(double x, DateTime min, DateTime lower, DateTime med, DateTime upper, DateTime max)
            : this()
        {
            X = x;
            Minimum = min;
            LowerQuartile = lower;
            Median = med;
            UpperQuartile = upper;
            Maximum = max;
        }
    }

The attached image is what renders. Is there a reason for this behavior? I was really hoping to use this plot type to support the floating bars use case. I can make a custom renderable series to achieve the result, but that seems like a lot of work.

Please advise.

Thanks,
Mike.

Version
4.1.0.8615
Images
  • Andrew
    Hi Mike, before we answer this question, would developing a custom renderable series be more suited to this task, rather than hacking an existing series? Alternatively, to rotate a chart, you don’t set YAxis with datetime values, but you instead rotate the chart as per https://www.scichart.com/wpf-chart-example-vertical-charts/
  • Michael Dusoe
    Yeah, I can get the horizontal lines, and that works fine. XAxis on the Left, YAxis on the Bottom – perfect. That is not the issue. Placing DateTime on the YAxis (whether on the bottom or the right) causes this behavior. I have considered a custom renderable, taking in a XyySeries, and drawing the boxes as approriate. I was just hoping to not have to go down that path. (Time to write Series + Time to write XAML) will always be greater than (Time to write XAML) :+) I have seen that some have implemented this as a collection of Box Annotations, but I am already looking at providing a pile of annotations (both native and custom) to interact with the data, and I would rather not have to manage “Data Annotations” separate from “Tool Annotations”, especially since they are all in a single collection for the Surface.
  • You must to post comments
1
0

For anyone who cares 🙂

I solved this by making the YAxis Numeric, of type long, instead of DateTime. I store the values as Ticks, and then use a simple LabelProvider like below to change the labels to dates:

public class GanttLabelProvider : NumericLabelProvider
{
    public override string FormatLabel(IComparable dataValue)
    {
        return new DateTime( Convert.ToInt64(dataValue)).ToString("yyyy/MM/dd hh:mm:ss.ffff");
    }
}

Runs like a champ!

  • You must to post comments
Showing 1 result
Your Answer

Please first to submit.