This is a migrated thread and some comments may be shown as answers.

axis label perfomance with big zoom

14 Answers 322 Views
ChartView
This is a migrated thread and some comments may be shown as answers.
Vladimir
Top achievements
Rank 1
Veteran
Vladimir asked on 24 Jul 2017, 02:14 PM

Hi, i'm using LinearAxis for TimeSpan values. Minimum = 1 second and Maximum - maybe 1000 days.

For values - ScatterLineSeries. In test with perfomance troubles ~500 points.

        <telerik:RadCartesianChart Zoom="10,1"

            Name="_chart" Palette="{StaticResource ScatterCustomPalette}">
            <telerik:RadCartesianChart.HorizontalAxis >
                <telerik:LinearAxis  IsStepRecalculationOnZoomEnabled="False" LabelFitMode="Rotate">
                    <telerik:LinearAxis.LabelTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Converter={StaticResource LinearAxisTimeSpanConverter}}" />
                        </DataTemplate>
                    </telerik:LinearAxis.LabelTemplate>
                </telerik:LinearAxis>
            </telerik:RadCartesianChart.HorizontalAxis>

            <telerik:RadCartesianChart.VerticalAxis>
                <telerik:LinearAxis MajorStep="1"/>
            </telerik:RadCartesianChart.VerticalAxis>

            <telerik:RadCartesianChart.Behaviors>
                <telerik:ChartPanAndZoomBehavior x:Name="_panZoomBehavior" ZoomMode="Horizontal" DragToZoomThreshold="10" />
            </telerik:RadCartesianChart.Behaviors>
        </telerik:RadCartesianChart>

Real max zoom depends from data, Axis.Maximum/coef (>1).

Its wide range for axis X, so i update major step on zoom changing. 

        private void UpdateAxisMajorStep()
        {
            var xAxis = (LinearAxis) _chart.HorizontalAxis;

            double modelWidth = xAxis.Maximum - xAxis.Minimum;
            double viewWidth = xAxis.ActualWidth > 1
                ? xAxis.ActualVisibleRange.Maximum - xAxis.ActualVisibleRange.Minimum
                : modelWidth / _chart.Zoom.Width;
            xAxis.MajorStep = viewWidth / 10;
        }

Behaviour looks good, but when i'm trying zoom in with big factor perfomance slow down.

Chart shows 10 ticks and labels at horizontal axis, but looks like

    calculated labels count = Chart.ActualRange/MajorStep. 

For example Maximum = 500000, Visible range 0 - 5000 and MajorStep = 500, count of calculated labels =  1000. Perfomance becomes unusable. 

14 Answers, 1 is accepted

Sort by
0
Martin Ivanov
Telerik team
answered on 27 Jul 2017, 10:25 AM
Hi Vladimir,

I tried to reproduce the slow performance, but I wasn't able to do it. However, I would suggest you try the axis smart labels mode. The feature tries to automatically calculate a reasonable step in order to avoid labels overlapping. 

If this doesn't help I would ask you to send the code snippets that reproduces the issue or open a new support ticket from your telerik.com account and attach a runnable project there.

Regards,
Martin Ivanov
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Vladimir
Top achievements
Rank 1
Veteran
answered on 28 Jul 2017, 07:19 AM
public class PlotTimeSecPnt
{
    public int Second { get; set; }
    public double Value { get; set; }
 
    public PlotTimeSecPnt(int second, double value)
    {
        Second = second;
        Value = value;
    }
}
public class LinearAxisTimeSpanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        double seconds = System.Convert.ToDouble(value);
        TimeSpan ts = TimeSpan.FromSeconds(seconds);
        return ts.ToString("g");
    }
 
    public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

 

<Window x:Class="LabelsTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <telerik:RadCartesianChart Zoom="10,1"
            Name="_chart" Palette="{StaticResource ScatterCustomPalette}">
            <telerik:RadCartesianChart.HorizontalAxis >
                <telerik:LinearAxis SmartLabelsMode="None"  IsStepRecalculationOnZoomEnabled="False" LabelFitMode="Rotate">
                    <telerik:LinearAxis.LabelTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Converter={StaticResource LinearAxisTimeSpanConverter}}" />
                        </DataTemplate>
                    </telerik:LinearAxis.LabelTemplate>
                </telerik:LinearAxis>
            </telerik:RadCartesianChart.HorizontalAxis>

            <telerik:RadCartesianChart.VerticalAxis>
                <telerik:LinearAxis MajorStep="1"/>
            </telerik:RadCartesianChart.VerticalAxis>

            <telerik:RadCartesianChart.Behaviors>
                <telerik:ChartTrackBallBehavior SnapMode="ClosestPoint"
                                                ShowIntersectionPoints="True"
                                                ShowTrackInfo="True"/>
                <telerik:ChartPanAndZoomBehavior x:Name="_panZoomBehavior" ZoomMode="Horizontal" DragToZoomThreshold="10" />
            </telerik:RadCartesianChart.Behaviors>
        </telerik:RadCartesianChart>

        <telerik:RadLegend Background="White" 
                           BorderBrush="Black" 
                           BorderThickness="1" 
                           Items="{Binding LegendItems, ElementName=_chart}" 
                           HorizontalAlignment="Right" 
                           VerticalAlignment="Top" />
    </Grid>
</Window>

 

public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
 
            SetData();
 
            _chart.ZoomChanged += Chart_ZoomChanged;
            _chart.Loaded += _chart_Loaded;
            UpdateAxisMajorStep();
        }
 
        private void _chart_Loaded(object sender, RoutedEventArgs e)
        {
            _chart.PanOffset = new Point(-500, 0);
            _chart.Zoom = new Size(1000, 1);
        }
 
        private void SetData()
        {
            ScatterLineSeries lineSeries = new ScatterLineSeries
            {
                YValueBinding = new PropertyNameDataPointBinding {PropertyName = "Value"},
                XValueBinding = new PropertyNameDataPointBinding {PropertyName = "Second"},
                LegendSettings = new SeriesLegendSettings {Title = "1"}
            };
 
            List<PlotTimeSecPnt> data = new List<PlotTimeSecPnt>();
            Random startShiftRandom = new Random(0);
            Random endShiftRandom = new Random(1);
 
            data.Add(new PlotTimeSecPnt(0, 0));
 
            int x = 0;
            for (int i = 0; i < 500; i++)
            {
                int shiftStepStart = startShiftRandom.Next(1, 5000);
                data.Add(new PlotTimeSecPnt(x + shiftStepStart, 0));
                data.Add(new PlotTimeSecPnt(x + shiftStepStart, 1));
                x += shiftStepStart;
                int shiftStepEnd = endShiftRandom.Next(1, 50);
                data.Add(new PlotTimeSecPnt(x + shiftStepEnd, 1));
                data.Add(new PlotTimeSecPnt(x + shiftStepEnd, 0));
                x += shiftStepStart;
            }
 
            lineSeries.ItemsSource = data;
            _chart.Series.Add(lineSeries);
 
 
            LinearAxis hAxis = (LinearAxis) _chart.HorizontalAxis;
 
            hAxis.Minimum = data.Min(r => r.Second);
            hAxis.Maximum = data.Max(r => r.Second);
 
            LinearAxis vAxis = (LinearAxis) _chart.VerticalAxis;
            vAxis.Minimum = 0;
            vAxis.Maximum = 2;
 
            _chart.MaxZoom = new Size(hAxis.Maximum / 100, vAxis.Maximum);
        }
 
        private void Chart_ZoomChanged(object sender, ChartZoomChangedEventArgs e)
        {
            try
            {
                UpdateAxisMajorStep();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
 
        private void UpdateAxisMajorStep()
        {
            var xAxis = (LinearAxis) _chart.HorizontalAxis;
            if (xAxis.MajorStep > 0)
            {
                double viewWidth = xAxis.ActualVisibleRange.Maximum - xAxis.ActualVisibleRange.Minimum;
 
                xAxis.MajorStep = Math.Max(1, viewWidth / 10);
            }
        }
    }

 

_chart_Loaded - for checking

Smart labels mode tried, but need more managed behavior

 

 

0
Vladimir
Top achievements
Rank 1
Veteran
answered on 28 Jul 2017, 07:24 AM
//doesn't matter for results, just logic fix
int shiftStepEnd = endShiftRandom.Next(1, 50);
data.Add(new PlotTimeSecPnt(x + shiftStepEnd, 1));
data.Add(new PlotTimeSecPnt(x + shiftStepEnd, 0));
x += shiftStepEnd;  // instead of     x += shiftStepStart;
0
Martin Ivanov
Telerik team
answered on 02 Aug 2017, 07:13 AM
Hello Vladimir,

Thank you for the attached code. I assembled it into a runnable project and I will check what happens. Will get back to you later with more information on the matter.

Regards,
Martin Ivanov
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Dinko | Tech Support Engineer
Telerik team
answered on 16 Aug 2017, 02:23 PM
Hello Vladimir,

Thank you for your patience.

I have further investigated this behavior on my side and this is expected.  Let me try to explain why. In the provided code snippet you are changing the MajorStep property every time ZoomChanged event is called. When you are setting the major step the chart is recalculating every label in the visible range of the axis. Important here is that the major step is applied to all labels not only on the ones in the visual port. So the reason why the chart hangs is that in your case in the UpdateAxisMajorStep() method the major step is calculated to be a very small number (for example 10) which generate a large number of labels (for example  2000000 / 10). A large number of these labels will be placed into the visible range of the chart. This operation is slow when you have thousands of labels.

Regards,
Dinko
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which you to write beautiful native mobile apps using a single shared C# codebase.
0
Vladimir
Top achievements
Rank 1
Veteran
answered on 21 Aug 2017, 06:18 AM

I know...

[quote] A large number of these labels will be placed into the visible range of the chart. This operation is slow when you have thousands of labels. [/quote]

So, control currently is not applicable for my requirements and i will continue to use oxyplot without similar problems from box. 

But for the future i think its not good to generate content, most of which i will not see.

Maybe it hard to implement now, but in another controls its solved by virtualization or async generation of visible parts or fully managed generation in event handlers.

0
Martin Ivanov
Telerik team
answered on 23 Aug 2017, 12:42 PM
Hello Vladimir,

The chart has a built-in UI virtualization. Its axes will generate only the labels that should be plotted into the visible range. The ones outside of this range won't be generated. However, the MajorStep is applied to the whole axis, not only to the visible range. For example, if you have a range between 0 and 100, and you apply a step of 3, the axis will know that it should position a label on each 3rd value (0, 3, 6, 9,..., 99). Anyway, labels will be generated and positioned only in the visible range. So, if the visible range is between 20 and 30, there will be labels generated for values, 21, 24, 27 and 30. 

In your case at some point the number of the visible labels is 100 and on each MouseMove (which happens in an few milliseconds interval), a new redrawing of those 100 labels is scheduled. This is a bit heavy operation for the framework and it causes the slow performance.

As an alternative approach you could try to hide the axis labels and use a custom annotations to draw the labels manually. I attached a small example demonstrating this approach. Can you give it a try and let me know if it works for your case?

Regards,
Martin Ivanov
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Vladimir
Top achievements
Rank 1
Veteran
answered on 24 Aug 2017, 07:15 AM
Simple calculations for 100 labels from attached perfomance tracing screenshot (on top of post):
(800ms/3300 labels for all range)*100 (target visible label count) = ~ 24ms for 1 measure event (Pan or Zoom change).
Not perfect method, but its enough speed for most cases.
For less number of labels it will even faster. In most of cases count of labels < 30.
But in current render system i need wait 800ms for 1 zoom event, and its not limit. Zoom becomes unusable.
[quote]
In your case at some point the number of the visible labels is 100 and on each MouseMove (which happens in an few milliseconds interval), a new redrawing of those 100 labels is scheduled. This is a bit heavy operation for the framework and it causes the slow performance.
[/quote]

Looks like working manual solution. Not perfect, because i need manually set offset for label area, but far better than drawing text labels manually out of control.
And i checked this with 100 annotations - it works enough fast in pan and zoom. So, problem not in count of visible labels/annotations. 
[quote]
As an alternative approach you could try to hide the axis labels and use a custom annotations to draw the labels manually. I attached a small example demonstrating this approach. Can you give it a try and let me know if it works for your case?
[/quote]
0
Martin Ivanov
Telerik team
answered on 28 Aug 2017, 02:14 PM
Hello Vladimir,

I am glad to hear the the annotations approach works for you.

About the default labels drawing, keep in mind that setting the major step triggers some calculations and processes in the chart which could lead (but not necessarily) to a slower performance for drawing 100 default labels compared to 100 annotations. As for the performance tracing screenshot, it shows ~3300 generated labels and this is the number of the labels in the visible range. On my side I reproduce this if at some point the major step gets too small. The example with the 100 labels in my last reply was merely an example. There are moments that the major step is so small, the number of visible labels could reach thousands. You can avoid this by limiting the MaxZoom. Or by changing the manual MajorStep setting to use some kind of minimum MajorStep if it gets too small.

As aside note, keep in mind that the MaxZoom property works with relative units. For example 'new Size(10, 0)' setting specifies that the data will be zoomed 10 times according to the horizontal axis and won't be zoomed by the vertical axis.

Regards,
Martin Ivanov
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Vladimir
Top achievements
Rank 1
Veteran
answered on 29 Aug 2017, 06:19 AM

In the visible range? Do you mean visible range = hAxis.Maximum - hAxis.Minimum?

[quote]

As for the performance tracing screenshot, it shows ~3300 generated labels and this is the number of the labels in the visible range.

[/quote]

Because i about ActualVisibleRange:
                double viewWidth = xAxis.ActualVisibleRange.Maximum - xAxis.ActualVisibleRange.Minimum;
                xAxis.MajorStep = Math.Max(1, viewWidth / 10);

Take a look at new screenshot for code from sample. Its calculated 10000 labes when changed MajorStep.

It shows only 10 from 10000 labels. Major step is as should be.

UpdateAxisMajorStep called after zoom changed and ActualVisibleRange is set right already. No reason to calc 10000 labels, except maybe caching for some future actions (pan). 

0
Martin Ivanov
Telerik team
answered on 31 Aug 2017, 01:58 PM
Hello Vladimir,

By visible range I mean the axis' ActualVisibleRange.Minimum and ActualVisibleRange.Maximum.

About the screen shot I am not sure why the calculated labels are 10 thousands but it seems that the MajorStep is too small at some point. Note that the traced information could be gathered after or before setting the major step, so we can't tell what is the step at this specific moment and therefore it could not match the rendered result.

Regards,
Martin Ivanov
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Vladimir
Top achievements
Rank 1
Veteran
answered on 01 Sep 2017, 06:26 AM

Hello, Martin.

Maybe MajorStep is too small at some render pipeline point, but not in result.

In this tracing no changes from code above. And no any user actions in UI, just start App, waiting few seconds for change of MajorStep and drawing lables. Then i click the button to create tracing snapshot. So you can just check it, and should get same results.

Attached screenshot with only 1 change - row with MajorStep change is commented.

 

 

 

0
Dinko | Tech Support Engineer
Telerik team
answered on 06 Sep 2017, 06:27 AM
Hi Vladimir,

Thank you for the provided screenshot. We need more time to check this. We will get back to you as soon as we have more information about your case.

Regards,
Dinko
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which you to write beautiful native mobile apps using a single shared C# codebase.
0
Martin Ivanov
Telerik team
answered on 30 Jan 2018, 02:13 PM
Hello Vladimir,

Maybe it is possible that the MajorStep is calculated to a very big value at some point. If you haven't found a solution for this yet, you can share a small project along with the exact steps to reproduce the issue shown in the image from your last post. And then send it over so I can check if I can come with some ideas for resolving this.

Note that the forum doesn't allow attaching files but you can open a new support ticket from your telerik.com account and attach a .zip there.

Regards,
Martin Ivanov
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
Tags
ChartView
Asked by
Vladimir
Top achievements
Rank 1
Veteran
Answers by
Martin Ivanov
Telerik team
Vladimir
Top achievements
Rank 1
Veteran
Dinko | Tech Support Engineer
Telerik team
Share this question
or