Merge branch 'wip_sync'

This commit is contained in:
Hannes Janetzek 2014-01-19 22:53:48 +01:00
commit 2c7cd1c880
14 changed files with 458 additions and 423 deletions

View File

@ -23,16 +23,10 @@ import android.widget.RelativeLayout.LayoutParams;
public class AndroidMap extends Map { public class AndroidMap extends Map {
private final MapView mMapView; private final MapView mMapView;
private boolean mWaitRedraw;
final GLView mGLView; final GLView mGLView;
boolean mPausing = false;
private final Runnable mRedrawRequest = new Runnable() { private volatile boolean mWaitRedraw;
@Override private volatile boolean mPausing;
public void run() {
redrawMapInternal(false);
}
};
public AndroidMap(MapView mapView) { public AndroidMap(MapView mapView) {
super(); super();
@ -45,8 +39,6 @@ public class AndroidMap extends Map {
android.view.ViewGroup.LayoutParams.MATCH_PARENT); android.view.ViewGroup.LayoutParams.MATCH_PARENT);
mapView.addView(mGLView, params); mapView.addView(mGLView, params);
//mGestureDetector =
} }
@Override @Override
@ -60,9 +52,9 @@ public class AndroidMap extends Map {
} }
@Override @Override
public synchronized void updateMap(boolean requestRender) { public void updateMap(boolean redraw) {
if (requestRender && !mClearMap && !mPausing) // && mInitialized) //if (redraw && !mClearMap && !mPausing)
mGLView.requestRender(); // mGLView.requestRender();
if (!mWaitRedraw) { if (!mWaitRedraw) {
mWaitRedraw = true; mWaitRedraw = true;
@ -71,26 +63,29 @@ public class AndroidMap extends Map {
} }
@Override @Override
public synchronized void render() { public void render() {
if (mPausing)
return;
if (mClearMap) if (mClearMap)
updateMap(false); updateMap(false);
else else
mGLView.requestRender(); mGLView.requestRender();
} }
synchronized void redrawMapInternal(boolean forceRedraw) { private final Runnable mRedrawRequest = new Runnable() {
boolean clear = mClearMap; @Override
mWaitRedraw = false; public void run() {
redrawMapInternal();
}
};
if (forceRedraw && !clear) void redrawMapInternal() {
mGLView.requestRender(); mWaitRedraw = false;
updateLayers(); updateLayers();
if (clear) {
mGLView.requestRender(); mGLView.requestRender();
mClearMap = false;
}
} }
@Override @Override
@ -103,9 +98,7 @@ public class AndroidMap extends Map {
return mMapView.postDelayed(action, delay); return mMapView.postDelayed(action, delay);
} }
public void pause(boolean pause) {
public synchronized void pause(boolean pause) {
mPausing = pause; mPausing = pause;
} }
} }

View File

@ -24,9 +24,12 @@ import org.oscim.map.Map;
import org.oscim.tiling.TileLoader; import org.oscim.tiling.TileLoader;
import org.oscim.tiling.TileManager; import org.oscim.tiling.TileManager;
import org.oscim.tiling.TileRenderer; import org.oscim.tiling.TileRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class TileLayer<T extends TileLoader> extends Layer implements Map.UpdateListener { public abstract class TileLayer<T extends TileLoader> extends Layer implements Map.UpdateListener {
//static final Logger log = LoggerFactory.getLogger(TileLayer.class); static final Logger log = LoggerFactory.getLogger(TileLayer.class);
private final static int MAX_ZOOMLEVEL = 17; private final static int MAX_ZOOMLEVEL = 17;
private final static int MIN_ZOOMLEVEL = 2; private final static int MIN_ZOOMLEVEL = 2;
private final static int CACHE_LIMIT = 250; private final static int CACHE_LIMIT = 250;
@ -37,8 +40,6 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements M
protected final int mNumTileLoader = 4; protected final int mNumTileLoader = 4;
protected final ArrayList<T> mTileLoader; protected final ArrayList<T> mTileLoader;
protected boolean mInitial = true;
public TileLayer(Map map) { public TileLayer(Map map) {
this(map, MIN_ZOOMLEVEL, MAX_ZOOMLEVEL, CACHE_LIMIT); this(map, MIN_ZOOMLEVEL, MAX_ZOOMLEVEL, CACHE_LIMIT);
} }
@ -71,10 +72,13 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements M
@Override @Override
public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) { public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
if (clear || mInitial) { if (clear) {
// sync with TileRenderer
synchronized (mRenderLayer) {
mRenderLayer.clearTiles(); mRenderLayer.clearTiles();
mTileManager.init(mInitial); mTileManager.init();
mInitial = false; }
changed = true; changed = true;
} }
@ -87,16 +91,13 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements M
for (T loader : mTileLoader) { for (T loader : mTileLoader) {
loader.pause(); loader.pause();
loader.interrupt(); loader.interrupt();
loader.cleanup(); try {
loader.join();
//try { } catch (InterruptedException e) {
// tileWorker.join(10000); e.printStackTrace();
//} catch (InterruptedException e) { }
// // restore the interrupted status loader.cleanup();
// Thread.currentThread().interrupt();
//}
} }
mTileManager.destroy();
} }
void notifyLoaders() { void notifyLoaders() {
@ -110,13 +111,14 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements M
if (!loader.isPausing()) if (!loader.isPausing())
loader.pause(); loader.pause();
} }
if (wait) { if (!wait)
return;
for (T loader : mTileLoader) { for (T loader : mTileLoader) {
if (!loader.isPausing()) if (!loader.isPausing())
loader.awaitPausing(); loader.awaitPausing();
} }
} }
}
protected void resumeLoaders() { protected void resumeLoaders() {
for (T loader : mTileLoader) for (T loader : mTileLoader)

View File

@ -16,6 +16,8 @@
*/ */
package org.oscim.layers.tile.vector; package org.oscim.layers.tile.vector;
import java.util.concurrent.CancellationException;
import org.oscim.core.GeometryBuffer.GeometryType; import org.oscim.core.GeometryBuffer.GeometryType;
import org.oscim.core.MapElement; import org.oscim.core.MapElement;
import org.oscim.core.MercatorProjection; import org.oscim.core.MercatorProjection;
@ -131,14 +133,18 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
mTile = tile; mTile = tile;
mTile.layers = new ElementLayers(); mTile.layers = new ElementLayers();
QueryResult result = null;
try {
// query database, which calls 'process' callback // query database, which calls 'process' callback
QueryResult result = mTileDataSource.executeQuery(mTile, this); result = mTileDataSource.executeQuery(mTile, this);
} catch (CancellationException e) {
log.debug("canceled {}", mTile);
} catch (Exception e) {
log.debug("{}", e);
} finally {
mTile = null; mTile = null;
clearState(); clearState();
}
return (result == QueryResult.SUCCESS); return (result == QueryResult.SUCCESS);
} }
@ -205,8 +211,12 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
@Override @Override
public void process(MapElement element) { public void process(MapElement element) {
clearState(); clearState();
if (isCanceled())
throw new CancellationException();
mElement = element; mElement = element;
if (element.type == GeometryType.POINT) { if (element.type == GeometryType.POINT) {

View File

@ -28,7 +28,7 @@ public class LabelLayer extends Layer implements Map.InputListener, Map.UpdateLi
static final Logger log = LoggerFactory.getLogger(LabelLayer.class); static final Logger log = LoggerFactory.getLogger(LabelLayer.class);
private final TextRenderer mTextRenderer; private final TextRenderer mTextRenderer;
private int multi; //private int multi;
public LabelLayer(Map map, TileRenderer tileRenderLayer) { public LabelLayer(Map map, TileRenderer tileRenderLayer) {
super(map); super(map);
@ -49,19 +49,19 @@ public class LabelLayer extends Layer implements Map.InputListener, Map.UpdateLi
@Override @Override
public void onMotionEvent(MotionEvent e) { public void onMotionEvent(MotionEvent e) {
int action = e.getAction() & MotionEvent.ACTION_MASK; // int action = e.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_POINTER_DOWN) { // if (action == MotionEvent.ACTION_POINTER_DOWN) {
multi++; // multi++;
mTextRenderer.hold(true); // mTextRenderer.hold(true);
} else if (action == MotionEvent.ACTION_POINTER_UP) { // } else if (action == MotionEvent.ACTION_POINTER_UP) {
multi--; // multi--;
if (multi == 0) // if (multi == 0)
mTextRenderer.hold(false); // mTextRenderer.hold(false);
} else if (action == MotionEvent.ACTION_CANCEL) { // } else if (action == MotionEvent.ACTION_CANCEL) {
multi = 0; // multi = 0;
log.debug("cancel " + multi); // log.debug("cancel " + multi);
mTextRenderer.hold(false); // mTextRenderer.hold(false);
} // }
} }
@Override @Override

View File

@ -51,6 +51,7 @@ import org.oscim.tiling.TileRenderer;
import org.oscim.tiling.TileSet; import org.oscim.tiling.TileSet;
import org.oscim.utils.FastMath; import org.oscim.utils.FastMath;
import org.oscim.utils.OBB2D; import org.oscim.utils.OBB2D;
import org.oscim.utils.async.ContinuousTask;
import org.oscim.utils.pool.Pool; import org.oscim.utils.pool.Pool;
class TextRenderer extends ElementRenderer { class TextRenderer extends ElementRenderer {
@ -143,6 +144,8 @@ class TextRenderer extends ElementRenderer {
//mActiveTiles = new HashMap<MapTile, LabelTile>(); //mActiveTiles = new HashMap<MapTile, LabelTile>();
mRelabelCnt = 0; mRelabelCnt = 0;
mLabelTask = new LabelTask(map);
} }
// remove Label l from mLabels and return l.next // remove Label l from mLabels and return l.next
@ -274,6 +277,108 @@ class TextRenderer extends ElementRenderer {
return dx; return dx;
} }
private Label updateWayLabels(MapTile t, Label l, float dx, float dy, double scale,
ElementLayers dbg) {
for (TextItem ti = t.labels; ti != null; ti = ti.next) {
if (ti.text.caption)
continue;
// acquire a TextItem to add to TextLayer
if (l == null)
l = getLabel();
// check if path at current scale is long enough for text
if (dbg == null && ti.width > ti.length * scale)
continue;
l.clone(ti);
l.move(ti, dx, dy, (float) scale);
// set line endpoints relative to view to be able to
// check intersections with label from other tiles
float w = (ti.x2 - ti.x1) / 2f;
float h = (ti.y2 - ti.y1) / 2f;
l.bbox = null;
l.x1 = l.x - w;
l.y1 = l.y - h;
l.x2 = l.x + w;
l.y2 = l.y + h;
if (!wayIsVisible(l))
continue;
byte overlaps = -1;
if (l.bbox == null)
l.bbox = new OBB2D(l.x, l.y, l.x1, l.y1,
l.width + MIN_WAY_DIST,
l.text.fontHeight + MIN_WAY_DIST);
else
l.bbox.set(l.x, l.y, l.x1, l.y1,
l.width + MIN_WAY_DIST,
l.text.fontHeight + MIN_WAY_DIST);
if (dbg == null || ti.width < ti.length * scale)
overlaps = checkOverlap(l);
if (dbg != null)
Debug.addDebugBox(dbg, l, ti, overlaps, false, (float) scale);
if (overlaps == 0) {
addLabel(l);
l.item = TextItem.copy(ti);
l.tile = t;
l.active = mRelabelCnt;
l = null;
}
}
return l;
}
private Label updateNodeLabels(MapTile t, Label l, float dx, float dy, double scale, float cos,
float sin) {
O: for (TextItem ti = t.labels; ti != null; ti = ti.next) {
if (!ti.text.caption)
continue;
// acquire a TextItem to add to TextLayer
if (l == null)
l = getLabel();
l.clone(ti);
l.move(ti, dx, dy, (float) scale);
if (!nodeIsVisible(l))
continue;
if (l.bbox == null)
l.bbox = new OBB2D();
l.bbox.setNormalized(l.x, l.y, cos, -sin,
l.width + MIN_CAPTION_DIST,
l.text.fontHeight + MIN_CAPTION_DIST,
l.text.dy);
for (Label lp = mLabels; lp != null;) {
if (l.bbox.overlaps(lp.bbox)) {
if (l.text.priority < lp.text.priority) {
lp = removeLabel(lp);
continue;
}
continue O;
}
lp = (Label) lp.next;
}
addLabel(l);
l.item = TextItem.copy(ti);
l.tile = t;
l.active = mRelabelCnt;
l = null;
}
return l;
}
boolean updateLabels() { boolean updateLabels() {
// nextLayer is not loaded yet // nextLayer is not loaded yet
if (mNextLayer.ready) if (mNextLayer.ready)
@ -400,6 +505,7 @@ class TextRenderer extends ElementRenderer {
/* add way labels */ /* add way labels */
for (int i = 0, n = mTileSet.cnt; i < n; i++) { for (int i = 0, n = mTileSet.cnt; i < n; i++) {
MapTile t = tiles[i]; MapTile t = tiles[i];
synchronized (t) {
if (!t.state(MapTile.STATE_READY)) if (!t.state(MapTile.STATE_READY))
continue; continue;
@ -407,65 +513,14 @@ class TextRenderer extends ElementRenderer {
float dy = (float) (t.tileY * Tile.SIZE - tileY); float dy = (float) (t.tileY * Tile.SIZE - tileY);
dx = flipLongitude(dx, maxx); dx = flipLongitude(dx, maxx);
for (TextItem ti = t.labels; ti != null; ti = ti.next) { l = updateWayLabels(t, l, dx, dy, scale, dbg);
if (ti.text.caption)
continue;
// acquire a TextItem to add to TextLayer
if (l == null)
l = getLabel();
// check if path at current scale is long enough for text
if (dbg == null && ti.width > ti.length * scale)
continue;
l.clone(ti);
l.move(ti, dx, dy, (float) scale);
// set line endpoints relative to view to be able to
// check intersections with label from other tiles
float w = (ti.x2 - ti.x1) / 2f;
float h = (ti.y2 - ti.y1) / 2f;
l.bbox = null;
l.x1 = l.x - w;
l.y1 = l.y - h;
l.x2 = l.x + w;
l.y2 = l.y + h;
if (!wayIsVisible(l))
continue;
byte overlaps = -1;
if (l.bbox == null)
l.bbox = new OBB2D(l.x, l.y, l.x1, l.y1,
l.width + MIN_WAY_DIST,
l.text.fontHeight + MIN_WAY_DIST);
else
l.bbox.set(l.x, l.y, l.x1, l.y1,
l.width + MIN_WAY_DIST,
l.text.fontHeight + MIN_WAY_DIST);
if (dbg == null || ti.width < ti.length * scale)
overlaps = checkOverlap(l);
if (dbg != null)
Debug.addDebugBox(dbg, l, ti, overlaps, false, (float) scale);
if (overlaps == 0) {
addLabel(l);
l.item = TextItem.copy(ti);
l.tile = t;
l.active = mRelabelCnt;
l = null;
}
} }
} }
/* add caption */ /* add caption */
for (int i = 0, n = mTileSet.cnt; i < n; i++) { for (int i = 0, n = mTileSet.cnt; i < n; i++) {
MapTile t = tiles[i]; MapTile t = tiles[i];
synchronized (t) {
if (!t.state(MapTile.STATE_READY)) if (!t.state(MapTile.STATE_READY))
continue; continue;
@ -473,45 +528,7 @@ class TextRenderer extends ElementRenderer {
float dy = (float) (t.tileY * Tile.SIZE - tileY); float dy = (float) (t.tileY * Tile.SIZE - tileY);
dx = flipLongitude(dx, maxx); dx = flipLongitude(dx, maxx);
O: for (TextItem ti = t.labels; ti != null; ti = ti.next) { l = updateNodeLabels(t, l, dx, dy, scale, cos, sin);
if (!ti.text.caption)
continue;
// acquire a TextItem to add to TextLayer
if (l == null)
l = getLabel();
l.clone(ti);
l.move(ti, dx, dy, (float) scale);
if (!nodeIsVisible(l))
continue;
//l.setAxisAlignedBBox();
if (l.bbox == null)
l.bbox = new OBB2D();
l.bbox.setNormalized(l.x, l.y, cos, -sin,
l.width + MIN_CAPTION_DIST,
l.text.fontHeight + MIN_CAPTION_DIST,
l.text.dy);
for (Label lp = mLabels; lp != null;) {
if (l.bbox.overlaps(lp.bbox)) {
if (l.text.priority < lp.text.priority) {
lp = removeLabel(lp);
continue;
}
continue O;
}
lp = (Label) lp.next;
}
addLabel(l);
l.item = TextItem.copy(ti);
l.tile = t;
l.active = mRelabelCnt;
l = null;
} }
} }
@ -541,6 +558,7 @@ class TextRenderer extends ElementRenderer {
for (int i = 0, n = mTileSet.cnt; i < n; i++) { for (int i = 0, n = mTileSet.cnt; i < n; i++) {
MapTile t = tiles[i]; MapTile t = tiles[i];
synchronized (t) {
if (!t.state(MapTile.STATE_READY)) if (!t.state(MapTile.STATE_READY))
continue; continue;
@ -562,6 +580,7 @@ class TextRenderer extends ElementRenderer {
sl.addSymbol(s); sl.addSymbol(s);
} }
} }
}
// temporary used Label // temporary used Label
l = (Label) mPool.release(l); l = (Label) mPool.release(l);
@ -588,11 +607,6 @@ class TextRenderer extends ElementRenderer {
public synchronized void update(MapPosition pos, boolean changed, public synchronized void update(MapPosition pos, boolean changed,
Matrices matrices) { Matrices matrices) {
//if (System.currentTimeMillis() - lastDraw > 1000){
// updateLabels();
// lastDraw = System.currentTimeMillis();
//
//}
if (mNextLayer.ready) { if (mNextLayer.ready) {
// exchange current with next layers // exchange current with next layers
TextureLayers tmp = mCurLayer; TextureLayers tmp = mCurLayer;
@ -615,92 +629,19 @@ class TextRenderer extends ElementRenderer {
compile(); compile();
} }
if (mRequestClear) mLabelTask.submit((mLastRun + MAX_RELABEL_DELAY) - System.currentTimeMillis());
cleanup();
//if (!mHolding)
postLabelTask();
}
/* private */LabelTask mLabelTask;
/* private */long mLastRun;
/* private */boolean mRequestRun;
/* private */boolean mRequestClear;
/* private */boolean mRelabel;
class LabelTask implements Runnable {
private boolean isCancelled;
@Override
public void run() {
boolean labelsChanged = false;
if (isCancelled) {
return;
}
long now = System.currentTimeMillis();
//log.debug("relabel after " + (now - mLastRun));
mLastRun = now;
labelsChanged = updateLabels();
if (!isCancelled && labelsChanged)
mMap.render();
mLabelTask = null;
mRequestRun = false;
isCancelled = false;
if (mRelabel) {
mRelabel = false;
postLabelTask();
}
}
public void cancel() {
isCancelled = true;
}
}
/* private */void cleanup() {
mLabels = (Label) mPool.releaseAll(mLabels);
mTileSet.releaseTiles();
mLabelTask = null;
mRequestClear = false;
}
private final Runnable mLabelUpdate = new Runnable() {
@Override
public void run() {
if (mLabelTask == null) {
mLabelTask = new LabelTask();
mMap.addTask(mLabelTask);
}
}
};
/* private */void postLabelTask() {
synchronized (mLabelUpdate) {
if (mRequestRun) {
mRelabel = true;
} else {
mRequestRun = true;
long delay = (mLastRun + MAX_RELABEL_DELAY) - System.currentTimeMillis();
//log.debug("relabel in: " + delay);
mMap.postDelayed(mLabelUpdate, Math.max(delay, 0));
}
}
} }
@Override @Override
public synchronized void render(MapPosition pos, Matrices m) { public synchronized void render(MapPosition pos, Matrices m) {
layers.vbo.bind();
GLState.test(false, false); GLState.test(false, false);
float scale = (float) (mMapPosition.scale / pos.scale); float scale = (float) (mMapPosition.scale / pos.scale);
setMatrix(pos, m, true); setMatrix(pos, m, true);
synchronized (layers) {
layers.vbo.bind();
if (layers.baseLayers != null) { if (layers.baseLayers != null) {
for (RenderElement l = layers.baseLayers; l != null;) { for (RenderElement l = layers.baseLayers; l != null;) {
if (l.type == RenderElement.POLYGON) { if (l.type == RenderElement.POLYGON) {
@ -717,25 +658,39 @@ class TextRenderer extends ElementRenderer {
for (RenderElement l = layers.textureLayers; l != null;) for (RenderElement l = layers.textureLayers; l != null;)
l = TextureLayer.Renderer.draw(l, scale, m); l = TextureLayer.Renderer.draw(l, scale, m);
} }
//private boolean mHolding;
/**
* @param enable layer updates
*/
public synchronized void hold(boolean enable) {
// mHolding = enable;
// if (!enable)
// runLabelTask();
} }
public synchronized void clearLabels() { final class LabelTask extends ContinuousTask {
if (mRequestRun) {
mRequestClear = true; public LabelTask(Map map) {
//mRelabel = true; super(map, 10);
} else { }
cleanup();
//postLabelTask(); @Override
public void doWork() {
if (updateLabels())
mMap.render();
mLastRun = System.currentTimeMillis();
}
@Override
public void cleanup() {
clearLabelsInternal();
} }
} }
private final LabelTask mLabelTask;
/* private */long mLastRun;
/* private */void clearLabelsInternal() {
mLabels = (Label) mPool.releaseAll(mLabels);
mTileSet.releaseTiles();
}
public void clearLabels() {
mLabelTask.cancel();
}
} }

View File

@ -90,10 +90,6 @@ public class MapRenderer {
private static volatile boolean mUpdateColor = false; private static volatile boolean mUpdateColor = false;
// drawlock to synchronize Main- and GL-Thread
// static ReentrantLock tilelock = new ReentrantLock();
public static Object drawlock = new Object();
public static long frametime; public static long frametime;
// Do not use the same buffer to upload data within a frame twice // Do not use the same buffer to upload data within a frame twice
@ -210,13 +206,8 @@ public class MapRenderer {
} }
public void onDrawFrame() { public void onDrawFrame() {
// prevent main thread recreating all tiles (updateMap)
// while rendering is going on.
synchronized (drawlock) {
frametime = System.currentTimeMillis(); frametime = System.currentTimeMillis();
draw(); draw();
}
mBufferPool.releaseBuffers(); mBufferPool.releaseBuffers();
} }

View File

@ -61,6 +61,8 @@ public class MapTile extends Tile {
*/ */
public final static byte STATE_ERROR = 1 << 3; public final static byte STATE_ERROR = 1 << 3;
public final static byte STATE_CANCEL = 1 << 4;
/** /**
* absolute tile coordinates: tileX,Y / Math.pow(2, zoomLevel) * absolute tile coordinates: tileX,Y / Math.pow(2, zoomLevel)
*/ */
@ -89,7 +91,7 @@ public class MapTile extends Tile {
} }
/** /**
* Tile is in view region. Set by GLRenderer. * Tile is in view region. Set by TileRenderer.
*/ */
public boolean isVisible; public boolean isVisible;
@ -100,7 +102,6 @@ public class MapTile extends Tile {
/** /**
* to avoid drawing a tile twice per frame * to avoid drawing a tile twice per frame
* FIXME what if multiple layers use the same tile?
*/ */
int lastDraw = 0; int lastDraw = 0;

View File

@ -42,6 +42,7 @@ public abstract class TileLoader extends PausableThread {
@Override @Override
protected void doWork() { protected void doWork() {
MapTile tile = mTileManager.getTileJob(); MapTile tile = mTileManager.getTileJob();
if (tile == null) if (tile == null)
@ -53,7 +54,7 @@ public abstract class TileLoader extends PausableThread {
success = executeJob(tile); success = executeJob(tile);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return; success = false;
} }
if (isInterrupted()) if (isInterrupted())

View File

@ -17,9 +17,9 @@
package org.oscim.tiling; package org.oscim.tiling;
import static org.oscim.tiling.MapTile.STATE_CANCEL;
import static org.oscim.tiling.MapTile.STATE_LOADING; import static org.oscim.tiling.MapTile.STATE_LOADING;
import static org.oscim.tiling.MapTile.STATE_NEW_DATA; import static org.oscim.tiling.MapTile.STATE_NEW_DATA;
import static org.oscim.tiling.MapTile.STATE_NONE;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -29,7 +29,6 @@ import org.oscim.core.Tile;
import org.oscim.map.Map; import org.oscim.map.Map;
import org.oscim.map.Viewport; import org.oscim.map.Viewport;
import org.oscim.renderer.BufferObject; import org.oscim.renderer.BufferObject;
import org.oscim.renderer.MapRenderer;
import org.oscim.utils.FastMath; import org.oscim.utils.FastMath;
import org.oscim.utils.ScanBox; import org.oscim.utils.ScanBox;
import org.oscim.utils.quadtree.QuadTree; import org.oscim.utils.quadtree.QuadTree;
@ -132,11 +131,6 @@ public class TileManager {
mUpdateSerial = 0; mUpdateSerial = 0;
} }
public void destroy() {
// there might be some leaks in here
// ... free static pools
}
private int[] mZoomTable; private int[] mZoomTable;
public void setZoomTable(int[] zoomLevel) { public void setZoomTable(int[] zoomLevel) {
@ -144,24 +138,10 @@ public class TileManager {
} }
public void init(boolean first) { public void init() {
// sync with GLRender thread
// ... and labeling thread?
synchronized (MapRenderer.drawlock) {
if (!first) {
// pass VBOs and VertexItems back to pools // pass VBOs and VertexItems back to pools
for (int i = 0; i < mTilesSize; i++) for (int i = 0; i < mTilesSize; i++)
clearTile(mTiles[i]); clearTile(mTiles[i]);
}
// FIXME any of this still needed?
// mInitialized is set when surface changed
// and VBOs might be lost
// VertexPool.init();
// clear cache index
// QuadTree.init();
// clear references to cached MapTiles // clear references to cached MapTiles
Arrays.fill(mTiles, null); Arrays.fill(mTiles, null);
@ -175,9 +155,6 @@ public class TileManager {
mNewTiles = new TileSet(numTiles); mNewTiles = new TileSet(numTiles);
mCurrentTiles = new TileSet(numTiles); mCurrentTiles = new TileSet(numTiles);
log.debug("max tiles: " + numTiles);
}
} }
/** /**
@ -187,7 +164,7 @@ public class TileManager {
* @param pos * @param pos
* current MapPosition * current MapPosition
*/ */
public synchronized boolean update(MapPosition pos) { public boolean update(MapPosition pos) {
// clear JobQueue and set tiles to state == NONE. // clear JobQueue and set tiles to state == NONE.
// one could also append new tiles and sort in JobQueue // one could also append new tiles and sort in JobQueue
// but this has the nice side-effect that MapWorkers dont // but this has the nice side-effect that MapWorkers dont
@ -405,13 +382,14 @@ public class TileManager {
if (t == null) if (t == null)
return; return;
synchronized (t) {
// still belongs to TileLoader thread
if (t.state != STATE_LOADING)
t.clear(); t.clear();
t.state = STATE_CANCEL;
mIndex.remove(t); mIndex.remove(t);
}
// QuadTree.remove(t);
t.state = STATE_NONE;
mTilesCount--; mTilesCount--;
} }
@ -549,8 +527,7 @@ public class TileManager {
* @return caller does not care * @return caller does not care
*/ */
public void jobCompleted(MapTile tile, boolean success) { public void jobCompleted(MapTile tile, boolean success) {
if (!success || tile.state == STATE_CANCEL) {
if (!success) {
tile.clear(); tile.clear();
return; return;
} }

View File

@ -64,8 +64,11 @@ public class TileRenderer extends LayerRenderer {
mAlpha = alpha; mAlpha = alpha;
} }
/**
* synced with clearTiles
*/
@Override @Override
protected void update(MapPosition pos, boolean positionChanged, Matrices m) { protected synchronized void update(MapPosition pos, boolean positionChanged, Matrices m) {
if (mAlpha == 0) { if (mAlpha == 0) {
mTileManager.releaseTiles(mDrawTiles); mTileManager.releaseTiles(mDrawTiles);
@ -106,10 +109,8 @@ public class TileRenderer extends LayerRenderer {
public void clearTiles() { public void clearTiles() {
// Clear all references to MapTiles as all current // Clear all references to MapTiles as all current
// tiles will also be removed from TileManager. // tiles will also be removed from TileManager.
synchronized (MapRenderer.drawlock) {
mDrawTiles = new TileSet(); mDrawTiles = new TileSet();
} }
}
/** compile tile layer data and upload to VBOs */ /** compile tile layer data and upload to VBOs */
private static int compileTileLayers(MapTile[] tiles, int tileCnt) { private static int compileTileLayers(MapTile[] tiles, int tileCnt) {

View File

@ -56,6 +56,7 @@ public class LwHttp {
private Socket mSocket; private Socket mSocket;
private OutputStream mCommandStream; private OutputStream mCommandStream;
private InputStream mResponseStream; private InputStream mResponseStream;
private OutputStream mCacheOutputStream;
private long mLastRequest = 0; private long mLastRequest = 0;
private SocketAddress mSockAddr; private SocketAddress mSockAddr;
@ -106,40 +107,34 @@ public class LwHttp {
} }
static class Buffer extends BufferedInputStream { static class Buffer extends BufferedInputStream {
final OutputStream mCache; final OutputStream mCacheOutputstream;
public Buffer(InputStream is, OutputStream cache) { public Buffer(InputStream is, OutputStream cache) {
super(is, 4096); super(is, 4096);
mCache = cache; mCacheOutputstream = cache;
} }
@Override @Override
public synchronized int read() throws IOException { public int read() throws IOException {
int data = super.read(); int data = super.read();
if (data >= 0) if (data >= 0)
mCache.write(data); mCacheOutputstream.write(data);
return data; return data;
} }
@Override @Override
public synchronized int read(byte[] buffer, int offset, int byteCount) public int read(byte[] buffer, int offset, int byteCount)
throws IOException { throws IOException {
int len = super.read(buffer, offset, byteCount); int len = super.read(buffer, offset, byteCount);
if (len >= 0) if (len >= 0)
mCache.write(buffer, offset, len); mCacheOutputstream.write(buffer, offset, len);
return len; return len;
} }
} }
OutputStream mCacheOutputStream;
public void setOutputStream(OutputStream outputStream) {
mCacheOutputStream = outputStream;
}
public void close() { public void close() {
if (mSocket != null) { if (mSocket != null) {
try { try {
@ -177,6 +172,10 @@ public class LwHttp {
while (end < read && (buf[end] != '\n')) while (end < read && (buf[end] != '\n'))
end++; end++;
if (end == BUFFER_SIZE) {
return null;
}
if (buf[end] != '\n') if (buf[end] != '\n')
continue; continue;
@ -209,7 +208,7 @@ public class LwHttp {
if (!ok) { if (!ok) {
String line = new String(buf, pos, end - pos - 1); String line = new String(buf, pos, end - pos - 1);
log.debug(">" + line + "< "); log.debug("> {} <", line);
} }
pos += (end - pos) + 1; pos += (end - pos) + 1;
@ -255,12 +254,14 @@ public class LwHttp {
mMaxReq = RESPONSE_EXPECTED_LIVES; mMaxReq = RESPONSE_EXPECTED_LIVES;
// log.debug("create connection"); // log.debug("create connection");
} else { } else {
// FIXME not sure if this is correct way to drain socket
int avail = mResponseStream.available(); int avail = mResponseStream.available();
if (avail > 0) { if (avail > 0) {
log.debug("Consume left-over bytes: " + avail); log.debug("left-over bytes: " + avail);
while ((avail = mResponseStream.available()) > 0) close();
mResponseStream.read(buffer); lwHttpConnect();
// FIXME not sure if this is correct way to drain socket
//while ((avail = mResponseStream.available()) > 0)
// mResponseStream.read(buffer);
} }
} }
@ -353,8 +354,16 @@ public class LwHttp {
return true; return true;
} }
public void requestCompleted() { public void setOutputStream(OutputStream outputStream) {
mCacheOutputStream = outputStream;
}
public void requestCompleted(boolean keepConnection) {
mLastRequest = System.nanoTime(); mLastRequest = System.nanoTime();
mCacheOutputStream = null;
if (!keepConnection)
close();
} }
public int getContentLength() { public int getContentLength() {

View File

@ -30,10 +30,6 @@ import org.oscim.utils.IOUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/**
*
*
*/
public abstract class PbfTileDataSource implements ITileDataSource { public abstract class PbfTileDataSource implements ITileDataSource {
static final Logger log = LoggerFactory.getLogger(PbfTileDataSource.class); static final Logger log = LoggerFactory.getLogger(PbfTileDataSource.class);
@ -48,66 +44,55 @@ public abstract class PbfTileDataSource implements ITileDataSource {
@Override @Override
public QueryResult executeQuery(MapTile tile, ITileDataSink sink) { public QueryResult executeQuery(MapTile tile, ITileDataSink sink) {
boolean success = true;
ITileCache.TileWriter cacheWriter = null; ITileCache.TileWriter cacheWriter = null;
if (mTileCache != null) { if (mTileCache != null) {
ITileCache.TileReader c = mTileCache.getTile(tile); ITileCache.TileReader c = mTileCache.getTile(tile);
if (c == null) { if (c == null) {
// create new cache entry
cacheWriter = mTileCache.writeTile(tile); cacheWriter = mTileCache.writeTile(tile);
mConn.setOutputStream(cacheWriter.getOutputStream());
} else { } else {
try {
InputStream is = c.getInputStream(); InputStream is = c.getInputStream();
try {
if (mTileDecoder.decode(tile, sink, is, c.getBytes())) { if (mTileDecoder.decode(tile, sink, is, c.getBytes())) {
IOUtils.closeQuietly(is);
return QueryResult.SUCCESS; return QueryResult.SUCCESS;
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} finally {
IOUtils.closeQuietly(is);
} }
log.debug(tile + " Cache read failed");
} }
} }
boolean success = false;
try { try {
if (cacheWriter != null)
mConn.setOutputStream(cacheWriter.getOutputStream());
InputStream is; InputStream is;
if (!mConn.sendRequest(tile)) { if (!mConn.sendRequest(tile)) {
log.debug(tile + " Request failed"); log.debug("{} Request failed", tile);
success = false; } else if ((is = mConn.readHeader()) == null) {
} else if ((is = mConn.readHeader()) != null) { log.debug("{} Network Error", tile);
} else {
int bytes = mConn.getContentLength(); int bytes = mConn.getContentLength();
success = mTileDecoder.decode(tile, sink, is, bytes); success = mTileDecoder.decode(tile, sink, is, bytes);
if (!success)
log.debug(tile + " Decoding failed");
} else {
log.debug(tile + " Network Error");
success = false;
} }
} catch (SocketException e) { } catch (SocketException e) {
log.debug(tile + " Socket exception: " + e.getMessage()); log.debug("{} Socket exception: {}", tile, e.getMessage());
success = false;
} catch (SocketTimeoutException e) { } catch (SocketTimeoutException e) {
log.debug(tile + " Socket Timeout"); log.debug("{} Socket Timeout", tile);
success = false;
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
log.debug(tile + " No Network"); log.debug("{} No Network", tile);
success = false; } catch (IOException e) {
} catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
success = false; } finally {
} mConn.requestCompleted(success);
mConn.requestCompleted();
if (cacheWriter != null) if (cacheWriter != null)
cacheWriter.complete(success); cacheWriter.complete(success);
}
if (success)
mConn.close();
return success ? QueryResult.SUCCESS : QueryResult.FAILED; return success ? QueryResult.SUCCESS : QueryResult.FAILED;
} }

View File

@ -17,12 +17,18 @@
*/ */
package org.oscim.utils; package org.oscim.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* An abstract base class for threads which support pausing and resuming. * An abstract base class for threads which support pausing and resuming.
*/ */
public abstract class PausableThread extends Thread { public abstract class PausableThread extends Thread {
private final static Logger log = LoggerFactory.getLogger(PausableThread.class);
private final static boolean DEBUG = false;
private boolean mPausing = true; private boolean mPausing = true;
private boolean mShouldPause; private boolean mShouldPause = false;
/** /**
* Causes the current thread to wait until this thread is pausing. * Causes the current thread to wait until this thread is pausing.
@ -30,8 +36,10 @@ public abstract class PausableThread extends Thread {
public final void awaitPausing() { public final void awaitPausing() {
synchronized (this) { synchronized (this) {
while (!isInterrupted() && !isPausing()) { while (!isInterrupted() && !isPausing()) {
if (DEBUG)
log.debug("await {}", getThreadName());
try { try {
wait(100); wait(10);
} catch (InterruptedException e) { } catch (InterruptedException e) {
// restore the interrupted status // restore the interrupted status
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
@ -42,17 +50,13 @@ public abstract class PausableThread extends Thread {
@Override @Override
public void interrupt() { public void interrupt() {
if (DEBUG)
log.debug("interrupt {}", getThreadName());
// first acquire the monitor which is used to call wait() // first acquire the monitor which is used to call wait()
synchronized (this) { synchronized (this) {
super.interrupt(); super.interrupt();
} }
//try {
// this.join(10000);
//} catch (InterruptedException e) {
// // restore the interrupted status
// Thread.currentThread().interrupt();
//}
} }
/** /**
@ -72,14 +76,16 @@ public abstract class PausableThread extends Thread {
} }
} }
public final synchronized boolean isCanceled() {
return mShouldPause;
}
/** /**
* The paused thread should continue with its work. * The paused thread should continue with its work.
*/ */
public final synchronized void proceed() { public final synchronized void proceed() {
if (mShouldPause) { if (mShouldPause) {
mShouldPause = false; mShouldPause = false;
mPausing = false;
afterPause();
notify(); notify();
} }
} }
@ -102,6 +108,14 @@ public abstract class PausableThread extends Thread {
interrupt(); interrupt();
} }
} }
if (mPausing) {
mPausing = false;
afterPause();
}
if (DEBUG)
log.debug("resume {}", getThreadName());
} }
if (isInterrupted()) { if (isInterrupted()) {
@ -116,6 +130,11 @@ public abstract class PausableThread extends Thread {
} }
} }
if (DEBUG)
log.debug("finish {}", getThreadName());
mPausing = true;
afterRun(); afterRun();
} }

View File

@ -0,0 +1,91 @@
package org.oscim.utils.async;
import org.oscim.map.Map;
public abstract class ContinuousTask implements Runnable {
private final Map mMap;
protected boolean mRunning;
protected boolean mWait;
protected boolean mCancel;
protected boolean mDelayed;
protected long mMinDelay;
public ContinuousTask(Map map, long minDelay) {
mMap = map;
mMinDelay = minDelay;
}
@Override
public void run() {
synchronized (this) {
//System.out.println("run " + mRunning + " "
// + mCancel + " " + mDelayed + " " + mWait);
if (mCancel) {
mCancel = false;
mRunning = false;
mDelayed = false;
mWait = false;
cleanup();
return;
}
if (mDelayed) {
// entered on main-loop
mDelayed = false;
mWait = false;
// unset running temporarily
mRunning = false;
submit(0);
return;
}
}
doWork();
synchronized (this) {
mRunning = false;
if (mCancel)
cleanup();
else if (mWait)
submit(mMinDelay);
mCancel = false;
mWait = false;
}
}
public abstract void doWork();
public abstract void cleanup();
public synchronized void submit(long delay) {
//System.out.println("submit " + mRunning + " " + mCancel + " " + delay);
if (mRunning) {
mWait = true;
return;
}
mRunning = true;
if (delay <= 0) {
mMap.addTask(this);
return;
}
mDelayed = true;
mMap.postDelayed(this, delay);
}
public synchronized void cancel() {
if (mRunning) {
mCancel = true;
return;
}
cleanup();
}
}