Back to project page android-open-street-map.
The source code is released under:
Apache License
If you think the Android project android-open-street-map listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.android.lib.map.osm; // w w w .j ava 2 s . c o m import java.util.ArrayList; import java.util.List; import java.util.Vector; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.PixelFormat; import android.view.GestureDetector.OnDoubleTapListener; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.animation.Animation; import android.view.animation.ScaleAnimation; import com.android.lib.map.osm.overlay.OsmOverlay; public abstract class OsmMapViewBase extends SurfaceView implements android.view.GestureDetector.OnGestureListener, OnDoubleTapListener { public final static int MAX_ZOOM_LEVEL = 19; public final static int MIN_ZOOM_LEVEL_FOR_TILES = 18; private final static int TILE_SIZE = Tile.TILE_SIZE; protected static int mMinZoomLevel = 0; protected int mZoomLevel; protected int mPendingZoomLevel; private int mOffsetX = 0; private int mOffsetY = 0; private int mTouchDownX = 0; private int mTouchDownY = 0; private int mTouchOffsetX; private int mTouchOffsetY; private Tile[] mTiles; private int[] mIncrementsX; private int[] mIncrementsY; private Animation mZoomInAnimation; private Animation mZoomOutAnimation; private Animation mZoomInDoubleTapAnimation; private TilesProvider mTilesProvider; private List<OsmOverlay> mOverlays; private int mMaptTypeId; private TileHandler mHandler; private GeoPoint setMapCenterWhenViewSizeChange; protected boolean mIsDoubleTap = false; private Bitmap mMapTileUnavailableBitmap = null; public OsmMapViewBase(Context context, int mapTypeId) { super(context); SurfaceHolder surfaceHolder = getHolder(); surfaceHolder.setFormat(PixelFormat.RGB_565); mZoomLevel = 2; mPendingZoomLevel = 2; mZoomInAnimation = new ScaleAnimation(1.0f, 2f, 1.0f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mZoomInAnimation.setDuration(400L); mZoomOutAnimation = new ScaleAnimation(1, 0.5f, 1, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mZoomOutAnimation.setDuration(400L); mOverlays = new ArrayList<OsmOverlay>(); mMaptTypeId = mapTypeId; mHandler = new TileHandler(this); } public List<OsmOverlay> getOverlays() { return mOverlays; } public boolean animateZoomIn() { if (mPendingZoomLevel >= MAX_ZOOM_LEVEL) { return false; } if (mIsDoubleTap) { double pivotX = this.mTouchDownX * 1.0 / getWidth(); double pivotY = this.mTouchDownY * 1.0 / getHeight(); mZoomInDoubleTapAnimation = new ScaleAnimation(1, 2, 1, 2, Animation.RELATIVE_TO_SELF, (float)pivotX, Animation.RELATIVE_TO_SELF, (float)pivotY); mZoomInDoubleTapAnimation.setDuration(400L); } if (this.mPendingZoomLevel == this.mZoomLevel) { this.mPendingZoomLevel++; if (mIsDoubleTap) { startAnimation(mZoomInDoubleTapAnimation); } else { startAnimation(mZoomInAnimation); } Tile[] tiles; if (mIsDoubleTap) { tiles = initializePendingTiles(mZoomLevel + 1, (getOffsetX()) * 2 - this.mTouchDownX, (getOffsetY()) * 2 - this.mTouchDownY); } else { tiles = initializePendingTiles(mZoomLevel + 1, (getOffsetX()) * 2 - (getWidth() / 2), (getOffsetY()) * 2 -(getHeight() / 2)); } for (int i = (mTiles.length-1); i >= 0; i--) { mTilesProvider.getTileBitmap(tiles[i]); } } return true; } public boolean animateZoomOut() { if (mPendingZoomLevel <= mMinZoomLevel) { return false; } if (mPendingZoomLevel == this.mZoomLevel) { this.mPendingZoomLevel--; startAnimation(mZoomOutAnimation); } return true; } public GeoPoint getCenter() { int offsetX = mOffsetX - (getWidth() / 2); int offsetY = mOffsetY - (getHeight() / 2); GeoPoint g = getProjectionFromPixels(offsetX, offsetY); return g; } public GeoPoint getProjectionFromPixels(int x, int y) { return Projection.getProjectionFromPixels(x, y, mZoomLevel); } public void setCenter(double lat, double lon, int mapWidth, int mapHeight) { int x = Projection.getXPixelFromLongitude(lon, mZoomLevel); int y = Projection.getYPixelFromLatitude(lat, mZoomLevel); int offsetX = (0 - x) + (mapWidth / 2); int offsetY = (0 - y) + (mapHeight / 2); setOffsetX(offsetX); setOffsetY(offsetY); invalidate(); } public void setCenter(double lat, double lon) { int screenWidth = getWidth(); int screenHeight = getHeight(); if (screenHeight == 0 && screenWidth == 0) setMapCenterWhenViewSizeChange = new GeoPoint((int)(lat*1E6),(int)(lon*1E6)); int x = Projection.getXPixelFromLongitude(lon, mZoomLevel); int y = Projection.getYPixelFromLatitude(lat, mZoomLevel); int offsetX = (0 - x) + (screenWidth / 2); int offsetY = (0 - y) + (screenHeight / 2); setOffsetX(offsetX); setOffsetY(offsetY); invalidate(); } @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // On view change we have enough room for all the tiles if (getWidth() > 0 && getHeight() > 0) initMapTileArrayCoordinate(); //--- if (setMapCenterWhenViewSizeChange != null) { double lat = setMapCenterWhenViewSizeChange.getLatitudeE6() / 1E6; double lon = setMapCenterWhenViewSizeChange.getLongitudeE6() / 1E6; setCenter(lat, lon); setMapCenterWhenViewSizeChange = null; } } public void setMapTileUnavailableBitmap(Bitmap bitmap){ mMapTileUnavailableBitmap = bitmap; } private void initMapTileArrayCoordinate() { int xRows = (int) Math.ceil((double)(getWidth()+Tile.TILE_SIZE) / (double)Tile.TILE_SIZE); int yRows = (int) Math.ceil((double)(getHeight()+Tile.TILE_SIZE) / (double)Tile.TILE_SIZE); mTilesProvider.setResizeBitmapCacheSize(xRows*yRows*2); mTilesProvider.setBitmapMemoryCacheSize(xRows*yRows*2); mTilesProvider.setMapTileUnavailableBitmap(mMapTileUnavailableBitmap); mIncrementsX = new int[xRows*yRows]; mIncrementsY = new int[xRows*yRows]; Tile[] oldTiles = mTiles; mTiles = new Tile[xRows*yRows]; if (oldTiles != null && oldTiles.length > 0) { for (int i = 0; i < oldTiles.length && i < mTiles.length; i++) mTiles[i] = oldTiles[i]; } int pos = 0; for (int x = 0; x < (yRows); x++) { for (int i = 0; i < xRows; i++) { mIncrementsX[pos++] = i; } } pos = 0; for (int y = 0; y < (yRows); y++) { for (int i = 0; i < xRows; i++) { mIncrementsY[pos++] = y; } } // if (getWidth() > 0 && getHeight() > 0 && getWidth() > getHeight()) { // mIncrementsX = new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3 }; // mIncrementsY = new int[] { 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2 }; // } else { // mIncrementsX = new int[] { 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2 }; // mIncrementsY = new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3 }; // } } public void clear() { try { mTilesProvider.clearCache(); mTilesProvider.clearResizeCache(); mTilesProvider.stopThreads(); } catch (Exception e) { e.printStackTrace(); } } public void startTileThreads(boolean allowTileRequestViaHttp) { mTilesProvider = new TilesProvider(this.getContext(), mHandler, allowTileRequestViaHttp); } private int getMaxOffsetX() { return (int) (0 - (Math.pow(2, mZoomLevel)) * TILE_SIZE); } public int getOffsetX() { return mOffsetX; } public int getOffsetY() { return mOffsetY; } private Tile[] initializeCurrentTiles(int zoomLevel, int offsetX, int offsetY) { int mapX = (0 - offsetX) / TILE_SIZE; int mapY = (0 - offsetY) / TILE_SIZE; for (int index = 0; mTiles != null && index < mTiles.length; ++index) { if (mTiles[index] == null) { mTiles[index] = new Tile(); } // try to save on string relocations if (mTiles[index].mapX != (mapX + mIncrementsX[index]) || mTiles[index].mapY != (mapY + mIncrementsY[index]) || mTiles[index].zoom != zoomLevel) { mTiles[index].mapX = mapX + mIncrementsX[index]; mTiles[index].mapY = mapY + mIncrementsY[index]; mTiles[index].offsetX = mTiles[index].mapX * TILE_SIZE; mTiles[index].offsetY = mTiles[index].mapY * TILE_SIZE; mTiles[index].zoom = zoomLevel; mTiles[index].key = (zoomLevel + "/" + mTiles[index].mapX + "/" + mTiles[index].mapY + ".png").intern(); mTiles[index].mapTypeId = mMaptTypeId; } } return mTiles; } private Tile[] initializePendingTiles(int zoomLevel, int offsetX, int offsetY) { int mapX = (0 - offsetX) / TILE_SIZE; int mapY = (0 - offsetY) / TILE_SIZE; Tile[] tiles = new Tile[mTiles.length]; for (int index = 0; mTiles != null && index < mTiles.length; ++index) { tiles[index] = new Tile(); tiles[index].mapX = mapX + mIncrementsX[index]; tiles[index].mapY = mapY + mIncrementsY[index]; tiles[index].offsetX = tiles[index].mapX * TILE_SIZE; tiles[index].offsetY = tiles[index].mapY * TILE_SIZE; tiles[index].zoom = zoomLevel; tiles[index].key = (zoomLevel + "/" + tiles[index].mapX + "/" + tiles[index].mapY + ".png").intern(); tiles[index].mapTypeId = mMaptTypeId; } return tiles; } private boolean isOnScreen(Tile tile) { if (tile == null) { return false; } int upperLeftX = tile.offsetX + this.mOffsetX; int upperLeftY = tile.offsetY + this.mOffsetY; int width = this.getWidth(); int height = this.getHeight(); if (((upperLeftX + TILE_SIZE) >= 0) && (upperLeftX < width) && ((upperLeftY + TILE_SIZE) >= 0) && (upperLeftY < height)) { return isSane(tile); } return false; } private boolean isSane(Tile tile) { if (tile.mapX >= 0 && tile.mapY >= 0 && tile.mapX <= (Math.pow(2, mZoomLevel) - 1) && tile.mapY <= (Math.pow(2, mZoomLevel) - 1)) { return true; } return false; } @Override protected void onAnimationEnd() { if (this.mZoomLevel > mPendingZoomLevel) { this.mZoomLevel = mPendingZoomLevel; zoomOut(); } else if (this.mZoomLevel < mPendingZoomLevel) { this.mZoomLevel = mPendingZoomLevel; if(mIsDoubleTap) zoomInForDoubleTap(); else zoomIn(); } mTouchOffsetX = getOffsetX(); mTouchOffsetY = getOffsetY(); invalidate(); super.onAnimationEnd(); } private void onZoomLevelChanges() { for (OsmOverlay overlay : mOverlays) { overlay.onZoomLevelChanges(this); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for (int i = (mTiles.length-1); i >= 0; i--) { if (isOnScreen(mTiles[i])) { Bitmap bitmap = mTilesProvider.getTileBitmap(mTiles[i]); if (bitmap != null) { canvas.drawBitmap(bitmap, mOffsetX + mTiles[i].offsetX, mOffsetY + mTiles[i].offsetY, null); } } } for (OsmOverlay overlay : mOverlays) { if (overlay == null) continue; overlay.draw(canvas, this); } } public void setOffsetX(int offsetX) { if (mPendingZoomLevel == this.mZoomLevel) { if ((this.getWidth() != 0) && ((offsetX + 255) > this.getWidth())) { this.mOffsetX = this.getWidth() - 255; } else if ((offsetX - 255) < getMaxOffsetX()) { this.mOffsetX = getMaxOffsetX() + 255; } else { this.mOffsetX = offsetX; } //Log.i("setOffsetX", "OffsetX= " + mOffsetX); } } public void setOffsetY(int offsetY) { if (mPendingZoomLevel == this.mZoomLevel) { if ((this.getHeight() != 0) && ((offsetY + 255) > this.getHeight())) { this.mOffsetY = this.getHeight() - 255; } else if ((offsetY - 255) < getMaxOffsetX()) { this.mOffsetY = getMaxOffsetX() + 255; } else { this.mOffsetY = offsetY; } initializeCurrentTiles(mZoomLevel, this.mOffsetX, this.mOffsetY); //Log.i("setOffsetY", "OffsetY= " + mOffsetY); } } public int setZoom(int zoomLevel) { if (zoomLevel > MAX_ZOOM_LEVEL) zoomLevel = MAX_ZOOM_LEVEL; if (zoomLevel < mMinZoomLevel) zoomLevel = mMinZoomLevel; this.mZoomLevel = zoomLevel; this.mPendingZoomLevel = zoomLevel; onZoomLevelChanges(); return this.mZoomLevel; } private void zoomInForDoubleTap() { setOffsetX((getOffsetX()) * 2 - this.mTouchDownX); setOffsetY((getOffsetY()) * 2 - this.mTouchDownY); // mTilesProvider.clearResizeCache(); mIsDoubleTap=false; onZoomLevelChanges(); } public void zoomIn() { setOffsetX((getOffsetX()) * 2 - (getWidth() / 2)); setOffsetY((getOffsetY()) * 2 -(getHeight() / 2)); // mTilesProvider.clearResizeCache(); onZoomLevelChanges(); } public void zoomOut() { setOffsetX(getOffsetX() / 2 + (getWidth() / 4)); setOffsetY(getOffsetY() / 2 + (getHeight() / 4)); // mTilesProvider.clearResizeCache(); onZoomLevelChanges(); } public class Tiles extends Vector<Tile> { private static final long serialVersionUID = -6468659912600523042L; } @Override public boolean onDown(MotionEvent event) { //ACTION_DOWN this.mTouchDownX = (int) event.getX(); this.mTouchDownY = (int) event.getY(); this.mTouchOffsetX = this.mOffsetX; this.mTouchOffsetY = this.mOffsetY; return true; } @Override public boolean onFling(MotionEvent downEvent, MotionEvent event, float distanceXf, float distanceYf) { //ACTION_UP setOffsetX(this.mTouchOffsetX + (int) event.getX() - this.mTouchDownX); setOffsetY(this.mTouchOffsetY + (int) event.getY() - this.mTouchDownY); return true; } @Override public void onLongPress(MotionEvent arg0) { } @Override public boolean onScroll(MotionEvent downEvent, MotionEvent currentEvent, float arg2, float arg3) { // ACTION_MOVE Equivalent setOffsetX(this.mTouchOffsetX + (int) currentEvent.getX() - this.mTouchDownX); setOffsetY(this.mTouchOffsetY+ (int) currentEvent.getY() - this.mTouchDownY); invalidate(); return true; } @Override public boolean onSingleTapConfirmed(MotionEvent event) { for (OsmOverlay overlay : mOverlays) { if (overlay.onInterceptSingleTap(event, this)) { overlay.onSingleTap(event, this); invalidate(); return true; } } for (OsmOverlay overlay : mOverlays) { if (overlay.onSingleTap(event, this)) { invalidate(); return true; } } return false; } @Override public void onShowPress(MotionEvent event) { } }