BarSeries and DateTimeContinuousAxis.MaximumTicks

8 posts, 0 answers
  1. Louis
    Louis avatar
    83 posts
    Member since:
    Aug 2013

    Posted 16 May 2014 Link to this post

    I have a problem where setting the MaximumTicks on a DateTimeContinousAxis causes the Bars on a BarSeries to be drawn incorrectly for large numbers of bars. Below is an example showing the problem.

    If you run the example, you can see that initially the bars correspond to the line series plotting the same data (ignoring the aliasing effect of too many bars for the number of pixels to display them on). However, you have way too many labels to display on the X axis, and a solid grey background due to the number of grid lines.

    Now, use the box at the bottom to change the value of MaximumTicks (note that this is the first time it's being set). Now the bars do not appear to correspond to the line series. (My theory is that they're being drawn way too wide.) The labels and grid lines now look good, however.

    Now drag the right zoom handle on the horizontal axis as far left as possible (maximum zoom at the start of the series). You see similar effects to the zoomed out view. 

    Now, again use the box at the bottom to change the value of MaximumTicks and the display goes back to what is expected. Now you can zoom back out, and you still have the expected view where the bars match the line.

    Is there another way to filter down the number of labels and grid lines and get the bars to draw at a reasonable width? I tried GapLength, but this didn't resolve the problem (0.95 looks much better, but they're still overlapping a bit, 0.5 looks the same as without).

    Thanks,
    Louis

    XAML:
    <Window x:Class="BarDensity.MainWindow"
                    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                    xmlns:local="clr-namespace:BarDensity"
                    Title="MainWindow" Height="750" Width="1000">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <telerik:RadCartesianChart Margin="5" x:Name="PropertyChart">
                <telerik:RadCartesianChart.Behaviors>
                    <telerik:ChartPanAndZoomBehavior ZoomMode="Both" PanMode="Both" />
                </telerik:RadCartesianChart.Behaviors>
                <telerik:RadCartesianChart.Grid>
                    <telerik:CartesianChartGrid MajorLinesVisibility="XY" />
                </telerik:RadCartesianChart.Grid>
                <telerik:RadCartesianChart.HorizontalAxis>
                    <telerik:DateTimeContinuousAxis LabelFitMode="Rotate" LabelFormat="yyyy-MMM" GapLength="0.5"/>
                </telerik:RadCartesianChart.HorizontalAxis>
                <telerik:RadCartesianChart.VerticalAxis>
                    <telerik:LinearAxis />
                </telerik:RadCartesianChart.VerticalAxis>
     
                <telerik:RadCartesianChart.Series>
                    <telerik:BarSeries CategoryBinding="Date"
                           ValueBinding="Value"
                           ItemsSource="{Binding Path=Series1}">
                        <telerik:BarSeries.PointTemplate>
                            <DataTemplate>
                                <Rectangle Fill="Blue" />
                            </DataTemplate>
                        </telerik:BarSeries.PointTemplate>
                    </telerik:BarSeries>
                    <telerik:LineSeries CategoryBinding="Date"
                           ValueBinding="Value"
                           ItemsSource="{Binding Path=Series1}"
                           Stroke="Red">
                    </telerik:LineSeries>
                </telerik:RadCartesianChart.Series>
            </telerik:RadCartesianChart>
            <StackPanel Grid.Row="1" Orientation="Horizontal">
                <Label>MaximumTicks:</Label>
                <telerik:RadNumericUpDown IsEditable="True" Minimum="10" Maximum="50"
                                          Value="{Binding Path=MaximumTicks}"/>
            </StackPanel>
        </Grid>
    </Window>

    Code Behind:
    namespace BarDensity
    {
        public class MyPoint
        {
            public DateTime Date { get; set; }
            public Double Value { get; set; }
        }
        public partial class MainWindow : Window
        {
            private int _MaximumTicks = 20;
            public List<MyPoint> Series1 { get; private set; }
            public int MaximumTicks
            {
                get { return _MaximumTicks; }
                set
                {
                    if (_MaximumTicks != value)
                    {
                        _MaximumTicks = value;
                        DateTimeContinuousAxis dateAxis = PropertyChart.HorizontalAxis as DateTimeContinuousAxis;
                        if (dateAxis != null)
                            dateAxis.MaximumTicks = _MaximumTicks;
                    }
                }
            }
            public MainWindow()
            {
                Series1 = new List<MyPoint>();
                DateTime startDate = new DateTime(2014, 1, 1);
                for (int i = 0; i < 1000; i++)
                    Series1.Add(new MyPoint() { Date = startDate.AddMonths(i),
                        Value = (Math.Sin(i / 100.0)) * 500 });
                InitializeComponent();
                DataContext = this;
            }
        }
    }

  2. Martin
    Admin
    Martin avatar
    1099 posts

    Posted 21 May 2014 Link to this post

    Hello Louis,

    Thank you for the provided code.

    Note that the width of the bars in the BarSeries depends on the tick slot width (the length between two adjacent ticks). When you use the DateTimeCategoricalAxis the bars will be resized so that they fit in their category without overlapping. On the other hand the DateTimeContinuousAxis behave little differently. As it is a numeric axis and there are no fixed number of categories the bars will be positioned on the value that is passed as their Category, which might not be exactly on the tick. For example if you have two ticks on 12:40 and 14:40 and you place two bars respectively on 13:30 and  13:50, (the bars width will be the distance between the ticks which is 2 hours) the first bar will be positioned around the middle of the slot and will spread to the middle of the next slot, the second bar will be positioned over the first one and it will spread after the middle of the next slot.

    In general I don't think that the BarSeries is most suitable in a scenario with so much data points. However, if you insist using it you can try the DateTimeCategoricalAxis and instead MaximumTicks property set the MajorTickeInterval.

    Also keep in mind that the GridLines of the chart depends on the ticks count of the axes. For example if you have 10 ticks on the horizontal axis there will be 10 lines snapped to the ticks.

    Please let me know if you have any further questions.

    Regards,
    Martin
    Telerik
     
    Check out Telerik Analytics, the service which allows developers to discover app usage patterns, analyze user data, log exceptions, solve problems and profile application performance at run time. Watch the videos and start improving your app based on facts, not hunches.
     
  3. UI for WPF is Visual Studio 2017 Ready
  4. Louis
    Louis avatar
    83 posts
    Member since:
    Aug 2013

    Posted 28 May 2014 in reply to Martin Link to this post

    Hi Martin,

    Thanks for the reply, and sorry it's taken me a while to get back. I agree with you, bar charts aren't the best way to visualize this quantity of data; users' requirements don't always make the most sense, but that's what we are asked to implement.

    I've changed to a DateTimeContinuousAxis and now things look correct. However, I've had to implement additional filtering on the data being sent to the chart as the DateTimeContinuousAxis doesn't allow have a Minimum or Maximum property like the CategoricalAxis does to adjust what is being displayed. Could I put in a feature request for these two properties?

    Thanks again,
    Louis
  5. Martin
    Admin
    Martin avatar
    1099 posts

    Posted 29 May 2014 Link to this post

    Hi Louis,

    I noticed that you said you changed the axis to a DateTimeContinuousAxis. Did you mean DateTimeCategorical? However, keep in mind that the Maximum and Minimum properties makes sense only in continuous axes (like DateTimeContinuousAxis and LinearAxis) with quantitative data.

    On the other hand the categorical axis doesn't have range because there can be various categories. For example if you have the following categories - apples, bottles, buildings, clothes  - you cannot determine which one is the minimum/maximum. The DateTimeCategoricalAxis derives from the CategoricalAxis and behaves the same, except that it sorts its categories by date before they are plotted to the chart.

    Please let me know if you need have any further questions.

    Regards,
    Martin
    Telerik
     
    Check out Telerik Analytics, the service which allows developers to discover app usage patterns, analyze user data, log exceptions, solve problems and profile application performance at run time. Watch the videos and start improving your app based on facts, not hunches.
     
  6. Louis
    Louis avatar
    83 posts
    Member since:
    Aug 2013

    Posted 02 Jun 2014 in reply to Martin Link to this post

    Hi Martin, yes, sorry, I swapped the two... I switched from a DateTimeContinuous to a DateTimeCategorical.

    I understand the general categorical axis not implementing Minimum and Maximum, but since the DateTimeCategorical does have an implicit ordering for a continuous set of values, I think Minimum and Maximum do make sense for that subclass.

    Thanks again,
    Louis
  7. Martin
    Admin
    Martin avatar
    1099 posts

    Posted 03 Jun 2014 Link to this post

    Hello Louis,

    Note that the DateTimeCategoricalAxis does not implement Minimum and Maximum properties not just because they are not inherited from the base class, but because it is an axis that represents categorical data. If there was such properties the conception for a categorical data will be broken.

    Regards,
    Martin
    Telerik
     
    Check out Telerik Analytics, the service which allows developers to discover app usage patterns, analyze user data, log exceptions, solve problems and profile application performance at run time. Watch the videos and start improving your app based on facts, not hunches.
     
  8. Louis
    Louis avatar
    83 posts
    Member since:
    Aug 2013

    Posted 03 Jun 2014 in reply to Martin Link to this post

    Hi Martin,

    I do not think the conception of categorical data is broken any more by having a min and a max than by an inherent sort. The sort, and a range implied by a min and max are semantically appropriate given the nature of a date time axis. They don't conflict with having a categorical axis, but merely add additional meaning. Similar to the way a DateTimeContinuousAxis adds semantics to a CartesianAxis.

    If C# had multiple inheritance, you could imagine both DateTimeContinuousAxis and DateTimeCategoricalAxis inheriting from a DateTimeAxis base class with these properties as well  as their respective base classes. The semantics comes along with the DateTime type used by both axes.

    Louis
  9. Petar Marchev
    Admin
    Petar Marchev avatar
    968 posts

    Posted 06 Jun 2014 Link to this post

    Hello,

    I agree with Martin. When it comes to a categorical axis, there is no notion of a range (min, max and step). You can index the categories and find some data-specific way of sorting. For instance you can sort "Bananas", "Carrots" and "Papaya" alphabetically. You can sort "2023 Jan", "2014 Jan" and "2015 Jan" in a date time manner. Here you can extract the first category, but you cannot set a minimum. Setting a minimum means you are actually filtering the data.

    I think that you have already implemented the proper solution here, filtering the incoming data. Let us know if you have any other questions.

    Regards,
    Petar Marchev
    Telerik
     
    Check out Telerik Analytics, the service which allows developers to discover app usage patterns, analyze user data, log exceptions, solve problems and profile application performance at run time. Watch the videos and start improving your app based on facts, not hunches.
     
Back to Top
UI for WPF is Visual Studio 2017 Ready