diff --git a/src/it/reyboz/bustorino/ActivityMap.java b/src/it/reyboz/bustorino/ActivityMap.java index d1b3736..1ef2806 100644 --- a/src/it/reyboz/bustorino/ActivityMap.java +++ b/src/it/reyboz/bustorino/ActivityMap.java @@ -1,370 +1,387 @@ /* BusTO Activities Copyright (C) 2020 Andrea Ugo e Fabio Mazza This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package it.reyboz.bustorino; import android.content.Context; import android.content.Intent; import android.location.Location; import android.location.LocationManager; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.ImageButton; import android.support.annotation.RequiresApi; import android.support.v7.app.AppCompatActivity; import android.support.v7.preference.PreferenceManager; import android.widget.Toast; import org.osmdroid.api.IMapController; import org.osmdroid.config.Configuration; import org.osmdroid.events.DelayedMapListener; import org.osmdroid.events.MapListener; import org.osmdroid.events.ScrollEvent; import org.osmdroid.events.ZoomEvent; import org.osmdroid.tileprovider.tilesource.TileSourceFactory; import org.osmdroid.util.BoundingBox; import org.osmdroid.util.GeoPoint; import org.osmdroid.views.MapView; import org.osmdroid.views.overlay.FolderOverlay; import org.osmdroid.views.overlay.Marker; import org.osmdroid.views.overlay.Overlay; import org.osmdroid.views.overlay.infowindow.InfoWindow; import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider; import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay; -import java.util.List; +import java.util.*; import it.reyboz.bustorino.backend.Stop; import it.reyboz.bustorino.map.CustomInfoWindow; import it.reyboz.bustorino.middleware.StopsDB; public class ActivityMap extends AppCompatActivity { private static final String TAG = "Busto-MapActivity"; private static final String MAP_CURRENT_ZOOM_KEY = "map-current-zoom"; private static final String MAP_CENTER_LAT_KEY = "map-center-lat"; private static final String MAP_CENTER_LON_KEY = "map-center-lon"; public static final String BUNDLE_LATIT = "lat"; public static final String BUNDLE_LONGIT = "lon"; public static final String BUNDLE_NAME = "name"; public static final String BUNDLE_ID = "ID"; private static final double DEFAULT_CENTER_LAT = 45.0708; private static final double DEFAULT_CENTER_LON = 7.6858; - private static final double POSITION_FOUND_ZOOM = 18.6; + private static final double POSITION_FOUND_ZOOM = 18.3; + + private HashSet shownStops = null; private MapView map = null; public Context ctx; private MyLocationNewOverlay mLocationOverlay = null; private FolderOverlay stopsFolderOverlay = null; protected ImageButton btCenterMap; protected ImageButton btFollowMe; @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB) @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //handle permissions first, before map is created. not depicted here //load/initialize the osmdroid configuration, this can be done ctx = getApplicationContext(); Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx)); //setting this before the layout is inflated is a good idea //it 'should' ensure that the map has a writable location for the map cache, even without permissions //if no tiles are displayed, you can try overriding the cache path using Configuration.getInstance().setCachePath //see also StorageUtils //note, the load method also sets the HTTP User Agent to your application's package name, abusing osm's tile servers will get you banned based on this string //inflate and create the map setContentView(R.layout.activity_map); map = (MapView) findViewById(R.id.map); map.setTileSource(TileSourceFactory.MAPNIK); //map.setTilesScaledToDpi(true); map.setFlingEnabled(true); // add ability to zoom with 2 fingers map.setMultiTouchControls(true); btCenterMap = (ImageButton) findViewById(R.id.ic_center_map); btFollowMe = (ImageButton) findViewById(R.id.ic_follow_me); //setup FolderOverlay stopsFolderOverlay = new FolderOverlay(); // take the parameters if it's called from other Activities Bundle b = getIntent().getExtras(); startMap(b, savedInstanceState); - - // on drag and zoom reload the markers map.addMapListener(new DelayedMapListener(new MapListener() { @Override public boolean onScroll(ScrollEvent paramScrollEvent) { loadMarkers(); return true; } @Override public boolean onZoom(ZoomEvent event) { loadMarkers(); return true; } })); btCenterMap.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.i(TAG, "centerMap clicked "); final GeoPoint myPosition = mLocationOverlay.getMyLocation(); map.getController().animateTo(myPosition); } }); btFollowMe.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.i(TAG, "btFollowMe clicked "); if (!mLocationOverlay.isFollowLocationEnabled()) { mLocationOverlay.enableFollowLocation(); btFollowMe.setImageResource(R.drawable.ic_follow_me_on); } else { mLocationOverlay.disableFollowLocation(); btFollowMe.setImageResource(R.drawable.ic_follow_me); } } }); + } public void startMap(Bundle incoming, Bundle savedInstanceState) { //parse incoming bundle GeoPoint marker = null; String name = null; String ID = null; if(incoming != null) { double lat = incoming.getDouble(BUNDLE_LATIT); double lon = incoming.getDouble(BUNDLE_LONGIT); marker = new GeoPoint(lat, lon); name = incoming.getString(BUNDLE_NAME); ID = incoming.getString(BUNDLE_ID); } + shownStops = new HashSet<>(); // move the map on the marker position or on a default view point: Turin, Piazza Castello // and set the start zoom IMapController mapController = map.getController(); GeoPoint startPoint = null; if (marker != null) { startPoint = marker; mapController.setZoom(POSITION_FOUND_ZOOM); } else if (savedInstanceState != null) { mapController.setZoom(savedInstanceState.getDouble(MAP_CURRENT_ZOOM_KEY)); mapController.setCenter(new GeoPoint(savedInstanceState.getDouble(MAP_CENTER_LAT_KEY), savedInstanceState.getDouble(MAP_CENTER_LON_KEY))); } else { boolean found = false; LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); if(locationManager!=null) { Location userLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); if (userLocation != null) { mapController.setZoom(POSITION_FOUND_ZOOM); startPoint = new GeoPoint(userLocation); found = true; } } if(!found){ startPoint = new GeoPoint(DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON); mapController.setZoom(16.0); } } // set the minimum zoom level map.setMinZoomLevel(15.0); //add contingency check (shouldn't happen..., but) if (startPoint != null) { mapController.setCenter(startPoint); } // Location Overlay // from OpenBikeSharing (THANK GOD) GpsMyLocationProvider imlp = new GpsMyLocationProvider(this.getBaseContext()); - imlp.setLocationUpdateMinDistance(10); + imlp.setLocationUpdateMinDistance(5); imlp.setLocationUpdateMinTime(2000); this.mLocationOverlay = new MyLocationNewOverlay(imlp,map); mLocationOverlay.enableMyLocation(); mLocationOverlay.enableFollowLocation(); + btFollowMe.setImageResource(R.drawable.ic_follow_me_on); mLocationOverlay.setOptionsMenuEnabled(true); /* mLocationOverlay.runOnFirstFix(() -> { mapController.setCenter(mLocationOverlay.getMyLocation()); mapController.animateTo(mLocationOverlay.getMyLocation()); }); */ map.getOverlays().add(this.mLocationOverlay); //add stops overlay map.getOverlays().add(this.stopsFolderOverlay); loadMarkers(); if (marker != null) { // make a marker with the info window open for the searched marker makeMarker(startPoint, name , ID, true); } } public Marker makeMarker(GeoPoint geoPoint, String stopName, String ID, boolean isStartMarker) { // add a marker Marker marker = new Marker(map); // set custom info window as info window CustomInfoWindow popup = new CustomInfoWindow(map, ID, stopName); marker.setInfoWindow(popup); // make the marker clickable marker.setOnMarkerClickListener((thisMarker, mapView) -> { if (thisMarker.isInfoWindowOpen()) { // on second click // create an intent with these extras Intent intent = new Intent(ActivityMap.this, ActivityMain.class); Bundle b = new Bundle(); b.putString("bus-stop-ID", ID); b.putString("bus-stop-display-name", stopName); intent.putExtras(b); intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); // start ActivityMain with the previous intent startActivity(intent); } else { // on first click // hide all opened info window InfoWindow.closeAllInfoWindowsOn(map); // show this particular info window thisMarker.showInfoWindow(); // move the map to its position map.getController().animateTo(thisMarker.getPosition()); } return true; }); // set its position marker.setPosition(geoPoint); marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM); // add to it an icon marker.setIcon(getResources().getDrawable(R.drawable.bus_marker)); // add to it a title marker.setTitle(stopName); // set the description as the ID marker.setSnippet(ID); // show popup info window of the searched marker if (isStartMarker) { marker.showInfoWindow(); } return marker; } public void loadMarkers() { // get rid of the previous markers //map.getOverlays().clear(); //stopsFolderOverlay = new FolderOverlay(); List stopsOverlays = stopsFolderOverlay.getItems(); - if (stopsOverlays != null){ + /*if (stopsOverlays != null){ stopsOverlays.clear(); - } + }*/ // get the top, bottom, left and right screen's coordinate BoundingBox bb = map.getBoundingBox(); double latFrom = bb.getLatSouth(); double latTo = bb.getLatNorth(); double lngFrom = bb.getLonWest(); double lngTo = bb.getLonEast(); // get the stops located in those coordinates StopsDB stopsDB = new StopsDB(ctx); stopsDB.openIfNeeded(); Stop[] stops = stopsDB.queryAllInsideMapView(latFrom, latTo, lngFrom, lngTo); stopsDB.closeIfNeeded(); // add new markers of those stops for (Stop stop : stops) { + if (shownStops.contains(stop.ID)){ + continue; + } + try{ + stop.getLatitude(); + stop.getLongitude(); + } catch (NullPointerException e) { + Log.e(TAG,"Stop "+stop.ID+ " gives null coordinates"); + e.printStackTrace(); + continue; + } + + shownStops.add(stop.ID); GeoPoint marker = new GeoPoint(stop.getLatitude(), stop.getLongitude()); Marker stopMarker = makeMarker(marker, stop.getStopDefaultName(), stop.ID, false); stopsFolderOverlay.add(stopMarker); + } } protected boolean detachMapFromPosition(){ if (mLocationOverlay.isFollowLocationEnabled()) { mLocationOverlay.disableFollowLocation(); btFollowMe.setImageResource(R.drawable.ic_follow_me); return true; } return false; } public void onResume(){ super.onResume(); //this will refresh the osmdroid configuration on resuming. //if you make changes to the configuration, use //SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); //Configuration.getInstance().load(this, PreferenceManager.getDefaultSharedPreferences(this)); map.onResume(); //needed for compass, my location overlays, v6.0.0 and up mLocationOverlay.enableMyLocation(); } public void onPause(){ super.onPause(); //this will refresh the osmdroid configuration on resuming. //if you make changes to the configuration, use //SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); //Configuration.getInstance().save(this, prefs); map.onPause(); //needed for compass, my location overlays, v6.0.0 and up mLocationOverlay.disableMyLocation(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putDouble(MAP_CURRENT_ZOOM_KEY, map.getZoomLevelDouble()); outState.putDouble(MAP_CENTER_LAT_KEY, map.getMapCenter().getLatitude()); outState.putDouble(MAP_CENTER_LON_KEY, map.getMapCenter().getLongitude()); } } \ No newline at end of file