package net.osmand.plus.views; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PointF; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.widget.ArrayAdapter; import net.osmand.Location; import net.osmand.binary.RouteDataObject; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.data.RotatedTileBox; import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidSpecificRoadsCallback; import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.views.ContextMenuLayer.ApplyMovedObjectCallback; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; public class ImpassableRoadsLayer extends OsmandMapLayer implements ContextMenuLayer.IContextMenuProvider, ContextMenuLayer.IMoveObjectProvider { private static final int startZoom = 10; private final MapActivity activity; private Bitmap roadWorkIcon; private OsmandMapTileView view; private Paint paint; private Map<Long, Location> missingRoadLocations; private List<RouteDataObject> missingRoads; private RoutingHelper routingHelper; private ContextMenuLayer contextMenuLayer; private Set<PreservedRoadDataObject> mPreservedRoadDataObjects; public ImpassableRoadsLayer(MapActivity activity) { this.activity = activity; } @Override public void initLayer(OsmandMapTileView view) { this.view = view; roadWorkIcon = BitmapFactory.decodeResource(view.getResources(), R.drawable.map_pin_avoid_road); paint = new Paint(); routingHelper = activity.getRoutingHelper(); contextMenuLayer = view.getLayerByClass(ContextMenuLayer.class); List<LatLon> impassibleRoads = activity.getMyApplication().getSettings().getImpassableRoadPoints(); mPreservedRoadDataObjects = new HashSet<>(impassibleRoads.size()); for (LatLon impassibleRoad : impassibleRoads) { mPreservedRoadDataObjects.add(new PreservedRoadDataObject(impassibleRoad)); } } @Override public void onDraw(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) { if (contextMenuLayer.getMoveableObject() instanceof RouteDataObject) { PointF pf = contextMenuLayer.getMovableCenterPoint(tileBox); drawPoint(canvas, pf.x, pf.y); } } @Override public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) { if (tileBox.getZoom() >= startZoom) { for (long id : getMissingRoadLocations().keySet()) { if(contextMenuLayer.getMoveableObject() instanceof RouteDataObject) { RouteDataObject object = (RouteDataObject) contextMenuLayer.getMoveableObject(); if (object.id == id) { continue; } } Location location = getMissingRoadLocations().get(id); final double latitude = location.getLatitude(); final double longitude = location.getLongitude(); drawPoint(canvas, tileBox, latitude, longitude); } for (PreservedRoadDataObject preservedRoadDataObject : mPreservedRoadDataObjects) { final LatLon latLon = preservedRoadDataObject.getLatLon(); drawPoint(canvas, tileBox, latLon.getLatitude(), latLon.getLongitude()); } } } private void drawPoint(Canvas canvas, RotatedTileBox tileBox, double latitude, double longitude) { float x = tileBox.getPixXFromLatLon(latitude, longitude); float y = tileBox.getPixYFromLatLon(latitude, longitude); drawPoint(canvas, x, y); } private void drawPoint(Canvas canvas, float x, float y) { float left = x - roadWorkIcon.getWidth() / 2; float top = y - roadWorkIcon.getHeight(); canvas.drawBitmap(roadWorkIcon, left, top, paint); } public Map<Long, Location> getMissingRoadLocations() { if (missingRoadLocations == null) { missingRoadLocations = activity.getMyApplication().getDefaultRoutingConfig().getImpassableRoadLocations(); } return missingRoadLocations; } public List<RouteDataObject> getMissingRoads() { if (missingRoads == null) { missingRoads = activity.getMyApplication().getDefaultRoutingConfig().getImpassableRoads(); } return missingRoads; } @Override public void destroyLayer() { } @Override public boolean drawInScreenPixels() { return true; } public int getRadiusPoi(RotatedTileBox tb) { int r; if (tb.getZoom() < startZoom) { r = 0; } else { r = 15; } return (int) (r * tb.getDensity()); } private boolean calculateBelongs(int ex, int ey, int objx, int objy, int radius) { return Math.abs(objx - ex) <= radius && (ey - objy) <= radius / 2 && (objy - ey) <= 3 * radius; } @Override public boolean disableSingleTap() { return false; } @Override public boolean disableLongPressOnMap() { return false; } @Override public boolean isObjectClickable(Object o) { return false; } @Override public void collectObjectsFromPoint(PointF point, RotatedTileBox tileBox, List<Object> o) { if (tileBox.getZoom() >= startZoom) { int ex = (int) point.x; int ey = (int) point.y; int compare = getRadiusPoi(tileBox); int radius = compare * 3 / 2; for (RouteDataObject road : getMissingRoads()) { Location location = getMissingRoadLocations().get(road.getId()); if (location != null) { int x = (int) tileBox.getPixXFromLatLon(location.getLatitude(), location.getLongitude()); int y = (int) tileBox.getPixYFromLatLon(location.getLatitude(), location.getLongitude()); if (calculateBelongs(ex, ey, x, y, compare)) { compare = radius; o.add(road); } } } } if (!mPreservedRoadDataObjects.isEmpty()) { activity.getMyApplication().getAvoidSpecificRoads().initPreservedData(); mPreservedRoadDataObjects.clear(); } } @Override public LatLon getObjectLocation(Object o) { if (o instanceof RouteDataObject) { RouteDataObject route = (RouteDataObject) o; Location location = missingRoadLocations.get(route.getId()); return new LatLon(location.getLatitude(), location.getLongitude()); } return null; } @Override public PointDescription getObjectName(Object o) { if (o instanceof RouteDataObject) { RouteDataObject route = (RouteDataObject) o; return new PointDescription(PointDescription.POINT_TYPE_BLOCKED_ROAD, route.getName()); } return null; } @Override public void populateObjectContextMenu(final LatLon latLon, final Object o, ContextMenuAdapter adapter, MapActivity mapActivity) { if (latLon != null && o == null && (routingHelper.isRoutePlanningMode() || routingHelper.isFollowingMode())) { ContextMenuAdapter.ItemClickListener listener = new ContextMenuAdapter.ItemClickListener() { @Override public boolean onContextMenuClick(ArrayAdapter<ContextMenuItem> adapter, int itemId, int pos, boolean isChecked) { if (itemId == R.string.avoid_road) { activity.getMyApplication().getAvoidSpecificRoads().addImpassableRoad( activity, latLon, false, null, false); } return true; } }; adapter.addItem(new ContextMenuItem.ItemBuilder().setTitleId(R.string.avoid_road, activity) .setIcon(R.drawable.ic_action_road_works_dark) .setListener(listener) .createItem()); } } @Override public boolean isObjectMovable(Object o) { return o instanceof RouteDataObject; } @Override public void applyNewObjectPosition(@NonNull Object o, @NonNull LatLon position, @Nullable final ApplyMovedObjectCallback callback) { if (o instanceof RouteDataObject) { final RouteDataObject object = (RouteDataObject) o; final OsmandApplication application = activity.getMyApplication(); application.getAvoidSpecificRoads().replaceImpassableRoad(activity, object, position, false, new AvoidSpecificRoadsCallback() { @Override public void onAddImpassableRoad(boolean success, RouteDataObject newObject) { if (callback != null) { callback.onApplyMovedObject(success, newObject); } } @Override public boolean isCancelled() { return callback != null && callback.isCancelled(); } }); } } private static class PreservedRoadDataObject { private final LatLon mLatLon; private PreservedRoadDataObject(LatLon latLon) { this.mLatLon = latLon; } public LatLon getLatLon() { return mLatLon; } } }