Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package org.knowm.xchart.demo.charts.bubble;

import java.text.DecimalFormat;
import org.knowm.xchart.BubbleChart;
import org.knowm.xchart.BubbleChartBuilder;
import org.knowm.xchart.BubbleSeries;
import org.knowm.xchart.SwingWrapper;
import org.knowm.xchart.XChartPanel;
import org.knowm.xchart.demo.charts.ExampleChart;
import org.knowm.xchart.style.Styler;

/**
* Bubble Chart with Custom Tooltips
*
* <p>Demonstrates the following:
*
* <ul>
* <li>Custom, per-data-point tooltip strings via {@link BubbleSeries#setToolTips(String[])}
* <li>Tooltips always visible
*/
public class BubbleChart02 implements ExampleChart<BubbleChart> {

public static void main(String[] args) {

ExampleChart<BubbleChart> exampleChart = new BubbleChart02();
BubbleChart chart = exampleChart.getChart();
SwingWrapper<BubbleChart> wrapper = new SwingWrapper<>(chart);
wrapper.displayChart();
wrapper.getXChartPanel().setToolTipsEnabled(true);
}

@Override
public BubbleChart getChart() {

// Create Chart
BubbleChart chart =
new BubbleChartBuilder()
.width(800)
.height(600)
.title(getClass().getSimpleName())
.xAxisTitle("Volume")
.yAxisTitle("Rate")
.build();
chart.getStyler().setLegendPosition(Styler.LegendPosition.InsideSW);
chart.getStyler().setLegendLayout(Styler.LegendLayout.Horizontal);
chart.getStyler().setYAxisDecimalPattern("#%");
chart.getStyler().setToolTipsAlwaysVisible(true);

// Series: volume (x), rate (y), number of hits (bubble size)
double[] volume = new double[] {1298, 843, 2755, 1520};
double[] rate = new double[] {0.0215, 0.041, 0.017, 0.033};
double[] hits = new double[] {279, 346, 468, 502};

BubbleSeries series = chart.addSeries("conversion", volume, rate, hits);

// Build a custom tooltip string per data point combining all three values, e.g. "2% (279/1298)"
DecimalFormat percentFormat = new DecimalFormat("#%");
String[] toolTips = new String[volume.length];
for (int i = 0; i < volume.length; i++) {
toolTips[i] =
percentFormat.format(rate[i]) + " (" + (int) hits[i] + "/" + (int) volume[i] + ")";
}
series.setCustomToolTips(true);
series.setToolTips(toolTips);

return chart;
}

@Override
public void customizePanel(XChartPanel<BubbleChart> panel) {

panel.setToolTipsEnabled(true);
}

@Override
public String getExampleChartName() {

return getClass().getSimpleName() + " - Bubble Chart with Custom Tooltips";
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package org.knowm.xchart.standalone.issues;

import java.awt.Font;
import java.text.DecimalFormat;
import java.text.ParseException;

import org.knowm.xchart.BubbleChart;
import org.knowm.xchart.BubbleChartBuilder;
import org.knowm.xchart.BubbleSeries;
import org.knowm.xchart.SwingWrapper;
import org.knowm.xchart.style.BubbleStyler;
import org.knowm.xchart.style.Styler;
Expand All @@ -30,14 +33,18 @@ public static BubbleChart getBubbleChart() {
.yAxisTitle("Rate")
.build();
setBubbleStyler(chart);
chart.addSeries(
"serieName", new double[] {data[0]}, new double[] {data[1]}, new double[] {data[2]});
// bubbleSeries.setCustomToolTips(true);
// String tooltip =
// new DecimalFormat("#%").format(data[1]) + " (" + ((int) data[2]) + "/" + ((int)
// data[0]) + ")";
//
// bubbleSeries.setToolTips(new String[] {tooltip});
BubbleSeries bubbleSeries =
chart.addSeries(
"seriesName", new double[] {data[0]}, new double[] {data[1]}, new double[] {data[2]});
String tooltip =
new DecimalFormat("#%").format(data[1])
+ " ("
+ ((int) data[2])
+ "/"
+ ((int) data[0])
+ ")";
bubbleSeries.setCustomToolTips(true);
bubbleSeries.setToolTips(new String[] {tooltip});

return chart;
}
Expand Down
44 changes: 44 additions & 0 deletions xchart/src/main/java/org/knowm/xchart/BubbleSeries.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ public class BubbleSeries extends NoMarkersSeries {

private Optional<BubbleSeriesRenderStyle> bubbleSeriesRenderStyle = Optional.empty();

/** whether to use the custom, per-data-point tooltip strings instead of the x/y axis values */
private boolean customToolTips;

/** the custom tooltip strings, one per data point, indexed the same as the x/y/size data */
private String[] toolTips;

/**
* Constructor
*
Expand All @@ -34,6 +40,44 @@ public BubbleSeries setBubbleSeriesRenderStyle(BubbleSeriesRenderStyle bubbleSer
return this;
}

public boolean isCustomToolTips() {

return customToolTips;
}

/**
* Set whether to show the custom, per-data-point tooltip strings set via {@link
* #setToolTips(String[])} instead of the default formatted x/y axis values. Requires tooltips to
* be enabled on the styler.
*
* @param customToolTips true to show the per-data-point strings from {@link
* #setToolTips(String[])}; false to show the default formatted x/y axis values
*/
public BubbleSeries setCustomToolTips(boolean customToolTips) {

this.customToolTips = customToolTips;
return this;
}

public String[] getToolTips() {

return toolTips;
}

/**
* Set the custom tooltip strings, one per data point, indexed the same as the x/y/bubble-size
* data. A null entry (or a null array) falls back to the default formatted x/y axis values for
* that data point. Also requires {@link #setCustomToolTips(boolean)} to be set to true.
*
* @param toolTips the tooltip strings, one per data point; a null entry (or a null array) falls
* back to the default formatted x/y axis values for that data point
*/
public BubbleSeries setToolTips(String[] toolTips) {

this.toolTips = toolTips;
return this;
}

@Override
public LegendRenderType getLegendRenderType() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,22 @@ public void doPaint(Graphics2D g) {

// add tooltips
if (interactionData != null) {
interactionData.addToolTip(
bubble,
xOffset,
yOffset,
0,
axesChart.getXAxisFormat().format(x),
axesChart.getYAxisFormat().format(yOrig));

String[] customToolTips = series.getToolTips();
if (series.isCustomToolTips()
&& customToolTips != null
&& i < customToolTips.length
&& customToolTips[i] != null) {
interactionData.addToolTip(bubble, xOffset, yOffset, 0, customToolTips[i]);
} else {
interactionData.addToolTip(
bubble,
xOffset,
yOffset,
0,
axesChart.getXAxisFormat().format(x),
axesChart.getYAxisFormat().format(yOrig));
}
}
}
}
Expand Down
73 changes: 73 additions & 0 deletions xchart/src/test/java/org/knowm/xchart/BubbleChartTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.knowm.xchart;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;

import java.awt.image.BufferedImage;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class BubbleChartTest {

private BubbleChart chart;

@BeforeEach
void setUp() {

chart = new BubbleChartBuilder().title("test").xAxisTitle("X").yAxisTitle("Y").build();
}

// https://github.com/knowm/XChart/issues/545
@Test
void customToolTipsDefaultOff() {

BubbleSeries series =
chart.addSeries("s", new double[] {1, 2}, new double[] {3, 4}, new double[] {5, 6});

assertThat(series.isCustomToolTips()).isFalse();
assertThat(series.getToolTips()).isNull();
}

// https://github.com/knowm/XChart/issues/545
@Test
void customToolTipsRoundTrip() {

BubbleSeries series =
chart.addSeries("s", new double[] {1, 2}, new double[] {3, 4}, new double[] {5, 6});

String[] toolTips = {"2% (279/1298)", "4% (346/843)"};
series.setCustomToolTips(true);
series.setToolTips(toolTips);

assertThat(series.isCustomToolTips()).isTrue();
assertThat(series.getToolTips()).containsExactly("2% (279/1298)", "4% (346/843)");
}

// https://github.com/knowm/XChart/issues/545
@Test
void renderWithCustomToolTipsDoesNotThrow() {

BubbleSeries series =
chart.addSeries("s", new double[] {1, 2}, new double[] {3, 4}, new double[] {5, 6});
series.setCustomToolTips(true);
series.setToolTips(new String[] {"2% (279/1298)", "4% (346/843)"});
chart.getStyler().setToolTipsAlwaysVisible(true);

BufferedImage image = assertDoesNotThrow(() -> BitmapEncoder.getBufferedImage(chart));
assertThat(image).isNotNull();
}

// https://github.com/knowm/XChart/issues/545
@Test
void renderWithPartialCustomToolTipsFallsBackWithoutThrowing() {

// fewer tooltip entries than data points, plus a null entry -> falls back to axis values
BubbleSeries series =
chart.addSeries("s", new double[] {1, 2, 3}, new double[] {3, 4, 5}, new double[] {5, 6, 7});
Comment on lines +65 to +66
series.setCustomToolTips(true);
series.setToolTips(new String[] {null});
chart.getStyler().setToolTipsAlwaysVisible(true);

assertDoesNotThrow(() -> BitmapEncoder.getBufferedImage(chart));
}
}
Loading