I want to have x axis labels on my linechart (using MPAndroidChart) but no matter what I do, I can't get them to display. I have a method called setupChart which handles everything for that chart and this is what it looks like:
private void setupChart(LineChart chart, LineData data, int color) {
((LineDataSet) data.getDataSetByIndex(0)).setCircleColorHole(Color.WHITE);
((LineDataSet) data.getDataSetByIndex(0)).setCircleColor(color);
((LineDataSet) data.getDataSetByIndex(0)).setColors(dataColors);
data.getDataSetByIndex(0).setAxisDependency(YAxis.AxisDependency.LEFT);
((LineDataSet) data.getDataSetByIndex(0)).setDrawCircles(false);
// no description text
chart.getDescription().setEnabled(false);
// mChart.setDrawHorizontalGrid(false);
//
// enable / disable grid background
chart.setDrawGridBackground(false);
// enable touch gestures
chart.setTouchEnabled(true);
// disable scaling and dragging
chart.setDragEnabled(false);
chart.setScaleEnabled(false);
chart.setAutoScaleMinMaxEnabled(false);
// if disabled, scaling can be done on x- and y-axis separately
chart.setPinchZoom(false);
chart.setBackgroundColor(Color.parseColor("#dddddd"));
// set custom chart offsets (automatic offset calculation is hereby disabled)
chart.setViewPortOffsets(10, 0, 10, 0);
// add data
chart.setData(data);
// get the legend (only possible after setting data)
Legend l = chart.getLegend();
l.setEnabled(false);
chart.getAxisLeft().setEnabled(false);
chart.getAxisRight().setEnabled(false);
chart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
chart.getXAxis().setEnabled(false);
chart.getXAxis().setDrawLabels(true);
chart.getXAxis().setTextColor(context.getResources().getColor(R.color.colorPrimary));
chart.getXAxis().setTypeface(Typeface.SANS_SERIF);
YAxis yAxis = chart.getAxisLeft();
yAxis.setAxisMinimum(0f);
yAxis.setAxisMaximum(100f);
final String[] weeks = new String[52];
for(int i = 0; i < weeks.length; i++) weeks[i] = "Week " + (i+1);
IAxisValueFormatter formatter = new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
return weeks[(int) value];
}
};
XAxis xAxis = chart.getXAxis();
xAxis.setGranularity(1f);
xAxis.setValueFormatter(formatter);
// animate calls invalidate()...
chart.animateXY(2000,2500);
}
I am not sure what I am doing wrong.
I solved this by increasing my offsets:
chart.setViewPortOffsets(60, 0, 50, 60);
linechart.setExtraBottomOffset(ff)
Modifying the offsets will help you to solve this problem as well.
Adjust the (ff) float values to get desired space.
My chart was not showing x-axis and y-axis. I remove this line the x-axis and y-axis appeared.
when this line exist lineChart.setViewPortOffsets(0,0,0,0) the chart look like this
when I remove this line lineChart.setViewPortOffsets(0,0,0,0) the chart look like this
Related
I am trying to build a series chart using JavaFX, where data is inserted dynamically.
Each time that a new value is inserted I would like to check if this is the highest value so far, and if so, I want to draw an horizontal line to show that this is the maximum value.
In JFree chart I would have used a ValueMarker, but I am trying to do the same with JavaFX.
I tried using the Line object, but it is definitely not the same, because I cannot provide the Chart values, it takes the relative pixel positions in the windows.
Here is the screenshot of chart I want to achieve:
http://postimg.org/image/s5fkupsuz/
Any suggestions?
Thank you.
To convert chart values to pixels you can use method NumberAxis#getDisplayPosition() which return actual coordinates of the chart nodes.
Although these coordinates are relative to chart area, which you can find out by next code:
Node chartArea = chart.lookup(".chart-plot-background");
Bounds chartAreaBounds = chartArea.localToScene(chartArea.getBoundsInLocal());
Note localToScene() method which allows you to convert any coordinates to Scene ones. Thus you can use them to update your value marker coordinates. Make sure you make localToScene call after your Scene have been shown.
See sample program below which produces next chart:
public class LineChartValueMarker extends Application {
private Line valueMarker = new Line();
private XYChart.Series<Number, Number> series = new XYChart.Series<>();
private NumberAxis yAxis;
private double yShift;
private void updateMarker() {
// find maximal y value
double max = 0;
for (Data<Number, Number> value : series.getData()) {
double y = value.getYValue().doubleValue();
if (y > max) {
max = y;
}
}
// find pixel position of that value
double displayPosition = yAxis.getDisplayPosition(max);
// update marker
valueMarker.setStartY(yShift + displayPosition);
valueMarker.setEndY(yShift + displayPosition);
}
#Override
public void start(Stage stage) {
LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(0, 100, 10), yAxis = new NumberAxis(0, 100, 10));
series.getData().add(new XYChart.Data(0, 0));
series.getData().add(new XYChart.Data(10, 20));
chart.getData().addAll(series);
Pane pane = new Pane();
pane.getChildren().addAll(chart, valueMarker);
Scene scene = new Scene(pane);
// add new value on mouseclick for testing
chart.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
series.getData().add(new XYChart.Data(series.getData().size() * 10, 30 + 50 * new Random().nextDouble()));
updateMarker();
}
});
stage.setScene(scene);
stage.show();
// find chart area Node
Node chartArea = chart.lookup(".chart-plot-background");
Bounds chartAreaBounds = chartArea.localToScene(chartArea.getBoundsInLocal());
// remember scene position of chart area
yShift = chartAreaBounds.getMinY();
// set x parameters of the valueMarker to chart area bounds
valueMarker.setStartX(chartAreaBounds.getMinX());
valueMarker.setEndX(chartAreaBounds.getMaxX());
updateMarker();
}
public static void main(String[] args) {
launch();
}
}
I'm using an XYBoxAnnotation to demarcate a rectangular area on a JFreeChart. I would like one side of the box to be "open", i.e go out to infinity. I tried setting the value to Double.POSITIVE_INFINITY but this did not seem to work. I also tried setting it to Double.MAX_VALUE, with no luck either. In these cases, the annotation doesn't even show up on the plot at all. And there are no exceptions thrown.
Below is a very simple version of my code in which I generate the XYBoxAnnotation and add it to the plot.
XYBoxAnnotation _axisMarker = new XYBoxAnnotation(xLow, yLow, Double.POSITIVE_INFINITY, yHigh, new BasicStroke(0.5F), Color.WHITE, Color.WHITE);
_plot.getRenderer().addAnnotation(_axisMarker, Layer.BACKGROUND);
EDIT:
I figured out that the reason the annotation wasn't showing up was because the x value for the annotation was much much larger than the axis scale. For some reason, this causes the annotation to not be visible until you zoom out enough.
Thanks to #trashgod's answer below, I came up with a solution. His answer didn't quite work for me since my plot allows zooming and you could see the edge of the box when you zoomed out.
First, I added a PlotChangeListener to listen for when the plot is zoomed:
// define PlotChangeListener to update the annotation when the plot is zoomed
private PlotChangeListener _zoomListener = new PlotChangeListener() {
#Override
public void plotChanged(PlotChangeEvent plotChangeEvent) {
if (_basisIsotope != null) {
updateAxisMarkers();
}
}
};
Then I created a function to re-draw the annotation based on the new plot bounds:
// function to re-draw the annotation
private void updateAxisMarkers() {
_plot.removeChangeListener(_zoomListener); // remove to prevent triggering infinite loop
// define xLow, yLow and yHigh...
double xHigh = _plot.getDomainAxis().getUpperBound() * 1.1;
XYBoxAnnotation _axisMarker = new = new XYBoxAnnotation(xLow, yLow, xHigh, yHigh, new BasicStroke(0.5F), Color.WHITE, Color.WHITE);
_plot.getRenderer().addAnnotation(annotation);
_plot.addChangeListener(_zoomListener); // add back
}
Double.MAX_VALUE is too large to scale to the relevant axis, but Double.MAX_VALUE / 2 works as well as any value larger than the upper bound of the axis. A better choice might be a value that exceeds the maximum value of the domain by some margin. The fragment below shades a plot of some Gaussian data with an XYBoxAnnotation that has domain bounds extending from 42 to the maximum domain value + 10%; the range bounds are ±1σ.
XYSeriesCollection dataset = createDataset();
JFreeChart chart = createChart(dataset);
Color color = new Color(0, 0, 255, 63);
double max = dataset.getSeries(0).getMaxX() * 1.1;
XYBoxAnnotation annotation = new XYBoxAnnotation(
42, -1, max, 1, new BasicStroke(1f), color, color);
chart.getXYPlot().getRenderer().addAnnotation(annotation);
I am drawing a bar chart using a chart i want to customize the chart.
give spave between first bar from start
Increase the width of each bar
incerese text size of numbers which are at top of the bars
Following is the code for my bar chart
private void openChart()
{
int[] x = { 0,1,2,3,4,5,6};
// Creating an XYSeries for Income
XYSeries wSeries = new XYSeries("Workout");
// Creating an XYSeries for Expense
for(int i=0;i<x.length;i++){
wSeries.add(i,workout[i]);
}
// Creating a dataset to hold each series
XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
// Adding Income Series to the dataset
dataset.addSeries(wSeries);
// Adding Expense Series to dataset
// Creating XYSeriesRenderer to customize incomeSeries
XYSeriesRenderer wRenderer = new XYSeriesRenderer();
wRenderer.setColor(getResources().getColor(R.color.header_blue)); //color of the graph set to cyan
wRenderer.setFillPoints(true);
wRenderer.setLineWidth(5);
wRenderer.setDisplayChartValues(true);
wRenderer.setDisplayChartValuesDistance(20); //setting chart value distance
// Creating a XYMultipleSeriesRenderer to customize the whole chart
XYMultipleSeriesRenderer multiRenderer = new XYMultipleSeriesRenderer();
multiRenderer.setOrientation(XYMultipleSeriesRenderer.Orientation.HORIZONTAL);
multiRenderer.setXLabels(0);
/***
* Customizing graphs
*/
//setting text size of the title
multiRenderer.setChartTitleTextSize(30);
//setting text size of the axis title
multiRenderer.setAxisTitleTextSize(30);
//setting text size of the graph lable
multiRenderer.setLabelsTextSize(30);
//setting zoom buttons visiblity
multiRenderer.setZoomButtonsVisible(false);
//setting pan enablity which uses graph to move on both axis
multiRenderer.setPanEnabled(false, false);
//setting click false on graph
multiRenderer.setClickEnabled(false);
//setting zoom to false on both axis
multiRenderer.setZoomEnabled(false, false);
//setting lines to display on y axis
multiRenderer.setShowGridY(false);
//setting lines to display on x axis
multiRenderer.setShowGridX(false);
//setting legend to fit the screen size
multiRenderer.setFitLegend(true);
//setting displaying line on grid
multiRenderer.setShowGrid(false);
//setting zoom to false
multiRenderer.setZoomEnabled(false);
//setting external zoom functions to false
multiRenderer.setExternalZoomEnabled(false);
//setting displaying lines on graph to be formatted(like using graphics)
multiRenderer.setAntialiasing(true);
//setting to in scroll to false
multiRenderer.setInScroll(false);
//setting to set legend height of the graph
multiRenderer.setLegendHeight(30);
//setting x axis label align
multiRenderer.setXLabelsAlign(Align.CENTER);
//setting y axis label to align
multiRenderer.setYLabelsAlign(Align.RIGHT);
//setting text style
multiRenderer.setTextTypeface("sans_serif", Typeface.BOLD);
//change y axis label color
multiRenderer.setYLabelsColor(0,Color.BLACK);
//change x axis label color
multiRenderer.setXLabelsColor(Color.BLACK);
//setting no of values to display in y axis
multiRenderer.setYLabels(7);
// setting y axis max value, Since i'm using static values inside the graph so i'm setting y max value to 4000.
// if you use dynamic values then get the max y value and set here
multiRenderer.setYAxisMin(0);
multiRenderer.setYAxisMax(49);
//setting used to move the graph on xaxiz to .5 to the right
multiRenderer.setXAxisMin(0);
//setting max values to be display in x axis
multiRenderer.setXAxisMax(7);
//setting bar size or space between two bars
multiRenderer.setBarSpacing(3);
//Setting background color of the graph to transparent
multiRenderer.setBackgroundColor(Color.TRANSPARENT);
//Setting margin color of the graph to transparent
multiRenderer.setMarginsColor(getResources().getColor(R.color.light_red));
multiRenderer.setApplyBackgroundColor(true);
//setting the margin size for the graph in the order top, left, bottom, right
multiRenderer.setMargins(new int[]{20, 40, 20, 20});
for(int i=0; i< x.length;i++){
multiRenderer.addXTextLabel(i, week[i]);
}
// Adding incomeRenderer and expenseRenderer to multipleRenderer
// Note: The order of adding dataseries to dataset and renderers to multipleRenderer
// should be same
multiRenderer.addSeriesRenderer(wRenderer);
//this part is used to display graph on the xml
LinearLayout chartContainer = (LinearLayout) findViewById(R.id.nut_sum_graphLayout);
//remove any views before u paint the chart
chartContainer.removeAllViews();
//drawing bar chart
mChart = ChartFactory.getBarChartView(HealthNutritionSummary.this, dataset, multiRenderer,Type.DEFAULT);
//adding the view to the linearlayout
chartContainer.addView(mChart);
}![enter image description here][2]
to give space on the left side a chart, I set X axis min to smaller size. For example, this should do the trick in your case.
multiRenderer.setXAxisMin(-0.5);
To increase width, you have two options.You can set bar spacing or bar width.
multiRenderer.setBarSpacing(0.5);
or
multiRenderer.setBarWidth(20); // I think this is in pixels
And finally, for text size there is option for XYSeriesRenderer, in your case:
wRenderer.setChartValuesTextSize(20);
I have a single series XYAreaChart
final JFreeChart chart = ChartFactory.createXYAreaChart(
"",
"", "Rolling",
dataset,
PlotOrientation.VERTICAL,
false, // legend
true, // tool tips
false // URLs
);
final XYPlot plot = chart.getXYPlot();
I want to set paint color to Red when RangeValue >0 otherwise set it to Green. This is the nearest bit of code I can find BUT I think it is for two series:
plot.setRenderer(new XYDifferenceRenderer(Color.green, Color.red, false));
Which renderer do I need for a single series?
This may be able to help you. Below is an example showing a basic chart from a JFreechart tutorial. I have added the renderer in your example to it, my theory is that when you have 1 series the 'second series' as it were is effectively 0. Therefore when you apply the two series renderer, if the first series dips below 0 it becomes the negative color, and when it is above 0 becomes the positive colour.
When I opened my saved chart it had red on the minus points, and green on the posotive. See if it helps:
public class SO{
public static void main(String[] args) {
// Create a simple XY chart
XYSeries series = new XYSeries("Gradient logs chopped");
series.add(1, -7);
series.add(2, -2);
series.add(3, 4);
series.add(4, 7);
series.add(5, 10);
// Add the series to your data set
XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(series);
// Generate the graph
JFreeChart chart = ChartFactory.createXYLineChart(
"Logs chopped", // Title
"Week", // x-axis Label
"Logs", // y-axis Label
dataset, // Dataset
PlotOrientation.VERTICAL, // Plot Orientation
true, // Show Legend
true, // Use tooltips
false // Configure chart to generate URLs?
);
//The renderer part
XYPlot plot = chart.getXYPlot();
XYDifferenceRenderer rend = new XYDifferenceRenderer(Color.GREEN, Color.RED, false);
rend.setSeriesFillPaint(0, Color.DARK_GRAY);
rend.setRoundXCoordinates(true);
plot.setRenderer(rend);
//The renderer part
try {
ChartUtilities.saveChartAsJPEG(new File("D:\\Users\\user2777005\\Desktop\\XYchart.jpg"), chart, 500, 300);
} catch (IOException e) {
System.err.println("Problem occurred creating chart.");
}
}
}
Good luck!
I'm building multiple stacked bar charts (subplots) that are combined through a CombinedRangeCategoryPlot.
As the subplots datasets do not have the same number of items and since JFreeChart decides to allocate the same space for each subplot, I have different widths of bars.
Is there any way I can align their width (even if it means that the subplots have different widths)?
Please see below for the result and the code I have so far.
Many thanks,
Thomas
//Builds commong range axis
NumberAxis rangeAxis = new NumberAxis("%");
rangeAxis.setRange(0, 1.0);
rangeAxis.setNumberFormatOverride(NumberFormat.getPercentInstance());
//Builds common data set
CombinedRangeCategoryPlot combinedPlots = new CombinedRangeCategoryPlot(rangeAxis);
for (int groupIndex=0; groupIndex<LeakGroups.values().length; ++groupIndex){
//Builds category axis
CategoryAxis categoryAxis = new CategoryAxis(GuiConstants.LEAK_GROUPS_LABELS[groupIndex]);
//Sets margins between bars
categoryAxis.setCategoryMargin(0.5f);
//Builds bar renderer
StackedBarRenderer barRenderer = new StackedBarRenderer();
barRenderer.setRenderAsPercentages(true);
//Builds dot/level renderer
LineAndShapeRenderer dotRenderer = new LineAndShapeRenderer();
//dotRenderer.setSeriesLinesVisible(0, false);
//dotRenderer.setSeriesShapesVisible(0, false);
//dotRenderer.setSeriesLinesVisible(1, false);
//Defines level shape height (depends on chart size): nominal values are for a height of 1000px
int shapeHeightPx = (int) Math.round(20 * (this.getHeight() / 1000.0));
dotRenderer.setSeriesShape(1, new Rectangle(-1, -shapeHeightPx/2, 2, shapeHeightPx));
//Builds plot
CategoryPlot plot = new CategoryPlot();
plot.setDomainAxis(categoryAxis);
plot.setDataset(0, data[groupIndex].bars);
plot.setRenderer(0, barRenderer);
plot.setDataset(1, data[groupIndex].dots);
plot.setRenderer(1, dotRenderer);
//Adds to combined
combinedPlots.add(plot);
}
combinedPlots.setOrientation(PlotOrientation.HORIZONTAL);
//Puts range axis at the bottom
combinedPlots.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT);
//Changes plot render sequence so that bars are in the background and shapes in front
combinedPlots.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
//Shows gridlines for categories and not for values
combinedPlots.setDomainGridlinesVisible(true);
combinedPlots.setRangeGridlinesVisible(false);
//Creates chart
JFreeChart chart = new JFreeChart("Leaks", combinedPlots);
//Sets a margin right to allow space for last catergory label ("100%")
chart.setPadding(new RectangleInsets(0, 0, 0, 20));
return chart;
For some reason, the weight gets reset to value 1 when adding the plot.
By way of explanation,
The add(CategoryPlot subplot) method specifies a default weight of 1,
The add(CategoryPlot subplot, int weight) method lets you specify a weight value.
After a few hours of search, found the solution: use plot.setWeight().
For some reason, the weight gets reset to value 1 when adding the plot to the CombinedRangeCategoryPlot, hence it has to be set after.
Hope this helps.