/** * Copyright (C) 2016 Cambridge Systematics, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onebusaway.android.map; import org.onebusaway.android.io.elements.ObaShape; import org.onebusaway.android.io.elements.ObaShapeElement; import org.onebusaway.android.util.LocationUtils; import org.opentripplanner.api.model.EncodedPolylineBean; import org.opentripplanner.api.model.Itinerary; import org.opentripplanner.api.model.Leg; import org.opentripplanner.routing.core.TraverseMode; import android.graphics.Color; import android.location.Location; import android.os.Bundle; import android.util.Log; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Used to show trip plan results on the map */ public class DirectionsMapController implements MapModeController { private static final String TAG = "DirectionsMapController"; private final Callback mFragment; private Itinerary mItinerary; private boolean mHasRoute = false; private Location mCenter; private Set<Integer> mMarkerIds; public DirectionsMapController(Callback callback) { mFragment = callback; mMarkerIds = new HashSet<>(); } @Override public void setState(Bundle args) { if (args != null) { mItinerary = (Itinerary) args.getSerializable(MapParams.ITINERARY); } setMapState(); } private void setMapState() { clearCurrentState(); if (mItinerary == null) { return; } // Set route overlays for map. If there are no routes (ie start and end are same location) // zoom to origin. Leg firstLeg = mItinerary.legs.get(0); Leg lastLeg = mItinerary.legs.get(mItinerary.legs.size() - 1); Location start = LocationUtils.makeLocation(firstLeg.from.getLat(), firstLeg.from.getLon()); Location end = LocationUtils.makeLocation(lastLeg.to.getLat(), lastLeg.to.getLon()); mCenter = start; for (Leg leg : mItinerary.legs) { LegShape shape = new LegShape(leg.legGeometry); if (shape.getLength() > 0) { mHasRoute = true; int color = resolveColor(leg); mFragment.getMapView().setRouteOverlay(color, new LegShape[]{shape}, false); } } // Colors from https://developers.google.com/android/reference/com/google/android/gms/maps/model/BitmapDescriptorFactory.html // but we can't use the constants directly because we can't import Google Maps classes here float HUE_GREEN = 120.0f; float HUE_RED = 0.0f; // Add beginning marker int markerId = mFragment.getMapView().addMarker(start, HUE_GREEN); if (markerId != -1) { // If marker was successfully added, keep track of ID so we can clear it later mMarkerIds.add(markerId); } // Add end marker markerId = mFragment.getMapView().addMarker(end, HUE_RED); if (markerId != -1) { // If marker was successfully added, keep track of ID so we can clear it later mMarkerIds.add(markerId); } zoom(); } /** * Clears the current state of the controller, so a new route can be loaded */ private void clearCurrentState() { // Clear the existing route and vehicle overlays mFragment.getMapView().removeRouteOverlay(); mFragment.getMapView().removeVehicleOverlay(); mFragment.getMapView().removeStopOverlay(false); // Clear start/end markers for (int i : mMarkerIds) { mFragment.getMapView().removeMarker(i); } mMarkerIds.clear(); } @Override public String getMode() { return MapParams.MODE_DIRECTIONS; } @Override public void destroy() { clearCurrentState(); } @Override public void onPause() { // Don't care } /** * This is called when fm.beginTransaction().hide() or fm.beginTransaction().show() is called * * @param hidden True if the fragment is now hidden, false if it is not visible. */ @Override public void onHidden(boolean hidden) { // Don't care } @Override public void onResume() { setMapState(); } @Override public void onSaveInstanceState(Bundle outState) { // Don't care } @Override public void onLocation() { // Don't care } @Override public void onNoLocation() { // Don't care } @Override public void notifyMapChanged() { // Don't care } @Override public void onViewStateRestored(Bundle bundle) { // Don't care } private void zoom() { ObaMapView view = mFragment.getMapView(); if (mHasRoute) { view.zoomToItinerary(); } else { view.setMapCenter(mCenter, false, false); view.setZoom(MapParams.DEFAULT_ZOOM); } } private static int resolveColor(Leg leg) { if (leg.routeColor != null) { try { return Long.decode("0xFF" + leg.routeColor).intValue(); } catch (Exception ex) { Log.e(TAG, "Error parsing color=" + leg.routeColor + ": " + ex.getMessage()); } } if (TraverseMode.valueOf(leg.mode).isTransit()) { return Color.BLUE; } return Color.GRAY; } class LegShape implements ObaShape { private EncodedPolylineBean bean; LegShape(EncodedPolylineBean bean) { this.bean = bean; } @Override public int getLength() { return bean.getLength(); } @Override public String getRawLevels() { return bean.getLevels(); } @Override public List<Integer> getLevels() { return ObaShapeElement.decodeLevels(bean.getLevels(), bean.getLength()); } @Override public List<Location> getPoints() { return ObaShapeElement.decodeLine(bean.getPoints(), bean.getLength()); } @Override public String getRawPoints() { return bean.getPoints(); } } }