import 'dart:async'; import 'dart:math'; import 'dart:typed_data'; import 'package:bus_infotainment/backend/live_information.dart'; import 'package:bus_infotainment/backend/modules/info_module.dart'; import 'package:bus_infotainment/tfl_datasets.dart'; import 'package:bus_infotainment/utils/OrdinanceSurveyUtils.dart'; import 'package:bus_infotainment/utils/audio%20wrapper.dart'; import 'package:flutter/foundation.dart'; import 'package:geolocator/geolocator.dart'; import 'package:vector_math/vector_math.dart'; class TrackerModule extends InfoModule { // Constructor TrackerModule() { locationStream(); if (!kIsWeb) { Geolocator.getLastKnownPosition().then((Position? position) { this._position = position; updateNearestStop(); }); } liveInformation.routeVariantDelegate.addListener((routeVariant) { print("Route variant changed"); updateNearestStop(); }); } // Location Tracker - will update the recorded location when the user moves. Position? _position; Position? get position => _position; StreamSubscription locationStream() => Geolocator.getPositionStream( locationSettings: const LocationSettings( accuracy: LocationAccuracy.high, distanceFilter: 1, ) ).listen((Position position) { if (position == null) { return; } this._position = position; updateNearestStop(); }); // Location Refresher - will update the recorded location periodically. Timer refreshTimer() => Timer.periodic(Duration(seconds: 1), (timer) async { _position = await Geolocator.getCurrentPosition(); }); BusRouteStop? nearestStop; bool hasArrived = false; Future updateNearestStop() async { if (liveInformation.getRouteVariant() == null) { return; } // Get the closest stop BusRouteStop closestStop = liveInformation.getRouteVariant()!.busStops.first; double closestDistance = OSGrid .toNorthingEasting(_position!.latitude, _position!.longitude) .distanceTo(Vector2(closestStop.easting.toDouble(), closestStop.northing.toDouble())); for (BusRouteStop stop in liveInformation.getRouteVariant()!.busStops) { double distance = OSGrid .toNorthingEasting(_position!.latitude, _position!.longitude) .distanceTo(Vector2(stop.easting.toDouble(), stop.northing.toDouble())); if (distance < closestDistance) { closestStop = stop; closestDistance = distance; } } double relativeDistance = _calculateRelativeDistance(closestStop, _position!.latitude, _position!.longitude); if (relativeDistance < -10) { print("Closest stop is behind us: ${closestStop.formattedStopName}"); print("Relative distance: $relativeDistance"); int stopIndex = liveInformation.getRouteVariant()!.busStops.indexOf(closestStop); closestStop = liveInformation.getRouteVariant()!.busStops[stopIndex + 1]; print("Closest stop is now: ${closestStop.formattedStopName}"); } else { print("Closest stop is in front of us: ${closestStop.formattedStopName}"); } bool preExisting = true; if (nearestStop != closestStop) { nearestStop = closestStop; hasArrived = false; preExisting = false; } print("Closest stop is the same as before"); double distance = OSGrid .toNorthingEasting(_position!.latitude, _position!.longitude) .distanceTo(Vector2(nearestStop!.easting.toDouble(), nearestStop!.northing.toDouble())); // get the speed in mph double speed = _position!.speed * 2.23694; print("Speed: $speed"); Duration? duration; { // Testing some audio stuff Uint8List? audioBytes = liveInformation.announcementModule.announcementCache[nearestStop!.getAudioFileName()]; if (audioBytes != null) { AudioWrapperByteSource audioSource = AudioWrapperByteSource(audioBytes!); AudioWrapper audio = AudioWrapper(); await audio.loadSource(audioSource); duration = await audio.getDuration(); print("Duration of audio: $duration"); } else { print("Audio bytes are null"); duration = Duration(seconds: 0); } } // get the estimated distance travelled in 5 seconds, in meters double distanceTravelled = speed * (3 + duration!.inSeconds); // adjust for the it takes to send the announcement to other devices distance -= distanceTravelled; // get the time to the stop in seconds double timeToStop = distance / speed; print("Distance to stop: $distance"); print("Time to stop: $timeToStop"); int secondsBefore = 7; print("Seconds before: $secondsBefore"); if ((timeToStop < secondsBefore ) && !hasArrived && relativeDistance > 0) { print("We are at the stop"); hasArrived = true; liveInformation.announcementModule.queueAnnounceByAudioName( displayText: "${nearestStop!.formattedStopName}", audioNames: [ // "A_NEXT_STOP_001.mp3", nearestStop!.getAudioFileName() ], sendToServer: true ); } if (!hasArrived && !preExisting) { liveInformation.announcementModule.queueAnnounceByAudioName( displayText: "${closestStop.formattedStopName}", audioNames: [], sendToServer: true ); } print("Closest stop: ${closestStop.formattedStopName} in ${closestDistance.round()} meters"); } } double _calculateRelativeDistance(BusRouteStop stop, double latitude, double longitude) { List toLatLong = OSGrid.toLatLong(stop.northing.toDouble(), stop.easting.toDouble()); Vector2 stopPoint = OSGrid.toNorthingEasting(toLatLong[0], toLatLong[1]); Vector2 currentPoint = OSGrid.toNorthingEasting(latitude, longitude); // calculate the heading from the current point to the stop point // 0 degrees is north, 90 degrees is east, 180 degrees is south, 270 degrees is west double toHeading = degrees(atan2(stopPoint.x - currentPoint.x, stopPoint.y - currentPoint.y)); toHeading = (toHeading + 360) % 360; // get the dot product of the heading and the stop heading double dotProduct = cos(radians(toHeading)) * cos(radians(stop.heading.toDouble())) + sin(radians(toHeading)) * sin(radians(stop.heading.toDouble())); return (dotProduct.sign) * _calculateDistance(latitude, longitude, toLatLong[0], toLatLong[1]); } double _calculateDistance(double lat1, double lon1, double lat2, double lon2) { // Convert to eastings and northings Vector2 point1 = OSGrid.toNorthingEasting(lat1, lon1); Vector2 point2 = OSGrid.toNorthingEasting(lat2, lon2); return point1.distanceTo(point2); }