Panning and Zooming

Oystein Bjorke 10 років тому 0
This discussion was imported from CodePlex

JuergenD wrote at 2012-11-07 15:51:

Hi, thanks for this great Plot-Library!

I have a question about panning and zooming charts. I have a time series with data for every day for about ten years. I created a simple line chart with this data and everything looks nice. But I have a problem with the zoom/pan feature. 

Using the Mousewheel or the +/- Key zooms the chart in both directions (x and y axis). But I need the zoom only on the x-axis and the y-axis should scale itself automatically by the new min/max values inside the chart window. 

Also panning the chart left/right with the cursor keys should rescale the y-axis.

Is this possible?

Best Regards
Juergen 


objo wrote at 2012-11-07 16:15:

Sorry, this is not supported 'out of the box'.

You could subscribe to the x-axis change event, then find the minimum and maximum y-values for the visible part of the curve (you have to write this yourself).
Then update the minimum and maximum properties of the y-axis and refresh the plot.

This should work for both mouse and keyboard events.


JuergenD wrote at 2012-11-08 09:31:

Oh wow, that was quick :-) Thanks for your fast response.

I will try to add your suggestions to my code. Thanks for your help.


JuergenD wrote at 2012-11-08 13:22:

Ok, I was playing around with the code but without success. How can I update the min/max Values of the y-axis?

I can access the min/max DateTime Values from the visible range inside the change event like so

void xAxisDateTime_AxisChanged(object sender, AxisChangedEventArgs e) {
    DateTimeAxis axis = sender as DateTimeAxis;
    DateTime dtMax = DateTime.FromOADate(axis.ActualMaximum);
    DateTime dtMin = DateTime.FromOADate(axis.ActualMinimum);

But when I try to update the min/max values of the y-axis with new values I already retrieved from my data:

myYAxis.Minimum = GetMin(dtMin, dtMax);
myYAxis.Maximum = GetMax(dtMin, dtMax);
myPlot.RefreshPlot(true);

nothing happens. How can I refresh the chart?
By the way, I am using class variables (myPlot, myYAxis) to access the plot components. Is it possible to get access to the components via the "sender" object? I am in the X-Axis event handler. How can I access the Y-Axis or the series of my plotmodel without using class variables?

Best Regards
Juergen 


JohnnyPP wrote at 2012-11-13 21:15:

Hello,

 

what is the status of this thread? I am also very interested in the feature that zooms only the x-axis and the y-axis scales itself automatically by the new min/max values inside the chart window.

 

Kind regards,

Johnny 


JuergenD wrote at 2012-11-14 09:48:

@JohnnyPP

The status is unchanged. Unfortunately I got no response from the author anymore.

The current implemented zoom/pan feature feels more like a zoom/pan inside a drawing software like Photoshop. In my opinion it makes no sense to zoom/pan a chart this way. It is pretty useless to see areas of my chart without the function graph or data series. Therefore I would prefer an option to force the chart to show always a piece of my function and disable the possibility to pan or zoom to an area where the function or the dataseries does not exist.

I would need an example or information about how I can change the min/max values for the y-axis and then force a redraw of the chart. It does not work with the method I already tested (see my prev. message). 

Another problem is to get access to the y-axis and the series from inside x-axis event handler. I can use "global" class variables, but this is a bit clumsy...

Please let me know when you get any further information. 

Best Regards,
Juergen 


objo wrote at 2012-11-14 18:36:

The Minimum and Maximum properties are overridden by the ViewMaximum and ViewMinimum properties (which are set when you pan/zoom your axes). You can

a) call Reset() then set Minimum and Maximum on the y-axis

or

b) call Zoom(newMinimum,newMaximum) on the y-axis

Then call RefreshPlot as you do in your code.


JuergenD wrote at 2012-11-15 09:57:

@objo

No need to be sorry, the software is free and I am very grateful for your work :-)

Your last hint was the missing puzzle-piece for my code. I am using variant b and now I can pan the chart on the x-axis and the y-axis and the function plot redraws perfectly. I have to fine tune the performance of my seek min/max method for really large data series, but that should be easy...

Many Thanks for your help

and best regards

Juergen 


JohnnyPP wrote at 2012-11-15 20:20:

Hello Juergen,

 

would it be possible for you to post your solution to the forum? It would help me a lot.

Thanks in advance!

 

Johnny


JuergenD wrote at 2012-11-16 10:00:

@Johnny

My program is rather to big to publish it here. But I wrote a small example, with my "solution". It is not perfect, but it works for me.
Create a new Windows Form Solution named SimpleChart, add  an OxyPlot.WindowsForms.Plot control on your form, change the Layout.Doc property from None to Fill and use my code inside form1.cs.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using OxyPlot;

namespace SimpleChart {
    public partial class Form1 : Form {
        //I am using a small helper class for my date/value pairs
        //providing an IComparable Interface for using BinarySearch
        //in the x-axis change event
        class MyValue : IComparable {
            public DateTime Date { get; set; }
            public double Value { get; set; }
            public MyValue(DateTime d) {
                Date = new DateTime(d.Year, d.Month, d.Day);
            }
            public MyValue(DateTime d, double v) {
                Date = new DateTime(d.Year, d.Month, d.Day);
                Value = v;
            }
            int IComparable.CompareTo(object to) {
                return Date.CompareTo(((MyValue)to).Date);
            }
        }
            
        DateTimeAxis xAxisDateTime;
        LinearAxis yAxisValues;
        List<MyValue> myValues;
        PlotModel plotModel;
        LineSeries lineSeries;

        public Form1() {
            InitializeComponent();

            myValues = new List<MyValue>();
            DateTime startDate = new DateTime(2000, 1, 1);
            double startValue = 100;
            Random r = new Random();
            for (int i = 0; i < 1000; i++, startDate = startDate.AddDays(1), startValue += (r.NextDouble() - 0.5)) {
                myValues.Add(new MyValue(startDate, startValue));
            }

            plotModel = new PlotModel("Chart");

            xAxisDateTime = new DateTimeAxis() { 
                IntervalType = DateTimeIntervalType.Auto, 
                MinorIntervalType = DateTimeIntervalType.Days, 
                MajorGridlineStyle = LineStyle.Solid, 
                MinorGridlineStyle = LineStyle.Dot, 
                CalendarWeekRule = System.Globalization.CalendarWeekRule.FirstFourDayWeek, 
                FirstDayOfWeek = DayOfWeek.Monday, 
                Position = AxisPosition.Bottom 
            };
            //Set the min/max values of the x-axis to my min/max date to prevent zoom/pan beyond data series
            xAxisDateTime.AbsoluteMinimum = myValues[0].Date.ToOADate();
            xAxisDateTime.AbsoluteMaximum = myValues[myValues.Count - 1].Date.ToOADate();
            
            //calling user defined event on change
            xAxisDateTime.AxisChanged += xAxisDateTime_AxisChanged;

            yAxisValues = new LinearAxis() { 
                MajorGridlineStyle = LineStyle.Solid, 
                MinorGridlineStyle = LineStyle.Dot 
            };

            plotModel.Axes.Add(xAxisDateTime);
            plotModel.Axes.Add(yAxisValues);

            lineSeries = new LineSeries {
                Title = "Values",
                Color = OxyColor.FromArgb(255, 78, 154, 6),
                MarkerType = MarkerType.None,
                StrokeThickness = 1,
                DataFieldX = "Date",
                DataFieldY = "Value",
                ItemsSource = myValues
            };

            plotModel.Series.Add(lineSeries);
            plot1.Model = plotModel;

        }

        void xAxisDateTime_AxisChanged(object sender, AxisChangedEventArgs e) {
            DateTimeAxis axis = sender as DateTimeAxis;

            //save the current min/max date values
            DateTime dtMax = DateTime.FromOADate(axis.ActualMaximum);
            DateTime dtMin = DateTime.FromOADate(axis.ActualMinimum);

            double minValue = double.MaxValue;
            double maxValue = double.MinValue;

            //BinarySearch, because the xAxisDateTime is sorted
            int idxMin = myValues.BinarySearch(new MyValue(dtMin));
            int idxMax = myValues.BinarySearch(new MyValue(dtMax));

            //if we can not find an exact location (result is negativ) we can use the complement 
            //see BinarySearch help for further explanation
            if (idxMin < 0) idxMin = ~idxMin;
            if (idxMax < 0) idxMax = ~idxMax;

            //find the corresponding min/max values in the selected intervall
            for (int i = idxMin; i < idxMax; i++) {
                minValue = Math.Min(minValue, myValues[i].Value);
                maxValue = Math.Max(maxValue, myValues[i].Value);
            }

            //set y-axis min/max and redraw the chart
            yAxisValues.Zoom(minValue, maxValue);
            plotModel.RefreshPlot(true);
        }
    }
}

When running the program, you have first to zoom (mouse wheel or ctrl-right mouse button) inside the chart to use the x-axis pan feature (drag with right mouse button or use cursor keys), because if it is "unzoomed" you already see the whole chart and cannot pan beyond valid x/y pairs. Ctrl-Double-Click with Right mouse button brings back the whole chart.

Hope that helps
bet regards

Juergen 


endorphing wrote at 2013-10-08 17:18:

I believe there's a property called 'IsZoomEnabled' in the Axis (in the WPF version), that lets you disable zooming in a particular direction.

Gubo wrote at 2014-03-19 10:34:

I think it does not work 100% accurately because instead of DateTime.FromOADate() you should use DateTimeAxis.ToDateTime(). There is a one day difference for some reason.

jackRose wrote at 2014-05-06 13:18:

I think it's very important feature - the zoom only on the x-axis or the y-axis. The best choice +/- for zooming and mouse wheel for panning. I will try to use info from this topic, but I hope it will be supported 'out of the box'. Oxyplot is really fantastic graphic library, but it needs to get this feature.

@objo
In any case thank you for great job!