refactor and document Map

- merge Layers into Map
- change UpdateEvent to Map.UpdateListener
- make updateLayers protected
This commit is contained in:
Hannes Janetzek 2013-09-17 22:35:41 +02:00
parent 9a82c24a89
commit 4f1b3f262b
10 changed files with 294 additions and 360 deletions

View File

@ -19,8 +19,6 @@ import java.net.URL;
import org.oscim.backend.canvas.Bitmap;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.event.MapEvent;
import org.oscim.event.UpdateEvent;
import org.oscim.gdx.client.GwtBitmap;
import org.oscim.layers.tile.TileLayer;
import org.oscim.layers.tile.bitmap.TileSource.FadeStep;
@ -50,66 +48,33 @@ public class BitmapTileLayer extends TileLayer<TileLoader> {
mFade = mTileSource.getFadeSteps();
}
// @Override
// public void onUpdate(MapPosition pos, boolean changed, boolean clear) {
// super.onUpdate(pos, changed, clear);
//
// if (mFade == null) {
// mRenderLayer.setBitmapAlpha(1);
// return;
// }
//
// float alpha = 0;
// for (FadeStep f : mFade) {
// if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd)
// continue;
//
// if (f.alphaStart == f.alphaEnd) {
// alpha = f.alphaStart;
// break;
// }
// double range = f.scaleEnd / f.scaleStart;
// float a = (float)((range - (pos.scale / f.scaleStart)) / range);
// a = FastMath.clamp(a, 0, 1);
// // interpolate alpha between start and end
// alpha = a * f.alphaStart + (1 - a) * f.alphaEnd;
// break;
// }
//
// mRenderLayer.setBitmapAlpha(alpha);
// }
@Override
public void handleEvent(MapEvent event) {
super.handleEvent(event);
public void onMapUpdate(MapPosition pos, boolean changed, boolean clear) {
super.onMapUpdate(pos, changed, clear);
if (event instanceof UpdateEvent) {
if (mFade == null) {
mRenderLayer.setBitmapAlpha(1);
return;
}
if (mFade == null) {
mRenderLayer.setBitmapAlpha(1);
return;
}
MapPosition pos = mMap.getMapPosition();
float alpha = 0;
for (FadeStep f : mFade) {
if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd)
continue;
float alpha = 0;
for (FadeStep f : mFade) {
if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd)
continue;
if (f.alphaStart == f.alphaEnd) {
alpha = f.alphaStart;
break;
}
double range = f.scaleEnd / f.scaleStart;
float a = (float) ((range - (pos.scale / f.scaleStart)) / range);
a = FastMath.clamp(a, 0, 1);
// interpolate alpha between start and end
alpha = a * f.alphaStart + (1 - a) * f.alphaEnd;
if (f.alphaStart == f.alphaEnd) {
alpha = f.alphaStart;
break;
}
mRenderLayer.setBitmapAlpha(alpha);
double range = f.scaleEnd / f.scaleStart;
float a = (float) ((range - (pos.scale / f.scaleStart)) / range);
a = FastMath.clamp(a, 0, 1);
// interpolate alpha between start and end
alpha = a * f.alphaStart + (1 - a) * f.alphaEnd;
break;
}
mRenderLayer.setBitmapAlpha(alpha);
}
@Override

View File

@ -82,6 +82,33 @@ public abstract class GdxMap implements ApplicationListener {
}, delay / 1000f);
return true;
}
/**
* Update all Layers on Main thread.
*
* @param forceRedraw
* also render frame FIXME (does nothing atm)
*/
private void redrawMapInternal(boolean forceRedraw) {
// FIXME needed?
GLState.blend(false);
GLState.test(false, false);
updateLayers();
mRenderRequest = true;
Gdx.graphics.requestRendering();
}
/* private */boolean mWaitRedraw;
private final Runnable mRedrawRequest = new Runnable() {
@Override
public void run() {
mWaitRedraw = false;
redrawMapInternal(false);
}
};
};
mMapRenderer = new MapRenderer(mMap);
@ -200,30 +227,6 @@ public abstract class GdxMap implements ApplicationListener {
public void resume() {
}
/**
* Update all Layers on Main thread.
*
* @param forceRedraw
* also render frame FIXME (does nothing atm)
*/
void redrawMapInternal(boolean forceRedraw) {
GLState.blend(false);
GLState.test(false, false);
mMap.updateLayers();
mRenderRequest = true;
Gdx.graphics.requestRendering();
}
/* private */boolean mWaitRedraw;
private final Runnable mRedrawRequest = new Runnable() {
@Override
public void run() {
mWaitRedraw = false;
redrawMapInternal(false);
}
};
class TouchHandler implements InputProcessor {

View File

@ -0,0 +1,19 @@
package org.oscim.event;
import java.util.ArrayList;
import java.util.List;
public abstract class Dispatcher<T> {
protected List<T> listeners = new ArrayList<T>();
public void addListener(T l) {
if (!listeners.contains(l))
listeners.add(l);
}
public void removeListener(T l) {
listeners.remove(l);
}
public abstract void dispatch();
}

View File

@ -0,0 +1,5 @@
package org.oscim.event;
public interface IListener {
}

View File

@ -1,16 +0,0 @@
package org.oscim.event;
public class UpdateEvent extends MapEvent {
private static final long serialVersionUID = 1L;
public static final String TYPE = "UpdateEvent";
public UpdateEvent(Object source) {
super(source);
}
public boolean positionChanged;
public boolean clearMap;
}

View File

@ -16,16 +16,14 @@ package org.oscim.layers.tile;
import java.util.ArrayList;
import org.oscim.event.EventListener;
import org.oscim.event.MapEvent;
import org.oscim.event.UpdateEvent;
import org.oscim.core.MapPosition;
import org.oscim.layers.Layer;
import org.oscim.map.Map;
import org.oscim.tiling.TileLoader;
import org.oscim.tiling.TileManager;
import org.oscim.tiling.TileRenderer;
public abstract class TileLayer<T extends TileLoader> extends Layer implements EventListener {
public abstract class TileLayer<T extends TileLoader> extends Layer implements Map.UpdateListener {
//private final static String TAG = TileLayer.class.getName();
private final static int MAX_ZOOMLEVEL = 17;
private final static int MIN_ZOOMLEVEL = 2;
@ -61,8 +59,6 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements E
// RenderLayer is working in GL Thread and actually
// drawing loaded tiles to screen.
mRenderer = mRenderLayer = new TileRenderer(mTileManager);
map.addListener(UpdateEvent.TYPE, this);
}
abstract protected T createLoader(TileManager tm);
@ -71,55 +67,32 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements E
return (TileRenderer) mRenderer;
}
// @Override
// public void onUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
//
// if (clear || mInitial) {
// mRenderLayer.clearTiles();
// mTileManager.init(mInitial);
// mInitial = false;
// changed = true;
// }
//
// if (changed && mTileManager.update(mapPosition))
// notifyLoaders();
// }
@Override
public void handleEvent(MapEvent event) {
if (event instanceof UpdateEvent) {
UpdateEvent e = (UpdateEvent) event;
boolean changed = e.positionChanged;
if (e.clearMap || mInitial) {
mRenderLayer.clearTiles();
mTileManager.init(mInitial);
mInitial = false;
changed = true;
}
if (changed && mTileManager.update(mMap.getMapPosition()))
notifyLoaders();
public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
if (clear || mInitial) {
mRenderLayer.clearTiles();
mTileManager.init(mInitial);
mInitial = false;
changed = true;
}
if (changed && mTileManager.update(mapPosition))
notifyLoaders();
}
@Override
public void onDetach() {
mMap.removeListener(UpdateEvent.TYPE, this);
for (T loader : mTileLoader) {
loader.pause();
loader.interrupt();
loader.cleanup();
// try {
// tileWorker.join(10000);
// } catch (InterruptedException e) {
// // restore the interrupted status
// Thread.currentThread().interrupt();
// }
//try {
// tileWorker.join(10000);
//} catch (InterruptedException e) {
// // restore the interrupted status
// Thread.currentThread().interrupt();
//}
}
mTileManager.destroy();
}

View File

@ -24,8 +24,6 @@ import org.oscim.backend.CanvasAdapter;
import org.oscim.backend.canvas.Bitmap;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.event.MapEvent;
import org.oscim.event.UpdateEvent;
import org.oscim.layers.tile.TileLayer;
import org.oscim.layers.tile.bitmap.TileSource.FadeStep;
import org.oscim.map.Map;
@ -53,66 +51,33 @@ public class BitmapTileLayer extends TileLayer<TileLoader> {
}
// @Override
// public void onUpdate(MapPosition pos, boolean changed, boolean clear) {
// super.onUpdate(pos, changed, clear);
//
// if (mFade == null) {
// mRenderLayer.setBitmapAlpha(1);
// return;
// }
//
// float alpha = 0;
// for (FadeStep f : mFade) {
// if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd)
// continue;
//
// if (f.alphaStart == f.alphaEnd) {
// alpha = f.alphaStart;
// break;
// }
// double range = f.scaleEnd / f.scaleStart;
// float a = (float)((range - (pos.scale / f.scaleStart)) / range);
// a = FastMath.clamp(a, 0, 1);
// // interpolate alpha between start and end
// alpha = a * f.alphaStart + (1 - a) * f.alphaEnd;
// break;
// }
//
// mRenderLayer.setBitmapAlpha(alpha);
// }
@Override
public void handleEvent(MapEvent event) {
super.handleEvent(event);
public void onMapUpdate(MapPosition pos, boolean changed, boolean clear) {
super.onMapUpdate(pos, changed, clear);
if (event instanceof UpdateEvent){
if (mFade == null) {
mRenderLayer.setBitmapAlpha(1);
return;
}
if (mFade == null) {
mRenderLayer.setBitmapAlpha(1);
return;
}
MapPosition pos = mMap.getMapPosition();
float alpha = 0;
for (FadeStep f : mFade) {
if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd)
continue;
float alpha = 0;
for (FadeStep f : mFade) {
if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd)
continue;
if (f.alphaStart == f.alphaEnd) {
alpha = f.alphaStart;
break;
}
double range = f.scaleEnd / f.scaleStart;
float a = (float)((range - (pos.scale / f.scaleStart)) / range);
a = FastMath.clamp(a, 0, 1);
// interpolate alpha between start and end
alpha = a * f.alphaStart + (1 - a) * f.alphaEnd;
if (f.alphaStart == f.alphaEnd) {
alpha = f.alphaStart;
break;
}
mRenderLayer.setBitmapAlpha(alpha);
double range = f.scaleEnd / f.scaleStart;
float a = (float)((range - (pos.scale / f.scaleStart)) / range);
a = FastMath.clamp(a, 0, 1);
// interpolate alpha between start and end
alpha = a * f.alphaStart + (1 - a) * f.alphaEnd;
break;
}
mRenderLayer.setBitmapAlpha(alpha);
}
@Override

View File

@ -15,15 +15,15 @@
package org.oscim.layers.tile.vector.labeling;
import org.oscim.backend.Log;
import org.oscim.core.MapPosition;
import org.oscim.event.EventListener;
import org.oscim.event.MapEvent;
import org.oscim.event.MotionEvent;
import org.oscim.event.UpdateEvent;
import org.oscim.layers.Layer;
import org.oscim.map.Map;
import org.oscim.tiling.TileRenderer;
public class LabelLayer extends Layer implements EventListener {
public class LabelLayer extends Layer implements EventListener, Map.UpdateListener {
private final static String TAG = LabelLayer.class.getName();
private final TextRenderer mTextRenderer;
@ -31,7 +31,7 @@ public class LabelLayer extends Layer implements EventListener {
public LabelLayer(Map map, TileRenderer tileRenderLayer) {
super(map);
map.addListener(UpdateEvent.TYPE, this);
map.addListener(MotionEvent.TYPE, this);
//mTextLayer = new org.oscim.renderer.layers.TextRenderLayer(map, tileRenderLayer);
@ -41,7 +41,6 @@ public class LabelLayer extends Layer implements EventListener {
@Override
public void onDetach() {
mMap.removeListener(UpdateEvent.TYPE, this);
mMap.removeListener(MotionEvent.TYPE, this);
// TODO stop and clear labeling thread
@ -50,13 +49,7 @@ public class LabelLayer extends Layer implements EventListener {
@Override
public void handleEvent(MapEvent event) {
if (event instanceof UpdateEvent) {
UpdateEvent e = (UpdateEvent) event;
if (e.clearMap)
mTextRenderer.clearLabels();
} else if (event instanceof MotionEvent) {
if (event instanceof MotionEvent) {
MotionEvent e = (MotionEvent) event;
int action = e.getAction() & MotionEvent.ACTION_MASK;
@ -75,11 +68,11 @@ public class LabelLayer extends Layer implements EventListener {
}
}
// @Override
// public void onUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
// if (clear)
// mTextRenderer.clearLabels();
// }
@Override
public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
if (clear)
mTextRenderer.clearLabels();
}
// @Override
// public boolean onTouchEvent(MotionEvent e) {

View File

@ -1,109 +0,0 @@
/*
* Copyright 2012 osmdroid authors
* Copyright 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.map;
import java.util.AbstractList;
import java.util.concurrent.CopyOnWriteArrayList;
import org.oscim.layers.Layer;
import org.oscim.renderer.LayerRenderer;
public class Layers extends AbstractList<Layer> {
//private final static String TAG = Layers.class.getName();
private final CopyOnWriteArrayList<Layer> mLayerList;
Layers() {
mLayerList = new CopyOnWriteArrayList<Layer>();
}
@Override
public synchronized Layer get(int index) {
return mLayerList.get(index);
}
@Override
public synchronized int size() {
return mLayerList.size();
}
@Override
public synchronized void add(int index, Layer element) {
mLayerList.add(index, element);
mDirtyLayers = true;
}
@Override
public synchronized Layer remove(int index) {
mDirtyLayers = true;
return mLayerList.remove(index);
}
@Override
public synchronized Layer set(int index, Layer element) {
mDirtyLayers = true;
return mLayerList.set(index, element);
}
private boolean mDirtyLayers;
private LayerRenderer[] mLayerRenderer;
public LayerRenderer[] getLayerRenderer() {
if (mDirtyLayers)
updateLayers();
return mLayerRenderer;
}
public void destroy() {
if (mDirtyLayers)
updateLayers();
for (Layer o : mLayers)
o.onDetach();
}
Layer[] mLayers;
private synchronized void updateLayers() {
if (!mDirtyLayers)
return;
mLayers = new Layer[mLayerList.size()];
int numRenderLayers = 0;
for (int i = 0, n = mLayerList.size(); i < n; i++) {
Layer o = mLayerList.get(i);
if (o.getRenderer() != null)
numRenderLayers++;
mLayers[i] = o;
}
mLayerRenderer = new LayerRenderer[numRenderLayers];
for (int i = 0, cntR = 0, n = mLayerList.size(); i < n; i++) {
Layer o = mLayerList.get(i);
LayerRenderer l = o.getRenderer();
if (l != null)
mLayerRenderer[cntR++] = l;
}
mDirtyLayers = false;
}
}

View File

@ -14,20 +14,24 @@
*/
package org.oscim.map;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
import org.oscim.backend.Log;
import org.oscim.core.BoundingBox;
import org.oscim.core.GeoPoint;
import org.oscim.core.MapPosition;
import org.oscim.core.MercatorProjection;
import org.oscim.event.Dispatcher;
import org.oscim.event.EventDispatcher;
import org.oscim.event.EventListener;
import org.oscim.event.IListener;
import org.oscim.event.MotionEvent;
import org.oscim.event.UpdateEvent;
import org.oscim.layers.Layer;
import org.oscim.layers.MapEventLayer;
import org.oscim.layers.tile.bitmap.BitmapTileLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.VectorTileLoader;
import org.oscim.renderer.LayerRenderer;
import org.oscim.renderer.MapRenderer;
import org.oscim.theme.IRenderTheme;
import org.oscim.theme.InternalRenderTheme;
@ -94,16 +98,21 @@ public abstract class Map implements EventDispatcher {
private InternalRenderTheme mCurrentTheme;
/**
* Utility function to set theme of base vector-layer and
* use map background color from theme.
*/
public void setTheme(InternalRenderTheme theme) {
if (mBaseLayer == null) {
Log.e(TAG, "No base layer set");
throw new IllegalStateException();
}
if (mCurrentTheme == theme){
if (mCurrentTheme == theme) {
Log.d(TAG, "same theme: " + theme);
return;
}
IRenderTheme t = ThemeLoader.load(theme);
if (t == null) {
Log.e(TAG, "Invalid theme");
@ -126,7 +135,7 @@ public abstract class Map implements EventDispatcher {
* Request call to onUpdate for all layers. This function can
* be called from any thread. Request will be handled on main
* thread.
*
*
* @param forceRedraw pass true to render next frame
*/
public abstract void updateMap(boolean forceRedraw);
@ -151,16 +160,21 @@ public abstract class Map implements EventDispatcher {
/**
* Post a task to run on a shared worker-thread. Only use for
* tasks running less than a second!
* */
public void addTask(Runnable task){
*/
public void addTask(Runnable task) {
mAsyncExecutor.post(task);
}
/**
* Return screen width in pixel.
*/
public abstract int getWidth();
/**
* Return screen height in pixel.
*/
public abstract int getHeight();
/**
* Request to clear all layers before rendering next frame
*/
@ -169,27 +183,9 @@ public abstract class Map implements EventDispatcher {
}
/**
* Do not call directly! This function is run on main-loop
* before rendering a frame.
* This function is run on main-loop before rendering a frame.
* Caution: Do not call directly!
*/
public void updateLayers() {
boolean changed = false;
// get the current MapPosition
changed |= mViewport.getMapPosition(mMapPosition);
//mLayers.onUpdate(mMapPosition, changed, mClearMap);
UpdateEvent e = new UpdateEvent(this);
e.clearMap = mClearMap;
e.positionChanged = changed;
for (EventListener l : mUpdateListeners)
l.handleEvent(e);
mClearMap = false;
}
public void setDebugSettings(DebugSettings debugSettings) {
mDebugSettings = debugSettings;
//MapTileLoader.setDebugSettings(debugSettings);
@ -197,6 +193,9 @@ public abstract class Map implements EventDispatcher {
public DebugSettings getDebugSettings() {
return mDebugSettings;
protected void updateLayers() {
mUpdateDispatcher.dispatch();
}
public void setMapPosition(MapPosition mapPosition) {
@ -242,32 +241,169 @@ public abstract class Map implements EventDispatcher {
return mAnimator;
}
ArrayList<EventListener> mUpdateListeners = new ArrayList<EventListener>();
ArrayList<EventListener> mMotionListeners = new ArrayList<EventListener>();
@Override
public void addListener(String type, EventListener listener) {
if (type == UpdateEvent.TYPE)
mUpdateListeners.add(listener);
else if (type == MotionEvent.TYPE)
if (type == MotionEvent.TYPE)
mMotionListeners.add(listener);
}
@Override
public void removeListener(String type, EventListener listener) {
if (type == UpdateEvent.TYPE)
mUpdateListeners.remove(listener);
else if (type == MotionEvent.TYPE)
if (type == MotionEvent.TYPE)
mMotionListeners.remove(listener);
}
public MapPosition getMapPosition() {
return mMapPosition;
}
public void handleMotionEvent(MotionEvent e) {
for (EventListener l : mMotionListeners)
l.handleEvent(e);
}
/**
* Listener interface for map update notifications.
*
* NOTE: Layers implementing this interface they will be automatically
* registered when the layer is added to the map and unresitered when
* the layer is removed.
*/
public interface UpdateListener extends IListener {
void onMapUpdate(MapPosition mapPosition, boolean positionChanged, boolean clear);
}
private class UpdateDispatcher extends Dispatcher<UpdateListener> {
@Override
public void dispatch() {
boolean changed = false;
// get the current MapPosition
changed |= mViewport.getMapPosition(mMapPosition);
for (UpdateListener l : listeners)
l.onMapUpdate(mMapPosition, changed, mClearMap);
mClearMap = false;
}
}
private final UpdateDispatcher mUpdateDispatcher = new UpdateDispatcher();
/**
* Register UpdateListener
*/
public void addUpdateListener(UpdateListener l) {
mUpdateDispatcher.addListener(l);
}
/**
* Unregister UpdateListener
*/
public void removeUpdateListener(UpdateListener l) {
mUpdateDispatcher.removeListener(l);
}
public final class Layers extends AbstractList<Layer> {
private final CopyOnWriteArrayList<Layer> mLayerList;
Layers() {
mLayerList = new CopyOnWriteArrayList<Layer>();
}
@Override
public synchronized Layer get(int index) {
return mLayerList.get(index);
}
@Override
public synchronized int size() {
return mLayerList.size();
}
@Override
public synchronized void add(int index, Layer layer) {
if (mLayerList.contains(layer))
throw new IllegalArgumentException("layer added twice");
if (layer instanceof UpdateListener)
addUpdateListener((UpdateListener) layer);
mLayerList.add(index, layer);
mDirtyLayers = true;
}
@Override
public synchronized Layer remove(int index) {
mDirtyLayers = true;
Layer remove = mLayerList.remove(index);
if (remove instanceof UpdateListener)
removeUpdateListener((UpdateListener) remove);
return remove;
}
@Override
public synchronized Layer set(int index, Layer layer) {
if (mLayerList.contains(layer))
throw new IllegalArgumentException("layer added twice");
mDirtyLayers = true;
Layer remove = mLayerList.set(index, layer);
if (remove instanceof UpdateListener)
removeUpdateListener((UpdateListener) remove);
return remove;
}
private boolean mDirtyLayers;
private LayerRenderer[] mLayerRenderer;
public LayerRenderer[] getLayerRenderer() {
if (mDirtyLayers)
updateLayers();
return mLayerRenderer;
}
public void destroy() {
if (mDirtyLayers)
updateLayers();
for (Layer o : mLayers)
o.onDetach();
}
Layer[] mLayers;
private synchronized void updateLayers() {
if (!mDirtyLayers)
return;
mLayers = new Layer[mLayerList.size()];
int numRenderLayers = 0;
for (int i = 0, n = mLayerList.size(); i < n; i++) {
Layer o = mLayerList.get(i);
if (o.getRenderer() != null)
numRenderLayers++;
mLayers[i] = o;
}
mLayerRenderer = new LayerRenderer[numRenderLayers];
for (int i = 0, cntR = 0, n = mLayerList.size(); i < n; i++) {
Layer o = mLayerList.get(i);
LayerRenderer l = o.getRenderer();
if (l != null)
mLayerRenderer[cntR++] = l;
}
mDirtyLayers = false;
}
}
}