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  .  j av  a 2s.c o  m*/
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;

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 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;

/**
 * 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>
 * <p/>
 * Important note 1: <br>
 * These methods only make sense for a MapView using an OnlineTileSourceBase:
 * bitmap tiles downloaded from urls. <br>
 * <p/>
 * 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();
    }

    /**
     * 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();
    }

    /**
     * @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;
    }

    /**
     * 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

    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

}




Java Source Code List

com.ilm.sandwich.BackgroundService.java
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.StartChooser.java
com.ilm.sandwich.TouchableWrapper.java
com.ilm.sandwich.tools.Core.java
com.ilm.sandwich.tools.HttpRequests.java
com.ilm.sandwich.tools.Locationer.java
com.ilm.sandwich.tools.MapDownload.java
com.ilm.sandwich.tools.MyItemizedOverlay.java
com.ilm.sandwich.tools.PlacesAutoComplete.java
com.ilm.sandwich.tools.PlacesTextSearch.java
com.ilm.sandwich.tools.Statistics.java
com.ilm.sandwich.tools.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