Difference between revisions of "Sample dynamic line chart"
(Created page with "<source lang="java") import javafx.application.Application; import javafx.collections.*; import javafx.geometry.Pos; import javafx.scene.*; import javafx.scene.chart.*; impor...") |
(No difference)
|
Revision as of 11:14, 14 July 2015
Invalid language.
You need to specify a language like this: <source lang="html4strict">...</source>
Supported languages for syntax highlighting:
4cs, 6502acme, 6502kickass, 6502tasm, 68000devpac, abap, actionscript, actionscript3, ada, algol68, apache, applescript, apt_sources, arm, asm, asp, asymptote, autoconf, autohotkey, autoit, avisynth, awk, bascomavr, bash, basic4gl, bf, bibtex, blitzbasic, bnf, boo, c, c_loadrunner, c_mac, caddcl, cadlisp, cfdg, cfm, chaiscript, cil, clojure, cmake, cobol, coffeescript, cpp, cpp-qt, csharp, css, cuesheet, d, dcl, dcpu16, dcs, delphi, diff, div, dos, dot, e, ecmascript, eiffel, email, epc, erlang, euphoria, f1, falcon, fo, fortran, freebasic, freeswitch, fsharp, gambas, gdb, genero, genie, gettext, glsl, gml, gnuplot, go, groovy, gwbasic, haskell, haxe, hicest, hq9plus, html4strict, html5, icon, idl, ini, inno, intercal, io, j, java, java5, javascript, jquery, kixtart, klonec, klonecpp, latex, lb, ldif, lisp, llvm, locobasic, logtalk, lolcode, lotusformulas, lotusscript, lscript, lsl2, lua, m68k, magiksf, make, mapbasic, matlab, mirc, mmix, modula2, modula3, mpasm, mxml, mysql, nagios, netrexx, newlisp, nsis, oberon2, objc, objeck, ocaml, ocaml-brief, octave, oobas, oorexx, oracle11, oracle8, oxygene, oz, parasail, parigp, pascal, pcre, per, perl, perl6, pf, php, php-brief, pic16, pike, pixelbender, pli, plsql, postgresql, povray, powerbuilder, powershell, proftpd, progress, prolog, properties, providex, purebasic, pycon, pys60, python, q, qbasic, rails, rebol, reg, rexx, robots, rpmspec, rsplus, ruby, sas, scala, scheme, scilab, sdlbasic, smalltalk, smarty, spark, sparql, sql, stonescript, systemverilog, tcl, teraterm, text, thinbasic, tsql, typoscript, unicon, upc, urbi, uscript, vala, vb, vbnet, vedit, verilog, vhdl, vim, visualfoxpro, visualprolog, whitespace, whois, winbatch, xbasic, xml, xorg_conf, xpp, yaml, z80, zxbasic
lineChart;
private ObservableList<Event> events;
private Pane layout = new HBox();
@Override public void init() throws Exception {
//defining the axes
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
xAxis.setLabel("Attempt");
yAxis.setLabel("Distance (meters)");
xAxis.setMinorTickVisible(false);
xAxis.setAutoRanging(false);
xAxis.setLowerBound(1);
xAxis.setUpperBound(3);
xAxis.setTickUnit(1);
//creating the chart
lineChart = new LineChart<>(xAxis, yAxis);
lineChart.setAnimated(false);
lineChart.setTitle("Event Performance");
events = FXCollections.observableArrayList(
new Event("Javelin", "6 6", FXCollections.observableArrayList(
createSeries("Javelin - Tokyo", FXCollections.observableArrayList(18, 20, 22)),
createSeries("Javelin - Kyoto", FXCollections.observableArrayList(23, 14, 15))
)),
new Event("Hammer", "12 2 2 12", FXCollections.observableArrayList(
createSeries("Hammer - Tokyo", FXCollections.observableArrayList(12, 11, 5)),
createSeries("Hammer - Kyoto", FXCollections.observableArrayList(9, 8, 13))
)),
new Event("Shotput", "", FXCollections.observableArrayList(
createSeries("Shotput - Tokyo", FXCollections.observableArrayList(3, 2, 4)),
createSeries("Shotput - Kyoto", FXCollections.observableArrayList(4, 6, 5))
))
);
populateData(events, lineChart);
// create some controls which can toggle series display on and off.
final VBox eventChecks = new VBox(20);
eventChecks.setStyle("-fx-padding: 10;");
final TitledPane controlPane = new TitledPane("Event Selection", eventChecks);
controlPane.setCollapsible(false);
for (final Event event: events) {
final CheckBox box = new CheckBox(event.getName());
box.setSelected(true);
Line line = new Line(0, 10, 50, 10);
StringBuilder styleString = new StringBuilder("-fx-stroke-width: 3; -fx-stroke: gray;");
if (event.getStrokeDashArray() != null && !event.getStrokeDashArray().isEmpty()) {
styleString.append("-fx-stroke-dash-array: ").append(event.getStrokeDashArray()).append(";");
}
line.setStyle(styleString.toString());
box.setGraphic(line);
eventChecks.getChildren().add(box);
box.setOnAction(action -> {
event.setActive(box.isSelected());
populateData(events, lineChart);
styleSeries(events, lineChart);
}
);
}
Label caption = new Label(
"The chart displays performance an athelete in selected events at various sporting meets. "
+ "Events in which the athelete performed above average at a given meet are shown blue and "
+ "events at which the athelete performed below average are shown red. For a given meet, "
+ "the athelete may make three attempts per event. The red and blue highlighting is calculated "
+ "based on the average distance achieved for an event at a meet, not the longest distance achieved for the event at the meet. Select events to display from the controls on the left."
);
caption.setWrapText(true);
// layout the scene
HBox controlledChart = new HBox(10,
controlPane, lineChart
);
controlledChart.setAlignment(Pos.CENTER);
VBox captionedChart = new VBox(10,
controlledChart,
caption
);
captionedChart.setAlignment(Pos.CENTER);
HBox.setHgrow(lineChart, Priority.ALWAYS);
VBox.setVgrow(captionedChart.getChildren().get(0), Priority.ALWAYS);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
layout.getChildren().addAll(captionedChart);
}
@Override public void start(Stage stage) {
stage.setTitle("Sports Day Results");
Scene scene = new Scene(layout, 800, 600);
stage.setScene(scene);
stage.show();
styleSeries(events, lineChart);
}
private void populateData(final ObservableList<Event> events, final LineChart<Number, Number> lineChart) {
lineChart.getData().clear();
for (Event event: events) {
if (event.isActive()) {
lineChart.getData().addAll(event.getSeries());
}
}
}
private void styleSeries(ObservableList<Event> events, final LineChart<Number, Number> lineChart) {
// force a css layout pass to ensure that subsequent lookup calls work.
lineChart.applyCss();
// mark different series with different depending on whether they are above or below average.
int nSeries = 0;
for (Event event : events) {
if (!event.isActive()) continue;
for (int j = 0; j < event.getSeries().size(); j++) {
XYChart.Series<Number, Number> series = event.getSeries().get(j);
Set<Node> nodes = lineChart.lookupAll(".series" + nSeries);
for (Node n : nodes) {
StringBuilder style = new StringBuilder();
if (event.isBelowAverage(series)) {
style.append("-fx-stroke: red; -fx-background-color: red, white; ");
} else {
style.append("-fx-stroke: blue; -fx-background-color: blue, white; ");
}
if (event.getStrokeDashArray() != null && !event.getStrokeDashArray().isEmpty()) {
style.append("-fx-stroke-dash-array: ").append(event.getStrokeDashArray()).append(";");
}
n.setStyle(style.toString());
}
nSeries++;
}
}
}
private XYChart.Series<Number, Number> createSeries(String name, List<Number> data) {
XYChart.Series<Number, Number> series = new XYChart.Series<>();
series.setName(name);
ObservableList<XYChart.Data<Number, Number>> seriesData = FXCollections.observableArrayList();
for (int i = 0; i < data.size(); i++) {
seriesData.add(new XYChart.Data<>(i+1, data.get(i)));
}
series.setData(seriesData);
return series;
}
private class Event {
private String name;
private ObservableList<XYChart.Series<Number, Number>> series;
private String strokeDashArray;
private boolean isActive = true;
public String getName() { return name; }
public String getStrokeDashArray() { return strokeDashArray; }
public Event(String name, String strokeDashArray, ObservableList<XYChart.Series<Number, Number>> series) {
this.name = name; this.strokeDashArray = strokeDashArray; this.series = series;
}
public boolean isBelowAverage(XYChart.Series<Number, Number> checkedSeries) {
double checkedSeriesAvg = calcSeriesAverage(checkedSeries);
double allSeriesAvgTot = 0;
double seriesCount = series.size();
for (XYChart.Series<Number, Number> curSeries: series) {
allSeriesAvgTot += calcSeriesAverage(curSeries);
}
double allSeriesAvg = seriesCount != 0 ? allSeriesAvgTot / seriesCount: 0;
return checkedSeriesAvg < allSeriesAvg;
}
public ObservableList<XYChart.Series<Number, Number>> getSeries() {
return series;
}
private double calcSeriesAverage(XYChart.Series<Number, Number> series) {
double sum = 0;
int count = series.getData().size();
for (XYChart.Data<Number, Number> data: series.getData()) {
sum += data.YValueProperty().get().doubleValue();
}
return count != 0 ? sum / count : 0;
}
private boolean isActive() {
return isActive;
}
private void setActive(boolean isActive) {
this.isActive = isActive;
}
}
public static void main(String[] args) {
launch(args);
}
}