Added load button and max HR dialog
This commit is contained in:
parent
05d052fb00
commit
414c3c3ee9
4 changed files with 96 additions and 42 deletions
|
@ -37,7 +37,10 @@ import es.kauron.estraba.model.DataBundle;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.chart.AreaChart;
|
import javafx.scene.chart.AreaChart;
|
||||||
import javafx.scene.chart.LineChart;
|
import javafx.scene.chart.LineChart;
|
||||||
import javafx.scene.chart.PieChart;
|
import javafx.scene.chart.PieChart;
|
||||||
|
@ -46,8 +49,10 @@ import javafx.scene.control.Tab;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.stage.Stage;
|
||||||
import jgpx.model.analysis.Chunk;
|
import jgpx.model.analysis.Chunk;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@ -62,7 +67,7 @@ public class DashboardController implements Initializable, MapComponentInitializ
|
||||||
private AnchorPane root;
|
private AnchorPane root;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Tab tabDashboard, tabMap, tabGraph, tabSettings;
|
private Tab tabDashboard, tabMap, tabGraph;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private ImageView imgHR, imgSpeed, imgCadence, imgDate, imgDistance, imgElevation;
|
private ImageView imgHR, imgSpeed, imgCadence, imgDate, imgDistance, imgElevation;
|
||||||
|
@ -92,6 +97,7 @@ public class DashboardController implements Initializable, MapComponentInitializ
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
mapView.setVisible(false);
|
||||||
// populate map icons
|
// populate map icons
|
||||||
((ImageView)elevationButton.getGraphic()).setImage(new Image(App.class.getResourceAsStream("img/elevation.png")));
|
((ImageView)elevationButton.getGraphic()).setImage(new Image(App.class.getResourceAsStream("img/elevation.png")));
|
||||||
((ImageView)speedButton.getGraphic()).setImage(new Image(App.class.getResourceAsStream("img/speed.png")));
|
((ImageView)speedButton.getGraphic()).setImage(new Image(App.class.getResourceAsStream("img/speed.png")));
|
||||||
|
@ -107,6 +113,19 @@ public class DashboardController implements Initializable, MapComponentInitializ
|
||||||
imgElevation.setImage(new Image(App.class.getResourceAsStream("img/elevation.png")));
|
imgElevation.setImage(new Image(App.class.getResourceAsStream("img/elevation.png")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void loadFile() {
|
||||||
|
FXMLLoader loader = new FXMLLoader(
|
||||||
|
App.class.getResource("fxml/Splash.fxml"), App.GENERAL_BUNDLE);
|
||||||
|
Parent parent;
|
||||||
|
try {
|
||||||
|
parent = loader.load();
|
||||||
|
((Stage) root.getScene().getWindow()).setScene(new Scene(parent));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void onMapButton(ActionEvent event){
|
private void onMapButton(ActionEvent event){
|
||||||
switch (((JFXButton)event.getSource()).getId()) {
|
switch (((JFXButton)event.getSource()).getId()) {
|
||||||
|
@ -196,16 +215,12 @@ public class DashboardController implements Initializable, MapComponentInitializ
|
||||||
coord[W] = Math.min(lon, coord[W]);
|
coord[W] = Math.min(lon, coord[W]);
|
||||||
pathArray.push(new LatLong(lat, lon));
|
pathArray.push(new LatLong(lat, lon));
|
||||||
});
|
});
|
||||||
// Create and add the polyline using the array
|
|
||||||
// This polyline displays instantly with no problem
|
|
||||||
// TODO: add color with PolylineOptions.strokeColor("#ffff00") to match the color schemes of the app
|
|
||||||
// When using that method, the line does not load properly, it needs an update to the zoom to show up.
|
|
||||||
map.addMarker(new Marker(new MarkerOptions()
|
map.addMarker(new Marker(new MarkerOptions()
|
||||||
.position(new LatLong(
|
.position(new LatLong(
|
||||||
chunks.get(0).getFirstPoint().getLatitude(),
|
chunks.get(0).getFirstPoint().getLatitude(),
|
||||||
chunks.get(0).getFirstPoint().getLongitude()))
|
chunks.get(0).getFirstPoint().getLongitude()))
|
||||||
.title("label.begin")));
|
.title("label.begin")));
|
||||||
map.addMapShape(new Polyline(new PolylineOptions().path(pathArray)));
|
map.addMapShape(new Polyline(new PolylineOptions().path(pathArray).strokeColor("#fc4c02")));
|
||||||
map.addMarker(new Marker(new MarkerOptions()
|
map.addMarker(new Marker(new MarkerOptions()
|
||||||
.position(new LatLong(
|
.position(new LatLong(
|
||||||
chunks.get(chunks.size() - 1).getLastPoint().getLatitude(),
|
chunks.get(chunks.size() - 1).getLastPoint().getLatitude(),
|
||||||
|
|
|
@ -38,12 +38,17 @@ import javafx.fxml.FXMLLoader;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Dialog;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.input.Dragboard;
|
import javafx.scene.input.Dragboard;
|
||||||
import javafx.scene.input.TransferMode;
|
import javafx.scene.input.TransferMode;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
@ -78,6 +83,7 @@ public class SplashController implements Initializable{
|
||||||
|
|
||||||
private JFXSnackbar snackbar;
|
private JFXSnackbar snackbar;
|
||||||
private File file;
|
private File file;
|
||||||
|
private int maxHR;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void loadGPXFile(ActionEvent event) throws Exception {
|
private void loadGPXFile(ActionEvent event) throws Exception {
|
||||||
|
@ -93,15 +99,16 @@ public class SplashController implements Initializable{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadGPXFile(File file) {
|
public void loadGPXFile(File file) {
|
||||||
|
maxHR = showHRDialog();
|
||||||
|
if (maxHR < 0) errorLoading();
|
||||||
buttonLoad.setVisible(false);
|
buttonLoad.setVisible(false);
|
||||||
labelWelcome.setVisible(false);
|
labelWelcome.setVisible(false);
|
||||||
spinner.setVisible(true);
|
spinner.setVisible(true);
|
||||||
snackbar.registerSnackbarContainer(root);
|
|
||||||
snackbar.show("Loading file", 5000);
|
snackbar.show("Loading file", 5000);
|
||||||
Thread th = new Thread(new Task<DataBundle>() {
|
Thread th = new Thread(new Task<DataBundle>() {
|
||||||
@Override
|
@Override
|
||||||
protected DataBundle call() throws Exception {
|
protected DataBundle call() throws Exception {
|
||||||
return DataBundle.loadFrom(file);
|
return DataBundle.loadFrom(file, maxHR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -169,6 +176,7 @@ public class SplashController implements Initializable{
|
||||||
e.setDropCompleted(success);
|
e.setDropCompleted(success);
|
||||||
e.consume();
|
e.consume();
|
||||||
}));
|
}));
|
||||||
|
snackbar.registerSnackbarContainer(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void errorLoading() {
|
private void errorLoading() {
|
||||||
|
@ -177,5 +185,36 @@ public class SplashController implements Initializable{
|
||||||
spinner.setVisible(false);
|
spinner.setVisible(false);
|
||||||
snackbar.show("Error loading file", 3000);
|
snackbar.show("Error loading file", 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int showHRDialog() {
|
||||||
|
Dialog<Integer> dialog = new Dialog<>();
|
||||||
|
dialog.setTitle("Input your maximum heart rate or age");
|
||||||
|
GridPane grid = new GridPane();
|
||||||
|
grid.setHgap(5);
|
||||||
|
grid.setVgap(2);
|
||||||
|
grid.addColumn(0, new Text("Heart rate:"), new Text("Age:"));
|
||||||
|
Spinner<Integer> spinnerAge = new Spinner<>(18, 99, 25, 1);
|
||||||
|
Spinner<Integer> spinnerHR = new Spinner<>(60, 202, 180, 5);
|
||||||
|
spinnerAge.valueProperty().addListener((obs, oldV, newV) ->
|
||||||
|
spinnerHR.setValueFactory(new SpinnerValueFactory
|
||||||
|
.IntegerSpinnerValueFactory(60, 202, 220 - newV, 5)));
|
||||||
|
spinnerHR.valueProperty().addListener((obs, old, newV) ->
|
||||||
|
spinnerAge.setValueFactory(new SpinnerValueFactory
|
||||||
|
.IntegerSpinnerValueFactory(18, 99, 220 - newV, 1)));
|
||||||
|
grid.addColumn(1, spinnerHR, spinnerAge);
|
||||||
|
Button buttonOk = new Button("Ok");
|
||||||
|
buttonOk.setDefaultButton(true);
|
||||||
|
buttonOk.setOnAction(event -> {
|
||||||
|
dialog.setResult(spinnerHR.getValue());
|
||||||
|
dialog.close();
|
||||||
|
});
|
||||||
|
grid.add(buttonOk, 1, 2);
|
||||||
|
dialog.getDialogPane().setContent(grid);
|
||||||
|
dialog.showAndWait();
|
||||||
|
if (dialog.getResult() != null)
|
||||||
|
return dialog.getResult();
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class DataBundle {
|
||||||
public ObservableList<PieChart.Data> pieData;
|
public ObservableList<PieChart.Data> pieData;
|
||||||
public ObservableList<Chunk> chunks;
|
public ObservableList<Chunk> chunks;
|
||||||
|
|
||||||
private DataBundle(TrackData track) {
|
private DataBundle(TrackData track, int maxHR) {
|
||||||
|
|
||||||
HRAvg = track.getAverageHeartrate() + App.GENERAL_BUNDLE.getString("unit.bpm");
|
HRAvg = track.getAverageHeartrate() + App.GENERAL_BUNDLE.getString("unit.bpm");
|
||||||
HRMax = track.getMaxHeartrate() + App.GENERAL_BUNDLE.getString("unit.bpm");
|
HRMax = track.getMaxHeartrate() + App.GENERAL_BUNDLE.getString("unit.bpm");
|
||||||
|
@ -106,10 +106,10 @@ public class DataBundle {
|
||||||
cadenceSeries.getData().add(new XYChart.Data<>(currentDistance, chunk.getAvgCadence()));
|
cadenceSeries.getData().add(new XYChart.Data<>(currentDistance, chunk.getAvgCadence()));
|
||||||
|
|
||||||
String zone;
|
String zone;
|
||||||
if (chunk.getAvgHeartRate() > 170) zone = App.GENERAL_BUNDLE.getString("zone.anaerobic");
|
if (chunk.getAvgHeartRate() > maxHR * .9) zone = App.GENERAL_BUNDLE.getString("zone.anaerobic");
|
||||||
else if (chunk.getAvgHeartRate() > 150) zone = App.GENERAL_BUNDLE.getString("zone.threshold");
|
else if (chunk.getAvgHeartRate() > maxHR * .8) zone = App.GENERAL_BUNDLE.getString("zone.threshold");
|
||||||
else if (chunk.getAvgHeartRate() > 130) zone = App.GENERAL_BUNDLE.getString("zone.tempo");
|
else if (chunk.getAvgHeartRate() > maxHR * .7) zone = App.GENERAL_BUNDLE.getString("zone.tempo");
|
||||||
else if (chunk.getAvgHeartRate() > 110) zone = App.GENERAL_BUNDLE.getString("zone.endurance");
|
else if (chunk.getAvgHeartRate() > maxHR * .6) zone = App.GENERAL_BUNDLE.getString("zone.endurance");
|
||||||
else zone = App.GENERAL_BUNDLE.getString("zone.recovery");
|
else zone = App.GENERAL_BUNDLE.getString("zone.recovery");
|
||||||
|
|
||||||
boolean pieFound = false;
|
boolean pieFound = false;
|
||||||
|
@ -117,13 +117,14 @@ public class DataBundle {
|
||||||
if (d.getName().equals(zone)) {
|
if (d.getName().equals(zone)) {
|
||||||
pieFound = true;
|
pieFound = true;
|
||||||
d.setPieValue(d.getPieValue() + 1);
|
d.setPieValue(d.getPieValue() + 1);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!pieFound) pieData.add( new PieChart.Data(zone, 1) );
|
if (!pieFound) pieData.add( new PieChart.Data(zone, 1) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DataBundle loadFrom(File file) throws Exception {
|
public static DataBundle loadFrom(File file, int maxHR) throws Exception {
|
||||||
JAXBElement<Object> jaxbElement;
|
JAXBElement<Object> jaxbElement;
|
||||||
JAXBContext jaxbContext = JAXBContext.newInstance(GpxType.class, TrackPointExtensionT.class);
|
JAXBContext jaxbContext = JAXBContext.newInstance(GpxType.class, TrackPointExtensionT.class);
|
||||||
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
|
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
|
||||||
|
@ -131,6 +132,6 @@ public class DataBundle {
|
||||||
GpxType gpx = (GpxType) jaxbElement.getValue();
|
GpxType gpx = (GpxType) jaxbElement.getValue();
|
||||||
|
|
||||||
if (gpx == null) throw new Exception();
|
if (gpx == null) throw new Exception();
|
||||||
return new DataBundle(new TrackData(new Track(gpx.getTrk().get(0))));
|
return new DataBundle(new TrackData(new Track(gpx.getTrk().get(0))), maxHR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,17 +25,26 @@
|
||||||
~
|
~
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<?import com.jfoenix.controls.*?>
|
<?import com.jfoenix.controls.JFXButton?>
|
||||||
|
<?import com.jfoenix.controls.JFXSpinner?>
|
||||||
|
<?import com.jfoenix.controls.JFXTabPane?>
|
||||||
<?import com.lynden.gmapsfx.GoogleMapView?>
|
<?import com.lynden.gmapsfx.GoogleMapView?>
|
||||||
<?import javafx.geometry.*?>
|
<?import java.lang.String?>
|
||||||
<?import javafx.scene.chart.*?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.chart.AreaChart?>
|
||||||
<?import javafx.scene.image.*?>
|
<?import javafx.scene.chart.LineChart?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.chart.NumberAxis?>
|
||||||
<?import javafx.scene.text.*?>
|
<?import javafx.scene.chart.PieChart?>
|
||||||
<?import java.lang.*?>
|
<?import javafx.scene.control.Label?>
|
||||||
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" fx:id="root" xmlns="http://javafx.com/javafx/8.0.76-ea"
|
<?import javafx.scene.control.Tab?>
|
||||||
fx:controller="es.kauron.estraba.controller.DashboardController">
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<AnchorPane fx:id="root" xmlns="http://javafx.com/javafx/8.0.76-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="es.kauron.estraba.controller.DashboardController">
|
||||||
<JFXTabPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="1000.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<JFXTabPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="1000.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<Tab fx:id="tabDashboard" styleClass=".estraba.dashboard" text="%tab.dashboard">
|
<Tab fx:id="tabDashboard" styleClass=".estraba.dashboard" text="%tab.dashboard">
|
||||||
<VBox prefHeight="200.0" prefWidth="100.0">
|
<VBox prefHeight="200.0" prefWidth="100.0">
|
||||||
|
@ -201,7 +210,7 @@
|
||||||
</VBox>
|
</VBox>
|
||||||
</HBox>
|
</HBox>
|
||||||
</VBox>
|
</VBox>
|
||||||
<PieChart fx:id="zoneChart" labelsVisible="false" legendVisible="true" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="360.0" startAngle="90" HBox.hgrow="ALWAYS">
|
<PieChart fx:id="zoneChart" labelsVisible="false" legendVisible="true" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="360.0" startAngle="90">
|
||||||
</PieChart>
|
</PieChart>
|
||||||
<VBox layoutX="15.0" layoutY="15.0" minHeight="360.0" minWidth="300.0" prefHeight="300.0">
|
<VBox layoutX="15.0" layoutY="15.0" minHeight="360.0" minWidth="300.0" prefHeight="300.0">
|
||||||
<HBox alignment="CENTER" layoutX="10.0" layoutY="10.0" minHeight="-Infinity" minWidth="-Infinity" prefHeight="90.0" prefWidth="200.0" HBox.hgrow="ALWAYS" VBox.vgrow="ALWAYS">
|
<HBox alignment="CENTER" layoutX="10.0" layoutY="10.0" minHeight="-Infinity" minWidth="-Infinity" prefHeight="90.0" prefWidth="200.0" HBox.hgrow="ALWAYS" VBox.vgrow="ALWAYS">
|
||||||
|
@ -346,18 +355,11 @@
|
||||||
</HBox>
|
</HBox>
|
||||||
</VBox>
|
</VBox>
|
||||||
</HBox>
|
</HBox>
|
||||||
<Label fx:id="labelMotivationLower" alignment="CENTER" focusTraversable="false" maxWidth="1.7976931348623157E308" text="%label.motivation">
|
<HBox alignment="CENTER" spacing="10.0">
|
||||||
<font>
|
<children>
|
||||||
<Font name="Roboto" size="56.0" />
|
<JFXButton minWidth="70.0" onAction="#loadFile" style="-fx-background-color: #fc4c02;" text="Load another file" textAlignment="CENTER" textFill="WHITE" />
|
||||||
</font>
|
</children>
|
||||||
<VBox.margin>
|
</HBox>
|
||||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
|
||||||
</VBox.margin>
|
|
||||||
<styleClass>
|
|
||||||
<String fx:value=".estraba.dashboard.motivation" />
|
|
||||||
<String fx:value=".estraba.dashboard.motivation.lower" />
|
|
||||||
</styleClass>
|
|
||||||
</Label>
|
|
||||||
</VBox>
|
</VBox>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab fx:id="tabMap" text="%tab.map">
|
<Tab fx:id="tabMap" text="%tab.map">
|
||||||
|
@ -456,8 +458,5 @@
|
||||||
</LineChart>
|
</LineChart>
|
||||||
</VBox>
|
</VBox>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab fx:id="tabSettings" text="%tab.settings">
|
|
||||||
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
|
|
||||||
</Tab>
|
|
||||||
</JFXTabPane>
|
</JFXTabPane>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
|
|
Reference in a new issue