Fixed sparkline not updating.

#story[1095]
main
fjc40 8 years ago
parent 5ae8393126
commit dc4610d6eb

@ -2,6 +2,8 @@ package shared.model;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import network.Messages.Enums.RaceStatusEnum;
import network.Messages.Enums.RaceTypeEnum;
import shared.dataInput.BoatDataSource;
@ -37,6 +39,12 @@ public abstract class RaceState {
*/
private RegattaDataSource regattaDataSource;
/**
* Legs in the race.
* We have this in a separate list so that it can be observed.
*/
private ObservableList<Leg> legs;
/**
@ -65,6 +73,9 @@ public abstract class RaceState {
*/
public RaceState() {
//Legs.
this.legs = FXCollections.observableArrayList();
//Race clock.
this.raceClock = new RaceClock(ZonedDateTime.now());
@ -90,8 +101,9 @@ public abstract class RaceState {
* @param legs The new list of legs to use.
*/
protected void useLegsList(List<Leg> legs) {
this.legs.setAll(legs);
//We add a "dummy" leg at the end of the race.
if (legs.size() > 0) {
if (getLegs().size() > 0) {
getLegs().add(new Leg("Finish", getLegs().size()));
}
}
@ -126,6 +138,7 @@ public abstract class RaceState {
public void setRaceDataSource(RaceDataSource raceDataSource) {
this.raceDataSource = raceDataSource;
this.getRaceClock().setStartingTime(raceDataSource.getStartDateTime());
useLegsList(raceDataSource.getLegs());
}
/**
@ -309,8 +322,8 @@ public abstract class RaceState {
* Returns the legs of the race.
* @return Legs of the race.
*/
public List<Leg> getLegs() {
return raceDataSource.getLegs();
public ObservableList<Leg> getLegs() {
return legs;
}

@ -531,6 +531,7 @@ public class ResizableRaceCanvas extends ResizableCanvas {
* draws the line leg by leg
* @param legs the legs of a race
* @param index the index of the current leg to use
* @param legStartPoint The position the current leg.
* @return the end point of the current leg that has been drawn
*/
private GPSCoordinate drawLineRounding(List<Leg> legs, int index, GPSCoordinate legStartPoint){

@ -1,14 +1,18 @@
package visualiser.model;
import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.paint.Color;
import shared.model.Leg;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ -32,10 +36,10 @@ public class Sparkline {
private ObservableList<VisualiserBoat> boats;
/**
* The number of legs in the race.
* Used to correctly scale the linechart.
* Race legs to observe.
* We need to observe legs as they may be added after the sparkline is created if race.xml is received after this is created.
*/
private Integer legNum;
private ObservableList<Leg> legs;
/**
@ -53,6 +57,14 @@ public class Sparkline {
*/
private NumberAxis yAxis;
/**
* A map between a boat and its data series in the sparkline.
* This is used so that we can remove a series when (or if) a boat is removed from the race.
*/
private Map<VisualiserBoat, XYChart.Series<Number, Number>> boatSeriesMap;
/**
* Constructor to set up initial sparkline (LineChart) object
@ -62,12 +74,14 @@ public class Sparkline {
public Sparkline(VisualiserRaceState race, LineChart<Number,Number> sparklineChart) {
this.race = race;
this.boats = new SortedList<>(race.getBoats());
this.legNum = race.getLegCount();
this.legs = race.getLegs();
this.sparklineChart = sparklineChart;
this.yAxis = (NumberAxis) sparklineChart.getYAxis();
this.xAxis = (NumberAxis) sparklineChart.getXAxis();
this.boatSeriesMap = new HashMap<>();
createSparkline();
}
@ -79,50 +93,45 @@ public class Sparkline {
* Position numbers are displayed.
*/
private void createSparkline() {
// NOTE: Y axis is in negatives to display correct positions
//For each boat...
for (VisualiserBoat boat : this.boats) {
//Create data series for each boat.
XYChart.Series<Number, Number> series = new XYChart.Series<>();
//All boats start in "last" place.
series.getData().add(new XYChart.Data<>(0, boats.size()));
//Listen for changes in the boat's leg - we only update the graph when it changes leg.
boat.legProperty().addListener(
(observable, oldValue, newValue) -> {
//We need to dynamically update the sparkline when boats are added/removed.
boats.addListener((ListChangeListener.Change<? extends VisualiserBoat> c) -> {
//Get the data to plot.
List<VisualiserBoat> boatOrder = race.getLegCompletionOrder().get(oldValue);
//Find boat position in list.
int boatPosition = boatOrder.indexOf(boat) + 1;
Platform.runLater(() -> {
//Get leg number.
int legNumber = oldValue.getLegNumber() + 1;
while (c.next()) {
if (c.wasAdded()) {
for (VisualiserBoat boat : c.getAddedSubList()) {
addBoatSeries(boat);
}
//Create new data point for boat's position at the new leg.
XYChart.Data<Number, Number> dataPoint = new XYChart.Data<>(legNumber, boatPosition);
} else if (c.wasRemoved()) {
for (VisualiserBoat boat : c.getRemoved()) {
removeBoatSeries(boat);
}
}
//Add to series.
Platform.runLater(() -> series.getData().add(dataPoint));
}
//Update height of y axis.
yAxis.setLowerBound(boats.size());
});
});
});
//Add to chart.
sparklineChart.getData().add(series);
//Color using boat's color. We need to do this after adding the series to a chart, otherwise we get null pointer exceptions.
series.getNode().setStyle("-fx-stroke: " + colourToHex(boat.getColor()) + ";");
legs.addListener((ListChangeListener.Change<? extends Leg> c) -> {
Platform.runLater(() -> xAxis.setUpperBound(race.getLegCount()));
});
//Initialise chart for existing boats.
for (VisualiserBoat boat : boats) {
addBoatSeries(boat);
}
sparklineChart.setCreateSymbols(false);
//Set x axis details
@ -131,7 +140,7 @@ public class Sparkline {
xAxis.setTickLabelsVisible(false);
xAxis.setMinorTickVisible(false);
xAxis.setLowerBound(0);
xAxis.setUpperBound(legNum + 2);
xAxis.setUpperBound(race.getLegCount());
xAxis.setTickUnit(1);
//Set y axis details
@ -148,6 +157,65 @@ public class Sparkline {
}
/**
* Removes the data series for a given boat from the sparkline.
* @param boat Boat to remove series for.
*/
private void removeBoatSeries(VisualiserBoat boat) {
sparklineChart.getData().remove(boatSeriesMap.get(boat));
boatSeriesMap.remove(boat);
}
/**
* Creates a data series for a boat, and adds it to the sparkline.
* @param boat Boat to add series for.
*/
private void addBoatSeries(VisualiserBoat boat) {
//Create data series for boat.
XYChart.Series<Number, Number> series = new XYChart.Series<>();
//All boats start in "last" place.
series.getData().add(new XYChart.Data<>(0, boats.size()));
//Listen for changes in the boat's leg - we only update the graph when it changes leg.
boat.legProperty().addListener(
(observable, oldValue, newValue) -> {
//Get the data to plot.
List<VisualiserBoat> boatOrder = race.getLegCompletionOrder().get(oldValue);
//Find boat position in list.
int boatPosition = boatOrder.indexOf(boat) + 1;
//Get leg number.
int legNumber = oldValue.getLegNumber() + 1;
//Create new data point for boat's position at the new leg.
XYChart.Data<Number, Number> dataPoint = new XYChart.Data<>(legNumber, boatPosition);
//Add to series.
Platform.runLater(() -> series.getData().add(dataPoint));
});
//Add to chart.
sparklineChart.getData().add(series);
//Color using boat's color. We need to do this after adding the series to a chart, otherwise we get null pointer exceptions.
series.getNode().setStyle("-fx-stroke: " + colourToHex(boat.getColor()) + ";");
boatSeriesMap.put(boat, series);
}
/**
* Converts a color to a hex string, starting with a {@literal #} symbol.
* @param color The color to convert.

@ -97,7 +97,7 @@ public class VisualiserRaceState extends RaceState {
this.generateVisualiserBoats(this.boats, getBoatDataSource().getBoats(), raceDataSource.getParticipants(), unassignedColors);
}
useLegsList(raceDataSource.getLegs());
initialiseLegCompletionOrder();
}
/**
@ -125,14 +125,9 @@ public class VisualiserRaceState extends RaceState {
/**
* See {@link RaceState#useLegsList(List)}.
* Also initialises the {@link #legCompletionOrder} map.
* @param legs The new list of legs to use.
* Initialises the {@link #legCompletionOrder} map.
*/
@Override
public void useLegsList(List<Leg> legs) {
super.useLegsList(legs);
public void initialiseLegCompletionOrder() {
//Initialise the leg completion order map.
for (Leg leg : getLegs()) {
this.legCompletionOrder.put(leg, new ArrayList<>(this.boats.size()));

Loading…
Cancel
Save