I have a few questions about MPAndroidCharts - android

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.

Related

Order of operations in Android/Kotlin

Could anyone explain to me why this code works:
if (questionList[currentIndex].answer == inputAnswer) {
correctAnswers += 1*100/questionList.size
percentage.text = "Правильно: $correctAnswers%"
and this one doesn't:
if (questionList[currentIndex].answer == inputAnswer) {
correctAnswers += 1/questionList.size*100
percentage.text = "Правильно: $correctAnswers%"
When I click a button, set up with the first bit of code, everything works fine and my textView gets updated, but when I change the order of operations, nothing happens (textView.text doesn't change it's value).
Am I missing something?
Thank you in advance!
When you do math with integers, fraction components are not preserved. Suppose the size of the question list is 10.
In your first block of code, you have
1 * 100 / 10
Operations are done from left to right, so after the first multiplication, you have
100 / 10
and that resolves to 10.
Now with your second block of code you have
1 / 10 * 100
The first division with floating point numbers would be 0.1, but with integers, since the fraction is not preserved, it evaluates to 0.
0 * 100
which resolves to 0. So it will always result in zero if the dividend is smaller than the divisor.
If you really want to present fractional numbers, you should use floating point numbers, and if you want the result to be an Int, use roundToInt() on the result. If you just use toInt(), it will just drop the fraction rather than rounding to the nearest integer.

android calculate rise over run from angle

We are trying to calculate the rise over the run of an object. We know the angle of a right triangle and we know the run. When we use the scientific Microsoft calculator we get the tangent of the angle and multiply by the run to get the rise.
angle = 7.5 tangent = 0.1316 in degrees multiply by run and ans 1.579 From this we now know how to set the X and Y coordinates of the Imageview object
We have seen all types of answers about how to do this with Java for Android none of which give the results based on the use of the MS calculator. We tried this
float T = (float) toRadians(tan(7.5));
Not even close we also tried toDegrees
So the we have two questions
How to calculate the rise knowing the run and the angle?
Is there a better way to set the X and Y value of the object so it will follow a path on a desired angle?
tan(angle) = rise/run. what you need is to rearrange this.
rise = tan(angle) * run
In PHP this is accomplished like this:
$rise = tan(deg2rad($degrees)) * $distance;
It took me a while to figure out that PHP's tan() function expects the angle to be in radians, so I had to convert to that first.
I know that's not an Android-specific response, but I'm leaving it here anyway in case it saves someone else some time.

MPAndroidChart show specified range of XValues even when there is no corresponding YValue?

The Problem I am having is that I have charts which should always show a fixed range of XValues let's say all 24 hours in a day, but the Chart is only showing XValues up to and starting from the point of the first and last YValue, that is if I have two Y events, let's say 2am and 3pm, instead of showing the whole 24 hours, it will start at 2am and end at 3pm.
I think this has something to do with the new way to handle XValues via IAxisValueFormatter, since this used to work fine when we could add both X and Y values to a ChartData.
One hack to workaround this I found is to add empty YValues to first and last hour in my case 0 values, but this brings another issue that no matter what I set the minimum in LeftAxis it starts showing negative label values (starts in -15 instead of 0).
Is there an official way to do this? I have researched quite a bit about this and I am thinking on going back to a previous version of the library where I could just use the old way.
Edit: Just to clarify the problem doesn't have anything to do with the labels, but with the start and end of the Chart in X, it is discarding the range of X which doesn't have any YValues which I understand makes sense but it makes the results look ugly in certain cases.
You can use the setLabelCount() method to force the axis to draw labels according to the count you specify
According to the documentation:
setLabelCount(int count, boolean force)
#param count: the number of y-axis labels that should be displayed
#param force if enabled, the set label count will be forced, meaning that the exact specified count of labels will be drawn and evenly distributed alongside the axis - this might cause labels to have uneven values
And the setLabelCount method is defined below
public void setLabelCount(int count) {
if (count > 25)
count = 25;
if (count < 2)
count = 2;
mLabelCount = count;
mForceLabels = false;
}
You can use setLabelCount() with setAxisMaximum() and setAxisMinimum()
For example:
axis.setLabelCount(24, true);
axis.setAxisMaximum(24f);
axis.setAxisMinimum(0f);
This will draw values from 0 - 24 regardless of Y-Axis values

Math.sin() does not give accurate results [I'm using Math.toRadians to pass the right values]

I'm actually using Math.sin() in my android app to calculate a sinus of a given angle (using Math.toRadians(angle_in_degrees)). For exemple when I want to get the Math.cos(90) which is 0, the result is 6.123233... E-17. Thanks you.
For floating point numbers, the system can often only approximate their values. For instance, the system would return something like 0.333333 for the expression (1.0 / 3). The number of 3s after the decimal point will be different depending on whether you're a floats or doubles, but it will still be limited to some finite length.
If you're just displaying the value, then you can limit the number of digits using something like String.format("%0.2f", value) or by rounding it using one of the rounding functions such as Math.round().
The tricky part comes when you need to compare the value to something. You can't just use if (value == some_constant) or even if (value == some_variable). At minimum, you usually have to use something like if (Math.abs(value - some_constant) < 0.001). The actual value of the '0.001' depends on the needs of your particular application and is customarily defined as a named constant.
For more complicated needs, you can implement the algorithm in the Floating-Point Guide.
You're getting back an approximation from Math.cos(Math.toRadians(90)) which is
6.123233... E-17 == 0.00000000000000006123233... which is basically 0
The following link should help clear things up as far as the precision of doubles/floats in programming.
http://www.java67.com/2015/09/float-and-double-value-comparison-in-java-use-relational.html

Chart disappears when zooming

When I zoom my Chart by X axle, graph disappears.Why this can happen?
Lib version - MPAndroidChart:v3.0.0-beta1
enter image description here
I used example LineChartTime. And edited long hourMillis = 36000L; in setData() function, and got problem with graph view.
UPDATED!!!
The problem become when time sampling smaller then 360000 millis or 6 minutes. When 1 hour like in example "LineChartTime" everything is ok. But if I reduce x value from 1451606400000L to 0 and make time sampling equal 1 millis everything is ok. So I think that main problem in float type of x value, that process incorrect big LONG values.
So, I found temporary solution.
When I get current time, I reduce it on 1451606400000L (2016 01.01 00:00) and draw not so big values, but also change Formatter string
xAxis.setValueFormatter(new AxisValueFormatter() {
private FormattedStringCache.Generic<Long, Date> mFormattedStringCache = new FormattedStringCache.Generic<>(new SimpleDateFormat("yyyy dd MMM HH:mm"));
#Override
public String getFormattedValue(float value, AxisBase axis) {
Long v = (long) value + 1451606400000L;
return mFormattedStringCache.getFormattedValue(new Date(v), v);
}
If someone have another solution please let me know.
When you zoom in, the library does not render values that are outside of the visible area. The algorithms here use the float type (probably for many good reasons), but this causes problems with "large" numbers.
float is internally a 32-bit IEEE 754, which means that it can represent only about 7 significant digits. Timestamps in milliseconds will get their last digits truncated (at least for the purposes of determining which lines to draw), so some of your lines wil disappear.
Option 1:
Use timestamps within range of float precision, or at least closer to it.
If you are plotting current data, substract a stored value of System.currentTimeMillis() from your timestamps and add the same value back when formatting, as you did in your own solution. If you can live with 1-second precision, divide your timestamps by 60000 and adjust the formatter accordingly.
Option 2: Patch the library to draw lines outside of the visible area.
A quick fix that will work if you don't have a large dataset is to render everything. For line charts, change the contents of the following 2 methods in BarLineChartBase.java near line 1280 like this:
public float getLowestVisibleX() {
return mXAxis.mAxisMinimum;
}
public float getHighestVisibleX() {
return mXAxis.mAxisMaximum;
}
This is probably wildly inefficient, which may or may not be an issue depending on your dataset size.

Categories

Resources