I want to use a datetimeaxis for the Y-axis of an Oxyplot heatmapseries (in winforms). The API allows me to add the axis to the PlotModel, but does not do much good since the y-value is mapped off of the integral column index of a matrix of doubles - the y values come out to around Jan. 1900.
How can I use times for the y data on the heat map?
The workaround to make this happen is not too difficult. When setting up the chart, create 2 axes: one for the time, and another for the integral index of the HeatMapSeries input.
DateTimeAxis dateTimeAxis;
HeatMapSeries series;
....
//set up the time axis for y
dateTimeAxis = new DateTimeAxis();
dateTimeAxis.Position = AxisPosition.Left;
dateTimeAxis.Key = "dateTimeAxis";
plotModel.Axes.Add(dateTimeAxis);
//set up a shadow axis for the HeatMapSeries
var linearAxis = new LinearAxis();
linearAxis.Position = AxisPosition.Left;
linearAxis.Key = "linearAxis";
plotModel.Axes.Add(linearAxis);
series.YAxisKey = linearAxis.Key;
Next, the assigned y-axis will have to be hidden - but we can't access the YAxis property until the plot has been updated:
//hide the linear axis
plotModel.Updated += (sender, e) =>
{
series.YAxis.IsAxisVisible = false;
};
Finally, when setting the heat map data, simply create the desired mapping from the matrix index to time. Then adjust the y-scale. For example:
public void UpdateData(double[,] data)
{
series.Data = data;
//adjust date/time axis
int numOfMinutes = data.GetLength(1);
dateTimeAxis.Minimum = DateTimeAxis.ToDouble(DateTime.Now);
dateTimeAxis.Maximum DateTimeAxis.ToDouble(DateTime.Now.AddMinutes(numOfMinutes));
}
I have not yet seen a way to do this directly in OxyPlot
Related
I need to display voltage and current on the same 2D graph (LightningChart.ViewXY). To keep values in a visible proportion, I uses 2 different axes. But my customers and I would like to ensure the zero will always be aligned together between the 2 different axes. Is that possible and if yes, how can I do that?
Thanks Eric for asking this.
The chart and axes have events to aid you with custom handling of cases like this.
As far as remember there is no property to several YAxes at same horizontal position, e.g. Zoomed, Panned event handle and modify Axis’ range accordingly. Below is example how to fix reference value of two axes (same segment) to the middle of graph area.
private void ViewXY_Zoomed(object sender, Arction.Wpf.Charting.Views.ViewXY.ZoomedXYEventArgs e)
{
AxisY axis0 = _chart.ViewXY.YAxes[0];
AxisY axis1 = _chart.ViewXY.YAxes[1];
// get segment top & bottom coordinates [PX]
GraphSegmentInfo gsi = _chart.ViewXY.GetGraphSegmentInfo();
float fTop = gsi.SegmentTops[0];
float fBottom = gsi.SegmentBottoms[0];
int iScreenCoordForRefValue = (int)(fTop + fBottom) / 2;
float fAxis0RefValue = 50, fAxis1RefValue = 0;
double dAxis0ValueAtCoord, dAxis1ValueAtCoord;
axis0.CoordToValue(iScreenCoordForRefValue, out dAxis0ValueAtCoord, false);
axis1.CoordToValue(iScreenCoordForRefValue, out dAxis1ValueAtCoord, false);
// pan Axis' RefValue position at RefScreenCoord
double dNewMinAxis0 = axis0.Minimum - (dAxis0ValueAtCoord - fAxis0RefValue);
double dNewMaxAxis0 = axis0.Maximum - (dAxis0ValueAtCoord - fAxis0RefValue);
double dNewMinAxis1 = axis1.Minimum - (dAxis1ValueAtCoord - fAxis1RefValue);
double dNewMaxAxis1 = axis1.Maximum - (dAxis1ValueAtCoord - fAxis1RefValue);
_chart.BeginUpdate();
axis0.SetRange(dNewMinAxis0, dNewMaxAxis0);
axis1.SetRange(dNewMinAxis1, dNewMaxAxis1);
_chart.EndUpdate();
}
function alignChartHorizontally(chart) {
chart.getDefaultAxisY().setThickness({
min: 60
})
}
if(yourFirstChart.show) alignChartHorizontally(yourFirstChart)
if(yourSecondChart.show) alignChartHorizontally(yourSeconcChart)
// Synchronize all X Axes.
let isAxisXScaleChangeActive = false
const syncAxisXEventHandler = (axis, start, end) => {
if (isAxisXScaleChangeActive) return
isAxisXScaleChangeActive = true
// Find all other X Axes.
const otherAxes = allCharts
.map(chart => chart.getDefaultAxisX())
.filter(axis2 => axis2 !== axis)
// Sync other X Axis intervals.
otherAxes.forEach((axis) => axis
.setInterval(start, end, false, true)
)
isAxisXScaleChangeActive = false
}
//add both (or all) of your charts to this array:
allCharts = [myChart1, myChart2]
//adds event listener and handler to all your charts' scaleChange event emitters
allCharts.forEach(chart => chart.getDefaultAxisX().onScaleChange((start, end) => syncAxisXEventHandler(chart.getDefaultAxisX(), start, end)))
Adapted from: https://github.com/Arction/lcjs-showcase-trading/blob/master/src/app.ts
I have a Winforms chart in which I have temperature readings arriving and displaying every second. I like the way the chart works automatically handling the display of the values, but I want to change one simple thing.
I want to increase the minimum displayed y axis range, so it displays a range of 20. At the moment it only displays around 5. I have tried a few things:
//(when new data arrives...)
//Does not work, I think because by default, Size is always NaN?
if (chart1.ChartAreas[0].AxisY.ScaleView.Size < 20)
{
chart1.ChartAreas[0].AxisY.ScaleView.Size = 20;
}
None of these work either:
chart1.ChartAreas[0].AxisY.ScaleView.SmallScrollMinSize = 20;
chart1.ChartAreas[0].AxisY.ScaleView.SmallScrollSize = 20;
chart1.ChartAreas[0].AxisY.ScaleView.MinSize = 20;
chart1.ChartAreas[0].AxisY.Minimum //doesn't seem to have any effect
chart1.ChartAreas[0].AxisY.Maximum //doesn't seem to have any effect
I'm sure I've missed something simple. I hope I have anyway.
The 'minimum display range' is not something built-in in the MSChart control.
But you can easily fake it:
Add a dummy Series which contains only two points to make sure the display range will not go below the range of their y-values..:
int rangeMin = -10;
int rangeMax = 20;
sDummy = chart.Series.Add("dummy");
sDummy.Color = Color.Transparent;
sDummy.IsVisibleInLegend = false;
sDummy.ChartType = SeriesChartType.Point;
sDummy.Points.AddXY(0, rangeMin + 1);
sDummy.Points.AddXY(0, rangeMax - 1);
Style your y-axis as you like:
Axis ay = chart.ChartAreas[0].AxisY;
ay.MajorGrid.Interval = 5;
And add one or more data Series:
sData = chart.Series.Add("data");
sData.LegendText = "Temperature";
sData.ChartType = SeriesChartType.Line;
Now as you add data points with a larger range of values the y-axis will grow its display range to accommodate them. And if you remove the larger points it will shrink back, but not below the range needed for the dummy series..:
Note that since the Chart automatically adds some slack I reduce the range on both sides by 1; with other Intervals etc other numbers are needed..
The code to remove the larger values, btw:
var toRemove = sData.Points.Cast<DataPoint>()
.Where(x => x.YValues[0] >= rangeMax).ToList();
foreach (var dp in toRemove) sData.Points.Remove(dp);
I am new to C# winform. As title, my problem is how to make the points more compact in fastpoint chart. To make it clear, how to proportionally reduce the distance of the blue points like the red line segments shown in the image; that is, make the X axis more compact:
I have searched and found a lot of information about control the interval of X or Y axis labels but which is not the situation here.
You can control the range of data values for each Axis by setting their Minimum and Maximum values.
The syntax is:
someChart.ChartAreas[CAIndexOrName].AxisX.Minimum = someDoubleValue;
Let's prepare a chart to show one day:
Axis ax = chart.ChartAreas[0].AxisX; // a short reference
ax.IntervalType = DateTimeIntervalType.Hours;
ax.Interval = 1;
To set the properties to some DateTime values you need to convert them to doubles; for this conversion there are two built-in functions: DateTime.ToOADate and, to reverse DateTime.FromOADate
This makes the x-axis display 24 hours:
DateTime dt = DateTime.Today;
ax.Minimum = dt.ToOADate();
ax.Maximum = (dt.AddHours(24)).ToOADate();
ax.LabelStyle.Format = "H:mm"; // optional
You also may want to control the data type of the series values:
Series s = chart.Series[0];
s.XValueType = ChartValueType.DateTime; // or some other type, maybe Time
Note you you can also control both the Interval of the Labels and TickMarks and Gridlines on each axis but also set an Offset to start them a little earlier or later..
I am using C# Excel API to generate some reports. However, Excel leaves gaps between the first axis point and minimum value in data set, and between last axis point and maximum value in data set. My data set is sorted by datetime. How do I force excel to set the lower and upper bounds of axes precisely to minimum and maximum values in my data set so that I don't see any gaps? I can do that in excel by manually setting min/max axis points in excel chart.
But is there a way to have excel do this automatically, or otherwise set min/max points from my C# application using the data set?
Example (marked gaps)
Hope it makes sense.
thanks
[Edited] OK, I did some playing, and I've figured out how to set the vertical and horizontal axis range. This is working with Excel 2010.
Here, I clear all charts on the page and create a new one (_resultsSheet is an Excel.Worksheet):
var resultCharts = (ChartObjects)_resultsSheet.ChartObjects();
foreach (ChartObject ch in resultCharts)
{
ch.Delete();
}
ChartObject resultChart = resultCharts.Add(150, 40, 300, 200);
_resultChartPage = resultChart.Chart;
Now set up the source - I've just used a predefined range of fixed values. You could scan your source to find the actual min and max values:
_resultChartRange = _resultsSheet.get_Range("J5", "K15");
_resultChartPage.SetSourceData(_resultChartRange);
_resultChartPage.ChartType = Excel.XlChartType.xlXYScatterLines;
_resultChartPage.HasLegend = false;
Now for the vertical axis setup:
Axis vertAxis = (Axis)resultChart.Chart.Axes(XlAxisType.xlValue, XlAxisGroup.xlPrimary);
vertAxis.HasMajorGridlines = true; // change this to whatever you wish
vertAxis.HasTitle = true;
vertAxis.AxisTitle.Text = "up the side";
vertAxis.MaximumScaleIsAuto = false;
vertAxis.MaximumScale = 500; // you can pick this based on your input
vertAxis.MinimumScaleIsAuto = false;
vertAxis.MinimumScale = 5;
now for the other axis. Note here I've used fixed times. To convert a time to an axis scale, just use the 24 hour time in decimal, divided by 24. Eg. 9:30pm is 21:30 which is 21.5 hours. Don't forget the (double) cast just in case you use to ints.
Axis horizAxis = resultChart.Chart.Axes(XlAxisType.xlCategory, XlAxisGroup.xlPrimary);
horizAxis.MaximumScaleIsAuto = false;
horizAxis.MaximumScale = (double)21.5 / 24; // 9:30 pm
horizAxis.MinimumScaleIsAuto = false;
horizAxis.MinimumScale = (double)13 / 24; // 1:00 pm
horizAxis.HasTitle = true;
horizAxis.AxisTitle.Text = "across the bottom";
and for those who "like to watch":
_resultsSheet.Activate();
_workBook.Application.Visible = true;
Im working on a polar chart with mschart, and I need to make this "hole" on the middle of the chart. I mean, i need the "0" of the Y axis not to be on the center. I saw this plot somewhere on stackoverflow. It´s exactly what i need, how can I do that?
In order to create a radar plot that look like the provided example
// disable grid a long X
chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
chart1.ChartAreas[0].AxisX.MajorTickMark.Enabled = false;
// set Y axis
chart1.ChartAreas[0].AxisY.Minimum = -20; // creates the 'hole' by setting min Y
chart1.ChartAreas[0].AxisY.MajorGrid.IntervalOffset = 20; // so the major grid does not fill the 'hole'
chart1.ChartAreas[0].AxisY.MajorGrid.Interval = 5;
chart1.ChartAreas[0].AxisY.MajorGrid.LineDashStyle = ChartDashStyle.Dash;