only update on minimum distance

This commit is contained in:
Freek van de Ven 2021-10-01 15:00:23 +02:00
parent b9a867eaca
commit 38ac7476a5
6 changed files with 215 additions and 68 deletions

View file

@ -13,10 +13,13 @@ class TrackTraceDemo extends StatefulWidget {
class _TrackTraceDemoState extends State<TrackTraceDemo> { class _TrackTraceDemoState extends State<TrackTraceDemo> {
TrackTraceController? controller; TrackTraceController? controller;
int step = 1;
int routeLength = 0;
late final Timer timer;
@override @override
void initState() { void initState() {
Timer.periodic(const Duration(seconds: 5), (_) { timer = Timer.periodic(const Duration(seconds: 2), (_) {
moveAlongRoute(); moveAlongRoute();
}); });
super.initState(); super.initState();
@ -48,6 +51,18 @@ class _TrackTraceDemoState extends State<TrackTraceDemo> {
{'visibility': 'off'}, {'visibility': 'off'},
], ],
), ),
// GoogleMapThemeFeature(
// featureType: 'water',
// stylers: [
// {'color': '#00ff00'}
// ],
// ),
// GoogleMapThemeFeature(
// featureType: 'road',
// stylers: [
// {'color': '#000000'}
// ],
// )
], ],
), ),
startPosition: const Marker( startPosition: const Marker(
@ -61,18 +76,37 @@ class _TrackTraceDemoState extends State<TrackTraceDemo> {
googleAPIKey: 'AIzaSyDaxZX8TeQeVf5tW-D6A66WLl20arbWV6c', googleAPIKey: 'AIzaSyDaxZX8TeQeVf5tW-D6A66WLl20arbWV6c',
travelMode: TravelMode.walking, travelMode: TravelMode.walking,
mapType: MapType.normal, mapType: MapType.normal,
routeUpdateInterval: 60, routeUpdateInterval: Duration(seconds: 30),
timerPrecision: TimePrecision.everySecond, timerPrecision: TimePrecision.everySecond,
zoomGesturesEnabled: true, zoomGesturesEnabled: true,
scrollGesturesEnabled: true,
line: const Polyline( line: const Polyline(
jointType: JointType.bevel,
polylineId: PolylineId('test route'), polylineId: PolylineId('test route'),
color: Color(0xFFFF7884), color: Color(0xFFFF7884),
width: 3, width: 3,
), ),
onArrived: () {
timer.cancel();
debugPrint('stopping simulation');
},
onTap: (value) {
debugPrint(value.toString());
},
onLongPress: (value) {
debugPrint(value.toString());
},
onCameraMove: (value) {
debugPrint(value.toString());
},
onMapCreated: (ctr) => { onMapCreated: (ctr) => {
controller = ctr, controller = ctr,
ctr.addListener(() { ctr.addListener(() {
setState(() {}); setState(() {
if (ctr.route != null && ctr.route!.distance != routeLength) {
step = 1;
}
});
}), }),
}, },
), ),
@ -100,10 +134,12 @@ class _TrackTraceDemoState extends State<TrackTraceDemo> {
controller!.start = Marker( controller!.start = Marker(
markerId: const MarkerId('Start Locatie'), markerId: const MarkerId('Start Locatie'),
position: LatLng( position: LatLng(
controller!.route!.line[1].latitude, controller!.route!.line[step].latitude,
controller!.route!.line[1].longitude, controller!.route!.line[step].longitude,
), ),
); );
step++;
routeLength = controller!.route!.distance;
} }
} }
} }

View file

@ -134,7 +134,7 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "0.0.1" version: "1.0.0"
html: html:
dependency: transitive dependency: transitive
description: description:

View file

@ -12,7 +12,16 @@ import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
export 'package:google_maps_flutter/google_maps_flutter.dart' export 'package:google_maps_flutter/google_maps_flutter.dart'
show MapType, Marker, MarkerId, Polyline, PolylineId, LatLng; show
MapType,
Marker,
MarkerId,
BitmapDescriptor,
InfoWindow,
Polyline,
PolylineId,
JointType,
LatLng;
part 'src/controller.dart'; part 'src/controller.dart';
part 'src/directions_repository.dart'; part 'src/directions_repository.dart';

View file

@ -31,6 +31,36 @@ class TrackTraceController extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void recenterCamera() {
mapController?.moveCamera(
CameraUpdate.newLatLngBounds(
LatLngBounds(
southwest: LatLng(
min(
_startPosition.position.latitude,
_destinationPosition.position.latitude,
),
min(
_startPosition.position.longitude,
_destinationPosition.position.longitude,
),
),
northeast: LatLng(
max(
_startPosition.position.latitude,
_destinationPosition.position.latitude,
),
max(
_startPosition.position.longitude,
_destinationPosition.position.longitude,
),
),
),
50,
),
);
}
@override @override
void dispose() { void dispose() {
mapController?.dispose(); mapController?.dispose();
@ -39,11 +69,14 @@ class TrackTraceController extends ChangeNotifier {
} }
class TrackTraceRoute { class TrackTraceRoute {
TrackTraceRoute( TrackTraceRoute(
int durationValue, int distanceValue, List<PointLatLng> lineValue,) int durationValue,
: duration = durationValue, int distanceValue,
List<PointLatLng> lineValue,
) : duration = durationValue,
distance = distanceValue, distance = distanceValue,
line = lineValue; line = lineValue;
/// route duration in seconds /// route duration in seconds
int duration = 0; int duration = 0;
@ -51,5 +84,5 @@ TrackTraceRoute(
int distance = 0; int distance = 0;
/// route edge points /// route edge points
final List<PointLatLng> line; List<PointLatLng> line;
} }

View file

@ -1,6 +1,5 @@
part of google_track_trace; part of google_track_trace;
// typedef void MapCreatedCallback(TrackTraceController controller);
enum TimePrecision { updateOnly, everySecond, everyMinute } enum TimePrecision { updateOnly, everySecond, everyMinute }
class GoogleTrackTraceMap extends StatefulWidget { class GoogleTrackTraceMap extends StatefulWidget {
@ -11,15 +10,29 @@ class GoogleTrackTraceMap extends StatefulWidget {
required this.googleAPIKey, required this.googleAPIKey,
required this.routeUpdateInterval, required this.routeUpdateInterval,
Key? key, Key? key,
this.markerUpdatePrecision = 50,
this.timerPrecision = TimePrecision.everyMinute, this.timerPrecision = TimePrecision.everyMinute,
this.travelMode = TravelMode.driving, this.travelMode = TravelMode.driving,
this.cameraTargetBounds,
this.compassEnabled = false, this.compassEnabled = false,
this.rotateGesturesEnabled = false,
this.scrollGesturesEnabled = false,
this.zoomControlsEnabled = false, this.zoomControlsEnabled = false,
this.zoomGesturesEnabled = false, this.zoomGesturesEnabled = false,
this.liteModeEnabled = false,
this.tiltGesturesEnabled = false,
this.myLocationEnabled = false,
this.myLocationButtonEnabled = false,
this.mapToolbarEnabled = false, this.mapToolbarEnabled = false,
this.mapType = MapType.normal, this.mapType = MapType.normal,
this.buildingsEnabled = false, this.buildingsEnabled = false,
this.indoorViewEnabled = false,
this.trafficEnabled = false,
this.mapStylingTheme, this.mapStylingTheme,
this.onTap,
this.onArrived,
this.onLongPress,
this.onCameraMove,
this.line, this.line,
}) : super(key: key); }) : super(key: key);
@ -31,8 +44,10 @@ class GoogleTrackTraceMap extends StatefulWidget {
final TravelMode travelMode; final TravelMode travelMode;
final int routeUpdateInterval; final Duration routeUpdateInterval;
/// amount of meter the marker needs to move to update
final int markerUpdatePrecision;
final TimePrecision timerPrecision; final TimePrecision timerPrecision;
final Marker startPosition; final Marker startPosition;
@ -41,13 +56,26 @@ class GoogleTrackTraceMap extends StatefulWidget {
final Polyline? line; final Polyline? line;
final bool compassEnabled; final bool compassEnabled;
final bool rotateGesturesEnabled;
final bool scrollGesturesEnabled;
final bool liteModeEnabled;
final bool tiltGesturesEnabled;
final bool myLocationEnabled;
final bool myLocationButtonEnabled;
final bool zoomControlsEnabled; final bool zoomControlsEnabled;
final bool zoomGesturesEnabled; final bool zoomGesturesEnabled;
final bool mapToolbarEnabled; final bool mapToolbarEnabled;
final bool buildingsEnabled; final bool buildingsEnabled;
final bool indoorViewEnabled;
final bool trafficEnabled;
final CameraTargetBounds? cameraTargetBounds;
final MapType mapType; final MapType mapType;
final GoogleTrackTraceMapTheme? mapStylingTheme; final GoogleTrackTraceMapTheme? mapStylingTheme;
final ArgumentCallback<LatLng>? onTap;
final void Function()? onArrived;
final ArgumentCallback<LatLng>? onLongPress;
final CameraPositionCallback? onCameraMove;
final String googleAPIKey; final String googleAPIKey;
@override @override
@ -59,12 +87,17 @@ class _GoogleTrackTraceMapState extends State<GoogleTrackTraceMap> {
DateTime lastRouteUpdate = DateTime.now(); DateTime lastRouteUpdate = DateTime.now();
late final Timer routeCalculateTimer;
late final Timer markerUpdateTimer;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
controller = controller =
TrackTraceController(widget.startPosition, widget.destinationPosition); TrackTraceController(widget.startPosition, widget.destinationPosition);
controller.addListener(_onChange); controller.addListener(
() => setState(() {}),
);
widget.onMapCreated(controller); widget.onMapCreated(controller);
startRouteUpdateTimer(); startRouteUpdateTimer();
startMarkerUpdateTimer(); startMarkerUpdateTimer();
@ -84,12 +117,23 @@ class _GoogleTrackTraceMapState extends State<GoogleTrackTraceMap> {
controller.end.position, controller.end.position,
), ),
onMapCreated: _onMapCreated, onMapCreated: _onMapCreated,
onTap: widget.onTap,
onLongPress: widget.onLongPress,
onCameraMove: widget.onCameraMove,
compassEnabled: widget.compassEnabled, compassEnabled: widget.compassEnabled,
rotateGesturesEnabled: widget.rotateGesturesEnabled,
scrollGesturesEnabled: widget.scrollGesturesEnabled,
zoomControlsEnabled: widget.zoomControlsEnabled, zoomControlsEnabled: widget.zoomControlsEnabled,
zoomGesturesEnabled: widget.zoomGesturesEnabled, zoomGesturesEnabled: widget.zoomGesturesEnabled,
liteModeEnabled: widget.liteModeEnabled,
tiltGesturesEnabled: widget.tiltGesturesEnabled,
myLocationEnabled: widget.myLocationEnabled,
myLocationButtonEnabled: widget.myLocationButtonEnabled,
mapToolbarEnabled: widget.mapToolbarEnabled, mapToolbarEnabled: widget.mapToolbarEnabled,
mapType: widget.mapType, mapType: widget.mapType,
buildingsEnabled: widget.buildingsEnabled, buildingsEnabled: widget.buildingsEnabled,
indoorViewEnabled: widget.indoorViewEnabled,
trafficEnabled: widget.trafficEnabled,
markers: <Marker>{ markers: <Marker>{
controller.start, controller.start,
controller.end, controller.end,
@ -115,10 +159,6 @@ class _GoogleTrackTraceMapState extends State<GoogleTrackTraceMap> {
); );
} }
void _onChange() {
setState(() {});
}
void _onMapCreated(GoogleMapController ctr) { void _onMapCreated(GoogleMapController ctr) {
if (mounted) { if (mounted) {
controller.mapController = ctr; controller.mapController = ctr;
@ -130,6 +170,7 @@ class _GoogleTrackTraceMapState extends State<GoogleTrackTraceMap> {
'[{"featureType": "poi","stylers": [{"visibility": "off"}]}]', '[{"featureType": "poi","stylers": [{"visibility": "off"}]}]',
); );
} }
controller.recenterCamera();
} }
} }
@ -138,7 +179,6 @@ class _GoogleTrackTraceMapState extends State<GoogleTrackTraceMap> {
(pointA.latitude + pointB.latitude) / 2, (pointA.latitude + pointB.latitude) / 2,
(pointA.longitude + pointB.longitude) / 2, (pointA.longitude + pointB.longitude) / 2,
); );
return CameraPosition( return CameraPosition(
target: target, target: target,
zoom: 13.0, zoom: 13.0,
@ -147,26 +187,10 @@ class _GoogleTrackTraceMapState extends State<GoogleTrackTraceMap> {
); );
} }
CameraUpdate moveCameraToCenter(LatLng pointA, LatLng pointB) {
return CameraUpdate.newLatLngBounds(
LatLngBounds(
southwest: LatLng(
min(pointA.latitude, pointB.latitude),
min(pointA.longitude, pointB.longitude),
),
northeast: LatLng(
max(pointA.latitude, pointB.latitude),
max(pointA.longitude, pointB.longitude),
),
),
50,
);
}
void startRouteUpdateTimer() { void startRouteUpdateTimer() {
calculateRoute(); // run at the start calculateRoute(); // run at the start
Timer.periodic(Duration(seconds: widget.routeUpdateInterval), routeCalculateTimer =
(Timer timer) { Timer.periodic(widget.routeUpdateInterval, (Timer timer) {
calculateRoute(); calculateRoute();
}); });
} }
@ -175,10 +199,14 @@ class _GoogleTrackTraceMapState extends State<GoogleTrackTraceMap> {
if (widget.timerPrecision != TimePrecision.updateOnly) { if (widget.timerPrecision != TimePrecision.updateOnly) {
var updateInterval = var updateInterval =
(widget.timerPrecision == TimePrecision.everyMinute) ? 60 : 1; (widget.timerPrecision == TimePrecision.everyMinute) ? 60 : 1;
Timer.periodic(Duration(seconds: updateInterval), (timer) { markerUpdateTimer =
Timer.periodic(Duration(seconds: updateInterval), (timer) {
if (controller.route != null) { if (controller.route != null) {
checkDestinationCloseBy();
controller.route = TrackTraceRoute( controller.route = TrackTraceRoute(
controller.route!.duration - updateInterval, (controller.route!.duration != 0)
? controller.route!.duration - updateInterval
: 0,
controller.route!.distance, controller.route!.distance,
controller.route!.line, controller.route!.line,
); );
@ -188,33 +216,74 @@ class _GoogleTrackTraceMapState extends State<GoogleTrackTraceMap> {
} }
void calculateRoute() { void calculateRoute() {
DirectionsRepository() // TODO(freek): refactor this away if (controller.route == null || checkTargetMoved()) {
.getDirections( DirectionsRepository() // TODO(freek): refactor this away
origin: controller.start.position, .getDirections(
destination: controller.end.position, origin: controller.start.position,
mode: widget.travelMode, destination: controller.end.position,
key: widget.googleAPIKey, mode: widget.travelMode,
) key: widget.googleAPIKey,
.then( )
(value) => { .then(
controller.route = TrackTraceRoute( (value) => {
value.totalDuration, controller.route = TrackTraceRoute(
value.totalDistance, value.totalDuration,
value.polylinePoints, value.totalDistance,
), value.polylinePoints,
if (controller.mapController != null) ),
{ checkDestinationCloseBy(),
controller.mapController!.moveCamera( controller.recenterCamera(),
moveCameraToCenter( setState(() {
controller.start.position, lastRouteUpdate = DateTime.now();
controller.end.position, }),
), },
), );
}, }
setState(() { }
lastRouteUpdate = DateTime.now();
}) void checkDestinationCloseBy() {
}, if (calculatePointProximity(
); controller.start.position,
controller.end.position,
) <
widget.markerUpdatePrecision) {
routeCalculateTimer.cancel();
markerUpdateTimer.cancel();
if (controller.route != null) {
controller.route!.line = <PointLatLng>[controller.route!.line[1]];
controller.route!.distance = 0;
controller.route!.duration = 0;
}
widget.onArrived?.call();
}
}
bool checkTargetMoved() {
return calculatePointProximity(
controller.start.position,
LatLng(
controller.route!.line[0].latitude,
controller.route!.line[0].longitude,
),
) >=
widget.markerUpdatePrecision;
}
double calculatePointProximity(LatLng pointA, LatLng pointB) {
var p = 0.017453292519943295;
var c = cos;
var a = 0.5 -
c(
(pointA.latitude - pointB.latitude) * p,
) /
2 +
c(controller.route!.line[0].latitude * p) *
c(pointA.latitude * p) *
(1 -
c(
(pointA.longitude - pointB.longitude) * p,
)) /
2;
return 12742 * asin(sqrt(a)) * 1000;
} }
} }

View file

@ -1,6 +1,6 @@
name: google_track_trace name: google_track_trace
description: An Iconica Flutter pluginin for Track & Trace Package description: An Iconica Flutter pluginin for Track & Trace Package
version: 0.0.1 version: 1.0.0
environment: environment:
sdk: ">=2.14.0 <3.0.0" sdk: ">=2.14.0 <3.0.0"