Android Line Chart x-axis Values - java

I am having some problem when trying to populate data to line chart using ACHartEngine library in Android. Basically I am trying to create a line chart with x-axis from Jan-Dec no matter the amount of the particular month:
As from the x-axis, there is Jan - Dec although some of the months were with 0 amount. My problem now is I only managed to plot a graph with the months with amount. Let's say currently my database have these records:
So basically my graph will only have Jun, Aug and Sep instead of Jan - Dec.
Here is how I set up my line chart:
private void openTotalMonthlyChart() {
int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
// Creating an XYSeries for Expenses
XYSeries expensesSeries = new XYSeries("Expenses");
// Adding data to Expense Series
for (int i = 0; i < trans_list.size(); i++) {
expensesSeries.add(x[i], trans_list.get(i).getAmount());
}
// Creating a dataset to hold each series
XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
// Adding Expenses Series to the dataset
dataset.addSeries(expensesSeries);
// Creating XYSeriesRenderer to customize expensesSeries
XYSeriesRenderer expensesRenderer = new XYSeriesRenderer();
expensesRenderer.setColor(Color.rgb(124, 181, 236));
expensesRenderer.setPointStyle(PointStyle.CIRCLE);
expensesRenderer.setFillPoints(true);
expensesRenderer.setLineWidth(2);
expensesRenderer.setDisplayChartValues(true);
// Creating a XYMultipleSeriesRenderer to customize the whole chart
XYMultipleSeriesRenderer multiRenderer = new XYMultipleSeriesRenderer();
multiRenderer.setXLabels(0);
multiRenderer.setChartTitle("Total Monthly Expenses");
multiRenderer.setXTitle("");
multiRenderer.setYTitle("Amount");
multiRenderer.setZoomButtonsVisible(true);
for (int j = 0; j < trans_list.size(); j++) {
multiRenderer.addXTextLabel(j + 1, trans_list.get(j).getDate());
}
multiRenderer.addSeriesRenderer(expensesRenderer);
// Get the component from XML file
RelativeLayout chartContainer = (RelativeLayout) totalMonthlyView
.findViewById(R.id.chart_totalMonthly);
// Creating a Line Chart
chartOverall = ChartFactory.getLineChartView(getActivity(), dataset,
multiRenderer);
// Adding the Line Chart to the RelativeLayout
chartContainer.addView(chartOverall);
}
And my database SQL statement which return the data as the second image above:
public ArrayList<TransactionRecModel> getTotalMonthly() {
try {
String sql = "SELECT SUM(amount) AS total, SUBSTR(date,4,2) AS Month FROM transactionRec WHERE type = 'W' GROUP BY Month";
Cursor mCur = mDb.rawQuery(sql, null);
Log.e(TAG, "Data Grab RECORD Success");
if (mCur.getCount() != 0) {
if (mCur.moveToFirst()) {
do {
TransactionRecModel trm = new TransactionRecModel();
trm.setAmount(mCur.getInt(mCur.getColumnIndex("total")));
trm.setDate(mCur.getString(mCur.getColumnIndex("Month")));
transList.add(trm);
} while (mCur.moveToNext());
}
}
return transList;
} catch (SQLException mSQLException) {
throw mSQLException;
}
}
I wonder is there any way to do this? Thanks in advance and sorry for my poor grammar.

You can use MathHelper.NULL_VALUE for the values that you want to be skipped. See the sensor values chart AChartEngine example to see how to do this.

Related

Java JFree chart realtime chart convert domain label from millis to HH:MM:SS

I created a real time chart with JFreechart where the Domain axis is epoch millis. I would like the labels to display HH:MM:SS.
Here is the block of code that I use to load the chart with data. I am very new to Java and any suggestions are very much appreciated.
Thread thread = new Thread(){
public void run() {
try (Scanner scanner = new Scanner(chosenPort.getInputStream())) { // Read Data from Serial Port
int x = 0; // Set data
while(scanner.hasNextLine()) {
long epoch = System.currentTimeMillis();
chart.getXYPlot().getDomainAxis().setRange(epoch - 30000.00, epoch + 1000.00);
try{
String line = scanner.nextLine();
int number = Integer.parseInt(line); //
series.add(epoch,number); // add Data to Chart
p1.repaint();
}catch(Exception e) {}
}
}
}
};
I was using an XYseries Line chart instead of a time series chart. By using JFreeChart chart = ChartFactory.createTimeSeriesChart instead of JFreeChart chart = ChartFactory.createXYLineChart the correct date/time values were interpreted and displayed automatically.

POI bar chart generate one series has question

I use JDK8 and POI-4.1.0 use they example here a link
export chart to Word .when create two series is ok two series img,
but I only create one series .the chart mistake category for series one series img
"lang1" "lang2" "lang3" is category name but they become series name.
i have no ideal. I also find use line chart have the same problem
my code
public static void main(String[] args) throws Exception {
List<String> listLanguages = new ArrayList<>(3);
listLanguages.add("lang1");listLanguages.add("lang2");listLanguages.add("lang3");
List<Double> listCountries = new ArrayList<>(3);
listCountries.add(10d);listCountries.add(20d);listCountries.add(30d);
List<Double> listSpeakers = new ArrayList<>(3);
listSpeakers.add(14d);listSpeakers.add(25d);listSpeakers.add(33d);
String[] categories = listLanguages.toArray(new String[listLanguages.size()]);
Double[] values1 = listCountries.toArray(new Double[listCountries.size()]);
Double[] values2 = listSpeakers.toArray(new Double[listSpeakers.size()]);
try (XWPFDocument doc = new XWPFDocument()) {
XWPFChart chart = doc.createChart(5000000, 4000000);
XDDFChartAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
final int numOfPoints = categories.length;
final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1));
final String valuesDataRange2 = chart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2));
final XDDFDataSource<?> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
final XDDFNumericalDataSource<? extends Number> valuesData = XDDFDataSourcesFactory.fromArray(values1, valuesDataRange, 1);
final XDDFNumericalDataSource<? extends Number> valuesData2 = XDDFDataSourcesFactory.fromArray(values2, valuesDataRange2, 2);
XDDFBarChartData bar = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
XDDFBarChartData.Series series1 = (XDDFBarChartData.Series) bar.addSeries(categoriesData, valuesData);
series1.setTitle("a",chart.setSheetTitle("a", 1));
//XDDFBarChartData.Series series2 = (XDDFBarChartData.Series) bar.addSeries(categoriesData, valuesData2);
//series2.setTitle("b",chart.setSheetTitle("b", 2));
bar.setVaryColors(true);
bar.setBarDirection(BarDirection.COL);
chart.plot(bar);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.LEFT);
// legend.setOverlay(false);
try (OutputStream out = new FileOutputStream("C:/Users/lyf/Desktop/barExample.docx")) {
doc.write(out);
}
}
catch(Exception e)
{
}
}
The main problem is that setting setVaryColors to true means the following:
If only one series, then do varying the colors for each data point of the series. Then the legend shows the varying data points instead of the series. If more than one series, then do varying the colors for each series. Then the legend shows the varying series.
So we need set setVaryColors to false if only one series is present.
But additional we need set AxisCrossBetween, so the left axis crosses the category axis between the categories. Else first and last category is exactly on cross points and the bars are only half visible.
And at last, XDDF is only half ready at the moment and there are many bugs. For example XDDFChart.setSheetTitle is buggy. It creates a Table but only half way and incomplete. Excel cannot opening the workbook after creating that incomplete Table. So updating the chart data in Word is not possible.
The following code works for me and can create a chart with only one series as well as with two. Additional I have tried making the code more structured in single steps. Each step is commented on what it is doing.
import java.io.*;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.util.Units;
import org.apache.poi.xddf.usermodel.*;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.*;
public class CreateWordXDDFChart {
// Methode to set title in the data sheet without creating a Table but using the sheet data only.
// Creating a Table is not really necessary.
static CellReference setTitleInDataSheet(XWPFChart chart, String title, int column) throws Exception {
XSSFWorkbook workbook = chart.getWorkbook();
XSSFSheet sheet = workbook.getSheetAt(0);
XSSFRow row = sheet.getRow(0); if (row == null) row = sheet.createRow(0);
XSSFCell cell = row.getCell(column); if (cell == null) cell = row.createCell(column);
cell.setCellValue(title);
return new CellReference(sheet.getSheetName(), 0, column, true, true);
}
public static void main(String[] args) throws Exception {
try (XWPFDocument document = new XWPFDocument()) {
// create the data
String[] categories = new String[]{"Lang 1", "Lang 2", "Lang 3"};
Double[] valuesA = new Double[]{10d, 20d, 30d};
Double[] valuesB = new Double[]{15d, 25d, 35d};
// create the chart
XWPFChart chart = document.createChart(15*Units.EMU_PER_CENTIMETER, 10*Units.EMU_PER_CENTIMETER);
// create data sources
int numOfPoints = categories.length;
String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
String valuesDataRangeA = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1));
String valuesDataRangeB = chart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2));
XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
XDDFNumericalDataSource<Double> valuesDataA = XDDFDataSourcesFactory.fromArray(valuesA, valuesDataRangeA, 1);
XDDFNumericalDataSource<Double> valuesDataB = XDDFDataSourcesFactory.fromArray(valuesB, valuesDataRangeB, 2);
// create axis
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
// Set AxisCrossBetween, so the left axis crosses the category axis between the categories.
// Else first and last category is exactly on cross points and the bars are only half visible.
leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
// create chart data
XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
((XDDFBarChartData)data).setBarDirection(BarDirection.COL);
// create series
// if only one series do not vary colors for each bar
((XDDFBarChartData)data).setVaryColors(false);
XDDFChartData.Series series = data.addSeries(categoriesData, valuesDataA);
// XDDFChart.setSheetTitle is buggy. It creates a Table but only half way and incomplete.
// Excel cannot opening the workbook after creatingg that incomplete Table.
// So updating the chart data in Word is not possible.
//series.setTitle("a", chart.setSheetTitle("a", 1));
series.setTitle("a", setTitleInDataSheet(chart, "a", 1));
/*
// if more than one series do vary colors of the series
((XDDFBarChartData)data).setVaryColors(true);
series = data.addSeries(categoriesData, valuesDataB);
//series.setTitle("b", chart.setSheetTitle("b", 2));
series.setTitle("b", setTitleInDataSheet(chart, "b", 2));
*/
// plot chart data
chart.plot(data);
// create legend
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.LEFT);
legend.setOverlay(false);
// Write the output to a file
try (FileOutputStream fileOut = new FileOutputStream("CreateWordXDDFChart.docx")) {
document.write(fileOut);
}
}
}
}

Creating Stacked Bar chart with line graph using jfreechart and taking values from database

I want to create a stacked bar chart with line graph on it using jfreechart by taking values from db.
Basically, I want to show two values on the bar - failed values and success values on top of that.
A line graph will touch all the bars and its value will be the summation of failed and success values for that respective hour.
Also, I want one horizontal line which will represent the average of all the values , i.e. total count of (failed+success) values
This Stacked Bar will have per hour values , i.e on the x-axis hours count will be present.
Y -axis should have no. of values.
I have written the following code for creating only StackedBarChart, but its not generating the graph
As I am new to jfreechart, I am not sure how to come up with the code. Any help would be appreciated.
code.....
String query1 = "select * from table1";
ps = con.prepareStatement(query1);
rs = ps.executeQuery();
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
System.out.println("Creating Graph of hourly count");
while (rs.next())
{
Number a = Long.parseLong(rs.getString("SuccessValues"));
String b = rs.getString("FailureValues");
String c = rs.getString("Hours");
dataset.setValue(a, b, c);
}
final String title = "Per Hour Count";
final String Xaxis = "Time";
final String Yaxis = "Orders";
barChartDisp(title,Xaxis,Yaxis,dataset);
private void barChartDisp(String title, String xaxis, String yaxis, DefaultCategoryDataset dataset) {
try{
JFreeChart barChart = ChartFactory.createStackedBarChart(title, xaxis, yaxis, dataset,
PlotOrientation.VERTICAL, true, true, false);
int width = 640;
int height = 480;
File BarChart = new File("BarChart.jpeg");
ChartUtilities.saveChartAsJPEG(BarChart, barChart, width, height);
}}

Android Time Ranking

I am currently having a problem with the time ranking on my android application. I am working with the sqlite database that will store the time the user finished solving the problem. I will use that sqlite database later to display the rankings of the user that played the game.
For example, the player finished the puzzle for about 1:25 (1 minute and 25 seconds), the app will store 1:25 on the sqlite database. But I'm having a problem on doing that.
I can store it on the sqlite as a string but I can't use ORDER
BY.
I tried storing it as an int but output says: invalid int
I tried storing it as a string while removing the colon (:) but the database on returns "125".
My question is: What is the best way to store that specific time value on sqlite databases?
I read this http://www.sqlite.org/lang_datefunc.html documentation about the Time and Date Functions but it seems it can't be applied on custom time. It only applies on either the date now or current time. Please I need help on this. I'm an android and sqlite amateur. This project is my thesis for this sem.
Any comments and suggestions are accepted. Thanks in advance!
By the way, this is the code that retrieves all the data on my sqlite. It's a testing program, to test if my codes/to modify codes and see if they are working.
try {
String selectQuery = "SELECT * FROM " + DatabaseHelper.TABLE_OUTLET + " WHERE category LIKE '" + cat + "' ORDER BY category ASC";
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
// Read columns data
int outlet_id = cursor.getInt(cursor.getColumnIndex("outlet_id"));
String outlet_name = cursor.getString(cursor.getColumnIndex("outlet_name"));
String outlet_type = cursor.getString(cursor.getColumnIndex("outlet_type"));
String category = cursor.getString(cursor.getColumnIndex("category"));
// dara rows
TableRow row = new TableRow(context);
row.setLayoutParams(new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
String[] colText = {outlet_name, outlet_type, category};
for (String text : colText) {
TextView tv = new TextView(this);
tv.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT));
tv.setGravity(Gravity.CENTER);
tv.setTextSize(16);
tv.setPadding(5, 5, 5, 5);
tv.setText(text);
row.addView(tv);
}
tableLayout.addView(row);
}
}
db.setTransactionSuccessful();
} catch (SQLiteException e) {
e.printStackTrace();
} finally {
db.endTransaction();
// End the transaction.
db.close();
// Close database
}
}
1:25 convert this to seconds only.
That is 1 * 60 + 25 = 85. So store 85.
Every time you want to store it do this. And you can do the reverse on restoring.
In example,
sec = 85 % 60 = 25
min = 85 / 60 = 1
You will get 1:25
Following the suggestion of #ShreyashSSarnayak I managed to fix this problem.
This is the resulting code:
try {
String selectQuery = "SELECT * FROM " + DatabaseHelper.TABLE_OUTLET + " WHERE category LIKE '" + cat + "' ORDER BY outlet_type ASC";
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
// Read columns data
int outlet_id = cursor.getInt(cursor.getColumnIndex("outlet_id"));
String outlet_name = cursor.getString(cursor.getColumnIndex("outlet_name"));
int outlet_type = Integer.parseInt(cursor.getString(cursor.getColumnIndex("outlet_type")));
String category = cursor.getString(cursor.getColumnIndex("category"));
// dara rows
TableRow row = new TableRow(context);
row.setLayoutParams(new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
int m, s;
s = outlet_type % 60;
m = outlet_type / 60;
String[] colText = {outlet_name, String.format("%02d:%02d", m, s), category};
for (String text : colText) {
TextView tv = new TextView(this);
tv.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT));
tv.setGravity(Gravity.CENTER);
tv.setTextSize(16);
tv.setPadding(5, 5, 5, 5);
tv.setText(text);
row.addView(tv);
}
tableLayout.addView(row);
}
}
db.setTransactionSuccessful();
} catch (SQLiteException e) {
e.printStackTrace();
} finally {
db.endTransaction();
// End the transaction.
db.close();
// Close database
}
}
I'll suggest you to convert your time to seconds and store it as integer.
Use INTEGER as database type instead. Check here available types. Then you need to convert the INTEGER value to time format using standard java clases or any other if you like.
Using INTEGER you will be also able to sort them easily.

Draws a block, rather than a point in jfreechart

i m using jfreechart to draw a graph about a logger of operations in a computer.
ex:
1:2012/09/39/28 06:55:37 8 S 0x1c0c762 Terminal --geometry=134x35 --display :0.0 --role=Terminal-0x10591b0-16869-1343137248 --show-menubar --show-borders --hide-toolbars --working-directory /home/termier "Terminal", "Terminal" "Terminal - termier#akagi: ~"
2:2012/09/39/28 06:55:41 8 S 0x1600313 /usr/lib/xfce4/notifyd/xfce4-notifyd "xfce4-notifyd", "Xfce4-notifyd" "xfce4-notifyd"
for now , i can draw every point just like (2012/09/39/28 06:55:37,Terminal),scilicet: x-axis is 2012/09/39/28 06:55:37 , Y-axis is: Terminal (i use 1 to present Terminal ,as for other commands just like Terminal... 2:/usr/lib/xfce4/notifyd/xfce4-notifyd ,etc...)
but what i need is draw a block ,like:
terminal 1: _________S||||||
/usr/lib/xfce4/notifyd/xfce4-notifyd2:______________S||||||
com 3: _____S|||||
(S:start ,eg: 2000/12/12 09:22:10 start)
.....
(when the first command end, another one will be start ,i just can get the start, it means that the post command is the end time of the previous command)
but not: 1: S
2: S
3: S
here some codes to you.
private XYDataset createDataset() {
Calendar precal;
Calendar postcal;
this.flags = modelfocus.getListflag();
commands = modelfocus.getListCommand();
DateFormat formatedate = new SimpleDateFormat("yyyy/MM/ww/dd HH:mm:ss");
precal = Calendar.getInstance();
postcal = Calendar.getInstance();
for (int i = 0; i < countCom; i++) {
this.series[i] = new TimeSeries(commands.get(i));
}
for (Focus listTxt : modelfocus.getList()) {
try {
Date d = new Date();
d = formatedate.parse(listTxt.date2String());
System.out.println(d);
precal.setTime(d);
//postcal.setTime();
} catch (ParseException e) {
System.out.println("Can't change this date");
e.printStackTrace();
}
String eachCmd = listTxt.getCommand();
for (int i = 0; i < countCom; i++) {
if (eachCmd == commands.get(i)) {
series[i].addOrUpdate(new Second(precal.getTime()),
flags.get(i));
}
}
}
TimeSeriesCollection dataset = new TimeSeriesCollection();
for (int i = 0; i < countCom; i++) {
dataset.addSeries(this.series[i]);
}
return dataset;
}
Please can someone give help to solve this problem, thank you very much.
As shown in this example, you can change the Shape used to render the values of a time series. A Rectangle is shown below.
r.setSeriesShape(0, new Rectangle(-4, -4, 9, 9));

Categories

Resources