From df057f4fd959a651e76df95be4045cd1d61132c9 Mon Sep 17 00:00:00 2001 From: crimean Date: Fri, 23 Nov 2018 22:06:34 +0300 Subject: [PATCH] Introduced public transport routing UI. Still in progress. --- OsmAnd/res/values/strings.xml | 1 + .../src/net/osmand/plus/AppInitializer.java | 2 + .../src/net/osmand/plus/ApplicationMode.java | 3 + .../net/osmand/plus/OsmandApplication.java | 6 + .../net/osmand/plus/TargetPointsHelper.java | 13 +- .../osmand/plus/activities/MapActivity.java | 25 +- .../plus/base/MapViewTrackingUtilities.java | 2 +- .../osmand/plus/dashboard/DashboardOnMap.java | 2 +- .../plus/resources/ResourceManager.java | 18 +- .../MapRouteInfoMenu.java | 2 +- .../routing/IRouteInformationListener.java | 12 + .../osmand/plus/routing/RoutingHelper.java | 67 ++-- .../plus/routing/TransportRoutingHelper.java | 341 ++++++++++++++++++ .../net/osmand/plus/views/POIMapLayer.java | 2 +- .../src/net/osmand/plus/views/RouteLayer.java | 107 +++++- 15 files changed, 552 insertions(+), 51 deletions(-) create mode 100644 OsmAnd/src/net/osmand/plus/routing/IRouteInformationListener.java create mode 100644 OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index c303adf04e..4365db3245 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -10,6 +10,7 @@ - For wording and consistency, please note https://osmand.net/help-online?id=technical-articles#Creating_a_Consistent_User_Experience Thx - Hardy --> + Public transport Select road on the map or from the list below that you want to avoid during navigation: Show along the route Simulate navigation diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index e3e071b9cd..51bd3b1aa0 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -44,6 +44,7 @@ import net.osmand.plus.render.RendererRegistry; import net.osmand.plus.resources.ResourceManager; import net.osmand.plus.routepreparationmenu.RoutingOptionsHelper; import net.osmand.plus.routing.RoutingHelper; +import net.osmand.plus.routing.TransportRoutingHelper; import net.osmand.plus.search.QuickSearchHelper; import net.osmand.plus.views.corenative.NativeCoreContext; import net.osmand.plus.voice.CommandPlayer; @@ -458,6 +459,7 @@ public class AppInitializer implements IProgress { app.applyTheme(app); app.inAppPurchaseHelper = startupInit(new InAppPurchaseHelper(app), InAppPurchaseHelper.class); app.poiTypes = startupInit(MapPoiTypes.getDefaultNoInit(), MapPoiTypes.class); + app.transportRoutingHelper = startupInit(new TransportRoutingHelper(app), TransportRoutingHelper.class); app.routingHelper = startupInit(new RoutingHelper(app), RoutingHelper.class); app.resourceManager = startupInit(new ResourceManager(app), ResourceManager.class); app.daynightHelper = startupInit(new DayNightHelper(app), DayNightHelper.class); diff --git a/OsmAnd/src/net/osmand/plus/ApplicationMode.java b/OsmAnd/src/net/osmand/plus/ApplicationMode.java index dac0b648ab..7c26428b85 100644 --- a/OsmAnd/src/net/osmand/plus/ApplicationMode.java +++ b/OsmAnd/src/net/osmand/plus/ApplicationMode.java @@ -57,6 +57,9 @@ public class ApplicationMode { public static final ApplicationMode TRAIN = create(R.string.app_mode_train, "train").speed(25f, 40). carLocation().icon(R.drawable.map_action_train, R.drawable.ic_action_train).reg(); + public static final ApplicationMode PUBLIC_TRANSPORT = create(R.string.app_mode_public_transport, "public_transport"). + icon(R.drawable.map_action_bus_dark, R.drawable.ic_action_bus_dark).reg(); + static { ApplicationMode[] exceptDefault = new ApplicationMode[]{CAR, PEDESTRIAN, BICYCLE, BOAT, AIRCRAFT, BUS, TRAIN}; ApplicationMode[] exceptPedestrianAndDefault = new ApplicationMode[]{CAR, BICYCLE, BOAT, AIRCRAFT, BUS, TRAIN}; diff --git a/OsmAnd/src/net/osmand/plus/OsmandApplication.java b/OsmAnd/src/net/osmand/plus/OsmandApplication.java index 55748624a2..4be50587b7 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandApplication.java +++ b/OsmAnd/src/net/osmand/plus/OsmandApplication.java @@ -57,6 +57,7 @@ import net.osmand.plus.render.RendererRegistry; import net.osmand.plus.resources.ResourceManager; import net.osmand.plus.routepreparationmenu.RoutingOptionsHelper; import net.osmand.plus.routing.RoutingHelper; +import net.osmand.plus.routing.TransportRoutingHelper; import net.osmand.plus.search.QuickSearchHelper; import net.osmand.plus.voice.CommandPlayer; import net.osmand.plus.wikivoyage.data.TravelDbHelper; @@ -106,6 +107,7 @@ public class OsmandApplication extends MultiDexApplication { PoiFiltersHelper poiFilters; MapPoiTypes poiTypes; RoutingHelper routingHelper; + TransportRoutingHelper transportRoutingHelper; FavouritesDbHelper favorites; CommandPlayer player; GpxSelectionHelper selectedGpxHelper; @@ -402,6 +404,10 @@ public class OsmandApplication extends MultiDexApplication { return routingHelper; } + public TransportRoutingHelper getTransportRoutingHelper() { + return transportRoutingHelper; + } + public RoutingOptionsHelper getRoutingOptionsHelper() { return routingOptionsHelper; } diff --git a/OsmAnd/src/net/osmand/plus/TargetPointsHelper.java b/OsmAnd/src/net/osmand/plus/TargetPointsHelper.java index 4abacf44ed..c49e349c54 100644 --- a/OsmAnd/src/net/osmand/plus/TargetPointsHelper.java +++ b/OsmAnd/src/net/osmand/plus/TargetPointsHelper.java @@ -379,8 +379,8 @@ public class TargetPointsHelper { } public void updateRouteAndRefresh(boolean updateRoute) { - if(updateRoute && ( routingHelper.isRouteBeingCalculated() || routingHelper.isRouteCalculated() || - routingHelper.isFollowingMode() || routingHelper.isRoutePlanningMode())) { + if(updateRoute && (routingHelper.isPublicTransportMode() || routingHelper.isRouteBeingCalculated() || + routingHelper.isRouteCalculated() || routingHelper.isFollowingMode() || routingHelper.isRoutePlanningMode())) { updateRoutingHelper(); } updateListeners(); @@ -388,15 +388,14 @@ public class TargetPointsHelper { private void updateRoutingHelper() { LatLon start = settings.getPointToStart(); - Location lastKnownLocation = ctx.getLocationProvider().getLastKnownLocation(); + LatLon finish = settings.getPointToNavigate(); List is = getIntermediatePointsLatLonNavigation(); + Location lastKnownLocation = ctx.getLocationProvider().getLastKnownLocation(); if((routingHelper.isFollowingMode() && lastKnownLocation != null) || start == null) { - routingHelper.setFinalAndCurrentLocation(settings.getPointToNavigate(), - is, lastKnownLocation); + routingHelper.setFinalAndCurrentLocation(finish, is, lastKnownLocation); } else { Location loc = wrap(start); - routingHelper.setFinalAndCurrentLocation(settings.getPointToNavigate(), - is, loc); + routingHelper.setFinalAndCurrentLocation(finish, is, loc); } } diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java index 86168b5947..155522dc84 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java @@ -110,9 +110,11 @@ import net.osmand.plus.measurementtool.MeasurementToolFragment; import net.osmand.plus.measurementtool.NewGpxData; import net.osmand.plus.render.RendererRegistry; import net.osmand.plus.resources.ResourceManager; +import net.osmand.plus.routing.IRouteInformationListener; import net.osmand.plus.routing.RoutingHelper; -import net.osmand.plus.routing.RoutingHelper.IRouteInformationListener; import net.osmand.plus.routing.RoutingHelper.RouteCalculationProgressCallback; +import net.osmand.plus.routing.TransportRoutingHelper; +import net.osmand.plus.routing.TransportRoutingHelper.TransportRouteCalculationProgressCallback; import net.osmand.plus.search.QuickSearchDialogFragment; import net.osmand.plus.search.QuickSearchDialogFragment.QuickSearchTab; import net.osmand.plus.search.QuickSearchDialogFragment.QuickSearchType; @@ -439,7 +441,7 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven private void createProgressBarForRouting() { final ProgressBar pb = (ProgressBar) findViewById(R.id.map_horizontal_progress); - app.getRoutingHelper().setProgressBar(new RouteCalculationProgressCallback() { + final RouteCalculationProgressCallback progressCallback = new RouteCalculationProgressCallback() { @Override public void start() { @@ -501,6 +503,25 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven dashboardOnMap.routeCalculationFinished(); pb.setVisibility(View.GONE); } + }; + + app.getRoutingHelper().setProgressBar(progressCallback); + + app.getTransportRoutingHelper().setProgressBar(new TransportRouteCalculationProgressCallback() { + @Override + public void start() { + progressCallback.start(); + } + + @Override + public void updateProgress(int progress) { + progressCallback.updateProgress(progress); + } + + @Override + public void finish() { + progressCallback.finish(); + } }); } diff --git a/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java b/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java index e655960eea..af25adabeb 100644 --- a/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java +++ b/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java @@ -25,7 +25,7 @@ import net.osmand.plus.R; import net.osmand.plus.dashboard.DashboardOnMap; import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.routing.RoutingHelper; -import net.osmand.plus.routing.RoutingHelper.IRouteInformationListener; +import net.osmand.plus.routing.IRouteInformationListener; import net.osmand.plus.views.AnimateDraggingMapThread; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.util.MapUtils; diff --git a/OsmAnd/src/net/osmand/plus/dashboard/DashboardOnMap.java b/OsmAnd/src/net/osmand/plus/dashboard/DashboardOnMap.java index 9704c9f38d..bd395ee44c 100644 --- a/OsmAnd/src/net/osmand/plus/dashboard/DashboardOnMap.java +++ b/OsmAnd/src/net/osmand/plus/dashboard/DashboardOnMap.java @@ -80,7 +80,7 @@ import net.osmand.plus.mapillary.MapillaryPlugin.MapillaryFirstDialogFragment; import net.osmand.plus.osmedit.OsmNotesMenu; import net.osmand.plus.rastermaps.OsmandRasterMapsPlugin; import net.osmand.plus.routing.RoutingHelper; -import net.osmand.plus.routing.RoutingHelper.IRouteInformationListener; +import net.osmand.plus.routing.IRouteInformationListener; import net.osmand.plus.srtmplugin.ContourLinesMenu; import net.osmand.plus.srtmplugin.HillshadeMenu; import net.osmand.plus.srtmplugin.SRTMPlugin; diff --git a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java index 8e3e85ae94..a5ed90a0d9 100644 --- a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java @@ -108,7 +108,8 @@ public class ResourceManager { TRANSPORT, ADDRESS, QUICK_SEARCH, - ROUTING + ROUTING, + TRANSPORT_ROUTING } public static class BinaryMapReaderResource { @@ -998,7 +999,20 @@ public class ResourceManager { } return readers.toArray(new BinaryMapIndexReader[readers.size()]); } - + + public BinaryMapIndexReader[] getTransportRoutingMapFiles() { + List readers = new ArrayList<>(fileReaders.size()); + for(BinaryMapReaderResource r : fileReaders.values()) { + if(r.isUseForRouting()) { + BinaryMapIndexReader reader = r.getReader(BinaryMapReaderResourceType.TRANSPORT_ROUTING); + if (reader != null) { + readers.add(reader); + } + } + } + return readers.toArray(new BinaryMapIndexReader[readers.size()]); + } + public BinaryMapIndexReader[] getQuickSearchFiles() { List readers = new ArrayList<>(fileReaders.size()); for(BinaryMapReaderResource r : fileReaders.values()) { diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java index 00c30e6fa2..bba5f73a7e 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java @@ -71,7 +71,7 @@ import net.osmand.plus.mapmarkers.MapMarkerSelectionFragment; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.routing.RouteDirectionInfo; import net.osmand.plus.routing.RoutingHelper; -import net.osmand.plus.routing.RoutingHelper.IRouteInformationListener; +import net.osmand.plus.routing.IRouteInformationListener; import net.osmand.plus.views.MapControlsLayer; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.router.GeneralRouter; diff --git a/OsmAnd/src/net/osmand/plus/routing/IRouteInformationListener.java b/OsmAnd/src/net/osmand/plus/routing/IRouteInformationListener.java new file mode 100644 index 0000000000..917e2081c5 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/routing/IRouteInformationListener.java @@ -0,0 +1,12 @@ +package net.osmand.plus.routing; + +import net.osmand.ValueHolder; + +public interface IRouteInformationListener { + + void newRouteIsCalculated(boolean newRoute, ValueHolder showToast); + + void routeWasCancelled(); + + void routeWasFinished(); +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java index df11e56b6e..c9f6d15b95 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java +++ b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java @@ -36,21 +36,12 @@ public class RoutingHelper { private static final org.apache.commons.logging.Log log = PlatformUtil.getLog(RoutingHelper.class); - public interface IRouteInformationListener { - - void newRouteIsCalculated(boolean newRoute, ValueHolder showToast); - - void routeWasCancelled(); - - void routeWasFinished(); - } - private static final float POSITION_TOLERANCE = 60; - - private List> listeners = new LinkedList>(); + private List> listeners = new LinkedList<>(); private OsmandApplication app; + private TransportRoutingHelper transportRoutingHelper; private boolean isFollowingMode = false; private boolean isRoutePlanningMode = false; @@ -99,9 +90,13 @@ public class RoutingHelper { settings = context.getSettings(); voiceRouter = new VoiceRouter(this, settings); provider = new RouteProvider(); + transportRoutingHelper = context.getTransportRoutingHelper(); setAppMode(settings.APPLICATION_MODE.get()); } + public TransportRoutingHelper getTransportRoutingHelper() { + return transportRoutingHelper; + } public boolean isFollowingMode() { return isFollowingMode; @@ -160,13 +155,19 @@ public class RoutingHelper { this.isRoutePlanningMode = isRoutePlanningMode; } - - - public synchronized void setFinalAndCurrentLocation(LatLon finalLocation, List intermediatePoints, Location currentLocation){ - RouteCalculationResult previousRoute = route; - clearCurrentRoute(finalLocation, intermediatePoints); - // to update route - setCurrentLocation(currentLocation, false, previousRoute, true); + public synchronized void setFinalAndCurrentLocation(LatLon finalLocation, List intermediatePoints, Location currentLocation) { + if (isPublicTransportMode()) { + clearCurrentRoute(null, null); + if (currentLocation != null) { + transportRoutingHelper.setFinalAndCurrentLocation(finalLocation, + new LatLon(currentLocation.getLatitude(), currentLocation.getLongitude())); + } + } else { + RouteCalculationResult previousRoute = route; + clearCurrentRoute(finalLocation, intermediatePoints); + // to update route + setCurrentLocation(currentLocation, false, previousRoute, true); + } } public synchronized void clearCurrentRoute(LatLon newFinalLocation, List newIntermediatePoints) { @@ -265,7 +266,7 @@ public class RoutingHelper { } public void addListener(IRouteInformationListener l){ - listeners.add(new WeakReference(l)); + listeners.add(new WeakReference<>(l)); } public boolean removeListener(IRouteInformationListener lt){ @@ -664,7 +665,7 @@ public class RoutingHelper { app.runInUIThread(new Runnable() { @Override public void run() { - ValueHolder showToast = new ValueHolder(); + ValueHolder showToast = new ValueHolder<>(); showToast.value = true; Iterator> it = listeners.iterator(); while (it.hasNext()) { @@ -936,8 +937,27 @@ public class RoutingHelper { } public void recalculateRouteDueToSettingsChange() { - clearCurrentRoute(finalLocation, intermediatePoints); - recalculateRouteInBackground(lastFixedLocation, finalLocation, intermediatePoints, currentGPXRoute, route, true, false); + if (isPublicTransportMode()) { + Location start = lastFixedLocation; + LatLon finish = finalLocation; + clearCurrentRoute(null, null); + if (start != null && finish != null) { + transportRoutingHelper.setFinalAndCurrentLocation(finish, + new LatLon(start.getLatitude(), start.getLongitude())); + } else { + transportRoutingHelper.recalculateRouteDueToSettingsChange(); + } + } else { + if (finalLocation == null && intermediatePoints == null) { + LatLon finish = settings.getPointToNavigate(); + List intermediates = app.getTargetPointsHelper().getIntermediatePointsLatLonNavigation(); + clearCurrentRoute(finish, intermediates); + setCurrentLocation(lastFixedLocation, false); + } else { + clearCurrentRoute(finalLocation, intermediatePoints); + recalculateRouteInBackground(lastFixedLocation, finalLocation, intermediatePoints, currentGPXRoute, route, true, false); + } + } } private void recalculateRouteInBackground(final Location start, final LatLon end, final List intermediates, @@ -1060,6 +1080,9 @@ public class RoutingHelper { void finish(); } + public boolean isPublicTransportMode() { + return mode == ApplicationMode.PUBLIC_TRANSPORT; + } public boolean isRouteBeingCalculated(){ return currentRunningJob instanceof RouteRecalculationThread; diff --git a/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java new file mode 100644 index 0000000000..df686bd9a4 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java @@ -0,0 +1,341 @@ +package net.osmand.plus.routing; + +import android.support.annotation.NonNull; + +import net.osmand.PlatformUtil; +import net.osmand.ValueHolder; +import net.osmand.binary.BinaryMapIndexReader; +import net.osmand.data.LatLon; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.R; +import net.osmand.plus.routing.RouteProvider.RouteService; +import net.osmand.router.RouteCalculationProgress; +import net.osmand.router.RoutingConfiguration; +import net.osmand.router.TransportRoutePlanner; +import net.osmand.router.TransportRoutePlanner.TransportRouteResult; +import net.osmand.router.TransportRoutePlanner.TransportRoutingContext; +import net.osmand.router.TransportRoutingConfiguration; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import static net.osmand.plus.notifications.OsmandNotification.NotificationType.NAVIGATION; + +public class TransportRoutingHelper { + + private static final org.apache.commons.logging.Log log = PlatformUtil.getLog(TransportRoutingHelper.class); + + private List> listeners = new LinkedList<>(); + + private OsmandApplication app; + + private List routes; + private int currentRoute; + + private LatLon startLocation; + private LatLon endLocation; + private boolean useSchedule; + + private Thread currentRunningJob; + private String lastRouteCalcError; + private String lastRouteCalcErrorShort; + private long lastTimeEvaluatedRoute = 0; + + private TransportRouteCalculationProgressCallback progressRoute; + + public TransportRoutingHelper(@NonNull OsmandApplication app) { + this.app = app; + } + + public LatLon getStartLocation() { + return startLocation; + } + + public LatLon getEndLocation() { + return endLocation; + } + + public boolean isUseSchedule() { + return useSchedule; + } + + public void setUseSchedule(boolean useSchedule) { + this.useSchedule = useSchedule; + } + + public int getCurrentRoute() { + return currentRoute; + } + + public List getRoutes() { + return routes; + } + + public void setCurrentRoute(int currentRoute) { + this.currentRoute = currentRoute; + } + + public void recalculateRouteDueToSettingsChange() { + clearCurrentRoute(endLocation); + recalculateRouteInBackground(startLocation, endLocation); + } + + private void recalculateRouteInBackground(LatLon start, LatLon end) { + if (start == null || end == null) { + return; + } + TransportRouteCalculationParams params = new TransportRouteCalculationParams(); + params.start = start; + params.end = end; + params.useSchedule = useSchedule; + params.type = RouteService.OSMAND; + params.ctx = app; + params.calculationProgress = new RouteCalculationProgress(); + + startRouteCalculationThread(params); + } + + private void startRouteCalculationThread(TransportRouteCalculationParams params) { + synchronized (this) { + final Thread prevRunningJob = currentRunningJob; + RouteRecalculationThread newThread = + new RouteRecalculationThread("Calculating public transport route", params); + currentRunningJob = newThread; + startProgress(params); + updateProgress(params); + if (prevRunningJob != null) { + newThread.setWaitPrevJob(prevRunningJob); + } + currentRunningJob.start(); + } + } + + public void setProgressBar(TransportRouteCalculationProgressCallback progressRoute) { + this.progressRoute = progressRoute; + } + + private void startProgress(final TransportRouteCalculationParams params) { + final TransportRouteCalculationProgressCallback progressRoute = this.progressRoute; + if (progressRoute != null) { + progressRoute.start(); + } + } + + private void updateProgress(final TransportRouteCalculationParams params) { + final TransportRouteCalculationProgressCallback progressRoute = this.progressRoute; + if (progressRoute != null) { + app.runInUIThread(new Runnable() { + + @Override + public void run() { + RouteCalculationProgress calculationProgress = params.calculationProgress; + if (isRouteBeingCalculated()) { + float pr = calculationProgress.getLinearProgress(); + progressRoute.updateProgress((int) pr); + Thread t = currentRunningJob; + if (t instanceof RouteRecalculationThread && ((RouteRecalculationThread) t).params != params) { + // different calculation started + } else { + updateProgress(params); + } + } else { + progressRoute.finish(); + } + } + }, 300); + } + } + + public boolean isRouteBeingCalculated() { + return currentRunningJob instanceof RouteRecalculationThread; + } + + private void setNewRoute(final List res) { + app.runInUIThread(new Runnable() { + @Override + public void run() { + ValueHolder showToast = new ValueHolder<>(); + showToast.value = true; + Iterator> it = listeners.iterator(); + while (it.hasNext()) { + WeakReference ref = it.next(); + IRouteInformationListener l = ref.get(); + if (l == null) { + it.remove(); + } else { + l.newRouteIsCalculated(true, showToast); + } + } + if (showToast.value && OsmandPlugin.isDevelopment()) { + String msg = "Public transport routes calculated: " + res.size(); + app.showToastMessage(msg); + } + } + }); + } + + public synchronized void setFinalAndCurrentLocation(LatLon finalLocation, LatLon currentLocation) { + clearCurrentRoute(finalLocation); + // to update route + setCurrentLocation(currentLocation); + } + + public synchronized void clearCurrentRoute(LatLon newFinalLocation) { + routes = null; + app.getWaypointHelper().setNewRoute(new RouteCalculationResult("")); + app.runInUIThread(new Runnable() { + @Override + public void run() { + Iterator> it = listeners.iterator(); + while (it.hasNext()) { + WeakReference ref = it.next(); + IRouteInformationListener l = ref.get(); + if (l == null) { + it.remove(); + } else { + l.routeWasCancelled(); + } + } + } + }); + this.endLocation = newFinalLocation; + if (currentRunningJob instanceof RouteRecalculationThread) { + ((RouteRecalculationThread) currentRunningJob).stopCalculation(); + } + } + + private void setCurrentLocation(LatLon currentLocation) { + if (endLocation == null || currentLocation == null) { + return; + } + startLocation = currentLocation; + recalculateRouteInBackground(currentLocation, endLocation); + } + + private void showMessage(final String msg) { + app.runInUIThread(new Runnable() { + @Override + public void run() { + app.showToastMessage(msg); + } + }); + } + + public interface TransportRouteCalculationProgressCallback { + + void start(); + + void updateProgress(int progress); + + void finish(); + } + + public static class TransportRouteCalculationParams { + + public LatLon start; + public LatLon end; + + public OsmandApplication ctx; + public RouteService type; + public boolean useSchedule; + public RouteCalculationProgress calculationProgress; + public TransportRouteCalculationResultListener resultListener; + + public interface TransportRouteCalculationResultListener { + void onRouteCalculated(List route); + } + } + + private class RouteRecalculationThread extends Thread { + + private final TransportRouteCalculationParams params; + private Thread prevRunningJob; + + public RouteRecalculationThread(String name, TransportRouteCalculationParams params) { + super(name); + this.params = params; + if (params.calculationProgress == null) { + params.calculationProgress = new RouteCalculationProgress(); + } + } + + public void stopCalculation() { + params.calculationProgress.isCancelled = true; + } + + private List calculateRouteImpl(TransportRouteCalculationParams params) throws IOException { + RoutingConfiguration.Builder config = params.ctx.getDefaultRoutingConfig(); + BinaryMapIndexReader[] files = params.ctx.getResourceManager().getTransportRoutingMapFiles(); + + TransportRoutingConfiguration cfg = new TransportRoutingConfiguration(config); + cfg.useSchedule = params.useSchedule; + TransportRoutePlanner planner = new TransportRoutePlanner(); + TransportRoutingContext ctx = new TransportRoutingContext(cfg, files); + return planner.buildRoute(ctx, params.start, params.end); + } + + @Override + public void run() { + synchronized (TransportRoutingHelper.this) { + currentRunningJob = this; + } + if (prevRunningJob != null) { + while (prevRunningJob.isAlive()) { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + // ignore + } + } + synchronized (TransportRoutingHelper.this) { + currentRunningJob = this; + } + } + + List res = null; + String error = null; + try { + res = calculateRouteImpl(params); + } catch (IOException e) { + error = e.getMessage(); + log.error(e); + } + if (params.calculationProgress.isCancelled) { + synchronized (TransportRoutingHelper.this) { + currentRunningJob = null; + } + return; + } + synchronized (TransportRoutingHelper.this) { + routes = res; + if (res != null) { + if (params.resultListener != null) { + params.resultListener.onRouteCalculated(res); + } + } + currentRunningJob = null; + } + if (res != null) { + setNewRoute(res); + } else if (error != null) { + lastRouteCalcError = app.getString(R.string.error_calculating_route) + ":\n" + error; + lastRouteCalcErrorShort = app.getString(R.string.error_calculating_route); + showMessage(lastRouteCalcError); + } else { + lastRouteCalcError = app.getString(R.string.empty_route_calculated); + lastRouteCalcErrorShort = app.getString(R.string.empty_route_calculated); + showMessage(lastRouteCalcError); + } + app.getNotificationHelper().refreshNotification(NAVIGATION); + lastTimeEvaluatedRoute = System.currentTimeMillis(); + } + + public void setWaitPrevJob(Thread prevRunningJob) { + this.prevRunningJob = prevRunningJob; + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/views/POIMapLayer.java b/OsmAnd/src/net/osmand/plus/views/POIMapLayer.java index f03da3dac1..f475fedef8 100644 --- a/OsmAnd/src/net/osmand/plus/views/POIMapLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/POIMapLayer.java @@ -39,7 +39,7 @@ import net.osmand.plus.activities.MapActivity; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.render.RenderingIcons; import net.osmand.plus.routing.RoutingHelper; -import net.osmand.plus.routing.RoutingHelper.IRouteInformationListener; +import net.osmand.plus.routing.IRouteInformationListener; import net.osmand.plus.views.MapTextLayer.MapTextProvider; import net.osmand.util.Algorithms; diff --git a/OsmAnd/src/net/osmand/plus/views/RouteLayer.java b/OsmAnd/src/net/osmand/plus/views/RouteLayer.java index dd7349b754..3b0ef78466 100644 --- a/OsmAnd/src/net/osmand/plus/views/RouteLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/RouteLayer.java @@ -17,6 +17,9 @@ import net.osmand.Location; import net.osmand.data.LatLon; import net.osmand.data.QuadRect; import net.osmand.data.RotatedTileBox; +import net.osmand.osm.edit.Node; +import net.osmand.osm.edit.OSMSettings; +import net.osmand.osm.edit.Way; import net.osmand.plus.GPXUtilities.WptPt; import net.osmand.plus.R; import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu; @@ -24,6 +27,10 @@ import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu.TrackChartPoints; import net.osmand.plus.routing.RouteCalculationResult; import net.osmand.plus.routing.RouteDirectionInfo; import net.osmand.plus.routing.RoutingHelper; +import net.osmand.plus.routing.TransportRoutingHelper; +import net.osmand.router.TransportRoutePlanner; +import net.osmand.router.TransportRoutePlanner.TransportRouteResult; +import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment; import net.osmand.util.MapUtils; import java.util.ArrayList; @@ -42,6 +49,7 @@ public class RouteLayer extends OsmandMapLayer { private OsmandMapTileView view; private final RoutingHelper helper; + private final TransportRoutingHelper transportHelper; // keep array lists created private List actionPoints = new ArrayList(); @@ -63,8 +71,9 @@ public class RouteLayer extends OsmandMapLayer { private RenderingLineAttributes attrs; - public RouteLayer(RoutingHelper helper){ + public RouteLayer(RoutingHelper helper) { this.helper = helper; + this.transportHelper = helper.getTransportRoutingHelper(); } public void setTrackChartPoints(TrackDetailsMenu.TrackChartPoints trackChartPoints) { @@ -119,7 +128,9 @@ public class RouteLayer extends OsmandMapLayer { @Override public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) { - if (helper.getFinalLocation() != null && helper.getRoute().isCalculated()) { + if ((helper.isPublicTransportMode() && transportHelper.getRoutes() != null) || + (helper.getFinalLocation() != null && helper.getRoute().isCalculated())) { + updateAttrs(settings, tileBox); if(coloredArrowUp == null) { @@ -413,6 +424,7 @@ public class RouteLayer extends OsmandMapLayer { private class RouteSimplificationGeometry { RouteCalculationResult route; + TransportRouteResult transportRoute; double mapDensity; TreeMap zooms = new TreeMap<>(); List locations = Collections.emptyList(); @@ -426,7 +438,7 @@ public class RouteLayer extends OsmandMapLayer { public void updateRoute(RotatedTileBox tb, RouteCalculationResult route) { if(tb.getMapDensity() != mapDensity || this.route != route) { this.route = route; - if(route == null) { + if (route == null) { locations = Collections.emptyList(); } else { locations = route.getImmutableAllLocations(); @@ -436,6 +448,36 @@ public class RouteLayer extends OsmandMapLayer { } } + public void updateTransportRoute(RotatedTileBox tb, TransportRouteResult route) { + if (tb.getMapDensity() != mapDensity || this.transportRoute != route) { + this.transportRoute = route; + if (route == null) { + locations = Collections.emptyList(); + } else { + LatLon start = transportHelper.getStartLocation(); + LatLon end = transportHelper.getEndLocation(); + List list = new ArrayList<>(); + calculateTransportResult(start, end, route, list); + List locs = new ArrayList<>(); + for (Way w : list) { + //Location loc = new Location("transport"); + //loc.setLatitude(w.getLatitude()); + //loc.setLongitude(w.getLongitude()); + //locs.add(loc); + for (Node n : w.getNodes()) { + Location ln = new Location("transport"); + ln.setLatitude(n.getLatitude()); + ln.setLongitude(n.getLongitude()); + locs.add(ln); + } + } + locations = locs; + } + this.mapDensity = tb.getMapDensity(); + zooms.clear(); + } + } + private RouteGeometryZoom getGeometryZoom(RotatedTileBox tb) { RouteGeometryZoom zm = zooms.get(tb.getZoom()); if(zm == null) { @@ -555,16 +597,32 @@ public class RouteLayer extends OsmandMapLayer { } public void drawLocations(RotatedTileBox tb, Canvas canvas, double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude) { - RouteCalculationResult route = helper.getRoute(); - routeGeometry.updateRoute(tb, route); - routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude, - helper.getLastProjection(), route == null ? 0 : route.getCurrentRoute()); - List rd = helper.getRouteDirections(); - Iterator it = rd.iterator(); - if (tb.getZoom() >= 14) { - List actionPoints = calculateActionPoints(topLatitude, leftLongitude, bottomLatitude, rightLongitude, helper.getLastProjection(), - helper.getRoute().getRouteLocations(), helper.getRoute().getCurrentRoute(), it, tb.getZoom()); - drawAction(tb, canvas, actionPoints); + if (helper.isPublicTransportMode()) { + int currentRoute = transportHelper.getCurrentRoute(); + List routes = transportHelper.getRoutes(); + TransportRouteResult route = routes != null && routes.size() > currentRoute ? routes.get(currentRoute) : null; + routeGeometry.updateRoute(tb, null); + routeGeometry.updateTransportRoute(tb, route); + if (route != null) { + LatLon start = transportHelper.getStartLocation(); + Location startLocation = new Location("transport"); + startLocation.setLatitude(start.getLatitude()); + startLocation.setLongitude(start.getLongitude()); + routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude, startLocation, 0); + } + } else { + RouteCalculationResult route = helper.getRoute(); + routeGeometry.updateTransportRoute(tb, null); + routeGeometry.updateRoute(tb, route); + routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude, + helper.getLastProjection(), route == null ? 0 : route.getCurrentRoute()); + List rd = helper.getRouteDirections(); + Iterator it = rd.iterator(); + if (tb.getZoom() >= 14) { + List actionPoints = calculateActionPoints(topLatitude, leftLongitude, bottomLatitude, rightLongitude, helper.getLastProjection(), + helper.getRoute().getRouteLocations(), helper.getRoute().getCurrentRoute(), it, tb.getZoom()); + drawAction(tb, canvas, actionPoints); + } } } @@ -711,6 +769,27 @@ public class RouteLayer extends OsmandMapLayer { } - + private void calculateTransportResult(LatLon start, LatLon end, TransportRouteResult r, List res) { + if (r != null) { + LatLon p = start; + for (TransportRouteResultSegment s : r.getSegments()) { + LatLon floc = s.getStart().getLocation(); + addWalk(res, p, floc); + res.addAll(s.getGeometry()); + p = s.getEnd().getLocation(); + } + addWalk(res, p, end); + } + } + private void addWalk(List res, LatLon s, LatLon e) { + double dist = MapUtils.getDistance(s, e); + if (dist > 50) { + Way way = new Way(-1); + way.putTag(OSMSettings.OSMTagKey.NAME.getValue(), String.format("Walk %.1f m", dist)); + way.addNode(new Node(s.getLatitude(), s.getLongitude(), -1)); + way.addNode(new Node(e.getLatitude(), e.getLongitude(), -1)); + res.add(way); + } + } }