api changes: UrlTileSource

- move option to override getTileUrl() to UrlTileSource
- remove requirement to provide expected mime-type
- only need to use setExtension() when using the default
  url formatter.
This commit is contained in:
Hannes Janetzek 2014-01-26 05:01:00 +01:00
parent cb629744ec
commit 9f626e3716
10 changed files with 153 additions and 139 deletions

@ -18,46 +18,21 @@ import org.slf4j.LoggerFactory;
public abstract class BitmapTileSource extends UrlTileSource {
static final Logger log = LoggerFactory.getLogger(LwHttp.class);
private final String mFileExtension;
private final String mMimeType;
public BitmapTileSource(String url) {
this(url, 0, 17);
}
/**
* Create BitmapTileSource for 'url'
*
* By default path will be formatted as: url/z/x/y.png
* Use e.g. setExtension(".jpg") to overide ending or
* implement getUrlString() for custom formatting.
*/
public BitmapTileSource(String url, int zoomMin, int zoomMax) {
this(url, zoomMin, zoomMax, "image/png", ".png");
}
public BitmapTileSource(String url, int zoomMin, int zoomMax, String mimeType,
String fileExtension) {
super(url);
mZoomMin = zoomMin;
mZoomMax = zoomMax;
mFileExtension = fileExtension;
mMimeType = mimeType;
}
public String getTileUrl(Tile tile) {
return null;
super(url, zoomMin, zoomMax);
setExtension(".png");
}
@Override
public ITileDataSource getDataSource() {
LwHttp conn = new LwHttp(mUrl, mMimeType, mFileExtension, false) {
@Override
protected int formatTilePath(Tile tile, byte[] path, int curPos) {
String p = getTileUrl(tile);
if (p == null)
return super.formatTilePath(tile, path, curPos);
byte[] b = p.getBytes();
System.arraycopy(b, 0, path, curPos, b.length);
return curPos + b.length;
}
};
return new UrlTileDataSource(this, new BitmapTileDecoder(), conn);
return new UrlTileDataSource(this, new BitmapTileDecoder(), new LwHttp(mUrl));
}
public class BitmapTileDecoder implements ITileDecoder {

@ -31,15 +31,15 @@ public class DefaultSources {
public static class ImagicoLandcover extends BitmapTileSource {
public ImagicoLandcover() {
super("http://www.imagico.de/map/tiles/landcover",
0, 6, "image/jpeg", ".jpg");
super("http://www.imagico.de/map/tiles/landcover", 0, 6);
setExtension(".jpg");
}
}
public static class MapQuestAerial extends BitmapTileSource {
public MapQuestAerial() {
super("http://otile1.mqcdn.com/tiles/1.0.0/sat",
0, 8, "image/jpeg", ".jpg");
super("http://otile1.mqcdn.com/tiles/1.0.0/sat", 0, 8);
setExtension(".jpg");
}
@Override
@ -60,14 +60,15 @@ public class DefaultSources {
}
public static class ArcGISWorldShaded extends BitmapTileSource {
private final StringBuilder sb = new StringBuilder(32);
public ArcGISWorldShaded() {
super("http://server.arcgisonline.com/ArcGIS/rest/services",
0, 6, "image/jpg", "");
super("http://server.arcgisonline.com/ArcGIS/rest/services", 0, 6);
}
@Override
public String getTileUrl(Tile tile) {
StringBuilder sb = new StringBuilder(32);
public synchronized String getTileUrl(Tile tile) {
sb.setLength(0);
//sb.append("/World_Imagery/MapServer/tile/");
sb.append("/World_Shaded_Relief/MapServer/tile/");
sb.append(tile.zoomLevel);
@ -78,14 +79,15 @@ public class DefaultSources {
}
public static class HillShadeHD extends BitmapTileSource {
private final StringBuilder sb = new StringBuilder(32);
public HillShadeHD() {
super("http://129.206.74.245:8004/tms_hs.ashx",
2, 16, "image/png", "");
super("http://129.206.74.245:8004/tms_hs.ashx", 2, 16);
}
@Override
public String getTileUrl(Tile tile) {
StringBuilder sb = new StringBuilder(32);
public synchronized String getTileUrl(Tile tile) {
sb.setLength(0);
sb.append("?x=").append(tile.tileX);
sb.append("&y=").append(tile.tileY);
sb.append("&z=").append(tile.zoomLevel);
@ -102,14 +104,15 @@ public class DefaultSources {
*/
public static class GoogleMaps extends BitmapTileSource {
public static final GoogleMaps INSTANCE = new GoogleMaps("http://mt1.google.com");
private final StringBuilder sb = new StringBuilder(60);
public GoogleMaps(String hostName) {
super(hostName, 1, 20, "image/png", ""); //jpeg for sat
super(hostName, 1, 20); //jpeg for sat
}
@Override
public String getTileUrl(Tile tile) {
StringBuilder sb = new StringBuilder(60);
public synchronized String getTileUrl(Tile tile) {
sb.setLength(0);
sb.append("/vt/x="); //lyrs=y&
sb.append(tile.tileX);
sb.append("&y=");

@ -24,7 +24,6 @@ import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URL;
import java.util.zip.InflaterInputStream;
import org.oscim.core.Tile;
import org.oscim.utils.ArrayUtils;
@ -32,17 +31,18 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Lightweight HTTP connection for tile loading.
* Lightweight HTTP connection for tile loading. Does not do redirects,
* https, full header parsing or stuff.
*
* Default tile url format is 'z/x/y'. Override formatTilePath() for a
* different format.
* TODO extract API interface to be used by UrlTileSource so that one
* could also use HttpUrlConnection, etc.
*/
public class LwHttp {
static final Logger log = LoggerFactory.getLogger(LwHttp.class);
static final boolean dbg = false;
private final static byte[] HEADER_HTTP_OK = "200 OK".getBytes();
private final static byte[] HEADER_CONTENT_TYPE = "Content-Type".getBytes();
//private final static byte[] HEADER_CONTENT_TYPE = "Content-Type".getBytes();
private final static byte[] HEADER_CONTENT_LENGTH = "Content-Length".getBytes();
private final static int RESPONSE_EXPECTED_LIVES = 100;
private final static long RESPONSE_TIMEOUT = (long) 10E9; // 10 second in nanosecond
@ -64,9 +64,6 @@ public class LwHttp {
private final byte[] REQUEST_GET_END;
private final byte[] mRequestBuffer;
private final boolean mInflateContent;
private final byte[] mContentType;
/**
* @param url
* Base url for tiles
@ -77,9 +74,7 @@ public class LwHttp {
* @param deflate
* true when content uses gzip compression
*/
public LwHttp(URL url, String contentType, String extension, boolean deflate) {
mContentType = contentType.getBytes();
mInflateContent = deflate;
public LwHttp(URL url) {
int port = url.getPort();
if (port < 0)
@ -91,7 +86,7 @@ public class LwHttp {
REQUEST_GET_START = ("GET " + path).getBytes();
REQUEST_GET_END = (extension + " HTTP/1.1" +
REQUEST_GET_END = (" HTTP/1.1" +
"\nUser-Agent: vtm/0.5.9" +
"\nHost: " + host +
"\nConnection: Keep-Alive" +
@ -229,7 +224,7 @@ public class LwHttp {
}
}
public InputStream readHeader() throws IOException {
public InputStream read() throws IOException {
Buffer is = mResponseStream;
is.mark(BUFFER_SIZE);
@ -276,18 +271,17 @@ public class LwHttp {
if (!check(HEADER_HTTP_OK, buf, pos + 9, end))
ok = false;
} else if (check(HEADER_CONTENT_TYPE, buf, pos, end)) {
// check that response contains the expected
// Content-Type
if (!check(mContentType, buf, pos +
HEADER_CONTENT_TYPE.length + 2, end))
ok = false;
} else if (check(HEADER_CONTENT_LENGTH, buf, pos, end)) {
// parse Content-Length
contentLength = parseInt(buf, pos +
HEADER_CONTENT_LENGTH.length + 2, end - 1);
}
//} else if (check(HEADER_CONTENT_TYPE, buf, pos, end)) {
// check that response contains the expected
// Content-Type
//if (!check(mContentType, buf, pos +
// HEADER_CONTENT_TYPE.length + 2, end))
// ok = false;
if (!ok || dbg) {
String line = new String(buf, pos, end - pos - 1);
@ -307,13 +301,10 @@ public class LwHttp {
is.skip(end);
is.start(contentLength);
if (mInflateContent)
return new InflaterInputStream(is);
return is;
}
public boolean sendRequest(Tile tile) throws IOException {
public boolean sendRequest(UrlTileSource tileSource, Tile tile) throws IOException {
if (mSocket != null && ((mMaxReq-- <= 0)
|| (System.nanoTime() - mLastRequest > RESPONSE_TIMEOUT))) {
@ -344,7 +335,7 @@ public class LwHttp {
byte[] request = mRequestBuffer;
int pos = REQUEST_GET_START.length;
pos = formatTilePath(tile, request, pos);
pos = tileSource.formatTilePath(tile, request, pos);
int len = REQUEST_GET_END.length;
System.arraycopy(REQUEST_GET_END, 0, request, pos, len);
@ -384,7 +375,7 @@ public class LwHttp {
}
// write (positive) integer to byte array
protected static int writeInt(int val, int pos, byte[] buf) {
public static int writeInt(int val, int pos, byte[] buf) {
if (val == 0) {
buf[pos] = '0';
return pos + 1;
@ -444,23 +435,4 @@ public class LwHttp {
return true;
}
/**
* Write custom tile url
*
* @param tile Tile
* @param path to write url string
* @param curPos current position
* @return new position
*/
protected int formatTilePath(Tile tile, byte[] request, int pos) {
request[pos++] = '/';
pos = writeInt(tile.zoomLevel, pos, request);
request[pos++] = '/';
pos = writeInt(tile.tileX, pos, request);
request[pos++] = '/';
pos = writeInt(tile.tileY, pos, request);
return pos;
}
}

@ -29,7 +29,6 @@ import org.oscim.tiling.source.ITileCache.TileWriter;
import org.oscim.tiling.source.ITileDataSink;
import org.oscim.tiling.source.ITileDataSource;
import org.oscim.tiling.source.ITileDecoder;
import org.oscim.tiling.source.TileSource;
import org.oscim.utils.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -39,20 +38,22 @@ public class UrlTileDataSource implements ITileDataSource {
protected final LwHttp mConn;
protected final ITileDecoder mTileDecoder;
protected final ITileCache mTileCache;
protected final UrlTileSource mTileSource;
protected final boolean mUseCache;
public UrlTileDataSource(TileSource tileSource, ITileDecoder tileDecoder, LwHttp conn) {
public UrlTileDataSource(UrlTileSource tileSource, ITileDecoder tileDecoder, LwHttp conn) {
mTileDecoder = tileDecoder;
mTileCache = tileSource.tileCache;
mUseCache = (mTileCache != null);
mTileSource = tileSource;
mUseCache = (tileSource.tileCache != null);
mConn = conn;
}
@Override
public QueryResult executeQuery(MapTile tile, ITileDataSink sink) {
ITileCache cache = mTileSource.tileCache;
if (mUseCache) {
TileReader c = mTileCache.getTile(tile);
TileReader c = cache.getTile(tile);
if (c != null) {
InputStream is = c.getInputStream();
try {
@ -68,17 +69,17 @@ public class UrlTileDataSource implements ITileDataSource {
}
boolean success = false;
TileWriter cache = null;
TileWriter cacheWriter = null;
try {
InputStream is;
if (!mConn.sendRequest(tile)) {
if (!mConn.sendRequest(mTileSource, tile)) {
log.debug("{} Request failed", tile);
} else if ((is = mConn.readHeader()) == null) {
} else if ((is = mConn.read()) == null) {
log.debug("{} Network Error", tile);
} else {
if (mUseCache) {
cache = mTileCache.writeTile(tile);
mConn.setCache(cache.getOutputStream());
cacheWriter = cache.writeTile(tile);
mConn.setCache(cacheWriter.getOutputStream());
}
success = mTileDecoder.decode(tile, sink, is);
@ -94,8 +95,8 @@ public class UrlTileDataSource implements ITileDataSource {
} finally {
mConn.requestCompleted(success);
if (cache != null)
cache.complete(success);
if (cacheWriter != null)
cacheWriter.complete(success);
}
return success ? QueryResult.SUCCESS : QueryResult.FAILED;
}

@ -19,11 +19,13 @@ package org.oscim.tiling.source.common;
import java.net.MalformedURLException;
import java.net.URL;
import org.oscim.core.Tile;
import org.oscim.tiling.source.TileSource;
public abstract class UrlTileSource extends TileSource {
protected final URL mUrl;
private byte[] mExt;
public UrlTileSource(String urlString) {
URL url = null;
@ -35,6 +37,12 @@ public abstract class UrlTileSource extends TileSource {
mUrl = url;
}
public UrlTileSource(String url, int zoomMin, int zoomMax) {
this(url);
mZoomMin = zoomMin;
mZoomMax = zoomMax;
}
@Override
public OpenResult open() {
return OpenResult.SUCCESS;
@ -44,4 +52,55 @@ public abstract class UrlTileSource extends TileSource {
public void close() {
}
protected void setExtension(String ext) {
if (ext == null) {
mExt = null;
return;
}
mExt = ext.getBytes();
}
protected void setMimeType(String string) {
}
/**
* Create url path for tile
*/
protected String getTileUrl(Tile tile) {
return null;
}
/**
* Write tile url - the low level, no-allocations method,
*
* override getTileUrl() for custom url formatting using
* Strings
*
* @param tile Tile
* @param path to write url string
* @param curPos current position
* @return new position
*/
protected int formatTilePath(Tile tile, byte[] buf, int pos) {
String p = getTileUrl(tile);
if (p != null) {
byte[] b = p.getBytes();
System.arraycopy(b, 0, buf, pos, b.length);
return pos + b.length;
}
buf[pos++] = '/';
pos = LwHttp.writeInt(tile.zoomLevel, pos, buf);
buf[pos++] = '/';
pos = LwHttp.writeInt(tile.tileX, pos, buf);
buf[pos++] = '/';
pos = LwHttp.writeInt(tile.tileY, pos, buf);
if (mExt == null)
return pos;
System.arraycopy(mExt, 0, buf, pos, mExt.length);
return pos + mExt.length;
}
}

@ -30,29 +30,28 @@ public class MapnikVectorTileSource extends UrlTileSource {
@Override
public ITileDataSource getDataSource() {
LwHttp conn = new LwHttp(mUrl, "image/png", ".vector.pbf", true) {
@Override
protected int formatTilePath(Tile tile, byte[] path, int pos) {
// url formatter for mapbox streets
byte[] hexTable = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
return new UrlTileDataSource(this, new TileDecoder(), new LwHttp(mUrl));
}
path[pos++] = '/';
path[pos++] = hexTable[(tile.tileX) % 16];
path[pos++] = hexTable[(tile.tileY) % 16];
path[pos++] = '/';
pos = LwHttp.writeInt(tile.zoomLevel, pos, path);
path[pos++] = '/';
pos = LwHttp.writeInt(tile.tileX, pos, path);
path[pos++] = '/';
pos = LwHttp.writeInt(tile.tileY, pos, path);
return pos;
}
protected int formatTilePath(Tile tile, byte[] path, int pos) {
// url formatter for mapbox streets
byte[] hexTable = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
return new UrlTileDataSource(this, new TileDecoder(), conn);
path[pos++] = '/';
path[pos++] = hexTable[(tile.tileX) % 16];
path[pos++] = hexTable[(tile.tileY) % 16];
path[pos++] = '/';
pos = LwHttp.writeInt(tile.zoomLevel, pos, path);
path[pos++] = '/';
pos = LwHttp.writeInt(tile.tileX, pos, path);
path[pos++] = '/';
pos = LwHttp.writeInt(tile.tileY, pos, path);
return pos;
}
}

@ -19,6 +19,7 @@ package org.oscim.tiling.source.mapnik;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.zip.InflaterInputStream;
import org.oscim.core.GeometryBuffer.GeometryType;
import org.oscim.core.MapElement;
@ -77,7 +78,7 @@ public class TileDecoder extends PbfDecoder {
if (debug)
log.debug(tile + " decode");
setInputStream(is);
setInputStream(new InflaterInputStream(is));
mTile = tile;
mMapDataCallback = mapDataCallback;
mScale = REF_TILE_SIZE / Tile.SIZE;

@ -29,11 +29,12 @@ public class OSciMap1TileSource extends UrlTileSource {
public OSciMap1TileSource(String url) {
super(url);
setExtension(".osmtile");
setMimeType("application/osmtile");
}
@Override
public ITileDataSource getDataSource() {
LwHttp conn = new LwHttp(mUrl, "application/osmtile", ".osmtile", false);
return new UrlTileDataSource(this, new TileDecoder(), conn);
return new UrlTileDataSource(this, new TileDecoder(), new LwHttp(mUrl));
}
}

@ -38,12 +38,13 @@ public class OSciMap2TileSource extends UrlTileSource {
public OSciMap2TileSource(String url) {
super(url);
setExtension(".osmtile");
setMimeType("application/osmtile");
}
@Override
public ITileDataSource getDataSource() {
LwHttp conn = new LwHttp(mUrl, "application/osmtile", ".osmtile", false);
return new UrlTileDataSource(this, new TileDecoder(), conn);
return new UrlTileDataSource(this, new TileDecoder(), new LwHttp(mUrl));
}
static class TileDecoder extends PbfDecoder {

@ -24,17 +24,19 @@ import org.oscim.tiling.source.common.UrlTileSource;
public class OSciMap4TileSource extends UrlTileSource {
public OSciMap4TileSource() {
super("http://opensciencemap.org/tiles/vtm");
this("http://opensciencemap.org/tiles/vtm");
}
public OSciMap4TileSource(String url) {
super(url);
setExtension(".vtm");
// ignored for now
//setMimeType("image/png");
//setMimeType("application/x-protobuf");
}
@Override
public ITileDataSource getDataSource() {
//LwHttp conn = new LwHttp(url, "application/x-protobuf", ".vtm", false);
LwHttp conn = new LwHttp(mUrl, "image/png", ".vtm", false);
return new UrlTileDataSource(this, new TileDecoder(), conn);
return new UrlTileDataSource(this, new TileDecoder(), new LwHttp(mUrl));
}
}