package de.blau.android.grid; import android.graphics.Canvas; import android.graphics.Paint; import android.util.Log; import de.blau.android.App; import de.blau.android.Main; import de.blau.android.Map; import de.blau.android.Mode; import de.blau.android.osm.Server; import de.blau.android.resources.DataStyle; import de.blau.android.util.Density; import de.blau.android.util.GeoMath; import de.blau.android.util.ThemeUtils; import de.blau.android.views.IMapView; import de.blau.android.views.overlay.MapViewOverlay; public class MapOverlay extends MapViewOverlay { private static final String DEBUG_TAG = MapOverlay.class.getName(); private static final float DISTANCE2SIDE = 4f; private static final float SHORTTICKS = 12f; public static final float LONGTICKS = 20f; private static final double METERS2FEET = 3.28084; private static final double MILE2FEET = 5280; private static final double YARD2FEET = 3; /** Map this is an overlay of. */ private final Map map; private final Paint fullLine; private final Paint labelH; private final Paint labelV; private final float distance2side; private final float shortTicks; private final float longTicks; private final float oneDP; private final float textHeight; private final Main main; private final int actionBarHeight; public MapOverlay(final Map map, Server s) { this.map = map; fullLine = DataStyle.getCurrent(DataStyle.CROSSHAIRS).getPaint(); labelH = DataStyle.getCurrent(DataStyle.LABELTEXT).getPaint(); labelV = new Paint(labelH); labelV.setTextAlign(Paint.Align.RIGHT); textHeight = labelV.getTextSize(); distance2side = Density.dpToPx(map.getContext(),DISTANCE2SIDE); shortTicks = Density.dpToPx(map.getContext(),SHORTTICKS); longTicks = Density.dpToPx(map.getContext(),LONGTICKS); oneDP = Density.dpToPx(map.getContext(),1); main = map.getContext() instanceof Main ? (Main) map.getContext() : null; actionBarHeight = ThemeUtils.getActionBarHeight(map.getContext()); } @Override public boolean isReadyToDraw() { return !map.getPrefs().areBugsEnabled() || map.getOpenStreetMapTilesOverlay().isReadyToDraw(); } @Override protected void onDraw(Canvas c, IMapView osmv) { String mode = map.getPrefs().scaleLayer(); if (!mode.equals("SCALE_NONE") && map.getViewBox().getWidth() < 200000000L) { // testing for < 20° int w = map.getWidth(); int h = map.getHeight(); boolean metric = mode.equals("SCALE_METRIC") || mode.equals("SCALE_GRID_METRIC"); boolean grid = mode.equals("SCALE_GRID_METRIC") || mode.equals("SCALE_GRID_IMPERIAL"); double centerLat = map.getViewBox().getCenterLat(); double widthInMeters = GeoMath.haversineDistance(map.getViewBox().getLeft()/1E7D, centerLat, map.getViewBox().getRight()/1E7D, centerLat); // Log.d(DEBUG_TAG,"distance to side " + distance2side + " tick length long " + longTicks + " short " + shortTicks); if (widthInMeters < 1000000 && widthInMeters > 0) { // don't show zoomed out float topOffset = 0f; // avoid drawing behind the action bar if (App.getLogic().getMode() == Mode.MODE_ALIGN_BACKGROUND || (main != null && main.easyEditManager.isProcessingAction())) { topOffset = actionBarHeight; Log.d(DEBUG_TAG,"offset " + topOffset); } c.drawLine(distance2side, distance2side + topOffset, w-distance2side, distance2side + topOffset, fullLine); c.drawLine(w-distance2side, distance2side + topOffset, w-distance2side, h-distance2side, fullLine); if (grid) { c.drawLine(distance2side, h-distance2side, w-distance2side, h-distance2side, fullLine); c.drawLine(distance2side, distance2side, distance2side, h-distance2side, fullLine); } if (metric) { double metersPerPixel = widthInMeters/w; double log10 = Math.log10(widthInMeters); double tickDistance = Math.pow(10,Math.floor(log10)-1); // Log.d(DEBUG_TAG,"log10 " + log10 + " tick distance " + Math.pow(10,Math.floor(log10)-1)); if (widthInMeters/tickDistance <= 20) { // heuristic to make the visual effect a bit nicer tickDistance = tickDistance /10; } float tickDistanceH = Math.round(tickDistance/metersPerPixel); boolean km = tickDistance*10 >= 1000; c.drawText(km ? "km" : "m", distance2side, longTicks + topOffset + oneDP, labelH); float nextTick = distance2side; int i = 0; int nextLabel = 0; while (nextTick < (w-distance2side)) { if (i == 10) { i = 0; c.drawLine(nextTick, distance2side + topOffset, nextTick, (grid ? h-distance2side : longTicks) + topOffset, fullLine); nextLabel = (int) (nextLabel + 10*tickDistance); c.drawText(Integer.toString(km ? nextLabel/1000: nextLabel), nextTick + 2*oneDP, longTicks + topOffset + 2*oneDP, labelH); } else { c.drawLine(nextTick, distance2side + topOffset, nextTick, shortTicks + topOffset, fullLine); } i++; nextTick = nextTick + tickDistanceH; } nextTick = distance2side + tickDistanceH + topOffset; // dont't draw first tick i = 1; nextLabel = 0; while (nextTick < (h-distance2side)) { if (i == 10) { i = 0; c.drawLine(w-distance2side, nextTick, grid ? distance2side : w-longTicks, nextTick, fullLine); nextLabel = (int) (nextLabel + 10*tickDistance); c.drawText(Integer.toString(km ? nextLabel/1000: nextLabel), w-(shortTicks+distance2side), nextTick + textHeight + oneDP, labelV); } else { c.drawLine(w-distance2side, nextTick, w-shortTicks, nextTick, fullLine); } i++; nextTick = nextTick + tickDistanceH; } } else { // imperial FIXME we could probably get rid of some duplicate code here double widthInFeet = widthInMeters * METERS2FEET; double feetPerPixel = widthInFeet/w; boolean mile = widthInFeet > MILE2FEET; double tickDistance = 0; int smallTickMax = 10; if (mile) { // between 1 and 12 miles use fractions if (widthInFeet <= 2*MILE2FEET) { smallTickMax = 16; tickDistance = MILE2FEET / smallTickMax; } else if (widthInFeet <= 6*MILE2FEET) { smallTickMax = 8; tickDistance = MILE2FEET / smallTickMax; } else if (widthInFeet <= 10*MILE2FEET) { smallTickMax = 4; tickDistance = MILE2FEET / smallTickMax; } else if (widthInFeet <= 50*MILE2FEET) { smallTickMax = 2; tickDistance = MILE2FEET / smallTickMax; } else { double log10 = Math.log10(widthInFeet/MILE2FEET); tickDistance = MILE2FEET*Math.pow(10,Math.floor(log10)-1); } } else { double log10 = Math.log10(widthInFeet); tickDistance = Math.pow(10,Math.floor(log10)-1); // Log.d(DEBUG_TAG,"log10 " + log10 + " tick distance " + Math.pow(10,Math.floor(log10)-1)); if (widthInFeet/tickDistance <= 30) { // heuristic to make the visual effect a bit nicer tickDistance = tickDistance /10; } } float tickDistanceH = Math.round(tickDistance/feetPerPixel); c.drawText(mile ? "mile" : "ft", distance2side, longTicks + topOffset + oneDP, labelH); float nextTick = distance2side; int i = 0; int nextLabel = 0; while (nextTick < (w-distance2side)) { if (i == smallTickMax) { i = 0; c.drawLine(nextTick, distance2side + topOffset, nextTick, (grid ? h-distance2side : longTicks) + topOffset, fullLine); if (mile) { // Log.d(DEBUG_TAG,"mile tick " + nextTick + " label " + nextLabel); nextLabel = (int) (nextLabel + smallTickMax*tickDistance); c.drawText(Integer.toString((int)(nextLabel/MILE2FEET)), nextTick + 2*oneDP, longTicks + topOffset + 2*oneDP, labelH); } else { nextLabel = (int) (nextLabel + 10*tickDistance); c.drawText(Integer.toString((int)(nextLabel)), nextTick + 2*oneDP, longTicks + topOffset + 2*oneDP, labelH); } } else { c.drawLine(nextTick, distance2side + topOffset , nextTick, shortTicks + topOffset, fullLine); } i++; nextTick = nextTick + tickDistanceH; } nextTick = distance2side + tickDistanceH + topOffset; // dont't draw first tick i = 1; nextLabel = 0; while (nextTick < (h-distance2side)) { if (i == smallTickMax) { i = 0; c.drawLine(w-distance2side, nextTick, grid ? distance2side : w-longTicks, nextTick, fullLine); if (mile) { nextLabel = (int) (nextLabel + smallTickMax*tickDistance); c.drawText(Integer.toString((int)(nextLabel/MILE2FEET)), w-(shortTicks+distance2side), nextTick + textHeight + oneDP, labelV); } else { nextLabel = (int) (nextLabel + 10*tickDistance); c.drawText(Integer.toString((int)nextLabel), w-(shortTicks+distance2side), nextTick + textHeight + oneDP, labelV); } } else { c.drawLine(w-distance2side, nextTick, w-shortTicks, nextTick, fullLine); } i++; nextTick = nextTick + tickDistanceH; } } } } } @Override protected void onDrawFinished(Canvas c, IMapView osmv) { // do nothing } }