/* * Copyright 2012 osmdroid authors: Nicolas Gramlich, Theodore Hong, Fred Eisele * * 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.ArrayList; 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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ItemizedLayer<Item extends MarkerItem> extends MarkerLayer<Item> implements GestureListener { static final Logger log = LoggerFactory.getLogger(ItemizedLayer.class); protected final List<Item> mItemList; protected final Point mTmpPoint = new Point(); protected OnItemGestureListener<Item> mOnItemGestureListener; protected int mDrawnItemsLimit = Integer.MAX_VALUE; public ItemizedLayer(Map map, MarkerSymbol defaulMarker) { this(map, new ArrayList<Item>(), defaulMarker, null); } public ItemizedLayer(Map map, List<Item> list, MarkerSymbol defaultMarker, OnItemGestureListener<Item> listener) { super(map, defaultMarker); mItemList = list; mOnItemGestureListener = listener; populate(); } public void setOnItemGestureListener(OnItemGestureListener<Item> listener) { mOnItemGestureListener = listener; } @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 ItemizedLayer<Item> that = ItemizedLayer.this; if (mOnItemGestureListener == null) { return false; } return onSingleTapUpHelper(index, that.mItemList.get(index)); } }; 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 ItemizedLayer<Item> that = ItemizedLayer.this; if (that.mOnItemGestureListener == null) { return false; } return onLongPressHelper(index, that.mItemList.get(index)); } }; /** * 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.viewport(); BoundingBox bbox = mapPosition.getBBox(128); int nearest = -1; int inside = -1; double insideY = -Double.MAX_VALUE; /* squared dist: 50*50 pixel ~ 2mm on 400dpi */ double dist = 2500; for (int i = 0; i < size; i++) { Item item = mItemList.get(i); if (!bbox.contains(item.geoPoint)) continue; mapPosition.toScreenPoint(item.getPoint(), mTmpPoint); float dx = (float) (mTmpPoint.x - eventX); float dy = (float) (mTmpPoint.y - eventY); MarkerSymbol it = item.getMarker(); if (it == null) it = mMarkerRenderer.mDefaultMarker; if (it.isInside(dx, dy)) { if (mTmpPoint.y > insideY) { insideY = mTmpPoint.y; inside = i; } } if (inside >= 0) continue; double d = dx * dx + dy * dy; if (d > dist) continue; dist = d; nearest = i; } if (inside >= 0) nearest = inside; if (nearest >= 0 && task.run(nearest)) { mMarkerRenderer.update(); mMap.render(); return true; } return false; } /** * 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. */ 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); if (g instanceof Gesture.LongPress) return activateSelectedItems(e, mActiveItemLongPress); return false; } public Item getByUid(Object uid) { for (Item it : mItemList) if (it.getUid() == uid) return it; return null; } }