How to setup up iOS charts y-axis with min, max value and a fixed step between the grid lines? - swift

I'm just in the learning phase of using ios-charts. I like to change the x-axis grid to fixed values.
My plotted y-values are just int numbers like 1, 2, 3,..., 10. Nevertheless, the left y-axis shows values like 6.3, 9.1, etc., depending on my zoom level.
The second question is, how to set up the x-axis in order to show the labels 1,5,10,15,....40?
Is there any way to influence the step size like e.g. in Excel?
// zoom y-axis to min/max value
lineChart.leftAxis.customAxisMin = max(0.0, lineChart.data!.yMin - 1.0)
lineChart.leftAxis.customAxisMax = min(10.0, lineChart.data!.yMax + 1.0)
lineChart.leftAxis.startAtZeroEnabled = false
Chart (min = 6.0 and max = 10.0): The grid start at 6.3 instead of 6.0.
Chart (min = 7.0 and max = 10.0): The grid starts as expected with 7.0.
What's going wrong here?

I solved the issue just by setting the correct labelCount.
// zoom y-axis to min/max value
lineChart.leftAxis.customAxisMin = max(0.0, lineChart.data!.yMin - 1.0)
lineChart.leftAxis.customAxisMax = min(10.0, lineChart.data!.yMax + 1.0)
lineChart.leftAxis.labelCount = Int(lineChart.leftAxis.customAxisMax lineChart.leftAxis.customAxisMin)
lineChart.leftAxis.startAtZeroEnabled = false

Swift 4.2 and above:
startAtZeroEnabled - This property is deprecated - Use axisMinimum instead.
open var axisMinValue: Double
{
get { return axisMinimum }
set { axisMinimum = newValue }
}
lineChartView.leftAxis.axisMinimum = 0
lineChartView.leftAxis.axisMaximum = 10.0

Related

how to make horizontal padding in iOS danielgindi charts

I'm using the ios-charts library and I would like to add some horizontal padding to my line charts so that the line does not start immediately at the border of the graph.
This is my current chart:
but I would like the blue line to have some padding as shown below. The rest should remain as it is. The reference gray lines should still take the entire width as they currently do.
I found it. This "padding" is actually ruled by the chart.xAxis.axisMinimum and chart.xAxis.axisMaximum. Those values are automatically set to the data min x and max x.
So if I want a left padding I just have to set a chart.xAxis.axisMinimum
In my case, I want around 10% of the x values to be padded, so I calculate it as
// dates is an array of Date representing my x values
if let maxX = dates
.map(\.timeIntervalSince1970)
.max(),
let minX = dates
.map(\.timeIntervalSince1970)
.min() {
let spanX = maxX - minX
let padding = spanX * 0.1
let axisMinimum = minX - padding
// set the left padding
chart.xAxis.axisMinimum = axisMinimum
}

How to disable EChart's auto-adjust which happens on disabling one of the series?

I have an echart with multiple serieses. If user clicks on one of the legends and enable/disable a series, chart auto adusts (zoom in or zoom out) x and y axis to show optimal axis values.
How do I disable this behaviour so that chart do not auto-adjust axis when enabling/disabling serieses? I would like both axis to stay at initial values all the time without any change.
My Reserch: I know we can set min and max values on X and Y axis to keep X and Y axis fixed. But in this case, I have to calculate min/max manually across serieses. Any built-in flag or option will be much simpler to use.
Here is my solution at last:
I ended up setting min and max for each axis. I had to go through every data series, figure out max and min possible values among all serieses and set it on axis. This avoides above behaviour. (Some of the graphs may have multiple axes, so take that in account).
Here is generic function that can be used on chartOptions object to add min and max values based on data. Data is assumed to be part of each series. It only works on first axis incase of multiple axis.
static fixBothAxis(chartOptions) {
console.log(chartOptions);
const series = chartOptions.series;
let maxXAxis = -Infinity,
minXAxis = Infinity,
maxYAxis = -Infinity,
minYAxis = Infinity;
series.forEach((seriesItem: any) => {
// dont consider serieses that are related to secondary axis
if (seriesItem && seriesItem.data) {
const xSeries = seriesItem.data.map(item => item[0]);
const ySeries = seriesItem.data.map(item => item[1]);
maxXAxis = Math.ceil(Math.max(maxXAxis, ...xSeries));
minXAxis = Math.floor(Math.min(minXAxis, ...xSeries));
if(seriesItem.yAxisIndex !== 1){
maxYAxis = Math.ceil(Math.max(maxYAxis, ...ySeries));
minYAxis = Math.floor(Math.min(minYAxis, ...ySeries));
}
}
});
/* Make both max and min values "stick to nearest sensible value" */
const xAxis = Array.isArray(chartOptions.xAxis)
? chartOptions.xAxis[0]
: chartOptions.xAxis;
const yAxis = Array.isArray(chartOptions.yAxis)
? chartOptions.yAxis[0]
: chartOptions.yAxis;
if (xAxis && !isNaN(minXAxis) && !isNaN(maxXAxis)) {
xAxis.min = minXAxis;
xAxis.max = maxXAxis;
}
if (yAxis && !isNaN(minYAxis) && !isNaN(maxYAxis)) {
yAxis.min = minYAxis;
yAxis.max = maxYAxis;
}
return chartOptions;
}

Swift - How to set x-Axis to show hourly intervals using iOS Charts

I have the following data:
2019-08-14T13:00:00.000Z, 0.0015378000
2019-08-14T12:30:00.000Z, 0.0015172000
2019-08-14T12:00:00.000Z, 0.0014922000
2019-08-14T11:30:00.000Z, 0.0014706000
2019-08-14T11:00:00.000Z, 0.0014229000
2019-08-14T10:30:00.000Z, 0.0000989000
2019-08-14T10:00:00.000Z, 0.0000736000
2019-08-14T09:30:00.000Z, 0.0000508000
2019-08-14T09:00:00.000Z, 0.0000214000
2019-08-13T17:30:00.000Z, 0.0012805000
And have plotted this data into a Line Chart using the Charts library as shown below:
The data appears correct in the graph, however I've noticed that the x Axis is showing a scale that is not hourly, which I would ideally like to show.
The following code was applied to generate the above graph in an attempt to set an hourly scale on the x-Axis:
xAxis.drawAxisLineEnabled = true
xAxis.drawGridLinesEnabled = true
xAxis.granularityEnabled = true
xAxis.granularity = 1.0 / 24.0
When applying a granularity of 1.0 / 2.4 however, I was able to show a 10 hour interval on the graph as shown below:
It seems that the granularity does not line up to the hourly rate for the given graph, which may be associated with the fact that it contains a minimum interval between axis-values (In my case the maximum duration can be up to 2 days).
Is there a way to lock/snap the x-Axis grid to an hourly scale?
While I believe this is a workaround, I was able to snap the grid to an hourly basis for up to 5 x-axis labels using the following code (where diff is the date range in days presented on the graph:
// Determine a reasonable scale that complies to an hourly grid (Assume no more than 5 labels on grid)
let hourRange = diff * 24.0
let interval = Int(hourRange.rounded(FloatingPointRoundingRule.up)) / 5
if (interval <= 0)
{
xAxis.granularity = 1.0 / 24.0
} else {
xAxis.granularity = 1.0 / 24.0 * Double(interval)
}

XAxis entry count is greater than it should be in iOS Charts 3.0.1 in Swift 3

I have two BarChartDataSets. One of them is always size 3 and the other is either 2 or 3. I tested out the code in version 3.0.0 and everything was working fine. When 3.0.1 came out, it broke my chart. I have the correct number of bars always, but I have six labels instead of 5 when the second dataset is only size 2. It has nothing to do with the stringForValue Delegate function. I set the X values using int's that are associated linearly with the Bar I want represented at that index, so each bar is equally spaced when working properly, but none of them are equally spaced when I have 6 labels and 5 bars.
The one on the left shows the issue and the one on the right shows what it looks like when my BarChartDataSet is size 3. It is duplicating whatever the last value on the chart is and adding it as a 6th label on the left. In 3.0.0 the one on the left would have only had 5 labels.
I dug into their code and where they create the labels in XAxisRendererHorizontalBarChart.swift right before they call drawLabel() I callprint("xAxis entries: \(xAxis.entries.count)")which prints xAxis entries: 6to the console even though right before I call let chartData = BarChartData(dataSets: [chartDataSet1, chartDataSet2])I callprint("dataEntries1 count: \(dataEntries1.count),
dataEntries2 count: (dataEntries2.count)")which prints dataEntries1 count: 3, dataEntries2 count: 2
First, xAxis.entries and dataSet.entries are totally different.
xAxis.entries are the values that being displayed on the axis as labels, while dataSet.entries is the value displayed for the data, e.g. the dot value in line chart, or the bar value for bar chart.
for x axis, it calculate the label count by your data entries min/max value. In Chart 3.0, x axis behaves like y axis, so it calculates x axis entries like y axis, please take a look at computeAxisValues() for details.
So xAxis.entries and dataSet.entries don't have to be equal size.
If you want to set the x axis label count same as your bars count, you can call: setLabelCount(5(6), true):
open func setLabelCount(_ count: Int, force: Bool)
{
self.labelCount = count
forceLabelsEnabled = force
}
Note, don't call the labelCount setter as it's different:
/// the number of label entries the axis should have
/// max = 25,
/// min = 2,
/// default = 6,
/// be aware that this number is not fixed and can only be approximated
open var labelCount: Int
{
get
{
return _labelCount
}
set
{
_labelCount = newValue
if _labelCount > 25
{
_labelCount = 25
}
if _labelCount < 2
{
_labelCount = 2
}
forceLabelsEnabled = false
}
}

Changing the axis range dynamically doesnt refresh the lineplot but only the axis labels

I am trying to change the axes Min and Max properties to show only the plot points that lie in a particular range.
say If I have lineplot whose X values are ranging from 0 to 100, I want to display only the values that are greater than 50.However I noticed that the lineplot is drawn using all the position points.Only the x axis ticks got renamed such that they start from 50 and end at 100.
The following is the code I am using
var axes = m_plotCube.First<ILAxisCollection>();
if (axes != null)
{
ILAxis xAxis = axes.Where<ILAxis>(item => item.Label.Text == "X Axis").First();
xAxis.Min = 50;
xAxis.Max = 100;
xAxis.Configure();
}
Am I missing something ?
Use ILPlotCube.Limits instead:
var pc = ilPanel1.Scene.First<ILPlotCube>();
pc.Limits.Set(
new Vector3(50, pc.Limits.YMin, pc.Limits.ZMin),
new Vector3(100, pc.Limits.YMax, pc.Limits.ZMax));
BTW: the axes are accessed much easier: plotcube.Axes.XAxis ...