From 3cdf785533c6ca44ff315aea20cc5d691a561c80 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sat, 21 Feb 2015 16:40:26 +0100 Subject: [PATCH] Add basic Circle support --- src/org/microg/gms/maps/BackendMap.java | 50 +++- src/org/microg/gms/maps/GoogleMapImpl.java | 53 +++- .../microg/gms/maps/markup/CircleImpl.java | 231 ++++++++++++++++-- .../microg/gms/maps/markup/MarkerImpl.java | 86 ++----- src/org/microg/gms/maps/markup/Markup.java | 11 + 5 files changed, 318 insertions(+), 113 deletions(-) diff --git a/src/org/microg/gms/maps/BackendMap.java b/src/org/microg/gms/maps/BackendMap.java index f07362e3..77c8afab 100644 --- a/src/org/microg/gms/maps/BackendMap.java +++ b/src/org/microg/gms/maps/BackendMap.java @@ -18,9 +18,11 @@ package org.microg.gms.maps; import android.content.Context; import android.graphics.BitmapFactory; +import android.util.Log; import android.view.View; + import com.google.android.gms.R; -import org.microg.gms.maps.bitmap.DefaultBitmapDescriptor; + import org.microg.gms.maps.camera.CameraUpdate; import org.microg.gms.maps.markup.Markup; import org.oscim.android.MapView; @@ -40,6 +42,8 @@ import org.oscim.theme.VtmThemes; import org.oscim.tiling.source.oscimap4.OSciMap4TileSource; public class BackendMap { + private final static String TAG = "GmsBackendMap"; + private final Context context; private final MapView mapView; private final BuildingLayer buildings; @@ -135,8 +139,21 @@ public class BackendMap { } public T add(T markup) { - items.addItem(markup.getMarkerItem(context)); - redraw(); + switch (markup.getType()) { + case MARKER: + items.addItem(markup.getMarkerItem(context)); + redraw(); + break; + case LAYER: + Layers layers = mapView.map().layers(); + layers.add(markup.getLayer(context, mapView.map())); + layers.remove(items); + layers.add(items); + redraw(); + break; + default: + Log.d(TAG, "Unknown markup: " + markup); + } return markup; } @@ -146,14 +163,29 @@ public class BackendMap { } public void remove(Markup markup) { - items.removeItem(items.getByUid(markup.getId())); - redraw(); + switch (markup.getType()) { + case MARKER: + items.removeItem(items.getByUid(markup.getId())); + redraw(); + break; + default: + Log.d(TAG, "Unknown markup: " + markup); + } } public void update(Markup markup) { - // TODO: keep order - items.removeItem(items.getByUid(markup.getId())); - items.addItem(markup.getMarkerItem(context)); - redraw(); + switch (markup.getType()) { + case MARKER: + // TODO: keep order + items.removeItem(items.getByUid(markup.getId())); + items.addItem(markup.getMarkerItem(context)); + redraw(); + break; + case LAYER: + redraw(); + break; + default: + Log.d(TAG, "Unknown markup: " + markup); + } } } diff --git a/src/org/microg/gms/maps/GoogleMapImpl.java b/src/org/microg/gms/maps/GoogleMapImpl.java index 1651085f..6d7961ac 100644 --- a/src/org/microg/gms/maps/GoogleMapImpl.java +++ b/src/org/microg/gms/maps/GoogleMapImpl.java @@ -22,14 +22,48 @@ import android.os.RemoteException; import android.util.Log; import android.view.LayoutInflater; import android.view.View; + import com.google.android.gms.dynamic.IObjectWrapper; import com.google.android.gms.dynamic.ObjectWrapper; import com.google.android.gms.maps.GoogleMapOptions; -import com.google.android.gms.maps.internal.*; -import com.google.android.gms.maps.model.*; -import com.google.android.gms.maps.model.internal.*; +import com.google.android.gms.maps.internal.ICancelableCallback; +import com.google.android.gms.maps.internal.IGoogleMapDelegate; +import com.google.android.gms.maps.internal.IInfoWindowAdapter; +import com.google.android.gms.maps.internal.ILocationSourceDelegate; +import com.google.android.gms.maps.internal.IOnCameraChangeListener; +import com.google.android.gms.maps.internal.IOnInfoWindowClickListener; +import com.google.android.gms.maps.internal.IOnMapClickListener; +import com.google.android.gms.maps.internal.IOnMapLoadedCallback; +import com.google.android.gms.maps.internal.IOnMapLongClickListener; +import com.google.android.gms.maps.internal.IOnMarkerClickListener; +import com.google.android.gms.maps.internal.IOnMarkerDragListener; +import com.google.android.gms.maps.internal.IOnMyLocationButtonClickListener; +import com.google.android.gms.maps.internal.IOnMyLocationChangeListener; +import com.google.android.gms.maps.internal.IProjectionDelegate; +import com.google.android.gms.maps.internal.ISnapshotReadyCallback; +import com.google.android.gms.maps.internal.IUiSettingsDelegate; +import com.google.android.gms.maps.model.CameraPosition; +import com.google.android.gms.maps.model.CircleOptions; +import com.google.android.gms.maps.model.GroundOverlayOptions; +import com.google.android.gms.maps.model.MarkerOptions; +import com.google.android.gms.maps.model.PolygonOptions; +import com.google.android.gms.maps.model.PolylineOptions; +import com.google.android.gms.maps.model.TileOverlayOptions; +import com.google.android.gms.maps.model.internal.ICircleDelegate; +import com.google.android.gms.maps.model.internal.IGroundOverlayDelegate; +import com.google.android.gms.maps.model.internal.IMarkerDelegate; +import com.google.android.gms.maps.model.internal.IPolygonDelegate; +import com.google.android.gms.maps.model.internal.IPolylineDelegate; +import com.google.android.gms.maps.model.internal.ITileOverlayDelegate; + import org.microg.gms.maps.camera.CameraUpdate; -import org.microg.gms.maps.markup.*; +import org.microg.gms.maps.markup.CircleImpl; +import org.microg.gms.maps.markup.GroundOverlayImpl; +import org.microg.gms.maps.markup.MarkerImpl; +import org.microg.gms.maps.markup.Markup; +import org.microg.gms.maps.markup.PolygonImpl; +import org.microg.gms.maps.markup.PolylineImpl; +import org.microg.gms.maps.markup.TileOverlayImpl; import org.oscim.event.Event; import org.oscim.event.MotionEvent; import org.oscim.map.Map; @@ -45,6 +79,7 @@ public class GoogleMapImpl extends IGoogleMapDelegate.Stub private final ProjectionImpl projection; private int markerCounter = 0; + private int circleCounter = 0; public GoogleMapImpl(LayoutInflater inflater, GoogleMapOptions options) { context = inflater.getContext(); @@ -74,6 +109,10 @@ public class GoogleMapImpl extends IGoogleMapDelegate.Stub private String getNextMarkerId() { return "m" + markerCounter++; } + + private String getNextCircleId() { + return "c" + circleCounter++; + } /* Camera @@ -115,7 +154,7 @@ public class GoogleMapImpl extends IGoogleMapDelegate.Stub @Override public void animateCameraWithDurationAndCallback(IObjectWrapper cameraUpdate, int duration, - ICancelableCallback callback) throws RemoteException { + ICancelableCallback callback) throws RemoteException { CameraUpdate camUpdate = (CameraUpdate) ObjectWrapper.unwrap(cameraUpdate); backendMap.applyCameraUpdateAnimated(camUpdate, duration); } @@ -136,7 +175,7 @@ public class GoogleMapImpl extends IGoogleMapDelegate.Stub @Override public ICircleDelegate addCircle(CircleOptions options) throws RemoteException { - return new CircleImpl(options); // TODO + return backendMap.add(new CircleImpl(getNextCircleId(), options, this)); } @Override @@ -242,7 +281,7 @@ public class GoogleMapImpl extends IGoogleMapDelegate.Stub /* Ui Settings */ - + @Override public IUiSettingsDelegate getUiSettings() throws RemoteException { return uiSettings; diff --git a/src/org/microg/gms/maps/markup/CircleImpl.java b/src/org/microg/gms/maps/markup/CircleImpl.java index e98d1138..39b11be5 100644 --- a/src/org/microg/gms/maps/markup/CircleImpl.java +++ b/src/org/microg/gms/maps/markup/CircleImpl.java @@ -16,102 +16,136 @@ package org.microg.gms.maps.markup; +import android.content.Context; import android.os.RemoteException; + import com.google.android.gms.maps.model.CircleOptions; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.internal.ICircleDelegate; -public class CircleImpl extends ICircleDelegate.Stub { - private LatLng center; - private double radius; - private float zIndex; - private boolean visible; - private String id; - private float strokeWidth; - private int strokeColor; - private int fillColor; +import org.oscim.android.gl.AndroidGL; +import org.oscim.backend.GL20; +import org.oscim.backend.canvas.Color; +import org.oscim.core.Box; +import org.oscim.core.Point; +import org.oscim.core.Tile; +import org.oscim.layers.Layer; +import org.oscim.layers.marker.MarkerItem; +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; - public CircleImpl(CircleOptions options) { +import static org.oscim.core.MercatorProjection.groundResolution; +import static org.oscim.core.MercatorProjection.latitudeToY; +import static org.oscim.core.MercatorProjection.longitudeToX; + +public class CircleImpl extends ICircleDelegate.Stub implements Markup { + + private final String id; + private final CircleOptions options; + private final MarkupListener listener; + private CircleLayer layer; + private Point point; + private float drawRadius; + + public CircleImpl(String id, CircleOptions options, MarkupListener listener) { + this.id = id; + this.listener = listener; + this.options = options == null ? new CircleOptions() : options; + LatLng center = this.options.getCenter(); + if (center != null) { + point = new Point(longitudeToX(center.longitude), latitudeToY(center.latitude)); + drawRadius = (float) (options.getRadius() / groundResolution(center.latitude, 1)); + } } @Override public void remove() throws RemoteException { - + listener.remove(this); } @Override - public String getId() throws RemoteException { + public String getId() { return id; } @Override public void setCenter(LatLng center) throws RemoteException { - this.center = center; + options.center(center); + point = new Point(longitudeToX(center.longitude), latitudeToY(center.latitude)); + drawRadius = (float) (options.getRadius() / groundResolution(center.latitude, 1)); } @Override public LatLng getCenter() throws RemoteException { - return center; + return options.getCenter(); } @Override public void setRadius(double radius) throws RemoteException { - this.radius = radius; + options.radius(radius); + if (point != null) { + this.drawRadius = (float) (options.getRadius() / groundResolution(options.getCenter().latitude, 1)); + } } @Override public double getRadius() throws RemoteException { - return radius; + return options.getRadius(); } @Override public void setStrokeWidth(float width) throws RemoteException { - this.strokeWidth = width; + options.strokeWidth(width); } @Override public float getStrokeWidth() throws RemoteException { - return strokeWidth; + return options.getStrokeWidth(); } @Override public void setStrokeColor(int color) throws RemoteException { - this.strokeColor = color; + options.strokeColor(color); } @Override public int getStrokeColor() throws RemoteException { - return strokeColor; + return options.getStrokeColor(); } @Override public void setFillColor(int color) throws RemoteException { - this.fillColor = color; + options.fillColor(color); + listener.update(this); } @Override public int getFillColor() throws RemoteException { - return fillColor; + return options.getFillColor(); } @Override public void setZIndex(float zIndex) throws RemoteException { - this.zIndex = zIndex; + options.zIndex(zIndex); } @Override public float getZIndex() throws RemoteException { - return zIndex; + return options.getZIndex(); } @Override public void setVisible(boolean visible) throws RemoteException { - this.visible = visible; + options.visible(visible); } @Override public boolean isVisible() throws RemoteException { - return visible; + return options.isVisible(); } @Override @@ -123,4 +157,149 @@ public class CircleImpl extends ICircleDelegate.Stub { public int hashCodeRemote() throws RemoteException { return id.hashCode(); } + + @Override + public MarkerItem getMarkerItem(Context context) { + return null; + } + + @Override + public Layer getLayer(Context context, Map map) { + if (layer == null) { + layer = new CircleLayer(map); + } + return layer; + } + + @Override + public Type getType() { + return Type.LAYER; + } + + private class CircleLayer extends Layer { + + public CircleLayer(Map map) { + super(map); + mRenderer = new CircleRenderer(); + } + + private class CircleRenderer extends LayerRenderer { + private final Box bBox = new Box(); + private final Point screenPoint = new Point(); + private final Point indicatorPosition = new Point(); + private AndroidGL GL = new AndroidGL(); + + private int shader; + private int vertexPosition; + private int matrixPosition; + private int phase; + private int scale; + private int direction; + private int color; + + @Override + public void update(GLViewport viewport) { + if (!isEnabled()) { + setReady(false); + return; + } + + if (!viewport.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 + viewport.getBBox(bBox, 0); + + double x = point.x; + double y = point.y; + + // get position of Location in pixel relative to + // screen center + viewport.toScreenPoint(x, y, screenPoint); + + x = screenPoint.x + width / 2; + y = screenPoint.y + height / 2; + + viewport.fromScreenPoint(x, y, indicatorPosition); + } + + @Override + public void render(GLViewport viewport) { + + GLState.useProgram(shader); + GLState.blend(true); + GLState.test(false, false); + + GLState.enableVertexArrays(vertexPosition, -1); + MapRenderer.bindQuadVertexVBO(vertexPosition); + + float radius = (float) (drawRadius * viewport.pos.scale); + GL.uniform1f(scale, radius); + + double x = indicatorPosition.x - viewport.pos.x; + double y = indicatorPosition.y - viewport.pos.y; + double tileScale = Tile.SIZE * viewport.pos.scale; + + viewport.mvp.setTransScale((float) (x * tileScale), (float) (y * tileScale), 1); + viewport.mvp.multiplyMM(viewport.viewproj, viewport.mvp); + viewport.mvp.setAsUniform(matrixPosition); + GL.uniform1f(phase, 1); + GL.uniform2f(direction, 0, 0); + float alpha = Color.aToFloat(options.getFillColor()); + GL.uniform4f(color, + Color.rToFloat(options.getFillColor()) * alpha, + Color.gToFloat(options.getFillColor()) * alpha, + Color.bToFloat(options.getFillColor()) * alpha, + alpha); + + GL.drawArrays(GL20.GL_TRIANGLE_STRIP, 0, 4); + } + + @Override + public boolean setup() { + shader = GLShader.createProgram(vShaderStr, fShaderStr); + if (shader == 0) + return false; + vertexPosition = GL.getAttribLocation(shader, "a_pos"); + matrixPosition = GL.getUniformLocation(shader, "u_mvp"); + phase = GL.getUniformLocation(shader, "u_phase"); + scale = GL.getUniformLocation(shader, "u_scale"); + direction = GL.getUniformLocation(shader, "u_dir"); + color = GL.getUniformLocation(shader, "u_color"); + 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;" + + "uniform vec4 u_color;" + + + "void main() {" + + " float len = 1.0 - length(v_tex);" + + " float a = smoothstep(0.0, 2.0 / u_scale, len);" + + " gl_FragColor = u_color * a;" + + "}"; } diff --git a/src/org/microg/gms/maps/markup/MarkerImpl.java b/src/org/microg/gms/maps/markup/MarkerImpl.java index ca9c8407..cd61e9f8 100644 --- a/src/org/microg/gms/maps/markup/MarkerImpl.java +++ b/src/org/microg/gms/maps/markup/MarkerImpl.java @@ -27,85 +27,19 @@ import com.google.android.gms.maps.model.internal.IMarkerDelegate; import org.microg.gms.maps.GmsMapsTypeHelper; import org.microg.gms.maps.bitmap.BitmapDescriptorImpl; import org.oscim.android.canvas.AndroidBitmap; +import org.oscim.layers.Layer; import org.oscim.layers.marker.MarkerItem; import org.oscim.layers.marker.MarkerSymbol; +import org.oscim.map.Map; public class MarkerImpl extends IMarkerDelegate.Stub implements Markup { private static final String TAG = MarkerImpl.class.getName(); private final String id; - - private MarkerOptions options; + private final MarkerOptions options; + private final MarkupListener listener; private BitmapDescriptorImpl icon; - private MarkupListener listener; - /*private Overlay overlay = new Overlay() { - private Point point = new Point(); - - @Override - public boolean onTap(GeoPoint p, MapView mapView) { - Point touchPoint = mapView.getProjection().toPixels(p, null); - Bitmap bitmap = icon.getBitmap(); - if (bitmap == null) - return false; - //mapView.getProjection().toPixels(position.toGeoPoint(), point); - float xTest = bitmap.getWidth() * anchorU + touchPoint.x - point.x; - float yTest = bitmap.getHeight() * anchorV + touchPoint.y - point.y; - if (0 < xTest && xTest < bitmap.getWidth() && 0 < yTest && yTest < bitmap.getHeight()) { - Log.d(TAG, "touched " + title); - IOnMarkerClickListener markerClickListener = map.getMarkerClickListener(); - boolean result = false; - if (markerClickListener != null) { - try { - result = markerClickListener.onMarkerClick(MarkerImpl.this); - } catch (RemoteException e) { - Log.w(TAG, e); - } - } - if (!result) { - mapView.getController().animateTo(position.toGeoPoint()); - map.showInfoWindow(MarkerImpl.this); - } - return true; - } - return false; - } - - @Override - public void draw(Canvas canvas, MapView mapView, boolean shadow) { - if (shadow && flat) - return; // shadows are broken right now, we skip them - Bitmap bitmap = icon.getBitmap(); - if (bitmap != null) { - //mapView.getProjection().toPixels(position.toGeoPoint(), point); - float x = point.x - bitmap.getWidth() * anchorU; - float y = point.y - bitmap.getHeight() * anchorV; - Paint paint = new Paint(); - paint.setAlpha((int) (alpha * 255)); - if (shadow) { - paint.setColorFilter( - new PorterDuffColorFilter(Color.argb((int) (128 * alpha), 0, 0, 0), - PorterDuff.Mode.SRC_IN)); - } - Matrix matrix = new Matrix(); - matrix.setRotate(rotation, bitmap.getWidth() / 2, bitmap.getHeight() / 2); - if (shadow) { - matrix.postSkew(-0.9F, 0); - matrix.postScale(1, 0.5F); - } - matrix.postTranslate(x, y); - canvas.drawBitmap(bitmap, matrix, paint); - } else { - icon.loadBitmapAsync(map.getContext(), new Runnable() { - @Override - public void run() { - map.redraw(); - } - }); - } - } - };*/ - public MarkerImpl(String id, MarkerOptions options, MarkupListener listener) { this.id = id; this.listener = listener; @@ -274,7 +208,7 @@ public class MarkerImpl extends IMarkerDelegate.Stub implements Markup { if (icon.getBitmap() != null) { item.setMarker( new MarkerSymbol(new AndroidBitmap(icon.getBitmap()), options.getAnchorU(), - options.getAnchorV(), isFlat())); + options.getAnchorV(), !options.isFlat())); } else { icon.loadBitmapAsync(context, new Runnable() { @Override @@ -286,4 +220,14 @@ public class MarkerImpl extends IMarkerDelegate.Stub implements Markup { } return item; } + + @Override + public Layer getLayer(Context context, Map map) { + return null; + } + + @Override + public Type getType() { + return Type.MARKER; + } } diff --git a/src/org/microg/gms/maps/markup/Markup.java b/src/org/microg/gms/maps/markup/Markup.java index f399873e..9f30b1e5 100644 --- a/src/org/microg/gms/maps/markup/Markup.java +++ b/src/org/microg/gms/maps/markup/Markup.java @@ -17,13 +17,24 @@ package org.microg.gms.maps.markup; import android.content.Context; + +import org.oscim.layers.Layer; import org.oscim.layers.marker.MarkerItem; +import org.oscim.map.Map; public interface Markup { public MarkerItem getMarkerItem(Context context); + public Layer getLayer(Context context, Map map); + + public Type getType(); + public String getId(); + public static enum Type { + MARKER, LAYER + } + public static interface MarkupListener { void update(Markup markup);