Skip to content

Commit

Permalink
Fix bug with MapRoute onClick (#703)
Browse files Browse the repository at this point in the history
* Fix bug with MapRoute onClick

* Update click logic to ensure better precision

* Fixup checkstyle

* Refactor per review comments

* Clean up per review
  • Loading branch information
danesfeder authored Feb 15, 2018
1 parent 2b10c8e commit 27227c3
Showing 1 changed file with 82 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import com.mapbox.api.directions.v5.models.DirectionsRoute;
import com.mapbox.api.directions.v5.models.RouteLeg;
import com.mapbox.core.constants.Constants;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
Expand All @@ -26,7 +27,6 @@
import com.mapbox.mapboxsdk.style.layers.Property;
import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.core.constants.Constants;
import com.mapbox.services.android.navigation.ui.v5.R;
import com.mapbox.services.android.navigation.ui.v5.utils.MapImageUtils;
import com.mapbox.services.android.navigation.ui.v5.utils.MapUtils;
Expand All @@ -43,6 +43,8 @@
import com.mapbox.turf.TurfMisc;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;

Expand Down Expand Up @@ -73,7 +75,6 @@ public class NavigationMapRoute implements ProgressChangeListener, MapView.OnMap
private static final String SOURCE_KEY = "source";
private static final String INDEX_KEY = "index";

private static final int ROUTE_CLICK_PADDING = 250;
private static final String GENERIC_ROUTE_SOURCE_ID = "mapbox-navigation-route-source";
private static final String GENERIC_ROUTE_LAYER_ID = "mapbox-navigation-route-layer";
private static final String WAYPOINT_SOURCE_ID = "mapbox-navigation-waypoint-source";
Expand Down Expand Up @@ -106,6 +107,7 @@ public class NavigationMapRoute implements ProgressChangeListener, MapView.OnMap
private int destinationWaypointIcon;

private List<DirectionsRoute> directionsRoutes;
private HashMap<LineString, DirectionsRoute> routeLineStrings;
private final MapboxNavigation navigation;
private final MapboxMap mapboxMap;
private List<String> layerIds;
Expand Down Expand Up @@ -214,6 +216,7 @@ public NavigationMapRoute(@Nullable MapboxNavigation navigation, @NonNull MapVie
*/
private void initialize() {
layerIds = new ArrayList<>();
routeLineStrings = new HashMap<>();
getAttributes();
placeRouteBelow();
}
Expand Down Expand Up @@ -248,6 +251,9 @@ public void addRoutes(@NonNull @Size(min = 1) List<DirectionsRoute> directionsRo
mapboxMap.removeLayer(id);
}
}
if (directionsRoutes.size() == 1) {
alternativesVisible = false;
}
featureCollections.clear();
generateFeatureCollectionList(directionsRoutes);
drawRoutes();
Expand Down Expand Up @@ -390,7 +396,7 @@ private void updatePrimaryRoute(String layerId, int index) {
);
if (index == primaryRouteIndex) {
mapboxMap.removeLayer(layer);
mapboxMap.addLayerBelow(layer, belowLayer);
mapboxMap.addLayerBelow(layer, WAYPOINT_LAYER_ID);
}
}
}
Expand All @@ -404,7 +410,7 @@ private void updatePrimaryShieldRoute(String layerId, int index) {
);
if (index == primaryRouteIndex) {
mapboxMap.removeLayer(layer);
mapboxMap.addLayerBelow(layer, belowLayer);
mapboxMap.addLayerBelow(layer, WAYPOINT_LAYER_ID);
}
}
}
Expand Down Expand Up @@ -581,7 +587,7 @@ private static Feature getPointFromLineString(RouteLeg leg, int stepIndex) {
* Adds the necessary listeners
*/
private void addListeners() {
mapboxMap.setOnMapClickListener(this);
mapboxMap.addOnMapClickListener(this);
if (navigation != null) {
navigation.addProgressChangeListener(this);
}
Expand All @@ -601,50 +607,71 @@ public void removeRoute() {

@Override
public void onMapClick(@NonNull LatLng point) {
if (directionsRoutes == null || directionsRoutes.isEmpty() || !alternativesVisible) {
if (invalidMapClick()) {
return;
}
// determine which feature collections are alternative routes
for (FeatureCollection featureCollection : featureCollections) {
if (!(featureCollection.getFeatures().get(0).getGeometry() instanceof Point)) {
List<com.mapbox.geojson.Point> linePoints = calculateLinePoints(featureCollection);

com.mapbox.geojson.Feature feature = TurfMisc.pointOnLine(
com.mapbox.geojson.Point.fromLngLat(point.getLongitude(), point.getLatitude()),
linePoints);
com.mapbox.geojson.Point pointAlong = (com.mapbox.geojson.Point) feature.geometry();
// No linestring to get point on line, thus we just return.
if (pointAlong == null) {
return;
}
double dis = TurfMeasurement.distance(com.mapbox.geojson
.Point.fromLngLat(point.getLongitude(),
point.getLatitude()), pointAlong, TurfConstants.UNIT_METERS);
if (dis <= ROUTE_CLICK_PADDING) {
primaryRouteIndex = featureCollection.getFeatures()
.get(0).getNumberProperty(INDEX_KEY).intValue();
}
}
final int currentRouteIndex = primaryRouteIndex;

if (findClickedRoute(point)) {
return;
}
updateRoute();
if (onRouteSelectionChangeListener != null) {
onRouteSelectionChangeListener.onNewPrimaryRouteSelected(
directionsRoutes.get(primaryRouteIndex));
checkNewRouteFound(currentRouteIndex);
}

private boolean invalidMapClick() {
return routeLineStrings == null || routeLineStrings.isEmpty() || !alternativesVisible;
}

private boolean findClickedRoute(@NonNull LatLng point) {
HashMap<Double, DirectionsRoute> routeDistancesAwayFromClick = new HashMap<>();

com.mapbox.geojson.Point clickPoint
= com.mapbox.geojson.Point.fromLngLat(point.getLongitude(), point.getLatitude());

if (calculateClickDistancesFromRoutes(routeDistancesAwayFromClick, clickPoint)) {
return true;
}
List<Double> distancesAwayFromClick = new ArrayList<>(routeDistancesAwayFromClick.keySet());
Collections.sort(distancesAwayFromClick);

DirectionsRoute clickedRoute = routeDistancesAwayFromClick.get(distancesAwayFromClick.get(0));
primaryRouteIndex = directionsRoutes.indexOf(clickedRoute);
return false;
}

@SuppressWarnings("unchecked")
private static List<com.mapbox.geojson.Point> calculateLinePoints(
FeatureCollection featureCollection) {
private boolean calculateClickDistancesFromRoutes(HashMap<Double, DirectionsRoute> routeDistancesAwayFromClick,
com.mapbox.geojson.Point clickPoint) {
for (LineString lineString : routeLineStrings.keySet()) {
com.mapbox.geojson.Point pointOnLine = findPointOnLine(clickPoint, lineString);

if (pointOnLine == null) {
return true;
}
double distance = TurfMeasurement.distance(clickPoint, pointOnLine, TurfConstants.UNIT_METERS);
routeDistancesAwayFromClick.put(distance, routeLineStrings.get(lineString));
}
return false;
}

private com.mapbox.geojson.Point findPointOnLine(com.mapbox.geojson.Point clickPoint, LineString lineString) {
List<com.mapbox.geojson.Point> linePoints = new ArrayList<>();
for (Feature feature : featureCollection.getFeatures()) {
List<Position> positions = (List<Position>) feature.getGeometry().getCoordinates();
for (Position pos : positions) {
linePoints.add(com.mapbox.geojson
.Point.fromLngLat(pos.getLongitude(), pos.getLatitude()));
List<Position> positions = lineString.getCoordinates();
for (Position pos : positions) {
linePoints.add(com.mapbox.geojson.Point.fromLngLat(pos.getLongitude(), pos.getLatitude()));
}

com.mapbox.geojson.Feature feature = TurfMisc.pointOnLine(clickPoint, linePoints);
return (com.mapbox.geojson.Point) feature.geometry();
}

private void checkNewRouteFound(int currentRouteIndex) {
if (currentRouteIndex != primaryRouteIndex) {
updateRoute();
if (onRouteSelectionChangeListener != null) {
onRouteSelectionChangeListener.onNewPrimaryRouteSelected(
directionsRoutes.get(primaryRouteIndex));
}
}
return linePoints;
}

private void updateRoute() {
Expand All @@ -655,8 +682,8 @@ private void updateRoute() {
int index = featureCollection.getFeatures().get(0).getNumberProperty(INDEX_KEY).intValue();
updatePrimaryShieldRoute(String.format(Locale.US, ID_FORMAT, GENERIC_ROUTE_SHIELD_LAYER_ID,
index), index);
updatePrimaryRoute(String.format(Locale.US, ID_FORMAT, GENERIC_ROUTE_LAYER_ID, index),
index);
updatePrimaryRoute(String.format(Locale.US, ID_FORMAT, GENERIC_ROUTE_LAYER_ID,
index), index);
}
}
}
Expand Down Expand Up @@ -703,16 +730,27 @@ public void onProgressChange(Location location, RouteProgress routeProgress) {
* the source into pieces so data-driven styling can be used to change the route colors
* accordingly.
*/
private static FeatureCollection addTrafficToSource(DirectionsRoute route, int index) {
private FeatureCollection addTrafficToSource(DirectionsRoute route, int index) {
final List<Feature> features = new ArrayList<>();
LineString originalGeometry = LineString.fromPolyline(route.geometry(), Constants.PRECISION_6);
buildRouteFeatureFromGeometry(index, features, originalGeometry);
routeLineStrings.put(originalGeometry, route);

LineString lineString = LineString.fromPolyline(route.geometry(), Constants.PRECISION_6);
buildTrafficFeaturesFromRoute(route, index, features, lineString);
return FeatureCollection.fromFeatures(features);
}

private void buildRouteFeatureFromGeometry(int index, List<Feature> features, LineString originalGeometry) {
Feature feat = Feature.fromGeometry(originalGeometry);
feat.addStringProperty(SOURCE_KEY, String.format(Locale.US, ID_FORMAT, GENERIC_ROUTE_SOURCE_ID,
index));
feat.addNumberProperty(INDEX_KEY, index);
features.add(feat);
}

LineString lineString = LineString.fromPolyline(route.geometry(), Constants.PRECISION_6);
private void buildTrafficFeaturesFromRoute(DirectionsRoute route, int index,
List<Feature> features, LineString lineString) {
for (RouteLeg leg : route.legs()) {
if (leg.annotation() != null && leg.annotation().congestion() != null) {
for (int i = 0; i < leg.annotation().congestion().size(); i++) {
Expand All @@ -736,6 +774,5 @@ private static FeatureCollection addTrafficToSource(DirectionsRoute route, int i
features.add(feature);
}
}
return FeatureCollection.fromFeatures(features);
}
}

0 comments on commit 27227c3

Please sign in to comment.