diff --git a/app/build.gradle b/app/build.gradle
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -97,8 +97,6 @@
implementation 'org.jsoup:jsoup:1.15.3'
implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:2.0.1'
implementation 'com.android.volley:volley:1.2.1'
-
- implementation 'org.osmdroid:osmdroid-android:6.1.18'
//maplibre
implementation 'org.maplibre.gl:android-sdk:11.8.6'
implementation 'org.maplibre.gl:android-sdk-turf:6.0.1'
diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
--- a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
+++ b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
@@ -718,8 +718,8 @@
void createAndShowMapFragment(@Nullable Stop stop, boolean addToBackStack){
final FragmentManager fm = getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
- final MapLibreFragment fragment = MapLibreFragment.Companion.newInstance(stop);
- ft.replace(R.id.mainActContentFrame, fragment, MapFragmentKt.FRAGMENT_TAG);
+ final MapLibreFragment fragment = MapLibreFragment.newInstance(stop);
+ ft.replace(R.id.mainActContentFrame, fragment, MapLibreFragment.FRAGMENT_TAG);
if (addToBackStack) ft.addToBackStack(null);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/GPSPoint.java b/app/src/main/java/it/reyboz/bustorino/backend/GPSPoint.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/GPSPoint.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/GPSPoint.java
@@ -1,8 +1,7 @@
package it.reyboz.bustorino.backend;
-import org.osmdroid.api.IGeoPoint;
-public class GPSPoint implements IGeoPoint {
+public class GPSPoint {
public final double latitude;
public final double longitude;
@@ -12,22 +11,18 @@
this.longitude = longitude;
}
- @Override
public int getLatitudeE6() {
return (int) (latitude*1e6d);
}
- @Override
public int getLongitudeE6() {
return (int) (longitude*1e6d);
}
- @Override
public double getLatitude() {
return latitude;
}
- @Override
public double getLongitude() {
return longitude;
}
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/RealtimeVehicle.java b/app/src/main/java/it/reyboz/bustorino/backend/RealtimeVehicle.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/RealtimeVehicle.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/RealtimeVehicle.java
@@ -1,24 +1,23 @@
package it.reyboz.bustorino.backend;
-import org.osmdroid.util.GeoPoint;
public class RealtimeVehicle {
- private final GeoPoint location;
+ private final GPSPoint location;
private final float bearing;
public final int updateTimestamp;
private final String vehicleLabel;
private String routeID;
- public RealtimeVehicle(GeoPoint location, float bearing, int updateTimestamp, String vehicleLabel) {
+ public RealtimeVehicle(GPSPoint location, float bearing, int updateTimestamp, String vehicleLabel) {
this.location = location;
this.bearing = bearing;
this.updateTimestamp = updateTimestamp;
this.vehicleLabel = vehicleLabel;
}
- public GeoPoint getLocation() {
+ public GPSPoint getLocation() {
return location;
}
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/Stop.java b/app/src/main/java/it/reyboz/bustorino/backend/Stop.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/Stop.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/Stop.java
@@ -24,7 +24,7 @@
import androidx.annotation.Nullable;
import it.reyboz.bustorino.util.LinesNameSorter;
-import org.osmdroid.api.IGeoPoint;
+import org.jetbrains.annotations.NotNull;
import java.net.URLEncoder;
import java.util.ArrayList;
@@ -313,7 +313,7 @@
}
- public Double getDistanceFromLocation(IGeoPoint loc){
+ public Double getDistanceFromLocation(@NotNull GPSPoint loc){
return getDistanceFromLocation(loc.getLatitude(), loc.getLongitude());
}
public Double getDistanceFromLocation(double latitude, double longitude){
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/utils.java b/app/src/main/java/it/reyboz/bustorino/backend/utils.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/utils.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/utils.java
@@ -50,6 +50,8 @@
public static Double measuredistanceBetween(double lat1,double long1,double lat2,double long2){
+
+
final double phi1 = Math.toRadians(lat1);
final double phi2 = Math.toRadians(lat2);
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt
--- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt
@@ -514,7 +514,7 @@
this.map = mapReady
val context = requireContext()
- val mjson = Styles.getJsonStyleFromAsset(context, PreferencesHolder.getMapLibreStyleFile(context)) //ViewUtils.loadJsonFromAsset(requireContext(),"map_style_good.json")
+ val mjson = MapLibreStyles.getJsonStyleFromAsset(context, PreferencesHolder.getMapLibreStyleFile(context)) //ViewUtils.loadJsonFromAsset(requireContext(),"map_style_good.json")
activity?.run {
val builder = Style.Builder().fromJson(mjson!!)
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MapFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/MapFragment.java
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/fragments/MapFragment.java
+++ /dev/null
@@ -1,837 +0,0 @@
-/*
- BusTO - Fragments components
- Copyright (C) 2020 Andrea Ugo
- Copyright (C) 2021 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.fragments;
-
-import android.Manifest;
-import android.animation.ObjectAnimator;
-import android.annotation.SuppressLint;
-import android.content.Context;
-
-import android.graphics.drawable.Drawable;
-import android.location.Location;
-import android.location.LocationManager;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageButton;
-import android.widget.Toast;
-
-import androidx.activity.result.ActivityResultLauncher;
-import androidx.activity.result.contract.ActivityResultContracts;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import androidx.core.content.res.ResourcesCompat;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.preference.PreferenceManager;
-
-import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate;
-import it.reyboz.bustorino.backend.mato.MQTTMatoClient;
-import it.reyboz.bustorino.backend.utils;
-import it.reyboz.bustorino.data.gtfs.MatoPattern;
-import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops;
-import it.reyboz.bustorino.map.*;
-import it.reyboz.bustorino.viewmodels.LivePositionsViewModel;
-import it.reyboz.bustorino.viewmodels.StopsMapViewModel;
-import org.osmdroid.api.IGeoPoint;
-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.infowindow.InfoWindow;
-import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider;
-
-import java.util.*;
-
-import kotlin.Pair;
-import it.reyboz.bustorino.R;
-import it.reyboz.bustorino.backend.Stop;
-import it.reyboz.bustorino.middleware.GeneralActivity;
-import it.reyboz.bustorino.util.Permissions;
-
-import static it.reyboz.bustorino.fragments.SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE;
-
-public class MapFragment extends ScreenBaseFragment {
-
- //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";
- private static final String FOLLOWING_LOCAT_KEY ="following";
-
- 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";
- public static final String BUNDLE_ROUTES_STOPPING = "routesStopping";
-
- public static final String FRAGMENT_TAG="BusTOMapFragment";
-
-
- 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.3;
- public static final double NO_POSITION_ZOOM = 17.1;
-
- private static final String DEBUG_TAG=FRAGMENT_TAG;
-
- protected FragmentListenerMain listenerMain;
-
- private HashSet shownStops = null;
-
-
- private MapView map = null;
- public Context ctx;
- private LocationOverlay mLocationOverlay = null;
- private FolderOverlay stopsFolderOverlay = null;
- private Bundle savedMapState = null;
- protected ImageButton btCenterMap;
- protected ImageButton btFollowMe;
-
- protected CoordinatorLayout coordLayout;
- private boolean hasMapStartFinished = false;
- private boolean followingLocation = false;
-
- //the ViewModel from which we get the stop to display in the map
- private StopsMapViewModel stopsViewModel;
-
- //private GtfsPositionsViewModel gtfsPosViewModel; //= new ViewModelProvider(this).get(MapViewModel.class);
- private LivePositionsViewModel livePositionsViewModel;
- private Boolean useMQTTViewModel = true;
-
- private final HashMap busPositionMarkersByTrip = new HashMap<>();
- private FolderOverlay busPositionsOverlay = null;
-
- private final HashMap tripMarkersAnimators = new HashMap<>();
-
- protected final CustomInfoWindow.TouchResponder responder = new CustomInfoWindow.TouchResponder() {
- @Override
- public void onActionUp(@NonNull String stopID, @Nullable String stopName) {
- if (listenerMain!= null){
- Log.d(DEBUG_TAG, "Asked to show arrivals for stop ID: "+stopID);
- listenerMain.requestArrivalsForStopID(stopID);
- }
- }
- };
- protected final LocationOverlay.OverlayCallbacks locationCallbacks = new LocationOverlay.OverlayCallbacks() {
- @Override
- public void onDisableFollowMyLocation() {
- updateGUIForLocationFollowing(false);
- followingLocation=false;
- }
-
- @Override
- public void onEnableFollowMyLocation() {
- updateGUIForLocationFollowing(true);
- followingLocation=true;
- }
- };
-
- private final ActivityResultLauncher positionRequestLauncher =
- registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> {
- if (result == null){
- Log.w(DEBUG_TAG, "Got asked permission but request is null, doing nothing?");
- }
- else if(Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_COARSE_LOCATION)) &&
- Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_FINE_LOCATION))){
-
- map.getOverlays().remove(mLocationOverlay);
- startLocationOverlay(true, map);
- if(getContext()==null || getContext().getSystemService(Context.LOCATION_SERVICE)==null)
- return;
- LocationManager locationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
- @SuppressLint("MissingPermission")
- Location userLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
- if (userLocation != null) {
- map.getController().setZoom(POSITION_FOUND_ZOOM);
- GeoPoint startPoint = new GeoPoint(userLocation);
- setLocationFollowing(true);
- map.getController().setCenter(startPoint);
- }
- }
- else Log.w(DEBUG_TAG,"No location permission");
- });
-
- public MapFragment() {
- }
- public static MapFragment getInstance(){
- return new MapFragment();
- }
- public static MapFragment getInstance(@NonNull Stop stop){
- MapFragment fragment= new MapFragment();
- Bundle args = new Bundle();
- args.putDouble(BUNDLE_LATIT, stop.getLatitude());
- args.putDouble(BUNDLE_LONGIT, stop.getLongitude());
- args.putString(BUNDLE_NAME, stop.getStopDisplayName());
- args.putString(BUNDLE_ID, stop.ID);
- args.putString(BUNDLE_ROUTES_STOPPING, stop.routesThatStopHereToString());
- fragment.setArguments(args);
-
- return fragment;
- }
- //public static MapFragment getInstance(@NonNull Stop stop){
- // return getInstance(stop.getLatitude(), stop.getLongitude(), stop.getStopDisplayName(), stop.ID);
- //}
-
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
- //use the same layout as the activity
- View root = inflater.inflate(R.layout.fragment_map, container, false);
- if (getContext() == null){
- throw new IllegalStateException();
- }
- ctx = getContext().getApplicationContext();
- Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx));
- map = root.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 = root.findViewById(R.id.centerMapImageButton);
- btFollowMe = root.findViewById(R.id.followUserImageButton);
- coordLayout = root.findViewById(R.id.coord_layout);
-
- //setup FolderOverlay
- stopsFolderOverlay = new FolderOverlay();
- //setup Bus Markers Overlay
- busPositionsOverlay = new FolderOverlay();
- //reset shown bus updates
- busPositionMarkersByTrip.clear();
- tripMarkersAnimators.clear();
- //set map not done
- hasMapStartFinished = false;
-
- String keySourcePositions=getString(R.string.pref_positions_source);
- useMQTTViewModel = (
- PreferenceManager.getDefaultSharedPreferences(requireContext())
- .getString(keySourcePositions,LIVE_POSITIONS_PREF_MQTT_VALUE).contentEquals(LIVE_POSITIONS_PREF_MQTT_VALUE));
-
-
- //Start map from bundle
- if (savedInstanceState !=null)
- startMap(getArguments(), savedInstanceState);
- else startMap(getArguments(), savedMapState);
- //set listeners
- map.addMapListener(new DelayedMapListener(new MapListener() {
-
- @Override
- public boolean onScroll(ScrollEvent paramScrollEvent) {
- requestStopsToShow();
- //Log.d(DEBUG_TAG, "Scrolling");
- //if (moveTriggeredByCode) moveTriggeredByCode =false;
- //else setLocationFollowing(false);
- return true;
- }
-
- @Override
- public boolean onZoom(ZoomEvent event) {
- requestStopsToShow();
- return true;
- }
-
- }));
-
-
- btCenterMap.setOnClickListener(v -> {
- //Log.i(TAG, "centerMap clicked ");
- if(Permissions.bothLocationPermissionsGranted(getContext())) {
- final GeoPoint myPosition = mLocationOverlay.getMyLocation();
- map.getController().animateTo(myPosition);
- } else
- Toast.makeText(getContext(), R.string.enable_position_message_map, Toast.LENGTH_SHORT)
- .show();
- });
-
- btFollowMe.setOnClickListener(v -> {
- //Log.i(TAG, "btFollowMe clicked ");
- if(Permissions.bothLocationPermissionsGranted(getContext()))
- setLocationFollowing(!followingLocation);
- else
- Toast.makeText(getContext(), R.string.enable_position_message_map, Toast.LENGTH_SHORT)
- .show();
- });
-
-
- return root;
- }
-
- @Override
- public void onAttach(@NonNull Context context) {
- super.onAttach(context);
-
- ViewModelProvider provider = new ViewModelProvider(this);
- //gtfsPosViewModel = provider.get(GtfsPositionsViewModel.class);
- livePositionsViewModel = provider.get(LivePositionsViewModel.class);
- stopsViewModel = provider.get(StopsMapViewModel.class);
-
-
- if (context instanceof FragmentListenerMain) {
- listenerMain = (FragmentListenerMain) context;
- } else {
- throw new RuntimeException(context.toString()
- + " must implement FragmentListenerMain");
- }
- }
- @Override
- public void onDetach() {
- super.onDetach();
- listenerMain = null;
- //stop animations
-
- // setupOnAttached = true;
- Log.w(DEBUG_TAG, "Fragment detached");
- }
-
- @Override
- public void onPause() {
- super.onPause();
- Log.w(DEBUG_TAG, "On pause called mapfrag");
- saveMapState();
- for (ObjectAnimator animator : tripMarkersAnimators.values()) {
- if(animator!=null && animator.isRunning()){
- animator.cancel();
- }
- }
- tripMarkersAnimators.clear();
- if(useMQTTViewModel) livePositionsViewModel.stopMatoUpdates();
-
- }
-
- /**
- * Save the map state inside the fragment
- * (calls saveMapState(bundle))
- */
- private void saveMapState(){
- savedMapState = new Bundle();
- saveMapState(savedMapState);
- }
-
- /**
- * Save the state of the map to restore it to a later time
- * @param bundle the bundle in which to save the data
- */
- private void saveMapState(Bundle bundle){
- Log.d(DEBUG_TAG, "Saving state, location following: "+followingLocation);
- bundle.putBoolean(FOLLOWING_LOCAT_KEY, followingLocation);
- if (map == null){
- //The map is null, it can happen?
- Log.e(DEBUG_TAG, "Cannot save map center, map is null");
- return;
- }
- final IGeoPoint loc = map.getMapCenter();
- bundle.putDouble(MAP_CENTER_LAT_KEY, loc.getLatitude());
- bundle.putDouble(MAP_CENTER_LON_KEY, loc.getLongitude());
- bundle.putDouble(MAP_CURRENT_ZOOM_KEY, map.getZoomLevelDouble());
- }
-
-
- @Override
- public void onResume() {
- super.onResume();
- //TODO: cleanup duplicate code (maybe merging the positions classes?)
- if(listenerMain!=null) listenerMain.readyGUIfor(FragmentKind.MAP);
- /// choose which to use
- String keySourcePositions=getString(R.string.pref_positions_source);
- useMQTTViewModel = PreferenceManager.getDefaultSharedPreferences(requireContext())
- .getString(keySourcePositions,LIVE_POSITIONS_PREF_MQTT_VALUE).contentEquals(
- LIVE_POSITIONS_PREF_MQTT_VALUE);
- if(livePositionsViewModel !=null) {
- //gtfsPosViewModel.requestUpdates();
- if(useMQTTViewModel)
- livePositionsViewModel.requestMatoPosUpdates(MQTTMatoClient.LINES_ALL);
- else
- livePositionsViewModel.requestGTFSUpdates();
- //mapViewModel.testCascade();
- livePositionsViewModel.isLastWorkResultGood().observe(this, d ->
- Log.d(DEBUG_TAG, "Last trip download result is "+d));
- livePositionsViewModel.getTripsGtfsIDsToQuery().observe(this, dat -> {
- Log.i(DEBUG_TAG, "Have these trips IDs missing from the DB, to be queried: "+dat);
- livePositionsViewModel.downloadTripsFromMato(dat);
- /*MatoTripsDownloadWorker.Companion.requestMatoTripsDownload(dat,
- requireContext().getApplicationContext(),
- "BusTO-MatoTripDownload");
-
- */
- });
- } /*else if(gtfsPosViewModel!=null){
- gtfsPosViewModel.requestUpdates();
- gtfsPosViewModel.getTripsGtfsIDsToQuery().observe(this, dat -> {
- Log.i(DEBUG_TAG, "Have these trips IDs missing from the DB, to be queried: "+dat);
- //gtfsPosViewModel.downloadTripsFromMato(dat);
- MatoTripsDownloadWorker.Companion.downloadTripsFromMato(dat,getContext().getApplicationContext(),
- "BusTO-MatoTripDownload");
- });
- }
- */
- else Log.e(DEBUG_TAG, "livePositionsViewModel is null at onResume");
-
- //rerequest stop
- stopsViewModel.requestStopsInBoundingBox(map.getBoundingBox());
- }
-
- private void startRequestsPositions(){
- if (livePositionsViewModel != null) {
- //should always be the case
- livePositionsViewModel.getUpdatesWithTripAndPatterns().observe(getViewLifecycleOwner(), data -> {
- Log.d(DEBUG_TAG, "Have " + data.size() + " trip updates, has Map start finished: " + hasMapStartFinished);
- if (hasMapStartFinished) updateBusPositionsInMap(data);
-
- if(!isDetached() && !useMQTTViewModel)
- livePositionsViewModel.requestDelayedGTFSUpdates(3000);
-
- });
-
- } else {
- Log.e(DEBUG_TAG, "PositionsViewModel is null");
- }
- }
-
- @Override
- public void onSaveInstanceState(@NonNull Bundle outState) {
- saveMapState(outState);
-
- super.onSaveInstanceState(outState);
- }
-
- //own methods
-
- /**
- * Switch following the location on and off
- * @param value true if we want to follow location
- */
- public void setLocationFollowing(Boolean value){
- followingLocation = value;
- if(mLocationOverlay==null || getContext() == null || map ==null)
- //nothing else to do
- return;
- if (value){
- mLocationOverlay.enableFollowLocation();
- } else {
- mLocationOverlay.disableFollowLocation();
- }
- }
-
- /**
- * Do all the stuff you need to do on the gui, when parameter is changed to value
- * @param following value
- */
- protected void updateGUIForLocationFollowing(boolean following){
- if (following)
- btFollowMe.setImageResource(R.drawable.ic_follow_me_on);
- else
- btFollowMe.setImageResource(R.drawable.ic_follow_me);
-
- }
-
- /**
- * Build the location overlay. Enable only when
- * a) we know we have the permission
- * b) the location map is set
- */
- private void startLocationOverlay(boolean enableLocation, MapView map){
- if(getActivity()== null) throw new IllegalStateException("Cannot enable LocationOverlay now");
- // Location Overlay
- // from OpenBikeSharing (THANK GOD)
- Log.d(DEBUG_TAG, "Starting position overlay");
- GpsMyLocationProvider imlp = new GpsMyLocationProvider(getActivity().getBaseContext());
- imlp.setLocationUpdateMinDistance(5);
- imlp.setLocationUpdateMinTime(2000);
-
- final LocationOverlay overlay = new LocationOverlay(imlp,map, locationCallbacks);
- if (enableLocation) overlay.enableMyLocation();
- overlay.setOptionsMenuEnabled(true);
-
- //map.getOverlays().add(this.mLocationOverlay);
- this.mLocationOverlay = overlay;
- map.getOverlays().add(mLocationOverlay);
- }
-
- public void startMap(Bundle incoming, Bundle savedInstanceState) {
- //Check that we're attached
- GeneralActivity activity = getActivity() instanceof GeneralActivity ? (GeneralActivity) getActivity() : null;
- if(getContext()==null|| activity==null){
- //we are not attached
- Log.e(DEBUG_TAG, "Calling startMap when not attached");
- return;
- }else{
- Log.d(DEBUG_TAG, "Starting map from scratch");
- }
- //clear previous overlays
- map.getOverlays().clear();
-
-
- //parse incoming bundle
- GeoPoint marker = null;
- String name = null;
- String ID = null;
- String routesStopping = "";
- 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);
- routesStopping = incoming.getString(BUNDLE_ROUTES_STOPPING, "");
- }
-
-
- //ask for location permission
- if(!Permissions.bothLocationPermissionsGranted(activity)){
- if(shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)){
- //TODO: show dialog for permission rationale
- Toast.makeText(activity, R.string.enable_position_message_map, Toast.LENGTH_SHORT).show();
- }
- positionRequestLauncher.launch(Permissions.LOCATION_PERMISSIONS);
-
- }
-
- 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;
- startLocationOverlay(Permissions.bothLocationPermissionsGranted(activity),
- map);
- // set the center point
- if (marker != null) {
- //startPoint = marker;
- mapController.setZoom(POSITION_FOUND_ZOOM);
- setLocationFollowing(false);
- // put the center a little bit off (animate later)
- startPoint = new GeoPoint(marker);
- startPoint.setLatitude(marker.getLatitude()+ utils.angleRawDifferenceFromMeters(20));
- startPoint.setLongitude(marker.getLongitude()-utils.angleRawDifferenceFromMeters(20));
- //don't need to do all the rest since we want to show a point
- } else if (savedInstanceState != null && savedInstanceState.containsKey(MAP_CURRENT_ZOOM_KEY)) {
- mapController.setZoom(savedInstanceState.getDouble(MAP_CURRENT_ZOOM_KEY));
- mapController.setCenter(new GeoPoint(savedInstanceState.getDouble(MAP_CENTER_LAT_KEY),
- savedInstanceState.getDouble(MAP_CENTER_LON_KEY)));
- Log.d(DEBUG_TAG, "Location following from savedInstanceState: "+savedInstanceState.getBoolean(FOLLOWING_LOCAT_KEY));
- setLocationFollowing(savedInstanceState.getBoolean(FOLLOWING_LOCAT_KEY));
- } else {
- Log.d(DEBUG_TAG, "No position found from intent or saved state");
- boolean found = false;
- LocationManager locationManager =
- (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
- //check for permission
- if (locationManager != null && Permissions.bothLocationPermissionsGranted(activity)) {
-
- @SuppressLint("MissingPermission")
- Location userLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
-
- if (userLocation != null) {
- double distan = utils.measuredistanceBetween(userLocation.getLatitude(), userLocation.getLongitude(),
- DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON);
- if (distan < 100_000.0) {
- mapController.setZoom(POSITION_FOUND_ZOOM);
- startPoint = new GeoPoint(userLocation);
- found = true;
- setLocationFollowing(true);
- }
- }
- }
- if(!found){
- startPoint = new GeoPoint(DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON);
- mapController.setZoom(NO_POSITION_ZOOM);
- setLocationFollowing(false);
- }
- }
-
- // set the minimum zoom level
- map.setMinZoomLevel(15.0);
- //add contingency check (shouldn't happen..., but)
-
- if (startPoint != null) {
- mapController.setCenter(startPoint);
- }
-
-
- //add stops overlay
- //map.getOverlays().add(mLocationOverlay);
- map.getOverlays().add(this.stopsFolderOverlay);
-
- Log.d(DEBUG_TAG, "Requesting stops load");
- // This is not necessary, by setting the center we already move
- // the map and we trigger a stop request
- //requestStopsToShow();
- if (marker != null) {
- // make a marker with the info window open for the searched marker
- //TODO: make Stop Bundle-able
- Marker stopMarker = makeMarker(marker, ID , name, routesStopping,true);
- map.getController().animateTo(marker);
- }
- //add the overlays with the bus stops
- if(busPositionsOverlay == null){
- //Log.i(DEBUG_TAG, "Null bus positions overlay,redo");
- busPositionsOverlay = new FolderOverlay();
- }
- startRequestsPositions();
- if(stopsViewModel !=null){
-
- stopsViewModel.getStopsInBoundingBox().observe(getViewLifecycleOwner(),
- this::showStopsMarkers
- );
- } else Log.d(DEBUG_TAG, "Cannot observe new stops in map, stopsViewModel is null");
- map.getOverlays().add(this.busPositionsOverlay);
- //set map as started
- hasMapStartFinished = true;
- }
-
- /**
- * Start a request to load the stops that are in the current view
- * from the database
- */
- private void requestStopsToShow(){
- // get the top, bottom, left and right screen's coordinate
- BoundingBox bb = map.getBoundingBox();
- Log.d(DEBUG_TAG, "Requesting stops in bounding box, stopViewModel is null "+(stopsViewModel==null));
- if(stopsViewModel!=null){
- stopsViewModel.requestStopsInBoundingBox(bb);
- }
- /*double latFrom = bb.getLatSouth();
- double latTo = bb.getLatNorth();
- double lngFrom = bb.getLonWest();
- double lngTo = bb.getLonEast();
- if (stopFetcher!= null && stopFetcher.getStatus()!= AsyncTask.Status.FINISHED)
- stopFetcher.cancel(true);
- stopFetcher = new AsyncStopFetcher(this);
- stopFetcher.execute(
- new AsyncStopFetcher.BoundingBoxLimit(lngFrom,lngTo,latFrom, latTo));
-
- */
- }
-
- private void updateBusMarker(final Marker marker, final LivePositionUpdate posUpdate, @Nullable boolean justCreated){
- GeoPoint position;
- final String updateID = posUpdate.getTripID();
- if(!justCreated){
- position = marker.getPosition();
- if(posUpdate.getLatitude()!=position.getLatitude() || posUpdate.getLongitude()!=position.getLongitude()){
- GeoPoint newpos = new GeoPoint(posUpdate.getLatitude(), posUpdate.getLongitude());
- ObjectAnimator valueAnimator = MarkerUtils.makeMarkerAnimator(
- map, marker, newpos, MarkerUtils.LINEAR_ANIMATION, 1200);
- valueAnimator.setAutoCancel(true);
- tripMarkersAnimators.put(updateID,valueAnimator);
- valueAnimator.start();
- }
- //marker.setPosition(new GeoPoint(posUpdate.getLatitude(), posUpdate.getLongitude()));
- } else {
-
- position = new GeoPoint(posUpdate.getLatitude(), posUpdate.getLongitude());
- marker.setPosition(position);
- }
-
- if(posUpdate.getBearing()!=null)
- marker.setRotation(posUpdate.getBearing()*(-1.f));
- }
-
- private void updateBusPositionsInMap(HashMap> tripsPatterns){
- Log.d(DEBUG_TAG, "Updating positions of the buses");
- //if(busPositionsOverlay == null) busPositionsOverlay = new FolderOverlay();
- final ArrayList noPatternsTrips = new ArrayList<>();
- for(String tripID: tripsPatterns.keySet()) {
- final Pair pair = tripsPatterns.get(tripID);
- if (pair == null) continue;
- final LivePositionUpdate update = pair.getFirst();
- final TripAndPatternWithStops tripWithPatternStops = pair.getSecond();
-
-
- //check if Marker is already created
- if (busPositionMarkersByTrip.containsKey(tripID)){
- //need to change the position of the marker
- final Marker marker = busPositionMarkersByTrip.get(tripID);
- assert marker!=null;
- updateBusMarker(marker, update, false);
- if(marker.getInfoWindow()!=null && marker.getInfoWindow() instanceof BusInfoWindow){
- BusInfoWindow window = (BusInfoWindow) marker.getInfoWindow();
- if(tripWithPatternStops != null) {
- //Log.d(DEBUG_TAG, "Update pattern for trip: "+tripID);
- window.setPatternAndDraw(tripWithPatternStops.getPattern());
- }
-
- }
- } else{
- //marker is not there, need to make it
- if(map==null) Log.e(DEBUG_TAG, "Creating marker with null map, things will explode");
- final Marker marker = new Marker(map);
-
- /*final Drawable mDrawable = DrawableUtils.Companion.getScaledDrawableResources(
- getResources(),
- R.drawable.point_heading_icon,
- R.dimen.map_icons_size, R.dimen.map_icons_size);
-
- */
- //String route = GtfsUtils.getLineNameFromGtfsID(update.getRouteID());
- final Drawable mdraw = ResourcesCompat.getDrawable(getResources(),R.drawable.map_bus_position_icon, null);
- /*final Drawable mdraw = DrawableUtils.Companion.writeOnDrawable(getResources(),
- R.drawable.point_heading_icon,
- R.color.white,
- route,12);
-
- */
- assert mdraw != null;
- //mdraw.setBounds(0,0,28,28);
- marker.setIcon(mdraw);
- if(tripWithPatternStops == null){
- noPatternsTrips.add(tripID);
- }
- MatoPattern markerPattern = null;
- if(tripWithPatternStops != null && tripWithPatternStops.getPattern()!=null)
- markerPattern = tripWithPatternStops.getPattern();
- marker.setInfoWindow(new BusInfoWindow(map, update, markerPattern , false, (pattern) -> { }));
- marker.setInfoWindowAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER);
- marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER);
-
- updateBusMarker(marker, update, true);
- // the overlay is null when it's not attached yet?5
- // cannot recreate it because it becomes null very soon
- // if(busPositionsOverlay == null) busPositionsOverlay = new FolderOverlay();
- //save the marker
- if(busPositionsOverlay!=null) {
- busPositionsOverlay.add(marker);
- busPositionMarkersByTrip.put(tripID, marker);
- }
-
- }
- }
- if(noPatternsTrips.size()>0){
- Log.i(DEBUG_TAG, "These trips have no matching pattern: "+noPatternsTrips);
- }
- }
-
- /**
- * Add stops as Markers on the map
- * @param stops the list of stops that must be included
- */
- protected void showStopsMarkers(List stops){
- if (getContext() == null || stops == null){
- //we are not attached
- return;
- }
- boolean good = true;
-
- for (Stop stop : stops) {
- if (shownStops.contains(stop.ID)){
- continue;
- }
- if(stop.getLongitude()==null || stop.getLatitude()==null)
- continue;
-
- shownStops.add(stop.ID);
- if(!map.isShown()){
- if(good)
- Log.d(DEBUG_TAG, "Need to show stop but map is not shown, probably detached already");
- good = false;
- continue;
- } else if(map.getRepository() == null){
- Log.e(DEBUG_TAG, "Map view repository is null");
- }
- GeoPoint marker = new GeoPoint(stop.getLatitude(), stop.getLongitude());
-
- Marker stopMarker = makeMarker(marker, stop, false);
- stopsFolderOverlay.add(stopMarker);
- if (!map.getOverlays().contains(stopsFolderOverlay)) {
- Log.w(DEBUG_TAG, "Map doesn't have folder overlay");
- }
- good=true;
- }
- //Log.d(DEBUG_TAG,"We have " +stopsFolderOverlay.getItems().size()+" stops in the folderOverlay");
- //force redraw of markers
- map.invalidate();
- }
-
- public Marker makeMarker(GeoPoint geoPoint, Stop stop, boolean isStartMarker){
- return makeMarker(geoPoint,stop.ID,
- stop.getStopDefaultName(),
- stop.routesThatStopHereToString(), isStartMarker);
- }
-
- public Marker makeMarker(GeoPoint geoPoint, String stopID, String stopName,
- String routesStopping, boolean isStartMarker) {
-
- // add a marker
- final Marker marker = new Marker(map);
-
- // set custom info window as info window
- CustomInfoWindow popup = new CustomInfoWindow(map, stopID, stopName, routesStopping,
- responder, R.layout.linedetail_stop_infowindow, R.color.red_darker);
- marker.setInfoWindow(popup);
-
- // make the marker clickable
- marker.setOnMarkerClickListener((thisMarker, mapView) -> {
- if (thisMarker.isInfoWindowOpen()) {
- // on second click
- Log.w(DEBUG_TAG, "Pressed on the click marker");
- } 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_CENTER);
- // add to it an icon
- //marker.setIcon(getResources().getDrawable(R.drawable.bus_marker));
-
- marker.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.bus_stop, ctx.getTheme()));
- // add to it a title
- marker.setTitle(stopName);
- // set the description as the ID
- marker.setSnippet(stopID);
-
- // show popup info window of the searched marker
- if (isStartMarker) {
- marker.showInfoWindow();
- //map.getController().animateTo(marker.getPosition());
- }
-
- return marker;
- }
-
- @Nullable
- @Override
- public View getBaseViewForSnackBar() {
- return coordLayout;
- }
-
-}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MapFragmentKt.kt b/app/src/main/java/it/reyboz/bustorino/fragments/MapFragmentKt.kt
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/fragments/MapFragmentKt.kt
+++ /dev/null
@@ -1,750 +0,0 @@
-/*
- BusTO - Fragments components
- Copyright (C) 2020 Andrea Ugo
- Copyright (C) 2021 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.fragments
-
-import android.Manifest
-import android.animation.ObjectAnimator
-import android.annotation.SuppressLint
-import android.content.Context
-import android.location.LocationManager
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageButton
-import android.widget.Toast
-import androidx.activity.result.ActivityResultCallback
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.coordinatorlayout.widget.CoordinatorLayout
-import androidx.core.content.res.ResourcesCompat
-import androidx.fragment.app.viewModels
-import androidx.preference.PreferenceManager
-import it.reyboz.bustorino.R
-import it.reyboz.bustorino.backend.Stop
-import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate
-import it.reyboz.bustorino.backend.mato.MQTTMatoClient
-import it.reyboz.bustorino.backend.utils
-import it.reyboz.bustorino.data.gtfs.MatoPattern
-import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops
-import it.reyboz.bustorino.map.BusInfoWindow
-import it.reyboz.bustorino.map.CustomInfoWindow
-import it.reyboz.bustorino.map.CustomInfoWindow.TouchResponder
-import it.reyboz.bustorino.map.LocationOverlay
-import it.reyboz.bustorino.map.LocationOverlay.OverlayCallbacks
-import it.reyboz.bustorino.map.MarkerUtils
-import it.reyboz.bustorino.middleware.GeneralActivity
-import it.reyboz.bustorino.util.Permissions
-import it.reyboz.bustorino.viewmodels.LivePositionsViewModel
-import it.reyboz.bustorino.viewmodels.StopsMapViewModel
-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.GeoPoint
-import org.osmdroid.views.MapView
-import org.osmdroid.views.overlay.FolderOverlay
-import org.osmdroid.views.overlay.Marker
-import org.osmdroid.views.overlay.infowindow.InfoWindow
-import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider
-
-open class MapFragmentKt : ScreenBaseFragment() {
- protected var listenerMain: FragmentListenerMain? = null
- private var shownStops: HashSet? = null
- private lateinit var map: MapView
- var ctx: Context? = null
- private lateinit var mLocationOverlay: LocationOverlay
- private lateinit var stopsFolderOverlay: FolderOverlay
- private var savedMapState: Bundle? = null
- protected lateinit var btCenterMap: ImageButton
- protected lateinit var btFollowMe: ImageButton
- protected var coordLayout: CoordinatorLayout? = null
- private var hasMapStartFinished = false
- private var followingLocation = false
-
- //the ViewModel from which we get the stop to display in the map
- private val stopsViewModel: StopsMapViewModel by viewModels()
-
- //private GtfsPositionsViewModel gtfsPosViewModel; //= new ViewModelProvider(this).get(MapViewModel.class);
- private val livePositionsViewModel: LivePositionsViewModel by viewModels()
- private var useMQTTViewModel = true
- private val busPositionMarkersByTrip = HashMap()
- private var busPositionsOverlay: FolderOverlay? = null
- private val tripMarkersAnimators = HashMap()
- protected val responder = TouchResponder { stopID, stopName ->
- if (listenerMain != null) {
- Log.d(DEBUG_TAG, "Asked to show arrivals for stop ID: $stopID")
- listenerMain!!.requestArrivalsForStopID(stopID)
- }
- }
- protected val locationCallbacks: OverlayCallbacks = object : OverlayCallbacks {
- override fun onDisableFollowMyLocation() {
- updateGUIForLocationFollowing(false)
- followingLocation = false
- }
-
- override fun onEnableFollowMyLocation() {
- updateGUIForLocationFollowing(true)
- followingLocation = true
- }
- }
- private val positionRequestLauncher =
- registerForActivityResult, Map>(
- ActivityResultContracts.RequestMultiplePermissions(),
- ActivityResultCallback { result ->
- if (result == null) {
- Log.w(DEBUG_TAG, "Got asked permission but request is null, doing nothing?")
- } else if (java.lang.Boolean.TRUE == result[Manifest.permission.ACCESS_COARSE_LOCATION]
- && java.lang.Boolean.TRUE == result[Manifest.permission.ACCESS_FINE_LOCATION]) {
- // We can use the position, restart location overlay
- map.overlays.remove(mLocationOverlay)
- startLocationOverlay(true, map)
- if (context == null || requireContext().getSystemService(Context.LOCATION_SERVICE) == null)
- return@ActivityResultCallback ///@registerForActivityResult
- val locationManager =
- requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
- @SuppressLint("MissingPermission") val userLocation =
- locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
- if (userLocation != null) {
- map!!.controller.setZoom(POSITION_FOUND_ZOOM)
- val startPoint = GeoPoint(userLocation)
- setLocationFollowing(true)
- map!!.controller.setCenter(startPoint)
- }
- } else Log.w(DEBUG_TAG, "No location permission")
- })
-
- //public static MapFragment getInstance(@NonNull Stop stop){
- // return getInstance(stop.getLatitude(), stop.getLongitude(), stop.getStopDisplayName(), stop.ID);
- //}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- //use the same layout as the activity
- val root = inflater.inflate(R.layout.fragment_map, container, false)
- val context = requireContext()
- ctx = context.applicationContext
- Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(context))
- map = root.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 = root.findViewById(R.id.centerMapImageButton)
- btFollowMe = root.findViewById(R.id.followUserImageButton)
- coordLayout = root.findViewById(R.id.coord_layout)
-
- //setup FolderOverlay
- stopsFolderOverlay = FolderOverlay()
- //setup Bus Markers Overlay
- busPositionsOverlay = FolderOverlay()
- //reset shown bus updates
- busPositionMarkersByTrip.clear()
- tripMarkersAnimators.clear()
- //set map not done
- hasMapStartFinished = false
- val keySourcePositions = getString(R.string.pref_positions_source)
- useMQTTViewModel = PreferenceManager.getDefaultSharedPreferences(requireContext())
- .getString(keySourcePositions, SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE)
- .contentEquals(SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE)
-
-
- //Start map from bundle
- if (savedInstanceState != null) startMap(arguments, savedInstanceState) else startMap(
- arguments, savedMapState
- )
- //set listeners
- map.addMapListener(DelayedMapListener(object : MapListener {
- override fun onScroll(paramScrollEvent: ScrollEvent): Boolean {
- requestStopsToShow()
- //Log.d(DEBUG_TAG, "Scrolling");
- //if (moveTriggeredByCode) moveTriggeredByCode =false;
- //else setLocationFollowing(false);
- return true
- }
-
- override fun onZoom(event: ZoomEvent): Boolean {
- requestStopsToShow()
- return true
- }
- }))
- btCenterMap.setOnClickListener(View.OnClickListener { v: View? ->
- //Log.i(TAG, "centerMap clicked ");
- if (Permissions.bothLocationPermissionsGranted(context)) {
- val myPosition = mLocationOverlay!!.myLocation
- map.getController().animateTo(myPosition)
- } else Toast.makeText(context, R.string.enable_position_message_map, Toast.LENGTH_SHORT)
- .show()
- })
- btFollowMe.setOnClickListener(View.OnClickListener { v: View? ->
- //Log.i(TAG, "btFollowMe clicked ");
- if (Permissions.bothLocationPermissionsGranted(context)) setLocationFollowing(!followingLocation) else Toast.makeText(
- context, R.string.enable_position_message_map, Toast.LENGTH_SHORT
- )
- .show()
- })
- return root
- }
-
- override fun onAttach(context: Context) {
- super.onAttach(context)
- listenerMain = if (context is FragmentListenerMain) {
- context
- } else {
- throw RuntimeException(
- context.toString()
- + " must implement FragmentListenerMain"
- )
- }
- }
-
- override fun onDetach() {
- super.onDetach()
- listenerMain = null
-
- Log.w(DEBUG_TAG, "Fragment detached")
- }
-
- override fun onPause() {
- super.onPause()
- Log.w(DEBUG_TAG, "On pause called mapfrag")
- saveMapState()
- for (animator in tripMarkersAnimators.values) {
- if (animator != null && animator.isRunning) {
- animator.cancel()
- }
- }
- tripMarkersAnimators.clear()
- if (useMQTTViewModel) livePositionsViewModel.stopMatoUpdates()
- }
-
- /**
- * Save the map state inside the fragment
- * (calls saveMapState(bundle))
- */
- private fun saveMapState() {
- savedMapState = Bundle()
- saveMapState(savedMapState!!)
- }
-
- /**
- * Save the state of the map to restore it to a later time
- * @param bundle the bundle in which to save the data
- */
- private fun saveMapState(bundle: Bundle) {
- Log.d(DEBUG_TAG, "Saving state, location following: $followingLocation")
- bundle.putBoolean(FOLLOWING_LOCAT_KEY, followingLocation)
- if (map == null) {
- //The map is null, it can happen?
- Log.e(DEBUG_TAG, "Cannot save map center, map is null")
- return
- }
- val loc = map!!.mapCenter
- bundle.putDouble(MAP_CENTER_LAT_KEY, loc.latitude)
- bundle.putDouble(MAP_CENTER_LON_KEY, loc.longitude)
- bundle.putDouble(MAP_CURRENT_ZOOM_KEY, map!!.zoomLevelDouble)
- }
-
- override fun onResume() {
- super.onResume()
- //TODO: cleanup duplicate code (maybe merging the positions classes?)
- if (listenerMain != null) listenerMain!!.readyGUIfor(FragmentKind.MAP)
- /// choose which to use
- val keySourcePositions = getString(R.string.pref_positions_source)
- useMQTTViewModel = PreferenceManager.getDefaultSharedPreferences(requireContext())
- .getString(keySourcePositions, SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE)
- .contentEquals(
- SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE
- )
- //gtfsPosViewModel.requestUpdates();
- if (useMQTTViewModel) livePositionsViewModel.requestMatoPosUpdates(MQTTMatoClient.LINES_ALL)
- else livePositionsViewModel.requestGTFSUpdates()
- //mapViewModel.testCascade();
- livePositionsViewModel.isLastWorkResultGood.observe(this) { d: Boolean ->
- Log.d(
- DEBUG_TAG, "Last trip download result is $d"
- )
- }
- livePositionsViewModel.tripsGtfsIDsToQuery.observe(this) { dat: List ->
- Log.i(DEBUG_TAG, "Have these trips IDs missing from the DB, to be queried: $dat")
- livePositionsViewModel.downloadTripsFromMato(dat)
- }
-
- //rerequest stop
- stopsViewModel!!.requestStopsInBoundingBox(map!!.boundingBox)
- }
-
- private fun startRequestsPositions() {
- if (livePositionsViewModel != null) {
- //should always be the case
- livePositionsViewModel!!.updatesWithTripAndPatterns.observe(viewLifecycleOwner) { data: HashMap> ->
- Log.d(
- DEBUG_TAG,
- "Have " + data.size + " trip updates, has Map start finished: " + hasMapStartFinished
- )
- if (hasMapStartFinished) updateBusPositionsInMap(data)
- if (!isDetached && !useMQTTViewModel) livePositionsViewModel!!.requestDelayedGTFSUpdates(
- 3000
- )
- }
- } else {
- Log.e(DEBUG_TAG, "PositionsViewModel is null")
- }
- }
-
- override fun onSaveInstanceState(outState: Bundle) {
- saveMapState(outState)
- super.onSaveInstanceState(outState)
- }
- //own methods
- /**
- * Switch following the location on and off
- * @param value true if we want to follow location
- */
- fun setLocationFollowing(value: Boolean) {
- followingLocation = value
- if (mLocationOverlay == null || context == null || map == null) //nothing else to do
- return
- if (value) {
- mLocationOverlay!!.enableFollowLocation()
- } else {
- mLocationOverlay!!.disableFollowLocation()
- }
- }
-
- /**
- * Do all the stuff you need to do on the gui, when parameter is changed to value
- * @param following value
- */
- protected fun updateGUIForLocationFollowing(following: Boolean) {
- if (following) btFollowMe!!.setImageResource(R.drawable.ic_follow_me_on) else btFollowMe!!.setImageResource(
- R.drawable.ic_follow_me
- )
- }
-
- /**
- * Build the location overlay. Enable only when
- * a) we know we have the permission
- * b) the location map is set
- */
- private fun startLocationOverlay(enableLocation: Boolean, map: MapView?) {
- checkNotNull(activity) { "Cannot enable LocationOverlay now" }
- // Location Overlay
- // from OpenBikeSharing (THANK GOD)
- Log.d(DEBUG_TAG, "Starting position overlay")
- val imlp = GpsMyLocationProvider(requireActivity().baseContext)
- imlp.locationUpdateMinDistance = 5f
- imlp.locationUpdateMinTime = 2000
- val overlay = LocationOverlay(imlp, map, locationCallbacks)
- if (enableLocation) overlay.enableMyLocation()
- overlay.isOptionsMenuEnabled = true
-
- //map.getOverlays().add(this.mLocationOverlay);
- mLocationOverlay = overlay
- map!!.overlays.add(mLocationOverlay)
- }
-
- fun startMap(incoming: Bundle?, savedInstanceState: Bundle?) {
- //Check that we're attached
- val activity = if (activity is GeneralActivity) activity as GeneralActivity? else null
- if (context == null || activity == null) {
- //we are not attached
- Log.e(DEBUG_TAG, "Calling startMap when not attached")
- return
- } else {
- Log.d(DEBUG_TAG, "Starting map from scratch")
- }
- //clear previous overlays
- map!!.overlays.clear()
-
-
- //parse incoming bundle
- var marker: GeoPoint? = null
- var name: String? = null
- var ID: String? = null
- var routesStopping: String? = ""
- if (incoming != null) {
- val lat = incoming.getDouble(BUNDLE_LATIT)
- val lon = incoming.getDouble(BUNDLE_LONGIT)
- marker = GeoPoint(lat, lon)
- name = incoming.getString(BUNDLE_NAME)
- ID = incoming.getString(BUNDLE_ID)
- routesStopping = incoming.getString(BUNDLE_ROUTES_STOPPING, "")
- }
-
-
- //ask for location permission
- if (!Permissions.bothLocationPermissionsGranted(activity)) {
- if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
- //TODO: show dialog for permission rationale
- Toast.makeText(activity, R.string.enable_position_message_map, Toast.LENGTH_SHORT)
- .show()
- }
- positionRequestLauncher.launch(Permissions.LOCATION_PERMISSIONS)
- }
- shownStops = HashSet()
- // move the map on the marker position or on a default view point: Turin, Piazza Castello
- // and set the start zoom
- val mapController = map!!.controller
- var startPoint: GeoPoint? = null
- startLocationOverlay(
- Permissions.bothLocationPermissionsGranted(activity),
- map
- )
- // set the center point
- if (marker != null) {
- //startPoint = marker;
- mapController.setZoom(POSITION_FOUND_ZOOM)
- setLocationFollowing(false)
- // put the center a little bit off (animate later)
- startPoint = GeoPoint(marker)
- startPoint.latitude = marker.latitude + utils.angleRawDifferenceFromMeters(20.0)
- startPoint.longitude = marker.longitude - utils.angleRawDifferenceFromMeters(20.0)
- //don't need to do all the rest since we want to show a point
- } else if (savedInstanceState != null && savedInstanceState.containsKey(MAP_CURRENT_ZOOM_KEY)) {
- mapController.setZoom(savedInstanceState.getDouble(MAP_CURRENT_ZOOM_KEY))
- mapController.setCenter(
- GeoPoint(
- savedInstanceState.getDouble(MAP_CENTER_LAT_KEY),
- savedInstanceState.getDouble(MAP_CENTER_LON_KEY)
- )
- )
- Log.d(
- DEBUG_TAG,
- "Location following from savedInstanceState: " + savedInstanceState.getBoolean(
- FOLLOWING_LOCAT_KEY
- )
- )
- setLocationFollowing(savedInstanceState.getBoolean(FOLLOWING_LOCAT_KEY))
- } else {
- Log.d(DEBUG_TAG, "No position found from intent or saved state")
- var found = false
- val locationManager =
- requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
- //check for permission
- if (Permissions.bothLocationPermissionsGranted(activity)) {
- @SuppressLint("MissingPermission") val userLocation =
- locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
- if (userLocation != null) {
- val distan = utils.measuredistanceBetween(
- userLocation.latitude, userLocation.longitude,
- DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON
- )
- if (distan < 100000.0) {
- mapController.setZoom(POSITION_FOUND_ZOOM)
- startPoint = GeoPoint(userLocation)
- found = true
- setLocationFollowing(true)
- }
- }
- }
- if (!found) {
- startPoint = GeoPoint(DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON)
- mapController.setZoom(NO_POSITION_ZOOM)
- setLocationFollowing(false)
- }
- }
-
- // set the minimum zoom level
- map!!.minZoomLevel = 15.0
- //add contingency check (shouldn't happen..., but)
- if (startPoint != null) {
- mapController.setCenter(startPoint)
- }
-
-
- //add stops overlay
- //map.getOverlays().add(mLocationOverlay);
- map!!.overlays.add(stopsFolderOverlay)
- Log.d(DEBUG_TAG, "Requesting stops load")
- // This is not necessary, by setting the center we already move
- // the map and we trigger a stop request
- //requestStopsToShow();
- if (marker != null) {
- // make a marker with the info window open for the searched marker
- //TODO: make Stop Bundle-able
- val stopMarker = makeMarker(marker, ID, name, routesStopping, true)
- map!!.controller.animateTo(marker)
- }
- //add the overlays with the bus stops
- if (busPositionsOverlay == null) {
- //Log.i(DEBUG_TAG, "Null bus positions overlay,redo");
- busPositionsOverlay = FolderOverlay()
- }
- startRequestsPositions()
- if (stopsViewModel != null) {
- stopsViewModel!!.stopsInBoundingBox.observe(viewLifecycleOwner) { stops: List? ->
- showStopsMarkers(
- stops
- )
- }
- } else Log.d(DEBUG_TAG, "Cannot observe new stops in map, stopsViewModel is null")
- map!!.overlays.add(busPositionsOverlay)
- //set map as started
- hasMapStartFinished = true
- }
-
- /**
- * Start a request to load the stops that are in the current view
- * from the database
- */
- private fun requestStopsToShow() {
- // get the top, bottom, left and right screen's coordinate
- val bb = map!!.boundingBox
- Log.d(
- DEBUG_TAG,
- "Requesting stops in bounding box, stopViewModel is null " + (false)
- )
- stopsViewModel.requestStopsInBoundingBox(bb)
-
- }
-
- private fun updateBusMarker(
- marker: Marker?,
- posUpdate: LivePositionUpdate,
- justCreated: Boolean
- ) {
- val position: GeoPoint
- val updateID = posUpdate.tripID
- if (!justCreated) {
- position = marker!!.position
- if (posUpdate.latitude != position.latitude || posUpdate.longitude != position.longitude) {
- val newpos = GeoPoint(posUpdate.latitude, posUpdate.longitude)
- val valueAnimator = MarkerUtils.makeMarkerAnimator(
- map, marker, newpos, MarkerUtils.LINEAR_ANIMATION, 1200
- )
- valueAnimator.setAutoCancel(true)
- tripMarkersAnimators[updateID] = valueAnimator
- valueAnimator.start()
- }
- //marker.setPosition(new GeoPoint(posUpdate.getLatitude(), posUpdate.getLongitude()));
- } else {
- position = GeoPoint(posUpdate.latitude, posUpdate.longitude)
- marker!!.position = position
- }
- marker.rotation = posUpdate.bearing?.let { it*-1f } ?: 0.0f
- }
-
- private fun updateBusPositionsInMap(tripsPatterns: HashMap>) {
- Log.d(DEBUG_TAG, "Updating positions of the buses")
- //if(busPositionsOverlay == null) busPositionsOverlay = new FolderOverlay();
- val noPatternsTrips = ArrayList()
- for (tripID in tripsPatterns.keys) {
- val (update, tripWithPatternStops) = tripsPatterns[tripID] ?: continue
-
-
- //check if Marker is already created
- if (busPositionMarkersByTrip.containsKey(tripID)) {
- //need to change the position of the marker
- val marker = busPositionMarkersByTrip[tripID]!!
- updateBusMarker(marker, update, false)
- if (marker.infoWindow != null && marker.infoWindow is BusInfoWindow) {
- val window = marker.infoWindow as BusInfoWindow
- if (tripWithPatternStops != null) {
- //Log.d(DEBUG_TAG, "Update pattern for trip: "+tripID);
- window.setPatternAndDraw(tripWithPatternStops.pattern)
- }
- }
- } else {
- //marker is not there, need to make it
- val marker = Marker(map)
-
- /*final Drawable mDrawable = DrawableUtils.Companion.getScaledDrawableResources(
- getResources(),
- R.drawable.point_heading_icon,
- R.dimen.map_icons_size, R.dimen.map_icons_size);
-
- */
- //String route = GtfsUtils.getLineNameFromGtfsID(update.getRouteID());
- val mdraw =
- ResourcesCompat.getDrawable(resources, R.drawable.map_bus_position_icon, null)!!
- //mdraw.setBounds(0,0,28,28);
- marker.icon = mdraw
- if (tripWithPatternStops == null) {
- noPatternsTrips.add(tripID)
- }
- var markerPattern: MatoPattern? = null
- if (tripWithPatternStops != null && tripWithPatternStops.pattern != null) markerPattern =
- tripWithPatternStops.pattern
- marker.infoWindow =
- BusInfoWindow(map!!, update, markerPattern, false) { pattern: MatoPattern? -> }
- marker.setInfoWindowAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
- marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
- updateBusMarker(marker, update, true)
- // the overlay is null when it's not attached yet?5
- // cannot recreate it because it becomes null very soon
- // if(busPositionsOverlay == null) busPositionsOverlay = new FolderOverlay();
- //save the marker
- if (busPositionsOverlay != null) {
- busPositionsOverlay!!.add(marker)
- busPositionMarkersByTrip[tripID] = marker
- }
- }
- }
- if (noPatternsTrips.size > 0) {
- Log.i(DEBUG_TAG, "These trips have no matching pattern: $noPatternsTrips")
- }
- }
-
- /**
- * Add stops as Markers on the map
- * @param stops the list of stops that must be included
- */
- protected fun showStopsMarkers(stops: List?) {
- if (context == null || stops == null) {
- //we are not attached
- return
- }
- var good = true
- for (stop in stops) {
- if (shownStops!!.contains(stop.ID)) {
- continue
- }
- if (stop.longitude == null || stop.latitude == null) continue
- shownStops!!.add(stop.ID)
- if (!map!!.isShown) {
- if (good) Log.d(
- DEBUG_TAG,
- "Need to show stop but map is not shown, probably detached already"
- )
- good = false
- continue
- } else if (map!!.repository == null) {
- Log.e(DEBUG_TAG, "Map view repository is null")
- }
- val marker = GeoPoint(stop.latitude!!, stop.longitude!!)
- val stopMarker = makeMarker(marker, stop, false)
- stopsFolderOverlay!!.add(stopMarker)
- if (!map!!.overlays.contains(stopsFolderOverlay)) {
- Log.w(DEBUG_TAG, "Map doesn't have folder overlay")
- }
- good = true
- }
- //Log.d(DEBUG_TAG,"We have " +stopsFolderOverlay.getItems().size()+" stops in the folderOverlay");
- //force redraw of markers
- map!!.invalidate()
- }
-
- fun makeMarker(geoPoint: GeoPoint?, stop: Stop, isStartMarker: Boolean): Marker {
- return makeMarker(
- geoPoint, stop.ID,
- stop.stopDefaultName,
- stop.routesThatStopHereToString(), isStartMarker
- )
- }
-
- fun makeMarker(
- geoPoint: GeoPoint?, stopID: String?, stopName: String?,
- routesStopping: String?, isStartMarker: Boolean
- ): Marker {
-
- // add a marker
- val marker = Marker(map)
-
- // set custom info window as info window
- val popup = CustomInfoWindow(
- map, stopID, stopName, routesStopping,
- responder, R.layout.linedetail_stop_infowindow, R.color.red_darker
- )
- marker.infoWindow = popup
-
- // make the marker clickable
- marker.setOnMarkerClickListener { thisMarker: Marker, mapView: MapView? ->
- if (thisMarker.isInfoWindowOpen) {
- // on second click
- Log.w(DEBUG_TAG, "Pressed on the click marker")
- } 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!!.controller.animateTo(thisMarker.position)
- }
- true
- }
-
- // set its position
- marker.position = geoPoint
- marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
- // add to it an icon
- //marker.setIcon(getResources().getDrawable(R.drawable.bus_marker));
- marker.icon = ResourcesCompat.getDrawable(resources, R.drawable.bus_stop, ctx!!.theme)
- // add to it a title
- marker.title = stopName
- // set the description as the ID
- marker.snippet = stopID
-
- // show popup info window of the searched marker
- if (isStartMarker) {
- marker.showInfoWindow()
- //map.getController().animateTo(marker.getPosition());
- }
- return marker
- }
-
- override fun getBaseViewForSnackBar(): View? {
- return coordLayout
- }
-
- companion object {
- //private static final String TAG = "Busto-MapActivity";
- private const val MAP_CURRENT_ZOOM_KEY = "map-current-zoom"
- private const val MAP_CENTER_LAT_KEY = "map-center-lat"
- private const val MAP_CENTER_LON_KEY = "map-center-lon"
- private const val FOLLOWING_LOCAT_KEY = "following"
- const val BUNDLE_LATIT = "lat"
- const val BUNDLE_LONGIT = "lon"
- const val BUNDLE_NAME = "name"
- const val BUNDLE_ID = "ID"
- const val BUNDLE_ROUTES_STOPPING = "routesStopping"
- const val FRAGMENT_TAG = "BusTOMapFragment"
- private const val DEFAULT_CENTER_LAT = 45.0708
- private const val DEFAULT_CENTER_LON = 7.6858
- private const val POSITION_FOUND_ZOOM = 18.3
- const val NO_POSITION_ZOOM = 17.1
- private const val DEBUG_TAG = FRAGMENT_TAG
-
- @JvmStatic
- fun getInstance(): MapFragmentKt {
- return MapFragmentKt()
- }
- @JvmStatic
- fun getInstance(stop: Stop): MapFragmentKt {
- val fragment = MapFragmentKt()
- val args = Bundle()
- args.putDouble(MapFragment.BUNDLE_LATIT, stop.latitude!!)
- args.putDouble(MapFragment.BUNDLE_LONGIT, stop.longitude!!)
- args.putString(MapFragment.BUNDLE_NAME, stop.stopDisplayName)
- args.putString(MapFragment.BUNDLE_ID, stop.ID)
- args.putString(MapFragment.BUNDLE_ROUTES_STOPPING, stop.routesThatStopHereToString())
- fragment.arguments = args
-
- return fragment
- }
- }
-}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt
--- a/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt
@@ -7,12 +7,10 @@
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
-import android.content.Intent
import android.graphics.Color
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
-import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
@@ -32,7 +30,6 @@
import androidx.fragment.app.viewModels
import androidx.preference.PreferenceManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.gson.Gson
import com.google.gson.JsonObject
import it.reyboz.bustorino.R
import it.reyboz.bustorino.backend.Stop
@@ -42,12 +39,11 @@
import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops
import it.reyboz.bustorino.fragments.SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE
import it.reyboz.bustorino.map.MapLibreUtils
-import it.reyboz.bustorino.map.Styles
+import it.reyboz.bustorino.map.MapLibreStyles
import it.reyboz.bustorino.util.Permissions
import it.reyboz.bustorino.util.ViewUtils
import it.reyboz.bustorino.viewmodels.LivePositionsViewModel
import it.reyboz.bustorino.viewmodels.StopsMapViewModel
-import org.maplibre.android.MapLibre
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.camera.CameraUpdateFactory
import org.maplibre.android.geometry.LatLng
@@ -56,14 +52,9 @@
import org.maplibre.android.location.LocationComponentOptions
import org.maplibre.android.location.modes.CameraMode
import org.maplibre.android.maps.MapLibreMap
-import org.maplibre.android.maps.MapView
-import org.maplibre.android.maps.OnMapReadyCallback
import org.maplibre.android.maps.Style
import org.maplibre.android.plugins.annotation.Symbol
-import org.maplibre.android.plugins.annotation.SymbolManager
-import org.maplibre.android.plugins.annotation.SymbolOptions
import org.maplibre.android.style.expressions.Expression
-import org.maplibre.android.style.layers.Property
import org.maplibre.android.style.layers.Property.*
import org.maplibre.android.style.layers.PropertyFactory
import org.maplibre.android.style.layers.SymbolLayer
@@ -289,7 +280,7 @@
override fun onMapReady(mapReady: MapLibreMap) {
this.map = mapReady
val context = requireContext()
- val mjson = Styles.getJsonStyleFromAsset(context, PreferencesHolder.getMapLibreStyleFile(context))
+ val mjson = MapLibreStyles.getJsonStyleFromAsset(context, PreferencesHolder.getMapLibreStyleFile(context))
//ViewUtils.loadJsonFromAsset(requireContext(),"map_style_good.json")
activity?.run {
@@ -1125,6 +1116,8 @@
private const val DEBUG_TAG = "BusTO-MapLibreFrag"
private const val STOP_ACTIVE_IMG = "Stop-active"
+ const val FRAGMENT_TAG = "BusTOMapFragment"
+
private const val LOCATION_PERMISSION_REQUEST_CODE = 981202
/**
diff --git a/app/src/main/java/it/reyboz/bustorino/map/BusInfoWindow.kt b/app/src/main/java/it/reyboz/bustorino/map/BusInfoWindow.kt
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/map/BusInfoWindow.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- BusTO - Map components
- Copyright (C) 2023 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.map
-
-import android.annotation.SuppressLint
-import android.view.View.*
-import android.widget.ImageView
-import android.widget.TextView
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.core.view.marginEnd
-import it.reyboz.bustorino.R
-import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate
-import it.reyboz.bustorino.backend.gtfs.GtfsUtils
-import it.reyboz.bustorino.backend.utils
-import it.reyboz.bustorino.data.gtfs.MatoPattern
-import org.osmdroid.views.MapView
-import org.osmdroid.views.overlay.infowindow.BasicInfoWindow
-
-@SuppressLint("ClickableViewAccessibility")
-class BusInfoWindow(map: MapView,
- private val routeName: String,
- private val vehicleLabel: String,
- var pattern: MatoPattern?,
- val showClose: Boolean,
- private val touchUp: onTouchUp
- ):
- BasicInfoWindow(R.layout.bus_info_window,map) {
-
- init {
- mView.setOnTouchListener { view, motionEvent ->
- touchUp.onActionUp(pattern)
- close()
- //mView.performClick()
- true
-
- }
- }
- constructor(map: MapView, update: LivePositionUpdate, pattern: MatoPattern?, showClose: Boolean, touchUp: onTouchUp, ):
- this(map,
- GtfsUtils.getLineNameFromGtfsID(update.routeID),
- update.vehicle,
- pattern,
- showClose,
- touchUp
- )
-
-
- override fun onOpen(item: Any?) {
- // super.onOpen(item)
- val titleView = mView.findViewById(R.id.businfo_title)
- val descrView = mView.findViewById(R.id.businfo_description)
- val subdescrView = mView.findViewById(R.id.businfo_subdescription)
-
- val iconClose = mView.findViewById(R.id.closeIcon)
-
- //val nameRoute = GtfsUtils.getLineNameFromGtfsID(update.lineGtfsId)
-
- titleView.text = (mView.resources.getString(R.string.line_fill, routeName)
- )
- subdescrView.text = vehicleLabel
- //mView.resources.getString(R.string.vehicle_fill, vehicleLabel)
-
-
- if(pattern!=null){
- descrView.text = pattern!!.headsign
- descrView.visibility = VISIBLE
- } else{
- descrView.visibility = GONE
- }
- if(!showClose){
- iconClose.visibility = GONE
- val ctx = titleView.context
- val layPars = (titleView.layoutParams as ConstraintLayout.LayoutParams).apply {
- marginStart= 0 //utils.convertDipToPixelsInt(ctx, 8.0)//8.dpToPixels()
- topMargin=utils.convertDipToPixelsInt(ctx, 4.0)
- marginEnd=0
- bottomMargin=0
- }
- //titleView.layoutParams = layPars
- }
- }
-
- fun setPatternAndDraw(pattern: MatoPattern?){
- if(pattern==null){
- return
- }
- this.pattern = pattern
- if(isOpen){
- onOpen(pattern)
- }
- }
-
- fun interface onTouchUp{
- fun onActionUp(pattern: MatoPattern?)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/map/BusPositionUtils.kt b/app/src/main/java/it/reyboz/bustorino/map/BusPositionUtils.kt
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/map/BusPositionUtils.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package it.reyboz.bustorino.map
-
-import android.animation.ObjectAnimator
-import android.util.Log
-import androidx.core.content.res.ResourcesCompat
-import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate
-import it.reyboz.bustorino.data.gtfs.MatoPattern
-import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops
-import org.osmdroid.util.GeoPoint
-import org.osmdroid.views.MapView
-import org.osmdroid.views.overlay.Marker
-
-class BusPositionUtils {
- companion object{
- @JvmStatic
- public fun updateBusPositionMarker(map: MapView, marker: Marker?, posUpdate: LivePositionUpdate,
- tripMarkersAnimators: HashMap,
- justCreated: Boolean) {
- val position: GeoPoint
- val updateID = posUpdate.tripID
- if (!justCreated) {
- position = marker!!.position
- if (posUpdate.latitude != position.latitude || posUpdate.longitude != position.longitude) {
- val newpos = GeoPoint(posUpdate.latitude, posUpdate.longitude)
- val valueAnimator = MarkerUtils.makeMarkerAnimator(
- map, marker, newpos, MarkerUtils.LINEAR_ANIMATION, 1200
- )
- valueAnimator.setAutoCancel(true)
- tripMarkersAnimators.put(updateID, valueAnimator)
- valueAnimator.start()
- }
- //marker.setPosition(new GeoPoint(posUpdate.getLatitude(), posUpdate.getLongitude()));
- } else {
- position = GeoPoint(posUpdate.latitude, posUpdate.longitude)
- marker!!.position = position
- }
- //if (posUpdate.bearing != null) marker.rotation = posUpdate.bearing * -1f
- marker.rotation = posUpdate.bearing?.let { it*-1f } ?: 0.0f
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/map/CustomInfoWindow.java b/app/src/main/java/it/reyboz/bustorino/map/CustomInfoWindow.java
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/map/CustomInfoWindow.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- BusTO - Map components
- Copyright (C) 2020 Andrea Ugo
- Copyright (C) 2021 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.map;
-
-import android.annotation.SuppressLint;
-import android.os.Build;
-import android.view.MotionEvent;
-import android.view.View;
-
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import androidx.core.content.ContextCompat;
-import org.osmdroid.views.MapView;
-import org.osmdroid.views.overlay.infowindow.BasicInfoWindow;
-
-import it.reyboz.bustorino.R;
-
-public class CustomInfoWindow extends BasicInfoWindow {
- //TODO: Make the action on the Click customizable
- private final TouchResponder touchResponder;
- private final String stopID, name, routesStopping;
-
- private final int colorResID;
- //final DisplayMetrics metrics;
-
- @Override
- public void onOpen(Object item) {
- super.onOpen(item);
- TextView descr_textView = mView.findViewById(R.id.bubble_description);
- CharSequence text = descr_textView.getText();
- TextView titleTV = mView.findViewById(R.id.bubble_title);
- titleTV.setTextColor(ContextCompat.getColor(mView.getContext(),colorResID));
- //Log.d("BusTO-MapInfoWindow", "Descrip: "+text+", title "+(titleTV==null? "null": titleTV.getText()));
-
- if (text==null || !text.toString().isEmpty()){
- descr_textView.setVisibility(View.VISIBLE);
- } else
- descr_textView.setVisibility(View.GONE);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- mView.setElevation(3.2f);
- }
- TextView subDescriptTextView = mView.findViewById(R.id.bubble_subdescription);
- if (routesStopping!=null && !routesStopping.isEmpty()){
- subDescriptTextView.setText(routesStopping);
- subDescriptTextView.setVisibility(View.VISIBLE);
- }
-
- //check if there is a close image
- ImageView image = mView.findViewById(R.id.closeIcon);
- if (image != null) {
- image.setOnClickListener( view -> close());
- }
-
- }
- public CustomInfoWindow(MapView mapView, String stopID, String name, String routesStopping,
- TouchResponder responder){
-
- this(mapView, stopID, name, routesStopping, responder,R.layout.map_popup, R.color.red_darker);
- }
-
- @SuppressLint("ClickableViewAccessibility")
- public CustomInfoWindow(MapView mapView,
- String stopID,
- String name,
- String routesStopping,
- TouchResponder responder,
- int layoutId,
- int colorResId) {
- // get the personalized layout
- super(layoutId, mapView);
- touchResponder =responder;
- this.stopID = stopID;
- this.name = name;
- this.routesStopping = routesStopping;
- colorResID = colorResId;
-
- //metrics = Resources.getSystem().getDisplayMetrics();
-
- // make clickable
- mView.setOnTouchListener((View v, MotionEvent e) -> {
- if (e.getAction() == MotionEvent.ACTION_UP) {
- // on click
- touchResponder.onActionUp(this.stopID, this.name);
- }
- return true;
- });
-
-
- }
-
- public interface TouchResponder{
- /**
- * React to a click on the stop View
- * @param stopID the stop id
- * @param stopName the stop name
- */
- void onActionUp(@NonNull String stopID, @Nullable String stopName);
- }
-}
diff --git a/app/src/main/java/it/reyboz/bustorino/map/GeoPointInterpolator.java b/app/src/main/java/it/reyboz/bustorino/map/LatLngInterpolator.java
rename from app/src/main/java/it/reyboz/bustorino/map/GeoPointInterpolator.java
rename to app/src/main/java/it/reyboz/bustorino/map/LatLngInterpolator.java
--- a/app/src/main/java/it/reyboz/bustorino/map/GeoPointInterpolator.java
+++ b/app/src/main/java/it/reyboz/bustorino/map/LatLngInterpolator.java
@@ -3,7 +3,8 @@
/* Copyright 2013 Google Inc.
Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0.html */
-import org.osmdroid.util.GeoPoint;
+
+import org.maplibre.android.geometry.LatLng;
import static java.lang.Math.asin;
import static java.lang.Math.atan2;
@@ -14,21 +15,21 @@
import static java.lang.Math.toDegrees;
import static java.lang.Math.toRadians;
-public interface GeoPointInterpolator {
- public GeoPoint interpolate(float fraction, GeoPoint a, GeoPoint b);
+public interface LatLngInterpolator {
+ public LatLng interpolate(float fraction, LatLng a, LatLng b);
- public class Linear implements GeoPointInterpolator {
+ public class Linear implements LatLngInterpolator {
@Override
- public GeoPoint interpolate(float fraction, GeoPoint a, GeoPoint b) {
+ public LatLng interpolate(float fraction, LatLng a, LatLng b) {
double lat = (b.getLatitude() - a.getLatitude()) * fraction + a.getLatitude();
double lng = (b.getLongitude() - a.getLongitude()) * fraction + a.getLongitude();
- return new GeoPoint(lat, lng);
+ return new LatLng(lat, lng);
}
}
- public class LinearFixed implements GeoPointInterpolator {
+ public class LinearFixed implements LatLngInterpolator {
@Override
- public GeoPoint interpolate(float fraction, GeoPoint a, GeoPoint b) {
+ public LatLng interpolate(float fraction, LatLng a, LatLng b) {
double lat = (b.getLatitude() - a.getLatitude()) * fraction + a.getLatitude();
double lngDelta = b.getLongitude() - a.getLongitude();
@@ -37,15 +38,15 @@
lngDelta -= Math.signum(lngDelta) * 360;
}
double lng = lngDelta * fraction + a.getLongitude();
- return new GeoPoint(lat, lng);
+ return new LatLng(lat, lng);
}
}
- public class Spherical implements GeoPointInterpolator {
+ public class Spherical implements LatLngInterpolator {
/* From github.com/googlemaps/android-maps-utils */
@Override
- public GeoPoint interpolate(float fraction, GeoPoint from, GeoPoint to) {
+ public LatLng interpolate(float fraction, LatLng from, LatLng to) {
// http://en.wikipedia.org/wiki/Slerp
double fromLat = toRadians(from.getLatitude());
double fromLng = toRadians(from.getLongitude());
@@ -71,7 +72,7 @@
// Converts interpolated vector back to polar.
double lat = atan2(z, sqrt(x * x + y * y));
double lng = atan2(y, x);
- return new GeoPoint(toDegrees(lat), toDegrees(lng));
+ return new LatLng(toDegrees(lat), toDegrees(lng));
}
private double computeAngleBetween(double fromLat, double fromLng, double toLat, double toLng) {
diff --git a/app/src/main/java/it/reyboz/bustorino/map/LocationOverlay.java b/app/src/main/java/it/reyboz/bustorino/map/LocationOverlay.java
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/map/LocationOverlay.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- BusTO - Map components
- Copyright (C) 2021 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.map;
-
-
-import android.content.Context;
-import android.util.Log;
-import org.osmdroid.views.MapView;
-import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider;
-import org.osmdroid.views.overlay.mylocation.IMyLocationProvider;
-import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay;
-
-public class LocationOverlay extends MyLocationNewOverlay {
-
- private final static String DEBUG_TAG = "BusTOLocationOverlay";
- final OverlayCallbacks callbacks;
-
- public LocationOverlay(MapView mapView, OverlayCallbacks callbacks) {
- super(mapView);
- this.callbacks = callbacks;
- }
-
- public LocationOverlay(IMyLocationProvider myLocationProvider, MapView mapView, OverlayCallbacks callbacks) {
- super(myLocationProvider, mapView);
- this.callbacks = callbacks;
- }
-
- @Override
- public void enableFollowLocation() {
- super.enableFollowLocation();
- callbacks.onEnableFollowMyLocation();
- }
-
- @Override
- public void disableFollowLocation() {
-
- super.disableFollowLocation();
- callbacks.onDisableFollowMyLocation();
- }
-
- public static LocationOverlay createLocationOverlay(boolean enableLocation, MapView map, Context context, OverlayCallbacks locationCallbacks){
- if(context== null) {
- Log.d(DEBUG_TAG, "Cannot start location overlay, context is null");
- return null;
- }
- // Location Overlay
- // from OpenBikeSharing (THANK GOD)
- Log.d(DEBUG_TAG, "Starting position overlay");
- GpsMyLocationProvider imlp = new GpsMyLocationProvider(context.getApplicationContext());
- imlp.setLocationUpdateMinDistance(5);
- imlp.setLocationUpdateMinTime(2000);
-
- final LocationOverlay overlay = new LocationOverlay(imlp,map, locationCallbacks);
- if (enableLocation) overlay.enableMyLocation();
- //overlay.setOptionsMenuEnabled(true);
-
- return overlay;
- }
-
- public interface OverlayCallbacks{
- /**
- * Called right after disableFollowMyLocation
- */
- void onDisableFollowMyLocation();
-
- /**
- * Called right after enableFollowMyLocation
- */
- void onEnableFollowMyLocation();
- }
-}
diff --git a/app/src/main/java/it/reyboz/bustorino/map/Styles.kt b/app/src/main/java/it/reyboz/bustorino/map/MapLibreStyles.kt
rename from app/src/main/java/it/reyboz/bustorino/map/Styles.kt
rename to app/src/main/java/it/reyboz/bustorino/map/MapLibreStyles.kt
--- a/app/src/main/java/it/reyboz/bustorino/map/Styles.kt
+++ b/app/src/main/java/it/reyboz/bustorino/map/MapLibreStyles.kt
@@ -3,7 +3,7 @@
import it.reyboz.bustorino.util.ViewUtils
import org.maplibre.android.maps.Style
-object Styles {
+object MapLibreStyles {
const val DEMOTILES = "https://demotiles.maplibre.org/style.json"
const val VERSATILES = "https://tiles.versatiles.org/assets/styles/colorful.json"
diff --git a/app/src/main/java/it/reyboz/bustorino/map/MarkerUtils.java b/app/src/main/java/it/reyboz/bustorino/map/MarkerUtils.java
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/map/MarkerUtils.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package it.reyboz.bustorino.map;
-
-import android.animation.ObjectAnimator;
-import android.animation.TypeEvaluator;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-import android.util.Property;
-
-
-import android.view.animation.LinearInterpolator;
-import it.reyboz.bustorino.R;
-import org.osmdroid.util.GeoPoint;
-import org.osmdroid.views.MapView;
-import org.osmdroid.views.overlay.Marker;
-import org.osmdroid.views.overlay.infowindow.InfoWindow;
-
-public class MarkerUtils {
-
- public static final int LINEAR_ANIMATION = 1;
-
- /* Copyright 2013 Google Inc.
- Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0.html */
- public static ObjectAnimator makeMarkerAnimator(final MapView map, Marker marker, GeoPoint finalPosition, int animationType, int durationMs) {
-
- GeoPointInterpolator interpolator;
- switch (animationType){
- case LINEAR_ANIMATION:
- interpolator = new GeoPointInterpolator.Linear();
- break;
- default:
- throw new IllegalArgumentException("Value "+animationType+ " for animationType is invalid");
- }
- TypeEvaluator typeEvaluator = (fraction, startValue, endValue) ->
- interpolator.interpolate(fraction, startValue, endValue);
- Property property = Property.of(Marker.class, GeoPoint.class, "position");
- ObjectAnimator animator = ObjectAnimator.ofObject(marker, property, typeEvaluator, finalPosition);
- switch (animationType){
- case LINEAR_ANIMATION:
-
- animator.setInterpolator(new LinearInterpolator());
- default:
- }
- animator.setDuration(durationMs);
- //animator.start();
- return animator;
- }
-
- public static Marker makeMarker(GeoPoint geoPoint, String stopID, String stopName,
- String routesStopping,
- MapView map,
- CustomInfoWindow.TouchResponder responder,
- Drawable icon,
- int infoWindowLayout,
- int titleColorId) {
-
- // add a marker
- final Marker marker = new Marker(map);
-
- // set custom info window as info window
- CustomInfoWindow popup = new CustomInfoWindow(map, stopID, stopName, routesStopping, responder, infoWindowLayout, titleColorId);
- marker.setInfoWindow(popup);
-
- // make the marker clickable
- marker.setOnMarkerClickListener((thisMarker, mapView) -> {
- if (thisMarker.isInfoWindowOpen()) {
- // on second click
- Log.w("BusTO-OsmMap", "Pressed on the click marker");
- } 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));
-
- marker.setIcon(icon);
- // add to it a title
- marker.setTitle(stopName);
- // set the description as the ID
- marker.setSnippet(stopID);
-
- // show popup info window of the searched marker
- /*if (isStartMarker) {
- marker.showInfoWindow();
- //map.getController().animateTo(marker.getPosition());
- }*/
-
- return marker;
- }
-}
diff --git a/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java b/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java
--- a/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java
+++ b/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java
@@ -21,11 +21,7 @@
import androidx.core.util.Pair;
import android.util.Log;
-import it.reyboz.bustorino.backend.Passaggio;
-import it.reyboz.bustorino.backend.Route;
-import it.reyboz.bustorino.backend.Stop;
-import it.reyboz.bustorino.backend.utils;
-import org.osmdroid.api.IGeoPoint;
+import it.reyboz.bustorino.backend.*;
import java.util.Collections;
import java.util.Comparator;
@@ -39,7 +35,7 @@
latPos = latitude;
longPos = longitude;
}
- public RoutePositionSorter(IGeoPoint position){
+ public RoutePositionSorter(GPSPoint position){
this(position.getLatitude(), position.getLongitude());
}
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/StopsMapViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/StopsMapViewModel.kt
--- a/app/src/main/java/it/reyboz/bustorino/viewmodels/StopsMapViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/StopsMapViewModel.kt
@@ -9,7 +9,6 @@
import it.reyboz.bustorino.data.NextGenDB
import it.reyboz.bustorino.data.OldDataRepository
import org.maplibre.android.geometry.LatLngBounds
-import org.osmdroid.util.BoundingBox
import java.util.concurrent.Executors
import kotlin.collections.ArrayList
@@ -60,12 +59,14 @@
return ArrayList(allStopsLoaded.values)
}
- fun requestStopsInBoundingBox(bb: BoundingBox) {
+ /*fun requestStopsInBoundingBox(bb: BoundingBox) {
bb.let {
Log.d(DEBUG_TAG, "Launching stop request")
oldRepo.requestStopsInArea(it.latSouth, it.latNorth, it.lonWest, it.lonEast, callback)
}
}
+
+ */
fun requestStopsInLatLng(bb: LatLngBounds) {
bb.let {
Log.d(DEBUG_TAG, "Launching stop request")
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -285,13 +285,13 @@
Source of real time positions for buses and trams
-
+ MaTO (updated more frequently, might be offline)
+ GTFS RT (more stable, less frequently updated)
- @string/positions_source_mato_descr
- @string/positions_source_gtfsrt_descr
- MaTO (updated more frequently, might be offline)
- GTFS RT (more stable, less frequently updated)
+
Style of the map
Versatiles (vector)
OSM legacy (raster, lighter)
@@ -299,8 +299,6 @@
- @string/map_style_versatiles
- @string/map_style_legacy_raster
- MaTO (updated more frequently, might be offline)
- GTFS RT (more stable, less frequently updated)
Remove trips data (free up space)
All GTFS trips have been removed from the database