refactor: extract MarkerRenderer from ItemizedLayer
- move ItemizedLayer stuff to MarkerLayer - rename ItemizedIconLayer to ItemizedLayer
This commit is contained in:
parent
7f64fff46d
commit
5ef8026ac4
@ -1 +1 @@
|
||||
Subproject commit 7f3da3fb6e7e8d40280e944ad1fa3bf0504cb4b2
|
||||
Subproject commit b0e4f6b7579b22d5b01fe14ebb1b79b9c6f35b8f
|
@ -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);
|
||||
|
@ -53,4 +53,8 @@ public abstract class Layer {
|
||||
*/
|
||||
public void onDetach() {
|
||||
}
|
||||
|
||||
public Map map() {
|
||||
return mMap;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
213
vtm/src/org/oscim/layers/marker/MarkerRenderer.java
Normal file
213
vtm/src/org/oscim/layers/marker/MarkerRenderer.java
Normal file
@ -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;
|
||||
// }
|
||||
// }
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user