mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Merge branch 'Story34_Sparklines' into 'develop'
Story34 sparklines See merge request !38
This commit is contained in:
@@ -65,8 +65,8 @@ public class App extends Application {
|
|||||||
//Change the StreamReceiver in this else block to change the default data source.
|
//Change the StreamReceiver in this else block to change the default data source.
|
||||||
else{
|
else{
|
||||||
// sr = new StreamReceiver("localhost", 4949, "RaceStream");
|
// sr = new StreamReceiver("localhost", 4949, "RaceStream");
|
||||||
sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream");
|
|
||||||
// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream");
|
// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream");
|
||||||
|
sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream");
|
||||||
}
|
}
|
||||||
|
|
||||||
sr.start();
|
sr.start();
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ public class CanvasController {
|
|||||||
updateFPMCounter = 0;
|
updateFPMCounter = 0;
|
||||||
drawFps(frameRate.intValue());
|
drawFps(frameRate.intValue());
|
||||||
}
|
}
|
||||||
|
raceViewController.updateSparkLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 1/05/17 cir27 - Make the RaceObjects update on the actual delay.
|
// TODO: 1/05/17 cir27 - Make the RaceObjects update on the actual delay.
|
||||||
|
|||||||
@@ -6,7 +6,12 @@ import javafx.collections.FXCollections;
|
|||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.geometry.Side;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.chart.LineChart;
|
||||||
|
import javafx.scene.chart.NumberAxis;
|
||||||
|
import javafx.scene.chart.XYChart;
|
||||||
|
import javafx.scene.chart.XYChart.Series;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.ComboBox;
|
import javafx.scene.control.ComboBox;
|
||||||
@@ -31,12 +36,18 @@ import seng302.models.stream.StreamParser;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
|
import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* Created by ptg19 on 29/03/17.
|
* Created by ptg19 on 29/03/17.
|
||||||
*/
|
*/
|
||||||
public class RaceViewController extends Thread implements ImportantAnnotationDelegate {
|
public class RaceViewController extends Thread implements ImportantAnnotationDelegate {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private LineChart raceSparkLine;
|
||||||
|
@FXML
|
||||||
|
private NumberAxis sparklineYAxis;
|
||||||
@FXML
|
@FXML
|
||||||
private VBox positionVbox;
|
private VBox positionVbox;
|
||||||
@FXML
|
@FXML
|
||||||
@@ -60,7 +71,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
private boolean displayFps;
|
private boolean displayFps;
|
||||||
private Timeline timerTimeline;
|
private Timeline timerTimeline;
|
||||||
private Stage stage;
|
private Stage stage;
|
||||||
|
private static HashMap<Integer, Series<String, Double>> sparkLineData = new HashMap<>();
|
||||||
|
private static ArrayList<Yacht> racingBoats = new ArrayList<>();
|
||||||
private ImportantAnnotationsState importantAnnotations;
|
private ImportantAnnotationsState importantAnnotations;
|
||||||
private Yacht selectedBoat;
|
private Yacht selectedBoat;
|
||||||
|
|
||||||
@@ -68,6 +80,14 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
// Load a default important annotation state
|
// Load a default important annotation state
|
||||||
importantAnnotations = new ImportantAnnotationsState();
|
importantAnnotations = new ImportantAnnotationsState();
|
||||||
|
|
||||||
|
//Formatting the y axis of the sparkline
|
||||||
|
raceSparkLine.getYAxis().setRotate(180);
|
||||||
|
raceSparkLine.getYAxis().setTickLabelRotation(180);
|
||||||
|
raceSparkLine.getYAxis().setTranslateX(15);
|
||||||
|
raceSparkLine.getYAxis().setAutoRanging(false);
|
||||||
|
|
||||||
|
startingBoats = new ArrayList<>(StreamParser.getBoats().values());
|
||||||
|
|
||||||
includedCanvasController.setup(this);
|
includedCanvasController.setup(this);
|
||||||
includedCanvasController.initializeCanvas();
|
includedCanvasController.initializeCanvas();
|
||||||
initializeUpdateTimer();
|
initializeUpdateTimer();
|
||||||
@@ -75,12 +95,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
initialiseAnnotationSlider();
|
initialiseAnnotationSlider();
|
||||||
initialiseBoatSelectionComboBox();
|
initialiseBoatSelectionComboBox();
|
||||||
includedCanvasController.timer.start();
|
includedCanvasController.timer.start();
|
||||||
|
selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView());
|
||||||
selectAnnotationBtn.setOnAction(event -> {
|
|
||||||
loadSelectAnnotationView();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The important annotations have been changed, update this view
|
* The important annotations have been changed, update this view
|
||||||
*
|
*
|
||||||
@@ -91,6 +109,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
setAnnotations((int) annotationSlider.getValue()); // Refresh the displayed annotations
|
setAnnotations((int) annotationSlider.getValue()); // Refresh the displayed annotations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the "select annotations" view in a new window
|
* Loads the "select annotations" view in a new window
|
||||||
*/
|
*/
|
||||||
@@ -128,6 +147,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
(observable, oldValue, newValue) -> displayFps = !displayFps);
|
(observable, oldValue, newValue) -> displayFps = !displayFps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void initialiseAnnotationSlider() {
|
private void initialiseAnnotationSlider() {
|
||||||
annotationSlider.setLabelFormatter(new StringConverter<Double>() {
|
annotationSlider.setLabelFormatter(new StringConverter<Double>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -168,6 +188,79 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to add any new boats into the race that may have started late or not have had data received yet
|
||||||
|
*/
|
||||||
|
void updateSparkLine(){
|
||||||
|
// Collect the racing boats that aren't already in the chart
|
||||||
|
ArrayList<Yacht> sparkLineCandidates = startingBoats.stream().filter(yacht -> !sparkLineData.containsKey(yacht.getSourceID())
|
||||||
|
&& yacht.getPosition() != null & yacht.getPosition() != "-").collect(Collectors.toCollection(ArrayList::new));
|
||||||
|
|
||||||
|
// Obtain the qualifying boats to set the max on the Y axis
|
||||||
|
racingBoats = startingBoats.stream().filter(yacht ->
|
||||||
|
yacht.getPosition() != null & yacht.getPosition() != "-").collect(Collectors.toCollection(ArrayList::new));
|
||||||
|
sparklineYAxis.setUpperBound(racingBoats.size() + 1);
|
||||||
|
|
||||||
|
// Create a new data series for new boats
|
||||||
|
sparkLineCandidates.stream().filter(yacht -> yacht.getPosition() != null).forEach(yacht -> {
|
||||||
|
Series<String, Double> yachtData = new Series<>();
|
||||||
|
yachtData.setName(yacht.getBoatName());
|
||||||
|
yachtData.getData().add(new XYChart.Data<>(Integer.toString(yacht.getLegNumber()), 1 + racingBoats.size() - Double.parseDouble(yacht.getPosition())));
|
||||||
|
sparkLineData.put(yacht.getSourceID(), yachtData);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Lambda function to sort the series in order of leg (later legs shown more to the right)
|
||||||
|
List<XYChart.Series<String, Double>> positions = new ArrayList<>(sparkLineData.values());
|
||||||
|
Collections.sort(positions, (o1, o2) -> {
|
||||||
|
Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size()-1).getXValue());
|
||||||
|
Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size()-1).getXValue());
|
||||||
|
if (leg2 < leg1){
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Adds the new data series to the sparkline (and set the colour of the series)
|
||||||
|
raceSparkLine.setCreateSymbols(false);
|
||||||
|
positions.stream().filter(spark -> !raceSparkLine.getData().contains(spark)).forEach(spark -> {
|
||||||
|
raceSparkLine.getData().add(spark);
|
||||||
|
spark.getNode().lookup(".chart-series-line").setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the yachts sparkline of the desired boat and using the new leg number
|
||||||
|
* @param yacht The yacht to be updated on the sparkline
|
||||||
|
* @param legNumber the leg number that the position will be assigned to
|
||||||
|
*/
|
||||||
|
public static void updateYachtPositionSparkline(Yacht yacht, Integer legNumber){
|
||||||
|
XYChart.Series<String, Double> positionData = sparkLineData.get(yacht.getSourceID());
|
||||||
|
positionData.getData().add(new XYChart.Data<>(Integer.toString(legNumber), 1 + racingBoats.size() - Double.parseDouble(yacht.getPosition())));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets the rgb string of the boats colour to use for the chart via css
|
||||||
|
* @param boatName boat passed in to get the boats colour
|
||||||
|
* @return the colour as an rgb string
|
||||||
|
*/
|
||||||
|
private String getBoatColorAsRGB(String boatName){
|
||||||
|
Color color = Color.WHITE;
|
||||||
|
for (Yacht yacht: startingBoats){
|
||||||
|
if (Objects.equals(yacht.getBoatName(), boatName)){
|
||||||
|
color = yacht.getColour();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return String.format( "#%02X%02X%02X",
|
||||||
|
(int)( color.getRed() * 255 ),
|
||||||
|
(int)( color.getGreen() * 255 ),
|
||||||
|
(int)( color.getBlue() * 255 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initalises a timer which updates elements of the RaceView such as wind direction, boat
|
* Initalises a timer which updates elements of the RaceView such as wind direction, boat
|
||||||
* orderings etc.. which are dependent on the info from the stream parser constantly.
|
* orderings etc.. which are dependent on the info from the stream parser constantly.
|
||||||
@@ -184,7 +277,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
updateWindDirection();
|
updateWindDirection();
|
||||||
updateOrder();
|
updateOrder();
|
||||||
updateBoatSelectionComboBox();
|
updateBoatSelectionComboBox();
|
||||||
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -345,7 +437,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isDisplayFps() {
|
boolean isDisplayFps() {
|
||||||
return displayFps;
|
return displayFps;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,4 +543,13 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
Stage getStage() {
|
Stage getStage() {
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for when the boat attempts to add data to the sparkline (first checks if the sparkline contains info on it)
|
||||||
|
* @param yachtId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean sparkLineStatus(Integer yachtId) {
|
||||||
|
return sparkLineData.containsKey(yachtId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package seng302.models;
|
package seng302.models;
|
||||||
|
|
||||||
|
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
import seng302.controllers.RaceViewController;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
@@ -34,6 +36,7 @@ public class Yacht {
|
|||||||
// Mark rounding
|
// Mark rounding
|
||||||
private Long markRoundingTime;
|
private Long markRoundingTime;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used in EventTest and RaceTest.
|
* Used in EventTest and RaceTest.
|
||||||
*
|
*
|
||||||
@@ -105,6 +108,9 @@ public class Yacht {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setLegNumber(Integer legNumber) {
|
public void setLegNumber(Integer legNumber) {
|
||||||
|
if (colour != null && position != "-" && legNumber != this.legNumber&& RaceViewController.sparkLineStatus(sourceID)) {
|
||||||
|
RaceViewController.updateYachtPositionSparkline(this, legNumber);
|
||||||
|
}
|
||||||
this.legNumber = legNumber;
|
this.legNumber = legNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -182,3 +182,7 @@ Remove scroll bars
|
|||||||
-fx-background-insets: 0;
|
-fx-background-insets: 0;
|
||||||
-fx-padding: 0;
|
-fx-padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chart{
|
||||||
|
-fx-background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import java.lang.*?>
|
<?import java.lang.*?>
|
||||||
|
<?import javafx.scene.chart.*?>
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import javafx.scene.shape.*?>
|
<?import javafx.scene.shape.*?>
|
||||||
@@ -50,6 +51,14 @@
|
|||||||
<Button fx:id="selectAnnotationBtn" layoutX="35.0" layoutY="578.0" mnemonicParsing="false" prefHeight="18.0" prefWidth="170.0" styleClass="blue-ui-btn" text="Select Annotations" textFill="WHITE" />
|
<Button fx:id="selectAnnotationBtn" layoutX="35.0" layoutY="578.0" mnemonicParsing="false" prefHeight="18.0" prefWidth="170.0" styleClass="blue-ui-btn" text="Select Annotations" textFill="WHITE" />
|
||||||
<Text fill="WHITE" layoutX="11.0" layoutY="649.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Boat Selection" />
|
<Text fill="WHITE" layoutX="11.0" layoutY="649.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Boat Selection" />
|
||||||
<ComboBox fx:id="boatSelectionComboBox" layoutX="37.0" layoutY="664.0" prefHeight="25.0" prefWidth="170.0" promptText="Select Boat" styleClass="combo-box-base" />
|
<ComboBox fx:id="boatSelectionComboBox" layoutX="37.0" layoutY="664.0" prefHeight="25.0" prefWidth="170.0" promptText="Select Boat" styleClass="combo-box-base" />
|
||||||
|
<LineChart fx:id="raceSparkLine" layoutX="-15.0" layoutY="719.0" legendVisible="false" prefHeight="277.0" prefWidth="260.0" title="Boat Positions">
|
||||||
|
<xAxis>
|
||||||
|
<CategoryAxis label="Leg Number" side="BOTTOM" styleClass="spark-line-xaxis" />
|
||||||
|
</xAxis>
|
||||||
|
<yAxis>
|
||||||
|
<NumberAxis fx:id="sparklineYAxis" minorTickCount="1" minorTickLength="1.0" side="LEFT" styleClass="spark-line-yaxis" tickLabelGap="1.0" tickUnit="1.0" upperBound="7.0" />
|
||||||
|
</yAxis>
|
||||||
|
</LineChart>
|
||||||
</children>
|
</children>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
<AnchorPane fx:id="contentAnchorPane" maxHeight="960.0" maxWidth="1280.0" prefHeight="960.0" prefWidth="1280.0" style="-fx-background-color: skyblue;" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2147483647" GridPane.valignment="TOP">
|
<AnchorPane fx:id="contentAnchorPane" maxHeight="960.0" maxWidth="1280.0" prefHeight="960.0" prefWidth="1280.0" style="-fx-background-color: skyblue;" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2147483647" GridPane.valignment="TOP">
|
||||||
|
|||||||
Reference in New Issue
Block a user