package ch.wootbarrel.coinmapp;
import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.VisibleRegion;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ch.wootbarrel.coinmapp.db.CoinmapDataSource;
/**
* Created by n3utrino on 19.12.13.
* <p/>
* Holds and manages the map entries and the MapMarkers
*/
public class MapEntryManager implements GoogleMap.OnCameraChangeListener, GoogleMap.OnMapLoadedCallback, GoogleMap.OnMarkerClickListener {
public static final int MAX_MAP_ITEMS = 500;
private List<MapEntry> entries;
private List<Marker> markers = new ArrayList<Marker>();
private List<Circle> circles = new ArrayList<Circle>();
private GoogleMap map;
private Map<Integer, Map<LatLngBounds, List<MapEntry>>> bucketForZoom = new HashMap<Integer, Map<LatLngBounds, List<MapEntry>>>();
private float currentZoom;
private CoinmapDataSource dataSource;
private VisibleRegion lastVisibleRegion;
public MapEntryManager(Context ctx, ProgressDialog progressDialog) {
this.dataSource = new CoinmapDataSource(ctx, progressDialog);
dataSource.open();
entries = dataSource.getAllEntries();
}
public void updateMap(GoogleMap map) {
this.map = map;
map.setOnMarkerClickListener(this);
dataSource.open();
map.setOnCameraChangeListener(this);
map.setOnMapLoadedCallback(this);
onCameraChange(map.getCameraPosition());
}
public void dispose() {
dataSource.close();
removeAllMapItems();
}
private LatLng latLngFromDistanceAndBearing(LatLng start, double distance, double bearing) {
double R = 6371;
double lat1 = Math.toRadians(start.latitude);
double lon1 = Math.toRadians(start.longitude);
bearing = Math.toRadians(bearing);
double lat2 = Math.asin(Math.sin(lat1) * Math.cos(distance / R) +
Math.cos(lat1) * Math.sin(distance / R) * Math.cos(bearing));
double lon2 = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(distance / R) * Math.cos(lat1),
Math.cos(distance / R) - Math.sin(lat1) * Math.sin(lat2));
return new LatLng(Math.toDegrees(lat2), Math.toDegrees(lon2));
}
@Override
public void onCameraChange(final CameraPosition cameraPosition) {
double bucketSizePixel = 40;
double worldPixels = 265 * Math.pow(2, cameraPosition.zoom);
final double bucketSizeDistance = bucketSizePixel * 40000 / worldPixels;
final VisibleRegion visibleRegion = map.getProjection().getVisibleRegion();
new AsyncTask<Void, Void, Void>() {
Map<LatLngBounds, List<MapEntry>> buckets;
List<MarkerOptions> markerOptionsList = new ArrayList<MarkerOptions>();
List<CircleOptions> circleOptionsList = new ArrayList<CircleOptions>();
@Override
protected Void doInBackground(Void... voids) {
currentZoom = cameraPosition.zoom;
if (buckets == null) {
buckets = new HashMap<LatLngBounds, List<MapEntry>>();
bucketForZoom.put((int) currentZoom, buckets);
for (MapEntry entry : entries) {
LatLng entryLatLng = new LatLng(entry.lat, entry.lon);
if (!visibleRegion.latLngBounds.contains(entryLatLng)) {
continue;
}
boolean isAdded = false;
for (LatLngBounds bounds : buckets.keySet()) {
if (bounds.contains(entryLatLng)) {
buckets.get(bounds).add(entry);
isAdded = true;
}
}
if (!isAdded) {
LatLng southWest = latLngFromDistanceAndBearing(entryLatLng, bucketSizeDistance / 2, 360 - 135);
LatLng northEast = latLngFromDistanceAndBearing(entryLatLng, bucketSizeDistance / 2, 45);
List<MapEntry> mapEntries = new ArrayList<MapEntry>();
buckets.put(new LatLngBounds(southWest, northEast), mapEntries);
mapEntries.add(entry);
}
}
}
for (List<MapEntry> mapEntries : buckets.values()) {
if (mapEntries.size() == 1) {
//TODO: better marker image
MapEntry mapEntry = mapEntries.get(0);
LatLng markerCenter = new LatLng(mapEntry.lat, mapEntry.lon);
BitmapDescriptor icon = BitmapDescriptorFactory.fromResource(R.drawable.food_pub);
markerOptionsList.add(new MarkerOptions().visible(visibleRegion.latLngBounds.contains(markerCenter))
.anchor(0.5f, 0.5f).icon(icon).position(markerCenter).title(mapEntry.title));
} else if (mapEntries.size() > 1) {
MapEntry mapEntry = mapEntries.get(0);
LatLng markerCenter = new LatLng(mapEntry.lat, mapEntry.lon);
//Todo make custom bitmap with circle and number of venues in it.
circleOptionsList.add(new CircleOptions()
.center(markerCenter)
.radius(bucketSizeDistance * 250)
.visible(visibleRegion.latLngBounds.contains(markerCenter))
.strokeWidth(4)
.fillColor(0xAAFFD700)
.strokeColor(Color.YELLOW));
}
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
removeAllMapItems();
for (MarkerOptions markerOptions : markerOptionsList) {
Marker marker = map.addMarker(markerOptions);
markers.add(marker);
}
for (CircleOptions circleOptions : circleOptionsList) {
circles.add(map.addCircle(circleOptions));
}
// int markerIndex = 0;
// int circleIndex = 0;
//
// for (List<MapEntry> mapEntries : buckets.values()) {
//
// if (mapEntries.size() == 1) {
//
// if (markers.size() > markerIndex) {
// Marker marker = markers.get(markerIndex);
// MapEntry entry = mapEntries.get(0);
// LatLng position = new LatLng(entry.lat, entry.lon);
// marker.setPosition(position);
// marker.setVisible(true);
// marker.setTitle(entry.title);
// markerIndex++;
// }
// } else if (mapEntries.size() > 1) {
// if (circles.size()>circleIndex) {
// Circle circle = circles.get(circleIndex);
// MapEntry entry = mapEntries.get(0);
// LatLng position = new LatLng(entry.lat, entry.lon);
// circle.setRadius(bucketSizeDistance * 250);
// circle.setCenter(position);
// circle.setVisible(true);
// circleIndex++;
// }
// }
// }
}
}.execute();
lastVisibleRegion = visibleRegion;
}
private void removeAllMapItems() {
for (Marker marker : markers) {
marker.remove();
}
for (Circle circle : circles) {
circle.remove();
}
markers.clear();
circles.clear();
}
@Override
public void onMapLoaded() {
}
@Override
public boolean onMarkerClick(Marker marker) {
marker.showInfoWindow();
return true;
}
public List<MapEntry> getEntries() {
return entries;
}
}