How to handle time series in MPAndroidChart? - android

I want to add the following sort of data (can be any number of such pairs under 1000) to the newly introduced timeseries chart in MPAndroidChart library
Value : 50.0 at 1472112259
Value : 49.0 at 1472112294
Value : 50.0 at 1472112329
Value : 50.0 at 1472112360
Value : 50.0 at 1472112392
The following data will be fetched from the array.
Right now, I guess there is some mess up with the timestamps.
Here is the complete code: https://gist.github.com/utkarshns/e1723dcc57022fcd392bc3b127b6c898
UNIX timestamps will be parsed to required time format after I can successfully add values to the graph.
Currently, the problem I face is that the timestamps probably get clipped and values are overwritten which leads to a pretty messed up graph with really weird x-axis values.
Update:
Screenshots:
http://imgur.com/a/dGfmz

The problem is that Float values can't hold very big numbers and still be accurate, so you need a separate List with these timestamp values. BigDecimal should be ok for this purpose. Your distances must be in accordance to the time gaps between your events. Just iterate from the start date to end date keeping count of how many timestamps you have and add Entry with count from the timestamps you wish your value to be.
Long myValues[] = {1472112259L, 1472112294L, 1472112329L, 1472112360L, 1472112392L};// your values
ArrayList<Entry> values = new ArrayList<>();// Entry List
Long start = 1472112259L;//start
Long end = 1472112392L;//end
List<BigDecimal> mList = new ArrayList<>(); //Decimal list which holds timestamps
int count = 0;
for (Long i = start; i <= end; i++) {
mList.add(new BigDecimal(i));
if (myValues.equals(i)) {
values.add(new Entry(count, 50));
}
count++;//always increment
}
And your ValueFormatter should look like this:
AxisValueFormatter() {
private FormattedStringCache.Generic<Long, Date> mFormattedStringCache = new FormattedStringCache.Generic<>(new SimpleDateFormat("HH:mm:ss"));
#Override
public String getFormattedValue ( float value, AxisBase axis){
return mFormattedStringCache.getFormattedValue(new Date(mList.get((int)value).longValueExact()*1000), value);
}
#Override
public int getDecimalDigits () {
return 0;
}
}
If you have any question or something is unclear I'll be happy to help.

Related

Charts - using Mp Chart

I need to draw graph like the image i have uploaded , i am using MP chart library and develop a graph but want to customize it according to my requirement but not able to find solution for my requirements my basic requirement is for x axis i want to show custom values at axis like 5-11 12-18 but i am passing value to x axis like this
private ArrayList<String> setXAxisValues() {
ArrayList<String> xVals = new ArrayList<String>();
xVals.add("10");
xVals.add("20");
xVals.add("30");
xVals.add("30.5");
xVals.add("40");
return xVals;
}
So it is showing x values like this 10 20 30 so on so i want my graph to be built upon using these x value which is happening right now but want to show custom value at bottom like 5-11 etc and this value is dynamic coming from Api response so please help me about this , Waiting for positive and early response Thanks in Advance
To format the x-axis values, you should use the setValueFormatter method which takes a callback interface were you have to implement the getFormattedValue method which is called before drawing the x-axis value.
xAxis.setValueFormatter((value, axis) -> {
String res = "";
try {
// get current position of x-axis
int currentPosition = (int) value;
// check if position between array bounds
if (currentPosition > -1 && currentPosition < maxSize) {
// get value from formatted values array
res = xVals.get(currentPosition);
}
} catch (Exception e) {
// handle exception
}
return res;
});

How to set two decimal point into Bar Entry in Mp android Bar Chart in Android

How to set J SON data in bar entry in two decimal point?
You can use The ValueFormatter interface
The IValueFormatter interface can be used to create custom-made formatter classes that allow to format values within the chart (from DataSets) in a specific way before drawing them.
For using the IValueFormatter, simply create a new class and let it implement the interface and return whatever you want to be displayed from the getFormattedValue(...) method.
Creating a Formatter
public class MyValueFormatter implements IValueFormatter {
private DecimalFormat mFormat;
public MyValueFormatter() {
mFormat = new DecimalFormat("###,###,##0.0"); // use one decimal
}
#Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
// write your logic here
return mFormat.format(value) + " $"; // e.g. append a dollar-sign
}
}
Then, set your formatter to the ChartData or DataSet object:
// usage on whole data object
lineData.setValueFormatter(new MyValueFormatter());
// usage on individual dataset object
lineDataSet.setValueFormatter(new MyValueFormatter());
Predefined Formatters
LargeValueFormatter: Can be used for formatting large values >
"1.000". It will turn values like "1.000" into "1k", "1.000.000" will
be "1m" (million), "1.000.000.000" will be "1b" (billion) and values
like one trillion will be e.g. "1t".
PercentFormatter: Used for
displaying a "%" sign after each value with 1 decimal digit.
Especially useful for the PieChart. 50 -> 50.0 %
StackedValueFormatter: A formatter specifically designed to be used
with stacked BarChart. It allows to specify whether all stack values
should be drawn or just the top value.

Passing current time in milliseconds to MPChart breaks it

I have tried figuring this out, but it doesn't add up. The data doesn't appear as it should.
First I generate dummy data. This is done async because I need time between the calls to System.currentTimeMillis to get some spacing between them. (Look aside the crappy code here, this is just debug data that will not be in the release. Using Thread.sleep on the main thread is a bad idea considering ANR's)
public class AsyncGeneration extends AsyncTask<String, String, String>{
public AsyncGeneration() {
super();
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
#Override
protected void onCancelled(String s) {
super.onCancelled(s);
}
#Override
protected void onCancelled() {
super.onCancelled();
}
#Override
protected String doInBackground(String... strings) {
while(root == null) {
try {
Thread.sleep(200);
}catch(InterruptedException e){}
}
List<Entry> rdata = new ArrayList<>();
for(int i = 1; i < 30; i++){
try{
Thread.sleep(200);
}catch(Exception e){
//IGNORE
}
float time = System.currentTimeMillis();
Log.e("GChart", "Timex: " + time);
Session s = new Session(r.nextInt(5000), time);//Replace time with the index in the for-loop and it works for some reason
rdata.add(new Entry(s.getXValue(), s.getYValue()));
Log.e("GChart", "Timey: " + s.getXValue());
}
final List<Entry> entries = rdata;
OverviewFragment.this.getActivity().runOnUiThread(() ->{
LineDataSet data = new LineDataSet(entries, "Distance");
data.setCircleColor(Color.parseColor("#FF0000"));
LineData lineData = new LineData(data);
tab1chart.setData(lineData);
tab1chart.invalidate(); // refresh
tab1chart.getXAxis().setValueFormatter(new DateFormatter());
tab1chart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
tab1chart.getXAxis().setTextSize(10f);
tab1chart.getXAxis().setTextColor(Color.RED);
tab1chart.getXAxis().setDrawAxisLine(true);
tab1chart.getXAxis().setDrawGridLines(true);
tab1chart.getAxisLeft().setValueFormatter(new DistanceValueFormatter());
tab1chart.getAxisLeft().setDrawGridLines(true);
Log.v("Chart", "Chart data loaded and invalidated");//This prints
});
return null;
}
}
So far everything looks fine. The data gets put into the chart, no exceptions, no crashes.
When the chart renders, a single data point shows up at the far-left of the chart. I generate 30 data points, one shows up.
That's issue #1: Only one data point shows up.
Issue #2 is slightly harder. The entire X axis at the bottom disappears. X axis is gone and when zooming, the Y axis, and its text also disappears. This is fairly hard to explain, so here is a screenshot:
It is worth mentioning the fact that if I pass i in the for-loop as the time, it shows up just as expected: all the axises are in place, zoom doesn't break anything.
(Float values can take the same values as Longs except they have decimals in addition.)
And in addition, I format the data:
public class DateFormatter implements IAxisValueFormatter {
SimpleDateFormat formatter;
public DateFormatter(){
formatter = new SimpleDateFormat("dd/MM/yy, HH:mm");
}
#Override
public String getFormattedValue(float value, AxisBase axis) {
//This method is never called. Nothing is returned
if(axis instanceof XAxis) {
String formatted = formatter.format(new Date((long) value));
Log.d("Formatter", "Formatted \"" + value + "\" to \"" + formatted + "\".");
return formatted;
}
return "Not supported";
}
}
Removing the formatter doesn't change anything, the axis is still gone. Zoom still breaks the Y axis.
So my question is: How do I fix this? I don't get why the chart doesn't work when I pass the current time in milliseconds (and I checked the values with debug output, floats can handle it). I got some debug output earlier that eventually stopped coming at all that showed the value passed to the formatter was values < 2000. I can't reproduce this any more though
The chart itself doesn't appear to be broken but from touch events it looks like every single point is pushed into the same X coordinate but only one point renders. When I touch the chart, the orange-ish lines show up indicating the position of a data point. It pops up on points that aren't visible.
When I pass the for-loop index as the X value, it works as expected, the formatter works fine (looking aside the fact that it shows the date as in 1970, but it is counted as 1 millisecond into the epoch, so that is to expect)
I looked at this as well on formatting the date. When I then try passing the milliseconds since the epoch, the chart stops working.
I'm using MPChart v 3.0.2, compiling against Android 26, using Java 8, and running Android Studio 3.0 beta 2
As for the layout, it is just a LinearLayout with a LineChart in it.
This should work, but it breaks when I pass it the current time in milliseconds for some reason. Passing any other data (as long as the numbers aren't that big) works fine.
The two images are not how the chart is supposed to look. The X axis is supposed to be visible, but for some reason it isn't along with a large amount of the data.
This is known issue of Android MPChart (have a read this thread). Time series chart supported in MPChart - You can set time (in millis) as X values for Hourly Charts.
But too many consecutive data (points) won't correctly plot in Line Charts. Because Entry object will accept only float values due to some performance constraints.
So, keep the first value as the reference and subtract each up coming value from reference value & divide by some constants (say 1000) .So , you X value set will be like 10,20,30....& so on.
Do the reverse logic in your Axis Value formatter to render the X Axis Label properly (see the code snippet).
lineChart.getXAxis().setValueFormatter(new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
SimpleDateFormat format2 = new SimpleDateFormat("HH:mm:ss");
return format2.format(new Date(firstTimeStamp + ((long) value) * 1000L));
}
});

How to remove decimal value from the Barchart?

I want to remove decimal value from the BarChart in MPAndroidChart. Even though I have taken it as a Integer value while displaying it is showing decimal and adding up .0 for my value as.
ex: If my value is 65, it is displaying 75.0.(I want to remove that .0 part)
I m using the library called "mpandroidchartlibrary-2-1-4"
below is my code snippet in integer.
Double a= Double.parseDouble(total_week);
int b=a.intValue();
Double a1= Double.parseDouble(complitiontest);
int b1=a1.intValue();
Log.d("duoosfsd","**** "+b1);
entries.add(new BarEntry(b,0));
entries.add(new BarEntry(b1,1));
Here the b1 is value which is Integer value even when I print in log it prints only as 75 but while showing it will show as 75.0.
Attached screenshot please check.
public class MyValueFormatter implements ValueFormatter {
private DecimalFormat mFormat;
public MyValueFormatter() {
mFormat = new DecimalFormat("#");
}
#Override
public String getFormattedValue(float value) {
return mFormat.format(value);
}
}
call this on BarData class object
data.setValueFormatter(new MyValueFormatter());
I did try for same and below worked for me.
new DecimalFormat("#").format(100.0);
You first need to check the mpandroidchartlibrary-2-1-4 library's code.
In that library find the method which is used to display the number on the bar.
As per my understanding, it converts integer into float or either it is displaying float value to the bar at the time of display.
Advice : Please check library code and understand it first. And if you find something and want to change it, then you have to import this project as library and then only you can do changes as per your requirement.

MPAndroidChart Linechart unconnected data points

I am investigating MPAndroidChart for usage in my company's Android and iOS application and I have found a problem that I need a solution to in order to be able to use this framework.
The application will mostly use the Line Chart functionality and the supplied data can contain NULL entries. I have seen other posts that discusses this matter and apparently there is no solution to showing NULL values yet. MPAndroidChart with null values
The author suggests simply not adding the data point to the set, but in my case it's very important that there is a "hole" in the graph were there is more than two consecutive NULL values (or however to represent it) i.e that the graph is not continous between two points with NULL values in between. Is there any way I can accomplish this with this framework?
I have been looking into the possibility of separating the data points into different data sets, but it seems like kind of a hack.
Thank you!
Dataset example:
[1 2 10 NULL NULL NULL 20 25 30]
The Line must NOT connect the numbers 10 and 20.
This is what I ended up coming up with to solve this - for anyone in the future. It iterates through and creates new entries in a data set until it hits a null value, and then it creates data sets with "fake" entries that use a boolean in the entry constructor. Boolean can be found from
"entry.getData()"
and you can then you can use this to set that Dataset to not be visible
"mLineDataSet.setVisible(false);"
Note: Do not try to set Dataset Color to transparent - the library has a bug where if certain entries are null the graph doesn't even appear.
private void createDataSets() {
for (int index = 0; index < mGraph.getGraphDataSets().size(); index++) {
lastIndexCreated = 0;
final GraphDataSet mDataSet = mGraph.getGraphDataSets().get(index);
final ArrayList<Entry> mEntries = getEntries(mDataSet.getYValues(), lastIndexCreated);
final LineDataSet mLineDataSet = getDataSet(mEntries, mDataSet, color);
mGraphLineData.addDataSet(mLineDataSet);
lastIndexCreated = mEntries.size() - 1;
while (lastIndexCreated < mDataSet.getYValues().size() - 1) {
final LineDataSet set = getDataSet(mEntriesSet, mDataSet, colorSecondary);
if (mEntriesSet.size() != 0)
mGraphLineData.addDataSet(set);
lastIndexCreated = (int) mEntriesSet.get(mEntriesSet.size() - 1).getX();
}
}
}
private ArrayList<Entry> getEntries(final List<Float> yValues, final int firstValueIndex) {
final ArrayList<Entry> mEntries = new ArrayList<>();
for (int i = firstValueIndex; i < yValues.size(); i++) {
if (yValues.get(i) != null)
//boolean here is false means that dataset is not fake and should be shown
mEntries.add(new Entry(i, yValues.get(i), false));
else if (firstValueIndex == i) {
//add a "Fake" data entry, and use mEntry.getData to set line to not be visible.
mEntries.add(new Entry(i, 0, true));
break;
} else {
break;
}
}
return mEntries;
}

Categories

Resources