diff --git a/docs/Changelog.md b/docs/Changelog.md
index b4e0264db..d00410267 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -11,7 +11,8 @@
- Native libraries for all platforms [#14](https://github.com/mapsforge/vtm/issues/14)
- Line stipple and texture rendering [#105](https://github.com/mapsforge/vtm/issues/105)
- Layer groups [#99](https://github.com/mapsforge/vtm/issues/99) [#103](https://github.com/mapsforge/vtm/issues/103)
-- Map scale bar multi-platform [#84](https://github.com/mapsforge/vtm/issues/84)
+- Location renderer [#171](https://github.com/mapsforge/vtm/issues/171)
+- Map scale bar [#84](https://github.com/mapsforge/vtm/issues/84)
- libGDX layer gestures [#151](https://github.com/mapsforge/vtm/issues/151)
- LWJGL desktop libGDX backend [#129](https://github.com/mapsforge/vtm/issues/129)
- Render theme area tessellation option [#37](https://github.com/mapsforge/vtm/issues/37)
diff --git a/vtm-app/src/org/oscim/app/location/Compass.java b/vtm-app/src/org/oscim/app/location/Compass.java
index cdfbd8df9..4d5dcdd1e 100644
--- a/vtm-app/src/org/oscim/app/location/Compass.java
+++ b/vtm-app/src/org/oscim/app/location/Compass.java
@@ -1,6 +1,7 @@
/*
* Copyright 2013 Ahmad Saleem
* 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
@@ -13,7 +14,6 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see .
*/
-
package org.oscim.app.location;
import android.content.Context;
@@ -31,10 +31,11 @@
import org.oscim.event.Event;
import org.oscim.layers.Layer;
import org.oscim.map.Map;
+import org.oscim.renderer.LocationRenderer;
@SuppressWarnings("deprecation")
-public class Compass extends Layer implements SensorEventListener,
- Map.UpdateListener {
+public class Compass extends Layer implements SensorEventListener, Map.UpdateListener,
+ LocationRenderer.Callback {
// final static Logger log = LoggerFactory.getLogger(Compass.class);
@@ -84,6 +85,7 @@ public Compass(Context context, Map map) {
setEnabled(false);
}
+ @Override
public synchronized float getRotation() {
return mCurRotation;
}
diff --git a/vtm-app/src/org/oscim/app/location/LocationOverlay.java b/vtm-app/src/org/oscim/app/location/LocationOverlay.java
index c3a4dd2d1..cf63f662a 100644
--- a/vtm-app/src/org/oscim/app/location/LocationOverlay.java
+++ b/vtm-app/src/org/oscim/app/location/LocationOverlay.java
@@ -16,44 +16,29 @@
*/
package org.oscim.app.location;
-import android.os.SystemClock;
-
-import org.oscim.backend.GL;
-import org.oscim.core.Box;
import org.oscim.core.MercatorProjection;
-import org.oscim.core.Point;
-import org.oscim.core.Tile;
import org.oscim.layers.Layer;
import org.oscim.map.Map;
-import org.oscim.renderer.GLShader;
-import org.oscim.renderer.GLState;
-import org.oscim.renderer.GLViewport;
-import org.oscim.renderer.LayerRenderer;
-import org.oscim.renderer.MapRenderer;
-import org.oscim.utils.FastMath;
-import org.oscim.utils.math.Interpolation;
-
-import static org.oscim.backend.GLAdapter.gl;
+import org.oscim.renderer.LocationRenderer;
public class LocationOverlay extends Layer {
- private final int SHOW_ACCURACY_ZOOM = 16;
-
- private final Point mLocation = new Point();
- private double mRadius;
-
private final Compass mCompass;
+ private final LocationRenderer mLocationRenderer;
public LocationOverlay(Map map, Compass compass) {
super(map);
- mRenderer = new LocationIndicator(map);
mCompass = compass;
+
+ mRenderer = mLocationRenderer = new LocationRenderer(mMap, this);
+ mLocationRenderer.setCallback(compass);
}
public void setPosition(double latitude, double longitude, double accuracy) {
- mLocation.x = MercatorProjection.longitudeToX(longitude);
- mLocation.y = MercatorProjection.latitudeToY(latitude);
- mRadius = accuracy / MercatorProjection.groundResolution(latitude, 1);
- ((LocationIndicator) mRenderer).animate(true);
+ double x = MercatorProjection.longitudeToX(longitude);
+ double y = MercatorProjection.latitudeToY(latitude);
+ double radius = accuracy / MercatorProjection.groundResolution(latitude, 1);
+ mLocationRenderer.setLocation(x, y, radius);
+ mLocationRenderer.animate(true);
}
@Override
@@ -64,268 +49,8 @@ public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (!enabled)
- ((LocationIndicator) mRenderer).animate(false);
+ mLocationRenderer.animate(false);
mCompass.setEnabled(enabled);
}
-
- public class LocationIndicator extends LayerRenderer {
- private int mShaderProgram;
- private int hVertexPosition;
- private int hMatrixPosition;
- private int hScale;
- private int hPhase;
- private int hDirection;
-
- private final float CIRCLE_SIZE = 60;
-
- private final static long ANIM_RATE = 50;
- private final static long INTERVAL = 2000;
-
- private final Point mIndicatorPosition = new Point();
-
- private final Point mScreenPoint = new Point();
- private final Box mBBox = new Box();
-
- private boolean mInitialized;
-
- private boolean mLocationIsVisible;
-
- private boolean mRunAnim;
- private long mAnimStart;
-
- public LocationIndicator(final Map map) {
- super();
- }
-
- private void animate(boolean enable) {
- if (mRunAnim == enable)
- return;
-
- mRunAnim = enable;
- if (!enable)
- return;
-
- final Runnable action = new Runnable() {
- private long lastRun;
-
- @Override
- public void run() {
- if (!mRunAnim)
- return;
-
- long diff = SystemClock.elapsedRealtime() - lastRun;
- mMap.postDelayed(this, Math.min(ANIM_RATE, diff));
- mMap.render();
- }
- };
-
- mAnimStart = SystemClock.elapsedRealtime();
- mMap.postDelayed(action, ANIM_RATE);
- }
-
- private float animPhase() {
- return (float) ((MapRenderer.frametime - mAnimStart) % INTERVAL) / INTERVAL;
- }
-
- @Override
- public void update(GLViewport v) {
-
- if (!mInitialized) {
- init();
- mInitialized = true;
- }
-
- if (!isEnabled()) {
- setReady(false);
- return;
- }
-
- if (!v.changed() && isReady())
- return;
-
- setReady(true);
-
- int width = mMap.getWidth();
- int height = mMap.getHeight();
-
- // clamp location to a position that can be
- // savely translated to screen coordinates
- v.getBBox(mBBox, 0);
-
- double x = mLocation.x;
- double y = mLocation.y;
-
- if (!mBBox.contains(mLocation)) {
- x = FastMath.clamp(x, mBBox.xmin, mBBox.xmax);
- y = FastMath.clamp(y, mBBox.ymin, mBBox.ymax);
- }
-
- // get position of Location in pixel relative to
- // screen center
- v.toScreenPoint(x, y, mScreenPoint);
-
- x = mScreenPoint.x + width / 2;
- y = mScreenPoint.y + height / 2;
-
- // clip position to screen boundaries
- int visible = 0;
-
- if (x > width - 5)
- x = width;
- else if (x < 5)
- x = 0;
- else
- visible++;
-
- if (y > height - 5)
- y = height;
- else if (y < 5)
- y = 0;
- else
- visible++;
-
- mLocationIsVisible = (visible == 2);
-
- // set location indicator position
- v.fromScreenPoint(x, y, mIndicatorPosition);
- }
-
- @Override
- public void render(GLViewport v) {
-
- GLState.useProgram(mShaderProgram);
- GLState.blend(true);
- GLState.test(false, false);
-
- GLState.enableVertexArrays(hVertexPosition, -1);
- MapRenderer.bindQuadVertexVBO(hVertexPosition/*, true*/);
-
- float radius = CIRCLE_SIZE;
-
- animate(true);
- boolean viewShed = false;
- if (!mLocationIsVisible /* || pos.zoomLevel < SHOW_ACCURACY_ZOOM */) {
- //animate(true);
- } else {
- if (v.pos.zoomLevel >= SHOW_ACCURACY_ZOOM)
- radius = (float) (mRadius * v.pos.scale);
-
- viewShed = true;
- //animate(false);
- }
- gl.uniform1f(hScale, radius);
-
- double x = mIndicatorPosition.x - v.pos.x;
- double y = mIndicatorPosition.y - v.pos.y;
- double tileScale = Tile.SIZE * v.pos.scale;
-
- v.mvp.setTransScale((float) (x * tileScale), (float) (y * tileScale), 1);
- v.mvp.multiplyMM(v.viewproj, v.mvp);
- v.mvp.setAsUniform(hMatrixPosition);
-
- if (!viewShed) {
- float phase = Math.abs(animPhase() - 0.5f) * 2;
- //phase = Interpolation.fade.apply(phase);
- phase = Interpolation.swing.apply(phase);
-
- gl.uniform1f(hPhase, 0.8f + phase * 0.2f);
- } else {
- gl.uniform1f(hPhase, 1);
- }
-
- if (viewShed && mLocationIsVisible) {
- float rotation = mCompass.getRotation() - 90;
- gl.uniform2f(hDirection,
- (float) Math.cos(Math.toRadians(rotation)),
- (float) Math.sin(Math.toRadians(rotation)));
- } else {
- gl.uniform2f(hDirection, 0, 0);
- }
-
- gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
- }
-
- private boolean init() {
- int shader = GLShader.createProgram(vShaderStr, fShaderStr);
- if (shader == 0)
- return false;
-
- mShaderProgram = shader;
- hVertexPosition = gl.getAttribLocation(shader, "a_pos");
- hMatrixPosition = gl.getUniformLocation(shader, "u_mvp");
- hPhase = gl.getUniformLocation(shader, "u_phase");
- hScale = gl.getUniformLocation(shader, "u_scale");
- hDirection = gl.getUniformLocation(shader, "u_dir");
-
- return true;
- }
-
- private final static String vShaderStr = ""
- + "precision mediump float;"
- + "uniform mat4 u_mvp;"
- + "uniform float u_phase;"
- + "uniform float u_scale;"
- + "attribute vec2 a_pos;"
- + "varying vec2 v_tex;"
- + "void main() {"
- + " gl_Position = u_mvp * vec4(a_pos * u_scale * u_phase, 0.0, 1.0);"
- + " v_tex = a_pos;"
- + "}";
-
- private final static String fShaderStr = ""
- + "precision mediump float;"
- + "varying vec2 v_tex;"
- + "uniform float u_scale;"
- + "uniform float u_phase;"
- + "uniform vec2 u_dir;"
-
- + "void main() {"
- + " float len = 1.0 - length(v_tex);"
- + " if (u_dir.x == 0.0 && u_dir.y == 0.0){"
- + " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * len;"
- + " } else {"
- /// outer ring
- + " float a = smoothstep(0.0, 2.0 / u_scale, len);"
- /// inner ring
- + " float b = 0.5 * smoothstep(4.0 / u_scale, 5.0 / u_scale, len);"
- /// center point
- + " float c = 0.5 * (1.0 - smoothstep(14.0 / u_scale, 16.0 / u_scale, 1.0 - len));"
- + " vec2 dir = normalize(v_tex);"
- + " float d = 1.0 - dot(dir, u_dir); "
- /// 0.5 width of viewshed
- + " d = clamp(step(0.5, d), 0.4, 0.7);"
- /// - subtract inner from outer to create the outline
- /// - multiply by viewshed
- /// - add center point
- + " a = d * (a - (b + c)) + c;"
- + " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * a;"
- + "}}";
-
- //private final static String fShaderStr = ""
- // + "precision mediump float;"
- // + "varying vec2 v_tex;"
- // + "uniform float u_scale;"
- // + "uniform float u_phase;"
- // + "uniform vec2 u_dir;"
- // + "void main() {"
- // + " float len = 1.0 - length(v_tex);"
- // /// outer ring
- // + " float a = smoothstep(0.0, 2.0 / u_scale, len);"
- // /// inner ring
- // + " float b = 0.8 * smoothstep(3.0 / u_scale, 4.0 / u_scale, len);"
- // /// center point
- // + " float c = 0.5 * (1.0 - smoothstep(14.0 / u_scale, 16.0 / u_scale, 1.0 - len));"
- // + " vec2 dir = normalize(v_tex);"
- // + " float d = dot(dir, u_dir); "
- // /// 0.5 width of viewshed
- // + " d = clamp(smoothstep(0.7, 0.7 + 2.0/u_scale, d) * len, 0.0, 1.0);"
- // /// - subtract inner from outer to create the outline
- // /// - multiply by viewshed
- // /// - add center point
- // + " a = max(d, (a - (b + c)) + c);"
- // + " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * a;"
- // + "}";
-
- }
}
diff --git a/vtm/src/org/oscim/renderer/LocationRenderer.java b/vtm/src/org/oscim/renderer/LocationRenderer.java
new file mode 100644
index 000000000..7b9bc2964
--- /dev/null
+++ b/vtm/src/org/oscim/renderer/LocationRenderer.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2013 Ahmad Saleem
+ * 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.renderer;
+
+import org.oscim.backend.GL;
+import org.oscim.core.Box;
+import org.oscim.core.Point;
+import org.oscim.core.Tile;
+import org.oscim.layers.Layer;
+import org.oscim.map.Map;
+import org.oscim.utils.FastMath;
+import org.oscim.utils.math.Interpolation;
+
+import static org.oscim.backend.GLAdapter.gl;
+
+public class LocationRenderer extends LayerRenderer {
+ private static final int SHOW_ACCURACY_ZOOM = 16;
+
+ private final Map mMap;
+ private final Layer mLayer;
+
+ private int mShaderProgram;
+ private int hVertexPosition;
+ private int hMatrixPosition;
+ private int hScale;
+ private int hPhase;
+ private int hDirection;
+
+ private static final float CIRCLE_SIZE = 60;
+
+ private static final long ANIM_RATE = 50;
+ private static final long INTERVAL = 2000;
+
+ private final Point mIndicatorPosition = new Point();
+
+ private final Point mScreenPoint = new Point();
+ private final Box mBBox = new Box();
+
+ private boolean mInitialized;
+
+ private boolean mLocationIsVisible;
+
+ private boolean mRunAnim;
+ private long mAnimStart;
+
+ private Callback mCallback;
+ private final Point mLocation = new Point(Double.NaN, Double.NaN);
+ private double mRadius;
+ private int mShowAccuracyZoom = SHOW_ACCURACY_ZOOM;
+
+ public LocationRenderer(Map map, Layer layer) {
+ mMap = map;
+ mLayer = layer;
+ }
+
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ public void setLocation(double x, double y, double radius) {
+ mLocation.x = x;
+ mLocation.y = y;
+ mRadius = radius;
+ }
+
+ public void setShowAccuracyZoom(int showAccuracyZoom) {
+ mShowAccuracyZoom = showAccuracyZoom;
+ }
+
+ public void animate(boolean enable) {
+ if (mRunAnim == enable)
+ return;
+
+ mRunAnim = enable;
+ if (!enable)
+ return;
+
+ final Runnable action = new Runnable() {
+ private long lastRun;
+
+ @Override
+ public void run() {
+ if (!mRunAnim)
+ return;
+
+ long diff = System.currentTimeMillis() - lastRun;
+ mMap.postDelayed(this, Math.min(ANIM_RATE, diff));
+ mMap.render();
+ lastRun = System.currentTimeMillis();
+ }
+ };
+
+ mAnimStart = System.currentTimeMillis();
+ mMap.postDelayed(action, ANIM_RATE);
+ }
+
+ private float animPhase() {
+ return (float) ((MapRenderer.frametime - mAnimStart) % INTERVAL) / INTERVAL;
+ }
+
+ @Override
+ public void update(GLViewport v) {
+
+ if (!mInitialized) {
+ init();
+ mInitialized = true;
+ }
+
+ if (!mLayer.isEnabled()) {
+ setReady(false);
+ return;
+ }
+
+ /*if (!v.changed() && isReady())
+ return;*/
+
+ setReady(true);
+
+ int width = mMap.getWidth();
+ int height = mMap.getHeight();
+
+ // clamp location to a position that can be
+ // savely translated to screen coordinates
+ v.getBBox(mBBox, 0);
+
+ double x = mLocation.x;
+ double y = mLocation.y;
+
+ if (!mBBox.contains(mLocation)) {
+ x = FastMath.clamp(x, mBBox.xmin, mBBox.xmax);
+ y = FastMath.clamp(y, mBBox.ymin, mBBox.ymax);
+ }
+
+ // get position of Location in pixel relative to
+ // screen center
+ v.toScreenPoint(x, y, mScreenPoint);
+
+ x = mScreenPoint.x + width / 2;
+ y = mScreenPoint.y + height / 2;
+
+ // clip position to screen boundaries
+ int visible = 0;
+
+ if (x > width - 5)
+ x = width;
+ else if (x < 5)
+ x = 0;
+ else
+ visible++;
+
+ if (y > height - 5)
+ y = height;
+ else if (y < 5)
+ y = 0;
+ else
+ visible++;
+
+ mLocationIsVisible = (visible == 2);
+
+ // set location indicator position
+ v.fromScreenPoint(x, y, mIndicatorPosition);
+ }
+
+ @Override
+ public void render(GLViewport v) {
+
+ GLState.useProgram(mShaderProgram);
+ GLState.blend(true);
+ GLState.test(false, false);
+
+ GLState.enableVertexArrays(hVertexPosition, -1);
+ MapRenderer.bindQuadVertexVBO(hVertexPosition/*, true*/);
+
+ float radius = CIRCLE_SIZE;
+
+ animate(true);
+ boolean viewShed = false;
+ if (!mLocationIsVisible /* || pos.zoomLevel < SHOW_ACCURACY_ZOOM */) {
+ //animate(true);
+ } else {
+ if (v.pos.zoomLevel >= mShowAccuracyZoom && mRadius > 0)
+ radius = (float) (mRadius * v.pos.scale);
+
+ viewShed = true;
+ //animate(false);
+ }
+ gl.uniform1f(hScale, radius);
+
+ double x = mIndicatorPosition.x - v.pos.x;
+ double y = mIndicatorPosition.y - v.pos.y;
+ double tileScale = Tile.SIZE * v.pos.scale;
+
+ v.mvp.setTransScale((float) (x * tileScale), (float) (y * tileScale), 1);
+ v.mvp.multiplyMM(v.viewproj, v.mvp);
+ v.mvp.setAsUniform(hMatrixPosition);
+
+ if (!viewShed) {
+ float phase = Math.abs(animPhase() - 0.5f) * 2;
+ //phase = Interpolation.fade.apply(phase);
+ phase = Interpolation.swing.apply(phase);
+
+ gl.uniform1f(hPhase, 0.8f + phase * 0.2f);
+ } else {
+ gl.uniform1f(hPhase, 1);
+ }
+
+ if (viewShed && mLocationIsVisible) {
+ float rotation = 0;
+ if (mCallback != null)
+ rotation = mCallback.getRotation();
+ rotation -= 90;
+ gl.uniform2f(hDirection,
+ (float) Math.cos(Math.toRadians(rotation)),
+ (float) Math.sin(Math.toRadians(rotation)));
+ } else {
+ gl.uniform2f(hDirection, 0, 0);
+ }
+
+ gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
+ }
+
+ private boolean init() {
+ int shader = GLShader.createProgram(vShaderStr, fShaderStr);
+ if (shader == 0)
+ return false;
+
+ mShaderProgram = shader;
+ hVertexPosition = gl.getAttribLocation(shader, "a_pos");
+ hMatrixPosition = gl.getUniformLocation(shader, "u_mvp");
+ hPhase = gl.getUniformLocation(shader, "u_phase");
+ hScale = gl.getUniformLocation(shader, "u_scale");
+ hDirection = gl.getUniformLocation(shader, "u_dir");
+
+ return true;
+ }
+
+ private final static String vShaderStr = ""
+ + "precision mediump float;"
+ + "uniform mat4 u_mvp;"
+ + "uniform float u_phase;"
+ + "uniform float u_scale;"
+ + "attribute vec2 a_pos;"
+ + "varying vec2 v_tex;"
+ + "void main() {"
+ + " gl_Position = u_mvp * vec4(a_pos * u_scale * u_phase, 0.0, 1.0);"
+ + " v_tex = a_pos;"
+ + "}";
+
+ private final static String fShaderStr = ""
+ + "precision mediump float;"
+ + "varying vec2 v_tex;"
+ + "uniform float u_scale;"
+ + "uniform float u_phase;"
+ + "uniform vec2 u_dir;"
+
+ + "void main() {"
+ + " float len = 1.0 - length(v_tex);"
+ + " if (u_dir.x == 0.0 && u_dir.y == 0.0){"
+ + " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * len;"
+ + " } else {"
+ /// outer ring
+ + " float a = smoothstep(0.0, 2.0 / u_scale, len);"
+ /// inner ring
+ + " float b = 0.5 * smoothstep(4.0 / u_scale, 5.0 / u_scale, len);"
+ /// center point
+ + " float c = 0.5 * (1.0 - smoothstep(14.0 / u_scale, 16.0 / u_scale, 1.0 - len));"
+ + " vec2 dir = normalize(v_tex);"
+ + " float d = 1.0 - dot(dir, u_dir); "
+ /// 0.5 width of viewshed
+ + " d = clamp(step(0.5, d), 0.4, 0.7);"
+ /// - subtract inner from outer to create the outline
+ /// - multiply by viewshed
+ /// - add center point
+ + " a = d * (a - (b + c)) + c;"
+ + " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * a;"
+ + "}}";
+
+ //private final static String fShaderStr = ""
+ // + "precision mediump float;"
+ // + "varying vec2 v_tex;"
+ // + "uniform float u_scale;"
+ // + "uniform float u_phase;"
+ // + "uniform vec2 u_dir;"
+ // + "void main() {"
+ // + " float len = 1.0 - length(v_tex);"
+ // /// outer ring
+ // + " float a = smoothstep(0.0, 2.0 / u_scale, len);"
+ // /// inner ring
+ // + " float b = 0.8 * smoothstep(3.0 / u_scale, 4.0 / u_scale, len);"
+ // /// center point
+ // + " float c = 0.5 * (1.0 - smoothstep(14.0 / u_scale, 16.0 / u_scale, 1.0 - len));"
+ // + " vec2 dir = normalize(v_tex);"
+ // + " float d = dot(dir, u_dir); "
+ // /// 0.5 width of viewshed
+ // + " d = clamp(smoothstep(0.7, 0.7 + 2.0/u_scale, d) * len, 0.0, 1.0);"
+ // /// - subtract inner from outer to create the outline
+ // /// - multiply by viewshed
+ // /// - add center point
+ // + " a = max(d, (a - (b + c)) + c);"
+ // + " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * a;"
+ // + "}";
+
+ public interface Callback {
+ float getRotation();
+ }
+}