2012-10-02 15 views
9

Zastanawiałem się, czy można było coś takiego zrobić. Wiem, że należałoby zmodyfikować część istniejącego kodu, aby to zrobić, ale zastanawiałem się, czy ktoś ma jakiś kierunek, gdzie szukać i jak to zrobić.OSMDroid Ładowanie niestandardowych płytek offline z folderu zasobów

Umieszczam kilka niestandardowych płytek w określonym obszarze na mapie jako zamiennik dla dostawców kafelków OSM, ale trzeba je przechowywać w folderze/assets /. Jakieś pomysły?

Odpowiedz

9

Używam do tego następnych zajęć.

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

I

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

Zmienić sposób getTileRelativeFilenameString(), aby uzyskać yout płytek (ja używam następnego formatu: x_y_zoom.png)

Przykład :

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

Jak zastosować współrzędne dla nawigacji w tandemie z powiększaniem/pomniejszaniem? – Zyoo

+0

Klasy są dobre w użyciu, ale przykład nie jest kompletny. W przypadku ASSETS_TILE_SOURCE nie jest to stała, ale obiekt nowy z klasy AssetsTileSource. Po utworzeniu tileProviderArray używam podczas tworzenia mapView za pomocą konstruktora, który może przyjąć to jako argument, np. MapView mapView = new MapView (to, 256, resourceProxy, tileProviderArray). Bardziej intuicyjne jest dla mnie, aby MapView kojarzył się ze źródłem kafli, a nie kojarzyć go z nakładkami ... może to tylko ja. – Yam

+0

@jzafrilla dzięki próbce. Czy 'Bitmap.decodeStream()' nie spowoduje 'GC_FOR_ALLOC' na głównym wątku UI? – zIronManBox

3

Zamiast czytać bezpośrednio z zasobów, kopiuję/wdrażam maptiles spakowane (zgodnie z formatem struktury katalogów płytek osmdroid map) do katalogu mapmil OSMDroid, a następnie zadeklaruj 3 dostawców płytek, archiwum, pamięć podręczną i dostawcę online.

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

Twój kod jest niekompletny. Jest jedna metoda niedostępna: to FileTools.listFiles. Skomentowałem to i dodałem tę linię, aby ją zastąpić: setZipFileArchivesPath.add ("name_of_your_map_file.zip"); .... Nie jestem pewien, czy muszę dołączyć ciąg .zip, czy nie jako nazwę pliku. Sprawdzę dla siebie. –

+0

Tak kod pliku FileTools.listFiles nie jest obecny, ponieważ jest łatwy do wdrożenia. Ta metoda po prostu wyświetla wszystkie pliki zip we wpisanym katalogu archiwum mapy. –

Powiązane problemy