Commit 3a24c4d2 authored by CLOUARD Regis's avatar CLOUARD Regis
Browse files

Added GPS tracking

close #7
parent f3f0db97
# Application Android Vikazimut
# Application Mobile Vikazimut
## Le projet Vikazimut
Vikazimut est un projet d'étudiants en informatique de l'[ENSICAEN](https://www.ensicaen.fr).
Le projet répond à une demande de l'association [Vik'Azim](https://vikazim.fr)
visant la réalisation d'une application Android pour la pratique de la course d'orientation.
visant la réalisation d'une application mobile pour la pratique de la course d'orientation.
L’application mobile Vikazimut a pour objectif de faciliter la pratique de la course
L’application Vikazimut a pour objectif de faciliter la pratique de la course
d’orientation. Elle remplace la carte papier, la boussole et le poinçon de validation
des points de contrôle.
......@@ -20,7 +20,8 @@ L’orienteur utilise l’application pour se repérer à partir de la carte et
aux points de contrôle avec le lecteur de code QR, le lecteur de tag NFC ou la position GPS.
L’application affiche en fin du parcours des statistiques sur la réalisation du parcours :
le temps total, le temps intermédiaire entre chaque balise et le tracé du parcours réalisé.
le temps total, le temps intermédiaire entre chaque balise, le dénivelé positif cumulé et
le tracé du parcours réalisé.
L’application se présente sous deux modes : un mode course où l’orienteur n’est pas aidé pour
sa localisation et un mode promenade où l’orienteur est positionné sur la carte en temps réel.
......
......@@ -19,7 +19,6 @@ import 'package:Vikazimut_flutter/utils/time.dart';
import 'package:audioplayer/audioplayer.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sprintf/sprintf.dart';
import 'package:vibration/vibration.dart';
......@@ -52,6 +51,8 @@ abstract class AbstractModeCourseWidget extends StatelessWidget implements GpsPo
bool _startPressed = false;
OrienteeringMap _map;
MapView? _mapView;
// int _screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
// BroadcastReceiver _compassBroadcastReceiver;
......@@ -152,7 +153,7 @@ abstract class AbstractModeCourseWidget extends StatelessWidget implements GpsPo
Navigator.push(context, MaterialPageRoute(builder: (context) => UserManual()));
break;
case 2:
// forceControlPointValidation();
forceControlPointValidation();
skipCurrentControlPoint();
break;
case 3:
......@@ -192,10 +193,12 @@ abstract class AbstractModeCourseWidget extends StatelessWidget implements GpsPo
case ConnectionState.waiting:
return new Text('Image loading...'); // TODO i18n
default:
if (snapshot.hasError)
if (snapshot.hasError) {
return new Text('Error: ${snapshot.error}');
else
return MapView(snapshot.data!, map.getBoundingBox());
} else {
_mapView = MapView(snapshot.data!, map.getBoundingBox());
return _mapView!;
}
}
},
),
......@@ -213,6 +216,10 @@ abstract class AbstractModeCourseWidget extends StatelessWidget implements GpsPo
return navigatorKey.currentContext!;
}
MapView? getMapView() {
return _mapView;
}
// @override
// void onResume() {
// super.onResume();
......
......@@ -68,7 +68,7 @@ class _ChronometerState extends State<Chronometer> {
}
void update(timeString) {
print("update : $timeString");
// print("update : $timeString");
setState(() {
_timeString = timeString;
});
......
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
// /// Defines the main theme color.
// final MaterialColor themeMaterialColor = BaseflowPluginExample.createMaterialColor(
// const Color.fromRGBO(48, 49, 60, 1));
//
// void main() {
// runApp(BaseflowPluginExample(
// pluginName: 'Geolocator',
// githubURL: 'https://github.com/Baseflow/flutter-geolocator',
// pubDevURL: 'https://pub.dev/packages/geolocator',
// pages: [GeolocatorWidget.createPage()],
// ));
// }
//
// /// Example [Widget] showing the functionalities of the geolocator plugin
// class GeolocatorWidget extends StatefulWidget {
// /// Utility method to create a page with the Baseflow templating.
// static ExamplePage createPage() {
// return ExamplePage(Icons.location_on, (context) => GeolocatorWidget());
// }
//
// @override
// _GeolocatorWidgetState createState() => _GeolocatorWidgetState();
// }
//
// class _GeolocatorWidgetState extends State<GeolocatorWidget> {
// final List<_PositionItem> _positionItems = <_PositionItem>[];
// StreamSubscription<Position> _positionStreamSubscription;
//
// @override
// Widget build(BuildContext context) {
// return Scaffold(
// backgroundColor: Theme.of(context).backgroundColor,
// body: ListView.builder(
// itemCount: _positionItems.length,
// itemBuilder: (context, index) {
// final positionItem = _positionItems[index];
//
// if (positionItem.type == _PositionItemType.permission) {
// return ListTile(
// title: Text(positionItem.displayValue,
// textAlign: TextAlign.center,
// style: TextStyle(
// color: Colors.white,
// fontWeight: FontWeight.bold,
// )),
// );
// } else {
// return Card(
// child: ListTile(
// tileColor: themeMaterialColor,
// title: Text(
// positionItem.displayValue,
// style: TextStyle(color: Colors.white),
// ),
// ),
// );
// }
// },
// ),
// floatingActionButton: Stack(
// children: <Widget>[
// Positioned(
// bottom: 80.0,
// right: 10.0,
// child: FloatingActionButton.extended(
// onPressed: () async {
// await Geolocator.getLastKnownPosition().then((value) => {
// _positionItems.add(_PositionItem(
// _PositionItemType.position, value.toString()))
// });
// setState(() {});
// },
// label: Text("getLastKnownPosition"),
// ),
// ),
// Positioned(
// bottom: 10.0,
// right: 10.0,
// child: FloatingActionButton.extended(
// onPressed: () async {
// await Geolocator.getCurrentPosition().then((value) => {
// _positionItems.add(_PositionItem(
// _PositionItemType.position, value.toString()))
// });
//
// setState(
// () {},
// );
// },
// label: Text("getCurrentPosition")),
// ),
// Positioned(
// bottom: 150.0,
// right: 10.0,
// child: FloatingActionButton.extended(
// onPressed: _toggleListening,
// label: Text(() {
// if (_positionStreamSubscription == null) {
// return "getPositionStream = null";
// } else {
// return "getPositionStream = ${_positionStreamSubscription.isPaused ? "off" : "on"}";
// }
// }()),
// backgroundColor: _determineButtonColor(),
// ),
// ),
// Positioned(
// bottom: 220.0,
// right: 10.0,
// child: FloatingActionButton.extended(
// onPressed: () => setState(_positionItems.clear),
// label: Text("clear positions"),
// ),
// ),
// Positioned(
// bottom: 290.0,
// right: 10.0,
// child: FloatingActionButton.extended(
// onPressed: () async {
// await Geolocator.checkPermission().then((value) => {
// _positionItems.add(_PositionItem(
// _PositionItemType.permission, value.toString()))
// });
// setState(() {});
// },
// label: Text("getPermissionStatus"),
// ),
// ),
// ],
// ),
// );
// }
//
// bool _isListening() => !(_positionStreamSubscription == null ||
// _positionStreamSubscription.isPaused);
//
// Color _determineButtonColor() {
// return _isListening() ? Colors.green : Colors.red;
// }
//
// void _toggleListening() {
// if (_positionStreamSubscription == null) {
// final positionStream = Geolocator.getPositionStream();
// _positionStreamSubscription = positionStream.handleError((error) {
// _positionStreamSubscription.cancel();
// _positionStreamSubscription = null;
// }).listen((position) => setState(() => _positionItems.add(
// _PositionItem(_PositionItemType.position, position.toString()))));
// _positionStreamSubscription.pause();
// }
//
// setState(() {
// if (_positionStreamSubscription.isPaused) {
// _positionStreamSubscription.resume();
// } else {
// _positionStreamSubscription.pause();
// }
// });
// }
//
// @override
// void dispose() {
// if (_positionStreamSubscription != null) {
// _positionStreamSubscription.cancel();
// _positionStreamSubscription = null;
// }
//
// super.dispose();
// }
// }
//
// enum _PositionItemType {
// permission,
// position,
// }
//
// class _PositionItem {
// _PositionItem(this.type, this.displayValue);
//
// final _PositionItemType type;
// final String displayValue;
// }
\ No newline at end of file
......@@ -7,8 +7,8 @@ import 'package:geolocator/geolocator.dart';
import 'kalman_filter.dart';
class GpsTrackerService {
static final int MIN_TIME_FOR_UPDATE_IN_S = 2;
static final int MIN_DISTANCE_FOR_UPDATE_IN_M = 1;
static const int MIN_TIME_FOR_UPDATE_IN_S = 2;
static const int MIN_DISTANCE_FOR_UPDATE_IN_M = 1;
/* Accuracy is defined as the radius of 68% confidence in meters. */
static const int MAX_ACCURACY_IN_M = 30;
......@@ -31,13 +31,6 @@ class GpsTrackerService {
GpsTrackerService();
// static final int SERVICE_NOTIFICATION_ID = 12345678;
// @Override
// public void onCreate() {
// super.onCreate();
// _locationListener = new InnerLocationListener();
// }
void startGPS() {
if (_positionStreamSubscription == null) {
final positionStream = Geolocator.getPositionStream(
......
......@@ -4,6 +4,7 @@ import 'package:Vikazimut_flutter/map/orienteering_map.dart';
import 'package:Vikazimut_flutter/map/view/LocationLayer.dart';
import 'package:Vikazimut_flutter/map/view/MapView.dart';
import 'package:Vikazimut_flutter/result/Result.dart';
import 'package:Vikazimut_flutter/result/SportResultDisplayActivity.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
......@@ -22,8 +23,7 @@ class SportModeCourseWidget extends AbstractModeCourseWidget {
@override
void displayResult(int itemIndex) {
print("ici displayresult");
Navigator.push(getContext(), MaterialPageRoute(builder: (context) => Result(itemIndex)));
Navigator.push(getContext(), MaterialPageRoute(builder: (context) => SportResultDisplayActivity(itemIndex)));
// Intent intent = new Intent(this, SportResultDisplayActivity.class);
}
......@@ -76,7 +76,7 @@ class SportModeCourseWidget extends AbstractModeCourseWidget {
// @Override
// public void onClick( DialogInterface dialog, int which ) {
// dialog.dismiss();
// onCallRescue();
onCallAssistance();
// }
// })
// .setNegativeButton(getString(R.string.no_aid), null);
......@@ -87,42 +87,42 @@ class SportModeCourseWidget extends AbstractModeCourseWidget {
void handleStopCourse(Course course) {
course.setAssistanceCount(_assistanceCount);
super.handleStopCourse(course);
if (_locationNewOverlay != null) {
_locationNewOverlay?.stop();
}
// if (_locationNewOverlay != null) {
// _locationNewOverlay?.stop();
// }
}
void onCallRescue() {
// MapView mapView = getMapView();
// addLocationOverlayOnMap(mapView);
// recenter();
void onCallAssistance() {
// MapView mapView = getMapView();
// addLocationOverlayOnMap(mapView);
// recenter();
}
void addLocationOverlayOnMap(final MapView mapView) {
if (_locationNewOverlay == null && isStarted()) {
// _locationNewOverlay = new LocationLayer(mapView);
// mapView.addLayer(_locationNewOverlay);
// _locationNewOverlay?.start();
// final Handler handler = new Handler();
// handler.postDelayed(new Runnable() {
// @Override
// public void run() {
// if (_locationNewOverlay != null) {
// if (isStarted()) {
// _assistanceCount++;
// }
// mapView.removeLayer(_locationNewOverlay);
// _locationNewOverlay = null;
// }
// }
// }, 15000);
}
// if (_locationNewOverlay == null && isStarted()) {
// _locationNewOverlay = new LocationLayer(mapView);
// mapView.addLayer(_locationNewOverlay);
// _locationNewOverlay?.start();
// final Handler handler = new Handler();
// handler.postDelayed(new Runnable() {
// @override
// public void run() {
// if (_locationNewOverlay != null) {
// if (isStarted()) {
// _ assistanceCount++;
// }
// mapView.removeLayer(_locationNewOverlay);
// _locationNewOverlay = null;
// }
// }
// }, 15000);
// }
}
void recenter() {
// GeodesicPoint lastLocation = getLastLocation();
// if (lastLocation != null) {
// getMapView().setCenter(lastLocation);
// GeodesicPoint? lastPosition = getLastLocation();
// if (lastPosition != null) {
// getMapView().setCenter(lastPosition);
// }
}
}
......@@ -6,6 +6,7 @@ import 'package:Vikazimut_flutter/map/view/FlatRouteLayer.dart';
import 'package:Vikazimut_flutter/map/view/LocationLayer.dart';
import 'package:Vikazimut_flutter/map/view/MapView.dart';
import 'package:Vikazimut_flutter/result/Result.dart';
import 'package:Vikazimut_flutter/result/WalkResultDisplayActivity.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
......@@ -16,35 +17,34 @@ class WalkModeCourseWidget extends AbstractModeCourseWidget {
LocationLayer? _locationNewOverlay;
WalkModeCourseWidget(OrienteeringMap selectedMap) : super(selectedMap) {
// setContentView(R.layout.activity_walk_course);
// super.onCreate(savedInstanceState);
setChronometerVisibility(false);
// MapView mapView = getMapView();
// addRouteOverlayOnMap(mapView);
// addLocationOverlayOnMap(mapView);
MapView? mapView = getMapView();
if (mapView != null) {
addRouteOverlayOnMap(mapView);
addLocationOverlayOnMap(mapView);
}
}
@override
void initializeCourseMenu(Menu? menu, ValidationMode validationFormat) {
// TODO: implement initializeCourseMenu
// Nothing to do
}
@override
void displayResult(int itemIndex) {
Navigator.push(getContext(), MaterialPageRoute(builder: (context) => Result(itemIndex)));
// Intent activity = new Intent(this, WalkResultDisplayActivity.class);
Navigator.push(getContext(), MaterialPageRoute(builder: (context) => WalkResultDisplayActivity(itemIndex)));
}
@override
void handleStartCourse() {
super.handleStartCourse();
// _locationNewOverlay.start();
super.handleStartCourse();
_locationNewOverlay?.start();
}
@override
void handleStopCourse(Course course) {
super.handleStopCourse(course);
// _locationNewOverlay.stop();
super.handleStopCourse(course);
_locationNewOverlay?.stop();
}
@override
......@@ -58,21 +58,20 @@ class WalkModeCourseWidget extends AbstractModeCourseWidget {
}
}
void onCallCenter() {
GeodesicPoint? lastLocation = getLastLocation();
if (lastLocation != null) {
// getMapView().setCenter(lastLocation);
getMapView()!.setCenter(lastLocation);
}
}
void addLocationOverlayOnMap(MapView mapView) {
// _locationNewOverlay = new LocationLayer(mapView);
// mapView.addLayer(_locationNewOverlay);
_locationNewOverlay = new LocationLayer(mapView);
mapView.addLayer(_locationNewOverlay!);
}
void addRouteOverlayOnMap(MapView mapView) {
// _routeOverlay = new FlatRouteLayer(mapView);
// mapView.addLayer(_routeOverlay);
_routeOverlay = new FlatRouteLayer(mapView);
mapView.addLayer(_routeOverlay!);
}
}
import 'package:Vikazimut_flutter/database/course_result_entity.dart';
import 'package:Vikazimut_flutter/result/Result.dart';
import 'package:Vikazimut_flutter/result/SportResultDisplayActivity.dart';
import 'package:Vikazimut_flutter/translations.dart';
import 'package:flutter/material.dart';
......@@ -26,7 +27,7 @@ class HistoryItem extends StatelessWidget {
onPressed: () {
print("valeur id = ${item.totalTime}" );
print("valeur id = ${item.id}" );
Navigator.push(context, MaterialPageRoute(builder: (context) => Result(item.id!)));
Navigator.push(context, MaterialPageRoute(builder: (context) => SportResultDisplayActivity(item.id!)));
},
child: Row(
children: <Widget>[
......
//package map; //TODO
///**
// * Loads a bitmap image into memory.
// */
//final class LargeBitmapFactory {
// public static final long MAXIMUM_BITMAP_SIZE = 5000L * 5000L;
//
// private LargeBitmapFactory() {
// }
//
// static Bitmap decodeSampledBitmapFromResource( String pathName, int requiredWidth, int requiredHeight ) {
// final BitmapFactory.Options options = new BitmapFactory.Options();
// options.inJustDecodeBounds = true;
// BitmapFactory.decodeFile(pathName, options);
// options.inSampleSize = calculateTheLargestInSampleSizeValueThatIsAPowerOf2(options.outWidth, options.outHeight, requiredWidth, requiredHeight);
// options.inJustDecodeBounds = false;
// return BitmapFactory.decodeFile(pathName, options);
// }
//
// static int calculateTheLargestInSampleSizeValueThatIsAPowerOf2( int imageWidth, int imageHeight, int requiredWidth, int requiredHeight ) {
// int imageSize = Math.max(imageWidth, imageHeight);
// int requiredSize = Math.max(requiredWidth, requiredHeight);
// double zoom = imageSize / (double) requiredSize;
// double requiredMaxZoom = zoom * (2.0 / MAX_SCALE);
// if (requiredMaxZoom < 1.0) {
// requiredMaxZoom = 1.0;
// }
// int power2 = (int) Math.round(Math.log(requiredMaxZoom) / Math.log(2));
// int scale = (int) Math.pow(2, power2);
// int currentWidth = imageWidth / scale;
// while (currentWidth * (imageHeight / scale) > MAXIMUM_BITMAP_SIZE) {
// scale *= 2;
// }
// return scale;
// }
//}
......@@ -32,12 +32,6 @@ class GeodesicPoint {
_timeInMillis = timeInMillis;
}
// @NonNull
// @Override
// public String toString() {
// return _latitude + "," + _longitude + ", " + _timeInMillis + ", " + _altitude;
// }
//
// @Override
// public boolean equals( Object o ) {
// if (this == o) {
......
import 'dart:collection';
import 'package:Vikazimut_flutter/map/geodesic_point.dart';
import 'package:Vikazimut_flutter/map/view/MapView.dart';
import 'package:flutter/material.dart';
import 'Layer.dart';
abstract class AbstractRouteLayer implements Layer {
// final MapView _mapView;
final MapView _mapView;
late List<GeodesicPoint> _route;
late GeodesicPoint _lastLocation;
//
// AbstractRouteLayer( MapView mapView ) {
// _mapView = mapView;
// }
//
AbstractRouteLayer(MapView mapView) : _mapView = mapView;
void drawRoute(List<GeodesicPoint> route, GeodesicPoint lastLocation) {
_route = route;
_lastLocation = lastLocation;
// _mapView.invalidate();
_mapView.invalidate();
}
@override
void draw(Canvas canvas) {
// if (_route == null || _route.size() < 2) {
// return;
// }
// drawPolyLine(canvas, _route, _lastLocation);
if (_route.length < 2) {
return;
}
drawPolyLine(canvas, _route, _lastLocation);
}
//
// abstract void drawPolyLine( Canvas canvas, List<GeodesicPoint> route, GeodesicPoint lastLocation );
//
// Point convertGeodesicPointToMapPoint( GeodesicPoint currentPoint ) {
// return _mapView.convertGeoPointIntoMapCoordinates(currentPoint.getLatitude(), currentPoint.getLongitude());
// }
// @override
// void onResume() {
// }
//
// @override
// void onPause() {
// }
void drawPolyLine(Canvas canvas, List<GeodesicPoint> route, GeodesicPoint lastLocation);
Offset convertGeodesicPointToMapPoint(GeodesicPoint currentPoint) {
return _mapView.convertGeoPointIntoMapCoordinates(currentPoint.getLatitude(), currentPoint.getLongitude());
}
@override
void onResume() {}
@override
void onPause() {}
@override
void stop() {}
......
import 'package:Vikazimut_flutter/map/geodesic_point.dart';