diff --git a/vtm-android-example/src/org/oscim/android/test/SimpleMapActivity.java b/vtm-android-example/src/org/oscim/android/test/SimpleMapActivity.java index 90dac972..bcd6ebc9 100644 --- a/vtm-android-example/src/org/oscim/android/test/SimpleMapActivity.java +++ b/vtm-android-example/src/org/oscim/android/test/SimpleMapActivity.java @@ -19,7 +19,11 @@ package org.oscim.android.test; import android.os.Bundle; -import org.oscim.android.MapScaleBar; +import org.oscim.android.scalebar.DefaultMapScaleBar; +import org.oscim.android.scalebar.ImperialUnitAdapter; +import org.oscim.android.scalebar.MapScaleBar; +import org.oscim.android.scalebar.MapScaleBarLayer; +import org.oscim.android.scalebar.MetricUnitAdapter; import org.oscim.backend.CanvasAdapter; import org.oscim.core.MapPosition; import org.oscim.core.MercatorProjection; @@ -33,6 +37,7 @@ import org.oscim.theme.ThemeLoader; import org.oscim.theme.VtmThemes; public class SimpleMapActivity extends BaseMapActivity { + private DefaultMapScaleBar mapScaleBar; @Override public void onCreate(Bundle savedInstanceState) { @@ -42,14 +47,28 @@ public class SimpleMapActivity extends BaseMapActivity { layers.add(new BuildingLayer(mMap, mBaseLayer)); layers.add(new LabelLayer(mMap, mBaseLayer)); - MapScaleBar mapScaleBar = new MapScaleBar(mMapView); - ((BitmapRenderer) mapScaleBar.getRenderer()).setPosition(GLViewport.Position.BOTTOM_LEFT); - ((BitmapRenderer) mapScaleBar.getRenderer()).setOffset(5 * CanvasAdapter.dpi / 160, 0); - layers.add(mapScaleBar); + mapScaleBar = new DefaultMapScaleBar(mMap); + mapScaleBar.setScaleBarMode(DefaultMapScaleBar.ScaleBarMode.BOTH); + mapScaleBar.setDistanceUnitAdapter(MetricUnitAdapter.INSTANCE); + mapScaleBar.setSecondaryDistanceUnitAdapter(ImperialUnitAdapter.INSTANCE); + mapScaleBar.setScaleBarPosition(MapScaleBar.ScaleBarPosition.BOTTOM_LEFT); + + MapScaleBarLayer mapScaleBarLayer = new MapScaleBarLayer(mMap, mapScaleBar); + BitmapRenderer renderer = (BitmapRenderer) mapScaleBarLayer.getRenderer(); + renderer.setPosition(GLViewport.Position.BOTTOM_LEFT); + renderer.setOffset(5 * CanvasAdapter.dpi / 160, 0); + layers.add(mapScaleBarLayer); mMap.setTheme(VtmThemes.DEFAULT); } + @Override + protected void onDestroy() { + mapScaleBar.destroy(); + + super.onDestroy(); + } + void runTheMonkey() { themes[0] = ThemeLoader.load(VtmThemes.DEFAULT); themes[1] = ThemeLoader.load(VtmThemes.OSMARENDER); diff --git a/vtm-android/src/org/oscim/android/MapScaleBar.java b/vtm-android/src/org/oscim/android/MapScaleBar.java deleted file mode 100644 index 75e14fca..00000000 --- a/vtm-android/src/org/oscim/android/MapScaleBar.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright 2010, 2011, 2012 mapsforge.org - * Copyright 2013 Hannes Janetzek - * Copyright 2016 devemux86 - * - * This program is free software: you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with - * this program. If not, see . - */ -package org.oscim.android; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Typeface; - -import org.oscim.android.canvas.AndroidBitmap; -import org.oscim.core.MapPosition; -import org.oscim.core.MercatorProjection; -import org.oscim.event.Event; -import org.oscim.layers.Layer; -import org.oscim.map.Map; -import org.oscim.map.Map.UpdateListener; -import org.oscim.renderer.BitmapRenderer; - -import java.util.HashMap; - -/** - * A MapScaleBar displays the ratio of a distance on the map to the - * corresponding distance on the ground. - */ -public class MapScaleBar extends Layer implements UpdateListener { - - private static final int BITMAP_HEIGHT = 64; - private static final int BITMAP_WIDTH = 128; - private static final double LATITUDE_REDRAW_THRESHOLD = 0.2; - // private static final int MARGIN_BOTTOM = 5; - // private static final int MARGIN_LEFT = 5; - - private static final double METER_FOOT_RATIO = 0.3048; - private static final int ONE_KILOMETER = 1000; - private static final int ONE_MILE = 5280; - - private static final Paint SCALE_BAR = new Paint(Paint.ANTI_ALIAS_FLAG); - private static final Paint SCALE_BAR_STROKE = new Paint(Paint.ANTI_ALIAS_FLAG); - private static final Paint SCALE_TEXT = new Paint(Paint.ANTI_ALIAS_FLAG); - private static final Paint SCALE_TEXT_STROKE = new Paint(Paint.ANTI_ALIAS_FLAG); - - private static final int[] SCALE_BAR_VALUES_IMPERIAL = { - 26400000, 10560000, 5280000, - 2640000, 1056000, 528000, - 264000, 105600, 52800, 26400, - 10560, 5280, 2000, 1000, 500, - 200, 100, 50, 20, - 10, 5, 2, 1}; - private static final int[] SCALE_BAR_VALUES_METRIC = { - 10000000, 5000000, 2000000, 1000000, - 500000, 200000, 100000, 50000, - 20000, 10000, 5000, 2000, 1000, - 500, 200, 100, 50, 20, 10, 5, 2, 1}; - - private boolean mImperialUnits; - private final Canvas mMapScaleCanvas; - private boolean mRedrawNeeded; - private double mPrevLatitude = -1; - private final double mPrevScale = -1; - private final HashMap mTextFields; - - private final Bitmap mBitmap; - // passed to BitmapRenderer - need to sync on this object. - private final AndroidBitmap mLayerBitmap; - private final BitmapRenderer mBitmapLayer; - - public MapScaleBar(MapView map) { - super(map.map()); - - mBitmap = Bitmap.createBitmap(BITMAP_WIDTH, - BITMAP_HEIGHT, - Bitmap.Config.ARGB_8888); - - mMapScaleCanvas = new Canvas(mBitmap); - mTextFields = new HashMap(); - - setDefaultTexts(); - configurePaints(); - - mRedrawNeeded = true; - mRenderer = mBitmapLayer = new BitmapRenderer(); - mLayerBitmap = new AndroidBitmap(mBitmap); - mBitmapLayer.setBitmap(mLayerBitmap, BITMAP_WIDTH, BITMAP_HEIGHT); - } - - @Override - public void onMapEvent(Event e, MapPosition mapPosition) { - if (e == Map.UPDATE_EVENT) - return; - - double latitude = MercatorProjection.toLatitude(mapPosition.y); - - if (!mRedrawNeeded) { - double scaleDiff = mPrevScale / mapPosition.scale; - if (scaleDiff < 1.1 && scaleDiff > 0.9) { - double latitudeDiff = Math.abs(mPrevLatitude - latitude); - if (latitudeDiff < LATITUDE_REDRAW_THRESHOLD) - return; - } - } - mPrevLatitude = latitude; - - double groundResolution = MercatorProjection - .groundResolution(latitude, mapPosition.scale); - - int[] scaleBarValues; - if (mImperialUnits) { - groundResolution = groundResolution / METER_FOOT_RATIO; - scaleBarValues = SCALE_BAR_VALUES_IMPERIAL; - } else { - scaleBarValues = SCALE_BAR_VALUES_METRIC; - } - - float scaleBarLength = 0; - int mapScaleValue = 0; - - for (int i = 0; i < scaleBarValues.length; ++i) { - mapScaleValue = scaleBarValues[i]; - scaleBarLength = mapScaleValue / (float) groundResolution; - if (scaleBarLength < (BITMAP_WIDTH - 10)) { - break; - } - } - synchronized (mLayerBitmap) { - redrawMapScaleBitmap(scaleBarLength, mapScaleValue); - } - - mBitmapLayer.updateBitmap(); - - mRedrawNeeded = false; - } - - /** - * @return true if imperial units are used, false otherwise. - */ - public boolean isImperialUnits() { - return mImperialUnits; - } - - /** - * @param imperialUnits true if imperial units should be used rather than metric - * units. - */ - public void setImperialUnits(boolean imperialUnits) { - mImperialUnits = imperialUnits; - mRedrawNeeded = true; - } - - /** - * Overrides the specified text field with the given string. - * - * @param textField the text field to override. - * @param value the new value of the text field. - */ - public void setText(TextField textField, String value) { - mTextFields.put(textField, value); - mRedrawNeeded = true; - } - - private void drawScaleBar(float scaleBarLength, Paint paint) { - mMapScaleCanvas.drawLine(7, 25, scaleBarLength + 3, 25, paint); - mMapScaleCanvas.drawLine(5, 10, 5, 40, paint); - mMapScaleCanvas.drawLine(scaleBarLength + 5, 10, scaleBarLength + 5, 40, paint); - } - - private void drawScaleText(int scaleValue, String unitSymbol, Paint paint) { - mMapScaleCanvas.drawText(scaleValue + unitSymbol, 12, 18, paint); - } - - /** - * Redraws the map scale bitmap with the given parameters. - * - * @param scaleBarLength the length of the map scale bar in pixels. - * @param mapScaleValue the map scale value in meters. - */ - private void redrawMapScaleBitmap(float scaleBarLength, int mapScaleValue) { - mBitmap.eraseColor(Color.TRANSPARENT); - - // draw the scale bar - drawScaleBar(scaleBarLength, SCALE_BAR_STROKE); - drawScaleBar(scaleBarLength, SCALE_BAR); - - int scaleValue; - String unitSymbol; - if (mImperialUnits) { - if (mapScaleValue < ONE_MILE) { - scaleValue = mapScaleValue; - unitSymbol = mTextFields.get(TextField.FOOT); - } else { - scaleValue = mapScaleValue / ONE_MILE; - unitSymbol = mTextFields.get(TextField.MILE); - } - } else { - if (mapScaleValue < ONE_KILOMETER) { - scaleValue = mapScaleValue; - unitSymbol = mTextFields.get(TextField.METER); - } else { - scaleValue = mapScaleValue / ONE_KILOMETER; - unitSymbol = mTextFields.get(TextField.KILOMETER); - } - } - - // draw the scale text - drawScaleText(scaleValue, unitSymbol, SCALE_TEXT_STROKE); - drawScaleText(scaleValue, unitSymbol, SCALE_TEXT); - } - - private void setDefaultTexts() { - mTextFields.put(TextField.FOOT, " ft"); - mTextFields.put(TextField.MILE, " mi"); - - mTextFields.put(TextField.METER, " m"); - mTextFields.put(TextField.KILOMETER, " km"); - } - - private static void configurePaints() { - SCALE_BAR.setStrokeWidth(2); - SCALE_BAR.setStrokeCap(Paint.Cap.SQUARE); - SCALE_BAR.setColor(Color.BLACK); - SCALE_BAR_STROKE.setStrokeWidth(5); - SCALE_BAR_STROKE.setStrokeCap(Paint.Cap.SQUARE); - SCALE_BAR_STROKE.setColor(Color.WHITE); - - SCALE_TEXT.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); - SCALE_TEXT.setTextSize(17); - SCALE_TEXT.setColor(Color.BLACK); - SCALE_TEXT_STROKE.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); - SCALE_TEXT_STROKE.setStyle(Paint.Style.STROKE); - SCALE_TEXT_STROKE.setColor(Color.WHITE); - SCALE_TEXT_STROKE.setStrokeWidth(2); - SCALE_TEXT_STROKE.setTextSize(17); - } - - /** - * Enumeration of all text fields. - */ - public enum TextField { - /** - * Unit symbol for one foot. - */ - FOOT, - - /** - * Unit symbol for one kilometer. - */ - KILOMETER, - - /** - * Unit symbol for one meter. - */ - METER, - - /** - * Unit symbol for one mile. - */ - MILE; - } -} diff --git a/vtm-android/src/org/oscim/android/scalebar/DefaultMapScaleBar.java b/vtm-android/src/org/oscim/android/scalebar/DefaultMapScaleBar.java new file mode 100644 index 00000000..909acc9e --- /dev/null +++ b/vtm-android/src/org/oscim/android/scalebar/DefaultMapScaleBar.java @@ -0,0 +1,350 @@ +/* + * Copyright 2010, 2011, 2012, 2013 mapsforge.org + * Copyright 2014 Ludwig M Brinckmann + * Copyright 2014-2016 devemux86 + * Copyright 2014 Erik Duisters + * Copyright 2014 Christian Pesch + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.android.scalebar; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.Typeface; + +import org.oscim.backend.CanvasAdapter; +import org.oscim.map.Map; + +/** + * Displays the default MapScaleBar + */ +public class DefaultMapScaleBar extends MapScaleBar { + private static final int BITMAP_HEIGHT = 40; + private static final int BITMAP_WIDTH = 120; + private static final int SCALE_BAR_MARGIN = 10; + private static final float STROKE_EXTERNAL = 4; + private static final float STROKE_INTERNAL = 2; + private static final int TEXT_MARGIN = 1; + + public static enum ScaleBarMode {BOTH, SINGLE} + + private final float scale; + private ScaleBarMode scaleBarMode; + private DistanceUnitAdapter secondaryDistanceUnitAdapter; + + private final Paint paintScaleBar; + private final Paint paintScaleBarStroke; + private final Paint paintScaleText; + private final Paint paintScaleTextStroke; + + private final Rect rect = new Rect(); + + public DefaultMapScaleBar(Map map) { + super(map, (int) (BITMAP_WIDTH * CanvasAdapter.dpi / 160), (int) (BITMAP_HEIGHT * CanvasAdapter.dpi / 160)); + + this.scale = CanvasAdapter.dpi / 160; + this.scaleBarMode = ScaleBarMode.BOTH; + this.secondaryDistanceUnitAdapter = ImperialUnitAdapter.INSTANCE; + + this.paintScaleBar = createScaleBarPaint(Color.BLACK, STROKE_INTERNAL, Paint.Style.FILL); + this.paintScaleBarStroke = createScaleBarPaint(Color.WHITE, STROKE_EXTERNAL, Paint.Style.STROKE); + this.paintScaleText = createTextPaint(Color.BLACK, 0, Paint.Style.FILL); + this.paintScaleTextStroke = createTextPaint(Color.WHITE, 2, Paint.Style.STROKE); + } + + /** + * @return the secondary {@link DistanceUnitAdapter} in use by this MapScaleBar + */ + public DistanceUnitAdapter getSecondaryDistanceUnitAdapter() { + return this.secondaryDistanceUnitAdapter; + } + + /** + * Set the secondary {@link DistanceUnitAdapter} for the MapScaleBar + * + * @param distanceUnitAdapter The secondary {@link DistanceUnitAdapter} to be used by this {@link MapScaleBar} + */ + public void setSecondaryDistanceUnitAdapter(DistanceUnitAdapter distanceUnitAdapter) { + if (distanceUnitAdapter == null) { + throw new IllegalArgumentException("adapter must not be null"); + } + this.secondaryDistanceUnitAdapter = distanceUnitAdapter; + this.redrawNeeded = true; + } + + public ScaleBarMode getScaleBarMode() { + return this.scaleBarMode; + } + + public void setScaleBarMode(ScaleBarMode scaleBarMode) { + this.scaleBarMode = scaleBarMode; + this.redrawNeeded = true; + } + + private Paint createScaleBarPaint(int color, float strokeWidth, Paint.Style style) { + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setStrokeCap(Paint.Cap.ROUND); + paint.setStrokeJoin(Paint.Join.ROUND); + paint.setStyle(Paint.Style.FILL); + + paint.setColor(color); + paint.setStrokeWidth(strokeWidth * this.scale); + paint.setStyle(style); + paint.setStrokeCap(Paint.Cap.SQUARE); + return paint; + } + + private Paint createTextPaint(int color, float strokeWidth, Paint.Style style) { + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setStrokeCap(Paint.Cap.ROUND); + paint.setStrokeJoin(Paint.Join.ROUND); + paint.setStyle(Paint.Style.FILL); + + paint.setColor(color); + paint.setStrokeWidth(strokeWidth * this.scale); + paint.setStyle(style); + paint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD)); + paint.setTextSize(12 * this.scale); + + return paint; + } + + @Override + protected void redraw(Canvas canvas) { + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); + + ScaleBarLengthAndValue lengthAndValue = this.calculateScaleBarLengthAndValue(); + ScaleBarLengthAndValue lengthAndValue2; + + if (this.scaleBarMode == ScaleBarMode.BOTH) { + lengthAndValue2 = this.calculateScaleBarLengthAndValue(this.secondaryDistanceUnitAdapter); + } else { + lengthAndValue2 = new ScaleBarLengthAndValue(0, 0); + } + + drawScaleBar(canvas, lengthAndValue.scaleBarLength, lengthAndValue2.scaleBarLength, this.paintScaleBarStroke, scale); + drawScaleBar(canvas, lengthAndValue.scaleBarLength, lengthAndValue2.scaleBarLength, this.paintScaleBar, scale); + + String scaleText1 = this.distanceUnitAdapter.getScaleText(lengthAndValue.scaleBarValue); + String scaleText2 = this.scaleBarMode == ScaleBarMode.BOTH ? this.secondaryDistanceUnitAdapter.getScaleText(lengthAndValue2.scaleBarValue) : ""; + + drawScaleText(canvas, scaleText1, scaleText2, this.paintScaleTextStroke, scale); + drawScaleText(canvas, scaleText1, scaleText2, this.paintScaleText, scale); + } + + private void drawScaleBar(Canvas canvas, int scaleBarLength1, int scaleBarLength2, Paint paint, float scale) { + int maxScaleBarLength = Math.max(scaleBarLength1, scaleBarLength2); + + switch (scaleBarPosition) { + case BOTTOM_CENTER: + if (scaleBarLength2 == 0) { + canvas.drawLine(Math.round((canvas.getWidth() - maxScaleBarLength) * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), + Math.round((canvas.getWidth() + maxScaleBarLength) * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round((canvas.getWidth() - maxScaleBarLength) * 0.5f), Math.round(canvas.getHeight() * 0.5f), + Math.round((canvas.getWidth() - maxScaleBarLength) * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round((canvas.getWidth() + maxScaleBarLength) * 0.5f), Math.round(canvas.getHeight() * 0.5f), + Math.round((canvas.getWidth() + maxScaleBarLength) * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + } else { + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() * 0.5f), + Math.round(STROKE_EXTERNAL * scale * 0.5f + maxScaleBarLength), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength1), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength1), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength2), Math.round(canvas.getHeight() * 0.5f), + Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength2), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + } + break; + case BOTTOM_LEFT: + if (scaleBarLength2 == 0) { + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), + Math.round(STROKE_EXTERNAL * scale * 0.5f + maxScaleBarLength), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() * 0.5f), + Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f + maxScaleBarLength), Math.round(canvas.getHeight() * 0.5f), + Math.round(STROKE_EXTERNAL * scale * 0.5f + maxScaleBarLength), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + } else { + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() * 0.5f), + Math.round(STROKE_EXTERNAL * scale * 0.5f + maxScaleBarLength), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength1), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength1), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength2), Math.round(canvas.getHeight() * 0.5f), + Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength2), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + } + break; + case BOTTOM_RIGHT: + if (scaleBarLength2 == 0) { + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - maxScaleBarLength), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() * 0.5f), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - maxScaleBarLength), Math.round(canvas.getHeight() * 0.5f), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - maxScaleBarLength), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + } else { + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() * 0.5f), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - maxScaleBarLength), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - scaleBarLength1), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - scaleBarLength1), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - scaleBarLength2), Math.round(canvas.getHeight() * 0.5f), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - scaleBarLength2), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + } + break; + case TOP_CENTER: + if (scaleBarLength2 == 0) { + canvas.drawLine(Math.round((canvas.getWidth() - maxScaleBarLength) * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), + Math.round((canvas.getWidth() + maxScaleBarLength) * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round((canvas.getWidth() - maxScaleBarLength) * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), + Math.round((canvas.getWidth() - maxScaleBarLength) * 0.5f), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round((canvas.getWidth() + maxScaleBarLength) * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), + Math.round((canvas.getWidth() + maxScaleBarLength) * 0.5f), Math.round(canvas.getHeight() * 0.5f), paint); + } else { + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() * 0.5f), + Math.round(STROKE_EXTERNAL * scale * 0.5f + maxScaleBarLength), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength1), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength1), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength2), Math.round(canvas.getHeight() * 0.5f), + Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength2), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + } + break; + case TOP_LEFT: + if (scaleBarLength2 == 0) { + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(STROKE_EXTERNAL * scale * 0.5f + maxScaleBarLength), Math.round(SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f + maxScaleBarLength), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(STROKE_EXTERNAL * scale * 0.5f + maxScaleBarLength), Math.round(canvas.getHeight() * 0.5f), paint); + } else { + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() * 0.5f), + Math.round(STROKE_EXTERNAL * scale * 0.5f + maxScaleBarLength), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength1), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength1), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength2), Math.round(canvas.getHeight() * 0.5f), + Math.round(STROKE_EXTERNAL * scale * 0.5f + scaleBarLength2), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + } + break; + case TOP_RIGHT: + if (scaleBarLength2 == 0) { + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - maxScaleBarLength), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - maxScaleBarLength), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - maxScaleBarLength), Math.round(canvas.getHeight() * 0.5f), paint); + } else { + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() * 0.5f), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - maxScaleBarLength), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - scaleBarLength1), Math.round(SCALE_BAR_MARGIN * scale), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - scaleBarLength1), Math.round(canvas.getHeight() * 0.5f), paint); + canvas.drawLine(Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - scaleBarLength2), Math.round(canvas.getHeight() * 0.5f), + Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale * 0.5f - scaleBarLength2), Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale), paint); + } + break; + } + } + + private void drawScaleText(Canvas canvas, String scaleText1, String scaleText2, Paint paint, float scale) { + switch (scaleBarPosition) { + case BOTTOM_CENTER: + if (scaleText2.length() == 0) { + canvas.drawText(scaleText1, Math.round((canvas.getWidth() - getTextWidth(this.paintScaleTextStroke, scaleText1)) * 0.5f), + Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale - STROKE_EXTERNAL * scale * 0.5f - TEXT_MARGIN * scale), paint); + } else { + canvas.drawText(scaleText1, Math.round(STROKE_EXTERNAL * scale + TEXT_MARGIN * scale), + Math.round(canvas.getHeight() * 0.5f - STROKE_EXTERNAL * scale * 0.5f - TEXT_MARGIN * scale), paint); + canvas.drawText(scaleText2, Math.round(STROKE_EXTERNAL * scale + TEXT_MARGIN * scale), + Math.round(canvas.getHeight() * 0.5f + STROKE_EXTERNAL * scale * 0.5f + TEXT_MARGIN * scale + getTextHeight(this.paintScaleTextStroke, scaleText2)), paint); + } + break; + case BOTTOM_LEFT: + if (scaleText2.length() == 0) { + canvas.drawText(scaleText1, Math.round(STROKE_EXTERNAL * scale + TEXT_MARGIN * scale), + Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale - STROKE_EXTERNAL * scale * 0.5f - TEXT_MARGIN * scale), paint); + } else { + canvas.drawText(scaleText1, Math.round(STROKE_EXTERNAL * scale + TEXT_MARGIN * scale), + Math.round(canvas.getHeight() * 0.5f - STROKE_EXTERNAL * scale * 0.5f - TEXT_MARGIN * scale), paint); + canvas.drawText(scaleText2, Math.round(STROKE_EXTERNAL * scale + TEXT_MARGIN * scale), + Math.round(canvas.getHeight() * 0.5f + STROKE_EXTERNAL * scale * 0.5f + TEXT_MARGIN * scale + getTextHeight(this.paintScaleTextStroke, scaleText2)), paint); + } + break; + case BOTTOM_RIGHT: + if (scaleText2.length() == 0) { + canvas.drawText(scaleText1, Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale - TEXT_MARGIN * scale - getTextWidth(this.paintScaleTextStroke, scaleText1)), + Math.round(canvas.getHeight() - SCALE_BAR_MARGIN * scale - STROKE_EXTERNAL * scale * 0.5f - TEXT_MARGIN * scale), paint); + } else { + canvas.drawText(scaleText1, Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale - TEXT_MARGIN * scale - getTextWidth(this.paintScaleTextStroke, scaleText1)), + Math.round(canvas.getHeight() * 0.5f - STROKE_EXTERNAL * scale * 0.5f - TEXT_MARGIN * scale), paint); + canvas.drawText(scaleText2, Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale - TEXT_MARGIN * scale - getTextWidth(this.paintScaleTextStroke, scaleText2)), + Math.round(canvas.getHeight() * 0.5f + STROKE_EXTERNAL * scale * 0.5f + TEXT_MARGIN * scale + getTextHeight(this.paintScaleTextStroke, scaleText2)), paint); + } + break; + case TOP_CENTER: + if (scaleText2.length() == 0) { + canvas.drawText(scaleText1, Math.round((canvas.getWidth() - getTextWidth(this.paintScaleTextStroke, scaleText1)) * 0.5f), + Math.round(SCALE_BAR_MARGIN * scale + STROKE_EXTERNAL * scale * 0.5f + TEXT_MARGIN * scale + getTextHeight(this.paintScaleTextStroke, scaleText1)), paint); + } else { + canvas.drawText(scaleText1, Math.round(STROKE_EXTERNAL * scale + TEXT_MARGIN * scale), + Math.round(canvas.getHeight() * 0.5f - STROKE_EXTERNAL * scale * 0.5f - TEXT_MARGIN * scale), paint); + canvas.drawText(scaleText2, Math.round(STROKE_EXTERNAL * scale + TEXT_MARGIN * scale), + Math.round(canvas.getHeight() * 0.5f + STROKE_EXTERNAL * scale * 0.5f + TEXT_MARGIN * scale + getTextHeight(this.paintScaleTextStroke, scaleText2)), paint); + } + break; + case TOP_LEFT: + if (scaleText2.length() == 0) { + canvas.drawText(scaleText1, Math.round(STROKE_EXTERNAL * scale + TEXT_MARGIN * scale), + Math.round(SCALE_BAR_MARGIN * scale + STROKE_EXTERNAL * scale * 0.5f + TEXT_MARGIN * scale + getTextHeight(this.paintScaleTextStroke, scaleText1)), paint); + } else { + canvas.drawText(scaleText1, Math.round(STROKE_EXTERNAL * scale + TEXT_MARGIN * scale), + Math.round(canvas.getHeight() * 0.5f - STROKE_EXTERNAL * scale * 0.5f - TEXT_MARGIN * scale), paint); + canvas.drawText(scaleText2, Math.round(STROKE_EXTERNAL * scale + TEXT_MARGIN * scale), + Math.round(canvas.getHeight() * 0.5f + STROKE_EXTERNAL * scale * 0.5f + TEXT_MARGIN * scale + getTextHeight(this.paintScaleTextStroke, scaleText2)), paint); + } + break; + case TOP_RIGHT: + if (scaleText2.length() == 0) { + canvas.drawText(scaleText1, Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale - TEXT_MARGIN * scale - getTextWidth(this.paintScaleTextStroke, scaleText1)), + Math.round(SCALE_BAR_MARGIN * scale + STROKE_EXTERNAL * scale * 0.5f + TEXT_MARGIN * scale + getTextHeight(this.paintScaleTextStroke, scaleText1)), paint); + } else { + canvas.drawText(scaleText1, Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale - TEXT_MARGIN * scale - getTextWidth(this.paintScaleTextStroke, scaleText1)), + Math.round(canvas.getHeight() * 0.5f - STROKE_EXTERNAL * scale * 0.5f - TEXT_MARGIN * scale), paint); + canvas.drawText(scaleText2, Math.round(canvas.getWidth() - STROKE_EXTERNAL * scale - TEXT_MARGIN * scale - getTextWidth(this.paintScaleTextStroke, scaleText2)), + Math.round(canvas.getHeight() * 0.5f + STROKE_EXTERNAL * scale * 0.5f + TEXT_MARGIN * scale + getTextHeight(this.paintScaleTextStroke, scaleText2)), paint); + } + break; + } + } + + private int getTextHeight(Paint paint, String text) { + paint.getTextBounds(text, 0, text.length(), rect); + return rect.height(); + } + + private int getTextWidth(Paint paint, String text) { + return (int) paint.measureText(text); + } +} diff --git a/vtm-android/src/org/oscim/android/scalebar/DistanceUnitAdapter.java b/vtm-android/src/org/oscim/android/scalebar/DistanceUnitAdapter.java new file mode 100644 index 00000000..2b5a8bae --- /dev/null +++ b/vtm-android/src/org/oscim/android/scalebar/DistanceUnitAdapter.java @@ -0,0 +1,24 @@ +/* + * Copyright 2010, 2011, 2012, 2013 mapsforge.org + * Copyright 2016 devemux86 + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.android.scalebar; + +public interface DistanceUnitAdapter { + double getMeterRatio(); + + int[] getScaleBarValues(); + + String getScaleText(int mapScaleValue); +} diff --git a/vtm-android/src/org/oscim/android/scalebar/ImperialUnitAdapter.java b/vtm-android/src/org/oscim/android/scalebar/ImperialUnitAdapter.java new file mode 100644 index 00000000..287e7511 --- /dev/null +++ b/vtm-android/src/org/oscim/android/scalebar/ImperialUnitAdapter.java @@ -0,0 +1,46 @@ +/* + * Copyright 2010, 2011, 2012, 2013 mapsforge.org + * Copyright 2016 devemux86 + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.android.scalebar; + +public final class ImperialUnitAdapter implements DistanceUnitAdapter { + public static final ImperialUnitAdapter INSTANCE = new ImperialUnitAdapter(); + private static final double METER_FOOT_RATIO = 0.3048; + private static final int ONE_MILE = 5280; + private static final int[] SCALE_BAR_VALUES = {26400000, 10560000, 5280000, 2640000, 1056000, 528000, 264000, + 105600, 52800, 26400, 10560, 5280, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1}; + + private ImperialUnitAdapter() { + // do nothing + } + + @Override + public double getMeterRatio() { + return METER_FOOT_RATIO; + } + + @Override + public int[] getScaleBarValues() { + return SCALE_BAR_VALUES; + } + + @Override + public String getScaleText(int mapScaleValue) { + if (mapScaleValue < ONE_MILE) { + return mapScaleValue + " ft"; + } + return (mapScaleValue / ONE_MILE) + " mi"; + } +} diff --git a/vtm-android/src/org/oscim/android/scalebar/MapScaleBar.java b/vtm-android/src/org/oscim/android/scalebar/MapScaleBar.java new file mode 100644 index 00000000..23e108b6 --- /dev/null +++ b/vtm-android/src/org/oscim/android/scalebar/MapScaleBar.java @@ -0,0 +1,291 @@ +/* + * Copyright 2010, 2011, 2012, 2013 mapsforge.org + * Copyright 2014 Ludwig M Brinckmann + * Copyright 2014-2016 devemux86 + * Copyright 2014 Erik Duisters + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.android.scalebar; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; + +import org.oscim.core.MapPosition; +import org.oscim.core.MercatorProjection; +import org.oscim.map.Map; + +/** + * A MapScaleBar displays the ratio of a distance on the map to the corresponding distance on the ground. + */ +public abstract class MapScaleBar { + public static enum ScaleBarPosition {BOTTOM_CENTER, BOTTOM_LEFT, BOTTOM_RIGHT, TOP_CENTER, TOP_LEFT, TOP_RIGHT} + + /** + * Default position of the scale bar. + */ + private static final ScaleBarPosition DEFAULT_SCALE_BAR_POSITION = ScaleBarPosition.BOTTOM_LEFT; + + private static final int DEFAULT_HORIZONTAL_MARGIN = 5; + private static final int DEFAULT_VERTICAL_MARGIN = 0; + private static final double LATITUDE_REDRAW_THRESHOLD = 0.2; + + protected final Paint bitmapPaint = new Paint(); + private final MapPosition currentMapPosition = new MapPosition(); + protected DistanceUnitAdapter distanceUnitAdapter; + protected final Map map; + protected Bitmap mapScaleBitmap; + protected Canvas mapScaleCanvas; + private int marginHorizontal; + private int marginVertical; + private MapPosition prevMapPosition; + protected boolean redrawNeeded; + protected ScaleBarPosition scaleBarPosition; + private boolean visible; + + /** + * Internal class used by calculateScaleBarLengthAndValue + */ + protected static class ScaleBarLengthAndValue { + public int scaleBarLength; + public int scaleBarValue; + + public ScaleBarLengthAndValue(int scaleBarLength, int scaleBarValue) { + this.scaleBarLength = scaleBarLength; + this.scaleBarValue = scaleBarValue; + } + } + + public MapScaleBar(Map map, int width, int height) { + this.map = map; + this.mapScaleBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + + this.bitmapPaint.setAntiAlias(true); + this.bitmapPaint.setFilterBitmap(true); + + this.marginHorizontal = DEFAULT_HORIZONTAL_MARGIN; + this.marginVertical = DEFAULT_VERTICAL_MARGIN; + this.scaleBarPosition = DEFAULT_SCALE_BAR_POSITION; + + this.mapScaleCanvas = new Canvas(); + this.mapScaleCanvas.setBitmap(this.mapScaleBitmap); + this.distanceUnitAdapter = MetricUnitAdapter.INSTANCE; + this.visible = true; + this.redrawNeeded = true; + } + + /** + * Free all resources + */ + public void destroy() { + this.mapScaleBitmap.recycle(); + this.mapScaleBitmap = null; + this.mapScaleCanvas = null; + } + + /** + * @return true if this {@link MapScaleBar} is visible + */ + public boolean isVisible() { + return this.visible; + } + + /** + * Set the visibility of this {@link MapScaleBar} + * + * @param visible true if the MapScaleBar should be visible, false otherwise + */ + public void setVisible(boolean visible) { + this.visible = visible; + } + + /** + * @return the {@link DistanceUnitAdapter} in use by this MapScaleBar + */ + public DistanceUnitAdapter getDistanceUnitAdapter() { + return this.distanceUnitAdapter; + } + + /** + * Set the {@link DistanceUnitAdapter} for the MapScaleBar + * + * @param distanceUnitAdapter The {@link DistanceUnitAdapter} to be used by this {@link MapScaleBar} + */ + public void setDistanceUnitAdapter(DistanceUnitAdapter distanceUnitAdapter) { + if (distanceUnitAdapter == null) { + throw new IllegalArgumentException("adapter must not be null"); + } + this.distanceUnitAdapter = distanceUnitAdapter; + this.redrawNeeded = true; + } + + public int getMarginHorizontal() { + return marginHorizontal; + } + + public void setMarginHorizontal(int marginHorizontal) { + if (this.marginHorizontal != marginHorizontal) { + this.marginHorizontal = marginHorizontal; + this.redrawNeeded = true; + } + } + + public int getMarginVertical() { + return marginVertical; + } + + public void setMarginVertical(int marginVertical) { + if (this.marginVertical != marginVertical) { + this.marginVertical = marginVertical; + this.redrawNeeded = true; + } + } + + public ScaleBarPosition getScaleBarPosition() { + return scaleBarPosition; + } + + public void setScaleBarPosition(ScaleBarPosition scaleBarPosition) { + if (this.scaleBarPosition != scaleBarPosition) { + this.scaleBarPosition = scaleBarPosition; + this.redrawNeeded = true; + } + } + + private int calculatePositionLeft(int left, int right, int width) { + switch (scaleBarPosition) { + case BOTTOM_LEFT: + case TOP_LEFT: + return marginHorizontal; + + case BOTTOM_CENTER: + case TOP_CENTER: + return (right - left - width) / 2; + + case BOTTOM_RIGHT: + case TOP_RIGHT: + return right - left - width - marginHorizontal; + } + + throw new IllegalArgumentException("unknown horizontal position: " + scaleBarPosition); + } + + private int calculatePositionTop(int top, int bottom, int height) { + switch (scaleBarPosition) { + case TOP_CENTER: + case TOP_LEFT: + case TOP_RIGHT: + return marginVertical; + + case BOTTOM_CENTER: + case BOTTOM_LEFT: + case BOTTOM_RIGHT: + return bottom - top - height - marginVertical; + } + + throw new IllegalArgumentException("unknown vertical position: " + scaleBarPosition); + } + + /** + * Calculates the required length and value of the scalebar + * + * @param unitAdapter the DistanceUnitAdapter to calculate for + * @return a {@link ScaleBarLengthAndValue} object containing the required scaleBarLength and scaleBarValue + */ + protected ScaleBarLengthAndValue calculateScaleBarLengthAndValue(DistanceUnitAdapter unitAdapter) { + this.prevMapPosition = this.map.getMapPosition(); + double groundResolution = MercatorProjection.groundResolution(this.prevMapPosition); + + groundResolution = groundResolution / unitAdapter.getMeterRatio(); + int[] scaleBarValues = unitAdapter.getScaleBarValues(); + + int scaleBarLength = 0; + int mapScaleValue = 0; + + for (int i = 0; i < scaleBarValues.length; ++i) { + mapScaleValue = scaleBarValues[i]; + scaleBarLength = (int) (mapScaleValue / groundResolution); + if (scaleBarLength < (this.mapScaleBitmap.getWidth() - 10)) { + break; + } + } + + return new ScaleBarLengthAndValue(scaleBarLength, mapScaleValue); + } + + /** + * Calculates the required length and value of the scalebar using the current {@link DistanceUnitAdapter} + * + * @return a {@link ScaleBarLengthAndValue} object containing the required scaleBarLength and scaleBarValue + */ + protected ScaleBarLengthAndValue calculateScaleBarLengthAndValue() { + return calculateScaleBarLengthAndValue(this.distanceUnitAdapter); + } + + /** + * @param canvas The canvas to use to draw the MapScaleBar + */ + public void draw(Canvas canvas) { + if (!this.visible) { + return; + } + + if (this.map.getHeight() == 0) { + return; + } + + if (this.isRedrawNecessary()) { + redraw(this.mapScaleCanvas); + this.redrawNeeded = false; + } + + int positionLeft = calculatePositionLeft(0, this.map.getWidth(), this.mapScaleBitmap.getWidth()); + int positionTop = calculatePositionTop(0, this.map.getHeight(), this.mapScaleBitmap.getHeight()); + + canvas.drawBitmap(this.mapScaleBitmap, positionLeft, positionTop, this.bitmapPaint); + } + + /** + * The scalebar will be redrawn on the next draw() + */ + public void redrawScaleBar() { + this.redrawNeeded = true; + } + + /** + * Determines if a redraw is necessary or not + * + * @return true if redraw is necessary, false otherwise + */ + protected boolean isRedrawNecessary() { + if (this.redrawNeeded || this.prevMapPosition == null) { + return true; + } + + this.map.getMapPosition(this.currentMapPosition); + if (this.currentMapPosition.getScale() != this.prevMapPosition.getScale()) { + return true; + } + + double latitudeDiff = Math.abs(this.currentMapPosition.getLatitude() - this.prevMapPosition.getLatitude()); + return latitudeDiff > LATITUDE_REDRAW_THRESHOLD; + } + + /** + * Redraw the map scale bar. + * Make sure you always apply scale factor to all coordinates and dimensions. + * + * @param canvas The canvas to draw on + */ + protected abstract void redraw(Canvas canvas); +} diff --git a/vtm-android/src/org/oscim/android/scalebar/MapScaleBarLayer.java b/vtm-android/src/org/oscim/android/scalebar/MapScaleBarLayer.java new file mode 100644 index 00000000..84e1be16 --- /dev/null +++ b/vtm-android/src/org/oscim/android/scalebar/MapScaleBarLayer.java @@ -0,0 +1,61 @@ +/* + * Copyright 2016 devemux86 + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.android.scalebar; + +import org.oscim.android.canvas.AndroidBitmap; +import org.oscim.core.MapPosition; +import org.oscim.event.Event; +import org.oscim.layers.Layer; +import org.oscim.map.Map; +import org.oscim.renderer.BitmapRenderer; + +public class MapScaleBarLayer extends Layer implements Map.UpdateListener { + private final MapScaleBar mapScaleBar; + private final BitmapRenderer bitmapRenderer; + // Passed to BitmapRenderer - need to sync on this object + private final AndroidBitmap layerBitmap; + + public MapScaleBarLayer(Map map, MapScaleBar mapScaleBar) { + super(map); + this.mapScaleBar = mapScaleBar; + + mRenderer = bitmapRenderer = new BitmapRenderer(); + layerBitmap = new AndroidBitmap(mapScaleBar.mapScaleBitmap); + bitmapRenderer.setBitmap(layerBitmap, mapScaleBar.mapScaleBitmap.getWidth(), mapScaleBar.mapScaleBitmap.getHeight()); + } + + @Override + public void onMapEvent(Event e, MapPosition mapPosition) { + if (e == Map.UPDATE_EVENT) + return; + + if (!mapScaleBar.isVisible()) + return; + + if (mMap.getHeight() == 0) + return; + + if (!mapScaleBar.isRedrawNecessary()) + return; + + synchronized (layerBitmap) { + mapScaleBar.redraw(mapScaleBar.mapScaleCanvas); + } + + bitmapRenderer.updateBitmap(); + + mapScaleBar.redrawNeeded = false; + } +} diff --git a/vtm-android/src/org/oscim/android/scalebar/MetricUnitAdapter.java b/vtm-android/src/org/oscim/android/scalebar/MetricUnitAdapter.java new file mode 100644 index 00000000..1fddf3dc --- /dev/null +++ b/vtm-android/src/org/oscim/android/scalebar/MetricUnitAdapter.java @@ -0,0 +1,45 @@ +/* + * Copyright 2010, 2011, 2012, 2013 mapsforge.org + * Copyright 2016 devemux86 + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.android.scalebar; + +public final class MetricUnitAdapter implements DistanceUnitAdapter { + public static final MetricUnitAdapter INSTANCE = new MetricUnitAdapter(); + private static final int ONE_KILOMETER = 1000; + private static final int[] SCALE_BAR_VALUES = {10000000, 5000000, 2000000, 1000000, 500000, 200000, 100000, 50000, + 20000, 10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1}; + + private MetricUnitAdapter() { + // do nothing + } + + @Override + public double getMeterRatio() { + return 1; + } + + @Override + public int[] getScaleBarValues() { + return SCALE_BAR_VALUES; + } + + @Override + public String getScaleText(int mapScaleValue) { + if (mapScaleValue < ONE_KILOMETER) { + return mapScaleValue + " m"; + } + return (mapScaleValue / ONE_KILOMETER) + " km"; + } +} diff --git a/vtm-android/src/org/oscim/android/scalebar/NauticalUnitAdapter.java b/vtm-android/src/org/oscim/android/scalebar/NauticalUnitAdapter.java new file mode 100644 index 00000000..1ab92557 --- /dev/null +++ b/vtm-android/src/org/oscim/android/scalebar/NauticalUnitAdapter.java @@ -0,0 +1,48 @@ +/* + * Copyright 2014 Christian Pesch + * Copyright 2014-2016 devemux86 + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.android.scalebar; + +public final class NauticalUnitAdapter implements DistanceUnitAdapter { + public static final NauticalUnitAdapter INSTANCE = new NauticalUnitAdapter(); + private static final int ONE_MILE = 1852; + private static final int[] SCALE_BAR_VALUES = {9260000, 3704000, 1852000, 926000, 370400, 185200, 92600, + 37040, 18520, 9260, 3704, 1852, 926, 500, 200, 100, 50, 20, 10, 5, 2, 1}; + + private NauticalUnitAdapter() { + // do nothing + } + + @Override + public double getMeterRatio() { + return 1; + } + + @Override + public int[] getScaleBarValues() { + return SCALE_BAR_VALUES; + } + + @Override + public String getScaleText(int mapScaleValue) { + if (mapScaleValue < ONE_MILE / 2) { + return mapScaleValue + " m"; + } + if (mapScaleValue == ONE_MILE / 2) { + return "0.5 nmi"; + } + return (mapScaleValue / ONE_MILE) + " nmi"; + } +}