org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of osmdroid-android Show documentation
Show all versions of osmdroid-android Show documentation
An Android library to display OpenStreetMap views.
package org.osmdroid.views.overlay.mylocation;
import java.util.LinkedList;
import microsoft.mappoint.TileSystem;
import org.osmdroid.DefaultResourceProxyImpl;
import org.osmdroid.ResourceProxy;
import org.osmdroid.api.IMapView;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapController;
import org.osmdroid.views.MapView;
import org.osmdroid.views.MapView.Projection;
import org.osmdroid.views.overlay.IOverlayMenuProvider;
import org.osmdroid.views.overlay.Overlay.Snappable;
import org.osmdroid.views.overlay.SafeDrawOverlay;
import org.osmdroid.views.safecanvas.ISafeCanvas;
import org.osmdroid.views.safecanvas.SafePaint;
import org.osmdroid.views.util.constants.MapViewConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Paint.Style;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.location.Location;
import android.util.FloatMath;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
/**
*
* @author Marc Kurtz
* @author Manuel Stahl
*
*/
public class MyLocationNewOverlay extends SafeDrawOverlay implements IMyLocationConsumer,
IOverlayMenuProvider, Snappable
{
private static final Logger logger = LoggerFactory.getLogger(MyLocationNewOverlay.class);
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
protected final SafePaint mPaint = new SafePaint();
protected final SafePaint mCirclePaint = new SafePaint();
protected final Bitmap mPersonBitmap;
protected final Bitmap mDirectionArrowBitmap;
protected final MapView mMapView;
private final MapController mMapController;
public IMyLocationProvider mMyLocationProvider;
private final LinkedList mRunOnFirstFix = new LinkedList();
private final Point mMapCoords = new Point();
private Location mLocation;
private boolean mIsLocationEnabled = false;
protected boolean mIsFollowing = false; // follow location updates
protected boolean mDrawAccuracyEnabled = true;
/** Coordinates the feet of the person are located scaled for display density. */
protected final PointF mPersonHotspot;
protected final float mDirectionArrowCenterX;
protected final float mDirectionArrowCenterY;
public static final int MENU_MY_LOCATION = getSafeMenuId();
private boolean mOptionsMenuEnabled = true;
// to avoid allocations during onDraw
private final Matrix mDirectionRotater = new Matrix();
private final float[] mMatrixValues = new float[9];
private final Matrix mMatrix = new Matrix();
private final Rect mMyLocationRect = new Rect();
private final Rect mMyLocationPreviousRect = new Rect();
// ===========================================================
// Constructors
// ===========================================================
public MyLocationNewOverlay(final Context context, IMyLocationProvider myLocationProvider,
final MapView mapView)
{
this(context, myLocationProvider, mapView, new DefaultResourceProxyImpl(context));
}
public MyLocationNewOverlay(final Context context, IMyLocationProvider myLocationProvider,
final MapView mapView, final ResourceProxy resourceProxy)
{
super(resourceProxy);
if (myLocationProvider == null)
throw new RuntimeException("You must pass an IMyLocationProvider to enableMyLocation()");
mMyLocationProvider = myLocationProvider;
mMapView = mapView;
mMapController = mapView.getController();
mCirclePaint.setARGB(0, 100, 100, 255);
mCirclePaint.setAntiAlias(true);
mPersonBitmap = mResourceProxy.getBitmap(ResourceProxy.bitmap.person);
mDirectionArrowBitmap = mResourceProxy.getBitmap(ResourceProxy.bitmap.direction_arrow);
mDirectionArrowCenterX = mDirectionArrowBitmap.getWidth() / 2 - 0.5f;
mDirectionArrowCenterY = mDirectionArrowBitmap.getHeight() / 2 - 0.5f;
// Calculate position of person icon's feet, scaled to screen density
mPersonHotspot = new PointF(24.0f * mScale + 0.5f, 39.0f * mScale + 0.5f);
}
// ===========================================================
// Getter & Setter
// ===========================================================
/**
* If enabled, an accuracy circle will be drawn around your current position.
*
* @param drawAccuracyEnabled whether the accuracy circle will be enabled
*/
public void setDrawAccuracyEnabled(final boolean drawAccuracyEnabled)
{
mDrawAccuracyEnabled = drawAccuracyEnabled;
}
/**
* If enabled, an accuracy circle will be drawn around your current position.
*
* @return true if enabled, false otherwise
*/
public boolean isDrawAccuracyEnabled()
{
return mDrawAccuracyEnabled;
}
public IMyLocationProvider getMyLocationProvider()
{
return mMyLocationProvider;
}
protected void setMyLocationProvider(IMyLocationProvider myLocationProvider)
{
if (mMyLocationProvider != null)
mMyLocationProvider.stopLocationProvider();
mMyLocationProvider = myLocationProvider;
}
public void setPersonHotspot(float x, float y)
{
mPersonHotspot.set(x, y);
}
protected void drawMyLocation(final ISafeCanvas canvas, final MapView mapView, final Location lastFix)
{
final Projection pj = mapView.getProjection();
final int zoomDiff = MapViewConstants.MAXIMUM_ZOOMLEVEL - pj.getZoomLevel();
if (mDrawAccuracyEnabled) {
final float radius = lastFix.getAccuracy()
/ (float) TileSystem.GroundResolution(lastFix.getLatitude(), mapView.getZoomLevel());
mCirclePaint.setAlpha(50);
mCirclePaint.setStyle(Style.FILL);
canvas.drawCircle(mMapCoords.x >> zoomDiff, mMapCoords.y >> zoomDiff, radius, mCirclePaint);
mCirclePaint.setAlpha(150);
mCirclePaint.setStyle(Style.STROKE);
canvas.drawCircle(mMapCoords.x >> zoomDiff, mMapCoords.y >> zoomDiff, radius, mCirclePaint);
}
canvas.getMatrix(mMatrix);
mMatrix.getValues(mMatrixValues);
if (DEBUGMODE) {
final float tx = (-mMatrixValues[Matrix.MTRANS_X] + 20) / mMatrixValues[Matrix.MSCALE_X];
final float ty = (-mMatrixValues[Matrix.MTRANS_Y] + 90) / mMatrixValues[Matrix.MSCALE_Y];
canvas.drawText("Lat: " + lastFix.getLatitude(), tx, ty + 5, mPaint);
canvas.drawText("Lon: " + lastFix.getLongitude(), tx, ty + 20, mPaint);
canvas.drawText("Alt: " + lastFix.getAltitude(), tx, ty + 35, mPaint);
canvas.drawText("Acc: " + lastFix.getAccuracy(), tx, ty + 50, mPaint);
}
mDirectionRotater.reset();
if (lastFix.hasBearing()) {
/*
* Rotate the direction-Arrow according to the bearing we are driving. And draw it to the canvas.
*/
mDirectionRotater.setRotate(lastFix.getBearing(), mDirectionArrowCenterX, mDirectionArrowCenterY);
mDirectionRotater.postTranslate(-mDirectionArrowCenterX, -mDirectionArrowCenterY);
mDirectionRotater.postScale(1 / mMatrixValues[Matrix.MSCALE_X],
1 / mMatrixValues[Matrix.MSCALE_Y]);
mDirectionRotater.postTranslate(mMapCoords.x >> zoomDiff, mMapCoords.y >> zoomDiff);
canvas.drawBitmap(mDirectionArrowBitmap, mDirectionRotater, mPaint);
} else {
mDirectionRotater.setTranslate(-mPersonHotspot.x, -mPersonHotspot.y);
mDirectionRotater.postScale(1 / mMatrixValues[Matrix.MSCALE_X],
1 / mMatrixValues[Matrix.MSCALE_Y]);
mDirectionRotater.postTranslate(mMapCoords.x >> zoomDiff, mMapCoords.y >> zoomDiff);
canvas.drawBitmap(mPersonBitmap, mDirectionRotater, mPaint);
}
}
protected Rect getMyLocationDrawingBounds(int zoomLevel, Location lastFix, Rect reuse)
{
if (reuse == null)
reuse = new Rect();
final int zoomDiff = MapViewConstants.MAXIMUM_ZOOMLEVEL - zoomLevel;
final int posX = mMapCoords.x >> zoomDiff;
final int posY = mMapCoords.y >> zoomDiff;
// Start with the bitmap bounds
if (lastFix.hasBearing()) {
// Get a square bounding box around the object, and expand by the length of the diagonal
// so as to allow for extra space for rotating
int widestEdge = (int) Math.ceil(Math.max(mDirectionArrowBitmap.getWidth(),
mDirectionArrowBitmap.getHeight())
* Math.sqrt(2));
reuse.set(posX, posY, posX + widestEdge, posY + widestEdge);
reuse.offset((int) -widestEdge / 2, (int) -widestEdge / 2);
} else {
reuse.set(posX, posY, posX + mPersonBitmap.getWidth(), posY + mPersonBitmap.getHeight());
reuse.offset((int) -mPersonHotspot.x, (int) -mPersonHotspot.y);
}
// Add in the accuracy circle if enabled
if (mDrawAccuracyEnabled) {
final int radius = (int) FloatMath.ceil(lastFix.getAccuracy()
/ (float) TileSystem.GroundResolution(lastFix.getLatitude(), zoomLevel));
reuse.union(posX - radius, posY - radius, posX + radius, posY + radius);
final int strokeWidth = (int) FloatMath.ceil(mCirclePaint.getStrokeWidth() == 0 ? 1
: mCirclePaint.getStrokeWidth());
reuse.inset(-strokeWidth, -strokeWidth);
}
reuse.offset(mMapView.getWidth() / 2, mMapView.getHeight() / 2);
return reuse;
}
// ===========================================================
// Methods from SuperClass/Interfaces
// ===========================================================
@Override
protected void drawSafe(ISafeCanvas canvas, MapView mapView, boolean shadow)
{
if (shadow)
return;
if (mLocation != null) {
drawMyLocation(canvas, mapView, mLocation);
}
}
@Override
public boolean onSnapToItem(final int x, final int y, final Point snapPoint, final IMapView mapView)
{
if (this.mLocation != null) {
snapPoint.x = mMapCoords.x;
snapPoint.y = mMapCoords.y;
final double xDiff = x - mMapCoords.x;
final double yDiff = y - mMapCoords.y;
final boolean snap = xDiff * xDiff + yDiff * yDiff < 64;
if (DEBUGMODE) {
logger.debug("snap=" + snap);
}
return snap;
} else {
return false;
}
}
@Override
public boolean onTouchEvent(final MotionEvent event, final MapView mapView)
{
if (event.getAction() == MotionEvent.ACTION_MOVE) {
this.disableFollowLocation();
}
return super.onTouchEvent(event, mapView);
}
// ===========================================================
// Menu handling methods
// ===========================================================
@Override
public void setOptionsMenuEnabled(final boolean pOptionsMenuEnabled)
{
this.mOptionsMenuEnabled = pOptionsMenuEnabled;
}
@Override
public boolean isOptionsMenuEnabled()
{
return this.mOptionsMenuEnabled;
}
@Override
public boolean onCreateOptionsMenu(final Menu pMenu, final int pMenuIdOffset, final MapView pMapView)
{
pMenu.add(0, MENU_MY_LOCATION + pMenuIdOffset, Menu.NONE,
mResourceProxy.getString(ResourceProxy.string.my_location)).setIcon(
mResourceProxy.getDrawable(ResourceProxy.bitmap.ic_menu_mylocation));
return true;
}
@Override
public boolean onPrepareOptionsMenu(final Menu pMenu, final int pMenuIdOffset, final MapView pMapView)
{
return false;
}
@Override
public boolean onOptionsItemSelected(final MenuItem pItem, final int pMenuIdOffset, final MapView pMapView)
{
final int menuId = pItem.getItemId() - pMenuIdOffset;
if (menuId == MENU_MY_LOCATION) {
if (this.isMyLocationEnabled()) {
this.disableFollowLocation();
this.disableMyLocation();
} else {
this.enableFollowLocation();
this.enableMyLocation(this.getMyLocationProvider());
}
return true;
} else {
return false;
}
}
// ===========================================================
// Methods
// ===========================================================
/**
* Return a GeoPoint of the last known location, or null if not known.
*/
public GeoPoint getMyLocation()
{
if (mLocation == null) {
return null;
} else {
return new GeoPoint(mLocation);
}
}
public Location getLastFix()
{
return mLocation;
}
/**
* Enables "follow" functionality. The map will center on your current location and automatically scroll
* as you move. Scrolling the map in the UI will disable.
*/
public void enableFollowLocation()
{
mIsFollowing = true;
// set initial location when enabled
if (isMyLocationEnabled()) {
mLocation = mMyLocationProvider.getLastKnownLocation();
if (mLocation != null) {
TileSystem.LatLongToPixelXY(mLocation.getLatitude(), mLocation.getLongitude(),
MapViewConstants.MAXIMUM_ZOOMLEVEL, mMapCoords);
final int worldSize_2 = TileSystem.MapSize(MapViewConstants.MAXIMUM_ZOOMLEVEL) / 2;
mMapCoords.offset(-worldSize_2, -worldSize_2);
mMapController.animateTo(new GeoPoint(mLocation));
}
}
// Update the screen to see changes take effect
if (mMapView != null) {
mMapView.postInvalidate();
}
}
/**
* Disables "follow" functionality.
*/
public void disableFollowLocation()
{
mIsFollowing = false;
}
/**
* If enabled, the map will center on your current location and automatically scroll as you move.
* Scrolling the map in the UI will disable.
*
* @return true if enabled, false otherwise
*/
public boolean isFollowLocationEnabled()
{
return mIsFollowing;
}
@Override
public void onLocationChanged(Location location, IMyLocationProvider source)
{
// If we had a previous location, let's get those bounds
Location oldLocation = mLocation;
if (oldLocation != null) {
this.getMyLocationDrawingBounds(mMapView.getZoomLevel(), oldLocation, mMyLocationPreviousRect);
}
mLocation = location;
mMapCoords.set(0, 0);
if (mLocation != null) {
TileSystem.LatLongToPixelXY(mLocation.getLatitude(), mLocation.getLongitude(),
MapViewConstants.MAXIMUM_ZOOMLEVEL, mMapCoords);
final int worldSize_2 = TileSystem.MapSize(MapViewConstants.MAXIMUM_ZOOMLEVEL) / 2;
mMapCoords.offset(-worldSize_2, -worldSize_2);
if (mIsFollowing) {
mMapController.animateTo(mLocation.getLatitude(), mLocation.getLongitude());
} else {
// Get new drawing bounds
this.getMyLocationDrawingBounds(mMapView.getZoomLevel(), mLocation, mMyLocationRect);
// If we had a previous location, merge in those bounds too
if (oldLocation != null) {
mMyLocationRect.union(mMyLocationPreviousRect);
}
// Invalidate the bounds
mMapView.postInvalidate(mMyLocationRect.left, mMyLocationRect.top, mMyLocationRect.right,
mMyLocationRect.bottom);
}
}
for (final Runnable runnable : mRunOnFirstFix) {
new Thread(runnable).start();
}
mRunOnFirstFix.clear();
}
/**
* Enable receiving location updates from the provided IMyLocationProvider and show your location on the
* maps. You will likely want to call enableMyLocation() from your Activity's Activity.onResume() method,
* to enable the features of this overlay. Remember to call the corresponding disableMyLocation() in your
* Activity's Activity.onPause() method to turn off updates when in the background.
*/
public boolean enableMyLocation(IMyLocationProvider myLocationProvider)
{
boolean result = true;
if (myLocationProvider == null)
throw new RuntimeException("You must pass an IMyLocationProvider to enableMyLocation()");
this.setMyLocationProvider(myLocationProvider);
result = mMyLocationProvider.startLocationProvider(this);
mIsLocationEnabled = result;
// set initial location when enabled
if (result && isFollowLocationEnabled()) {
mLocation = mMyLocationProvider.getLastKnownLocation();
if (mLocation != null) {
TileSystem.LatLongToPixelXY(mLocation.getLatitude(), mLocation.getLongitude(),
MapViewConstants.MAXIMUM_ZOOMLEVEL, mMapCoords);
final int worldSize_2 = TileSystem.MapSize(MapViewConstants.MAXIMUM_ZOOMLEVEL) / 2;
mMapCoords.offset(-worldSize_2, -worldSize_2);
mMapController.animateTo(new GeoPoint(mLocation));
}
}
// Update the screen to see changes take effect
if (mMapView != null) {
mMapView.postInvalidate();
}
return result;
}
/**
* Disable location updates
*/
public void disableMyLocation()
{
mIsLocationEnabled = false;
if (mMyLocationProvider != null) {
mMyLocationProvider.stopLocationProvider();
}
// Update the screen to see changes take effect
if (mMapView != null) {
mMapView.postInvalidate();
}
}
/**
* If enabled, the map is receiving location updates and drawing your location on the map.
*
* @return true if enabled, false otherwise
*/
public boolean isMyLocationEnabled()
{
return mIsLocationEnabled;
}
public boolean runOnFirstFix(final Runnable runnable)
{
if (mMyLocationProvider != null && mLocation != null) {
new Thread(runnable).start();
return true;
} else {
mRunOnFirstFix.addLast(runnable);
return false;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy