serious refactor: TileLoader:

- no more duplication of TileLoaders for GWT ->
- decouple loadTile() from TileDataSource completed() call
- all TileDataSource MUST call completed(success) in any case now
This commit is contained in:
Hannes Janetzek
2014-03-09 05:43:38 +01:00
parent a8f46fdd8d
commit c24b4addfa
17 changed files with 216 additions and 658 deletions

View File

@@ -14,10 +14,17 @@
*/
package org.oscim.layers.tile;
import static org.oscim.tiling.ITileDataSink.QueryResult.FAILED;
import static org.oscim.tiling.ITileDataSink.QueryResult.SUCCESS;
import org.oscim.backend.canvas.Bitmap;
import org.oscim.core.MapElement;
import org.oscim.tiling.ITileDataSink;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.Timer;
public abstract class TileLoader {
public abstract class TileLoader implements ITileDataSink {
private final TileManager mTileManager;
private Timer mTimer;
@@ -31,7 +38,7 @@ public abstract class TileLoader {
public abstract void cleanup();
protected abstract boolean executeJob(MapTile tile);
protected abstract boolean loadTile(MapTile tile);
boolean isInterrupted;
@@ -70,48 +77,65 @@ public abstract class TileLoader {
mPausing = false;
}
boolean mWorking;
protected boolean mWorking;
protected MapTile mTile;
public void go() {
if (mWorking) {
if (mWorking)
return;
}
MapTile tile = mTileManager.getTileJob();
mTile = mTileManager.getTileJob();
if (tile == null)
if (mTile == null)
return;
try {
executeJob(tile);
loadTile(mTile);
mWorking = true;
} catch (Exception e) {
e.printStackTrace();
tile.clear();
jobCompleted(tile, false);
completed(FAILED);
}
}
public void jobCompleted(MapTile tile, boolean success) {
if (isInterrupted)
success = false;
/**
* Callback to be called by TileDataSource when finished
* loading or on failure. MUST BE CALLED IN ANY CASE!
*/
@Override
public void completed(QueryResult result) {
boolean success = (result == SUCCESS) && !isInterrupted;
mTileManager.jobCompleted(tile, success);
mTileManager.jobCompleted(mTile, success);
mTile = null;
mWorking = false;
if (!mPausing && mTileManager.hasTileJobs()) {
if (mPausing || !mTileManager.hasTileJobs())
return;
Gdx.app.postRunnable(new Runnable() {
@Override
public void run() {
go();
}
});
}
Gdx.app.postRunnable(new Runnable() {
@Override
public void run() {
go();
}
});
}
/**
* Called by TileDataSource
*/
@Override
public void process(MapElement element) {
}
/**
* Called by TileDataSource
*/
@Override
public void setTileImage(Bitmap bitmap) {
}
}

View File

@@ -1,79 +0,0 @@
package org.oscim.layers.tile.bitmap;
import static org.oscim.layers.tile.MapTile.State.CANCEL;
import org.oscim.backend.canvas.Bitmap;
import org.oscim.core.MapElement;
import org.oscim.core.Tile;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileLoader;
import org.oscim.layers.tile.TileManager;
import org.oscim.renderer.elements.BitmapLayer;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.tiling.ITileDataSink;
import org.oscim.tiling.ITileDataSource;
import org.oscim.tiling.ITileDataSource.QueryResult;
import org.oscim.tiling.TileSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BitmapTileLoader extends TileLoader implements ITileDataSink {
protected static final Logger log = LoggerFactory.getLogger(BitmapTileLoader.class);
private final ITileDataSource mTileDataSource;
private MapTile mTile;
public BitmapTileLoader(TileManager tileManager, TileSource tileSource) {
super(tileManager);
mTileDataSource = tileSource.getDataSource();
}
@Override
public void cleanup() {
mTile = null;
}
@Override
protected boolean executeJob(MapTile tile) {
mTile = tile;
//QueryResult result = null;
//try {
if (mTileDataSource.executeQuery(tile, this) != QueryResult.SUCCESS) {
return false;
}
//}
// catch (CancellationException e) {
// log.debug("{} was canceled", mTile);
// } catch (Exception e) {
// log.debug("{} {}", mTile, e.getMessage());
// } finally {
// mTile = null;
// }
return true;
//return result == QueryResult.SUCCESS;
}
@Override
public void setTileImage(Bitmap bitmap) {
if (isCanceled() || mTile.state(CANCEL))
return;
//throw new CancellationException();
BitmapLayer l = new BitmapLayer(false);
l.setBitmap(bitmap, Tile.SIZE, Tile.SIZE);
mTile.layers = new ElementLayers();
mTile.layers.setTextureLayers(l);
}
@Override
public void process(MapElement element) {
}
@Override
public void completed(boolean success) {
jobCompleted(mTile, success);
mTile = null;
}
}

View File

@@ -1,404 +0,0 @@
/*
* Copyright 2012, 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.layers.tile.vector;
import org.oscim.backend.canvas.Bitmap;
import org.oscim.core.GeometryBuffer.GeometryType;
import org.oscim.core.MapElement;
import org.oscim.core.MercatorProjection;
import org.oscim.core.PointF;
import org.oscim.core.Tag;
import org.oscim.core.TagSet;
import org.oscim.core.Tile;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileLoader;
import org.oscim.layers.tile.TileManager;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.renderer.elements.ExtrusionLayer;
import org.oscim.renderer.elements.LineLayer;
import org.oscim.renderer.elements.LineTexLayer;
import org.oscim.renderer.elements.MeshLayer;
import org.oscim.renderer.elements.PolygonLayer;
import org.oscim.renderer.elements.SymbolItem;
import org.oscim.renderer.elements.TextItem;
import org.oscim.theme.IRenderTheme;
import org.oscim.theme.styles.Area;
import org.oscim.theme.styles.Circle;
import org.oscim.theme.styles.Extrusion;
import org.oscim.theme.styles.Line;
import org.oscim.theme.styles.LineSymbol;
import org.oscim.theme.styles.RenderStyle;
import org.oscim.theme.styles.Symbol;
import org.oscim.theme.styles.Text;
import org.oscim.tiling.ITileDataSink;
import org.oscim.tiling.ITileDataSource;
import org.oscim.tiling.ITileDataSource.QueryResult;
import org.oscim.utils.geom.LineClipper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VectorTileLoader extends TileLoader implements IRenderTheme.Callback, ITileDataSink {
static final Logger log = LoggerFactory.getLogger(VectorTileLoader.class);
protected static final double STROKE_INCREASE = Math.sqrt(2.5);
protected static final byte LAYERS = 11;
public static final byte STROKE_MIN_ZOOM = 12;
public static final byte STROKE_MAX_ZOOM = 17;
protected IRenderTheme renderTheme;
protected int renderLevels;
/** current TileDataSource used by this MapTileLoader */
protected ITileDataSource mTileDataSource;
/** currently processed tile */
protected MapTile mTile;
/** currently processed MapElement */
protected MapElement mElement;
/** current line layer (will be used for outline layers) */
protected LineLayer mCurLineLayer;
/** Current layer for adding elements */
protected int mCurLayer;
/** Line-scale-factor depending on zoom and latitude */
protected float mLineScale = 1.0f;
protected final LineClipper mClipper;
protected final TagSet mFilteredTags;
public void setRenderTheme(IRenderTheme theme) {
renderTheme = theme;
renderLevels = theme.getLevels();
}
public VectorTileLoader(TileManager tileManager) {
super(tileManager);
mClipper = new LineClipper(0, 0, Tile.SIZE, Tile.SIZE, true);
mFilteredTags = new TagSet();
}
@Override
public void cleanup() {
mTileDataSource.destroy();
}
@Override
public boolean executeJob(MapTile tile) {
if (mTileDataSource == null)
return false;
// account for area changes with latitude
double lat = MercatorProjection.toLatitude(tile.y);
mLineScale = (float) Math.pow(STROKE_INCREASE, tile.zoomLevel - STROKE_MIN_ZOOM);
if (mLineScale < 1)
mLineScale = 1;
// scale line width relative to latitude + PI * thumb
mLineScale *= 0.4f + 0.6f * ((float) Math.sin(Math.abs(lat) * (Math.PI / 180)));
mTile = tile;
mTile.layers = new ElementLayers();
// query database, which calls renderWay and renderPOI
// callbacks while processing map tile data.
if (mTileDataSource.executeQuery(mTile, this) != QueryResult.SUCCESS) {
return false;
}
return true;
}
public void completed(boolean success) {
jobCompleted(mTile, success);
mTile = null;
}
protected static int getValidLayer(int layer) {
if (layer < 0) {
return 0;
} else if (layer >= LAYERS) {
return LAYERS - 1;
} else {
return layer;
}
}
public void setDataSource(ITileDataSource mapDatabase) {
if (mTileDataSource != null)
mTileDataSource.destroy();
mTileDataSource = mapDatabase;
}
static class TagReplacement {
public TagReplacement(String key) {
this.key = key;
this.tag = new Tag(key, null);
}
String key;
Tag tag;
}
protected boolean filterTags(TagSet tagSet) {
return true;
}
@Override
public void process(MapElement element) {
clearState();
mElement = element;
if (element.type == GeometryType.POINT) {
// remove tags that should not be cached in Rendertheme
filterTags(element.tags);
// get and apply render instructions
renderNode(renderTheme.matchElement(element.type, mFilteredTags, mTile.zoomLevel));
} else {
// replace tags that should not be cached in Rendertheme (e.g. name)
if (!filterTags(element.tags))
return;
mCurLayer = getValidLayer(element.layer) * renderLevels;
// get and apply render instructions
renderWay(renderTheme.matchElement(element.type, mFilteredTags, mTile.zoomLevel));
//boolean closed = element.type == GeometryType.POLY;
//if (debug.debugTheme && ri == null)
// debugUnmatched(closed, element.tags);
mCurLineLayer = null;
}
mElement = null;
}
//protected void debugUnmatched(boolean closed, TagSet tags) {
// log.debug("DBG way not matched: " + closed + " "
// + Arrays.deepToString(tags));
//
// mTagName = new Tag("name", tags[0].key + ":"
// + tags[0].value, false);
//
// mElement.tags = closed ? debugTagArea : debugTagWay;
// RenderInstruction[] ri = renderTheme.matchElement(mElement, mTile.zoomLevel);
// renderWay(ri);
//}
protected void renderWay(RenderStyle[] ri) {
if (ri == null)
return;
for (int i = 0, n = ri.length; i < n; i++)
ri[i].renderWay(this);
}
protected void renderNode(RenderStyle[] ri) {
if (ri == null)
return;
for (int i = 0, n = ri.length; i < n; i++)
ri[i].renderNode(this);
}
protected void clearState() {
mCurLineLayer = null;
}
/*** RenderThemeCallback ***/
@Override
public void renderWay(Line line, int level) {
int numLayer = mCurLayer + level;
if (line.stipple == 0) {
if (line.outline && mCurLineLayer == null) {
log.error("BUG in theme: line must come before outline!");
return;
}
LineLayer ll = mTile.layers.getLineLayer(numLayer);
if (ll == null)
return;
if (ll.line == null) {
ll.line = line;
ll.scale = line.fixed ? 1 : mLineScale;
}
if (line.outline) {
ll.addOutline(mCurLineLayer);
} else {
ll.addLine(mElement);
// keep reference for outline layer(s)
mCurLineLayer = ll;
}
} else {
LineTexLayer ll = mTile.layers.getLineTexLayer(numLayer);
if (ll == null)
return;
if (ll.line == null) {
ll.line = line;
float w = line.width;
if (!line.fixed)
w *= mLineScale;
ll.width = w;
}
ll.addLine(mElement);
}
}
protected final static boolean USE_MESH_POLY = false;
@Override
public void renderArea(Area area, int level) {
int numLayer = mCurLayer + level;
if (USE_MESH_POLY) {
MeshLayer l = mTile.layers.getMeshLayer(numLayer);
l.area = area;
l.addMesh(mElement);
} else {
PolygonLayer l = mTile.layers.getPolygonLayer(numLayer);
l.area = area;
l.addPolygon(mElement.points, mElement.index);
}
}
@Override
public void renderAreaText(Text text) {
// TODO place somewhere on polygon
String value = mElement.tags.getValue(text.textKey);
if (value == null)
return;
PointF p = mElement.getPoint(0);
mTile.labels.push(TextItem.pool.get().set(p.x, p.y, value, text));
}
@Override
public void renderPointText(Text text) {
String value = mElement.tags.getValue(text.textKey);
if (value == null)
return;
for (int i = 0, n = mElement.getNumPoints(); i < n; i++) {
PointF p = mElement.getPoint(i);
mTile.labels.push(TextItem.pool.get().set(p.x, p.y, value, text));
}
}
@Override
public void renderWayText(Text text) {
String value = mElement.tags.getValue(text.textKey);
if (value == null)
return;
int offset = 0;
for (int i = 0, n = mElement.index.length; i < n; i++) {
int length = mElement.index[i];
if (length < 4)
break;
WayDecorator.renderText(mClipper, mElement.points, value, text,
offset, length, mTile);
offset += length;
}
}
@Override
public void renderPointCircle(Circle circle, int level) {
}
@Override
public void renderPointSymbol(Symbol symbol) {
if (symbol.texture == null) {
log.debug("missing symbol for " + mElement.tags.toString());
return;
}
for (int i = 0, n = mElement.getNumPoints(); i < n; i++) {
PointF p = mElement.getPoint(i);
SymbolItem it = SymbolItem.pool.get();
it.set(p.x, p.y, symbol.texture, true);
mTile.symbols.push(it);
}
}
@Override
public void renderAreaSymbol(Symbol symbol) {
}
@Override
public void renderWaySymbol(LineSymbol symbol) {
}
@Override
public void renderExtrusion(Extrusion extrusion, int level) {
int height = 0;
int minHeight = 0;
String v = mElement.tags.getValue(Tag.KEY_HEIGHT);
if (v != null)
height = Integer.parseInt(v);
v = mElement.tags.getValue(Tag.KEY_MIN_HEIGHT);
if (v != null)
minHeight = Integer.parseInt(v);
ExtrusionLayer l = mTile.layers.getExtrusionLayers();
if (l == null) {
double lat = MercatorProjection.toLatitude(mTile.y);
float groundScale = (float) MercatorProjection
.groundResolution(lat, 1 << mTile.zoomLevel);
l = new ExtrusionLayer(0, groundScale, extrusion.colors);
mTile.layers.setExtrusionLayers(l);
}
/* 12m default */
if (height == 0)
height = 12 * 100;
l.add(mElement, height, minHeight);
}
@Override
public void setTileImage(Bitmap bitmap) {
// TODO Auto-generated method stub
}
}

View File

@@ -14,16 +14,15 @@
*/
package org.oscim.tiling.source;
import static org.oscim.tiling.ITileDataSink.QueryResult.FAILED;
import static org.oscim.tiling.ITileDataSink.QueryResult.SUCCESS;
import java.io.IOException;
import java.io.InputStream;
import org.oscim.layers.tile.MapTile;
import org.oscim.tiling.ITileDataSink;
import org.oscim.tiling.ITileDataSource;
import org.oscim.tiling.source.ITileDecoder;
import org.oscim.tiling.source.LwHttp;
import org.oscim.tiling.source.UrlTileDataSource;
import org.oscim.tiling.source.UrlTileSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,25 +39,23 @@ public class UrlTileDataSource implements ITileDataSource {
mConn = conn;
}
UrlTileSource getTileSource(){
UrlTileSource getTileSource() {
return mTileSource;
}
private ITileDataSink mSink;
private MapTile mTile;
@Override
public QueryResult executeQuery(MapTile tile, ITileDataSink sink) {
QueryResult result = QueryResult.SUCCESS;
public void query(MapTile tile, ITileDataSink sink) {
mTile = tile;
mSink = sink;
try {
mConn.sendRequest(tile, this);
} catch (Exception e) {
} catch (IOException e) {
e.printStackTrace();
result = QueryResult.FAILED;
sink.completed(FAILED);
}
return result;
}
public void process(InputStream is) {
@@ -72,10 +69,14 @@ public class UrlTileDataSource implements ITileDataSource {
}
}
if (!win)
log.debug(mTile + " failed");
log.debug("{} failed", mTile);
mConn.requestCompleted();
mSink.completed(win);
mSink.completed(win ? SUCCESS : FAILED);
mTile = null;
mSink = null;
}
@Override

View File

@@ -1,5 +1,8 @@
package org.oscim.tiling.source.bitmap;
import static org.oscim.tiling.ITileDataSink.QueryResult.FAILED;
import static org.oscim.tiling.ITileDataSink.QueryResult.SUCCESS;
import org.oscim.gdx.client.GwtBitmap;
import org.oscim.layers.tile.MapTile;
import org.oscim.tiling.ITileDataSink;
@@ -48,7 +51,7 @@ public abstract class BitmapTileSource extends UrlTileSource {
}
@Override
public QueryResult executeQuery(final MapTile tile, final ITileDataSink sink) {
public void query(final MapTile tile, final ITileDataSink sink) {
int pos = mTileSource.formatTilePath(tile, mRequestBuffer, 0);
@@ -73,7 +76,7 @@ public abstract class BitmapTileSource extends UrlTileSource {
img.addLoadHandler(new LoadHandler() {
public void onLoad(LoadEvent event) {
sink.setTileImage(new GwtBitmap(img));
sink.completed(true);
sink.completed(SUCCESS);
}
});
@@ -81,11 +84,10 @@ public abstract class BitmapTileSource extends UrlTileSource {
@Override
public void onError(ErrorEvent event) {
sink.completed(false);
sink.completed(FAILED);
RootPanel.get().remove(img);
}
});
return QueryResult.SUCCESS;
}
@Override