add sqlite tile cache
This commit is contained in:
@@ -2,6 +2,7 @@ package org.oscim.android.test;
|
||||
|
||||
import org.oscim.android.MapActivity;
|
||||
import org.oscim.android.MapView;
|
||||
import org.oscim.android.cache.TileCache;
|
||||
import org.oscim.layers.tile.vector.VectorTileLayer;
|
||||
import org.oscim.tiling.source.TileSource;
|
||||
import org.oscim.tiling.source.oscimap4.OSciMap4TileSource;
|
||||
@@ -13,6 +14,9 @@ public class BaseMapActivity extends MapActivity {
|
||||
|
||||
MapView mMapView;
|
||||
VectorTileLayer mBaseLayer;
|
||||
TileSource mTileSource;
|
||||
|
||||
private TileCache mCache;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@@ -21,10 +25,21 @@ public class BaseMapActivity extends MapActivity {
|
||||
|
||||
mMapView = (MapView) findViewById(R.id.mapView);
|
||||
|
||||
TileSource tileSource = new OSciMap4TileSource();
|
||||
tileSource.setOption("url", "http://opensciencemap.org/tiles/vtm");
|
||||
mTileSource = new OSciMap4TileSource();
|
||||
mTileSource.setOption("url", "http://opensciencemap.org/tiles/vtm");
|
||||
|
||||
mBaseLayer = mMap.setBaseMap(tileSource);
|
||||
|
||||
mCache = new TileCache(this, "cachedir", "testdb");
|
||||
mCache.setCacheSize(512 * (1 << 10));
|
||||
mTileSource.setCache(mCache);
|
||||
|
||||
mBaseLayer = mMap.setBaseMap(mTileSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mCache.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
277
vtm-android/src/org/oscim/android/cache/TileCache.java
vendored
Normal file
277
vtm-android/src/org/oscim/android/cache/TileCache.java
vendored
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.android.cache;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.tiling.source.ITileCache;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteDoneException;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
public class TileCache implements ITileCache {
|
||||
|
||||
final static org.slf4j.Logger log = LoggerFactory.getLogger(TileCache.class);
|
||||
|
||||
class CacheTileReader implements TileReader {
|
||||
final InputStream mInputStream;
|
||||
final Tile mTile;
|
||||
final int mSize;
|
||||
|
||||
public CacheTileReader(Tile tile, InputStream is, int size) {
|
||||
mTile = tile;
|
||||
mInputStream = is;
|
||||
mSize = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile getTile() {
|
||||
return mTile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
return mInputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBytes() {
|
||||
return mSize;
|
||||
}
|
||||
}
|
||||
|
||||
class CacheTileWriter implements TileWriter {
|
||||
final ByteArrayOutputStream mOutputStream;
|
||||
final Tile mTile;
|
||||
|
||||
CacheTileWriter(Tile tile, ByteArrayOutputStream os) {
|
||||
mTile = tile;
|
||||
mOutputStream = os;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile getTile() {
|
||||
return mTile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
return mOutputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(boolean success) {
|
||||
saveTile(mTile, mOutputStream, success);
|
||||
}
|
||||
}
|
||||
|
||||
private final ArrayList<ByteArrayOutputStream> mCacheBuffers;
|
||||
|
||||
private final SQLiteHelper dbHelper;
|
||||
|
||||
private final SQLiteDatabase mDatabase;
|
||||
private final SQLiteStatement mStmtGetTile;
|
||||
private final SQLiteStatement mStmtPutTile;
|
||||
private final SQLiteStatement mStmtUpdateTile;
|
||||
|
||||
public void dispose() {
|
||||
if (mDatabase.isOpen())
|
||||
mDatabase.close();
|
||||
}
|
||||
|
||||
public TileCache(Context context, String cacheDirectory, String dbName) {
|
||||
|
||||
dbHelper = new SQLiteHelper(context);
|
||||
mDatabase = dbHelper.getWritableDatabase();
|
||||
|
||||
mStmtGetTile = mDatabase.compileStatement("" +
|
||||
"SELECT " + COLUMN_DATA +
|
||||
" FROM " + TABLE_NAME +
|
||||
" WHERE x=? AND y=? AND z = ?");
|
||||
|
||||
mStmtPutTile = mDatabase.compileStatement("" +
|
||||
"INSERT INTO " + TABLE_NAME +
|
||||
" (x, y, z, time, last_access, data)" +
|
||||
" VALUES(?,?,?,?,?,?)");
|
||||
|
||||
mStmtUpdateTile = mDatabase.compileStatement("" +
|
||||
"UPDATE " + TABLE_NAME +
|
||||
" SET last_access=?" +
|
||||
" WHERE x=? AND y=? AND z=?");
|
||||
|
||||
mCacheBuffers = new ArrayList<ByteArrayOutputStream>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileWriter writeTile(Tile tile) {
|
||||
ByteArrayOutputStream os;
|
||||
|
||||
synchronized (mCacheBuffers) {
|
||||
if (mCacheBuffers.size() == 0)
|
||||
os = new ByteArrayOutputStream(32 * 1024);
|
||||
else
|
||||
os = mCacheBuffers.remove(mCacheBuffers.size() - 1);
|
||||
}
|
||||
return new CacheTileWriter(tile, os);
|
||||
}
|
||||
|
||||
static final String TABLE_NAME = "tiles";
|
||||
static final String COLUMN_TIME = "time";
|
||||
static final String COLUMN_ACCESS = "last_access";
|
||||
static final String COLUMN_DATA = "data";
|
||||
|
||||
//static final String COLUMN_SIZE = "size";
|
||||
|
||||
class SQLiteHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final String DATABASE_NAME = "tile.db";
|
||||
private static final int DATABASE_VERSION = 3;
|
||||
|
||||
private static final String DATABASE_CREATE =
|
||||
"CREATE TABLE "
|
||||
+ TABLE_NAME + "("
|
||||
+ "x INTEGER NOT NULL,"
|
||||
+ "y INTEGER NOT NULL,"
|
||||
+ "z INTEGER NOT NULL,"
|
||||
+ COLUMN_TIME + " LONG NOT NULL,"
|
||||
//+ COLUMN_SIZE + " LONG NOT NULL,"
|
||||
+ COLUMN_ACCESS + " LONG NOT NULL,"
|
||||
+ COLUMN_DATA + " BLOB,"
|
||||
+ "PRIMARY KEY(x,y,z));";
|
||||
|
||||
public SQLiteHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(DATABASE_CREATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
|
||||
onCreate(db);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveTile(Tile tile, ByteArrayOutputStream data, boolean success) {
|
||||
byte[] bytes = null;
|
||||
|
||||
if (success)
|
||||
bytes = data.toByteArray();
|
||||
|
||||
synchronized (mCacheBuffers) {
|
||||
data.reset();
|
||||
mCacheBuffers.add(data);
|
||||
}
|
||||
|
||||
log.debug("store tile {} {}", tile, Boolean.valueOf(success));
|
||||
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
synchronized (mStmtPutTile) {
|
||||
mStmtPutTile.bindLong(1, tile.tileX);
|
||||
mStmtPutTile.bindLong(2, tile.tileY);
|
||||
mStmtPutTile.bindLong(3, tile.zoomLevel);
|
||||
mStmtPutTile.bindLong(4, 0);
|
||||
mStmtPutTile.bindLong(5, 0);
|
||||
mStmtPutTile.bindBlob(6, bytes);
|
||||
|
||||
mStmtPutTile.execute();
|
||||
|
||||
mStmtPutTile.clearBindings();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public synchronized TileReader getTileApi11(Tile tile) {
|
||||
InputStream in = null;
|
||||
|
||||
mStmtGetTile.bindLong(1, tile.tileX);
|
||||
mStmtGetTile.bindLong(2, tile.tileY);
|
||||
mStmtGetTile.bindLong(3, tile.zoomLevel);
|
||||
|
||||
try {
|
||||
ParcelFileDescriptor result = mStmtGetTile.simpleQueryForBlobFileDescriptor();
|
||||
in = new FileInputStream(result.getFileDescriptor());
|
||||
} catch (SQLiteDoneException e) {
|
||||
log.debug("not in cache {}", tile);
|
||||
return null;
|
||||
} finally {
|
||||
mStmtGetTile.clearBindings();
|
||||
}
|
||||
|
||||
log.debug("load tile {}", tile);
|
||||
|
||||
return new CacheTileReader(tile, in, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
private final String[] mQueryVals = new String[3];
|
||||
|
||||
@Override
|
||||
public synchronized TileReader getTile(Tile tile) {
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||
return getTileApi11(tile);
|
||||
|
||||
mQueryVals[0] = String.valueOf(tile.zoomLevel);
|
||||
mQueryVals[1] = String.valueOf(tile.tileX);
|
||||
mQueryVals[2] = String.valueOf(tile.tileY);
|
||||
|
||||
Cursor cursor = mDatabase.rawQuery("SELECT " + COLUMN_DATA +
|
||||
" FROM " + TABLE_NAME +
|
||||
" WHERE z=? AND x=? AND y=?", mQueryVals);
|
||||
|
||||
if (!cursor.moveToFirst()) {
|
||||
log.debug("not in cache {}", tile);
|
||||
return null;
|
||||
}
|
||||
|
||||
InputStream in = new ByteArrayInputStream(cursor.getBlob(0));
|
||||
|
||||
if (!cursor.isClosed())
|
||||
cursor.close();
|
||||
|
||||
log.debug("load tile {}", tile);
|
||||
|
||||
return new CacheTileReader(tile, in, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public SQLiteDatabase open() throws SQLException {
|
||||
return dbHelper.getWritableDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCacheSize(long size) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,12 @@ public abstract class TileSource {
|
||||
|
||||
protected final Options options = new Options();
|
||||
|
||||
public ITileCache tileCache;
|
||||
|
||||
public void setCache(ITileCache cache) {
|
||||
tileCache = cache;
|
||||
}
|
||||
|
||||
public TileSource setOption(String key, String value) {
|
||||
options.put(key, value);
|
||||
return this;
|
||||
|
||||
@@ -104,22 +104,40 @@ public class LwHttp {
|
||||
}
|
||||
|
||||
static class Buffer extends BufferedInputStream {
|
||||
public Buffer(InputStream is) {
|
||||
final OutputStream mCache;
|
||||
|
||||
public Buffer(InputStream is, OutputStream cache) {
|
||||
super(is, 4096);
|
||||
mCache = cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int read() throws IOException {
|
||||
return super.read();
|
||||
int data = super.read();
|
||||
if (data >= 0)
|
||||
mCache.write(data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int read(byte[] buffer, int offset, int byteCount)
|
||||
throws IOException {
|
||||
return super.read(buffer, offset, byteCount);
|
||||
int len = super.read(buffer, offset, byteCount);
|
||||
|
||||
if (len >= 0)
|
||||
mCache.write(buffer, offset, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
OutputStream mCacheOutputStream;
|
||||
|
||||
public void setOutputStream(OutputStream outputStream) {
|
||||
mCacheOutputStream = outputStream;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (mSocket != null) {
|
||||
try {
|
||||
@@ -204,6 +222,10 @@ public class LwHttp {
|
||||
is.mark(0);
|
||||
is.skip(end);
|
||||
|
||||
if (mCacheOutputStream != null) {
|
||||
is = new Buffer(is, mCacheOutputStream);
|
||||
}
|
||||
|
||||
if (mInflateContent)
|
||||
return new InflaterInputStream(is);
|
||||
|
||||
|
||||
@@ -14,14 +14,17 @@
|
||||
*/
|
||||
package org.oscim.tiling.source.common;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.oscim.tiling.MapTile;
|
||||
import org.oscim.tiling.source.ITileCache;
|
||||
import org.oscim.tiling.source.ITileDataSink;
|
||||
import org.oscim.tiling.source.ITileDataSource;
|
||||
import org.oscim.utils.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -34,48 +37,76 @@ public abstract class PbfTileDataSource implements ITileDataSource {
|
||||
|
||||
protected LwHttp mConn;
|
||||
protected final PbfDecoder mTileDecoder;
|
||||
protected final ITileCache mTileCache;
|
||||
|
||||
public PbfTileDataSource(PbfDecoder tileDecoder) {
|
||||
public PbfTileDataSource(PbfDecoder tileDecoder, ITileCache tileCache) {
|
||||
mTileDecoder = tileDecoder;
|
||||
mTileCache = tileCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryResult executeQuery(MapTile tile, ITileDataSink sink) {
|
||||
QueryResult result = QueryResult.SUCCESS;
|
||||
boolean success = true;
|
||||
|
||||
ITileCache.TileWriter cacheWriter = null;
|
||||
|
||||
if (mTileCache != null) {
|
||||
ITileCache.TileReader c = mTileCache.getTile(tile);
|
||||
if (c == null) {
|
||||
// create new cache entry
|
||||
cacheWriter = mTileCache.writeTile(tile);
|
||||
mConn.setOutputStream(cacheWriter.getOutputStream());
|
||||
} else {
|
||||
try {
|
||||
InputStream is = c.getInputStream();
|
||||
if (mTileDecoder.decode(tile, sink, is, c.getBytes())) {
|
||||
IOUtils.closeQuietly(is);
|
||||
return QueryResult.SUCCESS;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
log.debug(tile + " Cache read failed");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream is;
|
||||
if (!mConn.sendRequest(tile)) {
|
||||
log.debug(tile + " Request Failed");
|
||||
result = QueryResult.FAILED;
|
||||
log.debug(tile + " Request failed");
|
||||
success = false;
|
||||
} else if ((is = mConn.readHeader()) != null) {
|
||||
boolean win = mTileDecoder.decode(tile, sink, is, mConn.getContentLength());
|
||||
if (!win)
|
||||
log.debug(tile + " failed");
|
||||
int bytes = mConn.getContentLength();
|
||||
success = mTileDecoder.decode(tile, sink, is, bytes);
|
||||
if (!success)
|
||||
log.debug(tile + " Decoding failed");
|
||||
} else {
|
||||
log.debug(tile + " Network Error");
|
||||
result = QueryResult.FAILED;
|
||||
success = false;
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
log.debug(tile + " Socket exception: " + e.getMessage());
|
||||
result = QueryResult.FAILED;
|
||||
success = false;
|
||||
} catch (SocketTimeoutException e) {
|
||||
log.debug(tile + " Socket Timeout");
|
||||
result = QueryResult.FAILED;
|
||||
success = false;
|
||||
} catch (UnknownHostException e) {
|
||||
log.debug(tile + " No Network");
|
||||
result = QueryResult.FAILED;
|
||||
success = false;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
result = QueryResult.FAILED;
|
||||
success = false;
|
||||
}
|
||||
|
||||
mConn.requestCompleted();
|
||||
|
||||
if (result != QueryResult.SUCCESS)
|
||||
if (cacheWriter != null)
|
||||
cacheWriter.complete(success);
|
||||
|
||||
if (success)
|
||||
mConn.close();
|
||||
|
||||
return result;
|
||||
return success ? QueryResult.SUCCESS : QueryResult.FAILED;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,6 +18,7 @@ import java.net.URL;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.tiling.source.ITileDataSource;
|
||||
import org.oscim.tiling.source.TileSource;
|
||||
import org.oscim.tiling.source.common.LwHttp;
|
||||
import org.oscim.tiling.source.common.PbfTileDataSource;
|
||||
import org.oscim.tiling.source.common.UrlTileSource;
|
||||
@@ -26,13 +27,13 @@ public class MapnikVectorTileSource extends UrlTileSource {
|
||||
|
||||
@Override
|
||||
public ITileDataSource getDataSource() {
|
||||
return new TileDataSource(mUrl);
|
||||
return new TileDataSource(this, mUrl);
|
||||
}
|
||||
|
||||
static class TileDataSource extends PbfTileDataSource {
|
||||
|
||||
public TileDataSource(URL url) {
|
||||
super(new TileDecoder());
|
||||
public TileDataSource(TileSource tileSource, URL url) {
|
||||
super(new TileDecoder(), tileSource.tileCache);
|
||||
|
||||
mConn = new LwHttp(url, "image/png", "vector.pbf", true) {
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.oscim.tiling.source.oscimap;
|
||||
import java.net.URL;
|
||||
|
||||
import org.oscim.tiling.source.ITileDataSource;
|
||||
import org.oscim.tiling.source.TileSource;
|
||||
import org.oscim.tiling.source.common.LwHttp;
|
||||
import org.oscim.tiling.source.common.PbfTileDataSource;
|
||||
import org.oscim.tiling.source.common.UrlTileSource;
|
||||
@@ -29,12 +30,12 @@ public class OSciMap1TileSource extends UrlTileSource {
|
||||
|
||||
@Override
|
||||
public ITileDataSource getDataSource() {
|
||||
return new TileDataSource(mUrl);
|
||||
return new TileDataSource(this, mUrl);
|
||||
}
|
||||
|
||||
class TileDataSource extends PbfTileDataSource {
|
||||
public TileDataSource(URL url) {
|
||||
super(new TileDecoder());
|
||||
public TileDataSource(TileSource tileSource, URL url) {
|
||||
super(new TileDecoder(), tileSource.tileCache);
|
||||
mConn = new LwHttp(url, "application/osmtile", "osmtile", false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.oscim.core.TagSet;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.tiling.source.ITileDataSink;
|
||||
import org.oscim.tiling.source.ITileDataSource;
|
||||
import org.oscim.tiling.source.TileSource;
|
||||
import org.oscim.tiling.source.common.LwHttp;
|
||||
import org.oscim.tiling.source.common.PbfDecoder;
|
||||
import org.oscim.tiling.source.common.PbfTileDataSource;
|
||||
@@ -37,12 +38,12 @@ public class OSciMap2TileSource extends UrlTileSource {
|
||||
|
||||
@Override
|
||||
public ITileDataSource getDataSource() {
|
||||
return new TileDataSource(mUrl);
|
||||
return new TileDataSource(this, mUrl);
|
||||
}
|
||||
|
||||
class TileDataSource extends PbfTileDataSource {
|
||||
public TileDataSource(URL url) {
|
||||
super(new TileDecoder());
|
||||
public TileDataSource(TileSource tileSource, URL url) {
|
||||
super(new TileDecoder(), tileSource.tileCache);
|
||||
mConn = new LwHttp(url, "application/osmtile", "osmtile", false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.oscim.tiling.source.oscimap4;
|
||||
import java.net.URL;
|
||||
|
||||
import org.oscim.tiling.source.ITileDataSource;
|
||||
import org.oscim.tiling.source.TileSource;
|
||||
import org.oscim.tiling.source.common.LwHttp;
|
||||
import org.oscim.tiling.source.common.PbfTileDataSource;
|
||||
import org.oscim.tiling.source.common.UrlTileSource;
|
||||
@@ -25,12 +26,12 @@ public class OSciMap4TileSource extends UrlTileSource {
|
||||
|
||||
@Override
|
||||
public ITileDataSource getDataSource() {
|
||||
return new TileDataSource(mUrl);
|
||||
return new TileDataSource(this, mUrl);
|
||||
}
|
||||
|
||||
class TileDataSource extends PbfTileDataSource {
|
||||
public TileDataSource(URL url) {
|
||||
super(new TileDecoder());
|
||||
public TileDataSource(TileSource tileSource, URL url) {
|
||||
super(new TileDecoder(), tileSource.tileCache);
|
||||
//mConn = new LwHttp(url, "application/x-protobuf", "vtm", false);
|
||||
mConn = new LwHttp(url, "image/png", "vtm", false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user