diff --git a/settings.gradle b/settings.gradle index 3cbd0ee17..06ecb65de 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,5 @@ include ':vtm' +include ':vtm-tests' include ':vtm-extras' include ':vtm-android' include ':vtm-android-example' diff --git a/vtm-android-example/build.gradle b/vtm-android-example/build.gradle index b77ab2f14..e05f9b8bf 100644 --- a/vtm-android-example/build.gradle +++ b/vtm-android-example/build.gradle @@ -14,6 +14,7 @@ dependencies { compile project(':vtm-jeo') compile project(':vtm-extras') compile project(':vtm-themes') + compile 'com.squareup.okhttp:okhttp:1.5.2' } android { diff --git a/vtm-android-example/src/org/oscim/android/test/BaseMapActivity.java b/vtm-android-example/src/org/oscim/android/test/BaseMapActivity.java index 8f74553f3..64020f980 100644 --- a/vtm-android-example/src/org/oscim/android/test/BaseMapActivity.java +++ b/vtm-android-example/src/org/oscim/android/test/BaseMapActivity.java @@ -22,7 +22,8 @@ import org.oscim.core.MapPosition; import org.oscim.layers.tile.vector.VectorTileLayer; import org.oscim.theme.VtmThemes; -import org.oscim.tiling.TileSource; +import org.oscim.tiling.source.OkHttpEngine.OkHttpFactory; +import org.oscim.tiling.source.UrlTileSource; import org.oscim.tiling.source.oscimap4.OSciMap4TileSource; import android.os.Bundle; @@ -31,11 +32,11 @@ public class BaseMapActivity extends MapActivity { - final static boolean USE_CACHE = true; + final static boolean USE_CACHE = false; MapView mMapView; VectorTileLayer mBaseLayer; - TileSource mTileSource; + UrlTileSource mTileSource; private TileCache mCache; @@ -58,6 +59,7 @@ public void onCreate(Bundle savedInstanceState) { registerMapView(mMapView); mTileSource = new OSciMap4TileSource(); + mTileSource.setHttpEngine(new OkHttpFactory()); if (USE_CACHE) { mCache = new TileCache(this, null, "tile.db"); diff --git a/vtm-android-example/src/org/oscim/android/test/BitmapTileMapActivity.java b/vtm-android-example/src/org/oscim/android/test/BitmapTileMapActivity.java index 035b88121..e27491f89 100644 --- a/vtm-android-example/src/org/oscim/android/test/BitmapTileMapActivity.java +++ b/vtm-android-example/src/org/oscim/android/test/BitmapTileMapActivity.java @@ -21,22 +21,23 @@ import org.oscim.layers.TileGridLayer; import org.oscim.layers.tile.bitmap.BitmapTileLayer; import org.oscim.renderer.MapRenderer; -import org.oscim.tiling.TileSource; +import org.oscim.tiling.source.OkHttpEngine.OkHttpFactory; +import org.oscim.tiling.source.bitmap.BitmapTileSource; import org.oscim.tiling.source.bitmap.DefaultSources; import android.os.Bundle; public class BitmapTileMapActivity extends MapActivity { - private final static boolean USE_CACHE = true; - private final TileSource mTileSource; + private final static boolean USE_CACHE = false; + private final BitmapTileSource mTileSource; protected BitmapTileLayer mBitmapLayer; public BitmapTileMapActivity() { mTileSource = new DefaultSources.OpenStreetMap(); } - public BitmapTileMapActivity(TileSource tileSource) { + public BitmapTileMapActivity(BitmapTileSource tileSource) { mTileSource = tileSource; } @@ -61,6 +62,7 @@ public void onCreate(Bundle savedInstanceState) { mTileSource.setCache(mCache); } + mTileSource.setHttpEngine(new OkHttpFactory()); mBitmapLayer = new BitmapTileLayer(mMap, mTileSource); mMap.layers().add(mBitmapLayer); } diff --git a/vtm-extras/src/org/oscim/tiling/source/geojson/GeoJsonTileSource.java b/vtm-extras/src/org/oscim/tiling/source/geojson/GeoJsonTileSource.java index e381f6a18..440562f51 100644 --- a/vtm-extras/src/org/oscim/tiling/source/geojson/GeoJsonTileSource.java +++ b/vtm-extras/src/org/oscim/tiling/source/geojson/GeoJsonTileSource.java @@ -22,27 +22,24 @@ import org.oscim.core.MapElement; import org.oscim.core.Tag; import org.oscim.tiling.ITileDataSource; -import org.oscim.tiling.source.LwHttp; import org.oscim.tiling.source.UrlTileDataSource; import org.oscim.tiling.source.UrlTileSource; public abstract class GeoJsonTileSource extends UrlTileSource { public GeoJsonTileSource(String url) { - super(url); - setExtension(".json"); + super(url, "/{Z}/{X}/{Y}.json"); } public GeoJsonTileSource(String url, int zoomMin, int zoomMax) { - super(url, zoomMin, zoomMax); - setExtension(".json"); + super(url, "/{Z}/{X}/{Y}.json", zoomMin, zoomMax); } @Override public ITileDataSource getDataSource() { Map opt = new HashMap(); opt.put("Accept-Encoding", "gzip"); - return new UrlTileDataSource(this, new GeoJsonTileDecoder(this), new LwHttp(getUrl(), opt)); + return new UrlTileDataSource(this, new GeoJsonTileDecoder(this), getHttpEngine()); } public Tag getFeatureTag() { diff --git a/vtm-extras/src/org/oscim/tiling/source/mapnik/MapnikVectorTileSource.java b/vtm-extras/src/org/oscim/tiling/source/mapnik/MapnikVectorTileSource.java index abb551ada..a977a6814 100644 --- a/vtm-extras/src/org/oscim/tiling/source/mapnik/MapnikVectorTileSource.java +++ b/vtm-extras/src/org/oscim/tiling/source/mapnik/MapnikVectorTileSource.java @@ -25,12 +25,12 @@ public class MapnikVectorTileSource extends UrlTileSource { public MapnikVectorTileSource() { - super("http://d1s11ojcu7opje.cloudfront.net/dev/764e0b8d"); + super("http://d1s11ojcu7opje.cloudfront.net/dev/764e0b8d", ""); } @Override public ITileDataSource getDataSource() { - return new UrlTileDataSource(this, new TileDecoder(), new LwHttp(getUrl())); + return new UrlTileDataSource(this, new TileDecoder(), getHttpEngine()); } public int formatTilePath(Tile tile, byte[] path, int pos) { diff --git a/vtm-extras/src/org/oscim/tiling/source/oscimap/OSciMap1TileSource.java b/vtm-extras/src/org/oscim/tiling/source/oscimap/OSciMap1TileSource.java index 50ae2b269..326265197 100644 --- a/vtm-extras/src/org/oscim/tiling/source/oscimap/OSciMap1TileSource.java +++ b/vtm-extras/src/org/oscim/tiling/source/oscimap/OSciMap1TileSource.java @@ -17,7 +17,6 @@ package org.oscim.tiling.source.oscimap; import org.oscim.tiling.ITileDataSource; -import org.oscim.tiling.source.LwHttp; import org.oscim.tiling.source.UrlTileDataSource; import org.oscim.tiling.source.UrlTileSource; @@ -28,13 +27,11 @@ public class OSciMap1TileSource extends UrlTileSource { public OSciMap1TileSource(String url) { - super(url); - setExtension(".osmtile"); - setMimeType("application/osmtile"); + super(url, "/{Z}/{X}/{Y}.osmtile"); } @Override public ITileDataSource getDataSource() { - return new UrlTileDataSource(this, new TileDecoder(), new LwHttp(getUrl())); + return new UrlTileDataSource(this, new TileDecoder(), getHttpEngine()); } } diff --git a/vtm-extras/src/org/oscim/tiling/source/oscimap2/OSciMap2TileSource.java b/vtm-extras/src/org/oscim/tiling/source/oscimap2/OSciMap2TileSource.java index 77f881e29..54aa0bf97 100644 --- a/vtm-extras/src/org/oscim/tiling/source/oscimap2/OSciMap2TileSource.java +++ b/vtm-extras/src/org/oscim/tiling/source/oscimap2/OSciMap2TileSource.java @@ -27,7 +27,6 @@ import org.oscim.core.Tile; import org.oscim.tiling.ITileDataSink; import org.oscim.tiling.ITileDataSource; -import org.oscim.tiling.source.LwHttp; import org.oscim.tiling.source.PbfDecoder; import org.oscim.tiling.source.UrlTileDataSource; import org.oscim.tiling.source.UrlTileSource; @@ -37,14 +36,12 @@ public class OSciMap2TileSource extends UrlTileSource { public OSciMap2TileSource(String url) { - super(url); - setExtension(".osmtile"); - setMimeType("application/osmtile"); + super(url, "/{Z}/{X}/{Y}.osmtile"); } @Override public ITileDataSource getDataSource() { - return new UrlTileDataSource(this, new TileDecoder(), new LwHttp(getUrl())); + return new UrlTileDataSource(this, new TileDecoder(), getHttpEngine()); } static class TileDecoder extends PbfDecoder { diff --git a/vtm-jeo/src/org/oscim/theme/carto/RenderTheme.java b/vtm-jeo/src/org/oscim/theme/carto/RenderTheme.java index 5fb4ea01d..d9a647ebe 100644 --- a/vtm-jeo/src/org/oscim/theme/carto/RenderTheme.java +++ b/vtm-jeo/src/org/oscim/theme/carto/RenderTheme.java @@ -93,7 +93,7 @@ public RenderTheme() { } // get map background - RuleList rules = mStyle.getRules().selectByName("Map", false); + RuleList rules = mStyle.getRules().selectByName("Map", false, false); if (!rules.isEmpty()) { Rule rule = rules.collapse(); RGB bgColor = rule.color(null, BACKGROUND_COLOR, null); diff --git a/vtm-tests/build.gradle b/vtm-tests/build.gradle new file mode 100644 index 000000000..02235b8a2 --- /dev/null +++ b/vtm-tests/build.gradle @@ -0,0 +1,16 @@ +apply plugin: 'java' +apply plugin: 'maven' + +dependencies { + compile project(':vtm') + compile 'com.squareup.okhttp:okhttp:1.5.2' + testCompile 'junit:junit:4.11' + testCompile 'org.mockito:mockito-all:1.9.5' + testCompile 'org.easytesting:fest-assert-core:2.0M10' + testCompile 'com.squareup.okhttp:mockwebserver:1.5.2' +} + +sourceSets { + main.java.srcDirs = ['src'] + test.java.srcDirs = ['test'] +} diff --git a/vtm-tests/test/org/oscim/tiling/source/OkHttpEngineTest.java b/vtm-tests/test/org/oscim/tiling/source/OkHttpEngineTest.java new file mode 100644 index 000000000..ddc1e083d --- /dev/null +++ b/vtm-tests/test/org/oscim/tiling/source/OkHttpEngineTest.java @@ -0,0 +1,93 @@ +package org.oscim.tiling.source; + +import static org.fest.assertions.api.Assertions.assertThat; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.oscim.core.Tile; +import org.oscim.tiling.source.oscimap4.OSciMap4TileSource; + +import com.squareup.okhttp.mockwebserver.MockResponse; +import com.squareup.okhttp.mockwebserver.MockWebServer; +import com.squareup.okhttp.mockwebserver.RecordedRequest; + +public class OkHttpEngineTest { + private OkHttpEngine engine; + private MockWebServer server; + private MockResponse mockResponse; + + @Before + public void setUp() throws Exception { + mockResponse = new MockResponse(); + mockResponse.setBody("TEST RESPONSE".getBytes()); + server = new MockWebServer(); + server.enqueue(mockResponse); + server.play(); + engine = (OkHttpEngine) new OkHttpEngine.OkHttpFactory() + .create(new OSciMap4TileSource(server.getUrl("/tiles/vtm").toString())); + } + + @After + public void tearDown() throws Exception { + server.shutdown(); + } + + @Test + public void shouldNotBeNull() throws Exception { + assertThat(engine).isNotNull(); + } + + @Test(expected = IllegalArgumentException.class) + public void sendRequest_shouldRejectNullTile() throws Exception { + engine.sendRequest(null); + } + + @Test + public void sendRequest_shouldAppendXYZToPath() throws Exception { + engine.sendRequest(new Tile(1, 2, new Integer(3).byteValue())); + + RecordedRequest request = server.takeRequest(); + assertThat(request.getPath()).isEqualTo("/tiles/vtm/3/1/2.vtm"); + } + + @Test + public void read_shouldReturnResponseStream() throws Exception { + engine.sendRequest(new Tile(1, 2, new Integer(3).byteValue())); + + InputStream responseStream = engine.read(); + String response = new BufferedReader(new InputStreamReader(responseStream)).readLine(); + assertThat(response).isEqualTo("TEST RESPONSE"); + } + + @Test(expected = IOException.class) + public void close_shouldCloseInputStream() throws Exception { + engine.sendRequest(new Tile(1, 2, new Integer(3).byteValue())); + engine.close(); + + // Calling read after the stream is closed should throw an exception. + InputStream responseStream = engine.read(); + responseStream.read(); + } + + @Test(expected = IOException.class) + public void requestCompleted_shouldCloseInputStream() throws Exception { + engine.sendRequest(new Tile(1, 2, new Integer(3).byteValue())); + engine.requestCompleted(true); + + // Calling read after the stream is closed should throw an exception. + InputStream responseStream = engine.read(); + responseStream.read(); + } + + @Test + public void requestCompleted_shouldReturnValueGiven() throws Exception { + assertThat(engine.requestCompleted(true)).isTrue(); + assertThat(engine.requestCompleted(false)).isFalse(); + } +} diff --git a/vtm-tests/test/org/oscim/tiling/source/UrlTileSourceTest.java b/vtm-tests/test/org/oscim/tiling/source/UrlTileSourceTest.java new file mode 100644 index 000000000..b52eb8305 --- /dev/null +++ b/vtm-tests/test/org/oscim/tiling/source/UrlTileSourceTest.java @@ -0,0 +1,56 @@ +package org.oscim.tiling.source; + +import static org.fest.assertions.api.Assertions.assertThat; + +import org.junit.Before; +import org.junit.Test; +import org.oscim.tiling.ITileDataSource; + +public class UrlTileSourceTest { + private UrlTileSource tileSource; + + @Before + public void setUp() throws Exception { + tileSource = new TestTileSource("http://example.org/tiles/vtm", "/{Z}/{X}/{Z}.vtm"); + } + + @Test + public void shouldNotBeNull() throws Exception { + assertThat(tileSource).isNotNull(); + } + + @Test + public void shouldUseDefaultHttpEngine() throws Exception { + TestTileDataSource dataSource = (TestTileDataSource) tileSource.getDataSource(); + assertThat(dataSource.getConnection()).isInstanceOf(LwHttp.class); + } + + @Test + public void shouldUseCustomHttpEngine() throws Exception { + tileSource.setHttpEngine(new OkHttpEngine.OkHttpFactory()); + TestTileDataSource dataSource = (TestTileDataSource) tileSource.getDataSource(); + assertThat(dataSource.getConnection()).isInstanceOf(OkHttpEngine.class); + } + + class TestTileSource extends UrlTileSource { + public TestTileSource(String urlString, String tilePath) { + super(urlString, tilePath); + } + + @Override + public ITileDataSource getDataSource() { + return new TestTileDataSource(this, null, getHttpEngine()); + } + } + + class TestTileDataSource extends UrlTileDataSource { + public TestTileDataSource(UrlTileSource tileSource, ITileDecoder tileDecoder, + HttpEngine conn) { + super(tileSource, tileDecoder, conn); + } + + public HttpEngine getConnection() { + return mConn; + } + } +} diff --git a/vtm-tests/test/org/oscim/tiling/source/bitmap/BitmapTileSourceTest.java b/vtm-tests/test/org/oscim/tiling/source/bitmap/BitmapTileSourceTest.java new file mode 100644 index 000000000..8381a1d2a --- /dev/null +++ b/vtm-tests/test/org/oscim/tiling/source/bitmap/BitmapTileSourceTest.java @@ -0,0 +1,62 @@ +package org.oscim.tiling.source.bitmap; + +import static org.fest.assertions.api.Assertions.assertThat; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.oscim.tiling.ITileDataSource; +import org.oscim.tiling.source.HttpEngine; +import org.oscim.tiling.source.LwHttp; +import org.oscim.tiling.source.OkHttpEngine; +import org.oscim.tiling.source.UrlTileDataSource; +import org.oscim.tiling.source.UrlTileSource; + +public class BitmapTileSourceTest { + private BitmapTileSource tileSource; + + @Before + public void setUp() throws Exception { + tileSource = new BitmapTileSource("http://tile.openstreetmap.org", 0, 18); + } + + @Test + public void shouldNotBeNull() throws Exception { + assertThat(tileSource).isNotNull(); + } + + @Test + public void shouldUseLwHttp() throws Exception { + LwHttp lwHttp = Mockito.mock(LwHttp.class); + tileSource.setHttpEngine(new TestHttpFactory(lwHttp)); + ITileDataSource dataSource = tileSource.getDataSource(); + dataSource.destroy(); + Mockito.verify(lwHttp).close(); + } + + @Test + public void shouldUseOkHttp() throws Exception { + OkHttpEngine okHttp = Mockito.mock(OkHttpEngine.class); + tileSource.setHttpEngine(new TestHttpFactory(okHttp)); + UrlTileDataSource dataSource = (UrlTileDataSource) tileSource.getDataSource(); + dataSource.destroy(); + Mockito.verify(okHttp).close(); + } + + /** + * Test factory that allows the specific {@link HttpEngine} instance to be + * set. + */ + class TestHttpFactory implements HttpEngine.Factory { + final HttpEngine engine; + + public TestHttpFactory(HttpEngine engine) { + this.engine = engine; + } + + @Override + public HttpEngine create(UrlTileSource tileSource) { + return engine; + } + } +} diff --git a/vtm-tests/test/org/oscim/tiling/source/oscimap4/OSciMap4TileSourceTest.java b/vtm-tests/test/org/oscim/tiling/source/oscimap4/OSciMap4TileSourceTest.java new file mode 100644 index 000000000..542769658 --- /dev/null +++ b/vtm-tests/test/org/oscim/tiling/source/oscimap4/OSciMap4TileSourceTest.java @@ -0,0 +1,61 @@ +package org.oscim.tiling.source.oscimap4; + +import static org.fest.assertions.api.Assertions.assertThat; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.oscim.tiling.ITileDataSource; +import org.oscim.tiling.source.HttpEngine; +import org.oscim.tiling.source.LwHttp; +import org.oscim.tiling.source.OkHttpEngine; +import org.oscim.tiling.source.UrlTileSource; + +public class OSciMap4TileSourceTest { + private OSciMap4TileSource tileSource; + + @Before + public void setUp() throws Exception { + tileSource = new OSciMap4TileSource("http://www.example.org/tiles/vtm"); + } + + @Test + public void shouldNotBeNull() throws Exception { + assertThat(tileSource).isNotNull(); + } + + @Test + public void shouldUseLwHttp() throws Exception { + LwHttp lwHttp = Mockito.mock(LwHttp.class); + tileSource.setHttpEngine(new TestHttpFactory(lwHttp)); + ITileDataSource dataSource = tileSource.getDataSource(); + dataSource.destroy(); + Mockito.verify(lwHttp).close(); + } + + @Test + public void shouldUseOkHttp() throws Exception { + OkHttpEngine okHttp = Mockito.mock(OkHttpEngine.class); + tileSource.setHttpEngine(new TestHttpFactory(okHttp)); + ITileDataSource dataSource = tileSource.getDataSource(); + dataSource.destroy(); + Mockito.verify(okHttp).close(); + } + + /** + * Test factory that allows the specific {@link HttpEngine} instance to be + * set. + */ + class TestHttpFactory implements HttpEngine.Factory { + final HttpEngine engine; + + public TestHttpFactory(HttpEngine engine) { + this.engine = engine; + } + + @Override + public HttpEngine create(UrlTileSource tileSource) { + return engine; + } + } +} diff --git a/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/LwHttp.java b/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/LwHttp.java index b42cd8310..3af96e530 100644 --- a/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/LwHttp.java +++ b/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/LwHttp.java @@ -16,9 +16,11 @@ import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.net.URL; import org.oscim.core.Tile; +import org.oscim.layers.tile.MapTile; import com.google.gwt.typedarrays.client.Uint8ArrayNative; import com.google.gwt.typedarrays.shared.Uint8Array; @@ -26,20 +28,20 @@ import com.google.gwt.xhr.client.XMLHttpRequest; import com.google.gwt.xhr.client.XMLHttpRequest.ResponseType; -public class LwHttp { +public class LwHttp implements HttpEngine { //static final Logger log = LoggerFactory.getLogger(LwHttp.class); private final String mUrlPath; - private final byte[] mRequestBuffer; private int mContentLength = -1; private XMLHttpRequest mHttpRequest; private ReadyStateChangeHandler mResponseHandler; - public LwHttp(URL url) { + public LwHttp(UrlTileSource tileSource) { + mTileSource = tileSource; + URL url = tileSource.getUrl(); mUrlPath = url.toString(); - mRequestBuffer = new byte[1024]; } static class Buffer extends InputStream { @@ -67,17 +69,11 @@ public void close() { mHttpRequest.abort(); } - private UrlTileDataSource mDataSource; + private UrlTileSource mTileSource; - public boolean sendRequest(Tile tile, UrlTileDataSource dataSource) throws IOException { - mDataSource = dataSource; + public boolean sendRequest(MapTile tile, final UrlTileDataSource dataSource) throws IOException { - byte[] request = mRequestBuffer; - int pos = 0; - - pos = dataSource.getTileSource().formatTilePath(tile, request, pos); - - String url = mUrlPath + (new String(request, 0, pos)); + String url = mUrlPath + mTileSource.formatTilePath(tile); mHttpRequest = XMLHttpRequest.create(); mHttpRequest.open("GET", url); @@ -97,9 +93,9 @@ public void onReadyStateChange(XMLHttpRequest xhr) { if (status == 200) { Uint8Array buf = Uint8ArrayNative.create(xhr.getResponseArrayBuffer()); - mDataSource.process(new Buffer(buf)); + dataSource.process(new Buffer(buf)); } else { - mDataSource.process(null); + dataSource.process(null); } } } @@ -150,4 +146,35 @@ public void requestCompleted() { public int getContentLength() { return mContentLength; } + + public static class LwHttpFactory implements HttpEngine.Factory { + + @Override + public HttpEngine create(UrlTileSource tileSource) { + return new LwHttp(tileSource); + } + } + + @Override + public InputStream read() throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setCache(OutputStream os) { + // TODO Auto-generated method stub + } + + @Override + public boolean requestCompleted(boolean success) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean sendRequest(Tile tile) throws IOException { + // TODO Auto-generated method stub + return false; + } } diff --git a/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/UrlTileDataSource.java b/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/UrlTileDataSource.java index f9afeb800..5f344d043 100644 --- a/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/UrlTileDataSource.java +++ b/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/UrlTileDataSource.java @@ -35,10 +35,10 @@ public class UrlTileDataSource implements ITileDataSource { protected final ITileDecoder mTileDecoder; protected final UrlTileSource mTileSource; - public UrlTileDataSource(UrlTileSource tileSource, ITileDecoder tileDecoder, LwHttp conn) { + public UrlTileDataSource(UrlTileSource tileSource, ITileDecoder tileDecoder, HttpEngine conn) { mTileSource = tileSource; mTileDecoder = tileDecoder; - mConn = conn; + mConn = (LwHttp) conn; } UrlTileSource getTileSource() { @@ -80,12 +80,12 @@ public void continueLoading() { log.debug("{} failed", tile); // FIXME - mConn.requestCompleted(); + mConn.requestCompleted(true); sink.completed(win ? SUCCESS : FAILED); } else { // FIXME - mConn.requestCompleted(); + mConn.requestCompleted(false); sink.completed(FAILED); } diff --git a/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/bitmap/BitmapTileSource.java b/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/bitmap/BitmapTileSource.java index 87801f572..f8af2cafe 100644 --- a/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/bitmap/BitmapTileSource.java +++ b/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/bitmap/BitmapTileSource.java @@ -31,9 +31,17 @@ public class BitmapTileSource extends UrlTileSource { * Use e.g. setExtension(".jpg") to overide ending or * implement getUrlString() for custom formatting. */ + public BitmapTileSource(String url, int zoomMin, int zoomMax) { - super(url, zoomMin, zoomMax); - setExtension(".png"); + super(url, "/{Z}/{X}/{Y}.png", zoomMin, zoomMax); + } + + public BitmapTileSource(String url, int zoomMin, int zoomMax, String extension) { + super(url, "/{Z}/{X}/{Y}" + extension, zoomMin, zoomMax); + } + + public BitmapTileSource(String url, String tilePath, int zoomMin, int zoomMax) { + super(url, tilePath, zoomMin, zoomMax); } @Override @@ -41,10 +49,9 @@ public ITileDataSource getDataSource() { return new BitmapTileDataSource(this); } - public static class BitmapTileDataSource implements ITileDataSource { + public class BitmapTileDataSource implements ITileDataSource { protected final UrlTileSource mTileSource; - private final byte[] mRequestBuffer = new byte[1024]; public BitmapTileDataSource(BitmapTileSource bitmapTileSource) { mTileSource = bitmapTileSource; @@ -53,10 +60,8 @@ public BitmapTileDataSource(BitmapTileSource bitmapTileSource) { @Override public void query(final MapTile tile, final ITileDataSink sink) { - int pos = mTileSource.formatTilePath(tile, mRequestBuffer, 0); - String url = mTileSource.getUrl() - + (new String(mRequestBuffer, 0, pos)); + + BitmapTileSource.this.formatTilePath(tile); SafeUri uri = UriUtils.fromTrustedString(url); diff --git a/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/geojson/GeoJsonTileSource.java b/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/geojson/GeoJsonTileSource.java index 2e5c2c8b6..934f8dc37 100644 --- a/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/geojson/GeoJsonTileSource.java +++ b/vtm-web/src/org/oscim/gdx/emu/org/oscim/tiling/source/geojson/GeoJsonTileSource.java @@ -30,8 +30,7 @@ public abstract class GeoJsonTileSource extends UrlTileSource { static final Logger log = LoggerFactory.getLogger(GeoJsonTileSource.class); public GeoJsonTileSource(String url) { - super(url); - setExtension(".json"); + super(url, "/{Z}/{X}/{Y}.json"); } @Override diff --git a/vtm-web/src/org/oscim/tiling/source/JsonTileDataSource.java b/vtm-web/src/org/oscim/tiling/source/JsonTileDataSource.java index f4b257e0f..35511ecc5 100644 --- a/vtm-web/src/org/oscim/tiling/source/JsonTileDataSource.java +++ b/vtm-web/src/org/oscim/tiling/source/JsonTileDataSource.java @@ -41,8 +41,6 @@ public class JsonTileDataSource implements ITileDataSource { protected final GeoJsonTileDecoder mTileDecoder; protected final UrlTileSource mTileSource; - private final byte[] mRequestBuffer = new byte[1024]; - public JsonTileDataSource(GeoJsonTileSource tileSource) { mTileSource = tileSource; mTileDecoder = new GeoJsonTileDecoder(tileSource); @@ -61,10 +59,8 @@ public void query(MapTile tile, ITileDataSink sink) { mSink = sink; try { - int pos = mTileSource.formatTilePath(tile, mRequestBuffer, 0); - String url = mTileSource.getUrl() - + (new String(mRequestBuffer, 0, pos)); + + mTileSource.formatTilePath(tile); doGet(url); } catch (Exception e) { diff --git a/vtm/build.gradle b/vtm/build.gradle index 457474275..6df7c2046 100644 --- a/vtm/build.gradle +++ b/vtm/build.gradle @@ -5,6 +5,7 @@ configurations { providedCompile } dependencies { compile 'org.slf4j:slf4j-api:1.7.6' + providedCompile 'com.squareup.okhttp:okhttp:1.5.2' providedCompile 'com.google.code.findbugs:annotations:2.0.1' } diff --git a/vtm/src/org/oscim/tiling/source/HttpEngine.java b/vtm/src/org/oscim/tiling/source/HttpEngine.java new file mode 100644 index 000000000..3483b6aad --- /dev/null +++ b/vtm/src/org/oscim/tiling/source/HttpEngine.java @@ -0,0 +1,25 @@ +package org.oscim.tiling.source; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.oscim.core.Tile; + +public interface HttpEngine { + + InputStream read() throws IOException; + + boolean sendRequest(Tile tile) throws IOException; + + void close(); + + void setCache(OutputStream os); + + boolean requestCompleted(boolean success); + + public interface Factory { + public abstract HttpEngine create(UrlTileSource tileSource); + } + +} diff --git a/vtm/src/org/oscim/tiling/source/LwHttp.java b/vtm/src/org/oscim/tiling/source/LwHttp.java index 66f959d9d..4aaa51483 100644 --- a/vtm/src/org/oscim/tiling/source/LwHttp.java +++ b/vtm/src/org/oscim/tiling/source/LwHttp.java @@ -24,7 +24,6 @@ import java.net.Socket; import java.net.SocketAddress; import java.net.URL; -import java.util.Map; import java.util.Map.Entry; import org.oscim.core.Tile; @@ -35,14 +34,13 @@ /** * Lightweight HTTP connection for tile loading. Does not do redirects, * https, full header parsing or stuff. - * - * TODO extract API interface to be used by UrlTileSource so that one - * could also use HttpUrlConnection, etc. */ -public class LwHttp { +public class LwHttp implements HttpEngine { static final Logger log = LoggerFactory.getLogger(LwHttp.class); static final boolean dbg = false; + private final UrlTileSource mTileSource; + 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_LENGTH = "Content-Length".getBytes(); @@ -71,16 +69,9 @@ public class LwHttp { private final byte[] REQUEST_GET_END; private final byte[] mRequestBuffer; - /** - * @param url - * Base url for tiles - */ - public LwHttp(URL url) { - this(url, null); - } - - public LwHttp(URL url, Map header) { - + private LwHttp(UrlTileSource tileSource) { + mTileSource = tileSource; + URL url = tileSource.getUrl(); int port = url.getPort(); if (port < 0) port = 80; @@ -92,9 +83,9 @@ public LwHttp(URL url, Map header) { REQUEST_GET_START = ("GET " + path).getBytes(); String addRequest = ""; - if (header != null) { + if (tileSource.getRequestHeader() != null) { StringBuffer sb = new StringBuffer(); - for (Entry l : header.entrySet()) + for (Entry l : tileSource.getRequestHeader().entrySet()) sb.append('\n').append(l.getKey()).append(": ").append(l.getValue()); addRequest = sb.toString(); } @@ -331,7 +322,8 @@ public InputStream read() throws IOException { return is; } - public boolean sendRequest(UrlTileSource tileSource, Tile tile) throws IOException { + @Override + public boolean sendRequest(Tile tile) throws IOException { if (mSocket != null) { if (mMaxReq-- <= 0) @@ -360,7 +352,7 @@ else if (System.nanoTime() - mLastRequest > RESPONSE_TIMEOUT) byte[] request = mRequestBuffer; int pos = REQUEST_GET_START.length; - pos = tileSource.formatTilePath(tile, request, pos); + pos = formatTilePath(mTileSource, tile, request, pos); int len = REQUEST_GET_END.length; System.arraycopy(REQUEST_GET_END, 0, request, pos, len); @@ -404,6 +396,7 @@ private boolean lwHttpConnect() throws IOException { return true; } + @Override public void close() { if (mSocket == null) return; @@ -417,6 +410,7 @@ public void close() { mResponseStream = null; } + @Override public void setCache(OutputStream os) { if (mResponseStream == null) return; @@ -424,6 +418,7 @@ public void setCache(OutputStream os) { mResponseStream.setCache(os); } + @Override public boolean requestCompleted(boolean success) { if (mResponseStream == null) return false; @@ -498,4 +493,46 @@ private static boolean check(byte[] string, byte[] buffer, return true; } + + /** + * Write tile url - the low level, no-allocations method, + * + * override getTileUrl() for custom url formatting using + * Strings + * + * @param tile the Tile + * @param buf to write url string + * @param pos current position + * @return new position + */ + public int formatTilePath(UrlTileSource tileSource, Tile tile, byte[] buf, int pos) { + String p = tileSource.formatTilePath(tile); + log.debug("path {}", p); + //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); + // byte[] ext = tileSource.mExtBytes; + // if (ext == null) + // return pos; + // + // System.arraycopy(ext, 0, buf, pos, ext.length); + // return pos + ext.length; + } + + public static class LwHttpFactory implements HttpEngine.Factory { + + @Override + public HttpEngine create(UrlTileSource tileSource) { + return new LwHttp(tileSource); + } + } } diff --git a/vtm/src/org/oscim/tiling/source/OkHttpEngine.java b/vtm/src/org/oscim/tiling/source/OkHttpEngine.java new file mode 100644 index 000000000..7e39ad739 --- /dev/null +++ b/vtm/src/org/oscim/tiling/source/OkHttpEngine.java @@ -0,0 +1,82 @@ +package org.oscim.tiling.source; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +import org.oscim.core.Tile; + +import com.squareup.okhttp.OkHttpClient; + +public class OkHttpEngine implements HttpEngine { + private final OkHttpClient mClient; + private final UrlTileSource mTileSource; + + public static class OkHttpFactory implements HttpEngine.Factory { + private final OkHttpClient mClient; + + public OkHttpFactory() { + mClient = new OkHttpClient(); + } + + @Override + public HttpEngine create(UrlTileSource tileSource) { + return new OkHttpEngine(mClient, tileSource); + } + } + + private InputStream inputStream; + + public OkHttpEngine(OkHttpClient client, UrlTileSource tileSource) { + mClient = client; + mTileSource = tileSource; + } + + @Override + public InputStream read() throws IOException { + return inputStream; + } + + HttpURLConnection openConnection(Tile tile) throws MalformedURLException { + return mClient.open(new URL(mTileSource.getUrl() + + mTileSource.formatTilePath(tile))); + } + + @Override + public boolean sendRequest(Tile tile) throws IOException { + if (tile == null) { + throw new IllegalArgumentException("Tile cannot be null."); + } + + final HttpURLConnection connection = openConnection(tile); + + inputStream = connection.getInputStream(); + + return true; + } + + @Override + public void close() { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @Override + public void setCache(OutputStream os) { + // TODO: Evaluate OkHttp response cache and determine if additional caching is required. + } + + @Override + public boolean requestCompleted(boolean success) { + close(); + return success; + } +} diff --git a/vtm/src/org/oscim/tiling/source/UrlTileDataSource.java b/vtm/src/org/oscim/tiling/source/UrlTileDataSource.java index f26c90c00..4cf814cf2 100644 --- a/vtm/src/org/oscim/tiling/source/UrlTileDataSource.java +++ b/vtm/src/org/oscim/tiling/source/UrlTileDataSource.java @@ -38,12 +38,12 @@ public class UrlTileDataSource implements ITileDataSource { static final Logger log = LoggerFactory.getLogger(UrlTileDataSource.class); - protected final LwHttp mConn; + protected final HttpEngine mConn; protected final ITileDecoder mTileDecoder; protected final UrlTileSource mTileSource; protected final boolean mUseCache; - public UrlTileDataSource(UrlTileSource tileSource, ITileDecoder tileDecoder, LwHttp conn) { + public UrlTileDataSource(UrlTileSource tileSource, ITileDecoder tileDecoder, HttpEngine conn) { mTileDecoder = tileDecoder; mTileSource = tileSource; mUseCache = (tileSource.tileCache != null); @@ -75,7 +75,7 @@ public void query(MapTile tile, ITileDataSink sink) { TileWriter cacheWriter = null; try { InputStream is; - if (!mConn.sendRequest(mTileSource, tile)) { + if (!mConn.sendRequest(tile)) { log.debug("{} Request failed", tile); } else if ((is = mConn.read()) == null) { log.debug("{} Network Error", tile); diff --git a/vtm/src/org/oscim/tiling/source/UrlTileSource.java b/vtm/src/org/oscim/tiling/source/UrlTileSource.java index 89515d14f..b7546ba3e 100644 --- a/vtm/src/org/oscim/tiling/source/UrlTileSource.java +++ b/vtm/src/org/oscim/tiling/source/UrlTileSource.java @@ -18,29 +18,44 @@ import java.net.MalformedURLException; import java.net.URL; +import java.util.Map; import org.oscim.core.Tile; import org.oscim.tiling.TileSource; +import org.oscim.tiling.source.LwHttp.LwHttpFactory; public abstract class UrlTileSource extends TileSource { private final URL mUrl; - private byte[] mExt; + private final String[] mTilePath; + + private HttpEngine.Factory mHttpFactory; + private Map mRequestHeaders; + + public UrlTileSource(String url, String tilePath, int zoomMin, int zoomMax) { + this(url, tilePath); + mZoomMin = zoomMin; + mZoomMax = zoomMax; + } + + /** + * @param urlString 'http://example.com/' + * @param tilePath replacement string for tile coordinates, + * e.g. '{Z}/{X}/{Y}.png' + */ + public UrlTileSource(String urlString, String tilePath) { + + if (tilePath == null) + throw new IllegalArgumentException("tilePath cannot be null."); - public UrlTileSource(String urlString) { URL url = null; try { url = new URL(urlString); } catch (MalformedURLException e) { - e.printStackTrace(); + throw new IllegalArgumentException(e); } mUrl = url; - } - - public UrlTileSource(String url, int zoomMin, int zoomMax) { - this(url); - mZoomMin = zoomMin; - mZoomMax = zoomMax; + mTilePath = tilePath.split("\\{|\\}"); } @Override @@ -53,58 +68,52 @@ public void close() { } - protected void setExtension(String ext) { - if (ext == null) { - mExt = null; - return; + public URL getUrl() { + return mUrl; + } + + public String formatTilePath(Tile tile) { + // TODO only use the actual replacement positions. + + StringBuilder sb = new StringBuilder(); + for (String b : mTilePath) { + if (b.length() == 1) { + switch (b.charAt(0)) { + case 'X': + sb.append(tile.tileX); + continue; + case 'Y': + sb.append(tile.tileY); + continue; + case 'Z': + sb.append(tile.zoomLevel); + continue; + default: + break; + } + } + sb.append(b); } - mExt = ext.getBytes(); + return sb.toString(); } - protected void setMimeType(String string) { + public void setHttpEngine(HttpEngine.Factory httpFactory) { + mHttpFactory = httpFactory; + } + public void setHttpRequestHeaders(Map options) { + mRequestHeaders = options; } - /** - * Create url path for tile - */ - protected String getTileUrl(Tile tile) { - return null; + protected Map getRequestHeader() { + return mRequestHeaders; } - /** - * Write tile url - the low level, no-allocations method, - * - * override getTileUrl() for custom url formatting using - * Strings - * - * @param tile the Tile - * @param buf to write url string - * @param pos current position - * @return new position - */ - public 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; + public HttpEngine getHttpEngine() { + if (mHttpFactory == null) { + mHttpFactory = new LwHttpFactory(); } - 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; - } - - public URL getUrl() { - return mUrl; + return mHttpFactory.create(this); } } diff --git a/vtm/src/org/oscim/tiling/source/bitmap/BitmapTileSource.java b/vtm/src/org/oscim/tiling/source/bitmap/BitmapTileSource.java index 92a718de5..3e2fc0553 100644 --- a/vtm/src/org/oscim/tiling/source/bitmap/BitmapTileSource.java +++ b/vtm/src/org/oscim/tiling/source/bitmap/BitmapTileSource.java @@ -26,13 +26,20 @@ public class BitmapTileSource extends UrlTileSource { * implement getUrlString() for custom formatting. */ public BitmapTileSource(String url, int zoomMin, int zoomMax) { - super(url, zoomMin, zoomMax); - setExtension(".png"); + super(url, "/{Z}/{X}/{Y}.png", zoomMin, zoomMax); + } + + public BitmapTileSource(String url, int zoomMin, int zoomMax, String extension) { + super(url, "/{Z}/{X}/{Y}" + extension, zoomMin, zoomMax); + } + + public BitmapTileSource(String url, String tilePath, int zoomMin, int zoomMax) { + super(url, tilePath, zoomMin, zoomMax); } @Override public ITileDataSource getDataSource() { - return new UrlTileDataSource(this, new BitmapTileDecoder(), new LwHttp(getUrl())); + return new UrlTileDataSource(this, new BitmapTileDecoder(), getHttpEngine()); } 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 92fea620f..f74d51195 100644 --- a/vtm/src/org/oscim/tiling/source/bitmap/DefaultSources.java +++ b/vtm/src/org/oscim/tiling/source/bitmap/DefaultSources.java @@ -1,6 +1,5 @@ package org.oscim.tiling.source.bitmap; -import org.oscim.core.Tile; import org.oscim.layers.tile.bitmap.BitmapTileLayer.FadeStep; /** @@ -35,15 +34,13 @@ public StamenWatercolor() { public static class ImagicoLandcover extends BitmapTileSource { public ImagicoLandcover() { - super("http://www.imagico.de/map/tiles/landcover", 0, 6); - setExtension(".jpg"); + super("http://www.imagico.de/map/tiles/landcover", 0, 6, ".jpg"); } } public static class MapQuestAerial extends BitmapTileSource { public MapQuestAerial() { - super("http://otile1.mqcdn.com/tiles/1.0.0/sat", 0, 8); - setExtension(".jpg"); + super("http://otile1.mqcdn.com/tiles/1.0.0/sat", 0, 8, ".jpg"); } @Override @@ -64,38 +61,17 @@ public FadeStep[] getFadeSteps() { } public static class ArcGISWorldShaded extends BitmapTileSource { - private final StringBuilder sb = new StringBuilder(32); - public ArcGISWorldShaded() { - super("http://server.arcgisonline.com/ArcGIS/rest/services", 0, 13); - } - - @Override - 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); - sb.append('/').append(tile.tileY); - sb.append('/').append(tile.tileX); - return sb.toString(); + super("http://server.arcgisonline.com/ArcGIS/rest/services" + + "/World_Shaded_Relief/MapServer/tile/", + "{Z}/{Y}/{X}", 0, 13); } } 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); - } - - @Override - 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); - return sb.toString(); + super("http://129.206.74.245:8004/tms_hs.ashx", + "?x={X}&y={Y}&z={Z}", 2, 16); } } @@ -104,23 +80,8 @@ public synchronized String getTileUrl(Tile tile) { * https://developers.google.com/maps/faq */ public static class GoogleMaps extends BitmapTileSource { - private final StringBuilder sb = new StringBuilder(60); - public GoogleMaps(String hostName) { - super(hostName, 1, 20); //jpeg for sat - } - - @Override - public synchronized String getTileUrl(Tile tile) { - sb.setLength(0); - sb.append("/vt/x="); //lyrs=y& - sb.append(tile.tileX); - sb.append("&y="); - sb.append(tile.tileY); - sb.append("&z="); - sb.append(tile.zoomLevel); - sb.append("&s=Galileo&scale=2"); - return sb.toString(); + super(hostName, "/vt/x={X}&y={Y}&z={Z}&s=Galileo&scale=2", 1, 20); //jpeg for sat } } diff --git a/vtm/src/org/oscim/tiling/source/oscimap4/OSciMap4TileSource.java b/vtm/src/org/oscim/tiling/source/oscimap4/OSciMap4TileSource.java index b5dbc5a8d..d98597ccb 100644 --- a/vtm/src/org/oscim/tiling/source/oscimap4/OSciMap4TileSource.java +++ b/vtm/src/org/oscim/tiling/source/oscimap4/OSciMap4TileSource.java @@ -17,7 +17,6 @@ package org.oscim.tiling.source.oscimap4; import org.oscim.tiling.ITileDataSource; -import org.oscim.tiling.source.LwHttp; import org.oscim.tiling.source.UrlTileDataSource; import org.oscim.tiling.source.UrlTileSource; @@ -28,12 +27,11 @@ public OSciMap4TileSource() { } public OSciMap4TileSource(String url) { - super(url); - setExtension(".vtm"); + super(url, "/{Z}/{X}/{Y}.vtm"); } @Override public ITileDataSource getDataSource() { - return new UrlTileDataSource(this, new TileDecoder(), new LwHttp(getUrl())); + return new UrlTileDataSource(this, new TileDecoder(), getHttpEngine()); } }