diff --git a/android/jni/com/mapswithme/maps/routing/RoutingOptions.cpp b/android/jni/com/mapswithme/maps/routing/RoutingOptions.cpp index 1cf670a297..7d601d20c8 100644 --- a/android/jni/com/mapswithme/maps/routing/RoutingOptions.cpp +++ b/android/jni/com/mapswithme/maps/routing/RoutingOptions.cpp @@ -10,6 +10,12 @@ routing::RoutingOptions::Road makeValue(jint option) return static_cast(road); } +routing::EdgeEstimator::Strategy makeStrategyValue(jint strategy) +{ + int convertedStrat = static_cast(strategy); + return static_cast(convertedStrat); +} + extern "C" { @@ -22,6 +28,14 @@ Java_com_mapswithme_maps_routing_RoutingOptions_nativeHasOption(JNIEnv * env, jc return static_cast(routingOptions.Has(road)); } +JNIEXPORT jint JNICALL +Java_com_mapswithme_maps_routing_RoutingOptions_nativeGetStrategy(JNIEnv * env, jclass clazz) +{ + CHECK(g_framework, ("Framework isn't created yet!")); + routing::EdgeEstimator::Strategy routingStrategy = routing::EdgeEstimator::LoadRoutingStrategyFromSettings(); + return static_cast(routingStrategy); +} + JNIEXPORT void JNICALL Java_com_mapswithme_maps_routing_RoutingOptions_nativeAddOption(JNIEnv * env, jclass clazz, jint option) { @@ -32,7 +46,6 @@ Java_com_mapswithme_maps_routing_RoutingOptions_nativeAddOption(JNIEnv * env, jc routing::RoutingOptions::SaveCarOptionsToSettings(routingOptions); } - JNIEXPORT void JNICALL Java_com_mapswithme_maps_routing_RoutingOptions_nativeRemoveOption(JNIEnv * env, jclass clazz, jint option) { @@ -42,4 +55,11 @@ Java_com_mapswithme_maps_routing_RoutingOptions_nativeRemoveOption(JNIEnv * env, routingOptions.Remove(road); routing::RoutingOptions::SaveCarOptionsToSettings(routingOptions); } + +JNIEXPORT void JNICALL +Java_com_mapswithme_maps_routing_RoutingOptions_nativeSetStrategy(JNIEnv * env, jclass clazz, jint strategy) +{ + CHECK(g_framework, ("Framework isn't created yet!")); + routing::EdgeEstimator::SaveRoutingStrategyToSettings(makeStrategyValue(strategy)); +} } diff --git a/android/res/layout/fragment_driving_options.xml b/android/res/layout/fragment_driving_options.xml index acfaa9c0d5..67febd740e 100644 --- a/android/res/layout/fragment_driving_options.xml +++ b/android/res/layout/fragment_driving_options.xml @@ -110,4 +110,46 @@ android:layout_width="wrap_content" android:layout_height="match_parent"/> + + + + + + + diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml index f9508a9d45..a70bc9a550 100644 --- a/android/res/values/strings.xml +++ b/android/res/values/strings.xml @@ -651,10 +651,14 @@ Online editing Routing options Avoid on every route + Routing Strategy Toll roads Unpaved roads Ferry crossings Motorways + Fastest + Shortest + Fewer turns Unable to calculate route Unfortunately, we couldn\'t find a route, probably due to the options you have chosen. Please change the settings and try again. Define roads to avoid diff --git a/android/src/com/mapswithme/maps/routing/RoutingOptions.java b/android/src/com/mapswithme/maps/routing/RoutingOptions.java index fcb240e9cb..99d47f8dfa 100644 --- a/android/src/com/mapswithme/maps/routing/RoutingOptions.java +++ b/android/src/com/mapswithme/maps/routing/RoutingOptions.java @@ -3,6 +3,7 @@ package com.mapswithme.maps.routing; import androidx.annotation.NonNull; import com.mapswithme.maps.settings.RoadType; +import com.mapswithme.maps.settings.RoutingStrategyType; import java.util.HashSet; import java.util.Set; @@ -24,9 +25,21 @@ public class RoutingOptions return nativeHasOption(roadType.ordinal()); } + public static int getStrategy() + { + return nativeGetStrategy(); + } + + public static void setStrategy(@NonNull RoutingStrategyType routingStrategyType) + { + nativeSetStrategy(routingStrategyType.ordinal()); + } + private static native void nativeAddOption(int option); private static native void nativeRemoveOption(int option); private static native boolean nativeHasOption(int option); + private static native int nativeGetStrategy(); + private static native void nativeSetStrategy(int strategy); public static boolean hasAnyOptions() { @@ -49,4 +62,10 @@ public class RoutingOptions } return roadTypes; } + + public static RoutingStrategyType getActiveRoutingStrategyType() + { + int strategyType = getStrategy(); + return RoutingStrategyType.values()[strategyType]; + } } diff --git a/android/src/com/mapswithme/maps/settings/DrivingOptionsFragment.java b/android/src/com/mapswithme/maps/settings/DrivingOptionsFragment.java index 9bdc251b5a..ef9d6b4855 100644 --- a/android/src/com/mapswithme/maps/settings/DrivingOptionsFragment.java +++ b/android/src/com/mapswithme/maps/settings/DrivingOptionsFragment.java @@ -6,6 +6,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CompoundButton; +import android.widget.RadioGroup; import android.widget.Switch; import androidx.annotation.NonNull; @@ -25,8 +26,11 @@ import java.util.Set; public class DrivingOptionsFragment extends BaseMwmToolbarFragment { public static final String BUNDLE_ROAD_TYPES = "road_types"; + public static final String BUNDLE_ROUTING_STRATEGY_TYPE = "routing_strategy_type"; @NonNull private Set mRoadTypes = Collections.emptySet(); + @NonNull + private RoutingStrategyType mStrategy = RoutingStrategyType.Fastest; @Nullable @Override @@ -38,6 +42,9 @@ public class DrivingOptionsFragment extends BaseMwmToolbarFragment mRoadTypes = savedInstanceState != null && savedInstanceState.containsKey(BUNDLE_ROAD_TYPES) ? makeRouteTypes(savedInstanceState) : RoutingOptions.getActiveRoadTypes(); + mStrategy = savedInstanceState != null && savedInstanceState.containsKey(BUNDLE_ROUTING_STRATEGY_TYPE) + ? makeRoutingStrategyType(savedInstanceState) + : RoutingOptions.getActiveRoutingStrategyType(); return root; } @@ -53,6 +60,13 @@ public class DrivingOptionsFragment extends BaseMwmToolbarFragment return result; } + @NonNull + private RoutingStrategyType makeRoutingStrategyType(@NonNull Bundle bundle) + { + RoutingStrategyType result = Objects.requireNonNull((RoutingStrategyType)bundle.getSerializable(BUNDLE_ROUTING_STRATEGY_TYPE)); + return result; + } + @Override public void onSaveInstanceState(@NonNull Bundle outState) { @@ -63,12 +77,15 @@ public class DrivingOptionsFragment extends BaseMwmToolbarFragment savedRoadTypes.add(each.ordinal()); } outState.putIntegerArrayList(BUNDLE_ROAD_TYPES, savedRoadTypes); + + outState.putSerializable(BUNDLE_ROUTING_STRATEGY_TYPE, mStrategy); } private boolean areSettingsNotChanged() { Set lastActiveRoadTypes = RoutingOptions.getActiveRoadTypes(); - return mRoadTypes.equals(lastActiveRoadTypes); + RoutingStrategyType lastActiveStrategyType = RoutingOptions.getActiveRoutingStrategyType(); + return mRoadTypes.equals(lastActiveRoadTypes) && mStrategy.equals(lastActiveStrategyType); } @Override @@ -104,6 +121,26 @@ public class DrivingOptionsFragment extends BaseMwmToolbarFragment CompoundButton.OnCheckedChangeListener dirtyBtnListener = new ToggleRoutingOptionListener(RoadType.Dirty); dirtyRoadsBtn.setOnCheckedChangeListener(dirtyBtnListener); + + RadioGroup strategyRadioGroup = root.findViewById(R.id.routing_strategy_types); + RoutingStrategyType currentStrategy = RoutingOptions.getActiveRoutingStrategyType(); + int currentCheckedStrategyId; + switch (currentStrategy) + { + case Shortest: + currentCheckedStrategyId = R.id.use_strategy_shortest; + break; + case FewerTurns: + currentCheckedStrategyId = R.id.use_strategy_fewerTurns; + break; + default: + currentCheckedStrategyId = R.id.use_strategy_fastest; + break; + } + strategyRadioGroup.check(currentCheckedStrategyId); + RadioGroup.OnCheckedChangeListener strategyRadioGroupListener = + new ToggleRoutingStrategyListener(); + strategyRadioGroup.setOnCheckedChangeListener(strategyRadioGroupListener); } private static class ToggleRoutingOptionListener implements CompoundButton.OnCheckedChangeListener @@ -125,4 +162,26 @@ public class DrivingOptionsFragment extends BaseMwmToolbarFragment RoutingOptions.removeOption(mRoadType); } } + + private static class ToggleRoutingStrategyListener implements RadioGroup.OnCheckedChangeListener + { + private ToggleRoutingStrategyListener() {} + + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) + { + switch (checkedId) + { + case R.id.use_strategy_fastest: + RoutingOptions.setStrategy(RoutingStrategyType.Fastest); + break; + case R.id.use_strategy_shortest: + RoutingOptions.setStrategy(RoutingStrategyType.Shortest); + break; + case R.id.use_strategy_fewerTurns: + RoutingOptions.setStrategy(RoutingStrategyType.FewerTurns); + break; + } + } + } } diff --git a/android/src/com/mapswithme/maps/settings/RoutingStrategyType.java b/android/src/com/mapswithme/maps/settings/RoutingStrategyType.java new file mode 100644 index 0000000000..8e5a063f6b --- /dev/null +++ b/android/src/com/mapswithme/maps/settings/RoutingStrategyType.java @@ -0,0 +1,8 @@ +package com.mapswithme.maps.settings; + +public enum RoutingStrategyType +{ + Fastest, + Shortest, + FewerTurns +} diff --git a/qt/routing_settings_dialog.cpp b/qt/routing_settings_dialog.cpp index 72a3f8a65b..3e64132969 100644 --- a/qt/routing_settings_dialog.cpp +++ b/qt/routing_settings_dialog.cpp @@ -3,6 +3,7 @@ #include "map/framework.hpp" #include "routing/router.hpp" +#include "routing/edge_estimator.hpp" #include "platform/settings.hpp" @@ -26,12 +27,26 @@ std::string const kUseCachedRoutingSettings = "use_cached_settings_desktop"; std::string const kStartCoordsCachedSettings = "start_coords_desktop"; std::string const kFinishCoordsCachedSettings = "finish_coords_desktop"; std::string const kRouterTypeCachedSettings = "router_type_desktop"; +std::string const kRouterStrategyCachedSettings = "router_strategy"; +std::string const kAvoidRoutingOptionSettings = "avoid_routing_options_car"; int constexpr DefaultRouterIndex() { // Car router by default. return static_cast(routing::RouterType::Vehicle); } + +int constexpr DefaultStrategyIndex() +{ + // Routing strategy by default + return static_cast(routing::EdgeEstimator::Strategy::Fastest); +} + +int constexpr DefaultAvoidOptionIndex() +{ + // Avoid routing option by default + return static_cast(routing::RoutingOptions::Road::Usual); +} } // namespace // static @@ -95,6 +110,8 @@ void RoutingSettings::LoadSession(Framework & framework) settings::Delete(kStartCoordsCachedSettings); settings::Delete(kFinishCoordsCachedSettings); settings::Delete(kRouterTypeCachedSettings); + settings::Delete(kRouterStrategyCachedSettings); + settings::Delete(kAvoidRoutingOptionSettings); } } @@ -135,6 +152,20 @@ RoutingSettings::RoutingSettings(QWidget * parent, Framework & framework) m_routerType->insertItem(static_cast(RouterType::Transit), "transit"); form->addRow("Choose router:", m_routerType); + m_routerStrategy = new QComboBox(frame); + m_routerStrategy->insertItem(static_cast(EdgeEstimator::Strategy::Fastest), "fastest"); + m_routerStrategy->insertItem(static_cast(EdgeEstimator::Strategy::Shortest), "shortest"); + m_routerStrategy->insertItem(static_cast(EdgeEstimator::Strategy::FewerTurns), "fewer turns"); + form->addRow("Choose strategy:", m_routerStrategy); + + m_avoidRoutingOptions = new QComboBox(frame); + m_avoidRoutingOptions->insertItem(static_cast(RoutingOptions::Road::Dirty), "dirty"); + m_avoidRoutingOptions->insertItem(static_cast(RoutingOptions::Road::Ferry), "ferry"); + m_avoidRoutingOptions->insertItem(static_cast(RoutingOptions::Road::Motorway), "motorway"); + m_avoidRoutingOptions->insertItem(static_cast(RoutingOptions::Road::Toll), "toll"); + m_avoidRoutingOptions->insertItem(static_cast(RoutingOptions::Road::Usual), "usual"); + form->addRow("Choose avoid option:", m_avoidRoutingOptions); + m_showTurnsCheckbox = new QCheckBox({}, frame); form->addRow("Show turns:", m_showTurnsCheckbox); @@ -191,6 +222,8 @@ bool RoutingSettings::SaveSettings() settings::Set(kUseDebugGuideSettings, m_useDebugGuideCheckbox->isChecked()); settings::Set(kUseCachedRoutingSettings, m_saveSessionCheckbox->isChecked()); settings::Set(kRouterTypeCachedSettings, m_routerType->currentIndex()); + settings::Set(kRouterStrategyCachedSettings, m_routerStrategy->currentIndex()); + settings::Set(kAvoidRoutingOptionSettings, m_avoidRoutingOptions->currentIndex()); return ValidateAndSaveCoordsFromInput(); } @@ -208,6 +241,14 @@ void RoutingSettings::LoadSettings() settings::TryGet(kRouterTypeCachedSettings, routerType); m_routerType->setCurrentIndex(routerType); + int routerStrategy = DefaultStrategyIndex(); + settings::TryGet(kRouterStrategyCachedSettings, routerStrategy); + m_routerStrategy->setCurrentIndex(routerStrategy); + + int avoidRoutingOption = DefaultAvoidOptionIndex(); + settings::TryGet(kAvoidRoutingOptionSettings, avoidRoutingOption); + m_avoidRoutingOptions->setCurrentIndex(avoidRoutingOption); + bool showTurns = false; settings::TryGet(kShowTurnsSettings, showTurns); m_showTurnsCheckbox->setChecked(showTurns); diff --git a/qt/routing_settings_dialog.hpp b/qt/routing_settings_dialog.hpp index 0d17b16196..e97d9962b4 100644 --- a/qt/routing_settings_dialog.hpp +++ b/qt/routing_settings_dialog.hpp @@ -40,6 +40,8 @@ private: QLineEdit * m_finishInput; QComboBox * m_routerType; + QComboBox * m_routerStrategy; + QComboBox * m_avoidRoutingOptions; QCheckBox * m_showTurnsCheckbox; QCheckBox * m_useDebugGuideCheckbox; QCheckBox * m_saveSessionCheckbox; diff --git a/routing/async_router.cpp b/routing/async_router.cpp index 8eb79b5ea2..703bc7d1d7 100644 --- a/routing/async_router.cpp +++ b/routing/async_router.cpp @@ -1,4 +1,5 @@ #include "routing/async_router.hpp" +#include "routing/routing_options.hpp" #include "geometry/mercator.hpp" @@ -344,6 +345,12 @@ void AsyncRouter::CalculateRoute() if (absentRegionsFinder) absentRegionsFinder->GenerateAbsentRegions(checkpoints, delegateProxy->GetDelegate()); + RoutingOptions const routingOptions = RoutingOptions::LoadCarOptionsFromSettings(); + router->SetEstimatorOptions(routingOptions.GetOptions()); + + EdgeEstimator::Strategy routingStrategy = EdgeEstimator::LoadRoutingStrategyFromSettings(); + router->SetEstimatorStrategy(routingStrategy); + // Run basic request. code = router->CalculateRoute(checkpoints, startDirection, adjustToPrevRoute, delegateProxy->GetDelegate(), *route); diff --git a/routing/edge_estimator.cpp b/routing/edge_estimator.cpp index ca15a3dfb6..5c1191bc35 100644 --- a/routing/edge_estimator.cpp +++ b/routing/edge_estimator.cpp @@ -1,9 +1,12 @@ #include "routing/edge_estimator.hpp" +#include "platform/settings.hpp" + #include "routing/geometry.hpp" #include "routing/latlon_with_altitude.hpp" #include "routing/routing_exceptions.hpp" #include "routing/routing_helpers.hpp" +#include "routing/routing_options.hpp" #include "routing/traffic_stash.hpp" #include "traffic/speed_groups.hpp" @@ -50,19 +53,23 @@ double CalcTrafficFactor(SpeedGroup speedGroup) template double CalcClimbSegment(EdgeEstimator::Purpose purpose, Segment const & segment, - RoadGeometry const & road, GetClimbPenalty && getClimbPenalty) + RoadGeometry const & road, GetClimbPenalty && getClimbPenalty, EdgeEstimator::Strategy strategy) { LatLonWithAltitude const & from = road.GetJunction(segment.GetPointId(false /* front */)); LatLonWithAltitude const & to = road.GetJunction(segment.GetPointId(true /* front */)); SpeedKMpH const & speed = road.GetSpeed(segment.IsForward()); double const distance = road.GetDistance(segment.GetSegmentIdx()); - double const speedMpS = + double speedMpS = KmphToMps(purpose == EdgeEstimator::Purpose::Weight ? speed.m_weight : speed.m_eta); + if (strategy == EdgeEstimator::Strategy::Shortest && purpose == EdgeEstimator::Purpose::Weight) + { + speedMpS = 1.0; + } CHECK_GREATER(speedMpS, 0.0, ("from:", from.GetLatLon(), "to:", to.GetLatLon(), "speed:", speed)); double const timeSec = distance / speedMpS; - if (base::AlmostEqualAbs(distance, 0.0, 0.1)) + if (base::AlmostEqualAbs(distance, 0.0, 0.1) || strategy == EdgeEstimator::Strategy::Shortest) return timeSec; double const altitudeDiff = @@ -118,6 +125,26 @@ double GetCarClimbPenalty(EdgeEstimator::Purpose /* purpose */, double /* tangen } // EdgeEstimator ----------------------------------------------------------------------------------- + +string const EdgeEstimator::kRoutingStrategySettings = "router_strategy"; + +// static +EdgeEstimator::Strategy EdgeEstimator::LoadRoutingStrategyFromSettings() +{ + int mode = 0; + if (!settings::Get(kRoutingStrategySettings, mode)) + mode = 0; + + return (EdgeEstimator::Strategy) mode; +} + +// static +void EdgeEstimator::SaveRoutingStrategyToSettings(Strategy strategy) +{ + settings::Set(kRoutingStrategySettings, + strings::to_string(static_cast(strategy))); +} + EdgeEstimator::EdgeEstimator(double maxWeightSpeedKMpH, SpeedKMpH const & offroadSpeedKMpH, DataSource * /*dataSourcePtr*/, std::shared_ptr /*numMwmIds*/) : m_maxWeightSpeedMpS(KmphToMps(maxWeightSpeedKMpH)) @@ -131,6 +158,9 @@ EdgeEstimator::EdgeEstimator(double maxWeightSpeedKMpH, SpeedKMpH const & offroa if (m_offroadSpeedKMpH.m_eta != kNotUsed) CHECK_GREATER_OR_EQUAL(m_maxWeightSpeedMpS, KmphToMps(m_offroadSpeedKMpH.m_eta), ()); + + m_avoidRoutingOptions = RoutingOptions(); + m_strategy = EdgeEstimator::Strategy::Fastest; } double EdgeEstimator::CalcHeuristic(ms::LatLon const & from, ms::LatLon const & to) const @@ -209,6 +239,26 @@ double EdgeEstimator::CalcOffroad(ms::LatLon const & from, ms::LatLon const & to return TimeBetweenSec(from, to, KmphToMps(offroadSpeedKMpH)); } +RoutingOptions EdgeEstimator::GetAvoidRoutingOptions() const +{ + return m_avoidRoutingOptions; +} + +void EdgeEstimator::SetAvoidRoutingOptions(RoutingOptions::RoadType options) +{ + m_avoidRoutingOptions.SetOptions(options); +} + +EdgeEstimator::Strategy EdgeEstimator::GetStrategy() const +{ + return m_strategy; +} + +void EdgeEstimator::SetStrategy(EdgeEstimator::Strategy strategy) +{ + m_strategy = strategy; +} + // PedestrianEstimator ----------------------------------------------------------------------------- class PedestrianEstimator final : public EdgeEstimator { @@ -219,7 +269,28 @@ public: } // EdgeEstimator overrides: - double GetUTurnPenalty(Purpose /* purpose */) const override { return 0.0 /* seconds */; } + double GetUTurnPenalty(Purpose purpose) const override + { + if (purpose == EdgeEstimator::Purpose::Weight) + { + if (this->GetStrategy() == EdgeEstimator::Strategy::FewerTurns) + { + return 60 * 60; + } + } + return 0.0 /* seconds */; + } + double GetTurnPenalty(Purpose purpose) const override + { + if (purpose == EdgeEstimator::Purpose::Weight) + { + if (this->GetStrategy() == EdgeEstimator::Strategy::FewerTurns) + { + return 60 * 60; + } + } + return 0.0; + } // Based on: https://confluence.mail.ru/display/MAPSME/Ferries double GetFerryLandingPenalty(Purpose purpose) const override { @@ -234,7 +305,17 @@ public: double CalcSegmentWeight(Segment const & segment, RoadGeometry const & road, Purpose purpose) const override { - return CalcClimbSegment(purpose, segment, road, GetPedestrianClimbPenalty); + double result = CalcClimbSegment(purpose, segment, road, GetPedestrianClimbPenalty, this->GetStrategy()); + + if (purpose == EdgeEstimator::Purpose::Weight) + { + if (!road.SuitableForOptions(EdgeEstimator::GetAvoidRoutingOptions())) + { + result += (24 * 60 * 60); + } + } + + return result; } }; @@ -248,7 +329,28 @@ public: } // EdgeEstimator overrides: - double GetUTurnPenalty(Purpose /* purpose */) const override { return 20.0 /* seconds */; } + double GetUTurnPenalty(Purpose purpose) const override + { + if (purpose == EdgeEstimator::Purpose::Weight) + { + if (this->GetStrategy() == EdgeEstimator::Strategy::FewerTurns) + { + return 60 * 60; + } + } + return 20.0 /* seconds */; + } + double GetTurnPenalty(Purpose purpose) const override + { + if (purpose == EdgeEstimator::Purpose::Weight) + { + if (this->GetStrategy() == EdgeEstimator::Strategy::FewerTurns) + { + return 60 * 60; + } + } + return 0.0; + } // Based on: https://confluence.mail.ru/display/MAPSME/Ferries double GetFerryLandingPenalty(Purpose purpose) const override { @@ -263,7 +365,17 @@ public: double CalcSegmentWeight(Segment const & segment, RoadGeometry const & road, Purpose purpose) const override { - return CalcClimbSegment(purpose, segment, road, GetBicycleClimbPenalty); + double result = CalcClimbSegment(purpose, segment, road, GetBicycleClimbPenalty, this->GetStrategy()); + + if (purpose == EdgeEstimator::Purpose::Weight) + { + if (!road.SuitableForOptions(EdgeEstimator::GetAvoidRoutingOptions())) + { + result += (24 * 60 * 60); + } + } + + return result; } }; @@ -277,7 +389,8 @@ public: // EdgeEstimator overrides: double CalcSegmentWeight(Segment const & segment, RoadGeometry const & road, Purpose purpose) const override; - double GetUTurnPenalty(Purpose /* purpose */) const override; + double GetUTurnPenalty(Purpose purpose) const override; + double GetTurnPenalty(Purpose purpose) const override; double GetFerryLandingPenalty(Purpose purpose) const override; private: @@ -292,14 +405,34 @@ CarEstimator::CarEstimator(DataSource * dataSourcePtr, std::shared_ptrGetStrategy() == EdgeEstimator::Strategy::FewerTurns) + { + return 60 * 60; + } + } + // Adds 2 minutes penalty for U-turn. The value is quite arbitrary // and needs to be properly selected after a number of real-world // experiments. return 2 * 60; // seconds } +double CarEstimator::GetTurnPenalty(Purpose purpose) const +{ + if (purpose == EdgeEstimator::Purpose::Weight) + { + if (this->GetStrategy() == EdgeEstimator::Strategy::FewerTurns) + { + return 60 * 60; + } + } + return 0.0; +} + double CarEstimator::GetFerryLandingPenalty(Purpose purpose) const { switch (purpose) @@ -313,7 +446,15 @@ double CarEstimator::GetFerryLandingPenalty(Purpose purpose) const double CarEstimator::CalcSegmentWeight(Segment const & segment, RoadGeometry const & road, Purpose purpose) const { - double result = CalcClimbSegment(purpose, segment, road, GetCarClimbPenalty); + double result = CalcClimbSegment(purpose, segment, road, GetCarClimbPenalty, this->GetStrategy()); + + if (purpose == EdgeEstimator::Purpose::Weight) + { + if (!road.SuitableForOptions(EdgeEstimator::GetAvoidRoutingOptions())) + { + result += (24 * 60 * 60); + } + } if (m_trafficStash) { diff --git a/routing/edge_estimator.hpp b/routing/edge_estimator.hpp index f7c36d4aa2..8079f14ef0 100644 --- a/routing/edge_estimator.hpp +++ b/routing/edge_estimator.hpp @@ -1,5 +1,6 @@ #pragma once +#include "routing/routing_options.hpp" #include "routing/segment.hpp" #include "routing/vehicle_mask.hpp" @@ -28,10 +29,22 @@ public: ETA }; + static std::string const kRoutingStrategySettings; + + enum class Strategy + { + Fastest, + Shortest, + FewerTurns + }; + EdgeEstimator(double maxWeightSpeedKMpH, SpeedKMpH const & offroadSpeedKMpH, DataSource * dataSourcePtr = nullptr, std::shared_ptr numMwmIds = nullptr); virtual ~EdgeEstimator() = default; + static Strategy LoadRoutingStrategyFromSettings(); + static void SaveRoutingStrategyToSettings(Strategy strategy); + double CalcHeuristic(ms::LatLon const & from, ms::LatLon const & to) const; // Estimates time in seconds it takes to go from point |from| to point |to| along a leap (fake) // edge |from|-|to| using real features. @@ -46,9 +59,16 @@ public: // Estimates time in seconds it takes to go from point |from| to point |to| along direct fake edge. double CalcOffroad(ms::LatLon const & from, ms::LatLon const & to, Purpose purpose) const; + RoutingOptions GetAvoidRoutingOptions() const; + void SetAvoidRoutingOptions(RoutingOptions::RoadType options); + + Strategy GetStrategy() const; + void SetStrategy(Strategy strategy); + virtual double CalcSegmentWeight(Segment const & segment, RoadGeometry const & road, Purpose purpose) const = 0; virtual double GetUTurnPenalty(Purpose purpose) const = 0; + virtual double GetTurnPenalty(Purpose purpose) const = 0; virtual double GetFerryLandingPenalty(Purpose purpose) const = 0; static std::shared_ptr Create(VehicleType vehicleType, double maxWeighSpeedKMpH, @@ -66,6 +86,8 @@ public: private: double const m_maxWeightSpeedMpS; SpeedKMpH const m_offroadSpeedKMpH; + RoutingOptions m_avoidRoutingOptions; + Strategy m_strategy; //DataSource * m_dataSourcePtr; //std::shared_ptr m_numMwmIds; diff --git a/routing/index_graph.cpp b/routing/index_graph.cpp index 399b1d07f8..1a2ff38916 100644 --- a/routing/index_graph.cpp +++ b/routing/index_graph.cpp @@ -12,6 +12,7 @@ #include "base/timer.hpp" #include +#include #include #include #include @@ -254,8 +255,8 @@ void IndexGraph::GetNeighboringEdges(astar::VertexData con if (!road.IsValid()) return; - if (useRoutingOptions && !road.SuitableForOptions(m_avoidRoutingOptions)) - return; +// if (useRoutingOptions && !road.SuitableForOptions(m_avoidRoutingOptions)) +// return; bool const bidirectional = !road.IsOneWay(); auto const & from = fromVertexData.m_vertex; @@ -282,8 +283,8 @@ void IndexGraph::GetSegmentCandidateForRoadPoint(RoadPoint const & rp, NumMwmId if (!road.IsValid()) return; - if (!road.SuitableForOptions(m_avoidRoutingOptions)) - return; +// if (!road.SuitableForOptions(m_avoidRoutingOptions)) +// return; bool const bidirectional = !road.IsOneWay(); auto const pointId = rp.GetPointId(); @@ -514,6 +515,9 @@ RouteWeight IndexGraph::GetPenalties(EdgeEstimator::Purpose purpose, Segment con if (IsUTurn(u, v)) weightPenalty += m_estimator->GetUTurnPenalty(purpose); + if (IsTurn(u, v)) + weightPenalty += m_estimator->GetTurnPenalty(purpose); + if (IsBoarding(fromPenaltyData.m_isFerry, toPenaltyData.m_isFerry)) weightPenalty += m_estimator->GetFerryLandingPenalty(purpose); @@ -549,6 +553,57 @@ bool IndexGraph::IsUTurnAndRestricted(Segment const & parent, Segment const & ch return uTurn.m_atTheEnd && turnPoint == n - 1; } +bool IndexGraph::IsTurn(Segment const & u, Segment const & v) const +{ + // Boundary check for segmentIdx + if (u.GetSegmentIdx() == 0 && !(u.IsForward())) + { + return false; + } + + if (v.GetSegmentIdx() == 0 && !(v.IsForward())) + { + return false; + } + + auto geoU = GetRoadGeometry(u.GetFeatureId()); + auto startPointU = geoU.GetPoint(u.GetSegmentIdx()); + auto endPointU = geoU.GetPoint(u.IsForward() ? u.GetSegmentIdx() + 1: u.GetSegmentIdx() - 1); + + auto geoV = GetRoadGeometry(v.GetFeatureId()); + auto startPointV = geoV.GetPoint(v.GetSegmentIdx()); + auto endPointV = geoV.GetPoint(v.IsForward() ? v.GetSegmentIdx() + 1: v.GetSegmentIdx() - 1); + + if (!(endPointU == startPointV)) + { + return false; + } + + double vectorU[2] = {endPointU.m_lat - startPointU.m_lat, endPointU.m_lon - startPointU.m_lon}; + double vectorV[2] = {endPointV.m_lat - startPointV.m_lat, endPointV.m_lon - startPointV.m_lon}; + + //dot product + double dot = vectorU[0] * vectorV[0] + vectorU[1] * vectorV[1]; + + //determinant + double det = vectorU[0] * vectorV[1] - vectorU[1] * vectorV[0]; + + //calculate the anlge + double angle = atan2(det, dot); + + //convert to degree value + angle = angle * 180 / 3.141592; + + if (abs(angle) >= 15) + { + return true; + } + else + { + return false; + } +} + RouteWeight IndexGraph::CalculateEdgeWeight(EdgeEstimator::Purpose purpose, bool isOutgoing, Segment const & from, Segment const & to, std::optional const & prevWeight) const @@ -556,7 +611,18 @@ RouteWeight IndexGraph::CalculateEdgeWeight(EdgeEstimator::Purpose purpose, bool auto const & segment = isOutgoing ? to : from; auto const & road = GetRoadGeometry(segment.GetFeatureId()); +// m_estimator->SetAvoidRoutingOptions(this->m_avoidRoutingOptions.GetOptions()); + auto const weight = RouteWeight(m_estimator->CalcSegmentWeight(segment, road, purpose)); + + if (purpose == EdgeEstimator::Purpose::Weight) + { + if(m_estimator->GetStrategy() == EdgeEstimator::Strategy::Shortest) + { + return weight; + } + } + auto const penalties = GetPenalties(purpose, isOutgoing ? from : to, isOutgoing ? to : from, prevWeight); return weight + penalties; diff --git a/routing/index_graph.hpp b/routing/index_graph.hpp index be7a1bab3c..e0cb7a2a4b 100644 --- a/routing/index_graph.hpp +++ b/routing/index_graph.hpp @@ -133,6 +133,8 @@ public: bool IsUTurnAndRestricted(Segment const & parent, Segment const & child, bool isOutgoing) const; + bool IsTurn(Segment const & u, Segment const & v) const; + /// @param[in] isOutgoing true, when movig from -> to, false otherwise. /// @param[in] prevWeight used for fetching access:conditional. /// I suppose :) its time when user will be at the end of |from| (|to| if \a isOutgoing == false) segment. diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 8394252dd2..0e2606058d 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -326,6 +326,10 @@ bool IndexRouter::FindClosestProjectionToRoad(m2::PointD const & point, return true; } +void IndexRouter::SetEstimatorOptions(RoutingOptions::RoadType options) {m_estimator ->SetAvoidRoutingOptions(options);} + +void IndexRouter::SetEstimatorStrategy(EdgeEstimator::Strategy strategy) {m_estimator ->SetStrategy(strategy);} + void IndexRouter::SetGuides(GuidesTracks && guides) { m_guides = GuidesConnections(guides); } RouterResultCode IndexRouter::CalculateRoute(Checkpoints const & checkpoints, diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 6590854f9a..47c78fedb3 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -14,6 +14,7 @@ #include "routing/regions_decl.hpp" #include "routing/router.hpp" #include "routing/routing_callbacks.hpp" +#include "routing/routing_options.hpp" #include "routing/segment.hpp" #include "routing/segmented_route.hpp" @@ -77,6 +78,10 @@ public: std::string GetName() const override { return m_name; } void ClearState() override; + void SetEstimatorOptions(RoutingOptions::RoadType options) override; + + void SetEstimatorStrategy(EdgeEstimator::Strategy strategy) override; + void SetGuides(GuidesTracks && guides) override; RouterResultCode CalculateRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, bool adjustToPrevRoute, diff --git a/routing/router.hpp b/routing/router.hpp index 1d5220f4f6..685c003b48 100644 --- a/routing/router.hpp +++ b/routing/router.hpp @@ -1,9 +1,11 @@ #pragma once #include "routing/checkpoints.hpp" +#include "routing/edge_estimator.hpp" #include "routing/road_graph.hpp" #include "routing/router_delegate.hpp" #include "routing/routing_callbacks.hpp" +#include "routing/routing_options.hpp" #include "kml/type_utils.hpp" @@ -61,6 +63,10 @@ public: /// Clear all temporary buffers. virtual void ClearState() {} + virtual void SetEstimatorOptions(RoutingOptions::RoadType options) {} + + virtual void SetEstimatorStrategy(EdgeEstimator::Strategy strategy) {} + virtual void SetGuides(GuidesTracks && guides) = 0; /// Override this function with routing implementation. diff --git a/routing/routing_options.cpp b/routing/routing_options.cpp index 8a7f144a53..42291deec9 100644 --- a/routing/routing_options.cpp +++ b/routing/routing_options.cpp @@ -55,6 +55,11 @@ bool RoutingOptions::Has(RoutingOptions::Road type) const return (m_options & static_cast(type)) != 0; } +void RoutingOptions::SetOptions(RoadType options) +{ + m_options = options; +} + // RoutingOptionsClassifier --------------------------------------------------------------------------- RoutingOptionsClassifier::RoutingOptionsClassifier() diff --git a/routing/routing_options.hpp b/routing/routing_options.hpp index 0b3faedca6..abef2498f0 100644 --- a/routing/routing_options.hpp +++ b/routing/routing_options.hpp @@ -36,6 +36,7 @@ public: bool Has(Road type) const; RoadType GetOptions() const { return m_options; } + void SetOptions(RoadType options); private: RoadType m_options = 0; diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index 425f9a7d97..86e57b793c 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -146,6 +146,7 @@ double WeightedEdgeEstimator::CalcSegmentWeight(Segment const & segment, } double WeightedEdgeEstimator::GetUTurnPenalty(Purpose purpose) const { return 0.0; } +double WeightedEdgeEstimator::GetTurnPenalty(Purpose purpose) const { return 0.0; } double WeightedEdgeEstimator::GetFerryLandingPenalty(Purpose purpose) const { return 0.0; } // TestIndexGraphTopology -------------------------------------------------------------------------- diff --git a/routing/routing_tests/index_graph_tools.hpp b/routing/routing_tests/index_graph_tools.hpp index 4587a1388e..c1893719e5 100644 --- a/routing/routing_tests/index_graph_tools.hpp +++ b/routing/routing_tests/index_graph_tools.hpp @@ -181,6 +181,7 @@ public: EdgeEstimator::Purpose purpose) const override; double GetUTurnPenalty(Purpose purpose) const override; + double GetTurnPenalty(Purpose purpose) const override; double GetFerryLandingPenalty(Purpose purpose) const override; private: diff --git a/tools/android/set_up_android.py b/tools/android/set_up_android.py index ed5dc34931..73718315db 100755 --- a/tools/android/set_up_android.py +++ b/tools/android/set_up_android.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/local/bin/python import os, sys, shutil, collections from optparse import OptionParser