JFreeChart MouseListener doesn't resolve chart elements - jfreechart

I've got stacked bar chart where I want to be able to select individual bars in the stack. But ChartMouseListener doesn't resolve ChartMouseEvent into corresponding ChartEntity. Here's the listener snippet :
public void chartMouseClicked(ChartMouseEvent event){
ChartEntity entity = event.getEntity();
if(entity != null && (entity instanceof XYItemEntity) ){
XYItemEntity item = (XYItemEntity)entity;
renderer.select(item.getSeriesIndex(), item.getItem());
return;
}
// deselect
renderer.select(-1,-1);
}
The problem is that event.getEntity() returns null when I am obviously clicking on some of the bars. Note that NOT all the bars fail. The further I go to the right end of the chart, the more obvious is the shift in coordinates. Snap shot below showing that selected bar actually appears when clicking outside of it. I am using JFreeChart within SWT composite. Can anyone confirm that this is a buggy behavior or is there a workaround?
Below is complete sscce, after you run it and click on bars - it will show up pinky. Then re-size the window and try to select bars - it will miss. And I think the miss is the function of the new size.
import java.awt.Color;
import java.awt.Paint;
import java.util.Random;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickMarkPosition;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.XYItemEntity;
import org.jfree.chart.event.RendererChangeEvent;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StackedXYBarRenderer;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.data.time.Day;
import org.jfree.data.time.Hour;
import org.jfree.data.time.TimeTableXYDataset;
import org.jfree.data.xy.TableXYDataset;
import org.jfree.experimental.chart.swt.ChartComposite;
public class StackedChartSwt {
private StackedRenderer renderer;
private Color[] colors = new Color[]{
new Color(230,240,255),
new Color(240,255,240),
new Color(255,255,255),
new Color(255,255,240),
new Color(255,240,240),
new Color(240,240,240)
};
public StackedChartSwt(){
Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
JFreeChart chart = createStackedChart(createStackedDataset());
ChartComposite chartComposite = new ChartComposite(shell, SWT.NONE, chart, false, false, false, false, false);
chartComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
chartComposite.setRangeZoomable(false);
chartComposite.setMenu(null);
chartComposite.addChartMouseListener(new ThisMouseListener());
shell.setSize(800, 600);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
private JFreeChart createStackedChart(TableXYDataset tablexydataset) {
DateAxis dateaxis = new DateAxis();
dateaxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
NumberAxis numberaxis = new NumberAxis("Event counts");
renderer = new StackedRenderer();
XYPlot plot = new XYPlot(tablexydataset, dateaxis, numberaxis, renderer);
plot.setBackgroundPaint(Color.white);
plot.setDomainGridlinePaint(Color.lightGray);
plot.setDomainGridlinesVisible(true);
plot.setRangeGridlinesVisible(true);
plot.setRangeGridlinePaint(Color.lightGray);
JFreeChart chart = new JFreeChart(null, plot);
chart.setBackgroundPaint(Color.white);
chart.setBorderVisible(false);
chart.setBorderPaint(null);
return chart;
}
class StackedRenderer extends StackedXYBarRenderer{
int selectedRow=-1, selectedCol=-1;
public StackedRenderer(){
setDrawBarOutline(true);
setBarPainter(new StandardXYBarPainter());
setShadowVisible(false);
setSeriesPaint(0, Color.blue);
setMargin(0.2);
}
public void select(int row, int col){
selectedRow = row;
selectedCol = col;
notifyListeners(new RendererChangeEvent(this));
}
#Override
public Paint getItemPaint(final int row, final int col){
if(row == selectedRow && col == selectedCol)
return Color.pink;
return colors[row];
}
}
class ThisMouseListener implements ChartMouseListener{
public void chartMouseMoved(ChartMouseEvent event){
}
public void chartMouseClicked(ChartMouseEvent event){
ChartEntity entity = event.getEntity();
if(entity != null && (entity instanceof XYItemEntity) ){
XYItemEntity item = (XYItemEntity)entity;
renderer.select(item.getSeriesIndex(), item.getItem());
return;
}
// deselect
renderer.select(-1,-1);
}
}
private TableXYDataset createStackedDataset(){
Random random = new Random(0);
TimeTableXYDataset ds = new TimeTableXYDataset();
Day day = new Day();
for( int i = 0; i < 24; i++ ){
Hour hour = new Hour( i, day );
ds.add(hour, random.nextInt( 20 ), "A");
ds.add(hour, random.nextInt( 20 ), "B");
ds.add(hour, random.nextInt( 20 ), "C");
ds.add(hour, random.nextInt( 20 ), "D");
ds.add(hour, random.nextInt( 20 ), "E");
ds.add(hour, random.nextInt( 20 ), "F");
}
return ds;
}
public static void main(String[] args){
new StackedChartSwt();
}
}

Your exemplary renderer has the correct geometry when run from Swing, as shown below. I'm unsure why things are awry with SWT, but this result may narrow the search.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Paint;
import java.util.Random;
import javax.swing.JFrame;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickMarkPosition;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.entity.XYItemEntity;
import org.jfree.chart.event.RendererChangeEvent;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StackedXYBarRenderer;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.data.time.Day;
import org.jfree.data.time.Hour;
import org.jfree.data.time.TimeTableXYDataset;
import org.jfree.data.xy.TableXYDataset;
public class StackedChartSwing {
private ChartPanel panel;
private StackedRenderer renderer;
private Color[] colors = new Color[]{
new Color(230, 240, 255),
new Color(240, 255, 240),
new Color(255, 255, 255),
new Color(255, 255, 240),
new Color(255, 240, 240),
new Color(240, 240, 240)
};
public StackedChartSwing() {
JFreeChart chart = createStackedChart(createStackedDataset());
panel = new ChartPanel(chart);
panel.addChartMouseListener(new ThisMouseListener());
}
private JFreeChart createStackedChart(TableXYDataset tablexydataset) {
DateAxis dateaxis = new DateAxis();
dateaxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
NumberAxis numberaxis = new NumberAxis("Event counts");
renderer = new StackedRenderer();
XYPlot plot = new XYPlot(tablexydataset, dateaxis, numberaxis, renderer);
plot.setBackgroundPaint(Color.white);
plot.setDomainGridlinePaint(Color.lightGray);
plot.setDomainGridlinesVisible(true);
plot.setRangeGridlinesVisible(true);
plot.setRangeGridlinePaint(Color.lightGray);
JFreeChart chart = new JFreeChart(null, plot);
chart.setBackgroundPaint(Color.white);
chart.setBorderVisible(false);
chart.setBorderPaint(null);
return chart;
}
class StackedRenderer extends StackedXYBarRenderer {
int selectedRow = -1, selectedCol = -1;
public StackedRenderer() {
setDrawBarOutline(true);
setBarPainter(new StandardXYBarPainter());
setShadowVisible(false);
setSeriesPaint(0, Color.blue);
setMargin(0.2);
}
public void select(int row, int col) {
selectedRow = row;
selectedCol = col;
notifyListeners(new RendererChangeEvent(this));
}
#Override
public Paint getItemPaint(final int row, final int col) {
if (row == selectedRow && col == selectedCol) {
return Color.pink;
}
return colors[row];
}
}
class ThisMouseListener implements ChartMouseListener {
#Override
public void chartMouseMoved(ChartMouseEvent event) {
}
#Override
public void chartMouseClicked(ChartMouseEvent event) {
ChartEntity entity = event.getEntity();
if (entity != null && (entity instanceof XYItemEntity)) {
XYItemEntity item = (XYItemEntity) entity;
renderer.select(item.getSeriesIndex(), item.getItem());
return;
}
// deselect
renderer.select(-1, -1);
}
}
private TableXYDataset createStackedDataset() {
Random random = new Random(0);
TimeTableXYDataset ds = new TimeTableXYDataset();
Day day = new Day();
for (int i = 0; i < 24; i++) {
Hour hour = new Hour(i, day);
ds.add(hour, random.nextInt(20), "A");
ds.add(hour, random.nextInt(20), "B");
ds.add(hour, random.nextInt(20), "C");
ds.add(hour, random.nextInt(20), "D");
ds.add(hour, random.nextInt(20), "E");
ds.add(hour, random.nextInt(20), "F");
}
return ds;
}
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(panel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new StackedChartSwing().display();
}
});
}
}

OK, I've found the problem. In SWT there are several constructors to create ChartComposite, most of the use defaults which specify min/max width and height. In my case, whenever I've enlarged the chart above DEFAULT_MAXIMUM_DRAW_WIDTH (800px) - the coordinates start to get messy. So, the solution is to use full constructor and specify relevant boundaries for your display. It was a tough one to crack... mainly due to lack of proper documentation.

Related

A trouble with Uber Clone

Hi Guys: I´m trying with Uber Clone code and I´m using Netbeans . I have two questions:
1).- In the countryPickerForm Listing 4.12 (The Listing number is the book´s listing "Create an Uber Clone..."); Netbeans marks me an error,("Cannot find symbol variable CommonCode"), of course, in the CommonCode object, i don´t know what library to use
´´´
public class CountryPickerForm extends Form{
//#SuppressWarnings("LeakingThisInConstructor")
public CountryPickerForm(Button sourceButton, Resources Flag){
super(BoxLayout.y());
**CommonCode.initBlackTitleForm(this,"Select a Country", val-> search(val));**
Image blankIcon = Image.createImage(100, 70, 0);
´´´
2).- And the second question: What is te correct place to the Listing (5.22) "Toogling the "WhereTo?" UI when focus changes". I placed it Inside the MapForm class outside from any method, but Neatbeans marks me an error: "< identifier > expected. Ilegal start of type "
This is the code:
from.addFocusListener(new FocusListener(){
public void focusGained(Component cmp){
fromSelected.setIcon(square);
if(layer.getComponentCount()> 1){
Component c = layer.getComponentAt(1);
c.setY(getDisplayHeight());
layer.animateUnlayout(200,150,() ->{
c.remove();
revalidate();
});
}
}
public void focusLost(){
fromSelected.setIcon(circle);
}
});
to.addFocusListener(new FocusListener(){
public void focusGained(Component cmp){
fromSelected.setIcon(circle):
toSelected.setIcon(square);
showToNavigationBar(layer);
}
public void focusLost(Component cmp){
toSelecte3dsetIcon(circle);
}
});
Thanks Guys!!!!
CommonCode was somehow lost in one of the edits to the book. It's a part of the downloadable zip listed at the start of the book and should be there. This is the full listing of that class:
package com.codename1.apps.uberclone.forms;
import com.codename1.apps.uberclone.server.UserService;
import com.codename1.components.MultiButton;
import com.codename1.io.Log;
import com.codename1.ui.Button;
import static com.codename1.ui.CN.*;
import com.codename1.ui.Command;
import com.codename1.ui.Container;
import com.codename1.ui.Display;
import com.codename1.ui.FontImage;
import com.codename1.ui.Form;
import com.codename1.ui.Graphics;
import com.codename1.ui.Image;
import com.codename1.ui.Label;
import com.codename1.ui.TextField;
import com.codename1.ui.Toolbar;
import com.codename1.ui.animations.CommonTransitions;
import com.codename1.ui.animations.Transition;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.events.ActionListener;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.layouts.LayeredLayout;
import com.codename1.ui.plaf.Style;
import com.codename1.util.LazyValue;
import com.codename1.util.SuccessCallback;
import java.io.IOException;
/**
* Common code for construction and initialization of various classes e.g. the side menu logic etc.
*
* #author Shai Almog
*/
public class CommonCode {
private static Image avatar;
public static Image getAvatar(SuccessCallback<Image> avatarChanged) {
if(avatar == null) {
int size = convertToPixels(10);
Image temp = Image.createImage(size, size, 0xff000000);
Graphics g = temp.getGraphics();
g.setAntiAliased(true);
g.setColor(0xffffff);
g.fillArc(0, 0, size, size, 0, 360);
Object mask = temp.createMask();
UserService.fetchAvatar(i -> {
avatar = i.fill(size, size).applyMask(mask);
avatarChanged.onSucess(avatar);
});
if(avatar != null) {
return avatar;
}
Style s = new Style();
s.setFgColor(0xc2c2c2);
s.setBgTransparency(255);
s.setBgColor(0xe9e9e9);
FontImage x = FontImage.createMaterial(FontImage.MATERIAL_PERSON, s, size);
avatar = x.fill(size, size);
if(avatar instanceof FontImage) {
avatar = ((FontImage)avatar).toImage();
}
avatar = avatar.applyMask(mask);
}
return avatar;
}
public static Image setAvatar(String imageFile) {
int size = convertToPixels(10);
Image temp = Image.createImage(size, size, 0xff000000);
Graphics g = temp.getGraphics();
g.setAntiAliased(true);
g.setColor(0xffffff);
g.fillArc(0, 0, size, size, 0, 360);
Object mask = temp.createMask();
try {
Image img = Image.createImage(imageFile);
avatar = img.fill(size, size).applyMask(mask);
} catch(IOException err) {
// this is unlikely as we just grabbed the image...
Log.e(err);
}
return avatar;
}
public static MultiButton createEntry(char icon, String title) {
MultiButton b = new MultiButton(title);
b.setUIID("Container");
b.setUIIDLine1("WhereToButtonLine1");
b.setIconUIID("WhereToButtonIcon");
FontImage.setMaterialIcon(b, icon);
return b;
}
public static MultiButton createEntry(char icon, String title, String subtitle) {
MultiButton b = new MultiButton(title);
b.setTextLine2(subtitle);
b.setUIID("Container");
b.setUIIDLine1("WhereToButtonLineNoBorder");
b.setUIIDLine2("WhereToButtonLine2");
b.setIconUIID("WhereToButtonIcon");
FontImage.setMaterialIcon(b, icon);
return b;
}
public static Label createSeparator() {
Label sep = new Label("", "WhereSeparator");
sep.setShowEvenIfBlank(true);
return sep;
}
public static void constructSideMenu(Toolbar tb) {
Button userAndAvatar = new Button("Shai Almog", "AvatarBlock");
userAndAvatar.setIcon(getAvatar(i -> userAndAvatar.setIcon(i)));
userAndAvatar.setGap(convertToPixels(3));
userAndAvatar.addActionListener(e -> new EditAccountForm().show());
tb.addComponentToSideMenu(userAndAvatar);
MultiButton uberForBusiness = new MultiButton("Do you Uber for business?");
uberForBusiness.setTextLine2("Tap to create your business profile");
uberForBusiness.setUIID("UberForBusinessBackground");
uberForBusiness.setUIIDLine1("UberForBusinessLine1");
uberForBusiness.setUIIDLine2("UberForBusinessLine2");
tb.addComponentToSideMenu(uberForBusiness);
tb.addCommandToSideMenu("Payment", null, e -> {});
tb.addCommandToSideMenu("Your Trips", null, e -> {});
tb.addCommandToSideMenu("Help", null, e -> {});
tb.addCommandToSideMenu("Free Rides", null, e -> {});
tb.addCommandToSideMenu("Settings", null, e -> new SettingsForm().show());
Button legalButton = new Button("Legal", "Legal");
Container legal = BorderLayout.centerCenterEastWest(null, new Label("v4.178.1001", "VersionNumber"), legalButton);
legal.setLeadComponent(legalButton);
legal.setUIID("SideNavigationPanel");
tb.setComponentToSideMenuSouth(legal);
}
/**
* Initializes a form with a black background title animation style
* #param f the form
*/
public static void initBlackTitleForm(Form f, String title, SuccessCallback<String> searchResults) {
Form backTo = getCurrentForm();
f.getContentPane().setScrollVisible(false);
Button back = new Button("", "TitleCommand");
removeTransitionsTemporarily(backTo);
back.addActionListener(e -> backTo.showBack());
back.getAllStyles().setFgColor(0xffffff);
FontImage.setMaterialIcon(back, FontImage.MATERIAL_ARROW_BACK);
f.setBackCommand(new Command("") {
#Override
public void actionPerformed(ActionEvent evt) {
backTo.showBack();
}
});
Container searchBack = null;
if(searchResults != null) {
Button search = new Button("", "TitleCommand");
search.getAllStyles().setFgColor(0xffffff);
FontImage.setMaterialIcon(search, FontImage.MATERIAL_SEARCH);
search.addActionListener(e -> {
});
searchBack = BorderLayout.north(
BorderLayout.centerEastWest(null, search, back));
} else {
searchBack = BorderLayout.north(
BorderLayout.centerEastWest(null, null, back));
}
Label titleLabel = new Label(title, "WhiteOnBlackTitle");
titleLabel.getAllStyles().setMarginTop(back.getPreferredH());
titleLabel.getAllStyles().setMarginUnit(Style.UNIT_TYPE_PIXELS, Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS);
f.getToolbar().setTitleComponent(LayeredLayout.encloseIn(searchBack, titleLabel));
f.getAnimationManager().onTitleScrollAnimation(titleLabel.createStyleAnimation("WhiteOnBlackTitleLeftMargin", 200));
f.setTransitionInAnimator(CommonTransitions.createCover(CommonTransitions.SLIDE_VERTICAL, false, 300));
f.setTransitionOutAnimator(CommonTransitions.createUncover(CommonTransitions.SLIDE_VERTICAL, true, 300));
}
public static void removeTransitionsTemporarily(final Form f) {
final Transition originalOut = f.getTransitionOutAnimator();
final Transition originalIn = f.getTransitionInAnimator();
f.setTransitionOutAnimator(CommonTransitions.createEmpty());
f.setTransitionInAnimator(CommonTransitions.createEmpty());
f.addShowListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
f.setTransitionOutAnimator(originalOut);
f.setTransitionInAnimator(originalIn);
f.removeShowListener(this);
}
});
}
}
The second listing appears in MapForm in showNavigationToolbar. This code goes through some additional refactoring later on and looks like this:
void showNavigationToolbar() {
final Container layer = getLayeredPane(MapForm.class, true);
final Container pinLayer = createPinLayer(layer);
Button back = new Button("", "TitleCommand");
FontImage.setMaterialIcon(back, FontImage.MATERIAL_ARROW_BACK);
CompletionContainer cc = new CompletionContainer();
AutoCompleteAddressInput from = new AutoCompleteAddressInput("Current Location", "From", layer, cc);
AutoCompleteAddressInput to = new AutoCompleteAddressInput("", "Where To?", layer, cc);
from.setCurrentLocation(LocationService.getCurrentLocation());
Image circle = createCircle();
Label fromSelected = new Label(circle);
Label toSelected = new Label(square);
SearchService.nameMyCurrentLocation(LocationService.getCurrentLocation(), name -> from.setTextNoEvent(name));
to.requestFocus();
lastFocused = to;
from.addFocusListener(createFromFocusListener(fromSelected, from, circle));
to.addFocusListener(createToFocusListener(fromSelected, circle, toSelected, to));
addMapListener((source, zoom, center) -> onMapChangeEvent(center));
Container navigationToolbar = BoxLayout.encloseY(back,
BorderLayout.centerCenterEastWest(from, null, fromSelected),
BorderLayout.centerCenterEastWest(to, null, toSelected)
);
navigationToolbar.setUIID("WhereToToolbar");
navigationToolbar.getUnselectedStyle().setBgPainter((g1, rect) ->
paintWhereToToolbarBackground(g1, layer, rect, fromSelected, circle, toSelected)
);
cc.addCompletionListener(e ->
onCompletionEvent(to, from, pinLayer, navigationToolbar, layer));
back.addActionListener(e ->
onBackFromNavigation(pinLayer, navigationToolbar, layer));
layer.add(NORTH, navigationToolbar);
navigationToolbar.setWidth(getDisplayWidth());
navigationToolbar.setHeight(getPreferredH());
navigationToolbar.setY(-navigationToolbar.getHeight());
getAnimationManager().addAnimation(layer.createAnimateLayout(200),
() -> cc.showCompletionBar(layer));
}
private FocusListener createToFocusListener(final Label fromSelected, Image circle, final Label toSelected, AutoCompleteAddressInput to) {
return new FocusListener() {
#Override
public void focusGained(Component cmp) {
fromSelected.setIcon(circle);
toSelected.setIcon(square);
lastFocused = to;
}
#Override
public void focusLost(Component cmp) {
toSelected.setIcon(circle);
}
};
}
private FocusListener createFromFocusListener(final Label fromSelected, AutoCompleteAddressInput from, Image circle) {
return new FocusListener() {
#Override
public void focusGained(Component cmp) {
fromSelected.setIcon(square);
lastFocused = from;
}
#Override
public void focusLost(Component cmp) {
fromSelected.setIcon(circle);
}
};
}

JFreeChart get getWidth() returns 0.0

I' m using the ChartPanel class from JFreeChart and want to know the width of the plot's data area.
The returned value from the method chartPanel.getChartRenderingInfo().getPlotInfo().getDataArea().getWidth() is 0.0d.
I'm using the JFreeChart version 1.5.0.
My suspicion was a thread-problem, which means that the plotting isn't finished, when I call the .getWidth()-method. So I tried to use ChartChange events, but the result is always a width of 0.0 pixels.
Here is a short example class I created to Show the Problem:
The println in the main() gives "xAxisLengthInPixels = 0.0":
package jFreeChartTest;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.event.ChartChangeEvent;
import org.jfree.chart.event.ChartChangeListener;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
public class Panel_Chart extends JFrame implements ChartChangeListener {
private ChartPanel chartPanel = null;
private int width = 1024;
private int height = 820;
public Panel_Chart() {
this.setTitle("XY Plot");
setBounds(0, 0, width, height);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
XYSeries testXYSeries = new XYSeries("Test-Series", true);
testXYSeries.add(4,10);
testXYSeries.add(5,20);
testXYSeries.add(6,30);
xySeriesCollection.addSeries(testXYSeries);
boolean includeLegend = true;
boolean useTooltips = true;
boolean useUrls = true;
JFreeChart chart = ChartFactory.createScatterPlot(//createXYLineChart(
"",
"X-Axis",
"Y-Axis",
xySeriesCollection,
PlotOrientation.VERTICAL,
includeLegend,
useTooltips,
useUrls);
this.chartPanel = new ChartPanel(chart);
getContentPane().add(chartPanel, BorderLayout.PAGE_START);
}
public Double getXAxisLengthInPixels(){
return chartPanel.getChartRenderingInfo().getPlotInfo().getDataArea().getWidth();
}
#Override
public void chartChanged(ChartChangeEvent arg0) {
// TODO Auto-generated method stub
System.out.println("ChartChangeEvent:" + arg0);
}
public static void main(String[] args){
Panel_Chart panelChart = new Panel_Chart();
panelChart.setVisible(true);
System.out.println("xAxisLengthInPixels = " + panelChart.getXAxisLengthInPixels());
}
}

CodenameOne Charts not rendering well when sharing or simulator screen capture

The recent updates draw the lines on the charts when sharing, but the chart seems to get a bit mangled both when shared and when doing a screen capture on the simulator. The legend gets rotated and moved into the grid area, the title is missing, and there is a yellow outline that runs through the grid area.
Different chart renderings. The far right image got mirrored somehow when I pasted it, it isn't actually a mirror-image when shared.
Here is a test case:
package com.fastlaneinnovations.chartexample;
import com.codename1.charts.ChartComponent;
import com.codename1.charts.models.XYMultipleSeriesDataset;
import com.codename1.charts.models.XYSeries;
import com.codename1.charts.renderers.XYMultipleSeriesRenderer;
import com.codename1.charts.renderers.XYSeriesRenderer;
import com.codename1.charts.util.ColorUtil;
import com.codename1.charts.views.LineChart;
import com.codename1.components.ShareButton;
import com.codename1.io.FileSystemStorage;
import com.codename1.io.Log;
import com.codename1.ui.Container;
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.Image;
import com.codename1.ui.Label;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.events.ActionListener;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.ImageIO;
import com.codename1.ui.util.Resources;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Hashtable;
import java.util.Set;
public class ChartExample {
private Form current;
private Resources theme;
private XYMultipleSeriesRenderer chartRenderer;
private Container rpmChartContainer;
private XYSeries series;
private LineChart lineChart;
private ChartComponent chart;
private Hashtable<Long, Integer> chartData;
private String chartName = "Chart";
private String path;
private Object os;
private int chartHeight;
private int chartWidth;
private ShareButton share;
private Set<Long> keys;
public void init(Object context) {
theme = UIManager.initFirstTheme("/theme");
// Pro only feature, uncomment if you have a pro subscription
// Log.bindCrashProtection(true);
}
public void start() {
if (current != null) {
current.show();
return;
}
Form form = new Form();
form.setLayout(new BorderLayout());
chartData = new Hashtable<Long, Integer>();
for (int i = 0; i < 8000; i += 100) {
chartData.put(Long.valueOf(i), Integer.valueOf(i));
}
rpmChartContainer = new Container();
rpmChartContainer.setLayout(new BorderLayout());
series = new XYSeries("RPM");
chartRenderer = createChartRenderer();
setChartSettings(chartRenderer, "RPM vs Time", "Time (s)", "RPM", 0.0,
10.0, 0.0, 8000.0, ColorUtil.YELLOW, ColorUtil.WHITE);
lineChart = new LineChart(buildDataSet(series), chartRenderer);
chart = new ChartComponent(lineChart);
chart.setUIID("ChartComponent");
rpmChartContainer.addComponent(BorderLayout.CENTER, chart);
updateChart();
share = new ShareButton();
share.setName(chartName);
share.setText("Share this chart");
share.setTextToShare("RPM chart created by R-P-M Control Center app");
rpmChartContainer.add(BorderLayout.SOUTH, share);
form.add(BorderLayout.CENTER, rpmChartContainer);
form.show();
createShareButton();
}
public void stop() {
current = Display.getInstance().getCurrent();
}
public void destroy() {
}
public void updateChart() {
keys = chartData.keySet();
series.clear();
for (Long key : keys) {
series.add((key / 1000.0), (chartData.get(key) / 1.0));
}
}
private XYMultipleSeriesRenderer createChartRenderer() {
XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
renderer.setPointSize(3f);
XYSeriesRenderer seriesRenderer = new XYSeriesRenderer();
seriesRenderer.setColor(ColorUtil.GREEN);
// seriesRenderer.setPointStyle(PointStyle.CIRCLE);
// seriesRenderer.setFillPoints(true);
seriesRenderer.setLineWidth(5f);
seriesRenderer.setShowLegendItem(true);
renderer.addSeriesRenderer(seriesRenderer);
return renderer;
}
private XYMultipleSeriesDataset buildDataSet(XYSeries dataSeries) {
XYMultipleSeriesDataset multiDataSet = new XYMultipleSeriesDataset();
multiDataSet.addSeries(dataSeries);
return multiDataSet;
}
private void setChartSettings(XYMultipleSeriesRenderer renderer,
String title, String xTitle, String yTitle, double xMin,
double xMax, double yMin, double yMax, int axesColor,
int labelsColor) {
renderer.setChartTitle(title);
renderer.setXTitle(xTitle);
renderer.setYTitle(yTitle);
renderer.setXAxisMin(xMin);
// renderer.setXAxisMax(xMax);
renderer.setYAxisMin(yMin);
renderer.setYAxisMax(yMax);
renderer.setAxesColor(axesColor);
renderer.setLabelsColor(labelsColor);
renderer.setMarginsColor(ColorUtil.BLACK);
renderer.setGridColor(ColorUtil.BLACK);
}
public void createShareButton() {
rpmChartContainer.revalidate();
chartWidth = rpmChartContainer.getWidth();
chartHeight = rpmChartContainer.getHeight();
Image chartAsImage = Image.createImage(chartWidth, chartHeight);
rpmChartContainer.paint(chartAsImage.getGraphics());
setShareImage(share, chartAsImage);
}
public void setShareImage(ShareButton shareButton, Image shareImage) {
OutputStream os = null;
final String shareImagePath = FileSystemStorage.getInstance()
.getAppHomePath() + shareButton.getName();
FileSystemStorage.getInstance().delete(shareImagePath);
try {
os = FileSystemStorage.getInstance().openOutputStream(
shareImagePath);
ImageIO.getImageIO().save(shareImage, os, ImageIO.FORMAT_JPEG,
0.95f);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
os.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
shareButton.setImageToShare(shareImagePath, "image/jpeg");
}
}
In implementing the 2D transformations on iOS mutable images, I fixed some bugs related to transforms on mutable images in the simulator. You may not have these until the next plugin update.
As for the third image on the right in your screenshot. What device was that from? Can you share a test case with your chart so I can test it out?

JfreeChart XYAreaChart draw shape at each point [duplicate]

I am using JFreeChart XYPLot for plotting a XYData set with different labels . I have created different XYSeries objects for different labels so that I can have different colors for different labels . Now I need to require the change the shapes of specific points(test data) in each XYDataSeries as below .
In the above plotting , there are two different XYSeries with blue and red color . Out of these two I need to change the shapes of some points(test data) to X instead of circle . Is it possible in JFreeChart. This post explained on how to do it for whole data set , but I want to change only specific points
Below is the code I have written so far
public static Map<String, XYSeries> createXYSeries(Data[] dataSet){
Map<String,XYSeries> xySeries = new HashMap<String, XYSeries>();
for(Data data : dataSet){
if(xySeries.get(data.actualLabel) == null){
xySeries.put(data.actualLabel, new XYSeries(data.actualLabel));
}
xySeries.get(data.actualLabel).add(data.dimensionValues[0],data.dimensionValues[1]);
}
return xySeries;
}
public XYDataset createXYSeriesCollection(Map<String, XYSeries> plottingDataSet) {
XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
for (String key : plottingDataSet.keySet()) {
xySeriesCollection.addSeries(plottingDataSet.get(key));
}
return xySeriesCollection;
}
private ChartPanel createPlottingPanel(String title,
Map<String, XYSeries> plottingDataSet) {
JFreeChart jfreechart = ChartFactory.createScatterPlot(title, "X", "Y",
createSampleData(plottingDataSet), PlotOrientation.VERTICAL,
true, true, false);
XYPlot xyPlot = (XYPlot) jfreechart.getPlot();
xyPlot.setDomainCrosshairVisible(true);
xyPlot.setRangeCrosshairVisible(true);
xyPlot.setBackgroundPaint(Color.white);
return new ChartPanel(jfreechart);
}
Note : I am trying to plot the KNearestNeighbors results .(Circles for train data and X for test data)
ChartFactory.createScatterPlot() instantiates an XYLineAndShapeRenderer. You can replace the renderer with one that lets you selectively replace the Shape returned by getItemShape(), as shown below.
xyPlot.setRenderer(new XYLineAndShapeRenderer(false, true) {
#Override
public Shape getItemShape(int row, int col) {
if (row == 0 & col == N) {
return ShapeUtilities.createDiagonalCross(5, 2);
} else {
return super.getItemShape(row, col);
}
}
});
Complete example, as run:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Shape;
import java.util.*;
import javax.swing.JFrame;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.util.ShapeUtilities;
/**
* #see http://stackoverflow.com/a/20359200/230513
* #see http://stackoverflow.com/a/6669529/230513
*/
public class ScatterShape extends JFrame {
private static final int N = 8;
private static final int SIZE = 345;
private static final String title = "Scatter Shape Demo";
private static final Random rand = new Random();
private final XYSeries series = new XYSeries("Data");
public ScatterShape(String s) {
super(s);
final ChartPanel chartPanel = createDemoPanel();
this.add(chartPanel, BorderLayout.CENTER);
}
private ChartPanel createDemoPanel() {
JFreeChart chart = ChartFactory.createScatterPlot(
title, "X", "Y", createSampleData(),
PlotOrientation.VERTICAL, true, true, false);
XYPlot xyPlot = (XYPlot) chart.getPlot();
xyPlot.setDomainCrosshairVisible(true);
xyPlot.setRangeCrosshairVisible(true);
xyPlot.setRenderer(new XYLineAndShapeRenderer(false, true) {
#Override
public Shape getItemShape(int row, int col) {
if (row == 0 & col == N) {
return ShapeUtilities.createDiagonalCross(5, 2);
} else {
return super.getItemShape(row, col);
}
}
});
adjustAxis((NumberAxis) xyPlot.getDomainAxis(), true);
adjustAxis((NumberAxis) xyPlot.getRangeAxis(), false);
xyPlot.setBackgroundPaint(Color.white);
return new ChartPanel(chart) {
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
};
}
private void adjustAxis(NumberAxis axis, boolean vertical) {
axis.setRange(-3.0, 3.0);
axis.setTickUnit(new NumberTickUnit(0.5));
axis.setVerticalTickLabels(vertical);
}
private XYDataset createSampleData() {
XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
for (int i = 0; i < N * N; i++) {
series.add(rand.nextGaussian(), rand.nextGaussian());
}
xySeriesCollection.addSeries(series);
return xySeriesCollection;
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ScatterShape demo = new ScatterShape(title);
demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
demo.pack();
demo.setLocationRelativeTo(null);
demo.setVisible(true);
}
});
}
}

JFreeChart Horizontal Charts

I'm using CombinedDomainXYPlot to plot the charts. I have another requirement where, I need to show the two charts horizontally.
Currently i'm having only one chart. what i need is, i need two charts in the first row.
like Chart1 Chart2
Code:
CombinedDomainXYPlot plot = new CombinedDomainXYPlot();
plot.add(getChart1(), 2);
plot.add(getChart2(), 2);
It is giving only one chart in the first row. and second chart2 in the another row.
Is there any way I can make these two charts into single row?
Edit: Actually I wanted it like your ThermometerDemo example. For that you have used JPanel, but here I'm using JFrame.
I wanted it like your ThermometerDemo example.
Based on this example, the code below adds two panels to a GridLayout(1, 0). Each panel includes it's own chart and control panel.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Random;
import javax.swing.*;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/**
* #see https://stackoverflow.com/a/20243624/230513
* #see https://stackoverflow.com/q/11870416/230513
*/
public class CombinedPlot {
private static final int MAX = 3;
private static final Random rand = new Random();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
init();
}
});
}
private static void init() {
JFrame frame = new JFrame("Combined Plot Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(1, 0));
frame.add(createPanel());
frame.add(createPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static JPanel createPanel() {
JPanel p = new JPanel(new BorderLayout());
XYItemRenderer renderer = new StandardXYItemRenderer();
XYPlot plot1 = new XYPlot(
generateData(), null, new NumberAxis("Range 1"), renderer);
XYPlot plot2 = new XYPlot(
generateData(), null, new NumberAxis("Range 2"), renderer);
final CombinedDomainXYPlot plot
= new CombinedDomainXYPlot(new NumberAxis("Domain"));
plot.add(plot1);
plot.add(plot2);
plot.setOrientation(PlotOrientation.VERTICAL);
JFreeChart chart = new JFreeChart(
"Combined Plots", JFreeChart.DEFAULT_TITLE_FONT, plot, false);
ChartPanel chartPanel = new ChartPanel(chart) {
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 320);
}
};
JPanel controlPanel = new JPanel();
controlPanel.add(new JButton(new UpdateAction(plot, 0)));
controlPanel.add(new JButton(new UpdateAction(plot, 1)));
p.add(chartPanel, BorderLayout.CENTER);
p.add(controlPanel, BorderLayout.SOUTH);
return p;
}
private static class UpdateAction extends AbstractAction {
private final XYPlot plot;
public UpdateAction(CombinedDomainXYPlot plot, int i) {
super("Update plot " + (i + 1));
this.plot = (XYPlot) plot.getSubplots().get(i);
}
#Override
public void actionPerformed(ActionEvent e) {
plot.setDataset(CombinedPlot.generateData());
}
}
private static XYSeriesCollection generateData() {
XYSeriesCollection data = new XYSeriesCollection();
for (int i = 0; i < MAX; i++) {
data.addSeries(generateSeries("Series " + (i + 1)));
}
return data;
}
private static XYSeries generateSeries(String key) {
XYSeries series = new XYSeries(key);
for (int i = 0; i < 16; i++) {
series.add(rand.nextGaussian(), rand.nextGaussian());
}
return series;
}
}

Resources