Commit 11efdf92 authored by CLOUARD Regis's avatar CLOUARD Regis
Browse files

Merge branch 'Add_track_animation' into 'master'

Add track animation

See merge request !59
parents 182c6e24 a9b28621
......@@ -7,12 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## 90 [3.1.0] - 2021-08-01
## 90 [3.1.0] - 2021-087-23
### Fixed
- NFC for iOS
- Disable insecure http url for the multimedia pages (tourist mode).
- Disable insecure http url for the multimedia pages (touristic wall mode).
### Added
......
......@@ -152,7 +152,7 @@
"developer_mode": "Developer mode",
"confirm_gps_title": "GPS Function",
"confirm_gps_message": "Vikazimut needs location access even in background to perform. The GPS location is used to control the route, show the current track, and display statistics. No data is exported without permission.",
"result_table_distance_column_tooltip": "The length of the track in meters\n(the distance between the two checkpoints in meters)\nthe overcost of length in percentage",
"result_table_pace_column_tooltip": "The pace expressed in min/km\n(the speed expressed in km/h)",
"result_table_distance_column_tooltip": "The length of the track in meters\n(The distance between the two checkpoints in meters)\nThe overcost of length in percentage",
"result_table_pace_column_tooltip": "The pace expressed in min/km\n(The speed expressed in km/h)",
"website_worldmap_page": "en/worldmap"
}
......@@ -152,7 +152,7 @@
"developer_mode": "Mode développeur",
"confirm_gps_title": "Fonction GPS",
"confirm_gps_message": "Vikazimut nécessite la location de votre appareil y compris en arrière plan pour fonctionner. La position GPS est utilisée pour gérer le parcours, dessiner la trace et afficher des statistiques sur le parcours. Aucune donnée n'est exportée de l'appareil sans votre accord et la trace est intemporalisée.",
"result_table_distance_column_tooltip": "La longueur du trajet effectué entre les deux postes en mètres\n(la distance à vol d'oiseau entre les deux postes en mètres)\nle surcoût de distance en pourcentage",
"result_table_pace_column_tooltip": "La réduction kilométrique en min/km\n(la vitesse en km/h)",
"result_table_distance_column_tooltip": "La longueur du trajet effectué entre les deux postes en mètres\n(La distance à vol d'oiseau entre les deux postes en mètres)\nLe surcoût de distance en pourcentage",
"result_table_pace_column_tooltip": "La réduction kilométrique en min/km\n(La vitesse en km/h)",
"website_worldmap_page": "worldmap"
}
......@@ -11,8 +11,8 @@ import '../result.dart';
class StatisticsResultView extends StatelessWidget {
final Result result;
late final String _title;
final GlobalKey tooltipColumn3Key = new GlobalKey();
final GlobalKey tooltipColumn4Key = new GlobalKey();
final GlobalKey _tooltipColumn3Key = new GlobalKey();
final GlobalKey _tooltipColumn4Key = new GlobalKey();
StatisticsResultView(this.result) {
_title = result.getMap()!.getName();
......@@ -47,53 +47,59 @@ class StatisticsResultView extends StatelessWidget {
List<DataColumn> _buildColumns() {
return [
DataColumn(label: TextCentered(Translations.getString('control_point_index'))),
DataColumn(label: TextCentered(Translations.getString('interval_time'))),
DataColumn(label: Expanded(child: TextCentered(Translations.getString('control_point_index')))),
DataColumn(label: Expanded(child: TextCentered(Translations.getString('interval_time')))),
DataColumn(
label: Row(
children: [
TextCentered(Translations.getString('interval_distance')),
Tooltip(
key: tooltipColumn3Key,
message: Translations.getString("result_table_distance_column_tooltip"),
child: IconButton(
constraints: BoxConstraints(maxHeight: 18, maxWidth: 18),
padding: const EdgeInsets.all(0.0),
icon: Icon(
Icons.help_outline,
color: Colors.white,
size: 16.0,
label: Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextCentered(Translations.getString('interval_distance')),
Tooltip(
key: _tooltipColumn3Key,
message: Translations.getString("result_table_distance_column_tooltip"),
child: IconButton(
constraints: BoxConstraints(maxHeight: 18, maxWidth: 18),
padding: const EdgeInsets.all(0.0),
icon: Icon(
Icons.help_outline,
color: Colors.white,
size: 16.0,
),
onPressed: () {
final dynamic tooltip = _tooltipColumn3Key.currentState;
tooltip.ensureTooltipVisible();
},
),
onPressed: () {
final dynamic tooltip = tooltipColumn3Key.currentState;
tooltip.ensureTooltipVisible();
},
),
),
],
],
),
),
),
DataColumn(
label: Row(
children: [
TextCentered(Translations.getString('interval_pace')),
Tooltip(
key: tooltipColumn4Key,
message: Translations.getString("result_table_pace_column_tooltip"),
child: IconButton(
constraints: BoxConstraints(maxHeight: 18, maxWidth: 18),
padding: const EdgeInsets.all(0.0),
icon: Icon(Icons.help_outline, color: Colors.white, size: 16.0),
onPressed: () {
final dynamic tooltip = tooltipColumn4Key.currentState;
tooltip.ensureTooltipVisible();
},
label: Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextCentered(Translations.getString('interval_pace')),
Tooltip(
key: _tooltipColumn4Key,
message: Translations.getString("result_table_pace_column_tooltip"),
child: IconButton(
constraints: BoxConstraints(maxHeight: 18, maxWidth: 18),
padding: const EdgeInsets.all(0.0),
icon: Icon(Icons.help_outline, color: Colors.white, size: 16.0),
onPressed: () {
final dynamic tooltip = _tooltipColumn4Key.currentState;
tooltip.ensureTooltipVisible();
},
),
),
),
],
],
),
),
),
DataColumn(label: TextCentered(Translations.getString('cumulative_time'))),
DataColumn(label: Expanded(child: TextCentered(Translations.getString('cumulative_time')))),
];
}
......@@ -107,10 +113,10 @@ class StatisticsResultView extends StatelessWidget {
}
List<DataCell> cells = [
DataCell(Center(child: _buildControlNumberCell(timeTable, i))),
DataCell(Center(child:_buildLegTimeCell(timeTable, i))),
DataCell(Center(child:_buildLegDistanceCell(timeTable, i))),
DataCell(Center(child:_buildPaceCell(timeTable, i))),
DataCell(Center(child:_buildCumulatedCell(timeTable, i))),
DataCell(Center(child: _buildLegTimeCell(timeTable, i))),
DataCell(Center(child: _buildLegDistanceCell(timeTable, i))),
DataCell(Center(child: _buildPaceCell(timeTable, i))),
DataCell(Center(child: _buildCumulatedCell(timeTable, i))),
];
DataRow tableRow = DataRow(cells: cells);
rows.add(tableRow);
......@@ -120,11 +126,11 @@ class StatisticsResultView extends StatelessWidget {
DataRow totalTableRow = DataRow(
color: MaterialStateProperty.all(primaryColorDisabled),
cells: [
DataCell(Center(child:TextCenteredBold(Translations.getString('Total')))),
DataCell(Center(child:TextCenteredBold('${result.getTotalTimeWithoutPenaltyAsString()}'))),
DataCell(Center(child:TextCenteredBold('${Translations.formatNumber(totalLength)}\n(${Translations.formatNumber(totalDistance)})\n+${Result.computeOvercost(totalLength, totalDistance)}%'))),
DataCell(Center(child:TextCenteredBold('${formatPace(result.getGlobalPaceInMillis())}\n${formatSpeed(Result.calculateSpeedFromPace(result.getGlobalPaceInMillis()))}'))),
DataCell(Center(child:TextCenteredBold('${result.getTotalTimeWithoutPenaltyAsString()}'))),
DataCell(Center(child: TextCenteredBold(Translations.getString('Total')))),
DataCell(Center(child: TextCenteredBold('${result.getTotalTimeWithoutPenaltyAsString()}'))),
DataCell(Center(child: TextCenteredBold('${Translations.formatNumber(totalLength)}\n(${Translations.formatNumber(totalDistance)})\n+${Result.computeOvercost(totalLength, totalDistance)}%'))),
DataCell(Center(child: TextCenteredBold('${formatPace(result.getGlobalPaceInMillis())}\n${formatSpeed(Result.calculateSpeedFromPace(result.getGlobalPaceInMillis()))}'))),
DataCell(Center(child: TextCenteredBold('${result.getTotalTimeWithoutPenaltyAsString()}'))),
],
);
rows.add(totalTableRow);
......
This diff is collapsed.
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
import 'package:vikazimut/map/geodesic_point.dart';
import 'package:vikazimut/map/view/animated_route_layer.dart';
import 'package:vikazimut/map/view/map_view.dart';
......@@ -8,6 +9,7 @@ import 'abstract_track_view.dart';
class TrackAnimation {
AnimationController? _controller;
Function()? _action;
int _currentLegIndex = 0;
void execute(AbstractRouteTraceViewState parent, MapView mapView, List<GeodesicPoint> track) async {
var animatedRouteLayer = AnimatedRouteLayer(mapView);
......@@ -42,29 +44,36 @@ class TrackAnimation {
_controller?.dispose();
}
var _currentLegIndex = 0;
List<GeodesicPoint> getSubTrack(List<GeodesicPoint> track, int currentTime) {
// TODO Tests
// TODO question garde t-on le temps réel ?
for (int i = _currentLegIndex; i < track.length; i++) {
_currentLegIndex = findCurrentGPSPoint(track, currentTime, _currentLegIndex);
return getSubList(track, currentTime, _currentLegIndex);
}
@visibleForTesting
static int findCurrentGPSPoint(List<GeodesicPoint> track, int currentTime, int currentLegIndex) {
for (int i = currentLegIndex; i < track.length; i++) {
if (track[i].getTimeInMillis() < currentTime) {
_currentLegIndex++;
currentLegIndex++;
} else {
break;
}
}
if (_currentLegIndex > 0) {
var t2 = track[_currentLegIndex].getTimeInMillis();
var t1 = track[_currentLegIndex - 1].getTimeInMillis();
return currentLegIndex;
}
@visibleForTesting
static List<GeodesicPoint> getSubList(List<GeodesicPoint> track, int currentTime, int currentLegIndex) {
if (currentLegIndex > 0) {
var t1 = track[currentLegIndex - 1].getTimeInMillis();
var t2 = track[currentLegIndex].getTimeInMillis();
double x = (currentTime - t1) / (t2 - t1);
var latitude = x * track[_currentLegIndex].getLatitude() + (1.0 - x) * track[_currentLegIndex - 1].getLatitude();
var longitude = x * track[_currentLegIndex].getLongitude() + (1.0 - x) * track[_currentLegIndex - 1].getLongitude();
var list = track.sublist(0, _currentLegIndex - 1);
list.add(new GeodesicPoint(latitude, longitude));
return list;
var latitude = x * track[currentLegIndex].getLatitude() + (1.0 - x) * track[currentLegIndex - 1].getLatitude();
var longitude = x * track[currentLegIndex].getLongitude() + (1.0 - x) * track[currentLegIndex - 1].getLongitude();
var subList = track.sublist(0, currentLegIndex);
subList.add(new GeodesicPoint(latitude, longitude));
return subList;
} else {
return track.sublist(0, 0);
return [];
}
}
......
......@@ -8,7 +8,7 @@ void main() {
const double COORDINATE_PRECISION = 1e-9;
test('_readKmlFile_1', () async {
File xmlFile = new File('test_resources/kml1.xml');
File xmlFile = new File('test/resources/kml1.xml');
LatLonBox bounds = OrienteeringMapKmlFileReader.readFile(xmlFile);
expect(bounds, isNotNull);
expect(bounds.north, moreOrLessEquals(49.218965810, epsilon: COORDINATE_PRECISION));
......@@ -19,7 +19,7 @@ void main() {
});
test('readKmlFile_2', () async {
File xmlFile = new File('test_resources/kml2.xml');
File xmlFile = new File('test/resources/kml2.xml');
LatLonBox bounds = OrienteeringMapKmlFileReader.readFile(xmlFile);
expect(bounds, isNotNull);
expect(bounds.north, moreOrLessEquals(49.23319176120304, epsilon: COORDINATE_PRECISION));
......
......@@ -10,7 +10,7 @@ void main() {
test('Read formatted XML example 1', () async {
final String name = "EnsiCaen";
File xmlFile = new File('test_resources/ensicaen.xml');
File xmlFile = new File('test/resources/ensicaen.xml');
OrienteeringMap map = OrienteeringMapXmlFileReader.readFile(xmlFile, name);
expect(map.getName(), name);
int controlPointsCount = map.getControlPointsCount();
......@@ -28,7 +28,7 @@ void main() {
test('Read formatted XML example 2', () async {
final String name = "Jeune";
File xmlFile = new File('test_resources/mdjeunes.xml');
File xmlFile = new File('test/resources/mdjeunes.xml');
OrienteeringMap map = OrienteeringMapXmlFileReader.readFile(xmlFile, name);
expect(name, map.getName());
int controlPointsCount = map.getControlPointsCount();
......@@ -46,14 +46,14 @@ void main() {
test('Read Xml File URL empty', () async {
final String name = "EnsiCaen";
File xmlFile = new File('test_resources/ensicaen.xml');
File xmlFile = new File('test/resources/ensicaen.xml');
OrienteeringMap map = OrienteeringMapXmlFileReader.readFile(xmlFile, name);
expect(map.getControlPoint(1).getText(), null);
});
test('Read XmlFile when augmented with control point texts', () async {
final String name = "EnsiCaen";
File xmlFile = new File('test_resources/ensicaen-augmented.xml');
File xmlFile = new File('test/resources/ensicaen-augmented.xml');
OrienteeringMap map = OrienteeringMapXmlFileReader.readFile(xmlFile, name);
expect(map.getControlPoint(1).getText(), "Text 1");
expect(map.getControlPoint(2).getText(), "https://google.com");
......
......@@ -86,7 +86,7 @@ void main() {
}
List<GeodesicPoint> readGPXFile(String fileName) {
final file = new File('test_resources/$fileName');
final file = new File('test/resources/$fileName');
String contents = file.readAsStringSync();
// <trkpt lat="49.285776" lon="-0.538634"><time>1970-01-01T00:00:19.967Z</time><ele>94.1</ele>
List<GeodesicPoint> routerPoints = [];
......
This diff is collapsed.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment