add osmdroid overlays + bonuspack

This commit is contained in:
Hannes Janetzek
2012-10-27 13:35:51 +02:00
parent 65a6f91f3c
commit ab5962d56c
114 changed files with 9562 additions and 1636 deletions

View File

@@ -0,0 +1,159 @@
package org.oscim.overlay;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
public class DefaultResourceProxyImpl implements ResourceProxy, MapViewConstants {
// private static final String TAG = DefaultResourceProxyImpl.class.getSimpleName();
private DisplayMetrics mDisplayMetrics;
/**
* Constructor.
*
* @param pContext
* Used to get the display metrics that are used for scaling the
* bitmaps returned by {@@link getBitmap}. Can be null,
* in which case the bitmaps are not scaled.
*/
public DefaultResourceProxyImpl(final Context pContext) {
if (pContext != null) {
mDisplayMetrics = pContext.getResources().getDisplayMetrics();
// if (DEBUGMODE) {
// logger.debug("mDisplayMetrics=" + mDisplayMetrics);
// }
}
}
@Override
public String getString(final string pResId) {
switch (pResId) {
case mapnik:
return "Mapnik";
case cyclemap:
return "Cycle Map";
case public_transport:
return "Public transport";
case base:
return "OSM base layer";
case topo:
return "Topographic";
case hills:
return "Hills";
case cloudmade_standard:
return "CloudMade (Standard tiles)";
case cloudmade_small:
return "CloudMade (small tiles)";
case mapquest_osm:
return "Mapquest";
case mapquest_aerial:
return "Mapquest Aerial";
case bing:
return "Bing";
case fiets_nl:
return "OpenFietsKaart overlay";
case base_nl:
return "Netherlands base overlay";
case roads_nl:
return "Netherlands roads overlay";
case unknown:
return "Unknown";
case format_distance_meters:
return "%s m";
case format_distance_kilometers:
return "%s km";
case format_distance_miles:
return "%s mi";
case format_distance_nautical_miles:
return "%s nm";
case format_distance_feet:
return "%s ft";
case online_mode:
return "Online mode";
case offline_mode:
return "Offline mode";
case my_location:
return "My location";
case compass:
return "Compass";
case map_mode:
return "Map mode";
default:
throw new IllegalArgumentException();
}
}
@Override
public String getString(final string pResId, final Object... formatArgs) {
return String.format(getString(pResId), formatArgs);
}
@Override
public Bitmap getBitmap(final bitmap pResId) {
InputStream is = null;
try {
final String resName = pResId.name() + ".png";
is = getClass().getResourceAsStream(resName);
if (is == null) {
throw new IllegalArgumentException("Resource not found: " + resName);
}
BitmapFactory.Options options = null;
if (mDisplayMetrics != null) {
options = getBitmapOptions();
}
return BitmapFactory.decodeStream(is, null, options);
} catch (final OutOfMemoryError e) {
// logger.error("OutOfMemoryError getting bitmap resource: " +
// pResId);
System.gc();
// there's not much we can do here
// - when we load a bitmap from resources we expect it to be found
throw e;
} finally {
if (is != null) {
try {
is.close();
} catch (final IOException ignore) {
}
}
}
}
private BitmapFactory.Options getBitmapOptions() {
try {
final Field density = DisplayMetrics.class.getDeclaredField("DENSITY_DEFAULT");
final Field inDensity = BitmapFactory.Options.class.getDeclaredField("inDensity");
final Field inTargetDensity = BitmapFactory.Options.class
.getDeclaredField("inTargetDensity");
final Field targetDensity = DisplayMetrics.class.getDeclaredField("densityDpi");
final BitmapFactory.Options options = new BitmapFactory.Options();
inDensity.setInt(options, density.getInt(null));
inTargetDensity.setInt(options, targetDensity.getInt(mDisplayMetrics));
return options;
} catch (final IllegalAccessException ex) {
// ignore
} catch (final NoSuchFieldException ex) {
// ignore
}
return null;
}
@Override
public Drawable getDrawable(final bitmap pResId) {
return new BitmapDrawable(getBitmap(pResId));
}
@Override
public float getDisplayMetricsDensity() {
return mDisplayMetrics.density;
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2012 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.overlay;
import org.oscim.renderer.overlays.RenderOverlay;
import org.oscim.view.MapView;
public class GenericOverlay extends Overlay {
/**
* @param mapView
* ...
* @param renderer
* ...
*/
public GenericOverlay(MapView mapView, RenderOverlay renderer) {
super();
mLayer = renderer;
}
}

View File

@@ -0,0 +1,278 @@
package org.oscim.overlay;
import java.util.List;
import org.oscim.core.MercatorProjection;
import org.oscim.overlay.ResourceProxy.bitmap;
import org.oscim.view.MapView;
import org.oscim.view.MapViewPosition;
import android.content.Context;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
public class ItemizedIconOverlay<Item extends OverlayItem> extends ItemizedOverlay<Item> {
private static final String TAG = ItemizedIconOverlay.class.getSimpleName();
protected final List<Item> mItemList;
protected OnItemGestureListener<Item> mOnItemGestureListener;
private int mDrawnItemsLimit = Integer.MAX_VALUE;
private final Point mTouchScreenPoint = new Point();
private final Point mItemPoint = new Point();
public ItemizedIconOverlay(
final MapView mapView,
final List<Item> pList,
final Drawable pDefaultMarker,
final ItemizedIconOverlay.OnItemGestureListener<Item> pOnItemGestureListener,
final ResourceProxy pResourceProxy) {
super(mapView, pDefaultMarker, pResourceProxy);
this.mItemList = pList;
this.mOnItemGestureListener = pOnItemGestureListener;
populate();
}
public ItemizedIconOverlay(
final MapView mapView,
final List<Item> pList,
final ItemizedIconOverlay.OnItemGestureListener<Item> pOnItemGestureListener,
final ResourceProxy pResourceProxy) {
this(mapView, pList, pResourceProxy.getDrawable(bitmap.marker_default),
pOnItemGestureListener,
pResourceProxy);
}
public ItemizedIconOverlay(
final MapView mapView,
final Context pContext,
final List<Item> pList,
final ItemizedIconOverlay.OnItemGestureListener<Item> pOnItemGestureListener) {
this(mapView, pList, new DefaultResourceProxyImpl(pContext)
.getDrawable(bitmap.marker_default),
pOnItemGestureListener, new DefaultResourceProxyImpl(pContext));
}
@Override
public boolean onSnapToItem(final int pX, final int pY, final Point pSnapPoint,
final MapView pMapView) {
// TODO Implement this!
return false;
}
@Override
protected Item createItem(final int index) {
return mItemList.get(index);
}
@Override
public int size() {
return Math.min(mItemList.size(), mDrawnItemsLimit);
}
public boolean addItem(final Item item) {
final boolean result = mItemList.add(item);
populate();
return result;
}
public void addItem(final int location, final Item item) {
mItemList.add(location, item);
}
public boolean addItems(final List<Item> items) {
final boolean result = mItemList.addAll(items);
populate();
return result;
}
public void removeAllItems() {
removeAllItems(true);
}
public void removeAllItems(final boolean withPopulate) {
mItemList.clear();
if (withPopulate) {
populate();
}
}
public boolean removeItem(final Item item) {
final boolean result = mItemList.remove(item);
populate();
return result;
}
public Item removeItem(final 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 onSingleTapUp(final MotionEvent event, final MapView mapView) {
return (activateSelectedItems(event, mapView, new ActiveItem() {
@Override
public boolean run(final int index) {
final ItemizedIconOverlay<Item> that = ItemizedIconOverlay.this;
if (that.mOnItemGestureListener == null) {
return false;
}
return onSingleTapUpHelper(index, that.mItemList.get(index), mapView);
}
})) || super.onSingleTapUp(event, mapView);
}
/**
* @param index
* ...
* @param item
* ...
* @param mapView
* ...
* @return ...
*/
protected boolean onSingleTapUpHelper(final int index, final Item item, final MapView mapView) {
return this.mOnItemGestureListener.onItemSingleTapUp(index, item);
}
@Override
public boolean onLongPress(final MotionEvent event, final MapView mapView) {
Log.d(TAG, "onLongPress");
return (activateSelectedItems(event, mapView, new ActiveItem() {
@Override
public boolean run(final int index) {
final ItemizedIconOverlay<Item> that = ItemizedIconOverlay.this;
if (that.mOnItemGestureListener == null) {
return false;
}
return onLongPressHelper(index, getItem(index));
}
})) || super.onLongPress(event, mapView);
}
protected boolean onLongPressHelper(final int index, final Item item) {
return this.mOnItemGestureListener.onItemLongPress(index, item);
}
/**
* 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 mapView
* ...
* @param task
* ..
* @return true if event is handled false otherwise
*/
private boolean activateSelectedItems(final MotionEvent event, final MapView mapView,
final ActiveItem task) {
// final Projection pj = mapView.getProjection();
final int eventX = (int) event.getX();
final int eventY = (int) event.getY();
// Log.d("...", "test items " + eventX + " " + eventY);
/* These objects are created to avoid construct new ones every cycle. */
// pj.fromMapPixels(eventX, eventY, mTouchScreenPoint);
MapViewPosition mapViewPosition = mMapView.getMapViewPosition();
byte z = mapViewPosition.getMapPosition().zoomLevel;
mapViewPosition.getScreenPointOnMap(eventX, eventY, mTouchScreenPoint);
int nearest = -1;
float dist = Float.MAX_VALUE;
// TODO use intermediate projection and bounding box test
for (int i = 0; i < this.mItemList.size(); ++i) {
final Item item = getItem(i);
// final Drawable marker = (item.getMarker(0) == null) ? this.mDefaultMarker : item
// .getMarker(0);
// int x = (int) MercatorProjection.longitudeToPixelX(item.getPoint().getLongitude(), z);
// int y = (int) MercatorProjection.latitudeToPixelY(item.getPoint().getLatitude(), z);
MercatorProjection.projectPoint(item.getPoint(), z, mItemPoint);
// pj.toPixels(item.getPoint(), mItemPoint);
// Log.d("...", (x - mTouchScreenPoint.x) + " " + (y - mTouchScreenPoint.y));
float dx = mItemPoint.x - mTouchScreenPoint.x;
float dy = mItemPoint.y - mTouchScreenPoint.y;
float d = FloatMath.sqrt(dx * dx + dy * dy);
if (d < 50) {
// Log.d("...", "HIT! " + (x - mTouchScreenPoint.x) + " " + (y - mTouchScreenPoint.y));
if (d < dist) {
dist = d;
nearest = i;
}
// if (hitTest(item, marker, mTouchScreenPoint.x - mItemPoint.x, mTouchScreenPoint.y
// - mItemPoint.y)) {
}
}
if (nearest >= 0 && task.run(nearest)) {
return true;
}
return false;
}
// ===========================================================
// Getter & Setter
// ===========================================================
public int getDrawnItemsLimit() {
return this.mDrawnItemsLimit;
}
public void setDrawnItemsLimit(final int aLimit) {
this.mDrawnItemsLimit = aLimit;
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
/**
* 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(final int index, final T item);
public boolean onItemLongPress(final int index, final T item);
}
public static interface ActiveItem {
public boolean run(final int aIndex);
}
}

View File

@@ -0,0 +1,484 @@
// Created by plusminus on 23:18:23 - 02.10.2008
package org.oscim.overlay;
import org.oscim.core.GeoPoint;
import org.oscim.core.MapPosition;
import org.oscim.core.MercatorProjection;
import org.oscim.overlay.OverlayItem.HotspotPlace;
import org.oscim.renderer.layer.SymbolLayer;
import org.oscim.renderer.overlays.RenderOverlay;
import org.oscim.view.MapView;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.opengl.Matrix;
/**
* Draws a list of {@link OverlayItem} 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().
* @author Marc Kurtz
* @author Nicolas Gramlich
* @author Theodore Hong
* @author Fred Eisele
* @author Hannes Janetzek
* @param <Item>
* ...
*/
public abstract class ItemizedOverlay<Item extends OverlayItem> extends Overlay implements
Overlay.Snappable {
protected final Drawable mDefaultMarker;
protected boolean mDrawFocusedItem = true;
protected MapView mMapView;
class InternalItem {
InternalItem next;
Item item;
boolean visible;
boolean changes;
int x, y, px, py;
}
/* package */InternalItem mItems;
/* package */Object lock = new Object();
// /* package */final ArrayList<Item> mInternalItemList;
private final Rect mRect = new Rect();
/* package */Item mFocusedItem;
/* package */boolean mUpdate;
private int mSize;
// pre-projected points to zoomlovel 20
private static final byte MAX_ZOOM = 20;
class ItemOverlay extends RenderOverlay {
private SymbolLayer mSymbolLayer;
private float[] mMvp = new float[16];
private float[] mVec = new float[4];
public ItemOverlay(MapView mapView) {
super(mapView);
mSymbolLayer = new SymbolLayer();
}
// note: this is called from GL-Thread. so check your syncs!
@Override
public synchronized void update(MapPosition curPos, boolean positionChanged,
boolean tilesChanged) {
if (!tilesChanged && !mUpdate)
return;
mUpdate = false;
int diff = MAX_ZOOM - curPos.zoomLevel;
int mx = (int) curPos.x;
int my = (int) curPos.y;
// TODO could pass mvp as param
mMapView.getMapViewPosition().getMVP(mMvp);
float[] matrix = mMvp;
float[] vec = mVec;
// limit could be 1 if we update on every position change
float limit = 1.5f;
// no need to project these
int max = (1 << 11);
int changesInvisible = 0;
int changedVisible = 0;
int numVisible = 0;
synchronized (lock) {
// check changes
for (InternalItem it = mItems; it != null; it = it.next) {
it.x = (it.px >> diff) - mx;
it.y = (it.py >> diff) - my;
if (it.x > max || it.x < -max || it.y > max || it.y < -max) {
if (it.visible) {
it.changes = true;
changesInvisible++;
}
continue;
}
// map points to screen
vec[0] = it.x;
vec[1] = it.y;
vec[2] = 0;
vec[3] = 1;
Matrix.multiplyMV(vec, 0, matrix, 0, vec, 0);
float sx = vec[0] / vec[3];
float sy = vec[1] / vec[3];
// check if it is visible
if (sx < -limit || sx > limit || sy < -limit || sy > limit) {
// Log.d("..", "outside " + it.x + " " + it.y + " -> " + sx + " " + sy);
if (it.visible) {
it.changes = true;
changesInvisible++;
}
} else {
if (!it.visible) {
it.visible = true;
changedVisible++;
}
it.changes = false;
numVisible++;
}
}
// only update when zoomlevel changed, new items are visible
// or more than 10 of the current items became invisible
if (((curPos.zoomLevel == mMapPosition.zoomLevel || numVisible == 0)) &&
(changedVisible == 0 && changesInvisible < 10))
return;
// keep position for current state
// updateMapPosition();
// TODO add copy utility function
mMapPosition.x = curPos.x;
mMapPosition.y = curPos.y;
mMapPosition.zoomLevel = curPos.zoomLevel;
mMapPosition.scale = curPos.scale;
mMapPosition.angle = curPos.angle;
// items are placed relative to scale == 1
mMapPosition.scale = 1;
layers.clear();
for (InternalItem it = mItems; it != null; it = it.next) {
if (!it.visible)
continue;
if (it.changes) {
it.visible = false;
continue;
}
Item item = it.item; //mInternalItemList.get(i);
int state = 0;
if (mDrawFocusedItem && (mFocusedItem == item))
state = OverlayItem.ITEM_STATE_FOCUSED_MASK;
Drawable marker = item.getDrawable();
if (marker == null)
marker = mDefaultMarker;
// if (item.getMarker(state) == null) {
// OverlayItem.setState(mDefaultMarker, state);
// marker = mDefaultMarker;
// } else
// marker = item.getMarker(state);
boundToHotspot(marker, item.getMarkerHotspot());
mSymbolLayer.addDrawable(marker, state, it.x, it.y);
}
}
// Log.d("...", "changed " + changedVisible + " " + changesInvisible);
mSymbolLayer.prepare();
layers.textureLayers = mSymbolLayer;
newData = true;
}
}
/**
* 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 ...
*/
protected abstract Item createItem(int i);
/**
* The number of items in this overlay.
* @return ...
*/
public abstract int size();
public ItemizedOverlay(MapView mapView, final Drawable pDefaultMarker, final ResourceProxy
pResourceProxy) {
super(pResourceProxy);
if (pDefaultMarker == null) {
throw new IllegalArgumentException("You must pass a default marker to ItemizedOverlay.");
}
this.mDefaultMarker = pDefaultMarker;
// mInternalItemList = new ArrayList<Item>();
mMapView = mapView;
mLayer = new ItemOverlay(mapView);
}
/**
* 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
GeoPoint p = it.item.mGeoPoint;
it.px = (int) MercatorProjection.longitudeToPixelX(p.getLongitude(), MAX_ZOOM);
it.py = (int) MercatorProjection.latitudeToPixelY(p.getLatitude(), MAX_ZOOM);
}
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(final 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;
}
// return mInternalItemList.get(position);
}
// private Drawable getDefaultMarker(final int state) {
// OverlayItem.setState(mDefaultMarker, state);
// return mDefaultMarker;
// }
/**
* See if a given hit point is within the bounds of an item's marker.
* Override to modify the way an item is hit tested. The hit point is
* relative to the marker's bounds. The default implementation just checks
* to see if the hit point is within the touchable bounds of the marker.
* @param item
* the item to hit test
* @param marker
* the item's marker
* @param hitX
* x coordinate of point to check
* @param hitY
* y coordinate of point to check
* @return true if the hit point is within the marker
*/
protected boolean hitTest(final Item item, final android.graphics.drawable.Drawable marker,
final int hitX,
final int hitY) {
return marker.getBounds().contains(hitX, hitY);
}
/**
* Set whether or not to draw the focused item. The default is to draw it,
* but some clients may prefer to draw the focused item themselves.
* @param drawFocusedItem
* ...
*/
public void setDrawFocusedItem(final boolean drawFocusedItem) {
mDrawFocusedItem = drawFocusedItem;
}
/**
* If the given Item is found in the overlay, force it to be the current
* focus-bearer. Any registered {@@link
* ItemizedOverlay#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;
}
/**
* Adjusts a drawable's bounds so that (0,0) is a pixel in the location
* described by the hotspot parameter. Useful for "pin"-like graphics. For
* convenience, returns the same drawable that was passed in.
* @param marker
* the drawable to adjust
* @param hotspot
* the hotspot for the drawable
* @return the same drawable that was passed in.
*/
protected synchronized Drawable boundToHotspot(final Drawable marker, HotspotPlace hotspot) {
final int markerWidth = marker.getIntrinsicWidth();
final int markerHeight = marker.getIntrinsicHeight();
mRect.set(0, 0, 0 + markerWidth, 0 + markerHeight);
if (hotspot == null) {
hotspot = HotspotPlace.BOTTOM_CENTER;
}
switch (hotspot) {
default:
case NONE:
break;
case CENTER:
mRect.offset(-markerWidth / 2, -markerHeight / 2);
break;
case BOTTOM_CENTER:
mRect.offset(-markerWidth / 2, -markerHeight);
break;
case TOP_CENTER:
mRect.offset(-markerWidth / 2, 0);
break;
case RIGHT_CENTER:
mRect.offset(-markerWidth, -markerHeight / 2);
break;
case LEFT_CENTER:
mRect.offset(0, -markerHeight / 2);
break;
case UPPER_RIGHT_CORNER:
mRect.offset(-markerWidth, 0);
break;
case LOWER_RIGHT_CORNER:
mRect.offset(-markerWidth, -markerHeight);
break;
case UPPER_LEFT_CORNER:
mRect.offset(0, 0);
break;
case LOWER_LEFT_CORNER:
mRect.offset(0, -markerHeight);
break;
}
marker.setBounds(mRect);
return marker;
}
// /**
// * Draw a marker on each of our items. populate() must have been called
// * first.<br/>
// * <br/>
// * The marker will be drawn twice for each Item in the Overlay--once in the
// * shadow phase, skewed and darkened, then again in the non-shadow phase.
// * The bottom-center of the marker will be aligned with the geographical
// * coordinates of the Item.<br/>
// * <br/>
// * The order of drawing may be changed by overriding the getIndexToDraw(int)
// * method. An item may provide an alternate marker via its
// * OverlayItem.getMarker(int) method. If that method returns null, the
// * default marker is used.<br/>
// * <br/>
// * The focused item is always drawn last, which puts it visually on top of
// * the other items.<br/>
// *
// * @param canvas
// * the Canvas upon which to draw. Note that this may already have
// * a transformation applied, so be sure to leave it the way you
// * found it
// * @param mapView
// * the MapView that requested the draw. Use
// * MapView.getProjection() to convert between on-screen pixels
// * and latitude/longitude pairs
// * @param shadow
// * if true, draw the shadow layer. If false, draw the overlay
// * contents.
// */
// @Override
// public void draw(final Canvas canvas, final MapView mapView, final boolean shadow) {
//
// if (shadow) {
// return;
// }
//
// final Projection pj = mapView.getProjection();
// final int size = this.mInternalItemList.size() - 1;
//
// /*
// * Draw in backward cycle, so the items with the least index are on the
// * front.
// */
// for (int i = size; i >= 0; i--) {
// final Item item = getItem(i);
// pj.toMapPixels(item.mGeoPoint, mCurScreenCoords);
//
// onDrawItem(canvas, item, mCurScreenCoords);
// }
// }
// /**
// * Draws an item located at the provided screen coordinates to the canvas.
// *
// * @param canvas
// * what the item is drawn upon
// * @param item
// * the item to be drawn
// * @param curScreenCoords
// * the screen coordinates of the item
// */
// protected void onDrawItem(final Canvas canvas, final Item item, final Point curScreenCoords) {
// int state = 0;
//
// if (mDrawFocusedItem && (mFocusedItem == item))
// state = OverlayItem.ITEM_STATE_FOCUSED_MASK;
//
// Drawable marker;
//
// if (item.getMarker(state) == null)
// marker = getDefaultMarker(state);
// else
// marker = item.getMarker(state);
//
// boundToHotspot(marker, item.getMarkerHotspot());
//
// // draw it
// Overlay.drawAt(canvas, marker, curScreenCoords.x, curScreenCoords.y, false);
// }
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2012 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.overlay;
import org.oscim.renderer.overlays.OverlayText;
import org.oscim.view.MapView;
public class LabelingOverlay extends Overlay {
// private OverlayText mLayer;
// @Override
// public org.oscim.renderer.overlays.RenderOverlay getLayer() {
// return mLayer;
// }
public LabelingOverlay(MapView mapView) {
super();
mLayer = new OverlayText(mapView);
}
}

View File

@@ -0,0 +1,36 @@
// Created by plusminus on 18:00:24 - 25.09.2008
package org.oscim.overlay;
/**
*
* This class contains constants used by the map view.
*
* @author Nicolas Gramlich
*
*/
public interface MapViewConstants {
// ===========================================================
// Final Fields
// ===========================================================
public static final boolean DEBUGMODE = false;
public static final int NOT_SET = Integer.MIN_VALUE;
public static final int ANIMATION_SMOOTHNESS_LOW = 4;
public static final int ANIMATION_SMOOTHNESS_DEFAULT = 10;
public static final int ANIMATION_SMOOTHNESS_HIGH = 20;
public static final int ANIMATION_DURATION_SHORT = 500;
public static final int ANIMATION_DURATION_DEFAULT = 1000;
public static final int ANIMATION_DURATION_LONG = 2000;
/** Minimum Zoom Level */
public static final int MINIMUM_ZOOMLEVEL = 0;
/**
* Maximum Zoom Level - we use Integers to store zoom levels so overflow happens at 2^32 - 1,
* but we also have a tile size that is typically 2^8, so (32-1)-8-1 = 22
*/
public static final int MAXIMUM_ZOOMLEVEL = 22;
}

View File

@@ -0,0 +1,450 @@
// Created by plusminus on 20:32:01 - 27.09.2008
package org.oscim.overlay;
import java.util.concurrent.atomic.AtomicInteger;
import org.oscim.core.MapPosition;
import org.oscim.renderer.overlays.RenderOverlay;
import org.oscim.view.MapView;
import android.content.Context;
import android.graphics.Point;
import android.view.KeyEvent;
import android.view.MotionEvent;
/**
* Base class representing an overlay which may be displayed on top of a
* {@link MapView}. To add an overlay, subclass this class, create an instance,
* and add it to the list obtained from getOverlays() of {@link MapView}. This
* class implements a form of Gesture Handling similar to
* {@link android.view.GestureDetector.SimpleOnGestureListener} and
* GestureDetector.OnGestureListener. The difference is there is an additional
* argument for the item.
*
* @author Nicolas Gramlich
*/
public abstract class Overlay implements OverlayConstants {
// ===========================================================
// Constants
// ===========================================================
private static AtomicInteger sOrdinal = new AtomicInteger();
// From Google Maps API
protected static final float SHADOW_X_SKEW = -0.8999999761581421f;
protected static final float SHADOW_Y_SCALE = 0.5f;
// ===========================================================
// Fields
// ===========================================================
protected final ResourceProxy mResourceProxy;
protected final float mScale;
// private static final Rect mRect = new Rect();
private boolean mEnabled = true;
protected RenderOverlay mLayer;
public RenderOverlay getLayer() {
return mLayer;
}
// ===========================================================
// Constructors
// ===========================================================
public Overlay() {
mResourceProxy = null;
mScale = 1;
// mResourceProxy = new DefaultResourceProxyImpl(ctx);
// mScale = ctx.getResources().getDisplayMetrics().density;
}
public Overlay(final Context ctx) {
mResourceProxy = new DefaultResourceProxyImpl(ctx);
mScale = ctx.getResources().getDisplayMetrics().density;
}
public Overlay(final ResourceProxy pResourceProxy) {
mResourceProxy = pResourceProxy;
mScale = mResourceProxy.getDisplayMetricsDensity();
}
// ===========================================================
// Getter & Setter
// ===========================================================
/**
* Sets whether the Overlay is marked to be enabled. This setting does
* nothing by default, but should be checked before calling draw().
*
* @param pEnabled
* ...
*/
public void setEnabled(final boolean pEnabled) {
this.mEnabled = pEnabled;
}
/**
* Specifies if the Overlay is marked to be enabled. This should be checked
* before calling draw().
*
* @return true if the Overlay is marked enabled, false otherwise
*/
public boolean isEnabled() {
return this.mEnabled;
}
/**
* Since the menu-chain will pass through several independent Overlays, menu
* IDs cannot be fixed at compile time. Overlays should use this method to
* obtain and store a menu id for each menu item at construction time. This
* will ensure that two overlays don't use the same id.
*
* @return an integer suitable to be used as a menu identifier
*/
protected final static int getSafeMenuId() {
return sOrdinal.getAndIncrement();
}
/**
* Similar to <see cref="getSafeMenuId" />, except this reserves a sequence
* of IDs of length <param name="count" />. The returned number is the
* starting index of that sequential list.
*
* @param count
* ....
* @return an integer suitable to be used as a menu identifier
*/
protected final static int getSafeMenuIdSequence(final int count) {
return sOrdinal.getAndAdd(count);
}
// ===========================================================
// Methods for SuperClass/Interfaces
// ===========================================================
// /**
// * Draw the overlay over the map. This will be called on all active overlays
// * with shadow=true, to lay down the shadow layer, and then again on all
// * overlays with shadow=false. Callers should check isEnabled() before
// * calling draw(). By default, draws nothing.
// *
// * @param c
// * ...
// * @param osmv
// * ...
// * @param shadow
// * ...
// */
// protected abstract void draw(final Canvas c, final MapView osmv, final boolean shadow);
// ===========================================================
// Methods
// ===========================================================
/**
* Override to perform clean up of resources before shutdown. By default
* does nothing.
*
* @param mapView
* ...
*/
public void onDetach(final MapView mapView) {
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param keyCode
* ...
* @param event
* ...
* @param mapView
* ...
* @return ...
*/
public boolean onKeyDown(final int keyCode, final KeyEvent event, final MapView mapView) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param keyCode
* ...
* @param event
* ...
* @param mapView
* ...
* @return ...
*/
public boolean onKeyUp(final int keyCode, final KeyEvent event, final MapView mapView) {
return false;
}
/**
* <b>You can prevent all(!) other Touch-related events from happening!</b><br />
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @param mapView
* ...
* @return ...
*/
public boolean onTouchEvent(final MotionEvent e, final MapView mapView) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @param mapView
* ...
* @return ...
*/
public boolean onTrackballEvent(final MotionEvent e, final MapView mapView) {
return false;
}
/** GestureDetector.OnDoubleTapListener **/
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @param mapView
* ...
* @return ...
*/
public boolean onDoubleTap(final MotionEvent e, final MapView mapView) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @param mapView
* ...
* @return ...
*/
public boolean onDoubleTapEvent(final MotionEvent e, final MapView mapView) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @param mapView
* ...
* @return ...
*/
public boolean onSingleTapConfirmed(final MotionEvent e, final MapView mapView) {
return false;
}
/** OnGestureListener **/
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @param mapView
* ...
* @return ...
*/
public boolean onDown(final MotionEvent e, final MapView mapView) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param pEvent1
* ...
* @param pEvent2
* ...
* @param pVelocityX
* ...
* @param pVelocityY
* ...
* @param pMapView
* ...
* @return ...
*/
public boolean onFling(final MotionEvent pEvent1, final MotionEvent pEvent2,
final float pVelocityX, final float pVelocityY, final MapView pMapView) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @param mapView
* ...
* @return ...
*/
public boolean onLongPress(final MotionEvent e, final MapView mapView) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param pEvent1
* ...
* @param pEvent2
* ...
* @param pDistanceX
* ...
* @param pDistanceY
* ...
* @param pMapView
* ...
* @return ...
*/
public boolean onScroll(final MotionEvent pEvent1, final MotionEvent pEvent2,
final float pDistanceX, final float pDistanceY, final MapView pMapView) {
return false;
}
/**
* @param pEvent
* ...
* @param pMapView
* ...
*/
public void onShowPress(final MotionEvent pEvent, final MapView pMapView) {
return;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @param mapView
* ...
* @return ...
*/
public boolean onSingleTapUp(final MotionEvent e, final MapView mapView) {
return false;
}
/**
* @param mapPosition
* current MapPosition
*/
public void onUpdate(MapPosition mapPosition) {
}
// /**
// * Convenience method to draw a Drawable at an offset. x and y are pixel
// * coordinates. You can find appropriate coordinates from latitude/longitude
// * using the MapView.getProjection() method on the MapView passed to you in
// * draw(Canvas, MapView, boolean).
// *
// * @param canvas
// * ...
// * @param drawable
// * ...
// * @param x
// * ...
// * @param y
// * ...
// * @param shadow
// * If true, draw only the drawable's shadow. Otherwise, draw the
// * drawable itself.
// */
// protected synchronized static void drawAt(final android.graphics.Canvas canvas,
// final android.graphics.drawable.Drawable drawable, final int x, final int y,
// final boolean shadow) {
// drawable.copyBounds(mRect);
// drawable.setBounds(mRect.left + x, mRect.top + y, mRect.right + x, mRect.bottom + y);
// drawable.draw(canvas);
// drawable.setBounds(mRect);
// }
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
/**
* 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 {
/**
* Checks to see if the given x and y are close enough to an item
* resulting in snapping the current action (e.g. zoom) to the item.
*
* @param x
* The x in screen coordinates.
* @param y
* The y in screen coordinates.
* @param snapPoint
* To be filled with the the interesting point (in screen
* coordinates) that is closest to the given x and y. Can be
* untouched if not snapping.
* @param mapView
* The {@link MapView} that is requesting the snap. Use
* MapView.getProjection() to convert between on-screen
* pixels and latitude/longitude pairs.
* @return Whether or not to snap to the interesting point.
*/
boolean onSnapToItem(int x, int y, Point snapPoint, MapView mapView);
}
}

View File

@@ -0,0 +1,16 @@
package org.oscim.overlay;
/**
* This class contains constants used by the overlays.
*/
public interface OverlayConstants {
// ===========================================================
// Final Fields
// ===========================================================
public static final boolean DEBUGMODE = false;
public static final int NOT_SET = Integer.MIN_VALUE;
public static final int DEFAULT_ZOOMLEVEL_MINIMAP_DIFFERENCE = 3;
}

View File

@@ -0,0 +1,157 @@
// Created by plusminus on 00:02:58 - 03.10.2008
package org.oscim.overlay;
import org.oscim.core.GeoPoint;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
/** Immutable class describing a GeoPoint with a Title and a Description.
* @author Nicolas Gramlich
* @author Theodore Hong
* @author Fred Eisele */
public class OverlayItem {
// ===========================================================
// Constants
// ===========================================================
public static final int ITEM_STATE_FOCUSED_MASK = 4;
public static final int ITEM_STATE_PRESSED_MASK = 1;
public static final int ITEM_STATE_SELECTED_MASK = 2;
protected static final Point DEFAULT_MARKER_SIZE = new Point(26, 94);
/** Indicates a hotspot for an area. This is where the origin (0,0) of a
* point will be located relative to the area. In otherwords this acts as an
* offset. NONE indicates that no adjustment should be made. */
public enum HotspotPlace {
NONE, CENTER, BOTTOM_CENTER, TOP_CENTER, RIGHT_CENTER, LEFT_CENTER, UPPER_RIGHT_CORNER, LOWER_RIGHT_CORNER, UPPER_LEFT_CORNER, LOWER_LEFT_CORNER
}
// ===========================================================
// Fields
// ===========================================================
public final String mUid;
public final String mTitle;
public final String mDescription;
public final GeoPoint mGeoPoint;
protected Drawable mMarker;
protected HotspotPlace mHotspotPlace;
// ===========================================================
// Constructors
// ===========================================================
/** @param aTitle
* this should be <b>singleLine</b> (no <code>'\n'</code> )
* @param aDescription
* a <b>multiLine</b> description ( <code>'\n'</code> possible)
* @param aGeoPoint
* ... */
public OverlayItem(final String aTitle, final String aDescription, final GeoPoint aGeoPoint) {
this(null, aTitle, aDescription, aGeoPoint);
}
public OverlayItem(final String aUid, final String aTitle, final String aDescription,
final GeoPoint aGeoPoint) {
this.mTitle = aTitle;
this.mDescription = aDescription;
this.mGeoPoint = aGeoPoint;
this.mUid = aUid;
}
// ===========================================================
// Getter & Setter
// ===========================================================
public String getUid() {
return mUid;
}
public String getTitle() {
return mTitle;
}
public String getSnippet() {
return mDescription;
}
public GeoPoint getPoint() {
return mGeoPoint;
}
/* (copied from Google API docs) Returns the marker that should be used when
* drawing this item on the map. A null value means that the default marker
* should be drawn. Different markers can be returned for different states.
* The different markers can have different bounds. The default behavior is
* to call {@link setState(android.graphics.drawable.Drawable, int)} on the
* overlay item's marker, if it exists, and then return it.
* @param stateBitset The current state.
* @return The marker for the current state, or null if the default marker
* for the overlay should be used. */
public Drawable getMarker(final int stateBitset) {
// marker not specified
if (mMarker == null) {
return null;
}
// set marker state appropriately
setState(mMarker, stateBitset);
return mMarker;
}
public void setMarker(final Drawable marker) {
this.mMarker = marker;
}
public void setMarkerHotspot(final HotspotPlace place) {
this.mHotspotPlace = (place == null) ? HotspotPlace.BOTTOM_CENTER : place;
}
public HotspotPlace getMarkerHotspot() {
return this.mHotspotPlace;
}
// ===========================================================
// Methods from SuperClass/Interfaces
// ===========================================================
// ===========================================================
// Methods
// ===========================================================
/* (copied from the Google API docs) Sets the state of a drawable to match a
* given state bitset. This is done by converting the state bitset bits
* into
* a state set of R.attr.state_pressed, R.attr.state_selected and
* R.attr.state_focused attributes, and then calling {@link
* Drawable.setState(int[])}. */
public static void setState(final Drawable drawable, final int stateBitset) {
final int[] states = new int[3];
int index = 0;
if ((stateBitset & ITEM_STATE_PRESSED_MASK) > 0)
states[index++] = android.R.attr.state_pressed;
if ((stateBitset & ITEM_STATE_SELECTED_MASK) > 0)
states[index++] = android.R.attr.state_selected;
if ((stateBitset & ITEM_STATE_FOCUSED_MASK) > 0)
states[index++] = android.R.attr.state_focused;
drawable.setState(states);
}
public Drawable getDrawable() {
return this.mMarker;
}
public int getWidth() {
return this.mMarker.getIntrinsicWidth();
}
public int getHeight() {
return this.mMarker.getIntrinsicHeight();
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}

View File

@@ -0,0 +1,390 @@
package org.oscim.overlay;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.CopyOnWriteArrayList;
import org.oscim.core.MapPosition;
import org.oscim.overlay.Overlay.Snappable;
import org.oscim.renderer.overlays.RenderOverlay;
import org.oscim.view.MapView;
import android.graphics.Point;
import android.view.KeyEvent;
import android.view.MotionEvent;
public class OverlayManager extends AbstractList<Overlay> {
// private TilesOverlay mTilesOverlay;
/* package */final CopyOnWriteArrayList<Overlay> mOverlayList;
public OverlayManager() {
// final TilesOverlay tilesOverlay) {
// setTilesOverlay(tilesOverlay);
mOverlayList = new CopyOnWriteArrayList<Overlay>();
}
@Override
public Overlay get(final int pIndex) {
return mOverlayList.get(pIndex);
}
@Override
public int size() {
return mOverlayList.size();
}
@Override
public void add(final int pIndex, final Overlay pElement) {
mOverlayList.add(pIndex, pElement);
mUpdateLayers = true;
}
@Override
public Overlay remove(final int pIndex) {
mUpdateLayers = true;
return mOverlayList.remove(pIndex);
}
@Override
public Overlay set(final int pIndex, final Overlay pElement) {
mUpdateLayers = true;
return mOverlayList.set(pIndex, pElement);
}
// /**
// * Gets the optional TilesOverlay class.
// *
// * @return the tilesOverlay
// */
// public TilesOverlay getTilesOverlay() {
// return mTilesOverlay;
// }
//
// /**
// * Sets the optional TilesOverlay class. If set, this overlay will be
// drawn before all other
// * overlays and will not be included in the editable list of overlays and
// can't be cleared
// * except by a subsequent call to setTilesOverlay().
// *
// * @param tilesOverlay
// * the tilesOverlay to set
// */
// public void setTilesOverlay(final TilesOverlay tilesOverlay) {
// mTilesOverlay = tilesOverlay;
// }
public Iterable<Overlay> overlaysReversed() {
return new Iterable<Overlay>() {
@Override
public Iterator<Overlay> iterator() {
final ListIterator<Overlay> i = mOverlayList.listIterator(mOverlayList.size());
return new Iterator<Overlay>() {
@Override
public boolean hasNext() {
return i.hasPrevious();
}
@Override
public Overlay next() {
return i.previous();
}
@Override
public void remove() {
i.remove();
}
};
}
};
}
private boolean mUpdateLayers;
private List<RenderOverlay> mDrawLayers = new ArrayList<RenderOverlay>();
public List<RenderOverlay> getRenderLayers() {
if (mUpdateLayers) {
synchronized (this) {
mUpdateLayers = false;
mDrawLayers.clear();
for (Overlay o : mOverlayList) {
RenderOverlay l = o.getLayer();
if (l != null)
mDrawLayers.add(l);
}
}
}
return mDrawLayers;
}
// public void onDraw(final Canvas c, final MapView pMapView) {
// // if ((mTilesOverlay != null) && mTilesOverlay.isEnabled()) {
// // mTilesOverlay.draw(c, pMapView, true);
// // }
// //
// // if ((mTilesOverlay != null) && mTilesOverlay.isEnabled()) {
// // mTilesOverlay.draw(c, pMapView, false);
// // }
//
// for (final Overlay overlay : mOverlayList) {
// if (overlay.isEnabled()) {
// overlay.draw(c, pMapView, true);
// }
// }
//
// for (final Overlay overlay : mOverlayList) {
// if (overlay.isEnabled()) {
// overlay.draw(c, pMapView, false);
// }
// }
//
// }
public void onDetach(final MapView pMapView) {
// if (mTilesOverlay != null) {
// mTilesOverlay.onDetach(pMapView);
// }
for (final Overlay overlay : this.overlaysReversed()) {
overlay.onDetach(pMapView);
}
}
public boolean onKeyDown(final int keyCode, final KeyEvent event, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay.onKeyDown(keyCode, event, pMapView)) {
return true;
}
}
return false;
}
public boolean onKeyUp(final int keyCode, final KeyEvent event, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay.onKeyUp(keyCode, event, pMapView)) {
return true;
}
}
return false;
}
public boolean onTouchEvent(final MotionEvent event, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay.onTouchEvent(event, pMapView)) {
return true;
}
}
return false;
}
public boolean onTrackballEvent(final MotionEvent event, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay.onTrackballEvent(event, pMapView)) {
return true;
}
}
return false;
}
public boolean onSnapToItem(final int x, final int y, final Point snapPoint,
final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay instanceof Snappable) {
if (((Snappable) overlay).onSnapToItem(x, y, snapPoint, pMapView)) {
return true;
}
}
}
return false;
}
/* GestureDetector.OnDoubleTapListener */
public boolean onDoubleTap(final MotionEvent e, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay.onDoubleTap(e, pMapView)) {
return true;
}
}
return false;
}
public boolean onDoubleTapEvent(final MotionEvent e, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay.onDoubleTapEvent(e, pMapView)) {
return true;
}
}
return false;
}
public boolean onSingleTapConfirmed(final MotionEvent e, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay.onSingleTapConfirmed(e, pMapView)) {
return true;
}
}
return false;
}
/* OnGestureListener */
public boolean onDown(final MotionEvent pEvent, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay.onDown(pEvent, pMapView)) {
return true;
}
}
return false;
}
public boolean onFling(final MotionEvent pEvent1, final MotionEvent pEvent2,
final float pVelocityX, final float pVelocityY, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay.onFling(pEvent1, pEvent2, pVelocityX, pVelocityY, pMapView)) {
return true;
}
}
return false;
}
public boolean onLongPress(final MotionEvent pEvent, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay.onLongPress(pEvent, pMapView)) {
return true;
}
}
return false;
}
public boolean onScroll(final MotionEvent pEvent1, final MotionEvent pEvent2,
final float pDistanceX, final float pDistanceY, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay.onScroll(pEvent1, pEvent2, pDistanceX, pDistanceY, pMapView)) {
return true;
}
}
return false;
}
public void onShowPress(final MotionEvent pEvent, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
overlay.onShowPress(pEvent, pMapView);
}
}
public boolean onSingleTapUp(final MotionEvent pEvent, final MapView pMapView) {
for (final Overlay overlay : this.overlaysReversed()) {
if (overlay.onSingleTapUp(pEvent, pMapView)) {
return true;
}
}
return false;
}
public void onUpdate(MapPosition mapPosition) {
for (final Overlay overlay : this.overlaysReversed()) {
overlay.onUpdate(mapPosition);
}
}
// ** Options Menu **//
// public void setOptionsMenusEnabled(final boolean pEnabled) {
// for (final Overlay overlay : mOverlayList) {
// if ((overlay instanceof IOverlayMenuProvider)
// && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()) {
// ((IOverlayMenuProvider) overlay).setOptionsMenuEnabled(pEnabled);
// }
// }
// }
//
// public boolean onCreateOptionsMenu(final Menu pMenu, final int
// menuIdOffset,
// final MapView mapView) {
// boolean result = true;
// for (final Overlay overlay : this.overlaysReversed()) {
// if ((overlay instanceof IOverlayMenuProvider)
// && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()) {
// result &= ((IOverlayMenuProvider) overlay).onCreateOptionsMenu(pMenu,
// menuIdOffset,
// mapView);
// }
// }
//
// if ((mTilesOverlay != null) && (mTilesOverlay instanceof
// IOverlayMenuProvider)
// && ((IOverlayMenuProvider) mTilesOverlay).isOptionsMenuEnabled()) {
// result &= mTilesOverlay.onCreateOptionsMenu(pMenu, menuIdOffset,
// mapView);
// }
//
// return result;
// }
//
// public boolean onPrepareOptionsMenu(final Menu pMenu, final int
// menuIdOffset,
// final MapView mapView) {
// for (final Overlay overlay : this.overlaysReversed()) {
// if ((overlay instanceof IOverlayMenuProvider)
// && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()) {
// ((IOverlayMenuProvider) overlay).onPrepareOptionsMenu(pMenu,
// menuIdOffset, mapView);
// }
// }
//
// if ((mTilesOverlay != null) && (mTilesOverlay instanceof
// IOverlayMenuProvider)
// && ((IOverlayMenuProvider) mTilesOverlay).isOptionsMenuEnabled()) {
// mTilesOverlay.onPrepareOptionsMenu(pMenu, menuIdOffset, mapView);
// }
//
// return true;
// }
//
// public boolean onOptionsItemSelected(final MenuItem item, final int
// menuIdOffset,
// final MapView mapView) {
// for (final Overlay overlay : this.overlaysReversed()) {
// if ((overlay instanceof IOverlayMenuProvider)
// && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()
// && ((IOverlayMenuProvider) overlay).onOptionsItemSelected(item,
// menuIdOffset,
// mapView)) {
// return true;
// }
// }
//
// if ((mTilesOverlay != null)
// && (mTilesOverlay instanceof IOverlayMenuProvider)
// && ((IOverlayMenuProvider) mTilesOverlay).isOptionsMenuEnabled()
// && ((IOverlayMenuProvider) mTilesOverlay).onOptionsItemSelected(item,
// menuIdOffset,
// mapView)) {
// return true;
// }
//
// return false;
// }
}

View File

@@ -0,0 +1,280 @@
/*
* Copyright 2012, osmdroid: Viesturs Zarins, Martin Pearman
* Copyright 2012, 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.overlay;
import java.util.ArrayList;
import org.oscim.core.GeoPoint;
import org.oscim.core.MapPosition;
import org.oscim.core.MercatorProjection;
import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.LineLayer;
import org.oscim.renderer.overlays.RenderOverlay;
import org.oscim.theme.renderinstruction.Line;
import org.oscim.view.MapView;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
/** This class draws a path line in given color. */
public class PathOverlay extends Overlay {
/** Stores points, converted to the map projection. */
/* package */final ArrayList<GeoPoint> mPoints;
/* package */boolean mUpdatePoints;
/** Paint settings. */
protected Paint mPaint = new Paint();
class RenderPath extends RenderOverlay {
private static final byte MAX_ZOOM = 20;
// pre-projected points to zoomlovel 20
private int[] mPreprojected;
// projected points
private float[] mPPoints;
private short[] mIndex;
private int mSize;
private Line mLine;
// limit coords
private final int max = 2048;
public RenderPath(MapView mapView) {
super(mapView);
mLine = new Line(Color.BLUE, 3.0f, Cap.BUTT);
mIndex = new short[1];
mPPoints = new float[1];
}
// note: this is called from GL-Thread. so check your syncs!
// TODO use an Overlay-Thread to build up layers (like for Labeling)
@Override
public synchronized void update(MapPosition curPos, boolean positionChanged,
boolean tilesChanged) {
if (!tilesChanged && !mUpdatePoints)
return;
float[] projected = mPPoints;
if (mUpdatePoints) {
// pre-project point on zoomlelvel 20
synchronized (mPoints) {
mUpdatePoints = false;
ArrayList<GeoPoint> geopoints = mPoints;
int size = geopoints.size();
int[] points = mPreprojected;
mSize = size * 2;
if (mSize > projected.length) {
points = mPreprojected = new int[mSize];
projected = mPPoints = new float[mSize];
}
for (int i = 0, j = 0; i < size; i++, j += 2) {
GeoPoint p = geopoints.get(i);
points[j + 0] = (int) MercatorProjection.longitudeToPixelX(
p.getLongitude(), MAX_ZOOM);
points[j + 1] = (int) MercatorProjection.latitudeToPixelY(
p.getLatitude(), MAX_ZOOM);
}
}
}
int size = mSize;
// keep position to render relative to current state
updateMapPosition();
// items are placed relative to scale == 1
mMapPosition.scale = 1;
// layers.clear();
LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE);
// reset verticesCnt to reuse layer
ll.verticesCnt = 0;
ll.line = mLine;
ll.width = 2.5f;
int x, y, px = 0, py = 0;
int i = 0;
int diff = MAX_ZOOM - mMapPosition.zoomLevel;
int mx = (int) mMapPosition.x;
int my = (int) mMapPosition.y;
for (int j = 0; j < size; j += 2) {
// TODO translate mapPosition and do this after clipping
x = (mPreprojected[j + 0] >> diff) - mx;
y = (mPreprojected[j + 1] >> diff) - my;
// TODO use line clipping, this doesnt work with 'GreatCircle'
// TODO clip to view bounding box
if (x > max || x < -max || y > max || y < -max) {
if (i > 2) {
mIndex[0] = (short) i;
ll.addLine(projected, mIndex, false);
}
i = 0;
continue;
}
// skip too near points
int dx = x - px;
int dy = y - py;
if ((i == 0) || dx > 2 || dx < -2 || dy > 2 || dy < -2) {
projected[i + 0] = px = x;
projected[i + 1] = py = y;
i += 2;
}
}
mIndex[0] = (short) i;
ll.addLine(projected, mIndex, false);
newData = true;
}
}
public PathOverlay(MapView mapView, final int color, final Context ctx) {
super(ctx);
this.mPaint.setColor(color);
this.mPaint.setStrokeWidth(2.0f);
this.mPaint.setStyle(Paint.Style.STROKE);
this.mPoints = new ArrayList<GeoPoint>();
mLayer = new RenderPath(mapView);
}
public void setColor(final int color) {
this.mPaint.setColor(color);
}
public void setAlpha(final int a) {
this.mPaint.setAlpha(a);
}
public Paint getPaint() {
return mPaint;
}
public void setPaint(final Paint pPaint) {
if (pPaint == null) {
throw new IllegalArgumentException("pPaint argument cannot be null");
}
mPaint = pPaint;
}
/** Draw a great circle. Calculate a point for every 100km along the path.
* @param startPoint
* start point of the great circle
* @param endPoint
* end point of the great circle */
public void addGreatCircle(final GeoPoint startPoint, final GeoPoint endPoint) {
synchronized (mPoints) {
// get the great circle path length in meters
final int greatCircleLength = startPoint.distanceTo(endPoint);
// add one point for every 100kms of the great circle path
final int numberOfPoints = greatCircleLength / 100000;
addGreatCircle(startPoint, endPoint, numberOfPoints);
}
}
/** Draw a great circle.
* @param startPoint
* start point of the great circle
* @param endPoint
* end point of the great circle
* @param numberOfPoints
* number of points to calculate along the path */
public void addGreatCircle(final GeoPoint startPoint, final GeoPoint endPoint,
final int numberOfPoints) {
// adapted from page
// http://compastic.blogspot.co.uk/2011/07/how-to-draw-great-circle-on-map-in.html
// which was adapted from page http://maps.forum.nu/gm_flight_path.html
// convert to radians
final double lat1 = startPoint.getLatitude() * Math.PI / 180;
final double lon1 = startPoint.getLongitude() * Math.PI / 180;
final double lat2 = endPoint.getLatitude() * Math.PI / 180;
final double lon2 = endPoint.getLongitude() * Math.PI / 180;
final double d = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2)
+ Math.cos(lat1) * Math.cos(lat2)
* Math.pow(Math.sin((lon1 - lon2) / 2), 2)));
double bearing = Math.atan2(
Math.sin(lon1 - lon2) * Math.cos(lat2),
Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2)
* Math.cos(lon1 - lon2))
/ -(Math.PI / 180);
bearing = bearing < 0 ? 360 + bearing : bearing;
for (int i = 0, j = numberOfPoints + 1; i < j; i++) {
final double f = 1.0 / numberOfPoints * i;
final double A = Math.sin((1 - f) * d) / Math.sin(d);
final double B = Math.sin(f * d) / Math.sin(d);
final double x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2)
* Math.cos(lon2);
final double y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2)
* Math.sin(lon2);
final double z = A * Math.sin(lat1) + B * Math.sin(lat2);
final double latN = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
final double lonN = Math.atan2(y, x);
addPoint((int) (latN / (Math.PI / 180) * 1E6), (int) (lonN / (Math.PI / 180) * 1E6));
}
}
public void clearPath() {
synchronized (mPoints) {
mPoints.clear();
mUpdatePoints = true;
}
}
public void addPoint(final GeoPoint pt) {
synchronized (mPoints) {
this.mPoints.add(pt);
mUpdatePoints = true;
}
}
public void addPoint(final int latitudeE6, final int longitudeE6) {
synchronized (mPoints) {
this.mPoints.add(new GeoPoint(latitudeE6, longitudeE6));
mUpdatePoints = true;
}
}
public int getNumberOfPoints() {
return this.mPoints.size();
}
}

View File

@@ -0,0 +1,67 @@
package org.oscim.overlay;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
public interface ResourceProxy {
public static enum string {
// tile sources
mapnik, cyclemap, public_transport, base, topo, hills, cloudmade_small, cloudmade_standard, mapquest_osm, mapquest_aerial, bing,
// overlays
fiets_nl, base_nl, roads_nl,
// other stuff
unknown, format_distance_meters, format_distance_kilometers, format_distance_miles, format_distance_nautical_miles, format_distance_feet, online_mode, offline_mode, my_location, compass, map_mode,
}
public static enum bitmap {
/**
* For testing - the image doesn't exist.
*/
unknown,
center, direction_arrow, marker_default, marker_default_focused_base, navto_small, next, previous, person,
/**
* Menu icons
*/
ic_menu_offline, ic_menu_mylocation, ic_menu_compass, ic_menu_mapmode
}
String getString(string pResId);
/**
* Use a string resource as a format definition, and format using the
* supplied format arguments.
*
* @param pResId
* ...
* @param formatArgs
* ...
* @return ...
*/
String getString(string pResId, Object... formatArgs);
Bitmap getBitmap(bitmap pResId);
/**
* Get a bitmap as a {@link Drawable}
*
* @param pResId
* ...
* @return ...
*/
Drawable getDrawable(bitmap pResId);
/**
* Gets the density from the current screen's DisplayMetrics
*
* @return the screen's density
*/
float getDisplayMetricsDensity();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B