JFReeCharts XYPlot is it possible to change shapes inside legends - jfreechart

lt.setPosition(RectangleEdge.BOTTOM);
lt.setItemFont(old);
// get the range axis and add the $ symbol for the values
NumberAxis na = (NumberAxis) plot.getRangeAxis();
// set font
na.setLabelFont(fAxisFont);
na.setTickLabelFont(fAxisFont);
na.setAutoRange(true);
The above is my code snippet.Can anyone tell how to to chang shapes inside legends

I'm using the following workaround:
StandardXYItemRenderer renderer = new StandardXYItemRenderer() {
private static final long serialVersionUID = 0L;
#Override
public LegendItem getLegendItem(int datasetIndex, int series) {
LegendItem legend = super.getLegendItem(datasetIndex,
series);
return new LegendItem(legend.getLabel(),
legend.getDescription(), legend.getToolTipText(),
legend.getURLText(), Plot.DEFAULT_LEGEND_ITEM_BOX,
legend.getFillPaint());
}
};
...
plot.setRenderer(renderer);
Result:

A LegendItem infers its Shape from the corresponding series, which can be changed as shown here. This related example shows one way to render a LegendItem in an external component.

Related

How to adjust colors in the UI of OptaPlanner?

I am currently using the OptaPlanner's job schedule algorithm to create a certain planning. I want every execution mode used in the planning to be shown in a different color (instead of all different projects to be shown in different colors). Is it possible to implement this and if so, how? I have been searching through the code for a while now and have no idea how to do this.
This cannot be done easily with the Project Scheduling Swing application that's part of OptaPlanner project. It plots the data using JFreeChart and I couldn't find a simple way to associate metadata (like color) with the data that's being plotted.
You can override YIntervalRenderer behavior to return color of your choice based on data item's row (seriesIndex) and column (item's index in the series) but you have to keep the mapping between execution mode and [row, column] yourself, which is cumbersome.
Here's an example of modified ProjectJobSchedulingPanel that does the above:
public class ProjectJobSchedulingPanel extends SolutionPanel<Schedule> {
private static final Logger logger = LoggerFactory.getLogger(ProjectJobSchedulingPanel.class);
private static final Paint[] PAINT_SEQUENCE = DefaultDrawingSupplier.DEFAULT_PAINT_SEQUENCE;
public static final String LOGO_PATH = "/org/optaplanner/examples/projectjobscheduling/swingui/projectJobSchedulingLogo.png";
public ProjectJobSchedulingPanel() {
setLayout(new BorderLayout());
}
#Override
public void resetPanel(Schedule schedule) {
removeAll();
ChartPanel chartPanel = new ChartPanel(createChart(schedule));
add(chartPanel, BorderLayout.CENTER);
}
private JFreeChart createChart(Schedule schedule) {
YIntervalSeriesCollection seriesCollection = new YIntervalSeriesCollection();
Map<Project, YIntervalSeries> projectSeriesMap = new LinkedHashMap<>(
schedule.getProjectList().size());
ExecutionMode[][] executionModeByRowAndColumn = new ExecutionMode[schedule.getProjectList().size()][schedule.getAllocationList().size()];
YIntervalRenderer renderer = new YIntervalRenderer() {
#Override
public Paint getItemPaint(int row, int column) {
ExecutionMode executionMode = executionModeByRowAndColumn[row][column];
logger.info("getItemPaint: ExecutionMode [{},{}]: {}", row, column, executionMode);
return executionMode == null
? TangoColorFactory.ALUMINIUM_5
: PAINT_SEQUENCE[(int) (executionMode.getId() % PAINT_SEQUENCE.length)];
}
};
Map<Project, Integer> seriesIndexByProject = new HashMap<>();
int maximumEndDate = 0;
int seriesIndex = 0;
for (Project project : schedule.getProjectList()) {
YIntervalSeries projectSeries = new YIntervalSeries(project.getLabel());
seriesCollection.addSeries(projectSeries);
projectSeriesMap.put(project, projectSeries);
renderer.setSeriesShape(seriesIndex, new Rectangle());
renderer.setSeriesStroke(seriesIndex, new BasicStroke(3.0f));
seriesIndexByProject.put(project, seriesIndex);
seriesIndex++;
}
for (Allocation allocation : schedule.getAllocationList()) {
int startDate = allocation.getStartDate();
int endDate = allocation.getEndDate();
YIntervalSeries projectSeries = projectSeriesMap.get(allocation.getProject());
int column = projectSeries.getItemCount();
executionModeByRowAndColumn[seriesIndexByProject.get(allocation.getProject())][column] = allocation.getExecutionMode();
logger.info("ExecutionMode [{},{}] = {}", seriesIndexByProject.get(allocation.getProject()), column, allocation.getExecutionMode());
projectSeries.add(allocation.getId(), (startDate + endDate) / 2.0,
startDate, endDate);
maximumEndDate = Math.max(maximumEndDate, endDate);
}
NumberAxis domainAxis = new NumberAxis("Job");
domainAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
domainAxis.setRange(-0.5, schedule.getAllocationList().size() - 0.5);
domainAxis.setInverted(true);
NumberAxis rangeAxis = new NumberAxis("Day (start to end date)");
rangeAxis.setRange(-0.5, maximumEndDate + 0.5);
XYPlot plot = new XYPlot(seriesCollection, domainAxis, rangeAxis, renderer);
plot.setOrientation(PlotOrientation.HORIZONTAL);
// Uncomment this to use Tango color sequence instead of JFreeChart default sequence.
// This results in color per project mode.
// DefaultDrawingSupplier drawingSupplier = new DefaultDrawingSupplier(
// TangoColorFactory.SEQUENCE_1,
// DefaultDrawingSupplier.DEFAULT_FILL_PAINT_SEQUENCE,
// DefaultDrawingSupplier.DEFAULT_OUTLINE_PAINT_SEQUENCE,
// DefaultDrawingSupplier.DEFAULT_STROKE_SEQUENCE,
// DefaultDrawingSupplier.DEFAULT_OUTLINE_STROKE_SEQUENCE,
// DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE);
// plot.setDrawingSupplier(drawingSupplier);
return new JFreeChart("Project Job Scheduling", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
}
}
Result:
Another approach would be to implement JFreeChart interfaces and make custom Dataset and Renderer so that you could plot Allocations directly. Similar to the Gantt chart implementaion in JFreeChart.
Or write your custom UI from the ground up. Depends op how much effort you're willing to put into it :)

how to display customized values on a bar in bar chart using jfree chart?

By using StandardCategoryItemLabelGenerator() Iam able to display the exact range axis value on a bar, Now my problem is I dont want to display the range axis value instead I want to display some other value of that bar How I can achieve that?
for (String IDS : idMap.keySet()) {
List<String> listValues = idMap.get(IDS);
if(listValues != null && listValues.get(1) != null) {
dataSet.setValue(Double.valueOf(listValues.get(1)), "", IDS);
}
}
JFreeChart chart = ChartFactory.createBarChart3D("", "", "Time taken ", dataSet,PlotOrientation.VERTICAL, true, true, false);
chart.setTitle(new org.jfree.chart.title.TextTitle("Time Duration",new java.awt.Font("SansSerif", java.awt.Font.BOLD, 10)));
chart.getLegend().setItemFont(new java.awt.Font("SansSerif",0,7));
chart.removeLegend();
final CategoryPlot plot = chart.getCategoryPlot();
plot.setNoDataMessage("No data available");
final CategoryItemRenderer renderer = plot.getRenderer();
renderer.setItemLabelsVisible(true);
final BarRenderer r = (BarRenderer) renderer;
r.setMaximumBarWidth(0.05);
r.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
r.setBaseItemLabelsVisible(true);
r.setSeriesItemLabelFont(0, new java.awt.Font("Times New Roman",Font.NORMAL,7));
r.setSeriesPositiveItemLabelPosition(0,
new ItemLabelPosition(ItemLabelAnchor.OUTSIDE3, TextAnchor.BOTTOM_LEFT, TextAnchor.BOTTOM_LEFT, -Math.PI/2));
final ValueAxis rangeAxis = plot.getRangeAxis();
rangeAxis.setLabelFont(new java.awt.Font("SansSerif", java.awt.Font.BOLD, 8));
final CategoryAxis axis = plot.getDomainAxis();
axis.setLabel("ids");
axis.setLabelFont(new java.awt.Font("SansSerif", java.awt.Font.BOLD, 8));
axis.setCategoryLabelPositions(CategoryLabelPositions.createUpRotationLabelPositions(Math.PI / 8.0));
axis.setTickLabelFont(new java.awt.Font("SansSerif", java.awt.Font.BOLD, 6));
plot.setDomainGridlinesVisible(true);
plot.setRangeGridlinesVisible(true);
By using the above code iam able to display exact range axis value on a bar, but I dont want the range axis value I need some other value listValues.get(2) to be displayed on a bar How I can achieve that?
StandardCategoryItemLabelGenerator uses an instance of MessgeFormat for this, defaulting to ArgumentIndex {2}. The other ArgumentIndex values are defined in the abstract parent. For example,
renderer.setBaseItemLabelGenerator(
new StandardCategoryItemLabelGenerator(
"{0} {1} {2} {3}", NumberFormat.getInstance()));
renderer.setBaseItemLabelsVisible(true);
You can also override generateLabel() to return any value you want.
renderer.setBaseItemLabelGenerator(
new StandardCategoryItemLabelGenerator()
#Override
public String generateLabel(CategoryDataset dataset, int row, int column) {
return "Your Text" +row+","+column;
}
);

Plotting time in milliseconds - JFreeChart (Step Chart)

I'm trying to plot a step chart with the following properties:
x-axis: Time (ms) [Actual data contains this as a double value]
y-axis: Another value stored as an integer.
I'm filling up the dataset as follows:
private XYSeries populateStepChartDataSet(HashMap<Double, Integer> dataGrid){
XYSeries xySeries = new XYSeries("Step Plot", true, true);
if(dataGrid != null){
for (Double timeStamp : dataGrid.keySet()) {
xySeries.add(timeStamp, dataGrid.get(timeStamp));
}
}
return xySeries;
}
And the section where I create the plot is as follows:
final XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(populateStepChartDataSet(dspDataGrid));
final JFreeChart chart = ChartFactory.createXYStepChart(
title,
xAxisLabel, yAxisLabel,
dataset,
PlotOrientation.VERTICAL,
true, // legend
true, // tooltips
false // urls
);
What I expect is the plot to show time in ms at the x-axis but this value is getting converted to some weird time. Here's how the plot looks
Can someone please help me get back the timestamp in ms format for the x-axis?
It looks like the x Axis is formatting as a date one way of adressing this is to provide a NumberFormatOverride
Add this code after your chart is created:
XYPlot plot = (XYPlot)chart.getPlot();
plot.setDomainAxis(0, new NumberAxis());
NumberAxis axis = (NumberAxis) plot.getDomainAxis();
axis.setNumberFormatOverride( new NumberFormat(){
#Override
public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
return new StringBuffer(String.format("%f", number));
}
#Override
public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
return new StringBuffer(String.format("%9.0f", number));
}
#Override
public Number parse(String source, ParsePosition parsePosition) {
return null;
}
} );
axis.setAutoRange(true);
axis.setAutoRangeIncludesZero(false);
You shold then get this chart:
FYI, use entrySet() whenever you iterate through a Map instead of iterating through the keySet() and then getting the value for each key.

Jfree Using custom names to categories

I'm very new to Jfreechart, Please help me to get the solution for this, I'm using jfree to draw a graph for a categories which has specific code names like 563,258.855,etc... So i want to assign these values as labels to the domain axis, but by default it is assigning value1,value2,value3,etc... to categories.
But I wan to make it something like below
and here is my code,
final CategoryDataset dataset1 = DatasetUtilities.createCategoryDataset("Month to Date Occurences","value", data);
JFreeChart dualchart = ChartFactory.createBarChart(
"Top Ten Diagnostic Occurences", // chart title
"Category", // domain axis label
"Score", // range axis label
dataset1, // data
PlotOrientation.HORIZONTAL, // orientation
true, // include legend
true,
false
);
CategoryPlot plot = dualchart.getCategoryPlot();
plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);
plot.setBackgroundPaint(Color.WHITE);
plot.setRangeGridlinePaint(Color.black);
BarRenderer renderer= (BarRenderer) plot.getRenderer();
renderer.setItemMargin(0.0);
CategoryAxis domainAxis = plot.getDomainAxis ();
domainAxis.setCategoryMargin(0.30); //distance between series
NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setRange(0.0, 100.0);
rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
return dualchart;
Your suggestions are most welcome,
Thanks in advance.
You haven't included the code for dataset1 so it's hard to tell what the problem is but if you use something like this:
private static CategoryDataset createDataset() {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(15, "1", "451");
dataset.addValue(12, "1", "851");
dataset.addValue(10, "2", "362");
dataset.addValue(5, "2", "142");
return dataset;
}
You will get a chart like this:

Jfreechart get Mouse Co-ordinates

I have been trying to get current Mouse Co-ordinates in a JfreeChart and found that the following solution was working partially
JFreeChart get mouse coordinates
I have been using OHLC Dataset to draw the chart and while I could get the RangeAxis properly (meaning in the subplot values), I couldn't make anything out of the value recieved for the X-Axis from the above example.
I am sure that I am receiving the values in some other format (not the displayed date format), anyone could point out what I am doing wrong?
Got it solved after few hours of experiment. Here is the code for a complete MouseMotionListener. Just added this to the chartPanel and voila! - it works!
The chartY returns proper value of Y-Axis and the dateString returns complete date. Tried it in an OHLC Chart and seems proper.
MouseMotionListener mouselisten = new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
//
}
public void mouseMoved(MouseEvent e) {
Point2D p = e.getPoint();
Rectangle2D plotArea = chartPanel.getScreenDataArea();
XYPlot plot = (XYPlot) chart.getPlot(); // your plot
double chartX = plot.getDomainAxis().java2DToValue(p.getX(), plotArea, plot.getDomainAxisEdge());
double chartY = plot.getRangeAxis().java2DToValue(p.getY(), plotArea, plot.getRangeAxisEdge());
DecimalFormat dfT = new DecimalFormat("00");
GregorianCalendar gc = new GregorianCalendar();
long lDte = (long)chartX;
Date dtXX = new Date(lDte);
gc.setTime(dtXX);
String sDD = dfT.format(Double.valueOf(String.valueOf(gc.get(GregorianCalendar.DAY_OF_MONTH))));
String sMM = dfT.format(Double.valueOf(String.valueOf(gc.get(GregorianCalendar.MONTH)+1)));
String sYY = dfT.format(Double.valueOf(String.valueOf(gc.get(GregorianCalendar.YEAR))));
String dateString = sDD +"/"+ sMM +"/"+ sYY;
}
};

Resources