ASE Home Page Products Download Purchase Support About ASE
ChartDirector Support
Forum HomeForum Home   SearchSearch

Message ListMessage List     Post MessagePost Message

  Autoscale Y-axis when zooming
Posted by Eferov Ivan on Nov-02-2016 11:24
Good day.

i use version 6.1, programming language C# WinForm, operation system Windows 8.1 x64.

please help to understand, i use for limiting minimum and maximum Y-axis chartArea.yAxis().setAutoScale(0.01, 0.01, 0.01);, but if i use zoomIn or zoomOut the axis of the border as before.

me need so that axis borders adjust to maximum and minimum values in the visible region when zooming.

how can i do that?

Thanks very much
Eferov Ivan

  Re: Autoscale Y-axis when zooming
Posted by Peter Kwan on Nov-03-2016 00:36
Hi Eferov,

If the y-axis is auto-scaled, it is always scaled according to the data values passed to the chart.

In the Windows Forms sample code, there is an example called “Simple Zooming and Scrolling”. When you drag to scroll the chart, you can see the axis scale changes. You can also add c.yAxis().setAutoScale(0.01, 0.01, 0.01); to the code, and try again. The y-axis scale will change even more as your drag the chart.

Note that for auto-scaled axis, the scale will be based on the data passed to ChartDirector. In the “Simple Zooming and Scrolling”, only the visible data are passed to ChartDirector (the viewPortDataSeriesA, viewPortDataSeriesB, viewPortDataSeriesC and viewPortTimeStamps), so the axis is scaled based on the visible data.

For your case, if the axis scale does not appear to change when you scroll the chart, please check the followings:

(a) Are you passing only visible data to the chart?

(b) Is the axis auto-scaled? (Do you have another line that calls setLinearScale to scale the axis?)

(c) Is the Axis.setAutoScale called before the chart or track line is drawn?

If the above still does not solve the problem, is it possible to email me more complete code, and also provide a screen shot for the chart which you think is not auto-scaled correctly?

Regards
Peter Kwan

  Re: Autoscale Y-axis when zooming
Posted by Eferov Ivan on Nov-03-2016 08:49
Attachments:
My code:

chartArea = new XYChart(Width - 16, Height - 142);
chartArea.setPlotArea(50, 5, Width - 16 - 100, Height - 142 - 57, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
chartArea.xAxis().setTickDensity(Width - 16 - 100);
chartArea.setClipping();
chartArea.yAxis2().syncAxis(chartArea.yAxis());
chartArea.xAxis().setLabelFormat("");
chartArea.xAxis().setLabelStyle("Arial", 6);
viewer.ZoomInWidthLimit = 5.0 / ordinalList[0].Count;
chartArea.xAxis().setLabels(xAsixDate[0].ToArray());
try { viewer.setFullRange("x", ordinalList[0][0], ordinalList[0][ordinalList[0].Count - 1]); } catch { }
chartArea.xAxis().setLabelStep(ordinalList[0].Count / timeInterval / 200 * GridFactor);
viewer.syncLinearAxisWithViewPort("x", chartArea.xAxis());

layer[0] = chartArea.addLineLayer2();
layer[0].addDataSet(yAsixDate[0].ToArray(), colorInteget[0]);
layer[0].setXData(ordinalList[0].ToArray());
layer[0].setLineWidth(wLines[0]);
chartArea.yAxis().setAutoScale(0.01, 0.01, 0.01);
viewer.Chart = chartArea;

viewer.ImageMap = chartArea.getHTMLImageMap("clickable", "", "title='{xLabel|mm/dd/yyyy hh:mm:ss} n$ {value}'");

picture 1 - full graph
picture 2 - region of graph


if i use zooming y-axis dont auto scaling based min and max values in visible region on graph
Безымянный.png
12.png

  Re: Autoscale Y-axis when zooming
Posted by Peter Kwan on Nov-04-2016 00:11
Hi Eferov,

ChartDirector will auto-scale the axis based on the data passed to ChartDirector. For your case, the issue is because your code always pass all the data to ChartDirector, including the invisible data, so the axis scale is always the same and includes invisible data.

To achieve what you need, please only provide the visible data to ChartDirector.

For your case, you seem to be using a label based x-axis (configured using Axis.setLabels), rather than a true date/time based axis.

I am not sure what is ordinalList[0]. If it is just ordinal numbers 0, 1, 2, 3, ...., you can use the following method for a label based x-axis:


// The following 4 lines will not change when the chart zooms or scrolls, so they can be
// declared as Form members and run once after the data are available
DateTime[] xLabels = xAsixDate[0].ToArray();
double[] yData = yAsixDate[0].ToArray();
viewer.setFullRange("x", 0, xLabels.Length - 1);
viewer.ZoomInWidthLimit = 5.0 / xLabels.Length;


// Get the visible part of the data arrays
int startIndex = (int)Math.Floor(viewer.getValueAtViewPort("x", viewer.ViewPortLeft));
int endIndex = (int)Math.Ceiling(viewer.getValueAtViewPort("x", viewer.ViewPortLeft + viewer.ViewPortWidth));
int noOfPoints = endIndex - startIndex + 1;
DateTime[] viewPortLabels = (DateTime[])Chart.arraySlice(xLabels, startIndex, noOfPoints);
double[] viewPortData = (double[])Chart.arraySlice(yData, startIndex, noOfPoints);

// plot the chart
chartArea = new XYChart(Width - 16, Height - 142);
chartArea.setPlotArea(50, 5, Width - 16 - 100, Height - 142 - 57, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
chartArea.setClipping();
chartArea.yAxis2().syncAxis(chartArea.yAxis());
chartArea.xAxis().setLabelStyle("Arial", 6);
chartArea.xAxis().setLabels(viewPortLabels);
chartArea.xAxis().setLabelStep(viewPortLabels.Length / timeInterval / 200 * GridFactor);
layer[0] = chartArea.addLineLayer2();
layer[0].addDataSet(viewPortData, colorInteget[0]);
layer[0].setLineWidth(wLines[0]);
viewer.Chart = chartArea;

viewer.ImageMap = chartArea.getHTMLImageMap("clickable", "", "title='{xLabel|mm/dd/yyyy hh:mm:ss} n$ {value}'");



If you can use a true date/time x-axis, you can use similar code structure as in the ChartDirector zooming and scrolling sample code, like:

// The following 4 lines will not change when the chart zooms or scrolls, so they can be
// declared as Form members and run once after the data are available
DateTime[] timeStamps = xAsixDate[0].ToArray();
double[] dataSeriesA = yAsixDate[0].ToArray();
viewer.setFullRange("x", timeStamps[0], timeStamps[timeStamps.Length - 1]);
viewer.ZoomInWidthLimit = 5.0 / timeStamps.Length;

// Get the visible part of the data arrays
DateTime viewPortStartDate = Chart.NTime(viewer.getValueAtViewPort("x", viewer.ViewPortLeft));
DateTime viewPortEndDate = Chart.NTime(viewer.getValueAtViewPort("x", viewer.ViewPortLeft + viewer.ViewPortWidth));

int startIndex = (int)Math.Floor(Chart.bSearch(timeStamps, viewPortStartDate));
int endIndex = (int)Math.Ceiling(Chart.bSearch(timeStamps, viewPortEndDate));
int noOfPoints = endIndex - startIndex + 1;

DateTime[] viewPortTimeStamps = (DateTime[])Chart.arraySlice(timeStamps, startIndex, noOfPoints);
double[] viewPortDataSeriesA = (double[])Chart.arraySlice(dataSeriesA, startIndex, noOfPoints);

// Plot the chart
chartArea = new XYChart(Width - 16, Height - 142);
chartArea.setPlotArea(50, 5, Width - 16 - 100, Height - 142 - 57, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
chartArea.setClipping();
chartArea.yAxis2().syncAxis(chartArea.yAxis());
chartArea.xAxis().setLabelStyle("Arial", 6);
viewer.syncLinearAxisWithViewPort("x", chartArea.xAxis());

layer[0] = chartArea.addLineLayer2();
layer[0].addDataSet(viewPortDataSeriesA, colorInteget[0]);
layer[0].setXData(viewPortTimeStamps);
layer[0].setLineWidth(wLines[0]);
viewer.Chart = chartArea;

viewer.ImageMap = chartArea.getHTMLImageMap("clickable", "", "title='{x|mm/dd/yyyy hh:mm:ss} n$ {value}'");


*** NOTE ***: I have not tested the above code. There may be type mistakes and you may need to debug them.

Hope this can help.

Regards
Peter Kwan

  Re: Autoscale Y-axis when zooming
Posted by Eferov Ivan on Nov-05-2016 11:10
ooooo thx))

but that if i have 2 line and coordination Y and X axis != ?

  Re: Autoscale Y-axis when zooming
Posted by Eferov Ivan on Nov-05-2016 15:22
Attachments:
Line 1 coordinates:
X, Y
0, 91400
1, 91520
2, 91520
3, 91520
4, 91590
5, 91640
6, 91600
7, 91570
8, 91590
9, 91450
10, 91500
11, 91530
12, 91540
13, 91550
14, 91440
15, 91460
16, 91370
17, 91250
18, 91310
19, 91320
20, 91270
21, 91240
22, 91190
23, 91190
24, 91150
25, 91230
26, 91170
27, 91230
28, 91220
29, 91220
30, 91260
31, 91300
32, 91320
33, 91300
34, 91280
35, 91280
36, 91230
37, 91250
38, 91230
39, 91260
40, 91270

Line2 coordinates:
X, Y
0, 91400
5, 91640
7, 91570
8, 91590
9, 91450
13, 91550
14, 91440
15, 91460
17, 91250
19, 91320
24, 91150
25, 91230
26, 91170
27, 91230
29, 91220
32, 91320
36, 91230
37, 91250
38, 91230
40, 91270

and pic graph
Безымянный.png

  Re: Autoscale Y-axis when zooming
Posted by Peter Kwan on Nov-06-2016 00:57
Hi Eferov,

In this case, use the second method in my last message. Simply repeat the data extraction code for the two lines, like:

// Assume the data are in:
double[] x0 = ........;
double[] y0 = ........;
double[] x1 = ........;
double[] y1 = ........;


viewer.setFullRange("x", 0, max_x);
viewer.ZoomInWidthLimit = 5.0 / max_x;

// The visible x range
double startX = viewer.getValueAtViewPort("x", viewer.ViewPortLeft);
double endX = viewer.getValueAtViewPort("x", viewer.ViewPortLeft + viewer.ViewPortWidth);

// Get the visible part of x0, y0
int startIndex = (int)Math.Floor(Chart.bSearch(x0, startX));
int endIndex = (int)Math.Ceiling(Chart.bSearch(x0, endX));
int noOfPoints = endIndex - startIndex + 1;
double[] viewPortX0 = (double[])Chart.arraySlice(x0, startIndex, noOfPoints);
double[] viewPortY0 = (double[])Chart.arraySlice(y0, startIndex, noOfPoints);

// Get the visible part of x1, y1
startIndex = (int)Math.Floor(Chart.bSearch(x1, startX));
endIndex = (int)Math.Ceiling(Chart.bSearch(x1, endX));
noOfPoints = endIndex - startIndex + 1;
double[] viewPortX1 = (double[])Chart.arraySlice(x1, startIndex, noOfPoints);
double[] viewPortY1 = (double[])Chart.arraySlice(y1, startIndex, noOfPoints);


// Plot the chart
chartArea = new XYChart(Width - 16, Height - 142);
chartArea.setPlotArea(50, 5, Width - 16 - 100, Height - 142 - 57, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
chartArea.setClipping();
chartArea.yAxis2().syncAxis(chartArea.yAxis());
viewer.syncLinearAxisWithViewPort("x", chartArea.xAxis());

layer[0] = chartArea.addLineLayer2();
layer[0].addDataSet(viewPortY0, colorInteget[0]);
layer[0].setXData(viewPortX0);
layer[0].setLineWidth(wLines[0]);

layer[1] = chartArea.addLineLayer2();
layer[1].addDataSet(viewPortY1, colorInteget[1]);
layer[1].setXData(viewPortX1);
layer[1].setLineWidth(wLines[1]);

viewer.Chart = chartArea;


Hope this can help.

Regards
Peter Kwan

  Re: Autoscale Y-axis when zooming
Posted by Eferov Ivan on Nov-06-2016 04:26
sumptuously!!! thank you very much!!Peter, you are my savior!!!

  Re: Autoscale Y-axis when zooming
Posted by Eferov Ivan on Nov-06-2016 06:37
Attachments:
but if i can more lines, 12 lines the auto-scaled loses its meaning

my code:

public static List<double>[] yAsixDate = new List<double>[12];// Y coordinates
public static List<double>[] ordinalList = new List<double>[12];// X coordinates (1, 2, 3, ..., n, n+1)

public static List<double>[] xVisibleRange = new List<double>[12];// visible range X-axis
public static List<double>[] yVisibleRange = new List<double>[12];// visible range Y-axis

public static int startIndex, endIndex, noOfPoints;

private void LoadData()
{
      ........;
}

private void VisibleRange() //calculate visible range for all lines
{
            double startX = viewer.getValueAtViewPort("x", viewer.ViewPortLeft);
            double endX = viewer.getValueAtViewPort("x", viewer.ViewPortLeft + viewer.ViewPortWidth);
            for (int i = 0; i != 12; i++)
            {
                startIndex = (int)Math.Floor(Chart.bSearch(ordinalList[i].ToArray(), startX));
                endIndex = (int)Math.Ceiling(Chart.bSearch(ordinalList[i].ToArray(), endX));
                noOfPoints = endIndex - startIndex + 1;
                yVisibleRange[i].Clear();
                xVisibleRange[i].Clear();
                yVisibleRange[i].AddRange((double[])Chart.arraySlice(yAsixDate[i].ToArray(), startIndex, noOfPoints));
                xVisibleRange[i].AddRange((double[])Chart.arraySlice(ordinalList[i].ToArray(), startIndex, noOfPoints));
            }
}

private void refreshGraph()
{
            VisibleRange();//calculate visible range for all lines

            chartArea = new XYChart(Width - 16, Height - 142);
            chartArea.setPlotArea(50, 5, Width - 16 - 100, Height - 142 - 57, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
            chartArea.xAxis().setTickDensity(Width - 16 - 100);
            chartArea.setClipping();
            chartArea.yAxis2().syncAxis(chartArea.yAxis());
            chartArea.yAxis().setAutoScale(0.01, 0.01, 0.01);
            chartArea.xAxis().setLabelFormat("");
            chartArea.xAxis().setLabelStyle("Arial", 6);
            viewer.ZoomInWidthLimit = 25.0 / ordinalList[0].Count;
            viewer.ZoomOutWidthLimit = 2115.0 / ordinalList[0].Count;
            chartArea.xAxis().setLabels(xAsixDate[0].ToArray()); //add lables
            viewer.setFullRange("x", ordinalList[0][0], ordinalList[0][ordinalList[0].Count - 1]);
            viewer.syncLinearAxisWithViewPort("x", chartArea.xAxis());

            for (int i = 0; i != 12; i++)
            {
                layer[i] = chartArea.addLineLayer(yVisibleRange[i].ToArray(), colorInteget[i]);
                layer[i].setXData(xVisibleRange[i].ToArray());
                layer[i].setLineWidth(wLines[i]);
            }

            viewer.Chart = chartArea;
}


and
pic1 - full graph (5000 points)
pic2 - part of graph (25 points)
full.png
part.png

  Re: Autoscale Y-axis when zooming
Posted by Peter Kwan on Nov-07-2016 02:30
Hi Eferov,

The issue is due to the data used by the dark green line (the top line in your 2nd zoomed-in chart) or and other similar lines.

To draw a line, at least 2 points are needed. For the dark green line, there are only very few points. The two points for the bottom chart are at around 10.12.2015 15:30 and at around 14.12.2015 12:00, and the values are at 79350 and 74600. Although these two points are way outside the visible range, but they are the nearest points that can draw a line through the visible range. (The visible range code uses Math.Floor and Math.Ceiling to get the index, so it can get the points just outside the visible range.) Because the data point includes 74600, so the axis scale needs to include 74600 too.

For your case, I can think of two methods:

(a) For your data, sometimes the data points can be very far from the visible range, yet they may need to be used because there are only a few points available. You can consider to move the points at the boundary back inside the visible range by interpolation. The code is like:


public static List<double>[] yAsixDate = new List<double>[12];// Y coordinates
public static List<double>[] ordinalList = new List<double>[12];// X coordinates (1, 2, 3, ..., n, n+1)

private void LoadData()
{
      ........;
}

private void refreshGraph()
{
    chartArea = new XYChart(Width - 16, Height - 142);
    chartArea.setPlotArea(50, 5, Width - 16 - 100, Height - 142 - 57, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
    chartArea.xAxis().setTickDensity(Width - 16 - 100);
    chartArea.setClipping();
    chartArea.yAxis2().syncAxis(chartArea.yAxis());
    chartArea.yAxis().setAutoScale(0.01, 0.01, 0.01);
    chartArea.xAxis().setLabelFormat("");
    chartArea.xAxis().setLabelStyle("Arial", 6);
    viewer.ZoomInWidthLimit = 25.0 / ordinalList[0].Count;
    viewer.ZoomOutWidthLimit = 2115.0 / ordinalList[0].Count;
    chartArea.xAxis().setLabels(xAsixDate[0].ToArray()); //add lables
    viewer.setFullRange("x", ordinalList[0][0], ordinalList[0][ordinalList[0].Count - 1]);
    viewer.syncLinearAxisWithViewPort("x", chartArea.xAxis());

    for (int i = 0; i != 12; i++)
    {
        double[] xList = ordinalList[i].ToArray();
        int startIndex = (int)Math.Floor(Chart.bSearch(xList, startX));
        int endIndex = (int)Math.Ceiling(Chart.bSearch(xList, endX));
        int noOfPoints = endIndex - startIndex + 1;
        if (noOfPoints <= 1) continue;

        double[] y = (double[])Chart.arraySlice(yAsixDate[i].ToArray(), startIndex, noOfPoints);
        double[] x = (double[])Chart.arraySlice(xList, startIndex, noOfPoints)

        // Move the points at the border to exactly startX and endX.
        y[0] = (y[0] * (x[1] - startX) + y[1] * (startX - x[0])) / (x[1] - x[0]);
        x[0] = startX;
        y[y.Length - 1] = (y[y.Length - 2] * (x[x.Length - 1] - endX) + y[y.Length - 1] * (endX - x[x.Length - 2])) / (xList[x.Length - 1] - xList[x.Length - 2]);
        x[x.Length - 1] = endX;

        layer[i] = chartArea.addLineLayer(y, colorInteget[i]);
        layer[i].setXData(x);
        layer[i].setLineWidth(wLines[i]);
    }

    viewer.Chart = chartArea;
}


(b) When you obtain the data, inside of using ordinalList[i] for the x-coordinates, you may consider to interpolate the points so that every ordinal x coordinate always have a data point (instead of "skipping" some x-coordinates). Although you need additional code for interpolation, you no longer need all these ordinalList[i]. (You can use the first method I mentioned in my Nov-04-2016 response.) So the resulting code may even be simpler.


Regards
Peter Kwan