JFreeChart - CategoryAxis step - java

I have a method for generating dataset:
private CategoryDataset createDataset(double[] arr,
String seriesName) {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
for (int i = 0; i < arr.length; i++) {
dataset.addValue(arr[i], "mySeries", new Integer(i));
}
return dataset;
}
and create BarChart:
JFreeChart chart = ChartFactory.createBarChart(chartTitle,
xaxis, // domain axis label
yaxis, // range axis label
dataset, // data
orientation, // orientation
true, // include legend
true, // tooltips?
false // URLs?
);
Array of doubles hold histogram data, so there are 255 values.
When I display chart there are labels for
all values from 0 - 255 on x axis. I want display only labels for several indexes (for example: 0, 10, 20, 30). I saw that in RangeAxis there is setStandardTickUnits method. But in CategoryAxis:
CategoryAxis domainAxis = plot.getDomainAxis();
I didn't find this.
Any help?

You can try as follows,
NumberAxis vn = (NumberAxis) plot.getRangeAxis();
vn.setTickUnit(new NumberTickUnit(10d));
vn.setRange(0D, Math.ceil(factor * MAX_VALUE));
--that is you just need cast plot.getRangeAxis() to NumberAxis type.

I had same problem. I created new class implementing 'Comparable', and use it as last parameter in addValue(...). You can create something like
class MyCategory implements Comparable<MyCategory> {
Integer value;
String stValue;
MyCategory(int val) {
value = val;
stValue = val%10==0?""+val:"";}
public int compareTo(MyCategory key) { return value.compareTo(key.value); }
public String toString() { return stValue; }
}
And then instead of
dataset.addValue(arr[i], "mySeries", new Integer(i));
use
dataset.addValue(arr[i], "mySeries", new MyCategory(i));

Related

MPAndroidChart BarChart (Grouped DataSets) not showing all bars

I am using MPAndroidChart BarChart (Grouped DataSets) for showing data of two users. It is showing data but the problem is that its not displaying data on x-axis from start due to which all the bars are not visible.
Arrays:
String[] title_list = {"Whatsapp", "Visit", "Callback", "Interested"}
int[] title_values_1 = {50, 15, 25, 36};
int[] title_values_2 = {70, 35, 15, 10};
BarChart:
public void LoadBarChart()
{
List<BarEntry> barEntries1 = new ArrayList<>();
for (int i = 0; i < title_list.length; i++) {
barEntries1.add(new BarEntry(i, title_values_1[i]));
}
List<BarEntry> barEntries2 = new ArrayList<>();
for (int i = 0; i < title_list.length; i++) {
barEntries2.add(new BarEntry(i, title_values_2[i]));
}
BarDataSet dataSet1 = new BarDataSet(barEntries1, "Dataset 1");
dataSet1.setColors(getColor(R.color.pie_chart_blue));
dataSet1.setValueTextSize(10f); /* values size */
dataSet1.setValueTextColor(Color.WHITE);
BarDataSet dataSet2 = new BarDataSet(barEntries2, "Dataset 2");
dataSet2.setColors(getColor(R.color.pie_chart_red));
dataSet2.setValueTextSize(10f); /* values size */
dataSet2.setValueTextColor(Color.WHITE);
float groupSpace = 0.06f;
float barSpace = 0.02f; // x2 dataset
float barWidth = 0.45f; // x2 dataset
BarData data = new BarData(dataSet1, dataSet2);
ValueFormatter vf = new ValueFormatter() {
#Override
public String getFormattedValue(float value) { return ""+(int)value; }
};
data.setValueFormatter(vf);
data.setValueTextSize(12f);
data.setBarWidth(barWidth);
XAxis xAxis = barChart.getXAxis();
xAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
return title_list[(int) value];
}
});
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setDrawGridLines(false);
xAxis.setDrawAxisLine(false);
xAxis.setLabelCount(title_list.length);
barChart.setData(data);
barChart.groupBars(0f, groupSpace, barSpace);
barChart.getDescription().setEnabled(false);
barChart.setDrawValueAboveBar(false);
barChart.setTouchEnabled(false);
barChart.animateY(1000);
barChart.invalidate();
}
I have tried answers on stackoverflow but nothing resolved my issue. Kindly help!
UPDATE:
After Shayan answer all bars are now visible but labels are not centered.
Is it possible to center the lables with the bars?
You have to play with the spacings, I think when its unable to adjust the whole thing in the screen it starts cutting bars.
Add this line so bars may start from the begining
xAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true)
Setting spacing and widths a bit low like this
float groupSpace = 0.06f;
float barSpace = 0.02f; // x2 dataset
float barWidth = 0.40f; // x2 dataset
The complete method will look something like this
public void LoadBarChart()
{
List<BarEntry> barEntries1 = new ArrayList<>();
for (int i = 0; i < title_list.length; i++) {
barEntries1.add(new BarEntry(i, title_values_1[i]));
}
List<BarEntry> barEntries2 = new ArrayList<>();
for (int i = 0; i < title_list.length; i++) {
barEntries2.add(new BarEntry(i, title_values_2[i]));
}
BarDataSet dataSet1 = new BarDataSet(barEntries1, "Dataset 1");
dataSet1.setColors(R.color.colorPrimary);
dataSet1.setValueTextSize(10f); /* values size */
dataSet1.setValueTextColor(Color.WHITE);
BarDataSet dataSet2 = new BarDataSet(barEntries2, "Dataset 2");
dataSet2.setColors(R.color.colorPrimary);
dataSet2.setValueTextSize(10f); /* values size */
dataSet2.setValueTextColor(Color.WHITE);
float groupSpace = 0.06f;
float barSpace = 0.02f; // x2 dataset
float barWidth = 0.40f; // x2 dataset
BarData data = new BarData(dataSet1, dataSet2);
ValueFormatter vf = new ValueFormatter() {
#Override
public String getFormattedValue(float value) { return ""+(int)value; }
};
data.setValueFormatter(vf);
// data.setValueTextSize(12f);
data.setBarWidth(barWidth);
XAxis xAxis = barChart.getXAxis();
xAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
return title_list[(int) value];
}
});
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setDrawGridLines(false);
xAxis.setDrawAxisLine(false);
xAxis.setLabelCount(title_list.length);
xAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true)
barChart.setData(data);
barChart.groupBars(0f,groupSpace,barSpace);
barChart.getDescription().setEnabled(false);
barChart.setDrawValueAboveBar(false);
barChart.setTouchEnabled(false);
barChart.animateY(1000);
barChart.invalidate();
}
Hope this works!
I am able to achieve the desired result by the guidance of Shayan. I have added few attributes in the answer.
Spacing and Bar Width:
float groupSpace = 0.15f;
float barSpace = 0.01f; // x2 dataset
float barWidth = 0.42f; // x2 dataset
Addition at X-Axis:
xAxis.setAxisMinimum(0f);
xAxis.setGranularity(1);
xAxis.setCenterAxisLabels(true);
xAxis.setAxisMaximum(title_list.length);
Formatter:
barChart.getXAxis().setValueFormatter(new IndexAxisValueFormatter(title_list));
So bar chart method now looks like this:
public void LoadBarChart()
{
List<BarEntry> barEntries1 = new ArrayList<>();
for (int i = 0; i < title_list.length; i++) {
barEntries1.add(new BarEntry(i, title_values_1[i]));
}
List<BarEntry> barEntries2 = new ArrayList<>();
for (int i = 0; i < title_list.length; i++) {
barEntries2.add(new BarEntry(i, title_values_2[i]));
}
BarDataSet dataSet1 = new BarDataSet(barEntries1, Global.employeesComparisonList.get(0).getName());
dataSet1.setColors(getColor(R.color.blue));
dataSet1.setValueTextSize(10f); /* values size */
dataSet1.setValueTextColor(Color.WHITE);
BarDataSet dataSet2 = new BarDataSet(barEntries2, Global.employeesComparisonList.get(1).getName());
dataSet2.setColors(getColor(R.color.red));
dataSet2.setValueTextSize(10f); /* values size */
dataSet2.setValueTextColor(Color.WHITE);
float groupSpace = 0.15f;
float barSpace = 0.01f; // x2 dataset
float barWidth = 0.42f; // x2 dataset
BarData data = new BarData(dataSet1, dataSet2);
ValueFormatter vf = new ValueFormatter() {
#Override
public String getFormattedValue(float value) { return ""+(int)value; }
};
data.setValueFormatter(vf);
data.setValueTextSize(12f);
data.setBarWidth(barWidth);
XAxis xAxis = barChart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setDrawGridLines(false);
xAxis.setDrawAxisLine(false);
xAxis.setLabelCount(title_list.length);
xAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true)
xAxis.setGranularity(1);
xAxis.setCenterAxisLabels(true);
xAxis.setAxisMaximum(title_list.length);
Legend l = barChart.getLegend();
l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP);
l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER);
l.setOrientation(Legend.LegendOrientation.HORIZONTAL);
barChart.setData(data);
barChart.groupBars(0f,groupSpace,barSpace);
barChart.getXAxis().setValueFormatter(new IndexAxisValueFormatter(title_list));
barChart.getDescription().setEnabled(false);
barChart.setDrawValueAboveBar(false);
barChart.setTouchEnabled(false);
barChart.animateY(1000);
barChart.invalidate();
}
Final Result:
Yes, that can be done quite easily.
What you need is a BarChart with multiple BarDataSets where each set (in your case) represents count of each activity.
Here is an example of how to create a BarChart with multiple DataSets
Here is an tutorial of how to use MPAndroidChart with Realm.io
Example code (without realm.io)
List<String> xValues = ...; // "Denmark", "Finland", ...
XAxis xAxis = chart.getXAxis();
xAxis.setValueFormatter(new MyValueFormatter(xValues));
xAxis.setCenterAxisLabels(true);
// create 2 datasets
BarDataSet set1 = new BarDataSet(valuesMen, "Men");
set1.setColor(Color.BLUE);
BarDataSet set2 = new BarDataSet(valuesWomen, "Women");
set2.setColor(Color.RED);
BarData data = new BarData(set1, set2);
chart.setData(data);
chart.groupBars(...); // available since release v3.0.0
chart.invalidate(); // refresh
If you need further assistance, here is a detailed tutorial on grouped BarChart available on the wiki.
If you want to "stack" values in a BarChart above each other, you need to create a stacked-barchart: Android Stacked Bars Chart
**
Result Image
**

MPAndroidChart BarChart - Vertical values inside of bars

I am using the MPAndroidChart library for the bar chart, In that, I have used chart.setDrawValueAboveBar(false) to set bar values inside of bars, now I want to display the values vertically inside of bars.
Please help..Thank you in advance.
first make this false
mChart.setDrawValueAboveBar(false);
then enable draw values for your data set
barDataSet.setDrawValues(true);
Since you need to rotate the text as well.. you have to implement a custom renderer for your chart. If you want to know how renderer works, check this answer
I have provided a sample implementation below. You can modify it to control the position of the text as it suits you.
Custom Renderer
public class BarChartCustomRenderer extends BarChartRenderer {
public BarChartCustomRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
public void drawValue(Canvas c, IValueFormatter formatter, float value, Entry entry, int dataSetIndex, float x, float y, int color) {
mValuePaint.setColor(color);
c.save();
c.rotate(90f, x, y);
Log.d("here", formatter.getFormattedValue(value, entry, dataSetIndex, mViewPortHandler) );
c.drawText(formatter.getFormattedValue(value, entry, dataSetIndex, mViewPortHandler), x, y, mValuePaint);
c.restore();
}
}
Chart Code
BarChart mChart = (BarChart) findViewById(R.id.barChart);
mChart.setDrawBarShadow(false);
mChart.setDrawValueAboveBar(false);
mChart.getDescription().setEnabled(false);
mChart.setDrawGridBackground(false);
//**add renderer**
BarChartCustomRenderer barChartCustomRenderer = new BarChartCustomRenderer(mChart, mChart.getAnimator(), mChart.getViewPortHandler());
mChart.setRenderer(barChartCustomRenderer);
XAxis xaxis = mChart.getXAxis();
xaxis.setDrawGridLines(false);
xaxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xaxis.setGranularity(0.5f);
xaxis.setGranularityEnabled(true);
xaxis.setDrawLabels(true);
xaxis.setDrawAxisLine(false);
YAxis yAxisLeft = mChart.getAxisLeft();
yAxisLeft.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART);
yAxisLeft.setDrawGridLines(false);
yAxisLeft.setDrawAxisLine(false);
yAxisLeft.setEnabled(false);
mChart.getAxisRight().setEnabled(false);
Legend legend = mChart.getLegend();
legend.setEnabled(false);
ArrayList<BarEntry> valueSet1 = new ArrayList<BarEntry>();
ArrayList<String> ylabels = new ArrayList<>();
for (int i = 0; i < 6; ++i) {
BarEntry entry = new BarEntry(i, (i + 1) * 2);
valueSet1.add(entry);
ylabels.add(" " + i);
}
List<IBarDataSet> dataSets = new ArrayList<>();
BarDataSet barDataSet = new BarDataSet(valueSet1, " ");
barDataSet.setColor(Color.CYAN);
barDataSet.setDrawValues(true);
dataSets.add(barDataSet);
BarData data = new BarData(dataSets);
data.setBarWidth(0.4f);
data.setValueTextSize(10f);
data.setValueTextColor(Color.BLACK);
mChart.setData(data);
mChart.setFitBars(true);
mChart.invalidate();
Result
class MyCustomRenderer(
chart: BarDataProvider, animator: ChartAnimator, viewPortHandler: ViewPortHandler
) : HorizontalBarChartRenderer(chart, animator, viewPortHandler) {
override fun drawValue(c: Canvas, valueText: String, x: Float, y: Float, color: Int) {
mValuePaint.color = color
val xPoint = Utils.convertDpToPixel(26f)
val yPoint = Utils.convertDpToPixel(16f)
c.drawText(valueText, xPoint , yPoint , mValuePaint)
}
}

Outline to blocks in XY Block renderer jfreechart

I am drawing a kind of heatmap using XYZ dataset and XY block renderer. The block's color is a function of the Z value and color is assigned using grayscale. i.e. the block with 0 Z value is assigned white color and one with maximum is assigned black color.
My grayscale goes from 0 to 100 (or more lets say). If the scale is this big, the blocks with count as 0 and 10 will have very little difference in color values. To understand, lets say the whole grid is divided in blocks. One block hasZ value of 100, one has 2 and all others are 0. Then, this block with 2 as Z value is not much visible because of very light shade.
I want to give outline to blocks of some color so that they are distinguishable. I tried with setBaseItemOutline() etc functions but none does this.
Any help on this?
Edit: Below
One of my classes is this:
public class BlockRenderer extends ApplicationFrame {
/**
* Constructs the demo application.
*
* #param title the frame title.
*/
public BlockRenderer(String title) {
super(title);
JPanel chartPanel = createDemoPanel();
//chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
setContentPane(chartPanel);
}
/**
* Creates a chart for the specified dataset.
*
* #param dataset the dataset.
*
* #return A chart instance.
*/
private static JFreeChart createChart(XYZDataset dataset) {
NumberAxis xAxis = new NumberAxis("X");
xAxis.setLowerMargin(0.0);
xAxis.setUpperMargin(0.0);
NumberAxis yAxis = new NumberAxis("Y");
yAxis.setAutoRangeIncludesZero(false);
yAxis.setInverted(true);
yAxis.setLowerMargin(0.0);
yAxis.setUpperMargin(0.0);
yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
XYBlockRenderer renderer = new XYBlockRenderer();
CustomGrayPaintScale paintScale = new CustomGrayPaintScale(0,1000);
renderer.setPaintScale(paintScale);
XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer);
plot.setBackgroundPaint(Color.lightGray);
plot.setAxisOffset(new RectangleInsets(5, 5, 5, 5));
JFreeChart chart = new JFreeChart("XYBlockChartDemo3", plot);
chart.removeLegend();
chart.setBackgroundPaint(Color.white);
SymbolAxis scaleAxis = new SymbolAxis(null, new String[] {"", "OK",
"Uncertain", "Bad"});
scaleAxis.setRange(0.5, 3.5);
scaleAxis.setPlot(new PiePlot());
scaleAxis.setGridBandsVisible(false);
PaintScaleLegend psl = new PaintScaleLegend(paintScale, scaleAxis);
psl.setAxisOffset(5.0);
psl.setPosition(RectangleEdge.BOTTOM);
psl.setMargin(new RectangleInsets(5, 5, 5, 5));
chart.addSubtitle(psl);
renderer.setBaseToolTipGenerator(new XYToolTipGenerator() {
#Override
public String generateToolTip(XYDataset dataset, int arg1, int arg2) {
// TODO Auto-generated method stub
XYZDataset xyzDataset = (XYZDataset)dataset;
return String.valueOf(xyzDataset.getZValue(arg1, arg2));
}
});
return chart;
}
/**
* Utility method called by createDataset().
*
* #param data the data array.
* #param c the column.
* #param r the row.
* #param value the value.
*/
private static void setValue(double[][] data,
int c, int r, double value) {
data[0][(r) * 10 + c] = c;
data[1][(r) * 10 + c] = r;
data[2][(r) * 10 + c] = value;
}
/**
* Creates a sample dataset.
*/
private static XYZDataset createDataset() {
double[] xvalues = new double[10*10];
double[] yvalues = new double[10*10];
double[] zvalues = new double[10*10];
double[][] data = new double[][] {xvalues, yvalues, zvalues};
// set the default z-value to zero throughout the data array.
int count [][] = new int[10][10];
for ( int i=0; i<10; i++ ) {
for ( int j=0; j<10; j++ ) {
count[i][j] = i*j;
if ( i==0 && j== 5 )
count[i][j] = 3;
}
}
for ( int i=0; i<10; i++ ) {
for ( int j=0; j<10; j++ ) {
setValue(data,j,i,count[i][j]);
}
}
DefaultXYZDataset dataset = new DefaultXYZDataset();
dataset.addSeries("Series 1", data);
System.out.println(dataset.getZValue(0, 1));
return dataset;
}
/**
* Creates a panel for the demo.
*
* #return A panel.
*/
public static JPanel createDemoPanel() {
return new ChartPanel(createChart(createDataset()));
}
/**
* Starting point for the demonstration application.
*
* #param args ignored.
*/
public static void main(String[] args) {
BlockRenderer demo = new BlockRenderer("Block Chart Demo 3");
//demo.pack();
demo.setExtendedState(java.awt.Frame.MAXIMIZED_BOTH);
RefineryUtilities.centerFrameOnScreen(demo);
demo.setVisible(true);
}
}
I have made CustomGrayPaintScale class to get white color for 0 Z values by over riding its getPaint(). If above class is run, we'll notice that the blocks are not much distinguishable. There is cell in the top row with value as 3 and all others in that row are 0. Because of my large range, its color value is not much different from its adjacent. So, I wanted something which can draw outline to these blocks. Also, I want to assign lets say blue color to some block items and others should have the paintscale on the basis of Z value only (Better if paint scale is designed which assigns different intensity levels of any color ex green to all the items, not the spectrum paint scale which gives all the different colors to the block).
How can I achieve this?
XYBlockRenderer ignores the outline properties inherited from the parent, AbstractRenderer. The drawItem() method simply sets the paint returned from the PaintScale, using the same color for both fill() and draw(). Some possible approaches would include these:
Override drawItem() and set a different paint after fill() but before draw(), perhaps using a brighter() or darker() color.
Use a PaintScale other than GrayPaintScale, such as LookupPaintScale or your own implementation. You can specify more distinctive colors at the problem end, perhaps using Color.getHSBColor() to vary the hue, saturation and/or brightness.
This example implements the PaintScale interface.
This example varies the hue in an XYLineAndShapeRenderer.
This example varies the saturation in a GanttRenderer.

How can I plot points on a graph from function in another class which returns an array filled with values to plot

I have my first class
public class GetResults{
public double[] tableOfresults() {
double[] outputArray = new double[100];
double time;
double results
for(time = 0; time<outputArray.length; i++) {
//Some values are calculated and then stored in an array as below
outputArray[(int)Time] = Results
}
return outputarray;
This class just calculates some values I want to plot on a graph and stores them in the array.
This next class is what actually plots the points on my graph. I am unsure of a simple way to get points to plot on my X axis which are the Values of time(Time being the array index 0,1,2,3 ect) and my Y axis which are my Results. I am currently having to put all the positions in myself.
public class graph{
GetResults gr = new GetResuts();
public XYSeries inputOutputGraph() {
XYSeries graph = new XYSeries("My graph");
XYDataset xyDataset = new XYSeriesCollection(graph);
graph.add(1, gr.tableOfResults()[1]);
graph.add(2, gr.tableOfResults()[2]);
graph.add(3, gr.tableOfResults()[3]);
graph.add(4, gr.tableOfResults()[4]);
Here I am having to add the values myself(I have 1000 to do). I need something that looks like this graph.add(gr.tableOfResults()[gr.Time], gr.tableOfResults()[gr.Results]);
So that as my time goes up 0,1,2,3 it will plot my Results which is stored at index 0,1,2,3 at that position. How could I do this?? I have tried that code^^ and I got an array index out of bounds with the vaue my array size was set to
JFreeChart chart = ChartFactory.createXYLineChart(
"Graph", "Time", "results",
xyDataset, PlotOrientation.VERTICAL, true, true, false);
ChartFrame graphFrame = new ChartFrame("XYLine Chart", chart);
graphFrame.setVisible(true);
graphFrame.setSize(300, 300);
return graph;
}
}
You could just put your calculated values directly in an XYSeries and then let GetResults have a method that returns that series.
class GetResults {
public XYSeries getSeries() {
XYSeries series = new XYSeries("Series");
for (int i = 0; i < 10; i++) {
series.add(i, Math.pow(2, i));
}
return series;
}
}
Here's how you would use getSeries() in this example.
dataset.addSeries(new GetResults().getSeries());

Image histogram generated by JFreeChart

I want to display histogram of image color channels.
At first my reading of pixels looks like:
for(int i=0; i<width; i++)
for(int j=0; j<height; j++) {
data=writeableRaster.getDataElements(i, j, null);
red=colorModel.getRed(data);
green=colorModel.getGreen(data);
blue=colorModel.getBlue(data);
rgb=(red+green+blue)/3;
++redL[red];
++greenL[green];
++blueL[blue];
++rgbL[rgb];
}
}
I also have additional method for creating chart with given channel colors table:
int number = channelHistogram.length;
HistogramDataset dataset = new HistogramDataset();
dataset.setType(HistogramType.RELATIVE_FREQUENCY);
dataset.addSeries("Hist",channelHistogram,number);
String plotTitle = "Hist";
String xaxis = "number";
String yaxis = "value";
PlotOrientation orientation = PlotOrientation.VERTICAL;
boolean show = false;
boolean toolTips = false;
boolean urls = false;
JFreeChart chart = ChartFactory.createHistogram( plotTitle, xaxis, yaxis,
dataset, orientation, show, toolTips, urls);
But chart is wrong displayed. It means at Y axis there are "low" values (from ~ 0 - 0.09) and at X axis there aren't values from scope 0 - 255.
Any help?
dataset.setType(HistogramType.RELATIVE_FREQUENCY);
Can you try setting different options here and see if it helps? Also if you can show what channelHistogram field contains that may be helpful to debug.

Categories