diff --git a/vtm-android-app b/vtm-android-app
index 7f3da3fb..b0e4f6b7 160000
--- a/vtm-android-app
+++ b/vtm-android-app
@@ -1 +1 @@
-Subproject commit 7f3da3fb6e7e8d40280e944ad1fa3bf0504cb4b2
+Subproject commit b0e4f6b7579b22d5b01fe14ebb1b79b9c6f35b8f
diff --git a/vtm-android-example/src/org/oscim/android/test/MarkerOverlayActivity.java b/vtm-android-example/src/org/oscim/android/test/MarkerOverlayActivity.java
index bde28372..db2ab5da 100644
--- a/vtm-android-example/src/org/oscim/android/test/MarkerOverlayActivity.java
+++ b/vtm-android-example/src/org/oscim/android/test/MarkerOverlayActivity.java
@@ -22,8 +22,8 @@ import java.util.List;
 import org.oscim.android.canvas.AndroidGraphics;
 import org.oscim.core.GeoPoint;
 import org.oscim.layers.TileGridLayer;
-import org.oscim.layers.marker.ItemizedIconLayer;
-import org.oscim.layers.marker.ItemizedIconLayer.OnItemGestureListener;
+import org.oscim.layers.marker.ItemizedLayer;
+import org.oscim.layers.marker.ItemizedLayer.OnItemGestureListener;
 import org.oscim.layers.marker.MarkerItem;
 import org.oscim.layers.marker.MarkerItem.HotspotPlace;
 import org.oscim.layers.marker.MarkerSymbol;
@@ -47,8 +47,8 @@ implements OnItemGestureListener<MarkerItem> {
 		Drawable d = getResources().getDrawable(R.drawable.marker_poi);
 		MarkerSymbol symbol = AndroidGraphics.makeMarker(d, HotspotPlace.CENTER);
 
-		ItemizedIconLayer<MarkerItem> markerLayer =
-		        new ItemizedIconLayer<MarkerItem>(mMap, new ArrayList<MarkerItem>(),
+		ItemizedLayer<MarkerItem> markerLayer =
+		        new ItemizedLayer<MarkerItem>(mMap, new ArrayList<MarkerItem>(),
 		                                          symbol, this);
 
 		mMap.getLayers().add(markerLayer);
diff --git a/vtm/src/org/oscim/layers/Layer.java b/vtm/src/org/oscim/layers/Layer.java
index 405eff15..8ff7b7b5 100644
--- a/vtm/src/org/oscim/layers/Layer.java
+++ b/vtm/src/org/oscim/layers/Layer.java
@@ -53,4 +53,8 @@ public abstract class Layer {
 	 */
 	public void onDetach() {
 	}
+
+	public Map map() {
+		return mMap;
+	}
 }
diff --git a/vtm/src/org/oscim/layers/marker/ItemizedIconLayer.java b/vtm/src/org/oscim/layers/marker/ItemizedIconLayer.java
deleted file mode 100644
index ae026cd8..00000000
--- a/vtm/src/org/oscim/layers/marker/ItemizedIconLayer.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright 2012 osmdroid authors
- * Copyright 2013 Hannes Janetzek
- *
- * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
- *
- * 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.marker;
-
-import java.util.List;
-
-import org.oscim.core.BoundingBox;
-import org.oscim.core.Point;
-import org.oscim.event.Gesture;
-import org.oscim.event.GestureListener;
-import org.oscim.event.MotionEvent;
-import org.oscim.map.Map;
-import org.oscim.map.Viewport;
-
-public class ItemizedIconLayer<Item extends MarkerItem> extends ItemizedLayer<Item>
-        implements GestureListener {
-	//static final Logger log = LoggerFactory.getLogger(ItemizedIconOverlay.class);
-
-	protected final List<Item> mItemList;
-	protected OnItemGestureListener<Item> mOnItemGestureListener;
-	private int mDrawnItemsLimit = Integer.MAX_VALUE;
-
-	private final Point mTmpPoint = new Point();
-
-	public ItemizedIconLayer(Map map, List<Item> list,
-	        MarkerSymbol defaultMarker,
-	        OnItemGestureListener<Item> onItemGestureListener) {
-
-		super(map, defaultMarker);
-
-		mItemList = list;
-		mOnItemGestureListener = onItemGestureListener;
-		populate();
-	}
-
-	@Override
-	public boolean onSnapToItem(int x, int y, Point snapPoint) {
-		// TODO Implement this!
-		return false;
-	}
-
-	@Override
-	protected Item createItem(int index) {
-		return mItemList.get(index);
-	}
-
-	@Override
-	public int size() {
-		return Math.min(mItemList.size(), mDrawnItemsLimit);
-	}
-
-	public boolean addItem(Item item) {
-		final boolean result = mItemList.add(item);
-		populate();
-		return result;
-	}
-
-	public void addItem(int location, Item item) {
-		mItemList.add(location, item);
-	}
-
-	public boolean addItems(List<Item> items) {
-		final boolean result = mItemList.addAll(items);
-		populate();
-		return result;
-	}
-
-	public void removeAllItems() {
-		removeAllItems(true);
-	}
-
-	public void removeAllItems(boolean withPopulate) {
-		mItemList.clear();
-		if (withPopulate) {
-			populate();
-		}
-	}
-
-	public boolean removeItem(Item item) {
-		final boolean result = mItemList.remove(item);
-		populate();
-		return result;
-	}
-
-	public Item removeItem(int position) {
-		final Item result = mItemList.remove(position);
-		populate();
-		return result;
-	}
-
-	/**
-	 * Each of these methods performs a item sensitive check. If the item is
-	 * located its corresponding method is called. The result of the call is
-	 * returned. Helper methods are provided so that child classes may more
-	 * easily override behavior without resorting to overriding the
-	 * ItemGestureListener methods.
-	 */
-	//	@Override
-	//	public boolean onTap(MotionEvent event, MapPosition pos) {
-	//		return activateSelectedItems(event, mActiveItemSingleTap);
-	//	}
-
-	protected boolean onSingleTapUpHelper(int index, Item item) {
-		return mOnItemGestureListener.onItemSingleTapUp(index, item);
-	}
-
-	private final ActiveItem mActiveItemSingleTap = new ActiveItem() {
-		@Override
-		public boolean run(int index) {
-			final ItemizedIconLayer<Item> that = ItemizedIconLayer.this;
-			if (that.mOnItemGestureListener == null) {
-				return false;
-			}
-			return onSingleTapUpHelper(index, that.mItemList.get(index));
-		}
-	};
-
-	//	@Override
-	//	public boolean onLongPress(MotionEvent event, MapPosition pos) {
-	//		return activateSelectedItems(event, mActiveItemLongPress);
-	//	}
-
-	//	protected boolean onLongPressHelper(int index, Item item) {
-	//		return this.mOnItemGestureListener.onItemLongPress(index, item);
-	//	}
-	//
-	//	private final ActiveItem mActiveItemLongPress = new ActiveItem() {
-	//		@Override
-	//		public boolean run(final int index) {
-	//			final ItemizedIconLayer<Item> that = ItemizedIconLayer.this;
-	//			if (that.mOnItemGestureListener == null) {
-	//				return false;
-	//			}
-	//			return onLongPressHelper(index, getItem(index));
-	//		}
-	//	};
-
-	//	@Override
-	//	public boolean onPress(MotionEvent e, MapPosition pos) {
-	//		return false;
-	//	}
-
-	/**
-	 * When a content sensitive action is performed the content item needs to be
-	 * identified. This method does that and then performs the assigned task on
-	 * that item.
-	 * 
-	 * @param event
-	 *            ...
-	 * @param task
-	 *            ..
-	 * @return true if event is handled false otherwise
-	 */
-	protected boolean activateSelectedItems(MotionEvent event, ActiveItem task) {
-		int size = mItemList.size();
-		if (size == 0)
-			return false;
-
-		int eventX = (int) event.getX() - mMap.getWidth() / 2;
-		int eventY = (int) event.getY() - mMap.getHeight() / 2;
-		Viewport mapPosition = mMap.getViewport();
-
-		BoundingBox bbox = mapPosition.getViewBox();
-
-		int nearest = -1;
-		double dist = Double.MAX_VALUE;
-
-		for (int i = 0; i < size; i++) {
-			Item item = getItem(i);
-
-			if (!bbox.contains(item.mGeoPoint))
-				continue;
-
-			// TODO use intermediate projection
-			mapPosition.toScreenPoint(item.getPoint(), mTmpPoint);
-
-			float dx = (float) (mTmpPoint.x - eventX);
-			float dy = (float) (mTmpPoint.y - eventY);
-
-			// squared dist: 50*50 pixel
-			double d = dx * dx + dy * dy;
-			if (d < 2500) {
-				if (d < dist) {
-					dist = d;
-					nearest = i;
-				}
-			}
-		}
-
-		if (nearest >= 0 && task.run(nearest)) {
-			return true;
-		}
-
-		return false;
-	}
-
-	public int getDrawnItemsLimit() {
-		return this.mDrawnItemsLimit;
-	}
-
-	public void setDrawnItemsLimit(final int aLimit) {
-		this.mDrawnItemsLimit = aLimit;
-	}
-
-	/**
-	 * When the item is touched one of these methods may be invoked depending on
-	 * the type of touch. Each of them returns true if the event was completely
-	 * handled.
-	 * 
-	 * @param <T>
-	 *            ....
-	 */
-	public static interface OnItemGestureListener<T> {
-		public boolean onItemSingleTapUp(int index, T item);
-
-		public boolean onItemLongPress(int index, T item);
-	}
-
-	public static interface ActiveItem {
-		public boolean run(int aIndex);
-	}
-
-	@Override
-	public boolean onGesture(Gesture g, MotionEvent e) {
-		if (g instanceof Gesture.Tap)
-			return activateSelectedItems(e, mActiveItemSingleTap);
-
-		return false;
-	}
-}
diff --git a/vtm/src/org/oscim/layers/marker/ItemizedLayer.java b/vtm/src/org/oscim/layers/marker/ItemizedLayer.java
index e7bebbe2..36c81848 100644
--- a/vtm/src/org/oscim/layers/marker/ItemizedLayer.java
+++ b/vtm/src/org/oscim/layers/marker/ItemizedLayer.java
@@ -1,5 +1,9 @@
 /*
- * Copyright 2012 osmdroid authors
+ * Copyright 2012 osmdroid authors:
+ * Copyright 2012 Nicolas Gramlich
+ * Copyright 2012 Theodore Hong
+ * Copyright 2012 Fred Eisele
+ * 
  * Copyright 2013 Hannes Janetzek
  *
  * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
@@ -15,287 +19,208 @@
  * 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.marker;
 
-// TODO
-// - need to sort items back to front for rendering
-// - and to make this work for multiple overlays
-//   a global scenegraph is probably required.
+import java.util.List;
 
-import org.oscim.core.MapPosition;
-import org.oscim.core.MercatorProjection;
+import org.oscim.core.BoundingBox;
 import org.oscim.core.Point;
-import org.oscim.core.Tile;
+import org.oscim.event.Gesture;
+import org.oscim.event.GestureListener;
+import org.oscim.event.MotionEvent;
 import org.oscim.map.Map;
-import org.oscim.renderer.ElementRenderer;
-import org.oscim.renderer.MapRenderer.Matrices;
-import org.oscim.renderer.elements.SymbolItem;
-import org.oscim.renderer.elements.SymbolLayer;
-import org.oscim.utils.GeometryUtils;
+import org.oscim.map.Viewport;
 
-/* @author Marc Kurtz
- * @author Nicolas Gramlich
- * @author Theodore Hong
- * @author Fred Eisele
- * @author Hannes Janetzek
- * */
-/**
- * Draws a list of {@link MarkerItem} as markers to a map. The item with the
- * lowest index is drawn as last and therefore the 'topmost' marker. It also
- * gets checked for onTap first. This class is generic, because you then you get
- * your custom item-class passed back in onTap().
- * 
- * @param <Item>
- *            ...
- */
-public abstract class ItemizedLayer<Item extends MarkerItem> extends MarkerLayer implements
-        MarkerLayer.Snappable {
+public class ItemizedLayer<Item extends MarkerItem> extends MarkerLayer<Item>
+        implements GestureListener {
 
-	//static final Logger log = LoggerFactory.getLogger(ItemizedOverlay.class);
+	//static final Logger log = LoggerFactory.getLogger(ItemizedIconOverlay.class);
 
-	protected final MarkerSymbol mDefaultMarker;
+	protected final List<Item> mItemList;
+	protected final Point mTmpPoint = new Point();
+	protected OnItemGestureListener<Item> mOnItemGestureListener;
+	protected int mDrawnItemsLimit = Integer.MAX_VALUE;
 
-	/** increase view to show items that are partially visible */
-	protected int mExtents = 100;
+	public ItemizedLayer(Map map, List<Item> list,
+	        MarkerSymbol defaultMarker,
+	        OnItemGestureListener<Item> onItemGestureListener) {
 
-	class InternalItem {
-		InternalItem next;
+		super(map, defaultMarker);
 
-		Item item;
-		boolean visible;
-		boolean changes;
-		float x, y;
-		double px, py;
+		mItemList = list;
+		mOnItemGestureListener = onItemGestureListener;
+		populate();
 	}
 
-	/* package */InternalItem mItems;
-	/* package */Object lock = new Object();
-	/* package */Item mFocusedItem;
-	/* package */boolean mUpdate;
+	@Override
+	protected Item createItem(int index) {
+		return mItemList.get(index);
+	}
 
-	private int mSize;
+	@Override
+	public int size() {
+		return Math.min(mItemList.size(), mDrawnItemsLimit);
+	}
 
-	class ItemOverlay extends ElementRenderer {
+	public boolean addItem(Item item) {
+		final boolean result = mItemList.add(item);
+		populate();
+		return result;
+	}
 
-		private final SymbolLayer mSymbolLayer;
-		private final float[] mBox = new float[8];
+	public void addItem(int location, Item item) {
+		mItemList.add(location, item);
+	}
 
-		public ItemOverlay() {
-			mSymbolLayer = new SymbolLayer();
+	public boolean addItems(List<Item> items) {
+		final boolean result = mItemList.addAll(items);
+		populate();
+		return result;
+	}
+
+	public void removeAllItems() {
+		removeAllItems(true);
+	}
+
+	public void removeAllItems(boolean withPopulate) {
+		mItemList.clear();
+		if (withPopulate) {
+			populate();
 		}
+	}
 
-		// note: this is called from GL-Thread. so check your syncs!
+	public boolean removeItem(Item item) {
+		final boolean result = mItemList.remove(item);
+		populate();
+		return result;
+	}
+
+	public Item removeItem(int position) {
+		final Item result = mItemList.remove(position);
+		populate();
+		return result;
+	}
+
+	/**
+	 * Each of these methods performs a item sensitive check. If the item is
+	 * located its corresponding method is called. The result of the call is
+	 * returned. Helper methods are provided so that child classes may more
+	 * easily override behavior without resorting to overriding the
+	 * ItemGestureListener methods.
+	 */
+	//	@Override
+	//	public boolean onTap(MotionEvent event, MapPosition pos) {
+	//		return activateSelectedItems(event, mActiveItemSingleTap);
+	//	}
+
+	protected boolean onSingleTapUpHelper(int index, Item item) {
+		return mOnItemGestureListener.onItemSingleTapUp(index, item);
+	}
+
+	private final ActiveItem mActiveItemSingleTap = new ActiveItem() {
 		@Override
-		public synchronized void update(MapPosition pos, boolean changed, Matrices m) {
-
-			if (!changed && !mUpdate)
-				return;
-
-			mUpdate = false;
-
-			double mx = pos.x;
-			double my = pos.y;
-			double scale = Tile.SIZE * pos.scale;
-
-			int changesInvisible = 0;
-			int changedVisible = 0;
-			int numVisible = 0;
-
-			mMap.getViewport().getMapExtents(mBox, mExtents);
-
-			long flip = (long) (Tile.SIZE * pos.scale) >> 1;
-
-			synchronized (lock) {
-				if (mItems == null) {
-					if (layers.getTextureLayers() != null) {
-						layers.clear();
-						compile();
-					}
-					return;
-				}
-
-				// check visibility
-				for (InternalItem it = mItems; it != null; it = it.next) {
-					it.changes = false;
-					it.x = (float) ((it.px - mx) * scale);
-					it.y = (float) ((it.py - my) * scale);
-
-					if (it.x > flip)
-						it.x -= (flip << 1);
-					else if (it.x < -flip)
-						it.x += (flip << 1);
-
-					if (!GeometryUtils.pointInPoly(it.x, it.y, mBox, 8, 0)) {
-						if (it.visible) {
-							it.changes = true;
-							changesInvisible++;
-						}
-						continue;
-					}
-					if (!it.visible) {
-						it.visible = true;
-						changedVisible++;
-					}
-					numVisible++;
-				}
-
-				//log.debug(numVisible + " " + changedVisible + " " + changesInvisible);
-
-				// only update when zoomlevel changed, new items are visible
-				// or more than 10 of the current items became invisible
-				if ((numVisible == 0) && (changedVisible == 0 && changesInvisible < 10))
-					return;
-
-				// keep position for current state
-				// updateMapPosition();
-				mMapPosition.copy(pos);
-
-				layers.clear();
-
-				for (InternalItem it = mItems; it != null; it = it.next) {
-					if (!it.visible)
-						continue;
-
-					if (it.changes) {
-						it.visible = false;
-						continue;
-					}
-
-					MarkerSymbol marker = it.item.getMarker();
-					if (marker == null)
-						marker = mDefaultMarker;
-
-					SymbolItem s = SymbolItem.pool.get();
-					s.bitmap = marker.getBitmap();
-
-					s.x = it.x;
-					s.y = it.y;
-					s.offset = marker.getHotspot();
-					s.billboard = true;
-
-					mSymbolLayer.addSymbol(s);
-				}
+		public boolean run(int index) {
+			final ItemizedLayer<Item> that = ItemizedLayer.this;
+			if (mOnItemGestureListener == null) {
+				return false;
 			}
-			mSymbolLayer.prepare();
-			layers.setTextureLayers(mSymbolLayer);
+			return onSingleTapUpHelper(index, that.mItemList.get(index));
+		}
+	};
 
-			compile();
+	//	@Override
+	//	public boolean onLongPress(MotionEvent event, MapPosition pos) {
+	//		return activateSelectedItems(event, mActiveItemLongPress);
+	//	}
+
+	//	protected boolean onLongPressHelper(int index, Item item) {
+	//		return this.mOnItemGestureListener.onItemLongPress(index, item);
+	//	}
+	//
+	//	private final ActiveItem mActiveItemLongPress = new ActiveItem() {
+	//		@Override
+	//		public boolean run(final int index) {
+	//			final ItemizedIconLayer<Item> that = ItemizedIconLayer.this;
+	//			if (that.mOnItemGestureListener == null) {
+	//				return false;
+	//			}
+	//			return onLongPressHelper(index, getItem(index));
+	//		}
+	//	};
+
+	//	@Override
+	//	public boolean onPress(MotionEvent e, MapPosition pos) {
+	//		return false;
+	//	}
+
+	/**
+	 * When a content sensitive action is performed the content item needs to be
+	 * identified. This method does that and then performs the assigned task on
+	 * that item.
+	 * 
+	 * @return true if event is handled false otherwise
+	 */
+	protected boolean activateSelectedItems(MotionEvent event, ActiveItem task) {
+		int size = mItemList.size();
+		if (size == 0)
+			return false;
+
+		int eventX = (int) event.getX() - mMap.getWidth() / 2;
+		int eventY = (int) event.getY() - mMap.getHeight() / 2;
+		Viewport mapPosition = mMap.getViewport();
+
+		BoundingBox bbox = mapPosition.getViewBox();
+
+		int nearest = -1;
+
+		/* squared dist: 50*50 pixel */
+		double dist = 2500;
+
+		for (int i = 0; i < size; i++) {
+			Item item = mItemList.get(i);
+
+			if (!bbox.contains(item.mGeoPoint))
+				continue;
+
+			mapPosition.toScreenPoint(item.getPoint(), mTmpPoint);
+
+			float dx = (float) (mTmpPoint.x - eventX);
+			float dy = (float) (mTmpPoint.y - eventY);
+
+			double d = dx * dx + dy * dy;
+			if (d > dist)
+				continue;
+
+			dist = d;
+			nearest = i;
 		}
 
-		@Override
-		public synchronized void compile() {
-			super.compile();
-		}
+		if (nearest >= 0 && task.run(nearest))
+			return true;
+
+		return false;
 	}
 
 	/**
-	 * Method by which subclasses create the actual Items. This will only be
-	 * called from populate() we'll cache them for later use.
-	 * 
-	 * @param i
-	 *            ...
-	 * @return ...
+	 * When the item is touched one of these methods may be invoked depending on
+	 * the type of touch. Each of them returns true if the event was completely
+	 * handled.
 	 */
-	protected abstract Item createItem(int i);
+	public static interface OnItemGestureListener<T> {
+		public boolean onItemSingleTapUp(int index, T item);
 
-	/**
-	 * The number of items in this overlay.
-	 * 
-	 * @return ...
-	 */
-	public abstract int size();
-
-	public ItemizedLayer(Map map, MarkerSymbol defaultSymbol) {
-		super(map);
-
-		mDefaultMarker = defaultSymbol;
-		mRenderer = new ItemOverlay();
+		public boolean onItemLongPress(int index, T item);
 	}
 
-	private final Point mMapPoint = new Point();
-
-	/**
-	 * Utility method to perform all processing on a new ItemizedOverlay.
-	 * Subclasses provide Items through the createItem(int) method. The subclass
-	 * should call this as soon as it has data, before anything else gets
-	 * called.
-	 */
-	protected final void populate() {
-		synchronized (lock) {
-			final int size = size();
-			mSize = size;
-
-			// reuse previous items
-			InternalItem pool = mItems;
-			mItems = null;
-
-			// flip order to draw in backward cycle, so the items
-			// with the least index are on the front.
-			for (int a = 0; a < size; a++) {
-				InternalItem it;
-				if (pool != null) {
-					it = pool;
-					it.visible = false;
-					it.changes = false;
-					pool = pool.next;
-				} else {
-					it = new InternalItem();
-				}
-				it.next = mItems;
-				mItems = it;
-
-				it.item = createItem(a);
-
-				// pre-project points
-				MercatorProjection.project(it.item.getPoint(), mMapPoint);
-				it.px = mMapPoint.x;
-				it.py = mMapPoint.y;
-			}
-			mUpdate = true;
-		}
+	public static interface ActiveItem {
+		public boolean run(int aIndex);
 	}
 
-	/**
-	 * Returns the Item at the given index.
-	 * 
-	 * @param position
-	 *            the position of the item to return
-	 * @return the Item of the given index.
-	 */
-	public final Item getItem(final int position) {
-		synchronized (lock) {
-			InternalItem item = mItems;
-			for (int i = mSize - position - 1; i > 0 && item != null; i--)
-				item = item.next;
+	@Override
+	public boolean onGesture(Gesture g, MotionEvent e) {
+		if (g instanceof Gesture.Tap)
+			return activateSelectedItems(e, mActiveItemSingleTap);
 
-			if (item != null)
-				return item.item;
-
-			return null;
-		}
+		return false;
 	}
-
-	/**
-	 * TODO
-	 * If the given Item is found in the overlay, force it to be the current
-	 * focus-bearer. Any registered {link ItemizedLayer#OnFocusChangeListener}
-	 * will be notified. This does not move the map, so if the Item isn't
-	 * already centered, the user may get confused. If the Item is not found,
-	 * this is a no-op. You can also pass null to remove focus.
-	 * 
-	 * @param item
-	 */
-	public void setFocus(final Item item) {
-		mFocusedItem = item;
-	}
-
-	/**
-	 * @return the currently-focused item, or null if no item is currently
-	 *         focused.
-	 */
-	public Item getFocus() {
-		return mFocusedItem;
-	}
-
 }
diff --git a/vtm/src/org/oscim/layers/marker/MarkerItem.java b/vtm/src/org/oscim/layers/marker/MarkerItem.java
index d755d424..79fe8c4a 100644
--- a/vtm/src/org/oscim/layers/marker/MarkerItem.java
+++ b/vtm/src/org/oscim/layers/marker/MarkerItem.java
@@ -1,6 +1,10 @@
 /*
- * Copyright 2012 osmdroid authors
- * Copyright 2013 Hannes Janetzek
+ * Copyright 2012 osmdroid authors:
+ * Copyright 2012 Nicolas Gramlich
+ * Copyright 2012 Theodore Hong
+ * Copyright 2012 Fred Eisele
+ * 
+ * Copyright 2014 Hannes Janetzek
  *
  * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
  *
@@ -16,17 +20,12 @@
  * this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-// Created by plusminus on 00:02:58 - 03.10.2008
 package org.oscim.layers.marker;
 
 import org.oscim.core.GeoPoint;
 
 /**
  * Immutable class describing a GeoPoint with a Title and a Description.
- * 
- * @author Nicolas Gramlich
- * @author Theodore Hong
- * @author Fred Eisele
  */
 public class MarkerItem {
 
diff --git a/vtm/src/org/oscim/layers/marker/MarkerLayer.java b/vtm/src/org/oscim/layers/marker/MarkerLayer.java
index 2f9c9d84..f9a8894c 100644
--- a/vtm/src/org/oscim/layers/marker/MarkerLayer.java
+++ b/vtm/src/org/oscim/layers/marker/MarkerLayer.java
@@ -1,5 +1,9 @@
 /*
- * Copyright 2012 osmdroid authors
+ * Copyright 2012 osmdroid authors:
+ * Copyright 2012 Nicolas Gramlich
+ * Copyright 2012 Theodore Hong
+ * Copyright 2012 Fred Eisele
+ * 
  * Copyright 2013 Hannes Janetzek
  *
  * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
@@ -22,19 +26,73 @@ import org.oscim.core.Point;
 import org.oscim.layers.Layer;
 import org.oscim.map.Map;
 
-public abstract class MarkerLayer extends Layer {
+/**
+ * Draws a list of {@link MarkerItem} as markers to a map. The item with the
+ * lowest index is drawn as last and therefore the 'topmost' marker. It also
+ * gets checked for onTap first. This class is generic, because you then you get
+ * your custom item-class passed back in onTap(). << TODO
+ */
+public abstract class MarkerLayer<Item extends MarkerItem> extends Layer {
 
-	public MarkerLayer(Map map) {
+	protected final MarkerRenderer mMarkerRenderer;
+	protected Item mFocusedItem;
+
+	/**
+	 * Method by which subclasses create the actual Items. This will only be
+	 * called from populate() we'll cache them for later use.
+	 */
+	protected abstract Item createItem(int i);
+
+	/**
+	 * The number of items in this overlay.
+	 */
+	public abstract int size();
+
+	@SuppressWarnings("unchecked")
+	public MarkerLayer(Map map, MarkerSymbol defaultSymbol) {
 		super(map);
+
+		mMarkerRenderer = new MarkerRenderer((MarkerLayer<MarkerItem>) this, defaultSymbol);
+		mRenderer = mMarkerRenderer;
 	}
 
 	/**
-	 * TBD
+	 * Utility method to perform all processing on a new ItemizedOverlay.
+	 * Subclasses provide Items through the createItem(int) method. The subclass
+	 * should call this as soon as it has data, before anything else gets
+	 * called.
+	 */
+	protected final void populate() {
+		mMarkerRenderer.populate(size());
+	}
+
+	/**
+	 * TODO
+	 * If the given Item is found in the overlay, force it to be the current
+	 * focus-bearer. Any registered {link ItemizedLayer#OnFocusChangeListener}
+	 * will be notified. This does not move the map, so if the Item isn't
+	 * already centered, the user may get confused. If the Item is not found,
+	 * this is a no-op. You can also pass null to remove focus.
 	 * 
+	 * @param item
+	 */
+	public void setFocus(Item item) {
+		mFocusedItem = item;
+	}
+
+	/**
+	 * @return the currently-focused item, or null if no item is currently
+	 *         focused.
+	 */
+	public Item getFocus() {
+		return mFocusedItem;
+	}
+
+	/**
+	 * TODO
 	 * Interface definition for overlays that contain items that can be snapped
 	 * to (for example, when the user invokes a zoom, this could be called
 	 * allowing the user to snap the zoom to an interesting point.)
-	 * 
 	 */
 	public interface Snappable {
 
diff --git a/vtm/src/org/oscim/layers/marker/MarkerRenderer.java b/vtm/src/org/oscim/layers/marker/MarkerRenderer.java
new file mode 100644
index 00000000..b35b4c16
--- /dev/null
+++ b/vtm/src/org/oscim/layers/marker/MarkerRenderer.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2013 Hannes Janetzek
+ *
+ * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
+ *
+ * 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.marker;
+
+import org.oscim.core.MapPosition;
+import org.oscim.core.MercatorProjection;
+import org.oscim.core.Point;
+import org.oscim.core.Tile;
+import org.oscim.map.Map;
+import org.oscim.renderer.ElementRenderer;
+import org.oscim.renderer.MapRenderer.Matrices;
+import org.oscim.renderer.elements.SymbolItem;
+import org.oscim.renderer.elements.SymbolLayer;
+import org.oscim.utils.GeometryUtils;
+import org.oscim.utils.pool.Inlist;
+
+//TODO
+//- need to sort items back to front for rendering
+//- and to make this work for multiple overlays
+//a global scenegraph is probably required.
+
+public class MarkerRenderer extends ElementRenderer {
+
+	protected final MarkerSymbol mDefaultMarker;
+
+	private final SymbolLayer mSymbolLayer;
+	private final float[] mBox = new float[8];
+	private final MarkerLayer<MarkerItem> mMarkerLayer;
+	/** increase view to show items that are partially visible */
+	protected int mExtents = 100;
+	private boolean mUpdate;
+	private Map mMap;
+	private InternalItem mItems;
+	private final Point mMapPoint = new Point();
+
+	static class InternalItem extends Inlist<InternalItem> {
+		MarkerItem item;
+		boolean visible;
+		boolean changes;
+		float x, y;
+		double px, py;
+	}
+
+	public MarkerRenderer(MarkerLayer<MarkerItem> markerLayer, MarkerSymbol defaultSymbol) {
+		mSymbolLayer = new SymbolLayer();
+		mMarkerLayer = markerLayer;
+		mDefaultMarker = defaultSymbol;
+	}
+
+	@Override
+	public synchronized void update(MapPosition pos, boolean changed, Matrices m) {
+
+		if (!changed && !mUpdate)
+			return;
+
+		mUpdate = false;
+
+		double mx = pos.x;
+		double my = pos.y;
+		double scale = Tile.SIZE * pos.scale;
+
+		int changesInvisible = 0;
+		int changedVisible = 0;
+		int numVisible = 0;
+
+		mMarkerLayer.map().getViewport().getMapExtents(mBox, mExtents);
+
+		long flip = (long) (Tile.SIZE * pos.scale) >> 1;
+
+		if (mItems == null) {
+			if (layers.getTextureLayers() != null) {
+				layers.clear();
+				compile();
+			}
+			return;
+		}
+
+		/* check visibility */
+		for (InternalItem it = mItems; it != null; it = it.next) {
+			it.changes = false;
+			it.x = (float) ((it.px - mx) * scale);
+			it.y = (float) ((it.py - my) * scale);
+
+			if (it.x > flip)
+				it.x -= (flip << 1);
+			else if (it.x < -flip)
+				it.x += (flip << 1);
+
+			if (!GeometryUtils.pointInPoly(it.x, it.y, mBox, 8, 0)) {
+				if (it.visible) {
+					it.changes = true;
+					changesInvisible++;
+				}
+				continue;
+			}
+			if (!it.visible) {
+				it.visible = true;
+				changedVisible++;
+			}
+			numVisible++;
+		}
+
+		//log.debug(numVisible + " " + changedVisible + " " + changesInvisible);
+
+		/* only update when zoomlevel changed, new items are visible
+		 * or more than 10 of the current items became invisible */
+		if ((numVisible == 0) && (changedVisible == 0 && changesInvisible < 10))
+			return;
+
+		/* keep position for current state */
+		mMapPosition.copy(pos);
+
+		layers.clear();
+
+		for (InternalItem it = mItems; it != null; it = it.next) {
+			if (!it.visible)
+				continue;
+
+			if (it.changes) {
+				it.visible = false;
+				continue;
+			}
+
+			MarkerSymbol marker = it.item.getMarker();
+			if (marker == null)
+				marker = mDefaultMarker;
+
+			SymbolItem s = SymbolItem.pool.get();
+			s.bitmap = marker.getBitmap();
+
+			s.x = it.x;
+			s.y = it.y;
+			s.offset = marker.getHotspot();
+			s.billboard = true;
+
+			mSymbolLayer.addSymbol(s);
+		}
+
+		mSymbolLayer.prepare();
+		layers.setTextureLayers(mSymbolLayer);
+
+		compile();
+	}
+
+	@Override
+	public synchronized void compile() {
+		super.compile();
+	}
+
+	protected synchronized void populate(int size) {
+
+		InternalItem pool = mItems;
+		mItems = null;
+
+		for (int i = 0; i < size; i++) {
+			InternalItem it;
+			if (pool != null) {
+				it = pool;
+				it.visible = false;
+				it.changes = false;
+				pool = pool.next;
+			} else {
+				it = new InternalItem();
+			}
+			it.next = mItems;
+			mItems = it;
+
+			it.item = mMarkerLayer.createItem(i);
+
+			/* pre-project points */
+			MercatorProjection.project(it.item.getPoint(), mMapPoint);
+			it.px = mMapPoint.x;
+			it.py = mMapPoint.y;
+		}
+		mUpdate = true;
+	}
+
+	//	/**
+	//	 * Returns the Item at the given index.
+	//	 * 
+	//	 * @param position
+	//	 *            the position of the item to return
+	//	 * @return the Item of the given index.
+	//	 */
+	//	public final Item getItem(int position) {
+	//
+	//		synchronized (lock) {
+	//			InternalItem item = mItems;
+	//			for (int i = mSize - position - 1; i > 0 && item != null; i--)
+	//				item = item.next;
+	//
+	//			if (item != null)
+	//				return item.item;
+	//
+	//			return null;
+	//		}
+	//	}
+}