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";
+ }
+}