kauron/estraba
kauron
/
estraba
Archived
1
0
Fork 0
This repository has been archived on 2022-12-19. You can view files and clone it, but cannot push or open issues or pull requests.
estraba/src/main/java/es/kauron/estraba/controller/DashboardController.java

316 lines
12 KiB
Java

/*
* Copyright (c) 2016 Jesús "baudlord" Vélez Palacios, Carlos "kauron" Santiago Galindo Jiménez
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* If we meet some day, and you think this stuff is worth it, you can buy me a beer in return.
*
*/
package es.kauron.estraba.controller;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXSpinner;
import com.jfoenix.controls.JFXToggleButton;
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;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
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.scene.layout.StackPane;
import javafx.stage.Stage;
import jgpx.model.analysis.Chunk;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
/**
* es.kauron.estraba (estraba)
* Created by baudlord on 5/17/16.
*/
public class DashboardController implements Initializable, MapComponentInitializedListener {
private final int N = 0, S = 1, E = 2, W = 3;
private final double[] coord = new double[4];
@FXML
private AnchorPane root;
@FXML
private Tab tabDashboard, tabMap, tabGraph;
@FXML
private ImageView imgHR, imgSpeed, imgCadence, imgDate, imgDistance, imgElevation;
@FXML
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 ObservableList<Chunk> chunks;
@FXML
private JFXButton elevationButton, speedButton, hrButton, cadenceButton;
@FXML
private AreaChart<Double, Double> elevationChart;
@FXML
private AreaChart<Long, Double> elevationTChart;
@FXML
private LineChart<Double, Double> speedChart, hrChart, cadenceChart, mapChart;
@FXML
private LineChart<Long, Double> speedTChart, hrTChart, cadenceTChart;
@FXML
private JFXToggleButton toggleCharts;
@FXML
private StackPane charts;
@Override
public void initialize(URL location, ResourceBundle resources) {
root.getStylesheets().add(App.class.getResource("css/estraba.css").toExternalForm());
mapView.setVisible(false);
// populate map icons
((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)hrButton.getGraphic()).setImage(new Image(App.class.getResourceAsStream("img/hr.png")));
((ImageView)cadenceButton.getGraphic()).setImage(new Image(App.class.getResourceAsStream("img/cadence.png")));
// set icons of dashboard
imgHR.setImage(new Image(App.class.getResourceAsStream("img/hr.png")));
imgSpeed.setImage(new Image(App.class.getResourceAsStream("img/speed.png")));
imgCadence.setImage(new Image(App.class.getResourceAsStream("img/cadence.png")));
imgDate.setImage(new Image(App.class.getResourceAsStream("img/date.png")));
imgDistance.setImage(new Image(App.class.getResourceAsStream("img/distance.png")));
imgElevation.setImage(new Image(App.class.getResourceAsStream("img/elevation.png")));
toggleCharts.selectedProperty().addListener((observableValue, s, t1) -> {
for (Node n : charts.getChildrenUnmodifiable())
n.setVisible(!n.isVisible());
});
}
@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
private void onMapButton(ActionEvent event) {
XYChart.Series<Double, Double> data = elevationChart.getData().get(0);
String color = "#000000";
JFXButton b = ((JFXButton) event.getSource());
switch (b.getId()) {
case "elevationButton":
data = elevationChart.getData().get(0);
color = "#2c8f30";
break;
case "speedButton":
data = speedChart.getData().get(0);
color = "#c0775f";
break;
case "hrButton":
data = hrChart.getData().get(0);
color = "#df1d00";
break;
case "cadenceButton":
data = cadenceChart.getData().get(0);
color = "#1f31a5";
break;
}
if (mapChart.getData().contains(data)) {
mapChart.getData().remove(data);
} else {
mapChart.getData().add(data);
mapChart.setStyle(String.format(".default-color%d.chart-series-line { -fx-stroke: %s }",
mapChart.getData().size() - 1, color));
}
}
private String randomMotivation() {
return App.GENERAL_BUNDLE.getString("label.welcome");
}
void load(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);
zoneChart.setData(bundle.pieData);
// populate the charts
elevationChart.getData().add(bundle.elevationSeries);
elevationTChart.getData().add(bundle.elevationTSeries);
speedChart.getData().add(bundle.speedSeries);
speedTChart.getData().add(bundle.speedTSeries);
hrChart.getData().add(bundle.hrSeries);
hrTChart.getData().add(bundle.hrTSeries);
cadenceChart.getData().add(bundle.cadenceSeries);
cadenceTChart.getData().add(bundle.cadenceTSeries);
mapChart.setLegendVisible(true);
//initialize map
chunks = bundle.chunks;
mapView.addMapInializedListener(this);
}
@Override
public void mapInitialized() {
// When the JS init is done
coord[0] = Double.MIN_VALUE;
coord[1] = Double.MAX_VALUE;
coord[2] = Double.MIN_VALUE;
coord[3] = Double.MAX_VALUE;
// Create a new map with the appropriate options
GoogleMap map = mapView.createMap(new MapOptions()
.center(new LatLong(0.0, 0.0))
.mapType(MapTypeIdEnum.TERRAIN)
.overviewMapControl(true)
.panControl(false)
.rotateControl(false)
.scaleControl(true)
.streetViewControl(false)
.zoomControl(true)
.zoom(0)
);
// Prepare an array with LatLong objects
MVCArray pathArray = new MVCArray();
pathArray.push(new LatLong( // first step of the route
chunks.get(0).getFirstPoint().getLatitude(),
chunks.get(0).getFirstPoint().getLongitude()
));
chunks.forEach(chunk -> {
double lat = chunk.getLastPoint().getLatitude();
double lon = chunk.getLastPoint().getLongitude();
coord[N] = Math.max(lat, coord[N]);
coord[S] = Math.min(lat, coord[S]);
coord[E] = Math.max(lon, coord[E]);
coord[W] = Math.min(lon, coord[W]);
pathArray.push(new LatLong(lat, lon));
});
map.addMarker(new Marker(new MarkerOptions()
.position(new LatLong(
chunks.get(0).getFirstPoint().getLatitude(),
chunks.get(0).getFirstPoint().getLongitude()))
.title("label.begin")));
map.addMapShape(new Polyline(new PolylineOptions().path(pathArray).strokeColor("#fc4c02")));
map.addMarker(new Marker(new MarkerOptions()
.position(new LatLong(
chunks.get(chunks.size() - 1).getLastPoint().getLatitude(),
chunks.get(chunks.size() - 1).getLastPoint().getLongitude()))
.title("label.end")));
// Adjust the map to the correct center and zoom
mapView.setVisible(true);
mapSpinner.setVisible(false);
mapView.heightProperty().addListener(e -> centerMap());
mapView.widthProperty().addListener(e -> centerMap());
centerMap();
}
private void centerMap() {
mapView.getMap().setZoom(getBoundsZoomLevel(coord, mapView.getHeight(), mapView.getWidth()));
mapView.getMap().fitBounds(new LatLongBounds(
new LatLong(coord[S], coord[W]),
new LatLong(coord[N], coord[E])
));
}
/**
* Method to compute properly the amount of zoom in a GMap
* Many thanks to <a href=http://stackoverflow.com/a/13274361>http://stackoverflow.com/a/13274361</a>
* @param bound Array with coordinates bounds [N, S, E, W]
* @return Zoom from 0 to 21
*/
private int getBoundsZoomLevel(double[] bound, double mapHeight, double mapWidth) {
double latFraction = (latRad(bound[0]) - latRad(bound[1])) / Math.PI;
double lngDiff = bound[2] - bound[3];
double lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;
double latZoom = zoom(mapHeight, 256, latFraction);
double lngZoom = zoom(mapWidth, 256, lngFraction);
return (int) Math.min(Math.min(latZoom, lngZoom), 21);
}
private double zoom(double mapPx, int worldPx, double fraction) {
return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.log(2));
}
private double latRad(double lat) {
double sin = Math.sin(lat * Math.PI / 180);
double radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
}
}