kauron/estraba
Archived
1
0
Fork 0

Moved loading code to Task (not working)

This commit is contained in:
Carlos Galindo 2016-05-20 13:52:10 +02:00
parent 74cc5a6579
commit 2715b66d5b
Signed by: kauron
GPG key ID: 83E68706DEE119A3
8 changed files with 224 additions and 135 deletions

View file

@ -24,7 +24,6 @@
package es.kauron.estraba;
import es.kauron.estraba.controller.DashboardController;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
@ -51,7 +50,7 @@ public class App extends Application {
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(
App.class.getResource("fxml/Dashboard.fxml"), GENERAL_BUNDLE);
App.class.getResource("fxml/Splash.fxml"), GENERAL_BUNDLE);
Parent root = loader.load();
stage.getIcons().add(new Image(App.class.getResource("img/icon.png").toString()));
@ -59,7 +58,11 @@ public class App extends Application {
stage.setResizable(false);
stage.setScene(new Scene(root));
// Begin awesomewm code
stage.setMinHeight(500);
stage.setMinWidth(300);
stage.setResizable(false);
// End awesomewm code
stage.show();
loader.<DashboardController>getController().postinit();
}
}

View file

@ -2,12 +2,14 @@ package es.kauron.estraba.controller;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXSnackbar;
import com.jfoenix.controls.JFXSpinner;
import com.lynden.gmapsfx.GoogleMapView;
import com.lynden.gmapsfx.MapComponentInitializedListener;
import com.lynden.gmapsfx.javascript.object.*;
import com.lynden.gmapsfx.shapes.Polyline;
import com.lynden.gmapsfx.shapes.PolylineOptions;
import es.kauron.estraba.App;
import es.kauron.estraba.model.DataBundle;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
@ -15,28 +17,14 @@ import javafx.fxml.Initializable;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.PieChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.stage.FileChooser;
import jgpx.model.analysis.Chunk;
import jgpx.model.analysis.TrackData;
import jgpx.model.gpx.Track;
import jgpx.model.jaxb.GpxType;
import jgpx.model.jaxb.TrackPointExtensionT;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.net.URL;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.ResourceBundle;
/**
@ -59,13 +47,15 @@ public class DashboardController implements Initializable, MapComponentInitializ
private Label valueHRAvg, valueHRMin, valueHRMax, valueSpeedAvg, valueSpeedMax, valueCadenceAvg, valueCadenceMax,
valueDate, valueTime, valueActiveTime, valueTotalTime, valueDistance, valueElevation, labelMotivationUpper,
valueAscent, valueDescent, labelMotivatorLower;
@FXML
private JFXSpinner mapSpinner;
@FXML
private PieChart zoneChart;
@FXML
private GoogleMapView mapView;
private TrackData track;
private ObservableList<Chunk> chunks;
@FXML
private JFXButton elevationButton, speedButton, hrButton, cadenceButton;
@ -77,8 +67,6 @@ public class DashboardController implements Initializable, MapComponentInitializ
private LineChart<Double, Double> speedChart, hrChart, cadenceChart, mapChart;
private JFXSnackbar snackbar;
private static final double DISTANCE_EPSILON = 1E-6;
private static final double KILOMETER_CUTOFF = 10000;
@Override
public void initialize(URL location, ResourceBundle resources) {
@ -117,123 +105,41 @@ public class DashboardController implements Initializable, MapComponentInitializ
}
}
public void postinit() {
void postInit(DataBundle bundle) {
snackbar.registerSnackbarContainer(root);
try {load();} catch (JAXBException e) {e.printStackTrace();}
loadTrack(bundle);
}
private void loadTrack(TrackData track) {
valueHRAvg.setText(track.getAverageHeartrate()
+ App.GENERAL_BUNDLE.getString("unit.bpm"));
valueHRMax.setText(track.getMaxHeartrate()
+ App.GENERAL_BUNDLE.getString("unit.bpm"));
valueHRMin.setText(track.getMinHeartRate()
+ App.GENERAL_BUNDLE.getString("unit.bpm"));
private void loadTrack(DataBundle bundle) {
valueHRAvg.setText(bundle.HRAvg);
valueHRMax.setText(bundle.HRMax);
valueHRMin.setText(bundle.HRMin);
valueSpeedAvg.setText(bundle.speedAvg);
valueSpeedMax.setText(bundle.speedMax);
valueCadenceAvg.setText(bundle.cadenceAvg);
valueCadenceMax.setText(bundle.cadenceMax);
valueDate.setText(bundle.date);
valueTime.setText(bundle.time);
valueActiveTime.setText(bundle.activeTime);
valueTotalTime.setText(bundle.totalTime);
valueDistance.setText(bundle.distance);
valueElevation.setText(bundle.elevation);
valueAscent.setText(bundle.ascent);
valueDescent.setText(bundle.descent);
// speed is given as m/s
valueSpeedAvg.setText(String.format("%.2f", track.getAverageSpeed() * 3.6)
+ App.GENERAL_BUNDLE.getString("unit.kmph"));
valueSpeedMax.setText(String.format("%.2f", track.getMaxSpeed() * 3.6)
+ App.GENERAL_BUNDLE.getString("unit.kmph"));
valueCadenceAvg.setText(track.getAverageCadence()
+ App.GENERAL_BUNDLE.getString("unit.hz"));
valueCadenceMax.setText(track.getMaxCadence()
+ App.GENERAL_BUNDLE.getString("unit.hz"));
valueDate.setText(track.getStartTime().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)));
valueTime.setText(track.getStartTime().format(DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM)));
valueActiveTime.setText(LocalTime.MIDNIGHT.plus(track.getMovingTime())
.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
valueTotalTime.setText(App.GENERAL_BUNDLE.getString("time.of")
+ LocalTime.MIDNIGHT.plus(track.getTotalDuration())
.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
if (track.getTotalDistance() > KILOMETER_CUTOFF) {
valueDistance.setText(String.format("%.2f", track.getTotalDistance() / 1000)
+ App.GENERAL_BUNDLE.getString("unit.km"));
} else {
valueDistance.setText(String.format("%.2f", track.getTotalDistance())
+ App.GENERAL_BUNDLE.getString("unit.m"));
}
valueElevation.setText((int)(track.getTotalAscent() - track.getTotalDescend())
+ App.GENERAL_BUNDLE.getString("unit.m"));
valueAscent.setText((int)track.getTotalAscent()
+ App.GENERAL_BUNDLE.getString("unit.m"));
valueDescent.setText((int)track.getTotalDescend()
+ App.GENERAL_BUNDLE.getString("unit.m"));
// create charts data
XYChart.Series<Double, Double> elevationChartData = new XYChart.Series<>();
XYChart.Series<Double, Double> speedChartData = new XYChart.Series<>();
XYChart.Series<Double, Double> hrChartData = new XYChart.Series<>();
XYChart.Series<Double, Double> cadenceChartData = new XYChart.Series<>();
// traverse the chunks
ObservableList<Chunk> chunks = track.getChunks();
double currentDistance = 0.0;
double currentHeight = chunks.get(0).getFirstPoint().getElevation();
for (Chunk chunk : chunks) {
currentDistance += chunk.getDistance();
if (chunk.getDistance() < DISTANCE_EPSILON) continue;
currentHeight += chunk.getAscent() - chunk.getDescend();
elevationChartData.getData().add(new XYChart.Data<>(currentDistance, currentHeight));
speedChartData.getData().add(new XYChart.Data<>(currentDistance, chunk.getSpeed()*3.6)); // m/s
hrChartData.getData().add(new XYChart.Data<>(currentDistance, chunk.getAvgHeartRate()));
cadenceChartData.getData().add(new XYChart.Data<>(currentDistance, chunk.getAvgCadence()));
String zone;
if (chunk.getAvgHeartRate() > 170) zone = App.GENERAL_BUNDLE.getString("zone.anaerobic");
else if (chunk.getAvgHeartRate() > 150) zone = App.GENERAL_BUNDLE.getString("zone.threshold");
else if (chunk.getAvgHeartRate() > 130) zone = App.GENERAL_BUNDLE.getString("zone.tempo");
else if (chunk.getAvgHeartRate() > 110) zone = App.GENERAL_BUNDLE.getString("zone.endurance");
else zone = App.GENERAL_BUNDLE.getString("zone.recovery");
boolean pieFound = false;
for (PieChart.Data d : zoneChart.getData()){
if (d.getName().equals(zone)) {
pieFound = true;
d.setPieValue(d.getPieValue() + 1);
}
}
if (!pieFound) zoneChart.getData().add( new PieChart.Data(zone, 1) );
}
zoneChart.setData(bundle.pieData);
// populate the charts
elevationChart.getData().add(elevationChartData);
speedChart.getData().add(speedChartData);
hrChart.getData().add(hrChartData);
cadenceChart.getData().add(cadenceChartData);
elevationChart.getData().add(bundle.elevationSeries);
speedChart.getData().add(bundle.speedSeries);
hrChart.getData().add(bundle.hrSeries);
cadenceChart.getData().add(bundle.cadenceSeries);
//initialize map
chunks = bundle.chunks;
mapView.addMapInializedListener(this);
}
private void load() throws JAXBException {
FileChooser fileChooser = new FileChooser();
fileChooser.getExtensionFilters().add(
new FileChooser.ExtensionFilter(App.GENERAL_BUNDLE.getString("app.extension.filter.name"), "*.gpx"));
File file = fileChooser.showOpenDialog(root.getScene().getWindow());
if (file == null) return;
String name = file.getName();
JAXBContext jaxbContext = JAXBContext.newInstance(GpxType.class, TrackPointExtensionT.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
@SuppressWarnings("unchecked")
JAXBElement<Object> jaxbElement = (JAXBElement<Object>) unmarshaller.unmarshal(file);
GpxType gpx = (GpxType) jaxbElement.getValue();
if (gpx != null) {
track = new TrackData(new Track(gpx.getTrk().get(0)));
loadTrack(track);
snackbar.show("GPX file: " + name + "successfully loaded", 3000);
} else {
snackbar.show("Error loading GPX file: " + name, 3000);
}
}
@Override
public void mapInitialized() {
// When the JS init is done
@ -260,10 +166,10 @@ public class DashboardController implements Initializable, MapComponentInitializ
// Prepare an array with LatLong objects
MVCArray pathArray = new MVCArray();
pathArray.push(new LatLong( // first step of the route
track.getChunks().get(0).getFirstPoint().getLatitude(),
track.getChunks().get(0).getFirstPoint().getLongitude()
chunks.get(0).getFirstPoint().getLatitude(),
chunks.get(0).getFirstPoint().getLongitude()
));
track.getChunks().forEach(chunk -> {
chunks.forEach(chunk -> {
double lat = chunk.getLastPoint().getLatitude();
double lon = chunk.getLastPoint().getLongitude();
coord[N] = Math.max(lat, coord[N]);
@ -283,9 +189,8 @@ public class DashboardController implements Initializable, MapComponentInitializ
new LatLong(coord[N], coord[E])
));
map.setZoom(getBoundsZoomLevel(coord, mapView.getHeight(), mapView.getWidth()));
// Print some debug info
System.err.printf("Bound to coords: %.2fN, %.2S, %.2fE, %.2fW\n", coord[N], coord[S], coord[E], coord[W]);
System.err.printf("Selected zoom: %d\n", map.getZoom());
mapView.setVisible(true);
mapSpinner.setVisible(false);
}
/**

View file

@ -1,15 +1,27 @@
package es.kauron.estraba.controller;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXSnackbar;
import com.jfoenix.controls.JFXSpinner;
import es.kauron.estraba.App;
import es.kauron.estraba.model.DataBundle;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
@ -20,6 +32,9 @@ import java.util.ResourceBundle;
public class SplashController implements Initializable{
@FXML
private AnchorPane root;
@FXML
private ImageView imgLogo;
@ -32,17 +47,70 @@ public class SplashController implements Initializable{
@FXML
private JFXButton buttonLoad;
private JFXSnackbar snackbar;
private File file;
@FXML
void loadGPXFile(ActionEvent event) {
private void loadGPXFile(ActionEvent event) {
buttonLoad.setVisible(false);
labelWelcome.setVisible(false);
spinner.setVisible(true);
snackbar.registerSnackbarContainer(root);
snackbar.show("Loading file", 5000);
FileChooser fileChooser = new FileChooser();
fileChooser.getExtensionFilters().add(
new FileChooser.ExtensionFilter(App.GENERAL_BUNDLE.getString("app.extension.filter.name"), "*.gpx"));
file = fileChooser.showOpenDialog(root.getScene().getWindow());
if (file == null) {
errorLoading();
return;
}
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
try {
DataBundle bundle = DataBundle.loadFrom(file);
FXMLLoader loader = new FXMLLoader(
App.class.getResource("fxml/Dashboard.fxml"), App.GENERAL_BUNDLE);
Parent root = loader.load();
Stage stage = new Stage();
stage.getIcons().add(new Image(App.class.getResource("img/icon.png").toString()));
stage.setTitle(App.GENERAL_BUNDLE.getString("app.title"));
stage.setResizable(false);
stage.setScene(new Scene(root));
// Begin awesomewm code
stage.setMinHeight(500);
stage.setMinWidth(800);
stage.setResizable(false);
// End awesomewm code
stage.show();
loader.<DashboardController>getController().postInit(bundle);
Platform.runLater(() -> ((Stage) root.getScene().getWindow()).close());
} catch (IOException e) {
errorLoading();
}
return null;
}
};
Thread t = new Thread(task);
t.setDaemon(true);
t.start();
}
@Override
public void initialize(URL location, ResourceBundle resources) {
imgLogo.setImage(new Image(App.class.getResourceAsStream("img/splash.png")));
imgLogo.setImage(new Image(App.class.getResourceAsStream("img/strava-transparent.png")));
snackbar = new JFXSnackbar();
}
private void errorLoading() {
buttonLoad.setVisible(true);
labelWelcome.setVisible(true);
spinner.setVisible(false);
snackbar.show("Error loading file", 3000);
}
}

View file

@ -0,0 +1,107 @@
package es.kauron.estraba.model;
import es.kauron.estraba.App;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.chart.PieChart;
import javafx.scene.chart.XYChart;
import jgpx.model.analysis.Chunk;
import jgpx.model.analysis.TrackData;
import jgpx.model.gpx.Track;
import jgpx.model.jaxb.GpxType;
import jgpx.model.jaxb.TrackPointExtensionT;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
public class DataBundle {
public static final int N = 0, S = 1, E = 2, W = 3;
private static final double DISTANCE_EPSILON = 1E-6;
private static final double KILOMETER_CUTOFF = 10000;
public String HRAvg, HRMax, HRMin, speedAvg, speedMax, cadenceAvg, cadenceMax;
public String date, time, activeTime, totalTime, distance, elevation, ascent, descent;
public XYChart.Series<Double, Double> elevationSeries = new XYChart.Series<>(),
speedSeries = new XYChart.Series<>(),
hrSeries = new XYChart.Series<>(),
cadenceSeries = new XYChart.Series<>();
public ObservableList<PieChart.Data> pieData = FXCollections.emptyObservableList();
public ObservableList<Chunk> chunks;
public static DataBundle loadFrom(File file) throws Exception {
String name = file.getName();
JAXBElement<Object> jaxbElement;
JAXBContext jaxbContext = JAXBContext.newInstance(GpxType.class, TrackPointExtensionT.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
jaxbElement = (JAXBElement<Object>) unmarshaller.unmarshal(file);
GpxType gpx = (GpxType) jaxbElement.getValue();
if (gpx == null) throw new Exception();
return new DataBundle(new TrackData(new Track(gpx.getTrk().get(0))));
}
private DataBundle(TrackData track) {
HRAvg = track.getAverageHeartrate() + App.GENERAL_BUNDLE.getString("unit.bpm");
HRMax = (track.getMaxHeartrate() + App.GENERAL_BUNDLE.getString("unit.bpm"));
HRMin = track.getMinHeartRate() + App.GENERAL_BUNDLE.getString("unit.bpm");
// speed is given as m/s
speedAvg = String.format("%.2f", track.getAverageSpeed() * 3.6) + App.GENERAL_BUNDLE.getString("unit.kmph");
speedMax = String.format("%.2f", track.getMaxSpeed() * 3.6) + App.GENERAL_BUNDLE.getString("unit.kmph");
cadenceAvg = track.getAverageCadence() + App.GENERAL_BUNDLE.getString("unit.hz");
cadenceMax = track.getMaxCadence() + App.GENERAL_BUNDLE.getString("unit.hz");
date = track.getStartTime().format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL));
time = track.getStartTime().format(DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM));
activeTime = LocalTime.MIDNIGHT.plus(track.getMovingTime()).format(DateTimeFormatter.ofPattern("HH:mm:ss"));
totalTime = App.GENERAL_BUNDLE.getString("time.of")
+ LocalTime.MIDNIGHT.plus(track.getTotalDuration()).format(DateTimeFormatter.ofPattern("HH:mm:ss"));
if (track.getTotalDistance() > KILOMETER_CUTOFF) {
distance = String.format("%.2f", track.getTotalDistance() / 1000) + App.GENERAL_BUNDLE.getString("unit.km");
} else {
distance = String.format("%.2f", track.getTotalDistance()) + App.GENERAL_BUNDLE.getString("unit.m");
}
elevation = (int)(track.getTotalAscent() - track.getTotalDescend()) + App.GENERAL_BUNDLE.getString("unit.m");
ascent = (int)track.getTotalAscent() + App.GENERAL_BUNDLE.getString("unit.m");
descent = (int)track.getTotalDescend() + App.GENERAL_BUNDLE.getString("unit.m");
// traverse the chunks
chunks = track.getChunks();
double currentDistance = 0.0;
double currentHeight = chunks.get(0).getFirstPoint().getElevation();
for (Chunk chunk : chunks) {
currentDistance += chunk.getDistance();
if (chunk.getDistance() < DISTANCE_EPSILON) continue;
currentHeight += chunk.getAscent() - chunk.getDescend();
elevationSeries.getData().add(new XYChart.Data<>(currentDistance, currentHeight));
speedSeries.getData().add(new XYChart.Data<>(currentDistance, chunk.getSpeed()*3.6)); // m/s
hrSeries.getData().add(new XYChart.Data<>(currentDistance, chunk.getAvgHeartRate()));
cadenceSeries.getData().add(new XYChart.Data<>(currentDistance, chunk.getAvgCadence()));
String zone;
if (chunk.getAvgHeartRate() > 170) zone = App.GENERAL_BUNDLE.getString("zone.anaerobic");
else if (chunk.getAvgHeartRate() > 150) zone = App.GENERAL_BUNDLE.getString("zone.threshold");
else if (chunk.getAvgHeartRate() > 130) zone = App.GENERAL_BUNDLE.getString("zone.tempo");
else if (chunk.getAvgHeartRate() > 110) zone = App.GENERAL_BUNDLE.getString("zone.endurance");
else zone = App.GENERAL_BUNDLE.getString("zone.recovery");
boolean pieFound = false;
for (PieChart.Data d : pieData){
if (d.getName().equals(zone)) {
pieFound = true;
d.setPieValue(d.getPieValue() + 1);
}
}
if (!pieFound) pieData.add( new PieChart.Data(zone, 1) );
}
}
}

View file

@ -8,7 +8,7 @@
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.StackPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="300.0" styleClass="background" stylesheets="@../css/palette.css" xmlns="http://javafx.com/javafx/8.0.76-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="es.kauron.estraba.controller.SplashController">
<AnchorPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="300.0" styleClass="background" stylesheets="@../css/palette.css" xmlns="http://javafx.com/javafx/8.0.76-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="es.kauron.estraba.controller.SplashController">
<ImageView fx:id="imgLogo" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" AnchorPane.leftAnchor="50.0" AnchorPane.rightAnchor="50.0" AnchorPane.topAnchor="25.0">
</ImageView>
<JFXSpinner fx:id="spinner" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />

View file

@ -4,8 +4,10 @@ label.cadence=Cadence
label.distance=Distance
label.elevation=Elevation
label.hr=Heart rate
label.loadGPX=Load GPX file
label.motivation=Welcome!
label.speed=Speed
label.welcome=Welcome!
tab.dashboard=Dashboard
tab.graph=Stats
tab.map=Your Route

View file

@ -4,8 +4,10 @@ label.cadence=Cadencia
label.distance=Distancia
label.elevation=Elevacion
label.hr=YOLO
label.loadGPX=Obrir arxiu GPX
label.motivation=\u00a1Benvinguts!
label.speed=Speed
label.welcome=Benvingut!
tab.dashboard=Sumari
tab.graph=Estad\u00edstiques
tab.map=La teva ruta

View file

@ -4,8 +4,10 @@ label.cadence=Cadencia
label.distance=Distancia
label.elevation=Elevacion
label.hr=Pulsaci\u00f3nes
label.loadGPX=Abrir archivo GPX
label.motivation=\u00a1Bienvenido!
label.speed=Velocidad
label.welcome=¡Bienvenido!
tab.dashboard=Res\u00famen
tab.graph=Estad\u00edsticas
tab.map=Tu ruta