2012-10-02 7 views
9

Ich fragte mich, ob es möglich war, so etwas zu tun. Ich weiß, dass man etwas vom bestehenden Code ändern müsste, um das durchzuziehen, aber ich fragte mich, ob irgendjemand eine Richtung hatte, wohin er schauen und wie er das machen sollte.OSMDroid Loading Benutzerdefinierte Offline-Kacheln Von Assets Folder

Ich platziere ein paar benutzerdefinierte Kacheln in einem bestimmten Bereich auf der Karte als Ersatz für OSM-Kacheln Anbieter, aber müssen sie im/assets/Ordner gespeichert werden. Irgendwelche Ideen?

Antwort

9

Ich benutze die nexts-Klassen, um das zu tun.

import java.io.InputStream; 

import org.osmdroid.ResourceProxy.string; 
import org.osmdroid.tileprovider.util.StreamUtils; 

import android.content.res.AssetManager; 
import android.graphics.drawable.Drawable; 

public class AssetsTileSource extends CustomBitmapTileSourceBase { 
     private final AssetManager mAssetManager; 

     public AssetsTileSource(final AssetManager assetManager, final String aName, final string aResourceId, 
         final int aZoomMinLevel, final int aZoomMaxLevel, final int aTileSizePixels, 
         final String aImageFilenameEnding) { 
       super(aName, aResourceId, aZoomMinLevel, aZoomMaxLevel, aTileSizePixels, aImageFilenameEnding); 
       mAssetManager = assetManager; 
     } 

     @Override 
     public Drawable getDrawable(final String aFilePath) { 
       InputStream inputStream = null; 
       try { 
         inputStream = mAssetManager.open(aFilePath); 
         if (inputStream != null) { 
           final Drawable drawable = getDrawable(inputStream); 
           return drawable; 
         } 
       } catch (final Throwable e) { 
         // Tile does not exist in assets folder. 
         // Ignore silently 
       } finally { 
         if (inputStream != null) { 
           StreamUtils.closeStream(inputStream); 
         } 
       } 

       return null; 
     } 
} 

MapTileFileAssetsProvider.java

public class MapTileFileAssetsProvider extends MapTileModuleProviderBase { 

      protected ITileSource mTileSource; 

      public MapTileFileAssetsProvider(final ITileSource pTileSource) { 
        super(OpenStreetMapTileProviderConstants.NUMBER_OF_TILE_FILESYSTEM_THREADS, OpenStreetMapTileProviderConstants.TILE_FILESYSTEM_MAXIMUM_QUEUE_SIZE); 

        mTileSource = pTileSource; 
      } 

      @Override 
      public boolean getUsesDataConnection() { 
        return false; 
      } 

      @Override 
      protected String getName() { 
        return "Assets Folder Provider"; 
      } 

      @Override 
      protected String getThreadGroupName() { 
        return "assetsfolder"; 
      } 

      @Override 
      protected Runnable getTileLoader() { 
        return new TileLoader(); 
      } 

      @Override 
      public int getMinimumZoomLevel() { 
        return mTileSource != null ? mTileSource.getMinimumZoomLevel() : MAXIMUM_ZOOMLEVEL; 
      } 

      @Override 
      public int getMaximumZoomLevel() { 
        return mTileSource != null ? mTileSource.getMaximumZoomLevel() : MINIMUM_ZOOMLEVEL; 
      } 

      @Override 
      public void setTileSource(final ITileSource pTileSource) { 
        mTileSource = pTileSource; 
      } 

      private class TileLoader extends MapTileModuleProviderBase.TileLoader { 

        @Override 
        public Drawable loadTile(final MapTileRequestState pState) throws CantContinueException { 

          if (mTileSource == null) { 
            return null; 
          } 

          final MapTile pTile = pState.getMapTile(); 
          String path = mTileSource.getTileRelativeFilenameString(pTile); 

          Drawable drawable; 
          try { 
            drawable = mTileSource.getDrawable(path); 
          } catch (final LowMemoryException e) { 
            // low memory so empty the queue 
            throw new CantContinueException(e); 
          } 

          return drawable; 
        } 
      } 
    } 

Und

import java.io.File; 
import java.io.InputStream; 
import java.util.Random; 

import org.osmdroid.ResourceProxy; 
import org.osmdroid.ResourceProxy.string; 
import org.osmdroid.tileprovider.ExpirableBitmapDrawable; 
import org.osmdroid.tileprovider.MapTile; 
import org.osmdroid.tileprovider.constants.OpenStreetMapTileProviderConstants; 
import org.osmdroid.tileprovider.tilesource.ITileSource; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 

import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.drawable.Drawable; 

public abstract class CustomBitmapTileSourceBase implements ITileSource, 
       OpenStreetMapTileProviderConstants { 

     private static final Logger logger = LoggerFactory.getLogger(CustomBitmapTileSourceBase.class); 

     private static int globalOrdinal = 0; 

     private final int mMinimumZoomLevel; 
     private final int mMaximumZoomLevel; 

     private final int mOrdinal; 
     protected final String mName; 
     protected final String mImageFilenameEnding; 
     protected final Random random = new Random(); 

     private final int mTileSizePixels; 

     private final string mResourceId; 

     public CustomBitmapTileSourceBase(final String aName, final string aResourceId, 
         final int aZoomMinLevel, final int aZoomMaxLevel, final int aTileSizePixels, 
         final String aImageFilenameEnding) { 
       mResourceId = aResourceId; 
       mOrdinal = globalOrdinal++; 
       mName = aName; 
       mMinimumZoomLevel = aZoomMinLevel; 
       mMaximumZoomLevel = aZoomMaxLevel; 
       mTileSizePixels = aTileSizePixels; 
       mImageFilenameEnding = aImageFilenameEnding; 
     } 

     @Override 
     public int ordinal() { 
       return mOrdinal; 
     } 

     @Override 
     public String name() { 
       return mName; 
     } 

     public String pathBase() { 
       return mName; 
     } 

     public String imageFilenameEnding() { 
       return mImageFilenameEnding; 
     } 

     @Override 
     public int getMinimumZoomLevel() { 
       return mMinimumZoomLevel; 
     } 

     @Override 
     public int getMaximumZoomLevel() { 
       return mMaximumZoomLevel; 
     } 

     @Override 
     public int getTileSizePixels() { 
       return mTileSizePixels; 
     } 

     @Override 
     public String localizedName(final ResourceProxy proxy) { 
       return proxy.getString(mResourceId); 
     } 

     @Override 
     public Drawable getDrawable(final String aFilePath) { 
       try { 
         // default implementation will load the file as a bitmap and create 
         // a BitmapDrawable from it 
         final Bitmap bitmap = BitmapFactory.decodeFile(aFilePath); 
         if (bitmap != null) { 
           return new ExpirableBitmapDrawable(bitmap); 
         } else { 
           // if we couldn't load it then it's invalid - delete it 
           try { 
             new File(aFilePath).delete(); 
           } catch (final Throwable e) { 
             logger.error("Error deleting invalid file: " + aFilePath, e); 
           } 
         } 
       } catch (final OutOfMemoryError e) { 
         logger.error("OutOfMemoryError loading bitmap: " + aFilePath); 
         System.gc(); 
       } 
       return null; 
     } 

     @Override 
     public String getTileRelativeFilenameString(final MapTile tile) { 
       final StringBuilder sb = new StringBuilder(); 
       sb.append(pathBase()); 
       sb.append('/'); 
       sb.append(tile.getX()); 
       sb.append('_'); 
       sb.append(tile.getY()); 
       sb.append('_'); 
       sb.append(tile.getZoomLevel()); 
       sb.append(imageFilenameEnding()); 
       return sb.toString(); 
     } 


     @Override 
     public Drawable getDrawable(final InputStream aFileInputStream) { 
       try { 
         // default implementation will load the file as a bitmap and create 
         // a BitmapDrawable from it 
         final Bitmap bitmap = BitmapFactory.decodeStream(aFileInputStream); 
         if (bitmap != null) { 
           return new ExpirableBitmapDrawable(bitmap); 
         } 
         System.gc(); 
       } catch (final OutOfMemoryError e) { 
         logger.error("OutOfMemoryError loading bitmap"); 
         System.gc(); 
         //throw new LowMemoryException(e); 
       } 
       return null; 
     } 

     public final class LowMemoryException extends Exception { 
       private static final long serialVersionUID = 146526524087765134L; 

       public LowMemoryException(final String pDetailMessage) { 
         super(pDetailMessage); 
       } 

       public LowMemoryException(final Throwable pThrowable) { 
         super(pThrowable); 
       } 
     } 
} 

Ändern Methode getTileRelativeFilenameString() yout Fliesen zu bekommen (i das nächste Format verwenden: x_y_zoom.png)

Beispiel :

mapView = new MapView(getApplicationContext(), 256); 
mapView.setClickable(true); 
mapView.setTag("Mapa"); 
mapView.setTileSource(TileSourceFactory.MAPNIK); 
mapView.setMultiTouchControls(true); 
mapView.setUseDataConnection(true); 

MapTileModuleProviderBase moduleProvider = 
    new MapTileFileAssetsProvider(ASSETS_TILE_SOURCE); 
SimpleRegisterReceiver simpleReceiver = 
    new SimpleRegisterReceiver(getApplicationContext()); 
MapTileProviderArray tileProviderArray = 
    new MapTileProviderArray(ASSETS_TILE_SOURCE, simpleReceiver, 
     new MapTileModuleProviderBase[] { moduleProvider }); 
TilesOverlay tilesOverlay = 
    new TilesOverlay(tileProviderArray, getApplicationContext()); 

mapView.getOverlays().add(tilesOverlay); 
+0

Wie kann man Koordinaten für die Navigation zusammen mit dem Zoom in/out anwenden? – Zyoo

+0

Die Klassen sind gut zu verwenden, aber das Beispiel ist nicht vollständig. Für ASSETS_TILE_SOURCE ist es keine Konstante, sondern ein neues Objekt aus der Klasse AssetsTileSource. Mit dem erstellten tileProviderArray verwende ich beim Erstellen der mapView den Konstruktor, der das als Argument verwenden kann, z. MapView mapView = neue MapView (256, resourceProxy, tileProviderArray). Es ist für mich intuitiver, das MapView mit der Kachelquelle zu verknüpfen, anstatt es den Überlagerungen zuzuordnen ... vielleicht bin ich es nur. – Yam

+0

@jzafrilla dank der Probe. Wird '' Bitmap.decodeStream() 'nicht' GC_FOR_ALLOC' im Hauptthread der Benutzeroberfläche verursachen? – zIronManBox

3

Anstatt direkt von Assets zu lesen, kopiere/deploy die gepackten Maptiles (nach Osmdroid Map Kacheln Verzeichnisstruktur Format) in Osmdroid Maptiles Verzeichnis und dann 3 Kachel-Provider, Archiv, Cache und Online-Provider.

public class MapTileProviderAssets extends MapTileProviderArray 
     implements IMapTileProviderCallback { 

    private static final String LOG_TAG = "MapTileProviderAssets"; 

    private static final String ASSETS_MAP_DIRECTORY = "map"; 
    private static final String SDCARD_PATH = Environment.getExternalStorageDirectory().getPath(); 
    private static final String OSMDROID_MAP_FILE_SOURCE_DIRECTORY = "osmdroid"; 
    private static final String OSMDROID_MAP_FILE_SOURCE_DIRECTORY_PATH = 
      SDCARD_PATH + "/" + OSMDROID_MAP_FILE_SOURCE_DIRECTORY; 

    public MapTileProviderAssets(final Context pContext) { 
     this(pContext, TileSourceFactory.DEFAULT_TILE_SOURCE); 
    } 

    public MapTileProviderAssets(final Context pContext, final ITileSource pTileSource) { 
     this(pContext, new SimpleRegisterReceiver(pContext), 
       new NetworkAvailabliltyCheck(pContext), pTileSource); 

    } 

    public MapTileProviderAssets(final Context pContext, final IRegisterReceiver pRegisterReceiver, 
           final INetworkAvailablityCheck aNetworkAvailablityCheck, 
           final ITileSource pTileSource) { 
     super(pTileSource, pRegisterReceiver); 

     final TileWriter tileWriter = new TileWriter(); 

     // copy assets delivered in apk into osmdroid map source dir 
     // load zip archive first, then cache, then online 
     final List<String> zipArchivesRelativePathInAssets = 
       listArchives(pContext.getAssets(), ASSETS_MAP_DIRECTORY); 
     for (final String zipFileRelativePathInAssets : zipArchivesRelativePathInAssets) { 
      final String copiedFilePath = copyAssetFile(
        pContext.getAssets(), zipFileRelativePathInAssets, 
        OSMDROID_MAP_FILE_SOURCE_DIRECTORY); 
      Log.d(LOG_TAG, String.format(
        "Archive zip file copied into map source directory %s", copiedFilePath)); 
     } 
     // list zip files in map archive directory 
     final Set<String> setZipFileArchivesPath = new HashSet<String>(); 
     FileTools.listFiles(setZipFileArchivesPath, new File(
       OSMDROID_MAP_FILE_SOURCE_DIRECTORY_PATH), ".zip", true); 
     final Set<IArchiveFile> setZipFileArchives = new HashSet<IArchiveFile>(); 
     for (final String zipFileArchivesPath : setZipFileArchivesPath) { 
      final File zipfile = new File(zipFileArchivesPath); 
      final IArchiveFile archiveFile = ArchiveFileFactory.getArchiveFile(zipfile); 
      if (archiveFile != null) { 
       setZipFileArchives.add(archiveFile); 
      } 
      setZipFileArchives.add(archiveFile); 
      Log.d(LOG_TAG, String.format(
        "Archive zip file %s added to map source ", zipFileArchivesPath)); 
     } 

     final MapTileFileArchiveProvider archiveProvider; 
     Log.d(LOG_TAG, String.format(
       "%s archive zip files will be used as source", setZipFileArchives.size())); 
     if (setZipFileArchives.size() > 0) { 
      final IArchiveFile[] pArchives = 
        setZipFileArchives.toArray(new IArchiveFile[setZipFileArchives.size()]); 
      archiveProvider = new MapTileFileArchiveProvider(
        pRegisterReceiver, pTileSource, pArchives); 
     } else { 
      archiveProvider = new MapTileFileArchiveProvider(
        pRegisterReceiver, pTileSource); 
     } 
     mTileProviderList.add(archiveProvider); 

     // cache 
     final MapTileFilesystemProvider fileSystemProvider = 
       new MapTileFilesystemProvider(pRegisterReceiver, pTileSource); 
     mTileProviderList.add(fileSystemProvider); 

     // online tiles 
     final MapTileDownloader downloaderProvider = 
       new MapTileDownloader(pTileSource, tileWriter, aNetworkAvailablityCheck); 
     mTileProviderList.add(downloaderProvider); 
    } 

    public static List<String> listArchives(final AssetManager assetManager, 
              final String subDirectory) { 
     final List<String> listArchives = new ArrayList<String>(); 
     try { 
      final String[] lstFiles = assetManager.list(subDirectory); 
      if (lstFiles != null && lstFiles.length > 0) { 
       for (final String file : lstFiles) { 
        if (isZip(file)) { 
         listArchives.add(subDirectory + "/" + file); 
        } 
        // filter files (xxxxx.xxx format) and parse only directories, 
        // with out this all files are parsed and 
        // the process is VERY slow 
        // WARNNING: we could have directories with dot for versioning 
        else if (isDirectory(file)) {// (file.lastIndexOf(".") != (file.length() - 4)) { 
         listArchives(assetManager, subDirectory + "/" + file); 
        } 
       } 
      } 
     } catch (final IOException e) { 
      Log.w(LOG_TAG, String.format("List error: can't list %s, exception %s", 
        subDirectory, Log.getStackTraceString(e))); 
     } catch (final Exception e) { 
      Log.w(LOG_TAG, String.format("List error: can't list %s, exception %s", 
        subDirectory, Log.getStackTraceString(e))); 
     } 
     return listArchives; 
    } 

    private static boolean isZip(final String file) { 
     return file.endsWith(".zip"); 
    } 

    private static boolean isDirectory(final String file) { 
     return file.lastIndexOf(".") != (file.length() - 4); 
    } 

    private static String copyAssetFile(final AssetManager assetManager, 
             final String assetRelativePath, 
             final String destinationDirectoryOnSdcard) { 
     InputStream in = null; 
     OutputStream out = null; 
     final String newfilePath = SDCARD_PATH + "/" + 
       destinationDirectoryOnSdcard + "/" + assetRelativePath; 
     final File newFile = new File(newfilePath); 
     // copy file only if it doesn't exist yet 
     if (!newFile.exists()) { 
      Log.d(LOG_TAG, String.format(
        "Copy %s map archive in assets into %s", assetRelativePath, newfilePath)); 
      try { 
       final File directory = newFile.getParentFile(); 
       if (!directory.exists()) { 
        if (directory.mkdirs()) { 
         // Log.d(LOG_TAG, "Directory created: " + directory.getAbsolutePath()); 
        } 
       } 
       in = assetManager.open(assetRelativePath); 
       out = new FileOutputStream(newfilePath); 
       copyFile(in, out); 
       in.close(); 
       in = null; 
       out.flush(); 
       out.close(); 
       out = null; 
      } catch (final Exception e) { 
       Log.e(LOG_TAG, "Exception during copyAssetFile: " + Log.getStackTraceString(e)); 
      } 
     } 
     return newfilePath; 
    } 

    private static void copyFile(final InputStream in, final OutputStream out) throws IOException { 
     final byte[] buffer = new byte[1024]; 
     int read; 
     while ((read = in.read(buffer)) != -1) { 
      out.write(buffer, 0, read); 
     } 
    } 

} 
+0

Ihr Code ist unvollständig. Es gibt eine Methode, auf die nicht zugegriffen werden kann: FileTools.listFiles. Ich habe es kommentiert und ich habe diese Zeile hinzugefügt, um es zu ersetzen: setZipFileArchivesPath.add ("name_of_your_map_file.zip"); .... Ich bin mir noch nicht sicher, ob ich die Zeichenkette .zip oder nicht als Name der Datei einschließen muss. Ich werde nach mir selbst suchen. –

+0

Ja, der Code von FileTools.listFiles ist nicht vorhanden, weil es einfach zu implementieren ist. Diese Methode listet einfach alle ZIP-Dateien des eingegebenen Kartenarchivverzeichnisses auf. –