Android Open Source - SmartNavi Cache Manager






From Project

Back to project page SmartNavi.

License

The source code is released under:

Apache License

If you think the Android project SmartNavi listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package org.osmdroid.bonuspack.cachemanager;
/*from w w  w  .  java2 s . c o m*/
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.UnknownHostException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.osmdroid.bonuspack.utils.BonusPackHelper;
import org.osmdroid.http.HttpClientFactory;
import org.osmdroid.tileprovider.MapTile;
import org.osmdroid.tileprovider.MapTileProviderBase;
import org.osmdroid.tileprovider.constants.OpenStreetMapTileProviderConstants;
import org.osmdroid.tileprovider.modules.TileWriter;
import org.osmdroid.tileprovider.tilesource.ITileSource;
import org.osmdroid.tileprovider.tilesource.OnlineTileSourceBase;
import org.osmdroid.tileprovider.util.StreamUtils;
import org.osmdroid.util.BoundingBoxE6;
import org.osmdroid.util.MyMath;
import org.osmdroid.views.MapView;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.graphics.Point;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

/**
 * Provides various methods for managing the local filesystem cache of osmdroid tiles: <br>
 * - Dowloading of tiles inside a specified area, <br>
 * - Cleaning of tiles inside a specified area,<br>
 * - Information about cache capacity and current cache usage. <br>
 * 
 * Important note 1: <br>
 * These methods only make sense for a MapView using an OnlineTileSourceBase: 
 * bitmap tiles downloaded from urls. <br>
 * 
 * Important note 2 - about Bulk Downloading:<br>
 * When using OSM Mapnik tile server as the tile source, take care about OSM Tile usage policy 
 * (http://wiki.openstreetmap.org/wiki/Tile_usage_policy). 
 * Do not let to end-users the ability to download significant areas of tiles. <br>
 * 
 * @author M.Kergall
 *
 */
public class CacheManager {
  
  protected final MapTileProviderBase mTileProvider;
  protected final TileWriter mTileWriter;
  protected final MapView mMapView;
  
  public CacheManager(final MapView mapView){
    mTileProvider = mapView.getTileProvider();
    mTileWriter = new TileWriter();
    mMapView = mapView;
  }
  
  public Point getMapTileFromCoordinates(final double aLat, final double aLon, final int zoom){
    final int y = (int) Math.floor((1 - Math.log(Math.tan(aLat * Math.PI / 180) + 1 / Math.cos(aLat * Math.PI / 180)) / Math.PI) / 2 * (1 << zoom));
    final int x = (int) Math.floor((aLon + 180) / 360 * (1 << zoom));
    return new Point(x, y);
  }

  public File getFileName(ITileSource tileSource, MapTile tile){
    final File file = new File(OpenStreetMapTileProviderConstants.TILE_PATH_BASE,
        tileSource.getTileRelativeFilenameString(tile) + OpenStreetMapTileProviderConstants.TILE_PATH_EXTENSION);
    return file;
  }
  
  /** 
   * @return true if success, false if error 
   */
  public boolean loadTile(OnlineTileSourceBase tileSource, MapTile tile){
    //check if file is already downloaded:
    File file = getFileName(tileSource, tile);
    if (file.exists()){
      return true;
    }
    
    InputStream in = null;
    OutputStream out = null;
    try {
      final String tileURLString = tileSource.getTileURLString(tile);
  
      final HttpClient client = HttpClientFactory.createHttpClient();
      final HttpUriRequest head = new HttpGet(tileURLString);
      final HttpResponse response = client.execute(head);
  
      // Check to see if we got success
      final org.apache.http.StatusLine line = response.getStatusLine();
      if (line.getStatusCode() != 200) {
        Log.w(BonusPackHelper.LOG_TAG, "Problem downloading MapTile: " + tile + " HTTP response: " + line);
        return false;
      }
  
      final HttpEntity entity = response.getEntity();
      if (entity == null) {
        Log.w(BonusPackHelper.LOG_TAG, "No content downloading MapTile: " + tile);
        return false;
      }
      in = entity.getContent();
  
      final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
      out = new BufferedOutputStream(dataStream, StreamUtils.IO_BUFFER_SIZE);
      StreamUtils.copy(in, out);
      out.flush();
      final byte[] data = dataStream.toByteArray();
      final ByteArrayInputStream byteStream = new ByteArrayInputStream(data);
  
      // Save the data to the filesystem cache
      mTileWriter.saveFile(tileSource, tile, byteStream);
      byteStream.reset();
  
      //final Drawable result = tileSource.getDrawable(byteStream);
      return true;
    } catch (final UnknownHostException e) {
      // no network connection 
      Log.w(BonusPackHelper.LOG_TAG, "UnknownHostException downloading MapTile: " + tile + " : " + e);
    } catch (final FileNotFoundException e) {
      Log.w(BonusPackHelper.LOG_TAG, "Tile not found: " + tile + " : " + e);
    } catch (final IOException e) {
      Log.w(BonusPackHelper.LOG_TAG, "IOException downloading MapTile: " + tile + " : " + e);
    } catch (final Throwable e) {
      Log.e(BonusPackHelper.LOG_TAG, "Error downloading MapTile: " + tile, e);
    } finally {
      StreamUtils.closeStream(in);
      StreamUtils.closeStream(out);
    }

    return false;
  }
  
  /** @return the theoretical number of tiles in the specified area */
  public int possibleTilesInArea(BoundingBoxE6 bb, final int zoomMin, final int zoomMax){
    int total = 0;
    for (int zoomLevel=zoomMin; zoomLevel<=zoomMax; zoomLevel++){
      Point mLowerRight = getMapTileFromCoordinates(bb.getLatSouthE6()*1E-6, bb.getLonEastE6()*1E-6, zoomLevel);
      Point mUpperLeft = getMapTileFromCoordinates(bb.getLatNorthE6()*1E-6, bb.getLonWestE6()*1E-6, zoomLevel);
      int y = mLowerRight.y - mUpperLeft.y + 1;
      int x = mLowerRight.x - mUpperLeft.x +1;
      int nbTilesForZoomLevel = x * y;
      total += nbTilesForZoomLevel;
    }
    return total;
  }
  
  protected String zoomMessage(int zoomLevel, int zoomMin, int zoomMax){
    return "Handling zoom level: "+zoomLevel+" (from "+zoomMin+" to "+zoomMax+")";
  }
  
  /**
   * Download in background all tiles of the specified area in osmdroid cache. 
   * @param ctx
   * @param bb
   * @param zoomMin
   * @param zoomMax
   */
  public void downloadAreaAsync(Context ctx, BoundingBoxE6 bb, final int zoomMin, final int zoomMax){
    new DownloadingTask(ctx, bb, zoomMin, zoomMax).execute();
  }
  
  /** generic class for common code related to AsyncTask management */
  protected abstract class CacheManagerTask extends AsyncTask<Object, Integer, Integer>{
    ProgressDialog mProgressDialog;
    int mZoomMin, mZoomMax;
    BoundingBoxE6 mBB;
    Context mCtx;
    
    public CacheManagerTask(Context pCtx, BoundingBoxE6 pBB, final int pZoomMin, final int pZoomMax) {
      mCtx = pCtx;
      mProgressDialog = createProgressDialog(pCtx);
      mBB = pBB;
          mZoomMin = Math.max(pZoomMin, mMapView.getMinZoomLevel());
          mZoomMax = Math.min(pZoomMax, mMapView.getMaxZoomLevel());
      }
    
    protected ProgressDialog createProgressDialog(Context pCtx){
      ProgressDialog pd = new ProgressDialog(pCtx);
      pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
      pd.setCancelable(true);
      pd.setOnCancelListener(new OnCancelListener() {
        @Override public void onCancel(DialogInterface dialog) {
          cancel(true);
        }
          });
      return pd;
    }
    
    @Override protected void onProgressUpdate(final Integer... count){
      //count[0] = tile counter, count[1] = current zoom level
      mProgressDialog.setProgress(count[0]);
      mProgressDialog.setMessage(zoomMessage(count[1], mZoomMin, mZoomMax));
    }
    
  }
  
  protected class DownloadingTask extends CacheManagerTask {
    
    public DownloadingTask(Context pCtx, BoundingBoxE6 pBB, final int pZoomMin, final int pZoomMax) {
      super(pCtx, pBB, pZoomMin, pZoomMax);
      }
    
    @Override protected void onPreExecute(){
      mProgressDialog.setTitle("Downloading tiles");
      mProgressDialog.setMessage(zoomMessage(mZoomMin, mZoomMin, mZoomMax));
      int total = possibleTilesInArea(mBB, mZoomMin, mZoomMax);
      mProgressDialog.setMax(total);
      mProgressDialog.show();
    }
    
    @Override protected void onPostExecute(final Integer errors) {
      if (errors != 0)
        Toast.makeText(mCtx, "Loading completed with " + errors + " errors.", Toast.LENGTH_SHORT).show();
      if (mProgressDialog.isShowing()) {
        mProgressDialog.dismiss();
      }
    }
    
    @Override protected Integer doInBackground(Object... params) {
      int errors = downloadArea();
      return errors;
    }
    
    /** Do the job. No attempt to 
     * @return the number of loading errors. 
     */
    protected int downloadArea(){
      OnlineTileSourceBase tileSource;
      if (mTileProvider.getTileSource() instanceof OnlineTileSourceBase)
        tileSource = (OnlineTileSourceBase)mTileProvider.getTileSource();
      else {
        Log.e(BonusPackHelper.LOG_TAG, "TileSource is not an online tile source");
        return 0;
      }
      
      int tileCounter = 0;
      int errors = 0;
      for (int zoomLevel=mZoomMin; zoomLevel<=mZoomMax; zoomLevel++){
        Point mLowerRight = getMapTileFromCoordinates(mBB.getLatSouthE6()*1E-6, mBB.getLonEastE6()*1E-6, zoomLevel);
        Point mUpperLeft = getMapTileFromCoordinates(mBB.getLatNorthE6()*1E-6, mBB.getLonWestE6()*1E-6, zoomLevel);
        final int mapTileUpperBound = 1 << zoomLevel;
        //Get all the MapTiles from the upper left to the lower right:
        for (int y = mUpperLeft.y; y <= mLowerRight.y; y++) {
          for (int x = mUpperLeft.x; x <= mLowerRight.x; x++) {
            final int tileY = MyMath.mod(y, mapTileUpperBound);
            final int tileX = MyMath.mod(x, mapTileUpperBound);
            final MapTile tile = new MapTile(zoomLevel, tileX, tileY);
            //Drawable currentMapTile = mTileProvider.getMapTile(tile);
            boolean ok = loadTile(tileSource, tile);
            if (!ok)
              errors++;
            tileCounter++;
            if (tileCounter % 20 == 0){
              if (isCancelled())
                return errors;
              publishProgress(tileCounter, zoomLevel);
            }
          }
        }
      }
      return errors;
    }
    
  } //DownloadingTask
  
  /**
   * Remove all cached tiles in the specified area. 
   * @param bb
   * @param zoomMin
   * @param zoomMax
   * @param tileSource
   */
  public void cleanAreaAsync(Context ctx, BoundingBoxE6 bb, int zoomMin, int zoomMax){
    new CleaningTask(ctx, bb, zoomMin, zoomMax).execute();
  }
  
  protected class CleaningTask extends CacheManagerTask {
    
    public CleaningTask(Context pCtx, BoundingBoxE6 pBB, final int pZoomMin, final int pZoomMax) {
      super(pCtx, pBB, pZoomMin, pZoomMax);
      }
    
    @Override protected void onPreExecute(){
      mProgressDialog.setTitle("Cleaning tiles");
      mProgressDialog.setMessage(zoomMessage(mZoomMin, mZoomMin, mZoomMax));
      int total = possibleTilesInArea(mBB, mZoomMin, mZoomMax);
      mProgressDialog.setMax(total);
      mProgressDialog.show();
    }
    
    @Override protected void onPostExecute(final Integer deleted) {
      Toast.makeText(mCtx, "Cleaning completed, " + deleted + " tiles deleted.", Toast.LENGTH_SHORT).show();
      if (mProgressDialog.isShowing()) {
        mProgressDialog.dismiss();
      }
    }
    
    @Override protected Integer doInBackground(Object... params) {
      int errors = cleanArea();
      return errors;
    }
    
    /** Do the job. 
     * @return the number of tiles deleted. 
     */
    protected int cleanArea(){
      ITileSource tileSource = mTileProvider.getTileSource();
      int deleted = 0;
      int tileCounter = 0;
      for (int zoomLevel=mZoomMin; zoomLevel<=mZoomMax; zoomLevel++){
        Point mLowerRight = getMapTileFromCoordinates(mBB.getLatSouthE6()*1E-6, mBB.getLonEastE6()*1E-6, zoomLevel);
        Point mUpperLeft = getMapTileFromCoordinates(mBB.getLatNorthE6()*1E-6, mBB.getLonWestE6()*1E-6, zoomLevel);
        
        final int mapTileUpperBound = 1 << zoomLevel;
        //Get all the MapTiles from the upper left to the lower right:
        for (int y = mUpperLeft.y; y <= mLowerRight.y; y++) {
          for (int x = mUpperLeft.x; x <= mLowerRight.x; x++) {
            final int tileY = MyMath.mod(y, mapTileUpperBound);
            final int tileX = MyMath.mod(x, mapTileUpperBound);
            final MapTile tile = new MapTile(zoomLevel, tileX, tileY);
            File file = getFileName(tileSource, tile);
            if (file.exists()){
              file.delete();
              deleted++;
            }
            tileCounter++;
            if (tileCounter % 1000 == 0){
              if (isCancelled())
                return deleted;
              publishProgress(tileCounter, zoomLevel);
            }
          }
        }
      }
      return deleted;
    }
  } //CleaningTask
  
  /** @return volume currently use in the osmdroid local filesystem cache, in bytes. 
   * Unfortunately, due to lack of visibility in osmdroid classes, we cannot reuse the TileWriter.mUsedCacheSpace
   * Note that this method currently takes a while. 
   * */
  public long currentCacheUsage(){
    return directorySize(OpenStreetMapTileProviderConstants.TILE_PATH_BASE);
  }

  /** @return the capacity of the osmdroid local filesystem cache, in bytes. 
   * This capacity is currently a hard-coded constant inside osmdroid. */
  public long cacheCapacity(){
    return OpenStreetMapTileProviderConstants.TILE_MAX_CACHE_SIZE_BYTES;
  }

  /** @return the total size of a directory and of its whole content, recursively */
  public long directorySize(final File pDirectory) {
    long usedCacheSpace = 0;
    final File[] z = pDirectory.listFiles();
    if (z != null) {
      for (final File file : z) {
        if (file.isFile()) {
          usedCacheSpace += file.length();
        } else if (file.isDirectory()) {
          usedCacheSpace += directorySize(file);
        }
      }
    }
    return usedCacheSpace;
  }

}




Java Source Code List

com.ilm.sandwich.BuildConfig.java
com.ilm.sandwich.Config.java
com.ilm.sandwich.GoogleMapActivity.java
com.ilm.sandwich.Info.java
com.ilm.sandwich.MySupportMapFragment.java
com.ilm.sandwich.OsmMapActivity.java
com.ilm.sandwich.Settings.java
com.ilm.sandwich.Smartgeo.java
com.ilm.sandwich.StartChooser.java
com.ilm.sandwich.TouchableWrapper.java
com.ilm.sandwich.helferklassen.HttpRequests.java
com.ilm.sandwich.helferklassen.Locationer.java
com.ilm.sandwich.helferklassen.MapDownload.java
com.ilm.sandwich.helferklassen.MyItemizedOverlay.java
com.ilm.sandwich.helferklassen.PlacesAutoComplete.java
com.ilm.sandwich.helferklassen.PlacesTextSearch.java
com.ilm.sandwich.helferklassen.Rechnung.java
com.ilm.sandwich.helferklassen.SuggestionsAdapter.java
org.osmdroid.bonuspack.BuildConfig.java
org.osmdroid.bonuspack.BuildConfig.java
org.osmdroid.bonuspack.cachemanager.CacheManager.java
org.osmdroid.bonuspack.clustering.GridMarkerClusterer.java
org.osmdroid.bonuspack.clustering.MarkerClusterer.java
org.osmdroid.bonuspack.clustering.StaticCluster.java
org.osmdroid.bonuspack.kml.ColorStyle.java
org.osmdroid.bonuspack.kml.IconStyle.java
org.osmdroid.bonuspack.kml.KmlDocument.java
org.osmdroid.bonuspack.kml.KmlFeature.java
org.osmdroid.bonuspack.kml.KmlFolder.java
org.osmdroid.bonuspack.kml.KmlGeometry.java
org.osmdroid.bonuspack.kml.KmlGroundOverlay.java
org.osmdroid.bonuspack.kml.KmlLineString.java
org.osmdroid.bonuspack.kml.KmlMultiGeometry.java
org.osmdroid.bonuspack.kml.KmlPlacemark.java
org.osmdroid.bonuspack.kml.KmlPoint.java
org.osmdroid.bonuspack.kml.KmlPolygon.java
org.osmdroid.bonuspack.kml.LineStyle.java
org.osmdroid.bonuspack.kml.Style.java
org.osmdroid.bonuspack.location.FlickrPOIProvider.java
org.osmdroid.bonuspack.location.GeoNamesPOIProvider.java
org.osmdroid.bonuspack.location.GeocoderGisgraphy.java
org.osmdroid.bonuspack.location.GeocoderNominatimOld.java
org.osmdroid.bonuspack.location.GeocoderNominatim.java
org.osmdroid.bonuspack.location.NominatimPOIProvider.java
org.osmdroid.bonuspack.location.POI.java
org.osmdroid.bonuspack.location.PicasaPOIProvider.java
org.osmdroid.bonuspack.mapsforge.GenericMapView.java
org.osmdroid.bonuspack.mapsforge.MapsForgeTileModuleProvider.java
org.osmdroid.bonuspack.mapsforge.MapsForgeTileProvider.java
org.osmdroid.bonuspack.mapsforge.MapsForgeTileSource.java
org.osmdroid.bonuspack.overlays.DefaultInfoWindow.java
org.osmdroid.bonuspack.overlays.ExtendedOverlayItem.java
org.osmdroid.bonuspack.overlays.FolderOverlay.java
org.osmdroid.bonuspack.overlays.GroundOverlay.java
org.osmdroid.bonuspack.overlays.InfoWindow.java
org.osmdroid.bonuspack.overlays.ItemizedOverlayWithBubble.java
org.osmdroid.bonuspack.overlays.MapEventsOverlay.java
org.osmdroid.bonuspack.overlays.MapEventsReceiver.java
org.osmdroid.bonuspack.overlays.MarkerInfoWindow.java
org.osmdroid.bonuspack.overlays.Marker.java
org.osmdroid.bonuspack.overlays.Polygon.java
org.osmdroid.bonuspack.overlays.Polyline.java
org.osmdroid.bonuspack.routing.GoogleRoadManager.java
org.osmdroid.bonuspack.routing.MapQuestRoadManager.java
org.osmdroid.bonuspack.routing.OSRMRoadManager.java
org.osmdroid.bonuspack.routing.RoadLeg.java
org.osmdroid.bonuspack.routing.RoadManager.java
org.osmdroid.bonuspack.routing.RoadNode.java
org.osmdroid.bonuspack.routing.Road.java
org.osmdroid.bonuspack.utils.BonusPackHelper.java
org.osmdroid.bonuspack.utils.DouglasPeuckerReducer.java
org.osmdroid.bonuspack.utils.HttpConnection.java
org.osmdroid.bonuspack.utils.PolylineEncoder.java
org.osmdroid.bonuspack.utils.WebImageCache.java