package mil.nga.giat.mage.map.marker; import android.content.Context; import android.util.Log; import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener; 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.Marker; import com.google.android.gms.maps.model.MarkerOptions; import org.ocpsoft.prettytime.PrettyTime; import java.util.Date; import mil.nga.giat.mage.R; import mil.nga.giat.mage.filter.Filter; import mil.nga.giat.mage.observation.MapMarkerObservation; import mil.nga.giat.mage.observation.MapObservation; import mil.nga.giat.mage.observation.MapObservationManager; import mil.nga.giat.mage.observation.MapObservations; import mil.nga.giat.mage.observation.MapShapeObservation; import mil.nga.giat.mage.sdk.Temporal; import mil.nga.giat.mage.sdk.datastore.observation.Observation; import mil.nga.giat.mage.sdk.datastore.observation.ObservationProperty; import mil.nga.sf.Geometry; import mil.nga.sf.Point; import mil.nga.sf.util.GeometryUtils; public class ObservationMarkerCollection implements PointCollection<Observation>, OnMarkerClickListener { private static final String LOG_NAME = ObservationMarkerCollection.class.getName(); private GoogleMap map; private Context context; private Date latestObservationDate = new Date(0); protected Pair<String, Circle> observationAccuracyCircle = null; private boolean visible = true; /** * Used to create and add observations to the map */ private final MapObservationManager mapObservationManager; /** * Maintains the collection of map observations including markers and shapes */ private final MapObservations mapObservations = new MapObservations(); protected GoogleMap.InfoWindowAdapter infoWindowAdapter = new ObservationInfoWindowAdapter(); public ObservationMarkerCollection(Context context, GoogleMap map) { this.map = map; this.context = context; mapObservationManager = new MapObservationManager(context, map); } @Override public void add(MarkerOptions options, Observation observation) { // If I got an observation that I already have remove it mapObservations.remove(observation.getId()); // Add the new observation to the map and maintain it options.visible(visible); MapObservation mapObservation = mapObservationManager.addToMap(observation, options, visible); mapObservations.add(mapObservation); if (observation.getLastModified().after(latestObservationDate)) { latestObservationDate = observation.getLastModified(); } } @Override public void setVisibility(boolean visible) { if (this.visible == visible) return; this.visible = visible; mapObservations.setVisible(visible); if (observationAccuracyCircle != null) { observationAccuracyCircle.second.setVisible(visible); } } @Override public boolean isVisible() { return this.visible; } @Override public void remove(Observation o) { mapObservations.remove(o.getId()); if (observationAccuracyCircle != null && observationAccuracyCircle.second.getId().equals(o.getRemoteId())) { observationAccuracyCircle.second.remove(); observationAccuracyCircle = null; } } @Override public boolean onMarkerClick(Marker marker) { boolean handled = false; Observation observation = mapObservations.getMarkerObservation(marker.getId()); if (observation != null) { final Geometry g = observation.getGeometry(); if (g != null) { Point point = GeometryUtils.getCentroid(g); LatLng latLng = new LatLng(point.getY(), point.getX()); Float accuracy = observation.getAccuracy(); if (accuracy != null) { try { if (observationAccuracyCircle != null) { observationAccuracyCircle.second.remove(); } Circle circle = map.addCircle(new CircleOptions() .center(latLng) .radius(accuracy) .fillColor(context.getResources().getColor(R.color.accuracy_circle_fill)) .strokeColor(context.getResources().getColor(R.color.accuracy_circle_stroke)) .strokeWidth(2.0f)); observationAccuracyCircle = new Pair<>(observation.getRemoteId(), circle); } catch (NumberFormatException nfe) { Log.e(LOG_NAME, "Problem adding accuracy circle to the map.", nfe); } } } map.setInfoWindowAdapter(infoWindowAdapter); marker.showInfoWindow(); handled = true; } return handled; } @Override public void refresh(Observation observation) { } @Override public void refreshMarkerIcons(Filter<Temporal> filter) { for (MapMarkerObservation mapMarkerObservation : mapObservations.getMarkers()) { Marker marker = mapMarkerObservation.getMarker(); Observation observation = mapMarkerObservation.getObservation(); if (observation != null) { if (filter != null && !filter.passesFilter(observation)) { this.remove(observation); } else { boolean showWindow = marker.isInfoWindowShown(); // make sure to set the Anchor after this call as well, because the size of the icon might have changed marker.setIcon(ObservationBitmapFactory.bitmapDescriptor(context, observation)); marker.setAnchor(0.5f, 1.0f); if (showWindow) { marker.showInfoWindow(); } } } } } @Override public int count() { return mapObservations.size(); } @Override public void onMapClick(LatLng latLng) { MapShapeObservation mapShapeObservation = mapObservations.getClickedShape(map, latLng); if (mapShapeObservation != null) { Marker shapeMarker = mapObservationManager.addShapeMarker(latLng, visible); mapObservations.setShapeMarker(shapeMarker, mapShapeObservation); map.setInfoWindowAdapter(infoWindowAdapter); shapeMarker.showInfoWindow(); } } @Override public void offMarkerClick(){ mapObservations.clearShapeMarker(); if (observationAccuracyCircle != null) { observationAccuracyCircle.second.remove(); observationAccuracyCircle = null; } } @Override public void clear() { mapObservations.clear(); latestObservationDate = new Date(0); if (observationAccuracyCircle != null) { observationAccuracyCircle.second.remove(); observationAccuracyCircle = null; } } @Override public Date getLatestDate() { return latestObservationDate; } @Override public void onCameraIdle() { // do nothing I don't care } @Override public Observation pointForMarker(Marker marker) { return mapObservations.getMarkerObservation(marker.getId()); } private class ObservationInfoWindowAdapter implements GoogleMap.InfoWindowAdapter { @Override public View getInfoWindow(Marker marker) { final Observation observation = mapObservations.getMarkerObservation(marker.getId()); if (observation == null) { return null; } LayoutInflater inflater = LayoutInflater.from(context); View v = inflater.inflate(R.layout.observation_infowindow, null); ObservationProperty primaryField = observation.getPrimaryField(); String type = primaryField != null ? primaryField.getValue().toString() : "Observation"; TextView primary = v.findViewById(R.id.observation_primary); primary.setText(type); TextView date = v.findViewById(R.id.observation_date); date.setText(new PrettyTime().format(observation.getTimestamp())); return v; } @Override public View getInfoContents(Marker marker) { return null; } } }