TileProvider, der lokale Kacheln verwendet

Lesezeit: 6 Minuten

TileProvider der lokale Kacheln verwendet
Gyroskop

Ich möchte die neue verwenden TileProvider Funktionalität der neuesten Android Maps API (v2), um einige benutzerdefinierte Kacheln auf der zu überlagern GoogleMap. Da meine Benutzer jedoch die meiste Zeit nicht über das Internet verfügen, möchte ich die Kacheln in einer ZIP-Datei/Ordnerstruktur auf dem Gerät speichern. Ich werde meine Kacheln mit generieren Maptiler mit geotiffs. Meine Fragen sind:

  1. Was wäre der beste Weg, um die Kacheln auf dem Gerät zu speichern?
  2. Wie würde ich vorgehen, um einen TileProvider zu erstellen, der lokale Kacheln zurückgibt?

  • Außerdem würde ich gerne eine Sache wissen: Bietet Google Map V2 eine Möglichkeit zum Herunterladen/Zwischenspeichern der Kachel? (prntscr.com/3cyiqf) b’cos Ich bin in diesem Fall verwirrt, bedeutet, wenn sie angeben, wie Kacheln mit der TileProvider-Klasse geladen / verwendet werden, sollte es etwas für das Caching / Herunterladen von Kacheln verfügbar sein. Meine eigentliche Anforderung ist, dass ich die Karte gemäß den Anforderungen des Benutzers herunterladen / zwischenspeichern muss. Ich habe bereits OSMDROID lib überprüft, möchte aber nur Google Map v2 verwenden

    – Rajan

    25. April 2014 um 7:15 Uhr


  • @Rajan Ich bin auf das gleiche Problem gestoßen. Es scheint möglich zu sein, tileProvide für das Caching zu verwenden. Wofür haben Sie sich entschieden?

    – ar-g

    30. Oktober 2014 um 11:07 Uhr


  • @Gyroscope, woher hast du diese Kacheln? stehen sie zum download bereit?

    – Heshan Sandeepa

    23. Mai 2016 um 17:39 Uhr

TileProvider der lokale Kacheln verwendet
Alex Wasilkow

  1. Sie können Kacheln in den Assets-Ordner legen (sofern dies für die App-Größe akzeptabel ist) oder sie alle beim ersten Start herunterladen und im Gerätespeicher (SD-Karte) ablegen.

  2. Sie können TileProvider wie folgt implementieren:


public class CustomMapTileProvider implements TileProvider {
    private static final int TILE_WIDTH = 256;
    private static final int TILE_HEIGHT = 256;
    private static final int BUFFER_SIZE = 16 * 1024;

    private AssetManager mAssets;

    public CustomMapTileProvider(AssetManager assets) {
        mAssets = assets;
    }

    @Override
    public Tile getTile(int x, int y, int zoom) {
        byte[] image = readTileImage(x, y, zoom);
        return image == null ? null : new Tile(TILE_WIDTH, TILE_HEIGHT, image);
    }

    private byte[] readTileImage(int x, int y, int zoom) {
        InputStream in = null;
        ByteArrayOutputStream buffer = null;

        try {
            in = mAssets.open(getTileFilename(x, y, zoom));
            buffer = new ByteArrayOutputStream();

            int nRead;
            byte[] data = new byte[BUFFER_SIZE];

            while ((nRead = in.read(data, 0, BUFFER_SIZE)) != -1) {
                buffer.write(data, 0, nRead);
            }
            buffer.flush();

            return buffer.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            return null;
        } finally {
            if (in != null) try { in.close(); } catch (Exception ignored) {}
            if (buffer != null) try { buffer.close(); } catch (Exception ignored) {}
        }
    }

    private String getTileFilename(int x, int y, int zoom) {
        return "map/" + zoom + "https://stackoverflow.com/" + x + "https://stackoverflow.com/" + y + ".png";
    }
}

Und jetzt können Sie es mit Ihrer GoogleMap-Instanz verwenden:

private void setUpMap() {
    mMap.setMapType(GoogleMap.MAP_TYPE_NONE);

    mMap.addTileOverlay(new TileOverlayOptions().tileProvider(new CustomMapTileProvider(getResources().getAssets())));

    CameraUpdate upd = CameraUpdateFactory.newLatLngZoom(new LatLng(LAT, LON), ZOOM);
    mMap.moveCamera(upd);
}

In meinem Fall hatte ich auch ein Problem mit der y-Koordinate der von MapTiler generierten Kacheln, aber ich habe es geschafft, indem ich diese Methode zu CustomMapTileProvider hinzugefügt habe:

/**
 * Fixing tile's y index (reversing order)
 */
private int fixYCoordinate(int y, int zoom) {
    int size = 1 << zoom; // size = 2^zoom
    return size - 1 - y;
}

und rufen Sie es von der Methode getTile () wie folgt auf:

@Override
public Tile getTile(int x, int y, int zoom) {
    y = fixYCoordinate(y, zoom);
    ...
}

[Upd]

Wenn Sie den genauen Bereich Ihrer benutzerdefinierten Karte kennen, sollten Sie zurückkehren NO_TILE für fehlende Kacheln aus getTile(...) Methode.

So habe ich es gemacht:

private static final SparseArray<Rect> TILE_ZOOMS = new SparseArray<Rect>() {{
    put(8,  new Rect(135,  180,  135,  181 ));
    put(9,  new Rect(270,  361,  271,  363 ));
    put(10, new Rect(541,  723,  543,  726 ));
    put(11, new Rect(1082, 1447, 1086, 1452));
    put(12, new Rect(2165, 2894, 2172, 2905));
    put(13, new Rect(4330, 5789, 4345, 5810));
    put(14, new Rect(8661, 11578, 8691, 11621));
}};

@Override
public Tile getTile(int x, int y, int zoom) {
    y = fixYCoordinate(y, zoom);

    if (hasTile(x, y, zoom)) {
        byte[] image = readTileImage(x, y, zoom);
        return image == null ? null : new Tile(TILE_WIDTH, TILE_HEIGHT, image);
    } else {
        return NO_TILE;
    }
}

private boolean hasTile(int x, int y, int zoom) {
    Rect b = TILE_ZOOMS.get(zoom);
    return b == null ? false : (b.left <= x && x <= b.right && b.top <= y && y <= b.bottom);
}

  • Bitte beachten Sie die Dokumentation des Rückgabewerts von getTile(int,int,int): die für diese Kachelkoordinate zu verwendende Kachel. Wenn Sie keine Kachel für diese Kachelkoordinate bereitstellen möchten, geben Sie NO_TILE zurück. Wenn die Kachel zu diesem Zeitpunkt nicht gefunden werden konnte, geben Sie null zurück, und weitere Anforderungen können mit einem exponentiellen Backoff erfolgen.

    – JesperB

    24. Juni 2013 um 6:29 Uhr

  • Danke @JesperB, siehe bitte [Upd] Abschnitt der Antwort

    – Alex Wassilkow

    25. Juni 2013 um 10:07 Uhr

  • Danke – ich hatte eine klobige Version davon, die ich geschrieben habe, als API v2 zum ersten Mal herauskam, und Ihr Beispiel hat meine Leistung erheblich verbessert. FWIW, mein Kachelsatz bildet kein perfektes Rechteck, also überprüfe ich die Existenz der Kachel, indem ich überprüfe, ob der Eingabestrom null ist.

    – alice_silver_man

    1. Oktober 2013 um 4:27 Uhr

  • Alex Vasilkov: in ` CameraUpdate upd = CameraUpdateFactory.newLatLngZoom(new LatLng(LAT, LON), ZOOM);` Welches LAT, LAN muss ich passieren?

    – Harshal Kalavadiya

    13. Juli 2015 um 6:06 Uhr

  • Darf ich fragen, wie man die Werte kennt, die Sie in Ihr SparseArray eingegeben haben? . Was sind diese vier Werte in Rect() ?

    – Azmuhak

    21. April 2016 um 11:15 Uhr

1646440987 472 TileProvider der lokale Kacheln verwendet
erik_beus

Die Möglichkeit, benutzerdefinierte Tileprovider in der neuen API (v2) hinzuzufügen, ist großartig, aber Sie erwähnen, dass Ihre Benutzer meistens offline sind. Wenn ein Benutzer beim ersten Start der Anwendung offline ist, können Sie die neue API nicht verwenden, da der Benutzer online sein muss (anscheinend mindestens einmal, um einen Cache zu erstellen) – andernfalls wird nur ein schwarzer Bildschirm angezeigt.

BEARBEITEN 22.02.14: Ich bin kürzlich wieder auf das gleiche Problem gestoßen – mit benutzerdefinierten Kacheln für eine App, die offline funktionieren musste. Gelöst wurde es durch Hinzufügen einer unsichtbaren (w/h 0/0) Kartenansicht zu einer anfänglichen Ansicht, in der der Client einige Inhalte herunterladen musste. Dies scheint zu funktionieren und ermöglicht es mir, später eine Kartenansicht im Offline-Modus zu verwenden.

  • Morty, woher bekommen Sie diese Informationen? Ist das die offizielle Absicht von Google? Dies würde es unmöglich machen, die gmap api v2 zu verwenden, um lokal gespeicherte Maptiles offline ohne Internetverbindung anzuzeigen. Tom

    – Tom

    6. März 2014 um 16:54 Uhr


  • @Tom Ich fürchte, er hat recht. Ja, es ist wirklich anoing.

    – Warpzit

    15. April 2014 um 9:55 Uhr

  • In dieser Situation müssen Sie “ZoomTables.data” (ziehen Sie es aus dem Emulator, wenn Sie die Karte initialisieren) nach /data/data//files/ kopieren.

    – Juraj

    19. Februar 2016 um 12:46 Uhr

  • Kann jemand etwas über das Laden der benutzerdefinierten Kartenkacheln beim ersten Start im Flugzeugmodus sagen?

    – abishek

    4. November 2016 um 18:10 Uhr


  • Ich habe die benutzerdefinierten Kacheln, muss sie anzeigen, aber getTile() wird beim ersten Start nicht aufgerufen. Ich muss benutzerdefinierte Kacheln beim ersten Start im Flugzeugmodus anzeigen.

    – abishek

    7. Dezember 2016 um 18:09 Uhr

1646440987 339 TileProvider der lokale Kacheln verwendet
Sergej Stassischin

So habe ich das in Kotlin implementiert:

class LocalTileProvider : TileProvider
{

override fun getTile(x: Int, y: Int, zoom: Int): Tile?
{
    // This is for my case only
    if (zoom > 11)
        return TileProvider.NO_TILE

    val path = "${getImagesFolder()}/tiles/$zoom/$x/$y/filled.png"
    val file = File(path)
    if (!file.exists())
        return TileProvider.NO_TILE
    return try {
        Tile(TILE_SIZE, TILE_SIZE, file.readBytes())
    }
    catch (e: Exception)
    {
        TileProvider.NO_TILE
    }
}

companion object {
    const val TILE_SIZE = 512
}

}

940480cookie-checkTileProvider, der lokale Kacheln verwendet

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy