add MapView.clearMap() -> clear all previous map state in Layers on next onUpate

- now labels are cleared properly when the or mapdb changes.
- getTileSet now requires a TileSet instance as input
This commit is contained in:
Hannes Janetzek 2013-05-07 16:09:01 +02:00
parent 6bb5cd3f68
commit 82a3a880ac
14 changed files with 215 additions and 154 deletions

View File

@ -18,7 +18,7 @@ import org.oscim.core.MapPosition;
import org.oscim.renderer.RenderLayer;
import org.oscim.view.MapView;
public class Layer {
public abstract class Layer {
public Layer(MapView mapView) {
mMapView = mapView;
}
@ -45,7 +45,6 @@ public class Layer {
return mEnabled;
}
/**
* Called before each frame render request (on main thread).
*
@ -53,12 +52,15 @@ public class Layer {
* current MapPosition
* @param changed
* true when MapPosition has changed since last call
* @param clear
* Clear all resources that depend on previous map state. Most
* importantly all resources from previous GL context (hold by
* RenderLayer)
*/
public void onUpdate(MapPosition mapPosition, boolean changed) {
public void onUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
}
/**
* Override to perform clean up of resources before shutdown. By default
* does nothing.
@ -69,6 +71,5 @@ public class Layer {
public void destroy() {
// TODO Auto-generated method stub
}
}

View File

@ -70,7 +70,7 @@ public class BuildingOverlay extends Overlay {
private boolean mActive = false;
@Override
public void onUpdate(MapPosition mapPosition, boolean changed) {
public void onUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
boolean show = mapPosition.scale >= (1 << MIN_ZOOM);
if (show && mActive)

View File

@ -14,6 +14,7 @@
*/
package org.oscim.layers.overlay;
import org.oscim.core.MapPosition;
import org.oscim.layers.tile.TileRenderLayer;
import org.oscim.renderer.layers.TextRenderLayer;
import org.oscim.view.MapView;
@ -34,6 +35,12 @@ public class LabelingOverlay extends Overlay {
mLayer = mTextLayer;
}
@Override
public void onUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
if (clear)
mTextLayer.clearLabels();
}
private int multi;
@Override

View File

@ -95,7 +95,7 @@ public class MapScaleBar extends Layer {
}
@Override
public void onUpdate(MapPosition mapPosition, boolean changed) {
public void onUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
double latitude = MercatorProjection.toLatitude(mapPosition.y);
if (!mRedrawNeeded) {

View File

@ -24,9 +24,8 @@ public abstract class TileLayer<T extends TileLoader> extends Layer {
//private final static String TAG = TileLayer.class.getName();
private final static int MAX_ZOOMLEVEL = 17;
private boolean mClearMap = true;
protected final TileManager mTileManager;
protected final TileRenderLayer mRenderLayer;
protected final int mNumTileLoader = 4;
protected final ArrayList<T> mTileLoader;
@ -52,7 +51,7 @@ public abstract class TileLayer<T extends TileLoader> extends Layer {
// RenderLayer is working in GL Thread and actually
// drawing loaded tiles to screen.
mLayer = new TileRenderLayer(mapView, mTileManager);
mLayer = mRenderLayer = new TileRenderLayer(mapView, mTileManager);
}
abstract protected T createLoader(TileManager tm);
@ -62,11 +61,11 @@ public abstract class TileLayer<T extends TileLoader> extends Layer {
}
@Override
public void onUpdate(MapPosition mapPosition, boolean changed) {
public void onUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
if (mClearMap) {
if (clear) {
mRenderLayer.clearTiles();
mTileManager.init(mMapView.getWidth(), mMapView.getHeight());
mClearMap = false;
changed = true;
}
if (changed)
@ -75,11 +74,7 @@ public abstract class TileLayer<T extends TileLoader> extends Layer {
@Override
public void destroy() {
mTileManager.destroy();
for (T tileWorker : mTileLoader) {
tileWorker.pause();
tileWorker.interrupt();
tileWorker.cleanup();
@ -91,11 +86,7 @@ public abstract class TileLayer<T extends TileLoader> extends Layer {
Thread.currentThread().interrupt();
}
}
}
protected void clearMap() {
// clear tile and overlay data before next draw
mClearMap = true;
mTileManager.destroy();
}
void notifyLoaders() {

View File

@ -68,7 +68,8 @@ public class TileManager {
// current end position in mTiles
private int mTilesSize;
// counter for tiles with new data not uploaded to GL
// counter for tiles with new data not
// yet uploaded to GL
private volatile int mTilesForUpload;
// new tile jobs for MapWorkers
@ -80,12 +81,13 @@ public class TileManager {
// lock for TileSets while updating MapTile locks
private final Object mTilelock = new Object();
// need to keep track of TileSets to clear on reset...
private final ArrayList<TileSet> mTileSets = new ArrayList<TileSet>(4);
// need to keep track of TileSets to clear on reset.
//private final ArrayList<TileSet> mTileSets = new ArrayList<TileSet>(4);
private TileSet mCurrentTiles;
/* package */TileSet mNewTiles;
// job queue filled in TileManager and polled by TileLoaders
final JobQueue jobQueue;
private final QuadTreeIndex<MapTile> mIndex = new QuadTreeIndex<MapTile>() {
@ -139,7 +141,7 @@ public class TileManager {
// ... free static pools
}
public synchronized void init(int width, int height) {
public void init(int width, int height) {
// sync with GLRender thread
// ... and labeling thread?
@ -150,29 +152,24 @@ public class TileManager {
for (int i = 0; i < mTilesSize; i++)
clearTile(mTiles[i]);
}
//else {
// FIXME any of this still needed?
// mInitialized is set when surface changed
// and VBOs might be lost
// VertexPool.init();
//}
// clear cache index
//QuadTree.init();
// QuadTree.init();
// clear references to cached MapTiles
Arrays.fill(mTiles, null);
mTilesSize = 0;
mTilesCount = 0;
// clear all references to previous tiles
for (TileSet td : mTileSets) {
Arrays.fill(td.tiles, null);
td.cnt = 0;
}
// set up TileSet large enough to hold current tiles
int num = Math.max(width, height);
int size = Tile.SIZE >> 1;
int numTiles = (num * num) / (size * size) * 4;
mNewTiles = new TileSet(numTiles);
mCurrentTiles = new TileSet(numTiles);
Log.d(TAG, "max tiles: " + numTiles);
@ -278,7 +275,7 @@ public class TileManager {
limitCache(pos, remove);
}
/** only used in setmapDatabase -- deprecate?*/
/** only used in setmapDatabase -- deprecate? */
public void clearJobs() {
jobQueue.clear();
}
@ -288,13 +285,19 @@ public class TileManager {
* Tiles remain locked in cache until the set is unlocked by either passing
* it again to this function or to releaseTiles. If passed TileSet is null
* it will be allocated.
*
* @param tileSet to be updated
* @return true if TileSet has changed
*/
public TileSet getActiveTiles(TileSet td) {
public boolean getActiveTiles(TileSet tileSet) {
if (mCurrentTiles == null)
return td;
return false;
if (td != null && td.serial == mUpdateSerial)
return td;
if (tileSet == null)
return false;
if (tileSet.serial == mUpdateSerial)
return false;
// dont flip new/currentTiles while copying
synchronized (mTilelock) {
@ -306,26 +309,24 @@ public class TileManager {
newTiles[i].lock();
MapTile[] nextTiles;
if (td == null) {
td = new TileSet(newTiles.length);
mTileSets.add(td);
}
nextTiles = td.tiles;
nextTiles = tileSet.tiles;
// unlock previously active tiles
for (int i = 0, n = td.cnt; i < n; i++)
for (int i = 0, n = tileSet.cnt; i < n; i++)
nextTiles[i].unlock();
if (nextTiles.length != mCurrentTiles.tiles.length) {
tileSet.tiles = nextTiles = new MapTile[mCurrentTiles.tiles.length];
}
// copy newTiles to nextTiles
System.arraycopy(newTiles, 0, nextTiles, 0, cnt);
td.serial = mUpdateSerial;
td.cnt = cnt;
tileSet.serial = mUpdateSerial;
tileSet.cnt = cnt;
}
return td;
return true;
}
// /**
@ -392,8 +393,9 @@ public class TileManager {
if (mTilesSize == mTiles.length) {
if (mTilesSize > mTilesCount) {
//Log.d(TAG, "repack: " + mTiles.length + " / " + mTilesCount);
TileDistanceSort.sort(mTiles, 0, mTilesSize);
// sorting also repacks the 'sparse' filled array
// so end of mTiles is at mTilesCount now
mTilesSize = mTilesCount;
}
@ -551,10 +553,10 @@ public class TileManager {
* called from MapWorker Thread when tile is loaded by MapTileLoader
*
* @param tile
* Tile ready for upload to GL
* @return ... caller does not care
* Tile ready for upload in TileRenderLayer
* @return caller does not care
*/
public synchronized boolean passTile(MapTile tile) {
public boolean passTile(MapTile tile) {
if (tile.state != STATE_LOADING) {
// - should rather be STATE_FAILED
@ -565,6 +567,8 @@ public class TileManager {
}
tile.state = STATE_NEW_DATA;
// is volatile
mTilesForUpload++;
// locked means the tile is visible or referenced by

View File

@ -48,24 +48,15 @@ public class TileRenderLayer extends RenderLayer {
mMapPosition.copy(pos);
int serial = 0;
if (mDrawTiles != null)
serial = mDrawTiles.getSerial();
boolean tilesChanged;
synchronized (tilelock) {
// get current tiles to draw
mDrawTiles = mTileManager.getActiveTiles(mDrawTiles);
tilesChanged = mTileManager.getActiveTiles(mDrawTiles);
}
if (mDrawTiles == null || mDrawTiles.cnt == 0)
if (mDrawTiles.cnt == 0)
return;
boolean tilesChanged = false;
// check if tiles have changed.
if (serial != mDrawTiles.getSerial())
tilesChanged = true;
int tileCnt = mDrawTiles.cnt;
MapTile[] tiles = mDrawTiles.tiles;
@ -90,6 +81,14 @@ public class TileRenderLayer extends RenderLayer {
}
public void clearTiles() {
// Clear all references to MapTiles as all current
// tiles will also be removed from TileManager.
GLRenderer.drawlock.lock();
mDrawTiles = new TileSet();
GLRenderer.drawlock.unlock();
}
/** compile tile layer data and upload to VBOs */
private static int compileTileLayers(MapTile[] tiles, int tileCnt) {
int uploadCnt = 0;
@ -189,10 +188,22 @@ public class TileRenderLayer extends RenderLayer {
}
}
// get a TileSet of currently visible tiles
public TileSet getVisibleTiles(TileSet td) {
if (mDrawTiles == null)
return td;
/**
* Update tileSet with currently visible tiles
* get a TileSet of currently visible tiles
*/
public boolean getVisibleTiles(TileSet tileSet) {
if (tileSet == null)
return false;
if (mDrawTiles == null) {
releaseTiles(tileSet);
return false;
}
// same tiles as before
if (tileSet.serial == mDrawTiles.serial)
return false;
// ensure tiles keep visible state
synchronized (tilelock) {
@ -200,24 +211,26 @@ public class TileRenderLayer extends RenderLayer {
MapTile[] newTiles = mDrawTiles.tiles;
int cnt = mDrawTiles.cnt;
if (td == null)
td = new TileSet(newTiles.length);
// unlock previous tiles
for (int i = 0; i < td.cnt; i++)
td.tiles[i].unlock();
for (int i = 0; i < tileSet.cnt; i++)
tileSet.tiles[i].unlock();
// ensure same size
if (tileSet.tiles.length != mDrawTiles.tiles.length) {
tileSet.tiles = new MapTile[mDrawTiles.tiles.length];
}
// lock tiles to not be removed from cache
td.cnt = 0;
tileSet.cnt = 0;
for (int i = 0; i < cnt; i++) {
MapTile t = newTiles[i];
if (t.isVisible && t.state == STATE_READY) {
t.lock();
td.tiles[td.cnt++] = t;
tileSet.tiles[tileSet.cnt++] = t;
}
}
}
return td;
return true;
}
public void releaseTiles(TileSet td) {
@ -236,7 +249,7 @@ public class TileRenderLayer extends RenderLayer {
// happens rarely, unless you live on Fidschi
/* package */int mNumTileHolder;
/* package */TileSet mDrawTiles;
/* package */TileSet mDrawTiles = new TileSet();
// scanline fill class used to check tile visibility
private final ScanBox mScanBox = new ScanBox() {
@ -301,4 +314,5 @@ public class TileRenderLayer extends RenderLayer {
}
}
};
}

View File

@ -14,9 +14,9 @@
*/
package org.oscim.layers.tile;
import java.util.Arrays;
import java.util.Comparator;
/**
* use with TileManager.getActiveTiles(TileSet) to get the current tiles. tiles
* are locked to not be modifed until getActiveTiles passes them back on a
@ -28,18 +28,19 @@ public final class TileSet {
int serial;
public int getSerial(){
public int getSerial() {
return serial;
}
TileSet() {
public TileSet() {
tiles = new MapTile[1];
}
public TileSet(int numTiles) {
tiles = new MapTile[numTiles];
}
public MapTile getTile(int x, int y){
public MapTile getTile(int x, int y) {
for (int i = 0; i < cnt; i++)
if (tiles[i].tileX == x && tiles[i].tileY == y)
return tiles[i];
@ -47,6 +48,11 @@ public final class TileSet {
return null;
}
public void clear() {
Arrays.fill(tiles, null);
cnt = 0;
}
public static Comparator<MapTile> coordComparator = new CoordComparator();
public static class CoordComparator implements Comparator<MapTile> {

View File

@ -104,7 +104,7 @@ public class MapTileLayer extends TileLayer<MapTileLoader> {
else
MapView.enableClosePolygons = false;
clearMap();
mMapView.clearMap();
resumeLoaders();
@ -168,7 +168,7 @@ public class MapTileLayer extends TileLayer<MapTileLoader> {
mRenderTheme = internalRenderTheme.name();
}
clearMap();
mMapView.clearMap();
return ret;
}
@ -194,7 +194,7 @@ public class MapTileLayer extends TileLayer<MapTileLoader> {
mRenderTheme = renderThemePath;
}
clearMap();
mMapView.clearMap();
}
private boolean setRenderTheme(Theme theme) {

View File

@ -41,6 +41,7 @@ public class ExtrusionRenderLayer extends RenderLayer {
public ExtrusionRenderLayer(MapView mapView, org.oscim.layers.tile.TileRenderLayer tileRenderLayer) {
super(mapView);
mTileLayer = tileRenderLayer;
mTileSet = new TileSet();
}
private static int[] shaderProgram = new int[2];
@ -56,7 +57,7 @@ public class ExtrusionRenderLayer extends RenderLayer {
// FIXME sum up size used while filling layer only up to:
//public int mBufferSize = 65536;
private TileSet mTileSet;
private final TileSet mTileSet;
private MapTile[] mTiles;
private int mTileCnt;
@ -85,18 +86,15 @@ public class ExtrusionRenderLayer extends RenderLayer {
}
}
int ready = 0;
mTileSet = mTileLayer.getVisibleTiles(mTileSet);
if (mTileSet == null)
return;
MapTile[] tiles = mTileSet.tiles;
// FIXME just release tiles in this case
if (mAlpha == 0 || pos.zoomLevel < 16) {
isReady = false;
return;
}
int ready = 0;
mTileLayer.getVisibleTiles(mTileSet);
MapTile[] tiles = mTileSet.tiles;
// keep a list of tiles available for rendering
if (mTiles == null || mTiles.length < mTileSet.cnt * 4)
mTiles = new MapTile[mTileSet.cnt * 4];
@ -104,9 +102,6 @@ public class ExtrusionRenderLayer extends RenderLayer {
ExtrusionLayer el;
if (pos.zoomLevel >= 17) {
for (int i = 0; i < mTileSet.cnt; i++) {
if (!tiles[i].isVisible)
continue;
el = getLayer(tiles[i]);
if (el == null)
continue;
@ -123,12 +118,9 @@ public class ExtrusionRenderLayer extends RenderLayer {
mTiles[ready++] = tiles[i];
}
} else if (pos.zoomLevel == 16) {
// check if proxy children are ready
for (int i = 0; i < mTileSet.cnt; i++) {
if (!tiles[i].isVisible)
continue;
MapTile t = tiles[i];
for (byte j = 0; j < 4; j++) {
if ((t.proxies & (1 << j)) != 0) {
MapTile c = t.rel.get(j);

View File

@ -57,6 +57,7 @@ import org.oscim.view.MapViewPosition;
import android.opengl.GLES20;
import android.os.SystemClock;
import android.util.Log;
public class TextRenderLayer extends BasicRenderLayer {
private final static String TAG = TextRenderLayer.class.getName();
@ -64,7 +65,7 @@ public class TextRenderLayer extends BasicRenderLayer {
private final static float MIN_WAY_DIST = 3;
private final MapViewPosition mMapViewPosition;
private TileSet mTileSet;
private final TileSet mTileSet;
private final LabelThread mThread;
private MapPosition mTmpPos;
@ -170,15 +171,18 @@ public class TextRenderLayer extends BasicRenderLayer {
@Override
protected void doWork() {
SystemClock.sleep(250);
if (!mRun)
return;
mRun = false;
synchronized (this) {
if (updateLabels()) {
mMapView.render();
} else {
mRun = true;
if (updateLabels()) {
mRun = false;
mMapView.render();
} else {
mRun = true;
}
}
}
@ -202,8 +206,10 @@ public class TextRenderLayer extends BasicRenderLayer {
mMapViewPosition = mapView.getMapViewPosition();
mTileLayer = baseLayer;
mTileSet = new TileSet();
layers.textureLayers = new TextLayer();
mTmpLayer = new TextLayer();
//mActiveTiles = new HashMap<MapTile, LabelTile>();
mTmpPos = new MapPosition();
mThread = new LabelThread();
@ -348,9 +354,7 @@ public class TextRenderLayer extends BasicRenderLayer {
return false;
// get current tiles
mTileSet = mTileLayer.getVisibleTiles(mTileSet);
if (mTileSet == null)
return false;
mTileLayer.getVisibleTiles(mTileSet);
if (mTileSet.cnt == 0)
return false;
@ -613,10 +617,10 @@ public class TextRenderLayer extends BasicRenderLayer {
mTileLayer.releaseTiles(mTileSet);
// pass new labels for rendering
synchronized (this) {
//synchronized (this) {
mNextLayer = tl;
mDebugLayer = dbg;
}
//}
return true;
}
@ -774,4 +778,14 @@ public class TextRenderLayer extends BasicRenderLayer {
// mRun = false;
// }
}
public void clearLabels() {
Log.d(TAG, "clearLabels");
synchronized (mThread) {
mRun = false;
mPool.releaseAll(mPrevLabels);
mPrevLabels = null;
mTileSet.clear();
}
}
}

View File

@ -37,6 +37,22 @@ public abstract class Pool<T extends Inlist<T>> {
pool = item;
}
public void releaseAll(T item) {
if (item == null)
return;
while (item != null) {
T next = item.next;
clearItem(item);
item.next = pool;
pool = item;
item = next;
}
}
// remove 'item' from 'list' and add back to pool
public T release(T list, T item) {
if (item == null)
@ -44,7 +60,6 @@ public abstract class Pool<T extends Inlist<T>> {
clearItem(item);
if (item == list) {
T ret = item.next;

View File

@ -95,6 +95,24 @@ public class LayerManager extends AbstractList<Layer> implements OnGestureListen
o.onDetach();
}
public void onUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
if (mDirtyLayers)
updateLayers();
for (Layer l : mLayers)
l.onUpdate(mapPosition, changed, clear);
}
public void destroy() {
if (mDirtyLayers)
updateLayers();
for (Layer l : mLayers) {
l.destroy();
}
}
Layer[] mLayers;
InputLayer[] mInputLayer;
@ -371,22 +389,7 @@ public class LayerManager extends AbstractList<Layer> implements OnGestureListen
return false;
}
public void onUpdate(MapPosition mapPosition, boolean changed) {
if (mDirtyLayers)
updateLayers();
for (Layer l : mLayers)
l.onUpdate(mapPosition, changed);
}
public void destroy() {
if (mDirtyLayers)
updateLayers();
for (Layer l : mLayers) {
l.destroy();
}
}
// /**
// * Gets the optional TilesLayer class.

View File

@ -66,6 +66,7 @@ public class MapView extends RelativeLayout {
private int mWidth;
private int mHeight;
private boolean mInitialized;
// FIXME: keep until old pbmap reader is removed
public static boolean enableClosePolygons = false;
@ -133,6 +134,7 @@ public class MapView extends RelativeLayout {
addView(mGLView, params);
clearMap();
redrawMap(false);
}
@ -147,7 +149,7 @@ public class MapView extends RelativeLayout {
mRotationEnabled = true;
//mLayerManager.add(new GenericOverlay(this, new GridOverlay(this)));
//mLayerManager.add(new GenericOverlay(this, new GridRenderLayer(this)));
mLayerManager.add(new BuildingOverlay(this, baseLayer.getTileLayer()));
mLayerManager.add(new LabelingOverlay(this, baseLayer.getTileLayer()));
@ -161,8 +163,6 @@ public class MapView extends RelativeLayout {
public MapTileLayer setBaseMap(BitmapTileLayer tileLayer) {
mLayerManager.add(0, new MapEventLayer(this));
mLayerManager.add(1, tileLayer);
//mRotationEnabled = true;
return null;
}
@ -170,6 +170,11 @@ public class MapView extends RelativeLayout {
mLayerManager.destroy();
}
public void onStop() {
Log.d(TAG, "onStop");
//mLayerManager.destroy();
}
private boolean mPausing = false;
void onPause() {
@ -181,18 +186,12 @@ public class MapView extends RelativeLayout {
}
void onResume() {
if (this.mCompassEnabled)
mCompass.enable();
mPausing = false;
}
public void onStop() {
Log.d(TAG, "onStop");
//mLayerManager.destroy();
}
@Override
public boolean onTouchEvent(MotionEvent motionEvent) {
@ -212,11 +211,13 @@ public class MapView extends RelativeLayout {
mWidth = width;
mHeight = height;
if (width != 0 && height != 0)
mInitialized = (mWidth > 0 && mWidth > 0);
if (mInitialized)
mMapViewPosition.setViewport(width, height);
}
boolean mWaitRedraw;
/* private */boolean mWaitRedraw;
private final Runnable mRedrawRequest = new Runnable() {
@Override
@ -229,49 +230,62 @@ public class MapView extends RelativeLayout {
/**
* Request to redraw the map when a global state like position,
* datasource or theme has changed. This will trigger a call
* to onUpdate() to all Layers.
* to onUpdate() for all Layers.
*
* @param requestRender
* also request to draw a frame
*/
public void redrawMap(boolean requestRender) {
if (requestRender) {
if (!(mPausing || mWidth == 0 || mHeight == 0))
mGLView.requestRender();
}
if (requestRender && !mClearMap && !mPausing && mInitialized)
mGLView.requestRender();
if (!mWaitRedraw) {
mWaitRedraw = true;
post(mRedrawRequest);
}
}
private boolean mClearMap;
public void clearMap(){
mClearMap = true;
}
/**
* Request to render a frame. Use this for animations.
*/
public void render() {
mGLView.requestRender();
if (mClearMap)
redrawMap(false);
else
mGLView.requestRender();
}
/**
* Calculates all necessary tiles and adds jobs accordingly.
* Update all Layers on Main thread.
*
* @param forceRedraw TODO
* @param forceRedraw also render frame
* FIXME (does nothing atm)
*/
void redrawMapInternal(boolean forceRedraw) {
boolean changed = false;
boolean changed = forceRedraw;
if (mPausing || mWidth == 0 || mHeight == 0)
if (mPausing || !mInitialized)
return;
if (forceRedraw) {
if (forceRedraw && !mClearMap)
mGLView.requestRender();
changed = true;
}
// get the current MapPosition
changed |= mMapViewPosition.getMapPosition(mMapPosition);
mLayerManager.onUpdate(mMapPosition, changed);
mLayerManager.onUpdate(mMapPosition, changed, mClearMap);
// delay redraw until all layers had the chance to clear
// their state.
if (mClearMap){
mGLView.requestRender();
mClearMap =false;
}
}
/**