package de.blau.android.photos; import java.util.ArrayList; import java.util.Collection; import java.util.List; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.support.v4.content.ContextCompat; import android.util.Log; import de.blau.android.Map; import de.blau.android.R; import de.blau.android.osm.BoundingBox; import de.blau.android.osm.Server; import de.blau.android.resources.DataStyle; import de.blau.android.util.GeoMath; import de.blau.android.util.Snack; import de.blau.android.views.IMapView; import de.blau.android.views.overlay.MapViewOverlay; /** * implement a geo-referenced photo overlay, code stolen from the OSB implementation * @author simon * */ public class MapOverlay extends MapViewOverlay { private final static String DEBUG_TAG = "PhotoOverlay"; /** viewbox needs to be less wide than this for displaying bugs, just to avoid querying the whole world for bugs */ private static final int TOLERANCE_MIN_VIEWBOX_WIDTH = 40000 * 32; /** Map this is an overlay of. */ private final Map map; /** Photos visible on the overlay. */ private Collection<Photo> photos; /** have we already run a scan? */ private boolean indexed = false; /** set while we are actually creating index */ private boolean indexing = false; /** default icon */ private final Drawable icon; /** selected icon */ private final Drawable icon_selected; /** icon dimensions */ private int h2; private int w2; /** * Index disk/in-memory of photos */ private PhotoIndex pi = null; /** * Pref for this layer enabled */ private boolean enabled = false; /** last selected photo, may not be stil displayed */ private Photo selected = null; /** Request to update the bugs for the current view. * Ensure cur is set before invoking. */ private final AsyncTask<Void, Integer, Void> indexPhotos = new AsyncTask<Void, Integer, Void>() { @Override protected Void doInBackground(Void... params) { if (!indexing) { indexing = true; publishProgress(0); pi.createOrUpdateIndex(); pi.fill(null); publishProgress(1); indexing = false; indexed = true; } return null; } @Override protected void onProgressUpdate(Integer ... progress) { if (progress[0] == 0) { Snack.barInfoShort(map, R.string.toast_photo_indexing_started); } if (progress[0] == 1) { Snack.barInfoShort(map, R.string.toast_photo_indexing_finished); } } @Override protected void onPostExecute(Void params) { if (indexed) { map.invalidate(); } } }; public MapOverlay(final Map map, Server s) { Context context = map.getContext(); this.map = map; photos = new ArrayList<Photo>(); icon = ContextCompat.getDrawable(context, R.drawable.camera_red); icon_selected = ContextCompat.getDrawable(context, R.drawable.camera_green); // note this assumes the icons are the same size w2 = icon.getIntrinsicWidth() / 2; h2 = icon.getIntrinsicHeight() / 2; pi = new PhotoIndex(context); } @Override public boolean isReadyToDraw() { enabled = map.getPrefs().isPhotoLayerEnabled(); return !enabled || map.getOpenStreetMapTilesOverlay().isReadyToDraw(); } @Override protected void onDraw(Canvas c, IMapView osmv) { if (enabled) { BoundingBox bb = osmv.getViewBox(); if ((bb.getWidth() > TOLERANCE_MIN_VIEWBOX_WIDTH) || (bb.getHeight() > TOLERANCE_MIN_VIEWBOX_WIDTH)) { return; } if (!indexed && !indexing) { indexPhotos.execute(); return; } // draw all the photos int w = map.getWidth(); int h = map.getHeight(); photos = pi.getPhotos(bb); for (Photo p : photos) { Drawable i; if (p == selected) i = icon_selected; else i = icon; int x = (int) GeoMath.lonE7ToX(w , bb, p.getLon()); int y = (int) GeoMath.latE7ToY(h, w ,bb, p.getLat()); i.setBounds(new Rect(x - w2, y - h2, x + w2, y + h2)); if (p.hasDirection()) { c.rotate(p.getDirection(), x, y); i.draw(c); c.rotate(-p.getDirection(), x, y); } else { i.draw(c); } } } } @Override protected void onDrawFinished(Canvas c, IMapView osmv) { // do nothing } /** * Given screen coordinates, find all nearby photos. * @param x Screen X-coordinate. * @param y Screen Y-coordinate. * @param viewBox Map view box. * @return List of bugs close to given location. */ public List<Photo> getClickedPhotos(final float x, final float y, final BoundingBox viewBox) { List<Photo> result = new ArrayList<Photo>(); Log.d("photos.MapOverlay", "getClickedPhotos"); if (map.getPrefs().isPhotoLayerEnabled()) { final float tolerance = DataStyle.getCurrent().nodeToleranceValue; for (Photo p : photos) { int lat = p.getLat(); int lon = p.getLon(); float differenceX = Math.abs(GeoMath.lonE7ToX(map.getWidth(), viewBox, lon) - x); float differenceY = Math.abs(GeoMath.latE7ToY(map.getHeight(), map.getWidth(), viewBox, lat) - y); if ((differenceX <= tolerance) && (differenceY <= tolerance)) { if (Math.hypot(differenceX, differenceY) <= tolerance) { result.add(p); } } } } Log.d("photos.MapOverlay", "getClickedPhotos found " + result.size()); return result; } public void setSelected(Photo photo) { selected = photo; } }