package net.osmand.plus.helpers; import gnu.trove.list.array.TIntArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import net.osmand.Location; import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion; import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule; import net.osmand.binary.RouteDataObject; import net.osmand.data.Amenity; import net.osmand.data.Amenity.AmenityRoutePoint; import net.osmand.data.LocationPoint; import net.osmand.data.PointDescription; import net.osmand.osm.PoiType; import net.osmand.plus.ApplicationMode; import net.osmand.plus.IconsCache; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandSettings.MetricsConstants; import net.osmand.plus.R; import net.osmand.plus.TargetPointsHelper.TargetPoint; import net.osmand.plus.activities.IntermediatePointsDialog; import net.osmand.plus.base.FavoriteImageDrawable; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.render.RenderingIcons; import net.osmand.plus.routing.AlarmInfo; import net.osmand.plus.routing.AlarmInfo.AlarmInfoType; import net.osmand.plus.routing.RouteCalculationResult; import net.osmand.plus.routing.VoiceRouter; import net.osmand.util.MapUtils; import android.content.Context; import android.graphics.drawable.Drawable; // import android.widget.Toast; /** */ public class WaypointHelper { private static final int NOT_ANNOUNCED = 0; private static final int ANNOUNCED_ONCE = 1; private static final int ANNOUNCED_DONE = 2; private int searchDeviationRadius = 500; private int poiSearchDeviationRadius = 100; private static final int LONG_ANNOUNCE_RADIUS = 700; private static final int SHORT_ANNOUNCE_RADIUS = 150; private static final int ALARMS_ANNOUNCE_RADIUS = 150; // don't annoy users by lots of announcements private static final int APPROACH_POI_LIMIT = 1; private static final int ANNOUNCE_POI_LIMIT = 3; OsmandApplication app; // every time we modify this collection, we change the reference (copy on write list) public static final int TARGETS = 0; public static final int WAYPOINTS = 1; public static final int POI = 2; public static final int FAVORITES = 3; public static final int ALARMS = 4; public static final int MAX = 5; public static final int[] SEARCH_RADIUS_VALUES = {50, 100, 200, 500, 1000, 2000, 5000}; private List<List<LocationPointWrapper>> locationPoints = new ArrayList<List<LocationPointWrapper>>(); private ConcurrentHashMap<LocationPoint, Integer> locationPointsStates = new ConcurrentHashMap<LocationPoint, Integer>(); private TIntArrayList pointsProgress = new TIntArrayList(); private RouteCalculationResult route; private long announcedAlarmTime; private ApplicationMode appMode; public WaypointHelper(OsmandApplication application) { app = application; appMode = app.getSettings().getApplicationMode(); } public List<LocationPointWrapper> getWaypoints(int type) { if (type == TARGETS) { return getTargets(new ArrayList<WaypointHelper.LocationPointWrapper>()); } if (type >= locationPoints.size()) { return Collections.emptyList(); } return locationPoints.get(type); } public void locationChanged(Location location) { app.getAppCustomization(); announceVisibleLocations(); } public int getRouteDistance(LocationPointWrapper point) { return route.getDistanceToPoint(point.routeIndex); } public void removeVisibleLocationPoint(LocationPointWrapper lp) { if (lp.type < locationPoints.size()) { locationPoints.get(lp.type).remove(lp); } } public void removeVisibleLocationPoint(List<LocationPointWrapper> points) { List<TargetPoint> ps = app.getTargetPointsHelper().getIntermediatePointsWithTarget(); boolean[] checkedIntermediates = null; for (LocationPointWrapper lp : points) { if (lp.type == TARGETS) { if (checkedIntermediates == null) { checkedIntermediates = new boolean[ps.size()]; Arrays.fill(checkedIntermediates, true); } if (((TargetPoint) lp.point).intermediate) { checkedIntermediates[((TargetPoint) lp.point).index] = false; } else { checkedIntermediates[ps.size() - 1] = false; } } else if (lp.type < locationPoints.size()) { locationPoints.get(lp.type).remove(lp); } } if (checkedIntermediates != null) { IntermediatePointsDialog.commitPointsRemoval(app, checkedIntermediates); } } public LocationPointWrapper getMostImportantLocationPoint(List<LocationPointWrapper> list) { //Location lastProjection = app.getRoutingHelper().getLastProjection(); if (list != null) { list.clear(); } LocationPointWrapper found = null; for (int type = 0; type < locationPoints.size(); type++) { if (type == ALARMS || type == TARGETS) { continue; } int kIterator = pointsProgress.get(type); List<LocationPointWrapper> lp = locationPoints.get(type); while (kIterator < lp.size()) { LocationPointWrapper lwp = lp.get(kIterator); if (lp.get(kIterator).routeIndex < route.getCurrentRoute()) { // skip } else { if (route.getDistanceToPoint(lwp.routeIndex) <= LONG_ANNOUNCE_RADIUS) { if (found == null || found.routeIndex < lwp.routeIndex) { found = lwp; if (list != null) { list.add(lwp); } } } break; } kIterator++; } } return found; } public AlarmInfo getMostImportantAlarm(MetricsConstants mc, boolean showCameras) { Location lastProjection = app.getRoutingHelper().getLastProjection(); float mxspeed = route.getCurrentMaxSpeed(); float delta = app.getSettings().SPEED_LIMIT_EXCEED.get() / 3.6f; AlarmInfo speedAlarm = createSpeedAlarm(mc, mxspeed, lastProjection, delta); if (speedAlarm != null) { getVoiceRouter().announceSpeedAlarm(speedAlarm.getIntValue(), lastProjection.getSpeed()); } AlarmInfo mostImportant = speedAlarm; int value = speedAlarm != null ? speedAlarm.updateDistanceAndGetPriority(0, 0) : Integer.MAX_VALUE; if (ALARMS < pointsProgress.size()) { int kIterator = pointsProgress.get(ALARMS); List<LocationPointWrapper> lp = locationPoints.get(ALARMS); while (kIterator < lp.size()) { LocationPointWrapper lwp = lp.get(kIterator); if (lp.get(kIterator).routeIndex < route.getCurrentRoute()) { // skip } else { int d = route.getDistanceToPoint(lwp.routeIndex); if (d > LONG_ANNOUNCE_RADIUS) { break; } AlarmInfo inf = (AlarmInfo) lwp.point; float speed = lastProjection != null && lastProjection.hasSpeed() ? lastProjection.getSpeed() : 0; float time = speed > 0 ? d / speed : Integer.MAX_VALUE; int vl = inf.updateDistanceAndGetPriority(time, d); if (vl < value && (showCameras || inf.getType() != AlarmInfoType.SPEED_CAMERA)) { mostImportant = inf; value = vl; } } kIterator++; } } return mostImportant; } public void enableWaypointType(int type, boolean enable) { //An item will be displayed in the Waypoint list if either "Show..." or "Announce..." is selected for it in the Navigation settings //Keep both "Show..." and "Announce..." Nav settings in sync when user changes what to display in the Waypoint list, as follows: if (type == ALARMS) { app.getSettings().SHOW_TRAFFIC_WARNINGS.setModeValue(appMode, enable); app.getSettings().SPEAK_TRAFFIC_WARNINGS.setModeValue(appMode, enable); app.getSettings().SHOW_PEDESTRIAN.setModeValue(appMode, enable); app.getSettings().SPEAK_PEDESTRIAN.setModeValue(appMode, enable); //But do not implicitly change speed_cam settings here because of legal restrictions in some countries, so Nav settings must prevail } else if (type == POI) { app.getSettings().SHOW_NEARBY_POI.setModeValue(appMode, enable); app.getSettings().ANNOUNCE_NEARBY_POI.setModeValue(appMode, enable); } else if (type == FAVORITES) { app.getSettings().SHOW_NEARBY_FAVORITES.setModeValue(appMode, enable); app.getSettings().ANNOUNCE_NEARBY_FAVORITES.setModeValue(appMode, enable); } else if (type == WAYPOINTS) { app.getSettings().SHOW_WPT.set(enable); app.getSettings().ANNOUNCE_WPT.set(enable); } recalculatePoints(route, type, locationPoints); } public void recalculatePoints(int type) { recalculatePoints(route, type, locationPoints); } public boolean isTypeConfigurable(int waypointType) { return waypointType != TARGETS; } public boolean isTypeVisible(int waypointType) { boolean vis = app.getAppCustomization().isWaypointGroupVisible(waypointType, route); if (!vis) { return false; } return vis; } public boolean isTypeEnabled(int type) { if (type == ALARMS) { return app.getSettings().SHOW_TRAFFIC_WARNINGS.getModeValue(appMode); } else if (type == POI) { return app.getSettings().SHOW_NEARBY_POI.getModeValue(appMode); } else if (type == FAVORITES) { return app.getSettings().SHOW_NEARBY_FAVORITES.getModeValue(appMode); } else if (type == WAYPOINTS) { return app.getSettings().SHOW_WPT.get(); } return true; } public AlarmInfo calculateMostImportantAlarm(RouteDataObject ro, Location loc, MetricsConstants mc, boolean showCameras) { float mxspeed = ro.getMaximumSpeed(ro.bearingVsRouteDirection(loc)); float delta = app.getSettings().SPEED_LIMIT_EXCEED.get() / 3.6f; AlarmInfo speedAlarm = createSpeedAlarm(mc, mxspeed, loc, delta); if (speedAlarm != null) { getVoiceRouter().announceSpeedAlarm(speedAlarm.getIntValue(), loc.getSpeed()); return speedAlarm; } for (int i = 0; i < ro.getPointsLength(); i++) { int[] pointTypes = ro.getPointTypes(i); RouteRegion reg = ro.region; if (pointTypes != null) { for (int r = 0; r < pointTypes.length; r++) { RouteTypeRule typeRule = reg.quickGetEncodingRule(pointTypes[r]); AlarmInfo info = AlarmInfo.createAlarmInfo(typeRule, 0, loc); //Check if stop sign is tagged with direction=forward/backward if (info != null && info.getType() != null && info.getType() == AlarmInfoType.STOP) { //TODO: better than bearingVsRouteDirection would be routeVsWayDirection analysis if (ro.isStopDirectionOpposite(ro.bearingVsRouteDirection(loc))) { info = null; } //TODO: Still missing here is analysis if a stop without direction=* tagging is _behind_ an intersection } // Issue #2873 may indicate we need some sort of check here if Alarm is in forward direction // But cannot reproduce the issue for now //if (loc.hasBearing()) { // if (Math.abs(MapUtils.alignAngleDifference(bearingTo("actual alarm location") - loc.getBearing() / 180f * Math.PI)) >= Math.PI / 2f) { // info = null; // } //Toast.makeText(app.getApplicationContext(), Double.toString(ro.directionRoute(0, true)) + ",\n" + Double.toString(loc.getBearing()) + ",\n" + Double.toString(MapUtils.alignAngleDifference(ro.directionRoute(0, true) - loc.getBearing() / 180f * Math.PI))), Toast.LENGTH_LONG).show(); //} if (info != null) { if (info.getType() != AlarmInfoType.SPEED_CAMERA || showCameras) { long ms = System.currentTimeMillis(); if (ms - announcedAlarmTime > 50 * 1000) { announcedAlarmTime = ms; getVoiceRouter().announceAlarm(info, loc.getSpeed()); } return info; } } } } } return null; } private static AlarmInfo createSpeedAlarm(MetricsConstants mc, float mxspeed, Location loc, float delta) { AlarmInfo speedAlarm = null; if (mxspeed != 0 && loc != null && loc.hasSpeed() && mxspeed != RouteDataObject.NONE_MAX_SPEED) { if (loc.getSpeed() > mxspeed + delta) { int speed; if (mc == MetricsConstants.KILOMETERS_AND_METERS) { speed = Math.round(mxspeed * 3.6f); } else { speed = Math.round(mxspeed * 3.6f / 1.6f); } speedAlarm = AlarmInfo.createSpeedLimit(speed, loc); } } return speedAlarm; } public void announceVisibleLocations() { Location lastKnownLocation = app.getRoutingHelper().getLastProjection(); if (lastKnownLocation != null && app.getRoutingHelper().isFollowingMode()) { for (int type = 0; type < locationPoints.size(); type++) { int currentRoute = route.getCurrentRoute(); List<LocationPointWrapper> approachPoints = new ArrayList<LocationPointWrapper>(); List<LocationPointWrapper> announcePoints = new ArrayList<LocationPointWrapper>(); List<LocationPointWrapper> lp = locationPoints.get(type); if (lp != null) { int kIterator = pointsProgress.get(type); while (kIterator < lp.size() && lp.get(kIterator).routeIndex < currentRoute) { kIterator++; } pointsProgress.set(type, kIterator); while (kIterator < lp.size()) { LocationPointWrapper lwp = lp.get(kIterator); if (route.getDistanceToPoint(lwp.routeIndex) > LONG_ANNOUNCE_RADIUS * 2) { break; } LocationPoint point = lwp.point; double d1 = Math.max(0.0, MapUtils.getDistance(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude(), point.getLatitude(), point.getLongitude()) - lwp.getDeviationDistance()); Integer state = locationPointsStates.get(point); if (state != null && state.intValue() == ANNOUNCED_ONCE && getVoiceRouter().isDistanceLess(lastKnownLocation.getSpeed(), d1, SHORT_ANNOUNCE_RADIUS, 0f)) { locationPointsStates.put(point, ANNOUNCED_DONE); announcePoints.add(lwp); } else if (type != ALARMS && (state == null || state == NOT_ANNOUNCED) && getVoiceRouter().isDistanceLess(lastKnownLocation.getSpeed(), d1, LONG_ANNOUNCE_RADIUS, 0f)) { locationPointsStates.put(point, ANNOUNCED_ONCE); approachPoints.add(lwp); } else if (type == ALARMS && (state == null || state == NOT_ANNOUNCED) && getVoiceRouter().isDistanceLess(lastKnownLocation.getSpeed(), d1, ALARMS_ANNOUNCE_RADIUS, 0f)) { locationPointsStates.put(point, ANNOUNCED_ONCE); approachPoints.add(lwp); } kIterator++; } if (!announcePoints.isEmpty()) { if (announcePoints.size() > ANNOUNCE_POI_LIMIT) { announcePoints = announcePoints.subList(0, ANNOUNCE_POI_LIMIT); } if (type == WAYPOINTS) { getVoiceRouter().announceWaypoint(announcePoints); } else if (type == POI) { getVoiceRouter().announcePoi(announcePoints); } else if (type == ALARMS) { // nothing to announce } else if (type == FAVORITES) { getVoiceRouter().announceFavorite(announcePoints); } } if (!approachPoints.isEmpty()) { if (approachPoints.size() > APPROACH_POI_LIMIT) { approachPoints = approachPoints.subList(0, APPROACH_POI_LIMIT); } if (type == WAYPOINTS) { getVoiceRouter().approachWaypoint(lastKnownLocation, approachPoints); } else if (type == POI) { getVoiceRouter().approachPoi(lastKnownLocation, approachPoints); } else if (type == ALARMS) { EnumSet<AlarmInfoType> ait = EnumSet.noneOf(AlarmInfoType.class); for (LocationPointWrapper pw : approachPoints) { ait.add(((AlarmInfo) pw.point).getType()); } for (AlarmInfoType t : ait) { app.getRoutingHelper().getVoiceRouter().announceAlarm(new AlarmInfo(t, -1), lastKnownLocation.getSpeed()); } } else if (type == FAVORITES) { getVoiceRouter().approachFavorite(lastKnownLocation, approachPoints); } } } } } } protected VoiceRouter getVoiceRouter() { return app.getRoutingHelper().getVoiceRouter(); } public boolean isRouteCalculated() { return route != null && !route.isEmpty(); } public List<LocationPointWrapper> getAllPoints() { List<LocationPointWrapper> points = new ArrayList<WaypointHelper.LocationPointWrapper>(); List<List<LocationPointWrapper>> local = locationPoints; TIntArrayList ps = pointsProgress; for (int i = 0; i < local.size(); i++) { List<LocationPointWrapper> loc = local.get(i); if (ps.get(i) < loc.size()) { points.addAll(loc.subList(ps.get(i), loc.size())); } } getTargets(points); sortList(points); return points; } protected List<LocationPointWrapper> getTargets(List<LocationPointWrapper> points) { List<TargetPoint> wts = app.getTargetPointsHelper().getIntermediatePointsWithTarget(); for (int k = 0; k < wts.size(); k++) { final int index = wts.size() - k - 1; TargetPoint tp = wts.get(index); int routeIndex; if (route == null) { routeIndex = k == 0 ? Integer.MAX_VALUE : index; } else { routeIndex = k == 0 ? route.getImmutableAllLocations().size() - 1 : route.getIndexOfIntermediate(k - 1); } points.add(new LocationPointWrapper(route, TARGETS, tp, 0, routeIndex)); } Collections.reverse(points); return points; } public void clearAllVisiblePoints() { this.locationPointsStates.clear(); this.locationPoints = new ArrayList<List<LocationPointWrapper>>(); } public void setNewRoute(RouteCalculationResult route) { List<List<LocationPointWrapper>> locationPoints = new ArrayList<List<LocationPointWrapper>>(); recalculatePoints(route, -1, locationPoints); setLocationPoints(locationPoints, route); } protected void recalculatePoints(RouteCalculationResult route, int type, List<List<LocationPointWrapper>> locationPoints) { boolean all = type == -1; appMode = app.getSettings().getApplicationMode(); if (route != null && !route.isEmpty()) { boolean showWaypoints = app.getSettings().SHOW_WPT.get(); // global boolean announceWaypoints = app.getSettings().ANNOUNCE_WPT.get(); // global if(route.getAppMode() != null) { appMode = route.getAppMode(); } boolean showPOI = app.getSettings().SHOW_NEARBY_POI.getModeValue(appMode); boolean showFavorites = app.getSettings().SHOW_NEARBY_FAVORITES.getModeValue(appMode); boolean announceFavorites = app.getSettings().ANNOUNCE_NEARBY_FAVORITES.getModeValue(appMode); boolean announcePOI = app.getSettings().ANNOUNCE_NEARBY_POI.getModeValue(appMode); if ((type == FAVORITES || all)) { final List<LocationPointWrapper> array = clearAndGetArray(locationPoints, FAVORITES); if (showFavorites) { findLocationPoints(route, FAVORITES, array, app.getFavorites().getVisibleFavouritePoints(), announceFavorites); sortList(array); } } if ((type == ALARMS || all)) { final List<LocationPointWrapper> array = clearAndGetArray(locationPoints, ALARMS); if(route.getAppMode() != null) { calculateAlarms(route, array, appMode); sortList(array); } } if ((type == WAYPOINTS || all)) { final List<LocationPointWrapper> array = clearAndGetArray(locationPoints, WAYPOINTS); if (showWaypoints) { findLocationPoints(route, WAYPOINTS, array, app.getAppCustomization().getWaypoints(), announceWaypoints); findLocationPoints(route, WAYPOINTS, array, route.getLocationPoints(), announceWaypoints); sortList(array); } } if ((type == POI || all)) { final List<LocationPointWrapper> array = clearAndGetArray(locationPoints, POI); if (showPOI) { calculatePoi(route, array, announcePOI); sortList(array); } } } } private float dist(LocationPoint l, List<Location> locations, int[] ind, boolean[] devDirRight) { float dist = Float.POSITIVE_INFINITY; // Special iterations because points stored by pairs! for (int i = 1; i < locations.size(); i++) { final double ld = MapUtils.getOrthogonalDistance( l.getLatitude(), l.getLongitude(), locations.get(i - 1).getLatitude(), locations.get(i - 1).getLongitude(), locations.get(i).getLatitude(), locations.get(i).getLongitude()); if (ld < dist) { if (ind != null) { ind[0] = i; } dist = (float) ld; } } if (ind != null && dist < Float.POSITIVE_INFINITY) { int i = ind[0]; devDirRight[0] = MapUtils.rightSide(l.getLatitude(), l.getLongitude(), locations.get(i - 1).getLatitude(), locations.get(i - 1).getLongitude(), locations.get(i).getLatitude(), locations.get(i).getLongitude()); } return dist; } protected synchronized void setLocationPoints(List<List<LocationPointWrapper>> locationPoints, RouteCalculationResult route) { this.locationPoints = locationPoints; this.locationPointsStates.clear(); TIntArrayList list = new TIntArrayList(locationPoints.size()); list.fill(0, locationPoints.size(), 0); this.pointsProgress = list; this.route = route; } protected void sortList(List<LocationPointWrapper> list) { Collections.sort(list, new Comparator<LocationPointWrapper>() { @Override public int compare(LocationPointWrapper olhs, LocationPointWrapper orhs) { int lhs = olhs.routeIndex; int rhs = orhs.routeIndex; if (lhs == rhs) { return Float.compare(olhs.deviationDistance, orhs.deviationDistance); } return lhs < rhs ? -1 : 1; } }); } protected void calculatePoi(RouteCalculationResult route, List<LocationPointWrapper> locationPoints, boolean announcePOI) { if (app.getPoiFilters().isShowingAnyPoi()) { final List<Location> locs = route.getImmutableAllLocations(); List<Amenity> amenities = new ArrayList<>(); for (PoiUIFilter pf : app.getPoiFilters().getSelectedPoiFilters()) { amenities.addAll(pf.searchAmenitiesOnThePath(locs, poiSearchDeviationRadius)); } for (Amenity a : amenities) { AmenityRoutePoint rp = a.getRoutePoint(); int i = locs.indexOf(rp.pointA); if (i >= 0) { LocationPointWrapper lwp = new LocationPointWrapper(route, POI, new AmenityLocationPoint(a), (float) rp.deviateDistance, i); lwp.deviationDirectionRight = rp.deviationDirectionRight; lwp.setAnnounce(announcePOI); locationPoints.add(lwp); } } } } private void calculateAlarms(RouteCalculationResult route, List<LocationPointWrapper> array, ApplicationMode mode) { for (AlarmInfo i : route.getAlarmInfo()) { if (i.getType() == AlarmInfoType.SPEED_CAMERA) { if (app.getSettings().SHOW_CAMERAS.getModeValue(mode) || app.getSettings().SPEAK_SPEED_CAMERA.getModeValue(mode)) { LocationPointWrapper lw = new LocationPointWrapper(route, ALARMS, i, 0, i.getLocationIndex()); lw.setAnnounce(app.getSettings().SPEAK_SPEED_CAMERA.get()); array.add(lw); } } else { if (app.getSettings().SHOW_TRAFFIC_WARNINGS.getModeValue(mode) || app.getSettings().SPEAK_TRAFFIC_WARNINGS.getModeValue(mode)) { LocationPointWrapper lw = new LocationPointWrapper(route, ALARMS, i, 0, i.getLocationIndex()); lw.setAnnounce(app.getSettings().SPEAK_TRAFFIC_WARNINGS.get()); array.add(lw); } } } } private List<LocationPointWrapper> clearAndGetArray(List<List<LocationPointWrapper>> array, int ind) { while (array.size() <= ind) { array.add(new ArrayList<WaypointHelper.LocationPointWrapper>()); } array.get(ind).clear(); return array.get(ind); } private void findLocationPoints(RouteCalculationResult rt, int type, List<LocationPointWrapper> locationPoints, List<? extends LocationPoint> points, boolean announce) { List<Location> immutableAllLocations = rt.getImmutableAllLocations(); int[] ind = new int[1]; boolean[] devDirRight = new boolean[1]; for (LocationPoint p : points) { float dist = dist(p, immutableAllLocations, ind, devDirRight); int rad = getSearchDeviationRadius(type); if (dist <= rad) { LocationPointWrapper lpw = new LocationPointWrapper(rt, type, p, dist, ind[0]); lpw.deviationDirectionRight = devDirRight[0]; lpw.setAnnounce(announce); locationPoints.add(lpw); } } } /// public Set<PoiUIFilter> getPoiFilters() { return app.getPoiFilters().getSelectedPoiFilters(); } public static class LocationPointWrapper { LocationPoint point; float deviationDistance; boolean deviationDirectionRight; int routeIndex; boolean announce = true; RouteCalculationResult route; int type; public LocationPointWrapper() { } public LocationPointWrapper(RouteCalculationResult rt, int type, LocationPoint point, float deviationDistance, int routeIndex) { this.route = rt; this.type = type; this.point = point; this.deviationDistance = deviationDistance; this.routeIndex = routeIndex; } public void setAnnounce(boolean announce) { this.announce = announce; } public float getDeviationDistance() { return deviationDistance; } public boolean isDeviationDirectionRight() { return deviationDirectionRight; } public LocationPoint getPoint() { return point; } public Drawable getDrawable(Context uiCtx, OsmandApplication app, boolean nightMode) { if (type == POI) { Amenity amenity = ((AmenityLocationPoint) point).a; PoiType st = amenity.getType().getPoiTypeByKeyName(amenity.getSubType()); if (st != null) { if (RenderingIcons.containsBigIcon(st.getIconKeyName())) { return uiCtx.getResources().getDrawable( RenderingIcons.getBigIconResourceId(st.getIconKeyName())); } else if (RenderingIcons.containsBigIcon(st.getOsmTag() + "_" + st.getOsmValue())) { return uiCtx.getResources().getDrawable( RenderingIcons.getBigIconResourceId(st.getOsmTag() + "_" + st.getOsmValue())); } } return null; } else if (type == TARGETS) { IconsCache iconsCache = app.getIconsCache(); if (((TargetPoint) point).start) { if (app.getTargetPointsHelper().getPointToStart() == null) { ApplicationMode appMode = app.getSettings().getApplicationMode(); return uiCtx.getResources().getDrawable(appMode.getResourceLocationDay()); } else { return iconsCache.getIcon(R.drawable.list_startpoint, 0); } } else if (((TargetPoint) point).intermediate) { return iconsCache.getIcon(R.drawable.list_intermediate, 0); } else { return iconsCache.getIcon(R.drawable.list_destination, 0); } } else if (type == FAVORITES || type == WAYPOINTS) { return FavoriteImageDrawable.getOrCreate(uiCtx, point.getColor(), false); } else if (type == ALARMS) { //assign alarm list icons manually for now if (((AlarmInfo) point).getType().toString().equals("SPEED_CAMERA")) { return uiCtx.getResources().getDrawable(R.drawable.mx_highway_speed_camera); } else if (((AlarmInfo) point).getType().toString().equals("BORDER_CONTROL")) { return uiCtx.getResources().getDrawable(R.drawable.mx_barrier_border_control); } else if (((AlarmInfo) point).getType().toString().equals("RAILWAY")) { if (app.getSettings().DRIVING_REGION.get().americanSigns) { return uiCtx.getResources().getDrawable(R.drawable.list_warnings_railways_us); } else { return uiCtx.getResources().getDrawable(R.drawable.list_warnings_railways); } } else if (((AlarmInfo) point).getType().toString().equals("TRAFFIC_CALMING")) { if (app.getSettings().DRIVING_REGION.get().americanSigns) { return uiCtx.getResources().getDrawable(R.drawable.list_warnings_traffic_calming_us); } else { return uiCtx.getResources().getDrawable(R.drawable.list_warnings_traffic_calming); } } else if (((AlarmInfo) point).getType().toString().equals("TOLL_BOOTH")) { return uiCtx.getResources().getDrawable(R.drawable.mx_toll_booth); } else if (((AlarmInfo) point).getType().toString().equals("STOP")) { return uiCtx.getResources().getDrawable(R.drawable.list_stop); } else if (((AlarmInfo) point).getType().toString().equals("PEDESTRIAN")) { if (app.getSettings().DRIVING_REGION.get().americanSigns) { return uiCtx.getResources().getDrawable(R.drawable.list_warnings_pedestrian_us); } else { return uiCtx.getResources().getDrawable(R.drawable.list_warnings_pedestrian); } } else { return null; } } else { return null; } } @Override public int hashCode() { return ((point == null) ? 0 : point.hashCode()); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } LocationPointWrapper other = (LocationPointWrapper) obj; if (point == null) { if (other.point != null) { return false; } } else if (!point.equals(other.point)) { return false; } return true; } } public int getSearchDeviationRadius(int type) { return type == POI ? poiSearchDeviationRadius : searchDeviationRadius; } public void setSearchDeviationRadius(int type, int radius) { if (type == POI) { this.poiSearchDeviationRadius = radius; } else { this.searchDeviationRadius = radius; } } private class AmenityLocationPoint implements LocationPoint { Amenity a; public AmenityLocationPoint(Amenity a) { this.a = a; } @Override public double getLatitude() { return a.getLocation().getLatitude(); } @Override public double getLongitude() { return a.getLocation().getLongitude(); } @Override public PointDescription getPointDescription(Context ctx) { return new PointDescription(PointDescription.POINT_TYPE_POI, OsmAndFormatter.getPoiStringWithoutType(a, app.getSettings().MAP_PREFERRED_LOCALE.get(), app.getSettings().MAP_TRANSLITERATE_NAMES.get())); } @Override public int getColor() { return 0; } @Override public boolean isVisible() { return true; } } }