Skip to content

Commit

Permalink
Fix #346 - Show route on map as layer on existing map view
Browse files Browse the repository at this point in the history
* In the main Nearby mode with map and sliding panel, when selecting "Show route on map", the route is now shown as a layer on the existing map.  The map view only changes when the closest vehicle for the route isn't in the current view bounds - in that case, the map zooms and pans to include both the current view port bounds as well as the closest vehicle for the given route.  This should give much better context to the user of where they are (and their currently selected stop is) in relationship to the route.
* If you're viewing arrivals for a stop from a shortcut (i.e., via ArrivalListActivity), then "Show route on map" has the same old behavior of showing the entire route on the map in a new Activity
* Map padding is now changed so that components can set an individual padding without overwriting the other padding dimensions.  This is important because the sliding panel needs to set the bottom map padding based on the sliding panel state, while the RouteMapController needs to set the top padding.
  • Loading branch information
barbeau committed Nov 25, 2015
1 parent 28645a3 commit 88007bb
Show file tree
Hide file tree
Showing 11 changed files with 435 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ public class BaseMapFragment extends SupportMapFragment

public static final float CAMERA_DEFAULT_ZOOM = 16.0f;

public static final float DEFAULT_MAP_PADDING_DP = 20.0f;

// Keep track of current map padding
private int mMapPaddingLeft = 0;
private int mMapPaddingTop = 0;
private int mMapPaddingRight = 0;
private int mMapPaddingBottom = 0;

// Use fully-qualified class name to avoid import statement, because it interferes with scripted
// copying of Maps API v2 classes between Google/Amazon build flavors (see #254)
private com.amazon.geo.mapsv2.AmazonMap mMap;
Expand Down Expand Up @@ -418,15 +426,32 @@ public void removeMarker(int markerId) {
* Define a visible region on the map, to signal to the map that portions of the map around
* the edges may be obscured, by setting padding on each of the four edges of the map.
*
* @param left the number of pixels of padding to be added on the left of the map.
* @param top the number of pixels of padding to be added on the top of the map.
* @param right the number of pixels of padding to be added on the right of the map.
* @param bottom the number of pixels of padding to be added on the bottom of the map.
* @param left the number of pixels of padding to be added on the left of the map, or null
* if the existing padding should be used
* @param top the number of pixels of padding to be added on the top of the map, or null
* if the existing padding should be used
* @param right the number of pixels of padding to be added on the right of the map, or null
* if the existing padding should be used
* @param bottom the number of pixels of padding to be added on the bottom of the map, or null
* if the existing padding should be used
*/
@Override
public void setPadding(int left, int top, int right, int bottom) {
public void setPadding(Integer left, Integer top, Integer right, Integer bottom) {
if (left != null) {
mMapPaddingLeft = left;
}
if (top != null) {
mMapPaddingTop = top;
}
if (right != null) {
mMapPaddingRight = right;
}
if (bottom != null) {
mMapPaddingBottom = bottom;
}

if (mMap != null) {
mMap.setPadding(left, top, right, bottom);
mMap.setPadding(mMapPaddingLeft, mMapPaddingTop, mMapPaddingRight, mMapPaddingBottom);
}
}

Expand Down Expand Up @@ -628,11 +653,12 @@ public void setMapCenter(Location location, boolean animateToLocation,
LatLng target = MapHelpV2.makeLatLng(location);
LatLng offsetTarget;

if (overlayExpanded) {
// Adjust camera target based on MapModeController.OVERLAY_PERCENTAGE
// so it appears in the center of the visible map
if (isRouteDisplayed() && overlayExpanded) {
// Adjust camera target if the route header is currently displayed - map padding
// doesn't get this quite right, as the header is slid up some and full padding doesn't apply
double percentageOffset = 0.2;
double bias =
(getLongitudeSpanInDecDegrees() * MapModeController.OVERLAY_PERCENTAGE) / 2;
(getLongitudeSpanInDecDegrees() * percentageOffset) / 2;
offsetTarget = new LatLng(target.latitude - bias, target.longitude);
target = offsetTarget;
}
Expand Down Expand Up @@ -729,7 +755,7 @@ public void zoomToRoute() {

Activity a = getActivity();
if (a != null) {
int padding = UIHelp.dpToPixels(a, 20);
int padding = UIHelp.dpToPixels(a, DEFAULT_MAP_PADDING_DP);
mMap.moveCamera(
(CameraUpdateFactory.newLatLngBounds(builder.build(), padding)));
}
Expand All @@ -740,6 +766,41 @@ public void zoomToRoute() {
}
}

/**
* Zoom to include the current map bounds plus the location of the nearest vehicle
*
* @param routeIds markers representing real-time positions for the provided routeIds will be
* checked for proximity to the location (all other routes are ignored)
* @param response trips-for-route API response, which includes real-time vehicle locations in status
*/
@Override
public void zoomIncludeClosestVehicle(HashSet<String> routeIds, ObaTripsForRouteResponse response) {
if (mMap == null) {
return;
}
LatLng closestVehicleLocation = MapHelpV2
.getClosestVehicle(response, routeIds, getMapCenterAsLocation());

LatLngBounds visibleBounds = mMap.getProjection().getVisibleRegion().latLngBounds;

if (visibleBounds.contains(closestVehicleLocation)) {
// Closest vehicle is already in view - don't change camera
return;
}

// Zoom to include current map bounds and closest vehicle location
LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(visibleBounds.northeast);
builder.include(visibleBounds.southwest);
builder.include(closestVehicleLocation);

Activity a = getActivity();
if (a != null) {
int padding = UIHelp.dpToPixels(a, DEFAULT_MAP_PADDING_DP);
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), padding));
}
}

@Override
public void removeRouteOverlay() {
for (Polyline p : mLineOverlay) {
Expand All @@ -749,6 +810,17 @@ public void removeRouteOverlay() {
mLineOverlay.clear();
}

/**
* Clears any stop markers from the map
* @param clearFocusedStop true to clear the currently focused stop, false to leave it on map
*/
@Override
public void removeStopOverlay(boolean clearFocusedStop) {
if (mStopOverlay != null) {
mStopOverlay.clear(clearFocusedStop);
}
}

@Override
public boolean canWatchMapChanges() {
// Android Map API v2 has an OnCameraChangeListener
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,23 @@
import com.amazon.geo.mapsv2.model.LatLngBounds;

import org.onebusaway.android.io.elements.ObaRegion;
import org.onebusaway.android.io.elements.ObaTripDetails;
import org.onebusaway.android.io.elements.ObaTripStatus;
import org.onebusaway.android.io.request.ObaTripsForRouteResponse;

import android.content.Context;
import android.location.Location;
import android.util.Log;

import java.util.HashSet;

/**
* Utilities to help process data for Android Maps API v1
*/
public class MapHelpV2 {

public static final String TAG = "MapHelpV2";

/**
* Converts a latitude/longitude to a LatLng.
*
Expand Down Expand Up @@ -133,4 +141,64 @@ public static boolean isMapsInstalled(Context context) {
public static void promptUserInstallMaps(final Context context) {
ProprietaryMapHelpV2.promptUserInstallMaps(context);
}

/**
* Gets the location of the vehicle closest to the provided location running the provided routes
*
* @param response response containing list of trips with vehicle locations
* @param routeIds markers representing real-time positions for the provided routeIds will be
* checked for proximity to the location (all other routes are ignored)
* @param loc location
* @return the closest vehicle location to the given location, or null if a closest vehicle
* couldn't be found
*/
public static LatLng getClosestVehicle(ObaTripsForRouteResponse response, HashSet<String> routeIds, Location loc) {
if (loc == null) {
return null;
}
float minDist = Float.MAX_VALUE;
ObaTripStatus closestVehicle = null;
Location closestVehicleLocation = null;
Float distToVehicle;

for (ObaTripDetails detail : response.getTrips()) {
Location vehicleLocation;
ObaTripStatus status = detail.getStatus();
if (status == null) {
continue;
}
// Check if this vehicle is running a route we're interested in
String activeRoute = response.getTrip(status.getActiveTripId()).getRouteId();
if (!routeIds.contains(activeRoute)) {
continue;
}
if (status.getLastKnownLocation() != null) {
// Use last actual position
vehicleLocation = status.getLastKnownLocation();
} else if (status.getPosition() != null) {
// Use last interpolated position
vehicleLocation = status.getPosition();
} else {
// No vehicle location - continue to next trip
continue;
}
distToVehicle = vehicleLocation.distanceTo(loc);

if (distToVehicle < minDist) {
closestVehicleLocation = vehicleLocation;
closestVehicle = status;
minDist = distToVehicle;
}
}

if (closestVehicleLocation == null) {
return null;
}

Log.d(TAG, "Closest vehicle is vehicleId=" + closestVehicle.getVehicleId() + ", routeId=" + ", tripId=" +
closestVehicle.getActiveTripId() + " at " + closestVehicleLocation.getLatitude() + ","
+ closestVehicleLocation.getLongitude());

return makeLatLng(closestVehicleLocation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ public class BaseMapFragment extends SupportMapFragment

public static final float CAMERA_DEFAULT_ZOOM = 16.0f;

public static final float DEFAULT_MAP_PADDING_DP = 20.0f;

// Keep track of current map padding
private int mMapPaddingLeft = 0;

private int mMapPaddingTop = 0;

private int mMapPaddingRight = 0;

private int mMapPaddingBottom = 0;

// Use fully-qualified class name to avoid import statement, because it interferes with scripted
// copying of Maps API v2 classes between Google/Amazon build flavors (see #254)
private com.google.android.gms.maps.GoogleMap mMap;
Expand Down Expand Up @@ -407,15 +418,32 @@ public void removeMarker(int markerId) {
* Define a visible region on the map, to signal to the map that portions of the map around
* the edges may be obscured, by setting padding on each of the four edges of the map.
*
* @param left the number of pixels of padding to be added on the left of the map.
* @param top the number of pixels of padding to be added on the top of the map.
* @param right the number of pixels of padding to be added on the right of the map.
* @param bottom the number of pixels of padding to be added on the bottom of the map.
* @param left the number of pixels of padding to be added on the left of the map, or null
* if the existing padding should be used
* @param top the number of pixels of padding to be added on the top of the map, or null
* if the existing padding should be used
* @param right the number of pixels of padding to be added on the right of the map, or null
* if the existing padding should be used
* @param bottom the number of pixels of padding to be added on the bottom of the map, or null
* if the existing padding should be used
*/
@Override
public void setPadding(int left, int top, int right, int bottom) {
public void setPadding(Integer left, Integer top, Integer right, Integer bottom) {
if (left != null) {
mMapPaddingLeft = left;
}
if (top != null) {
mMapPaddingTop = top;
}
if (right != null) {
mMapPaddingRight = right;
}
if (bottom != null) {
mMapPaddingBottom = bottom;
}

if (mMap != null) {
mMap.setPadding(left, top, right, bottom);
mMap.setPadding(mMapPaddingLeft, mMapPaddingTop, mMapPaddingRight, mMapPaddingBottom);
}
}

Expand Down Expand Up @@ -617,11 +645,12 @@ public void setMapCenter(Location location, boolean animateToLocation,
LatLng target = MapHelpV2.makeLatLng(location);
LatLng offsetTarget;

if (overlayExpanded) {
// Adjust camera target based on MapModeController.OVERLAY_PERCENTAGE
// so it appears in the center of the visible map
if (isRouteDisplayed() && overlayExpanded) {
// Adjust camera target if the route header is currently displayed - map padding
// doesn't get this quite right, as the header is slid up some and full padding doesn't apply
double percentageOffset = 0.2;
double bias =
(getLongitudeSpanInDecDegrees() * MapModeController.OVERLAY_PERCENTAGE) / 2;
(getLongitudeSpanInDecDegrees() * percentageOffset) / 2;
offsetTarget = new LatLng(target.latitude - bias, target.longitude);
target = offsetTarget;
}
Expand Down Expand Up @@ -718,7 +747,7 @@ public void zoomToRoute() {

Activity a = getActivity();
if (a != null) {
int padding = UIHelp.dpToPixels(a, 20);
int padding = UIHelp.dpToPixels(a, DEFAULT_MAP_PADDING_DP);
mMap.moveCamera(
(CameraUpdateFactory.newLatLngBounds(builder.build(), padding)));
}
Expand All @@ -729,6 +758,43 @@ public void zoomToRoute() {
}
}

/**
* Zoom to include the current map bounds plus the location of the nearest vehicle
*
* @param routeIds markers representing real-time positions for the provided routeIds will be
* checked for proximity to the location (all other routes are ignored)
* @param response trips-for-route API response, which includes real-time vehicle locations in
* status
*/
@Override
public void zoomIncludeClosestVehicle(HashSet<String> routeIds,
ObaTripsForRouteResponse response) {
if (mMap == null) {
return;
}
LatLng closestVehicleLocation = MapHelpV2
.getClosestVehicle(response, routeIds, getMapCenterAsLocation());

LatLngBounds visibleBounds = mMap.getProjection().getVisibleRegion().latLngBounds;

if (visibleBounds.contains(closestVehicleLocation)) {
// Closest vehicle is already in view - don't change camera
return;
}

// Zoom to include current map bounds and closest vehicle location
LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(visibleBounds.northeast);
builder.include(visibleBounds.southwest);
builder.include(closestVehicleLocation);

Activity a = getActivity();
if (a != null) {
int padding = UIHelp.dpToPixels(a, DEFAULT_MAP_PADDING_DP);
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), padding));
}
}

@Override
public void removeRouteOverlay() {
for (Polyline p : mLineOverlay) {
Expand All @@ -738,6 +804,17 @@ public void removeRouteOverlay() {
mLineOverlay.clear();
}

/**
* Clears any stop markers from the map
* @param clearFocusedStop true to clear the currently focused stop, false to leave it on map
*/
@Override
public void removeStopOverlay(boolean clearFocusedStop) {
if (mStopOverlay != null) {
mStopOverlay.clear(clearFocusedStop);
}
}

@Override
public boolean canWatchMapChanges() {
// Android Map API v2 has an OnCameraChangeListener
Expand Down
Loading

0 comments on commit 88007bb

Please sign in to comment.