MPAndroidChart seems to force you to have data points in sorted for x.
I have a plot that returns to x = zero when the y values are negative the data set would look like this:
val dataVals = ArrayList<Entry>()
dataVals.add(Entry(0f, 0f))
dataVals.add(Entry(1f, 5f))
dataVals.add(Entry(2f, 5f))
dataVals.add(Entry(3f, 2f))
dataVals.add(Entry(4f, 0f))
dataVals.add(Entry(3f, -3f))
dataVals.add(Entry(2f, -3f))
dataVals.add(Entry(1f, -3f))
dataVals.add(Entry(0f, 0f))
val dataSet = LineDataSet(dataVals, "datatest")
Getting an error code java.lang.NegativeArraySizeException: which is fixed when the x values are sorted.
The desired plot would look similar too:
Does anyone know of a way this is possible in MPAndroidChart? If not if there are alternatives I am happy to change charting libraries?
I have started using HelloCharts which I believe is no longer updated but seems to do the trick for this type of data.
It is not as good for plotting livedata though.
Related
I'm developing fitness application where I would like to display chart of user running activities over months, so something like this:
Graph how I want it
I'm using MPAndroidChart for it, but I'm getting some weird values on the x axis (the y axis is alright).
I have a map (monthMileage in the code sample), where the key is the month and the year in string, so for example Jul 2020 as "072020" and the value is the total distance of km which user ran that month.
profileViewModel.monthOverview.observe(viewLifecycleOwner, Observer { monthMileage ->
val entries = ArrayList<BarEntry>()
for ((k, v) in monthMileage) {
entries.add(BarEntry(k, v.toFloat()))
}
val dataSet = BarDataSet(entries, "Monthly mileage")
val barData = BarData(dataSet)
activity_graph.xAxis.labelCount = entries.size
activity_graph.xAxis.valueFormatter = ActivityGraphFormatter()
activity_graph.axisRight.axisMinimum = 0.toFloat()
activity_graph.axisLeft.axisMinimum = 0.toFloat()
activity_graph.data = barData
activity_graph.invalidate()
})
This is how I'm trying to set data to the Bar Chart. The x value is the month-year string to Float and y is the total distance. I use custom value formatter, which looks weird, but should be working with the right values (and the values are still wrong even when I don't use the formatter so there's error probably somewhere else). In the whole process the values in the entries, data set and data are still the values I'm expecting, but when it's set to the graph it's just wrong.
For example for 72020 and 62020 I'm getting 65000 and 70000.
There's probably some correlation I don't see so I welcome all advicess or if anybody knows about some nicer chart library for android mobile app, I've been searching, but all the time everybody is just praising MPAndroidChart.
The data in my graphs use milliseconds and look approximately like this:
[1534928499109,52],[1534928522758,49],[1534928546408,51],[1534928570036,47],[1534928593671,54],
but with many thousand data points. For some reason the points stack on top of each other like in the picture I've attached. How can I fix this? This also happens with HelloCharts.
Points stacking on top of each other.
I prefer MPAndroidChart but HelloCharts got this awesome view, previewChart. Here's an example: https://github.com/lecho/hellocharts-android. Does MPAndroidCharts support previewCharts or something similar?
I am currently using a valueformatter to change milliseconds to date. Can I somehow get the difference between the smallest and biggest currently visible value and this way dynamically change the valueformatter to format more specific time?
Thanks in advance for any answers!
Only answering 3.:
chart.visibleXRange gives you the difference between the lowest and the highest visible x value. Similarly, chart.visibleYRange gives the values for the Y axis.
Be aware that (if you have defined a dragOffsetX) when scrolled all the way to the left or the right border of the chart, then the lowest or the highest value, respectively, is the lowest/highest value actually occurring in your data, but not the x value corresponding to the left/right border of the chart. To get that value, you can use chart.getValuesByTouchPoint(...) and chart.contentRect.
I use the following function to determine the exact interval between labels which helps me decide in what granularity I want to format the labels (in my case seconds vs milliseconds). The main part which transforms the rawInterval into interval is taken from com.github.mikephil.charting.renderer.AxisRenderer.computeAxisValue and translated to Kotlin:
fun calculateIntervalBetweenLabels(): Double {
val range = chart.getValuesByTouchPoint(chart.contentRect.right, 0f, YAxis.AxisDependency.LEFT).x - chart.getValuesByTouchPoint(chart.contentRect.left, 0f, YAxis.AxisDependency.LEFT).x
val rawInterval = range / chart.xAxis.labelCount
var interval = Utils.roundToNextSignificant(rawInterval).toDouble()
val intervalMagnitude = Utils.roundToNextSignificant(10.0.pow(log10(interval).toInt())).toDouble()
val intervalSigDigit = (interval / intervalMagnitude).toInt()
if (intervalSigDigit > 5) {
interval = floor(10 * intervalMagnitude)
}
return interval
}
In simpler cases without dragOffsetX, the first line could be replaced by val range = chart.visibleXRange.
In my ValueFormatter I do this:
override fun getFormattedValue(value: Float): String {
return when {
calculateIntervalBetweenLabels().roundToLong() >= 1000 -> formatValueInSeconds(value)
else -> formatValueInMilliseconds(value)
}
}
I've figured a few things out. In case anyone comes across this in the future and wonders the same thing.
MPAndroidCharts class Entry uses Float. Max value for Float is 2^23 and everything above that is rounded, the points get the same x-value. I fix this by subtracting 1.5 billion from every value and dividing by 100. Then in the ValueFormatter, I undo this.
I don't know, yet.
My solution was to calculate the difference between every value that gets formatted in the ValueFormatter. If the difference is less than zero, the formatter has looped around and that value is the displayed interval. Another solution suggested using chart.visibleXRange, which is much simpler.
I have multiple DataSet in line chart and i want to highlight all the Y points with custom overlay image. do we have any function for it ?
If not then
is there any function which can return corresponding pixel values for an entry object ?
Any direction would be appreciated.
Thanks you for reading.
The method is given by getPixelForValues(x,y) in Transform class
val viewPortHandler = ViewPortHandler()
viewPortHandler.setChartDimens(canvas.width.toFloat(),canvas.height.toFloat())
val transformer = Transformer(viewPortHandler)
var mpPointD = transformer.getPixelForValues(x, y)
But the transformation is not working for me with the points I pass but I know that the lib uses it with similar data that I set
I try to find the extrapolated Y value of a point on a androidplot curve.
For example, I have three points in a array: A (0; 0) B (15; 5) C (30; 0).
I displayed in androidplot with smoothing using SplineLineAndPointFormatter.
How can we do to find the Y value of the point N(10; ?)
Look at the example image
Thanks for any help in advance.
First off, you should not use that interpolation method because it uses cubic beziers and will plot false data. I posted a full answer in the linked question back in 2014.
Instead you should use the CatmullRomInterpolator. Once you've switched over to that, you can retrieve the interpolated series by invoking CatmullRomInterpolator.interpolate(XYSeries, Params). You can then retrieve N directly from the interpolated series.
I'm using achartengine in my Android Application, it works well but i have a question.
Is it possible to reverse plot?
I mean in my graph, new values which are added to my XY series with the function add, are added at end of the series, and then the graph is redrawn, so new values appears at right of the graph.
But i want to add my new value at index 0, in order to show on the graph only the last X values.
Sorry if it's not very clear
Thanks
There is no support for inserting data to XYSeries at a given index. However you could get to this behavior by clearing the current data and adding it back, with the new value included. I know this would not be a very optimal way, but it helps you get to your needed behavior.
Edit: I added an add(index, x, y) method in XYSeries. You can download a version including this feature here.
So, i've found a trick:
Dataset is my XYseries and mRenderer my multipleSeriesRenderer
I've changed alignment of the Y axis:
mRenderer.setYAxisAlign(Align.RIGHT, 0);
I've erased automatic labels:
mRenderer.setXLabels(0);
The 0 on the axe X is now a custom label, so every time a new value is added to the graph, I have to change is position:
mRenderer.setXAxisMax(dataset.getItemCount() - 1); mRenderer.addXTextLabel(dataset.getItemCount() - 1, "0");
And to finish, i change the X axis min everytime a new value is added, in order to get a fix scale:
mRenderer.setXAxisMin(dataset.getItemCount() - scale - 1);