@Override public boolean onCreateOptionsMenu( final Menu pMenu, final int pMenuIdOffset, final MapView pMapView) { final SubMenu mapMenu = pMenu .addSubMenu(0, MENU_MAP_MODE + pMenuIdOffset, Menu.NONE, R.string.map_mode) .setIcon(R.drawable.ic_menu_mapmode); for (int a = 0; a < TileSourceFactory.getTileSources().size(); a++) { final ITileSource tileSource = TileSourceFactory.getTileSources().get(a); mapMenu.add( MENU_MAP_MODE + pMenuIdOffset, MENU_TILE_SOURCE_STARTING_ID + a + pMenuIdOffset, Menu.NONE, tileSource.name()); } mapMenu.setGroupCheckable(MENU_MAP_MODE + pMenuIdOffset, true, true); if (ctx != null) { final String title = ctx.getString( pMapView.useDataConnection() ? R.string.set_mode_offline : R.string.set_mode_online); final Drawable icon = ctx.getResources().getDrawable(R.drawable.ic_menu_offline); pMenu.add(0, MENU_OFFLINE + pMenuIdOffset, Menu.NONE, title).setIcon(icon); } return true; }
@Override public boolean onOptionsItemSelected( final MenuItem pItem, final int pMenuIdOffset, final MapView pMapView) { final int menuId = pItem.getItemId() - pMenuIdOffset; if ((menuId >= MENU_TILE_SOURCE_STARTING_ID) && (menuId < MENU_TILE_SOURCE_STARTING_ID + TileSourceFactory.getTileSources().size())) { pMapView.setTileSource( TileSourceFactory.getTileSources().get(menuId - MENU_TILE_SOURCE_STARTING_ID)); return true; } else if (menuId == MENU_OFFLINE) { final boolean useDataConnection = !pMapView.useDataConnection(); pMapView.setUseDataConnection(useDataConnection); return true; } else { return false; } }
@Override public boolean onPrepareOptionsMenu( final Menu pMenu, final int pMenuIdOffset, final MapView pMapView) { final int index = TileSourceFactory.getTileSources().indexOf(pMapView.getTileProvider().getTileSource()); if (index >= 0) { pMenu.findItem(MENU_TILE_SOURCE_STARTING_ID + index + pMenuIdOffset).setChecked(true); } pMenu .findItem(MENU_OFFLINE + pMenuIdOffset) .setTitle( pMapView.useDataConnection() ? R.string.set_mode_offline : R.string.set_mode_online); return true; }
@Override public boolean onCreateOptionsMenu(final Menu pMenu) { pMenu.add(0, MENU_ZOOMIN_ID, Menu.NONE, "ZoomIn"); pMenu.add(0, MENU_ZOOMOUT_ID, Menu.NONE, "ZoomOut"); final SubMenu subMenu = pMenu.addSubMenu(0, MENU_TILE_SOURCE_ID, Menu.NONE, "Choose Tile Source"); { for (final ITileSource tileSource : TileSourceFactory.getTileSources()) { subMenu.add( 0, 1000 + tileSource.ordinal(), Menu.NONE, tileSource.localizedName(mResourceProxy)); } } pMenu.add(0, MENU_ANIMATION_ID, Menu.NONE, "Run Animation"); pMenu.add(0, MENU_MINIMAP_ID, Menu.NONE, "Toggle Minimap"); return true; }
@Override public boolean onMenuItemSelected(final int featureId, final MenuItem item) { switch (item.getItemId()) { case MENU_ZOOMIN_ID: this.mOsmvController.zoomIn(); return true; case MENU_ZOOMOUT_ID: this.mOsmvController.zoomOut(); return true; case MENU_TILE_SOURCE_ID: this.mOsmv.invalidate(); return true; case MENU_MINIMAP_ID: mMiniMapOverlay.setEnabled(!mMiniMapOverlay.isEnabled()); this.mOsmv.invalidate(); return true; case MENU_ANIMATION_ID: // this.mOsmv.getController().animateTo(52370816, 9735936, // MapControllerOld.AnimationType.MIDDLEPEAKSPEED, // MapControllerOld.ANIMATION_SMOOTHNESS_HIGH, // MapControllerOld.ANIMATION_DURATION_DEFAULT); // Hannover // Stop the Animation after 500ms (just to show that it works) // new Handler().postDelayed(new Runnable(){ // @Override // public void run() { // SampleExtensive.this.mOsmv.getController().stopAnimation(false); // } // }, 500); return true; default: ITileSource tileSource = TileSourceFactory.getTileSource(item.getItemId() - 1000); mOsmv.setTileSource(tileSource); mMiniMapOverlay.setTileSource(tileSource); } return false; }
/** * These objects are the principle consumer of map tiles. * * <p>see {@link MapTile} for an overview of how tiles are acquired by this overlay. */ public class TilesOverlay extends Overlay implements IOverlayMenuProvider { public static final int MENU_MAP_MODE = getSafeMenuId(); public static final int MENU_TILE_SOURCE_STARTING_ID = getSafeMenuIdSequence(TileSourceFactory.getTileSources().size()); public static final int MENU_OFFLINE = getSafeMenuId(); private Context ctx; /** Current tile source */ protected final MapTileProviderBase mTileProvider; protected Drawable userSelectedLoadingDrawable = null; /* to avoid allocations during draw */ protected final Paint mDebugPaint = new Paint(); private final Rect mTileRect = new Rect(); private final Point mTilePoint = new Point(); private final Rect mViewPort = new Rect(); private Point mTopLeftMercator = new Point(); private Point mBottomRightMercator = new Point(); private Point mTilePointMercator = new Point(); private Projection mProjection; private boolean mOptionsMenuEnabled = true; /** A drawable loading tile * */ private BitmapDrawable mLoadingTile = null; private int mLoadingBackgroundColor = Color.rgb(216, 208, 208); private int mLoadingLineColor = Color.rgb(200, 192, 192); /** For overshooting the tile cache * */ private int mOvershootTileCache = 0; // Issue 133 night mode private ColorFilter currentColorFilter = null; static final float[] negate = { -1.0f, 0, 0, 0, 255, // red 0, -1.0f, 0, 0, 255, // green 0, 0, -1.0f, 0, 255, // blue 0, 0, 0, 1.0f, 0 // alpha }; /** provides a night mode like affect by inverting the map tile colors */ public static final ColorFilter INVERT_COLORS = new ColorMatrixColorFilter(negate); public TilesOverlay(final MapTileProviderBase aTileProvider, final Context aContext) { super(); this.ctx = aContext; if (aTileProvider == null) { throw new IllegalArgumentException( "You must pass a valid tile provider to the tiles overlay."); } this.mTileProvider = aTileProvider; } /** * See issue https://github.com/osmdroid/osmdroid/issues/330 customizable override for the grey * grid * * @since 5.2+ * @param drawable */ public void setLoadingDrawable(final Drawable drawable) { userSelectedLoadingDrawable = drawable; } @Override public void onDetach(final MapView pMapView) { this.mTileProvider.detach(); ctx = null; if (mLoadingTile != null) { // Only recycle if we are running on a project less than 2.3.3 Gingerbread. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) { if (mLoadingTile instanceof BitmapDrawable) { final Bitmap bitmap = ((BitmapDrawable) mLoadingTile).getBitmap(); if (bitmap != null) { bitmap.recycle(); } } } if (mLoadingTile instanceof ReusableBitmapDrawable) BitmapPool.getInstance().returnDrawableToPool((ReusableBitmapDrawable) mLoadingTile); } mLoadingTile = null; if (userSelectedLoadingDrawable != null) { // Only recycle if we are running on a project less than 2.3.3 Gingerbread. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) { if (userSelectedLoadingDrawable instanceof BitmapDrawable) { final Bitmap bitmap = ((BitmapDrawable) userSelectedLoadingDrawable).getBitmap(); if (bitmap != null) { bitmap.recycle(); } } } if (userSelectedLoadingDrawable instanceof ReusableBitmapDrawable) BitmapPool.getInstance() .returnDrawableToPool((ReusableBitmapDrawable) userSelectedLoadingDrawable); } userSelectedLoadingDrawable = null; } public int getMinimumZoomLevel() { return mTileProvider.getMinimumZoomLevel(); } public int getMaximumZoomLevel() { return mTileProvider.getMaximumZoomLevel(); } /** Whether to use the network connection if it's available. */ public boolean useDataConnection() { return mTileProvider.useDataConnection(); } /** * Set whether to use the network connection if it's available. * * @param aMode if true use the network connection if it's available. if false don't use the * network connection even if it's available. */ public void setUseDataConnection(final boolean aMode) { mTileProvider.setUseDataConnection(aMode); } @Override protected void draw(Canvas c, MapView osmv, boolean shadow) { if (DEBUGMODE) { Log.d(IMapView.LOGTAG, "onDraw(" + shadow + ")"); } if (shadow) { return; } Projection projection = osmv.getProjection(); // Get the area we are drawing to Rect screenRect = projection.getScreenRect(); // No overflow detected here! Log.d(IMapView.LOGTAG, "BEFORE Rect is " + screenRect.toString() + // mTopLeftMercator.toString()); projection.toMercatorPixels(screenRect.left, screenRect.top, mTopLeftMercator); projection.toMercatorPixels(screenRect.right, screenRect.bottom, mBottomRightMercator); // No overflow detected here! Log.d(IMapView.LOGTAG, "AFTER Rect is " + screenRect.toString() + // mTopLeftMercator.toString() + mBottomRightMercator.toString()); mViewPort.set( mTopLeftMercator.x, mTopLeftMercator.y, mBottomRightMercator.x, mBottomRightMercator.y); // No overflow detected here! Log.d(IMapView.LOGTAG, "AFTER Rect is " + mViewPort.toString()); // Draw the tiles! drawTiles(c, projection, projection.getZoomLevel(), TileSystem.getTileSize(), mViewPort); } /** * This is meant to be a "pure" tile drawing function that doesn't take into account * osmdroid-specific characteristics (like osmdroid's canvas's having 0,0 as the center rather * than the upper-left corner). Once the tile is ready to be drawn, it is passed to * onTileReadyToDraw where custom manipulations can be made before drawing the tile. */ public void drawTiles( final Canvas c, final Projection projection, final int zoomLevel, final int tileSizePx, final Rect viewPort) { mProjection = projection; mTileLooper.loop(c, zoomLevel, tileSizePx, viewPort); // draw a cross at center in debug mode if (DEBUGMODE) { // final GeoPoint center = osmv.getMapCenter(); final Point centerPoint = new Point(viewPort.centerX(), viewPort.centerY()); c.drawLine(centerPoint.x, centerPoint.y - 9, centerPoint.x, centerPoint.y + 9, mDebugPaint); c.drawLine(centerPoint.x - 9, centerPoint.y, centerPoint.x + 9, centerPoint.y, mDebugPaint); } } private final TileLooper mTileLooper = new TileLooper() { @Override public void initialiseLoop(final int pZoomLevel, final int pTileSizePx) { // make sure the cache is big enough for all the tiles final int numNeeded = (mLowerRight.y - mUpperLeft.y + 1) * (mLowerRight.x - mUpperLeft.x + 1); mTileProvider.ensureCapacity(numNeeded + mOvershootTileCache); } @Override public void handleTile( final Canvas pCanvas, final int pTileSizePx, final MapTile pTile, final int pX, final int pY) { // no overflow detected here Log.d(IMapView.LOGTAG, "handleTile " + pTile.toString() + // ","+pX + "," + pY); Drawable currentMapTile = mTileProvider.getMapTile(pTile); boolean isReusable = currentMapTile instanceof ReusableBitmapDrawable; final ReusableBitmapDrawable reusableBitmapDrawable = isReusable ? (ReusableBitmapDrawable) currentMapTile : null; if (currentMapTile == null) { currentMapTile = getLoadingTile(); } if (currentMapTile != null) { mTilePoint.set(pX * pTileSizePx, pY * pTileSizePx); mTileRect.set( mTilePoint.x, mTilePoint.y, mTilePoint.x + pTileSizePx, mTilePoint.y + pTileSizePx); if (isReusable) { reusableBitmapDrawable.beginUsingDrawable(); } try { if (isReusable && !((ReusableBitmapDrawable) currentMapTile).isBitmapValid()) { currentMapTile = getLoadingTile(); isReusable = false; } onTileReadyToDraw(pCanvas, currentMapTile, mTileRect); } finally { if (isReusable) reusableBitmapDrawable.finishUsingDrawable(); } } if (DEBUGMODE) { mTileRect.set( pX * pTileSizePx, pY * pTileSizePx, pX * pTileSizePx + pTileSizePx, pY * pTileSizePx + pTileSizePx); pCanvas.drawText( pTile.toString(), mTileRect.left + 1, mTileRect.top + mDebugPaint.getTextSize(), mDebugPaint); pCanvas.drawLine( mTileRect.left, mTileRect.top, mTileRect.right, mTileRect.top, mDebugPaint); pCanvas.drawLine( mTileRect.left, mTileRect.top, mTileRect.left, mTileRect.bottom, mDebugPaint); } } @Override public void finaliseLoop() {} }; protected void onTileReadyToDraw( final Canvas c, final Drawable currentMapTile, final Rect tileRect) { currentMapTile.setColorFilter(currentColorFilter); mProjection.toPixelsFromMercator(tileRect.left, tileRect.top, mTilePointMercator); tileRect.offsetTo(mTilePointMercator.x, mTilePointMercator.y); currentMapTile.setBounds(tileRect); currentMapTile.draw(c); } @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) { final SubMenu mapMenu = pMenu .addSubMenu(0, MENU_MAP_MODE + pMenuIdOffset, Menu.NONE, R.string.map_mode) .setIcon(R.drawable.ic_menu_mapmode); for (int a = 0; a < TileSourceFactory.getTileSources().size(); a++) { final ITileSource tileSource = TileSourceFactory.getTileSources().get(a); mapMenu.add( MENU_MAP_MODE + pMenuIdOffset, MENU_TILE_SOURCE_STARTING_ID + a + pMenuIdOffset, Menu.NONE, tileSource.name()); } mapMenu.setGroupCheckable(MENU_MAP_MODE + pMenuIdOffset, true, true); if (ctx != null) { final String title = ctx.getString( pMapView.useDataConnection() ? R.string.set_mode_offline : R.string.set_mode_online); final Drawable icon = ctx.getResources().getDrawable(R.drawable.ic_menu_offline); pMenu.add(0, MENU_OFFLINE + pMenuIdOffset, Menu.NONE, title).setIcon(icon); } return true; } @Override public boolean onPrepareOptionsMenu( final Menu pMenu, final int pMenuIdOffset, final MapView pMapView) { final int index = TileSourceFactory.getTileSources().indexOf(pMapView.getTileProvider().getTileSource()); if (index >= 0) { pMenu.findItem(MENU_TILE_SOURCE_STARTING_ID + index + pMenuIdOffset).setChecked(true); } pMenu .findItem(MENU_OFFLINE + pMenuIdOffset) .setTitle( pMapView.useDataConnection() ? R.string.set_mode_offline : R.string.set_mode_online); return true; } @Override public boolean onOptionsItemSelected( final MenuItem pItem, final int pMenuIdOffset, final MapView pMapView) { final int menuId = pItem.getItemId() - pMenuIdOffset; if ((menuId >= MENU_TILE_SOURCE_STARTING_ID) && (menuId < MENU_TILE_SOURCE_STARTING_ID + TileSourceFactory.getTileSources().size())) { pMapView.setTileSource( TileSourceFactory.getTileSources().get(menuId - MENU_TILE_SOURCE_STARTING_ID)); return true; } else if (menuId == MENU_OFFLINE) { final boolean useDataConnection = !pMapView.useDataConnection(); pMapView.setUseDataConnection(useDataConnection); return true; } else { return false; } } public int getLoadingBackgroundColor() { return mLoadingBackgroundColor; } /** * Set the color to use to draw the background while we're waiting for the tile to load. * * @param pLoadingBackgroundColor the color to use. If the value is {@link Color#TRANSPARENT} then * there will be no loading tile. */ public void setLoadingBackgroundColor(final int pLoadingBackgroundColor) { if (mLoadingBackgroundColor != pLoadingBackgroundColor) { mLoadingBackgroundColor = pLoadingBackgroundColor; clearLoadingTile(); } } public int getLoadingLineColor() { return mLoadingLineColor; } public void setLoadingLineColor(final int pLoadingLineColor) { if (mLoadingLineColor != pLoadingLineColor) { mLoadingLineColor = pLoadingLineColor; clearLoadingTile(); } } private Drawable getLoadingTile() { if (userSelectedLoadingDrawable != null) return userSelectedLoadingDrawable; if (mLoadingTile == null && mLoadingBackgroundColor != Color.TRANSPARENT) { try { final int tileSize = mTileProvider.getTileSource() != null ? mTileProvider.getTileSource().getTileSizePixels() : 256; final Bitmap bitmap = Bitmap.createBitmap(tileSize, tileSize, Bitmap.Config.ARGB_8888); final Canvas canvas = new Canvas(bitmap); final Paint paint = new Paint(); canvas.drawColor(mLoadingBackgroundColor); paint.setColor(mLoadingLineColor); paint.setStrokeWidth(0); final int lineSize = tileSize / 16; for (int a = 0; a < tileSize; a += lineSize) { canvas.drawLine(0, a, tileSize, a, paint); canvas.drawLine(a, 0, a, tileSize, paint); } mLoadingTile = new BitmapDrawable(bitmap); } catch (final OutOfMemoryError e) { Log.e(IMapView.LOGTAG, "OutOfMemoryError getting loading tile"); System.gc(); } catch (final NullPointerException e) { Log.e(IMapView.LOGTAG, "NullPointerException getting loading tile"); System.gc(); } } return mLoadingTile; } private void clearLoadingTile() { final BitmapDrawable bitmapDrawable = mLoadingTile; mLoadingTile = null; // Only recycle if we are running on a project less than 2.3.3 Gingerbread. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) { if (bitmapDrawable != null) { bitmapDrawable.getBitmap().recycle(); } } } /** * Set this to overshoot the tile cache. By default the TilesOverlay only creates a cache large * enough to hold the minimum number of tiles necessary to draw to the screen. Setting this value * will allow you to overshoot the tile cache and allow more tiles to be cached. This increases * the memory usage, but increases drawing performance. * * @param overshootTileCache the number of tiles to overshoot the tile cache by */ public void setOvershootTileCache(int overshootTileCache) { mOvershootTileCache = overshootTileCache; } /** * Get the tile cache overshoot value. * * @return the number of tiles to overshoot tile cache */ public int getOvershootTileCache() { return mOvershootTileCache; } /** * sets the current color filter, which is applied to tiles before being drawn to the screen. Use * this to enable night mode or any other tile rendering adjustment as necessary. use null to * clear. INVERT_COLORS provides color inversion for convenience and to support the previous night * mode * * @param filter * @since 5.1 */ public void setColorFilter(ColorFilter filter) { this.currentColorFilter = filter; } }