Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 126 additions & 80 deletions lib/screens/map_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ class _MaizeBusCoreState extends State<MaizeBusCore> {
static const LatLng _defaultCenter = LatLng(42.276463, -83.7374598);

Set<Polyline> _displayedPolylines = {};
Set<Marker> _displayedStopMarkers = {};
Map<String, Marker> _displayedStopMarkers = {}; // maps from stopID to marker
Map<String, Marker> _displayedFavoriteStopMarkers = {};
Set<Marker> _displayedBusMarkers = {};
// Journey overlays for search results
Set<Polyline> _displayedJourneyPolylines = {};
Expand All @@ -119,6 +120,7 @@ class _MaizeBusCoreState extends State<MaizeBusCore> {
Marker? _searchLocationMarker;
final Set<String> _selectedRoutes = <String>{};
List<Map<String, String>> _availableRoutes = [];
Map<String, bool> _stopIsRide = {};

// Custom marker icons
BitmapDescriptor? _busIcon;
Expand All @@ -134,7 +136,7 @@ class _MaizeBusCoreState extends State<MaizeBusCore> {

// Memoization caches
final Map<String, Polyline> _routePolylines = {};
final Map<String, Set<Marker>> _routeStopMarkers = {};
final Map<String, Map<String, Marker>> _routeStopMarkers = {}; // maps from route to a map of stopID to marker
// Whether a journey search overlay is currently active (shows only journey path)
bool _journeyOverlayActive = false;
// maximum allowed distance (meters) from a stop to a candidate polyline point
Expand Down Expand Up @@ -770,51 +772,59 @@ class _MaizeBusCoreState extends State<MaizeBusCore> {
);
}
if (!_routeStopMarkers.containsKey(routeKey)) {
_routeStopMarkers[routeKey] = r.stops
.map(
(stop) => Marker(
markerId: MarkerId(
'stop_${stop.id}_${Object.hashAll(r.points)}',
),
position: stop.location,
flat: true,
icon: _favoriteStops.contains(stop.id)
? (stop.isRide
? _favRideStopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
)
: _favStopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
))
: (stop.isRide
? _rideStopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
)
: _stopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
)),
consumeTapEvents: true,
onTap: () {
try {
Haptics.vibrate(HapticsType.light);
} catch (e) {}

_showStopSheet(
stop.id,
stop.name,
stop.location.latitude,
stop.location.longitude,
);
},
rotation: stop.rotation,
anchor: Offset(0.5, 0.5),
),
)
.toSet();
_routeStopMarkers[routeKey] = {};
for (final stop in r.stops) { // iterate through all stops in this route
final isFavorite = _favoriteStops.contains(stop.id);

final marker = Marker(
markerId: MarkerId(
'stop_${stop.id}_${Object.hashAll(r.points)}',
),
position: stop.location,
flat: true,
icon: isFavorite
? (stop.isRide
? _favRideStopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
)
: _favStopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
))
: (stop.isRide
? _rideStopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
)
: _stopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
)),
consumeTapEvents: true,
onTap: () {
try {
Haptics.vibrate(HapticsType.light);
} catch (e) {}

_showStopSheet(
stop.id,
stop.name,
stop.location.latitude,
stop.location.longitude,
);
},
rotation: stop.rotation,
anchor: Offset(0.5, 0.5),
);
_routeStopMarkers[routeKey]?[stop.id] = marker;

// gets first marker of this stop and adds it to the favorited stop markers
if (isFavorite && !_displayedFavoriteStopMarkers.containsKey(stop.id)) {
_displayedFavoriteStopMarkers[stop.id] = marker;
}
_stopIsRide[stop.id] = stop.isRide;
}
}
}
}
Expand Down Expand Up @@ -850,45 +860,73 @@ class _MaizeBusCoreState extends State<MaizeBusCore> {
// Update cached markers for a specific stop id to reflect favorite/unfavorite
void _setStopFavorited(String stpid, bool favored) {
// Update all routeStopMarkers entries that match this stop id
final isRide = _stopIsRide[stpid] ?? false;
_routeStopMarkers.forEach((routeKey, markers) {
final updated = markers.map((m) {
if (m.markerId.value.startsWith('stop_${stpid}_')) {
return Marker(
flat: true,
markerId: m.markerId,
position: m.position,
icon: favored
? (_favStopIcon ??
_stopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
))
: (_stopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
)),
consumeTapEvents: m.consumeTapEvents,
onTap: m.onTap,
rotation: m.rotation,
anchor: m.anchor,
);
}
return m;
}).toSet();
_routeStopMarkers[routeKey] = updated;
// if marker does not exist in this route, return
if (!markers.containsKey(stpid)) return;

final m = markers[stpid]!; // get old marker
final newMarker = Marker(
flat: true,
markerId: m.markerId,
position: m.position,
icon: favored
? (isRide
? _favRideStopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
)
: _favStopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
))
: (isRide
? _rideStopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
)
: _stopIcon ??
BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
)),
consumeTapEvents: m.consumeTapEvents,
onTap: m.onTap,
rotation: m.rotation,
anchor: m.anchor,
);

// gets first marker of this stop id and adds it to the favorited stop markers
if (favored && !_displayedFavoriteStopMarkers.containsKey(stpid)) {
_displayedFavoriteStopMarkers[stpid] = newMarker;
}

markers[stpid] = newMarker; // set as new marker
});

// remove favorite stop marker if not favored
if (!favored) {
_displayedFavoriteStopMarkers.remove(stpid);
}

// If displayed, update displayed markers as well
setState(() {
// Rebuild displayed stop markers based on current selected routes
final selectedStopMarkers = <Marker>{};
final selectedStopMarkers = <String, Marker>{};
for (final routeId in _selectedRoutes) {
final routeVariants = _routePolylines.keys.where(
(key) => key.startsWith('${routeId}_'),
);
for (final routeKey in routeVariants) {
final stops = _routeStopMarkers[routeKey];
if (stops != null) selectedStopMarkers.addAll(stops);
if (stops == null) continue;

// iterate through and add the stop markers
// if they are not already in the selected stop markesr
stops.forEach((key, value) {
if (!selectedStopMarkers.containsKey(key)) {
selectedStopMarkers[key] = value;
}
});
}
}
_displayedStopMarkers = selectedStopMarkers;
Expand All @@ -898,7 +936,7 @@ class _MaizeBusCoreState extends State<MaizeBusCore> {

void _updateDisplayedRoutes() {
final selectedPolylines = <Polyline>{};
final selectedStopMarkers = <Marker>{};
final selectedStopMarkers = <String, Marker>{};

for (final routeId in _selectedRoutes) {
// Find all variants of this route
Expand All @@ -910,9 +948,13 @@ class _MaizeBusCoreState extends State<MaizeBusCore> {
final polyline = _routePolylines[routeKey];
if (polyline != null) selectedPolylines.add(polyline);
final stops = _routeStopMarkers[routeKey];
if (stops != null) {
selectedStopMarkers.addAll(stops);
}
if (stops == null) continue;

stops.forEach((key, value) {
if (!selectedStopMarkers.containsKey(key)) {
selectedStopMarkers[key] = value;
}
});
}
}

Expand Down Expand Up @@ -1009,7 +1051,8 @@ class _MaizeBusCoreState extends State<MaizeBusCore> {
}

void _updateAllDisplayedMarkers() {
_allDisplayedStopMarkers = _displayedStopMarkers
_allDisplayedStopMarkers = _displayedStopMarkers.values.toSet()
.union(_displayedFavoriteStopMarkers.values.toSet())
.union(_displayedBusMarkers)
.union(_displayedJourneyMarkers)
.union(_searchLocationMarker != null ? {_searchLocationMarker!} : {});
Expand Down Expand Up @@ -1078,6 +1121,8 @@ class _MaizeBusCoreState extends State<MaizeBusCore> {
void _refreshCachedStopMarkers() {
// Clear cached stop markers so they'll be recreated with the new icons
_routeStopMarkers.clear();
// also clear persistent favorited stop markers to be refreshed in _cacheRouteOverlays(..)
_displayedFavoriteStopMarkers.clear();
// Re-cache all route overlays with the new icons
_cacheRouteOverlays(
Provider.of<BusProvider>(context, listen: false).routes,
Expand Down Expand Up @@ -2128,7 +2173,8 @@ class _MaizeBusCoreState extends State<MaizeBusCore> {
? {_searchLocationMarker!}
: {},
)
: _displayedStopMarkers
: _displayedStopMarkers.values.toSet()
.union(_displayedFavoriteStopMarkers.values.toSet())
.union(_displayedJourneyMarkers)
.union(
_searchLocationMarker != null
Expand Down