Sample dynamic line chart

From MyWiki
Revision as of 11:14, 14 July 2015 by George2 (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

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);
  }
}