diff --git a/vtm/src/org/oscim/tiling/source/bitmap/BitmapTileSource.java b/vtm/src/org/oscim/tiling/source/bitmap/BitmapTileSource.java index a6c098ea..1c4c7f9c 100644 --- a/vtm/src/org/oscim/tiling/source/bitmap/BitmapTileSource.java +++ b/vtm/src/org/oscim/tiling/source/bitmap/BitmapTileSource.java @@ -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 { diff --git a/vtm/src/org/oscim/tiling/source/bitmap/DefaultSources.java b/vtm/src/org/oscim/tiling/source/bitmap/DefaultSources.java index e9c6f1ad..0e2881a9 100644 --- a/vtm/src/org/oscim/tiling/source/bitmap/DefaultSources.java +++ b/vtm/src/org/oscim/tiling/source/bitmap/DefaultSources.java @@ -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="); diff --git a/vtm/src/org/oscim/tiling/source/common/LwHttp.java b/vtm/src/org/oscim/tiling/source/common/LwHttp.java index c8a2aa4c..c57a2e4c 100644 --- a/vtm/src/org/oscim/tiling/source/common/LwHttp.java +++ b/vtm/src/org/oscim/tiling/source/common/LwHttp.java @@ -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; - } - } diff --git a/vtm/src/org/oscim/tiling/source/common/UrlTileDataSource.java b/vtm/src/org/oscim/tiling/source/common/UrlTileDataSource.java index ddbb5a25..7a9eb34c 100644 --- a/vtm/src/org/oscim/tiling/source/common/UrlTileDataSource.java +++ b/vtm/src/org/oscim/tiling/source/common/UrlTileDataSource.java @@ -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; } diff --git a/vtm/src/org/oscim/tiling/source/common/UrlTileSource.java b/vtm/src/org/oscim/tiling/source/common/UrlTileSource.java index 3d34e51c..956ecc16 100644 --- a/vtm/src/org/oscim/tiling/source/common/UrlTileSource.java +++ b/vtm/src/org/oscim/tiling/source/common/UrlTileSource.java @@ -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; + } } diff --git a/vtm/src/org/oscim/tiling/source/mapnik/MapnikVectorTileSource.java b/vtm/src/org/oscim/tiling/source/mapnik/MapnikVectorTileSource.java index 281ba244..00067488 100644 --- a/vtm/src/org/oscim/tiling/source/mapnik/MapnikVectorTileSource.java +++ b/vtm/src/org/oscim/tiling/source/mapnik/MapnikVectorTileSource.java @@ -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; } } diff --git a/vtm/src/org/oscim/tiling/source/mapnik/TileDecoder.java b/vtm/src/org/oscim/tiling/source/mapnik/TileDecoder.java index de6e364f..990aaf27 100644 --- a/vtm/src/org/oscim/tiling/source/mapnik/TileDecoder.java +++ b/vtm/src/org/oscim/tiling/source/mapnik/TileDecoder.java @@ -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; diff --git a/vtm/src/org/oscim/tiling/source/oscimap/OSciMap1TileSource.java b/vtm/src/org/oscim/tiling/source/oscimap/OSciMap1TileSource.java index dd12ccee..9d6d7563 100644 --- a/vtm/src/org/oscim/tiling/source/oscimap/OSciMap1TileSource.java +++ b/vtm/src/org/oscim/tiling/source/oscimap/OSciMap1TileSource.java @@ -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)); } } diff --git a/vtm/src/org/oscim/tiling/source/oscimap2/OSciMap2TileSource.java b/vtm/src/org/oscim/tiling/source/oscimap2/OSciMap2TileSource.java index ae5d1160..e9cf2405 100644 --- a/vtm/src/org/oscim/tiling/source/oscimap2/OSciMap2TileSource.java +++ b/vtm/src/org/oscim/tiling/source/oscimap2/OSciMap2TileSource.java @@ -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 { diff --git a/vtm/src/org/oscim/tiling/source/oscimap4/OSciMap4TileSource.java b/vtm/src/org/oscim/tiling/source/oscimap4/OSciMap4TileSource.java index ee9c4d07..289b4aae 100644 --- a/vtm/src/org/oscim/tiling/source/oscimap4/OSciMap4TileSource.java +++ b/vtm/src/org/oscim/tiling/source/oscimap4/OSciMap4TileSource.java @@ -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)); } }