diff --git a/android/flavors/gms-enabled/com/mapswithme/maps/location/GoogleFusedLocationProvider.java b/android/flavors/gms-enabled/com/mapswithme/maps/location/GoogleFusedLocationProvider.java index 8b7f63fac6..e7d78d0d8b 100644 --- a/android/flavors/gms-enabled/com/mapswithme/maps/location/GoogleFusedLocationProvider.java +++ b/android/flavors/gms-enabled/com/mapswithme/maps/location/GoogleFusedLocationProvider.java @@ -37,17 +37,16 @@ class GoogleFusedLocationProvider extends BaseLocationProvider // Documentation is inconsistent with the code: "returns null if no locations are available". // https://developers.google.com/android/reference/com/google/android/gms/location/LocationResult#getLastLocation() //noinspection ConstantConditions - if (location != null) - mListener.onLocationChanged(location); + if (location == null) + return; + mListener.onLocationChanged(location); } @Override public void onLocationAvailability(@NonNull LocationAvailability availability) { - if (!availability.isLocationAvailable()) { - LOGGER.w(TAG, "isLocationAvailable returned false"); - //mListener.onLocationError(ERROR_GPS_OFF); - } + if (!availability.isLocationAvailable()) + mListener.onLocationError(ERROR_GPS_OFF); } } diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index 0bdf3ac9b5..5563e46a8b 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -106,6 +106,8 @@ enum MultiTouchAction Framework::Framework() : m_lastCompass(0.0) , m_isSurfaceDestroyed(false) + , m_currentMode(location::PendingPosition) + , m_isCurrentModeInitialized(false) , m_isChoosePositionMode(false) { m_work.GetTrafficManager().SetStateListener(bind(&Framework::TrafficStateChanged, this, _1)); @@ -232,6 +234,8 @@ bool Framework::CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi p.m_surfaceHeight = oglFactory->GetHeight(); } p.m_visualScale = static_cast(dp::VisualScale(densityDpi)); + p.m_hasMyPositionState = m_isCurrentModeInitialized; + p.m_initialMyPositionState = m_currentMode; p.m_isChoosePositionMode = m_isChoosePositionMode; p.m_hints.m_isFirstLaunch = firstLaunch; p.m_hints.m_isLaunchByDeepLink = launchByDeepLink; @@ -249,7 +253,7 @@ bool Framework::CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi return true; } -bool Framework::IsDrapeEngineCreated() const +bool Framework::IsDrapeEngineCreated() { return m_work.IsDrapeEngineCreated(); } @@ -638,12 +642,23 @@ void Framework::SetMyPositionModeListener(location::TMyPositionModeChanged const m_myPositionModeSignal = fn; } -location::EMyPositionMode Framework::GetMyPositionMode() const +location::EMyPositionMode Framework::GetMyPositionMode() { - // No need in assertion here, return location::PendingPosition if no engine created. - //ASSERT(IsDrapeEngineCreated(), ()); + if (!m_isCurrentModeInitialized) + { + if (!settings::Get(settings::kLocationStateMode, m_currentMode)) + m_currentMode = location::NotFollowNoPosition; - return m_work.GetMyPositionMode(); + m_isCurrentModeInitialized = true; + } + + return m_currentMode; +} + +void Framework::OnMyPositionModeChanged(location::EMyPositionMode mode) +{ + m_currentMode = mode; + m_isCurrentModeInitialized = true; } void Framework::SwitchMyPositionNextMode() diff --git a/android/jni/com/mapswithme/maps/Framework.hpp b/android/jni/com/mapswithme/maps/Framework.hpp index 69974af199..ad693a0632 100644 --- a/android/jni/com/mapswithme/maps/Framework.hpp +++ b/android/jni/com/mapswithme/maps/Framework.hpp @@ -70,6 +70,8 @@ namespace android void MyPositionModeChanged(location::EMyPositionMode mode, bool routingActive); location::TMyPositionModeChanged m_myPositionModeSignal; + location::EMyPositionMode m_currentMode; + bool m_isCurrentModeInitialized; TrafficManager::TrafficStateChangedFn m_onTrafficStateChangedFn; TransitReadManager::TransitStateChangedFn m_onTransitStateChangedFn; @@ -91,7 +93,7 @@ namespace android bool CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi, bool firstLaunch, bool launchByDeepLink, uint32_t appVersionCode); - bool IsDrapeEngineCreated() const; + bool IsDrapeEngineCreated(); bool DestroySurfaceOnDetach(); void DetachSurface(bool destroySurface); bool AttachSurface(JNIEnv * env, jobject jSurface); @@ -158,7 +160,8 @@ namespace android // std::string GetOutdatedCountriesString(); void SetMyPositionModeListener(location::TMyPositionModeChanged const & fn); - location::EMyPositionMode GetMyPositionMode() const; + location::EMyPositionMode GetMyPositionMode(); + void OnMyPositionModeChanged(location::EMyPositionMode mode); void SwitchMyPositionNextMode(); void SetTrafficStateListener(TrafficManager::TrafficStateChangedFn const & fn); diff --git a/android/jni/com/mapswithme/maps/LocationState.cpp b/android/jni/com/mapswithme/maps/LocationState.cpp index 7d8e4b851d..4942acf310 100644 --- a/android/jni/com/mapswithme/maps/LocationState.cpp +++ b/android/jni/com/mapswithme/maps/LocationState.cpp @@ -10,6 +10,8 @@ extern "C" static void LocationStateModeChanged(location::EMyPositionMode mode, std::shared_ptr const & listener) { + g_framework->OnMyPositionModeChanged(mode); + JNIEnv * env = jni::GetEnv(); env->CallVoidMethod(*listener, jni::GetMethodID(env, *listener.get(), "onMyPositionModeChanged", "(I)V"), static_cast(mode)); diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index 02f7eea910..4b376c1792 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -124,7 +124,7 @@ public class MwmActivity extends BaseMwmFragmentActivity NoConnectionListener, MapWidgetOffsetsProvider { - private final Logger mLogger = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC); + private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC); private static final String TAG = MwmActivity.class.getSimpleName(); public static final String EXTRA_TASK = "map_task"; diff --git a/android/src/com/mapswithme/maps/location/LocationHelper.java b/android/src/com/mapswithme/maps/location/LocationHelper.java index fde5b43104..460dc8ad59 100644 --- a/android/src/com/mapswithme/maps/location/LocationHelper.java +++ b/android/src/com/mapswithme/maps/location/LocationHelper.java @@ -154,14 +154,12 @@ public enum LocationHelper implements Initializable, AppBackgroundTrack }; @SuppressWarnings("FieldCanBeLocal") - private final LocationState.LocationPendingTimeoutListener mLocationPendingTimeoutListener = () -> { - if (mActive) - { - stop(); - if (PermissionsUtils.isLocationGranted(mContext) && LocationUtils.areLocationServicesTurnedOn(mContext)) - notifyLocationNotFound(); - } - }; + private final LocationState.LocationPendingTimeoutListener mLocationPendingTimeoutListener = + () -> { + stop(); + if (PermissionsUtils.isLocationGranted(mContext) && LocationUtils.areLocationServicesTurnedOn(mContext)) + notifyLocationNotFound(); + }; @Override public void initialize(@NotNull Context context) @@ -248,7 +246,7 @@ public enum LocationHelper implements Initializable, AppBackgroundTrack mReceiverRegistered = false; } - initialStart(); + start(); } else { @@ -321,15 +319,11 @@ public enum LocationHelper implements Initializable, AppBackgroundTrack public void onLocationError(int errCode) { mLogger.d(TAG, "onLocationError(): " + errCode); - if (errCode == ERROR_NOT_SUPPORTED && - LocationUtils.areLocationServicesTurnedOn(mContext) && - !(mLocationProvider instanceof AndroidNativeProvider)) + if (errCode == ERROR_NOT_SUPPORTED && !(mLocationProvider instanceof AndroidNativeProvider)) { - // If location service is enabled, try to downgrade to the native provider first - // and restart the service before notifying the user. + // Try to downgrade to native provider first before notifying the user. mLogger.d(TAG, "Downgrading to use native provider"); mLocationProvider = new AndroidNativeProvider(mContext, this); - restart(); return; } @@ -442,16 +436,11 @@ public enum LocationHelper implements Initializable, AppBackgroundTrack */ public void restart() { + mLogger.i(TAG, "restart()"); stop(); start(); } - private void initialStart() - { - if (LocationState.nativeGetMode() != LocationState.NOT_FOLLOW_NO_POSITION) - start(); - } - /** * Adds the {@link #mCoreLocationListener} to listen location updates and notify UI. * Notifies about {@link #ERROR_DENIED} if there are no enabled location providers. @@ -462,7 +451,7 @@ public enum LocationHelper implements Initializable, AppBackgroundTrack { if (mActive) { - mLogger.w(TAG, "Provider '" + mLocationProvider + "' is already started"); + mLogger.i(TAG, "Provider '" + mLocationProvider + "' is already started"); return; } @@ -499,7 +488,7 @@ public enum LocationHelper implements Initializable, AppBackgroundTrack mLogger.i(TAG, "stop()"); if (!mActive) { - mLogger.w(TAG, "Provider '" + mLocationProvider + "' is already stopped"); + mLogger.i(TAG, "Provider '" + mLocationProvider + "' is already stopped"); return; } @@ -560,7 +549,7 @@ public enum LocationHelper implements Initializable, AppBackgroundTrack } else { - initialStart(); + restart(); } } @@ -604,6 +593,9 @@ public enum LocationHelper implements Initializable, AppBackgroundTrack mInFirstRun = false; + if (getMyPositionMode() != LocationState.NOT_FOLLOW_NO_POSITION) + throw new AssertionError("My position mode must be equal NOT_FOLLOW_NO_POSITION"); + // If there is a location we need just to pass it to the listeners, so that // my position state machine will be switched to the FOLLOW state. if (mSavedLocation != null) @@ -614,8 +606,12 @@ public enum LocationHelper implements Initializable, AppBackgroundTrack return; } - // Restart location service to show alert dialog if any location error. - restart(); + // If the location hasn't been obtained yet we need to switch to the next mode and wait for locations. + // Otherwise, try to restart location updates polling. + if (mActive) + switchToNextMode(); + else + restart(); } @Nullable diff --git a/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java b/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java index acf595afcd..7b758dbaa1 100644 --- a/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java +++ b/android/src/com/mapswithme/maps/settings/SettingsPrefsFragment.java @@ -1,5 +1,6 @@ package com.mapswithme.maps.settings; +import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -50,6 +51,8 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment { private static final int REQUEST_INSTALL_DATA = 1; + @NonNull + private final StoragePathManager mPathManager = new StoragePathManager(); @Nullable private Preference mStoragePref; @@ -68,6 +71,31 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment private LanguageData mCurrentLanguage; private String mSelectedLanguage; + private boolean singleStorageOnly() + { + return !mPathManager.hasMoreThanOneStorage(); + } + + private void updateStoragePrefs() + { + Preference old = findPreference(getString(R.string.pref_storage)); + + if (singleStorageOnly()) + { + if (old != null) + { + removePreference(getString(R.string.pref_settings_general), old); + } + } + else + { + if (old == null && mStoragePref != null) + { + getPreferenceScreen().addPreference(mStoragePref); + } + } + } + private final Preference.OnPreferenceChangeListener mEnabledListener = new Preference.OnPreferenceChangeListener() { @Override @@ -257,6 +285,7 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment mLangInfo = findPreference(getString(R.string.pref_tts_info)); mLangInfoLink = findPreference(getString(R.string.pref_tts_info_link)); initLangInfoLink(); + updateStoragePrefs(); initStoragePrefCallbacks(); initMeasureUnitsPrefsCallbacks(); initZoomPrefsCallbacks(); @@ -791,6 +820,31 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment category.removePreference(preference); } + @Override + public void onAttach(Context context) + { + super.onAttach(context); + + if (!(context instanceof Activity)) + return; + + mPathManager.startExternalStorageWatching((Activity) context, new StoragePathManager.OnStorageListChangedListener() + { + @Override + public void onStorageListChanged(List storageItems, int currentStorageIndex) + { + updateStoragePrefs(); + } + }, null); + } + + @Override + public void onDetach() + { + super.onDetach(); + mPathManager.stopExternalStorageWatching(); + } + enum ThemeMode { DEFAULT(R.string.theme_default), diff --git a/android/src/com/mapswithme/maps/settings/StoragePathManager.java b/android/src/com/mapswithme/maps/settings/StoragePathManager.java index 84e71458e5..28dce61b89 100644 --- a/android/src/com/mapswithme/maps/settings/StoragePathManager.java +++ b/android/src/com/mapswithme/maps/settings/StoragePathManager.java @@ -131,6 +131,11 @@ public class StoragePathManager } } + boolean hasMoreThanOneStorage() + { + return mItems.size() > 1; + } + List getStorageItems() { return mItems; @@ -222,7 +227,15 @@ public class StoragePathManager LOGGER.i(TAG, "Rejected " + path + ": not a directory"); continue; } - if (!dir.canWrite()) + + final long freeSize = StorageUtils.getFreeBytesAtPath(path); + if (freeSize <= 0) + { + LOGGER.i(TAG, "Rejected " + path + ": not enough space"); + continue; + } + + if (!dir.canWrite() || !StorageUtils.isDirWritable(path)) { LOGGER.i(TAG, "Rejected " + path + ": not writable"); continue; @@ -234,7 +247,6 @@ public class StoragePathManager continue; } - final long freeSize = StorageUtils.getFreeBytesAtPath(path); StorageItem item = new StorageItem(path, freeSize); if (!TextUtils.isEmpty(configDir) && configDir.equals(path)) { @@ -442,7 +454,7 @@ public class StoragePathManager throw new IllegalStateException("Cannot move maps. New path is not a directory. New path : " + newDir); if (!oldDir.isDirectory()) throw new IllegalStateException("Cannot move maps. Old path is not a directory. Old path : " + oldDir); - if (!newDir.canWrite()) + if (!StorageUtils.isDirWritable(fullNewPath)) throw new IllegalStateException("Cannot move maps. New path is not writable. New path : " + fullNewPath); } diff --git a/android/src/com/mapswithme/util/LocationUtils.java b/android/src/com/mapswithme/util/LocationUtils.java index 6d37a6c854..88084d1878 100644 --- a/android/src/com/mapswithme/util/LocationUtils.java +++ b/android/src/com/mapswithme/util/LocationUtils.java @@ -5,7 +5,6 @@ import android.content.ContentResolver; import android.content.Context; import android.location.Location; import android.location.LocationManager; -import android.os.Build; import android.provider.Settings; import android.view.Surface; @@ -100,20 +99,17 @@ public class LocationUtils return newLocation.getAccuracy() < lastAccuracy; } + + @SuppressLint("InlinedApi") + @SuppressWarnings("deprecation") public static boolean areLocationServicesTurnedOn(@NonNull Context context) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) - { - final LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); - return lm.isLocationEnabled(); - } - + final ContentResolver resolver = context.getContentResolver(); try { - return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE) + return Settings.Secure.getInt(resolver, Settings.Secure.LOCATION_MODE) != Settings.Secure.LOCATION_MODE_OFF; - } - catch (Settings.SettingNotFoundException e) + } catch (Settings.SettingNotFoundException e) { e.printStackTrace(); return false; diff --git a/android/src/com/mapswithme/util/StorageUtils.java b/android/src/com/mapswithme/util/StorageUtils.java index 3441c760d7..888c3e75b0 100644 --- a/android/src/com/mapswithme/util/StorageUtils.java +++ b/android/src/com/mapswithme/util/StorageUtils.java @@ -250,6 +250,26 @@ public class StorageUtils } } + /** + * Check if directory is writable. On some devices with KitKat (eg, Samsung S4) simple File.canWrite() returns + * true for some actually read only directories on sdcard. + * see https://code.google.com/p/android/issues/detail?id=66369 for details + * + * @param path path to ckeck + * @return result + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public static boolean isDirWritable(String path) + { + File f = new File(path, "mapsme_test_dir"); + f.mkdir(); + if (!f.exists()) + return false; + + f.delete(); + return true; + } + public static long getFreeBytesAtPath(String path) { long size = 0; diff --git a/base/base_tests/small_set_test.cpp b/base/base_tests/small_set_test.cpp index c2ebb6d5b5..6d0382ac4d 100644 --- a/base/base_tests/small_set_test.cpp +++ b/base/base_tests/small_set_test.cpp @@ -265,7 +265,7 @@ UNIT_TEST(SmallMap_Benchmark3) TEST_EQUAL(sum1, sum2, ()); TEST_EQUAL(sum1, sum3, ()); TEST_LESS(t2, t1, ()); - TEST(BenchmarkTimeLessOrNear(t3, t2, 0.05), (t3, t2)); + TEST_LESS(t3, t2, ()); LOG(LINFO, ("unordered_map time =", t1, "SmallMap time =", t2, "SmallMapBase time =", t3)); } #endif diff --git a/drape/overlay_tree.cpp b/drape/overlay_tree.cpp index b6086969e5..a14d1a9f61 100644 --- a/drape/overlay_tree.cpp +++ b/drape/overlay_tree.cpp @@ -223,8 +223,7 @@ void OverlayTree::Add(ref_ptr handle) void OverlayTree::InsertHandle(ref_ptr handle, int currentRank, ref_ptr const & parentOverlay) { - /// @todo Fires when updating country (delete-add) ?! - //ASSERT(handle->GetOverlayID().IsValid(), ()); + ASSERT(handle->GetOverlayID().IsValid(), ()); ASSERT(IsNeedUpdate(), ()); #ifdef DEBUG_OVERLAYS_OUTPUT diff --git a/drape_frontend/drape_engine.cpp b/drape_frontend/drape_engine.cpp index 9d8c5691e9..f792aa3d40 100644 --- a/drape_frontend/drape_engine.cpp +++ b/drape_frontend/drape_engine.cpp @@ -1,7 +1,7 @@ #include "drape_frontend/drape_engine.hpp" -#include "drape_frontend/gui/drape_gui.hpp" #include "drape_frontend/message_subclasses.hpp" +#include "drape_frontend/gui/drape_gui.hpp" #include "drape_frontend/my_position_controller.hpp" #include "drape_frontend/visual_params.hpp" @@ -13,16 +13,10 @@ #include -namespace df -{ using namespace std::placeholders; -namespace +namespace df { -std::string const kLocationStateMode = "LastLocationStateMode"; -std::string const kLastEnterBackground = "LastEnterBackground"; -} - DrapeEngine::DrapeEngine(Params && params) : m_myPositionModeChanged(std::move(params.m_myPositionModeChanged)) , m_viewport(std::move(params.m_viewport)) @@ -40,20 +34,24 @@ DrapeEngine::DrapeEngine(Params && params) m_threadCommutator = make_unique_dp(); m_requestedTiles = make_unique_dp(); - using namespace location; - EMyPositionMode mode = PendingPosition; - if (settings::Get(kLocationStateMode, mode) && mode == FollowAndRotate) + location::EMyPositionMode mode = params.m_initialMyPositionMode.first; + if (!params.m_initialMyPositionMode.second && !settings::Get(settings::kLocationStateMode, mode)) + { + mode = location::PendingPosition; + } + else if (mode == location::FollowAndRotate) { // If the screen rect setting in follow and rotate mode is missing or invalid, it could cause // invalid animations, so the follow and rotate mode should be discarded. m2::AnyRectD rect; if (!(settings::Get("ScreenClipRect", rect) && df::GetWorldRect().IsRectInside(rect.GetGlobalRect()))) - mode = Follow; + mode = location::Follow; } double timeInBackground = 0.0; - if (settings::Get(kLastEnterBackground, timeInBackground)) - timeInBackground = base::Timer::LocalTime() - timeInBackground; + double lastEnterBackground = 0.0; + if (settings::Get("LastEnterBackground", lastEnterBackground)) + timeInBackground = base::Timer::LocalTime() - lastEnterBackground; std::vector effects; @@ -450,16 +448,11 @@ void DrapeEngine::ModelViewChanged(ScreenBase const & screen) void DrapeEngine::MyPositionModeChanged(location::EMyPositionMode mode, bool routingActive) { - settings::Set(kLocationStateMode, mode); - if (m_myPositionModeChanged) + settings::Set(settings::kLocationStateMode, mode); + if (m_myPositionModeChanged != nullptr) m_myPositionModeChanged(mode, routingActive); } -location::EMyPositionMode DrapeEngine::GetMyPositionMode() const -{ - return m_frontend->GetMyPositionMode(); -} - void DrapeEngine::TapEvent(TapInfo const & tapInfo) { if (m_tapEventInfoHandler != nullptr) @@ -731,9 +724,8 @@ void DrapeEngine::SetKineticScrollEnabled(bool enabled) MessagePriority::Normal); } -void DrapeEngine::OnEnterForeground() +void DrapeEngine::OnEnterForeground(double backgroundTime) { - double const backgroundTime = base::Timer::LocalTime() - m_startBackgroundTime; m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp(backgroundTime), MessagePriority::High); @@ -741,9 +733,6 @@ void DrapeEngine::OnEnterForeground() void DrapeEngine::OnEnterBackground() { - m_startBackgroundTime = base::Timer::LocalTime(); - settings::Set(kLastEnterBackground, m_startBackgroundTime); - m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp(), MessagePriority::High); diff --git a/drape_frontend/drape_engine.hpp b/drape_frontend/drape_engine.hpp index b7b5b9019f..7f1f3eaaec 100644 --- a/drape_frontend/drape_engine.hpp +++ b/drape_frontend/drape_engine.hpp @@ -59,6 +59,7 @@ public: double vs, double fontsScaleFactor, gui::TWidgetsInitInfo && info, + std::pair const & initialMyPositionMode, location::TMyPositionModeChanged && myPositionModeChanged, bool allow3dBuildings, bool trafficEnabled, @@ -79,6 +80,7 @@ public: , m_vs(vs) , m_fontsScaleFactor(fontsScaleFactor) , m_info(std::move(info)) + , m_initialMyPositionMode(initialMyPositionMode) , m_myPositionModeChanged(std::move(myPositionModeChanged)) , m_allow3dBuildings(allow3dBuildings) , m_trafficEnabled(trafficEnabled) @@ -205,7 +207,7 @@ public: void SetKineticScrollEnabled(bool enabled); - void OnEnterForeground(); + void OnEnterForeground(double backgroundTime); void OnEnterBackground(); using TRequestSymbolsSizeCallback = std::function &&)>; @@ -248,8 +250,6 @@ public: void UpdateVisualScale(double vs, bool needStopRendering); void UpdateMyPositionRoutingOffset(bool useDefault, int offsetY); - location::EMyPositionMode GetMyPositionMode() const; - private: void AddUserEvent(drape_ptr && e); void PostUserEvent(drape_ptr && e); @@ -291,8 +291,6 @@ private: std::atomic m_drapeIdGenerator = 0; - double m_startBackgroundTime = 0; - friend class DrapeApi; }; } // namespace df diff --git a/drape_frontend/frontend_renderer.hpp b/drape_frontend/frontend_renderer.hpp index a84d763f1c..1d61bce6ad 100755 --- a/drape_frontend/frontend_renderer.hpp +++ b/drape_frontend/frontend_renderer.hpp @@ -150,8 +150,6 @@ public: drape_ptr const & GetScenarioManager() const; - location::EMyPositionMode GetMyPositionMode() const { return m_myPositionController->GetCurrentMode(); } - protected: void AcceptMessage(ref_ptr message) override; std::unique_ptr CreateRoutine() override; diff --git a/drape_frontend/my_position_controller.cpp b/drape_frontend/my_position_controller.cpp index 695b393273..ded3457614 100644 --- a/drape_frontend/my_position_controller.cpp +++ b/drape_frontend/my_position_controller.cpp @@ -118,6 +118,10 @@ bool IsModeChangeViewport(location::EMyPositionMode mode) MyPositionController::MyPositionController(Params && params, ref_ptr notifier) : m_notifier(notifier) + , m_mode(params.m_isRoutingActive || df::IsModeChangeViewport(params.m_initMode) + ? location::PendingPosition + : location::NotFollowNoPosition) + , m_desiredInitMode(params.m_initMode) , m_modeChangeCallback(std::move(params.m_myPositionModeCallback)) , m_hints(params.m_hints) , m_isInRouting(params.m_isRoutingActive) @@ -149,35 +153,22 @@ MyPositionController::MyPositionController(Params && params, ref_ptr= kMaxTimeInBackgroundSec) { - m_mode = PendingPosition; - m_desiredInitMode = Follow; - } - else - { - // Restore PendingPosition mode (and start location service on platform side accordingly) - // if we have routing mode or FollowXXX desired mode (usually loaded from settings). - m_desiredInitMode = params.m_initMode; - m_mode = (params.m_isRoutingActive || df::IsModeChangeViewport(m_desiredInitMode)) ? - PendingPosition : NotFollowNoPosition; + m_mode = location::PendingPosition; + m_desiredInitMode = location::Follow; } - m_pendingStarted = (m_mode == PendingPosition); - - if (m_modeChangeCallback) + if (m_modeChangeCallback != nullptr) m_modeChangeCallback(m_mode, m_isInRouting); } @@ -446,22 +437,15 @@ void MyPositionController::OnLocationUpdate(location::GpsInfo const & info, bool if (!m_isPositionAssigned) { - // If the position was never assigned, the new mode will be the desired one except next cases: + // If the position was never assigned, the new mode will be desired one except the case when + // we touch the map during the pending of position. In this case the current mode must be + // NotFollowNoPosition, new mode will be NotFollow to prevent spontaneous map snapping. location::EMyPositionMode newMode = m_desiredInitMode; if (m_mode == location::NotFollowNoPosition) { - // We touch the map during the PendingPosition mode and current mode was converted into NotFollowNoPosition. - // New mode will be NotFollow to prevent spontaneous map snapping. ResetRoutingNotFollowTimer(); newMode = location::NotFollow; } - else if (m_mode == location::PendingPosition && newMode < location::Follow) - { - // This is the first user location request (button touch) after controller's initialization - // with some previous not Follow state. New mode will be Follow to move on current position. - newMode = location::Follow; - } - ChangeMode(newMode); if (!m_hints.m_isFirstLaunch || !AnimationSystem::Instance().AnimationExists(Animation::Object::MapPlane)) @@ -661,7 +645,7 @@ void MyPositionController::ChangeMode(location::EMyPositionMode newMode) } m_mode = newMode; - if (m_modeChangeCallback) + if (m_modeChangeCallback != nullptr) m_modeChangeCallback(m_mode, m_isInRouting); } @@ -669,7 +653,7 @@ bool MyPositionController::IsWaitingForLocation() const { if (m_mode == location::NotFollowNoPosition) return false; - + if (!m_isPositionAssigned) return true; @@ -758,7 +742,7 @@ void MyPositionController::UpdateViewport(int zoomLevel) { if (IsWaitingForLocation()) return; - + if (m_mode == location::Follow) { ChangeModelView(m_position, zoomLevel); @@ -775,7 +759,7 @@ m2::PointD MyPositionController::GetRotationPixelCenter() const { if (m_mode == location::Follow) return m_visiblePixelRect.Center(); - + if (m_mode == location::FollowAndRotate) return m_isInRouting ? GetRoutingRotationPixelCenter() : m_visiblePixelRect.Center(); diff --git a/drape_frontend/my_position_controller.hpp b/drape_frontend/my_position_controller.hpp index 81b62255a5..d30c288592 100644 --- a/drape_frontend/my_position_controller.hpp +++ b/drape_frontend/my_position_controller.hpp @@ -114,7 +114,6 @@ public: void StopLocationFollow(); void NextMode(ScreenBase const & screen); void LoseLocation(); - location::EMyPositionMode GetCurrentMode() const { return m_mode; } void OnEnterForeground(double backgroundTime); void OnEnterBackground(); @@ -192,7 +191,7 @@ private: base::Timer m_lastGPSBearingTimer; base::Timer m_pendingTimer; - bool m_pendingStarted; + bool m_pendingStarted = true; base::Timer m_routingNotFollowTimer; bool m_blockRoutingNotFollowTimer = false; base::Timer m_blockAutoZoomTimer; diff --git a/generator/generator_tests/osm_type_test.cpp b/generator/generator_tests/osm_type_test.cpp index 202e516aa0..bdb8b6a775 100644 --- a/generator/generator_tests/osm_type_test.cpp +++ b/generator/generator_tests/osm_type_test.cpp @@ -555,8 +555,16 @@ UNIT_CLASS_TEST(TestWithClassificator, OsmType_Surface) TestSurfaceTypes("asphalt", "bad", "", "paved_bad"); TestSurfaceTypes("asphalt", "", "0", "paved_bad"); + TestSurfaceTypes("fine_gravel", "", "", "paved_bad"); TestSurfaceTypes("fine_gravel", "intermediate", "", "paved_bad"); + + // We definitely should store more than 4 surface options. + // Gravel (widely used tag) always goes to unpaved_bad which is strange sometimes. + // At the same time, we can't definitely say that it's unpaved_good :) + TestSurfaceTypes("gravel", "excellent", "", "unpaved_good"); + TestSurfaceTypes("gravel", "", "", "unpaved_bad"); TestSurfaceTypes("gravel", "intermediate", "", "unpaved_bad"); + TestSurfaceTypes("paved", "intermediate", "", "paved_good"); TestSurfaceTypes("", "intermediate", "", "unpaved_good"); diff --git a/iphone/Maps/Core/Location/MWMLocationHelpers.h b/iphone/Maps/Core/Location/MWMLocationHelpers.h index abb31343bf..ddeed6557e 100644 --- a/iphone/Maps/Core/Location/MWMLocationHelpers.h +++ b/iphone/Maps/Core/Location/MWMLocationHelpers.h @@ -3,6 +3,7 @@ #include "platform/localization.hpp" #include "platform/location.hpp" #include "platform/measurement_utils.hpp" +#include "platform/settings.hpp" #include "geometry/mercator.hpp" @@ -17,6 +18,15 @@ static inline NSString * formattedDistance(double const & meters) { return @(measurement_utils::FormatDistanceWithLocalization(meters, localizedUnits.m_high, localizedUnits.m_low).c_str()); } +static inline BOOL isMyPositionPendingOrNoPosition() +{ + location::EMyPositionMode mode; + if (!settings::Get(settings::kLocationStateMode, mode)) + return true; + return mode == location::EMyPositionMode::PendingPosition || + mode == location::EMyPositionMode::NotFollowNoPosition; +} + static inline ms::LatLon ToLatLon(m2::PointD const & p) { return mercator::ToLatLon(p); } static inline m2::PointD ToMercator(CLLocationCoordinate2D const & l) @@ -36,4 +46,4 @@ static inline MWMMyPositionMode mwmMyPositionMode(location::EMyPositionMode mode case location::EMyPositionMode::FollowAndRotate: return MWMMyPositionModeFollowAndRotate; } } -} // namespace location_helpers +} // namespace MWMLocationHelpers diff --git a/iphone/Maps/Core/Routing/MWMRouter.mm b/iphone/Maps/Core/Routing/MWMRouter.mm index a3e9e4f694..87a5476db7 100644 --- a/iphone/Maps/Core/Routing/MWMRouter.mm +++ b/iphone/Maps/Core/Routing/MWMRouter.mm @@ -279,23 +279,20 @@ char const *kRenderAltitudeImagesQueueLabel = "mapsme.mwmrouter.renderAltitudeIm auto const doStart = ^{ auto &rm = GetFramework().GetRoutingManager(); auto const routePoints = rm.GetRoutePoints(); - if (routePoints.size() >= 2) - { + if (routePoints.size() >= 2) { auto p1 = [[MWMRoutePoint alloc] initWithRouteMarkData:routePoints.front()]; auto p2 = [[MWMRoutePoint alloc] initWithRouteMarkData:routePoints.back()]; - CLLocation *lastLocation = [MWMLocationManager lastLocation]; - if (p1.isMyPosition && lastLocation) - { + if (p1.isMyPosition && [MWMLocationManager lastLocation]) { rm.FollowRoute(); [[MWMMapViewControlsManager manager] onRouteStart]; [MWMThemeManager setAutoUpdates:YES]; - } - else - { - BOOL const needToRebuild = lastLocation && [MWMLocationManager isStarted] && !p2.isMyPosition; - - [[MWMAlertViewController activeAlertController] + } else { + MWMAlertViewController *alertController = [MWMAlertViewController activeAlertController]; + CLLocation *lastLocation = [MWMLocationManager lastLocation]; + BOOL const needToRebuild = + lastLocation && !location_helpers::isMyPositionPendingOrNoPosition() && !p2.isMyPosition; + [alertController presentPoint2PointAlertWithOkBlock:^{ [self buildFromPoint:[[MWMRoutePoint alloc] initWithLastLocationAndType:MWMRoutePointTypeStart intermediateIndex:0] diff --git a/map/framework.cpp b/map/framework.cpp index ac7411fcf1..12b2e434be 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -104,6 +104,7 @@ using namespace location; using namespace routing; using namespace storage; +using namespace std::chrono; using namespace std::placeholders; using namespace std; @@ -236,11 +237,6 @@ void Framework::SetMyPositionPendingTimeoutListener(df::DrapeEngine::UserPositio m_myPositionPendingTimeoutListener = move(fn); } -EMyPositionMode Framework::GetMyPositionMode() const -{ - return m_drapeEngine ? m_drapeEngine->GetMyPositionMode() : PendingPosition; -} - TrafficManager & Framework::GetTrafficManager() { return m_trafficManager; @@ -1145,7 +1141,10 @@ void Framework::MemoryWarning() void Framework::EnterBackground() { - if (m_drapeEngine) + m_startBackgroundTime = base::Timer::LocalTime(); + settings::Set("LastEnterBackground", m_startBackgroundTime); + + if (m_drapeEngine != nullptr) m_drapeEngine->OnEnterBackground(); SaveViewport(); @@ -1162,8 +1161,12 @@ void Framework::EnterBackground() void Framework::EnterForeground() { - if (m_drapeEngine) - m_drapeEngine->OnEnterForeground(); + m_startForegroundTime = base::Timer::LocalTime(); + if (m_drapeEngine != nullptr && m_startBackgroundTime != 0.0) + { + auto const secondsInBackground = m_startForegroundTime - m_startBackgroundTime; + m_drapeEngine->OnEnterForeground(secondsInBackground); + } m_trafficManager.OnEnterForeground(); } @@ -1504,6 +1507,7 @@ void Framework::CreateDrapeEngine(ref_ptr contextFac df::MapDataProvider(move(idReadFn), move(featureReadFn), move(isCountryLoadedByNameFn), move(updateCurrentCountryFn)), params.m_hints, params.m_visualScale, fontsScaleFactor, move(params.m_widgetsInitInfo), + make_pair(params.m_initialMyPositionState, params.m_hasMyPositionState), move(myPositionModeChangedFn), allow3dBuildings, trafficEnabled, isolinesEnabled, params.m_isChoosePositionMode, params.m_isChoosePositionMode, GetSelectedFeatureTriangles(), @@ -1518,22 +1522,19 @@ void Framework::CreateDrapeEngine(ref_ptr contextFac }); m_drapeEngine->SetTapEventInfoListener([this](df::TapInfo const & tapInfo) { - GetPlatform().RunTask(Platform::Thread::Gui, [this, tapInfo]() - { + GetPlatform().RunTask(Platform::Thread::Gui, [this, tapInfo]() { OnTapEvent(place_page::BuildInfo(tapInfo)); }); }); m_drapeEngine->SetUserPositionListener([this](m2::PointD const & position, bool hasPosition) { - GetPlatform().RunTask(Platform::Thread::Gui, [this, position, hasPosition]() - { + GetPlatform().RunTask(Platform::Thread::Gui, [this, position, hasPosition](){ OnUserPositionChanged(position, hasPosition); }); }); m_drapeEngine->SetUserPositionPendingTimeoutListener([this]() { - GetPlatform().RunTask(Platform::Thread::Gui, [this]() - { + GetPlatform().RunTask(Platform::Thread::Gui, [this](){ if (m_myPositionPendingTimeoutListener) m_myPositionPendingTimeoutListener(); }); @@ -3236,6 +3237,11 @@ bool Framework::HaveTransit(m2::PointD const & pt) const return handle.GetValue()->m_cont.IsExist(TRANSIT_FILE_TAG); } +double Framework::GetLastBackgroundTime() const +{ + return m_startBackgroundTime; +} + void Framework::OnPowerFacilityChanged(power_management::Facility const facility, bool enabled) { if (facility == power_management::Facility::PerspectiveView || diff --git a/map/framework.hpp b/map/framework.hpp index bad029ccc7..9ea0ca7b0c 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -177,6 +177,10 @@ protected: drape_ptr m_drapeEngine; + // Time in seconds. + double m_startForegroundTime = 0.0; + double m_startBackgroundTime = 0.0; + StorageDownloadingPolicy m_storageDownloadingPolicy; storage::Storage m_storage; bool m_enabledDiffs; @@ -396,8 +400,6 @@ public: void SetMyPositionModeListener(location::TMyPositionModeChanged && fn); void SetMyPositionPendingTimeoutListener(df::DrapeEngine::UserPositionPendingTimeoutHandler && fn); - location::EMyPositionMode GetMyPositionMode() const; - private: void OnUserPositionChanged(m2::PointD const & position, bool hasPosition); @@ -410,6 +412,9 @@ public: int m_surfaceHeight = 0; gui::TWidgetsInitInfo m_widgetsInitInfo; + bool m_hasMyPositionState = false; + location::EMyPositionMode m_initialMyPositionState = location::PendingPosition; + bool m_isChoosePositionMode = false; df::Hints m_hints; }; @@ -736,6 +741,7 @@ private: public: // TipsApi::Delegate override. bool HaveTransit(m2::PointD const & pt) const; + double GetLastBackgroundTime() const; power_management::PowerManager & GetPowerManager() { return m_powerManager; } diff --git a/platform/platform.hpp b/platform/platform.hpp index 21796d0dca..8afb79efa4 100644 --- a/platform/platform.hpp +++ b/platform/platform.hpp @@ -243,6 +243,7 @@ public: NOT_ENOUGH_SPACE }; TStorageStatus GetWritableStorageStatus(uint64_t neededSize) const; + uint64_t GetWritableStorageSpace() const; // Please note, that number of active cores can vary at runtime. // DO NOT assume for the same return value between calls. diff --git a/platform/platform_unix_impl.cpp b/platform/platform_unix_impl.cpp index a5bf894b67..af77d44b55 100644 --- a/platform/platform_unix_impl.cpp +++ b/platform/platform_unix_impl.cpp @@ -124,14 +124,28 @@ Platform::TStorageStatus Platform::GetWritableStorageStatus(uint64_t neededSize) return STORAGE_DISCONNECTED; } - auto const availableBytes = st.f_bsize * st.f_bavail; - LOG(LDEBUG, ("Free space check: requested =", neededSize, "; available =", availableBytes)); - if (availableBytes < neededSize) + /// @todo May be add additional storage space. + if (st.f_bsize * st.f_bavail < neededSize) return NOT_ENOUGH_SPACE; return STORAGE_OK; } +uint64_t Platform::GetWritableStorageSpace() const +{ + struct statfs st; + int const ret = statfs(m_writableDir.c_str(), &st); + + LOG(LDEBUG, ("statfs return =", ret, + "; block size =", st.f_bsize, + "; blocks available =", st.f_bavail)); + + if (ret != 0) + LOG(LERROR, ("Path:", m_writableDir, "statfs error:", ErrnoToError())); + + return (ret != 0) ? 0 : st.f_bsize * st.f_bavail; +} + namespace pl { diff --git a/platform/settings.cpp b/platform/settings.cpp index 2e09afc2f4..aa8f91d187 100644 --- a/platform/settings.cpp +++ b/platform/settings.cpp @@ -16,10 +16,11 @@ #include #include -namespace settings -{ using namespace std; +namespace settings +{ +char const * kLocationStateMode = "LastLocationStateMode"; char const * kMeasurementUnits = "Units"; StringStorage::StringStorage() : StringStorageBase(GetPlatform().SettingsPathForFile(SETTINGS_FILE_NAME)) {} @@ -366,7 +367,6 @@ bool IsFirstLaunchForDate(int date) } } // namespace settings -/* namespace marketing { Settings::Settings() : platform::StringStorageBase(GetPlatform().SettingsPathForFile(MARKETING_SETTINGS_FILE_NAME)) {} @@ -378,4 +378,3 @@ Settings & Settings::Instance() return instance; } } // namespace marketing -*/ diff --git a/platform/settings.hpp b/platform/settings.hpp index 4cfb6ae5c5..2ff9a6c10a 100644 --- a/platform/settings.hpp +++ b/platform/settings.hpp @@ -8,6 +8,8 @@ namespace settings { +/// Current location state mode. @See location::EMyPositionMode. +extern char const * kLocationStateMode; /// Metric or Feet. extern char const * kMeasurementUnits; @@ -55,9 +57,8 @@ inline void Clear() { StringStorage::Instance().Clear(); } /// Use this function for running some stuff once according to date. /// @param[in] date Current date in format yymmdd. bool IsFirstLaunchForDate(int date); -} // namespace settings +} -/* namespace marketing { class Settings : public platform::StringStorageBase @@ -81,4 +82,3 @@ private: Settings(); }; } // namespace marketing -*/ diff --git a/routing/routing_integration_tests/route_test.cpp b/routing/routing_integration_tests/route_test.cpp index 1041399b49..568714de49 100644 --- a/routing/routing_integration_tests/route_test.cpp +++ b/routing/routing_integration_tests/route_test.cpp @@ -662,4 +662,17 @@ using namespace std; mercator::FromLatLon(34.7068976, 33.1199084), {0., 0.}, mercator::FromLatLon(34.7070505, 33.1198391), 670.077); } + + UNIT_TEST(Crimea_UseGravelTertiary) + { + integration::CalculateRouteAndTestRouteLength( + integration::GetVehicleComponents(VehicleType::Car), + mercator::FromLatLon(45.362591, 36.471533), {0., 0.}, + mercator::FromLatLon(45.475055, 36.341766), 18815.6); + + integration::CalculateRouteAndTestRouteLength( + integration::GetVehicleComponents(VehicleType::Car), + mercator::FromLatLon(45.470764, 36.331289), {0., 0.}, + mercator::FromLatLon(45.424964, 36.080336), 55220.2); + } } // namespace route_test diff --git a/routing/routing_tests/opening_hours_serdes_tests.cpp b/routing/routing_tests/opening_hours_serdes_tests.cpp index 61e7fbc2fd..59f9a6eee1 100644 --- a/routing/routing_tests/opening_hours_serdes_tests.cpp +++ b/routing/routing_tests/opening_hours_serdes_tests.cpp @@ -1,7 +1,8 @@ +#include "routing/routing_tests/index_graph_tools.hpp" + #include "testing/testing.hpp" #include "routing/opening_hours_serdes.hpp" -#include "routing/routing_tests/index_graph_tools.hpp" #include "coding/bit_streams.hpp" #include "coding/reader.hpp" @@ -14,16 +15,16 @@ #include #include -namespace opening_hours_serdes_tests -{ using namespace routing; using namespace routing_test; using Buffer = std::vector; -struct OHSerDesTestFixture +namespace { - OHSerDesTestFixture() +struct BitReaderWriter +{ + BitReaderWriter() : m_memWriter(m_buffer) , m_bitWriter(std::make_unique>>(m_memWriter)) { @@ -151,7 +152,7 @@ void TestMonth(osmoh::RuleSequence const & rule, Month startMonth, Month endMont 0 /* endDay */); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_EnableTests_1) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_EnableTests_1) { TEST(!IsEnabled(OpeningHoursSerDes::Header::Bits::Year), ()); Enable(OpeningHoursSerDes::Header::Bits::Year); @@ -166,7 +167,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_EnableTests_1) TEST(IsEnabled(OpeningHoursSerDes::Header::Bits::Minutes), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_EnableTests_2) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_EnableTests_2) { Enable(OpeningHoursSerDes::Header::Bits::Year); Enable(OpeningHoursSerDes::Header::Bits::WeekDay); @@ -184,7 +185,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_EnableTests_2) // Test on serialization ranges where start is later than end. // It is wrong but still possible data. -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_CannotSerialize) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_CannotSerialize) { Enable(OpeningHoursSerDes::Header::Bits::Year); Enable(OpeningHoursSerDes::Header::Bits::Month); @@ -192,7 +193,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_CannotSerialize) TEST(!Serialize("2020 May 20 - 2018 Nov 30"), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_YearOnly) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_YearOnly) { Enable(OpeningHoursSerDes::Header::Bits::Year); @@ -207,7 +208,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_YearOnly) TestYear(rule, 2019, 2090); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_YearAndMonthOnly) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_YearAndMonthOnly) { Enable(OpeningHoursSerDes::Header::Bits::Year); Enable(OpeningHoursSerDes::Header::Bits::Month); @@ -224,7 +225,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_YearAndMonthOnly) TestMonth(rule, 2019, Month::Apr, 10, 2051, Month::May, 19); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Weekday_1) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_Weekday_1) { Enable(OpeningHoursSerDes::Header::Bits::WeekDay); @@ -239,7 +240,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Weekday_1) TestWeekday(rule, Weekday::Monday, Weekday::Friday); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Weekday_2) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_Weekday_2) { Enable(OpeningHoursSerDes::Header::Bits::WeekDay); @@ -257,7 +258,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Weekday_2) TestWeekday(rule, Weekday::Friday, Weekday::Sunday); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Weekday_Hours_1) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_Weekday_Hours_1) { Enable(OpeningHoursSerDes::Header::Bits::WeekDay); Enable(OpeningHoursSerDes::Header::Bits::Hours); @@ -300,7 +301,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Weekday_Hours_1) } } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Off_SerDes_1_AndUsage) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_Off_SerDes_1_AndUsage) { SerializeEverything(); EnableAll(); @@ -323,7 +324,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Off_SerDes_1_AndUsage) TEST(oh.IsClosed(GetUnixtimeByDate(2020, Month::Feb, Weekday::Monday, 17 /* hh */, 30 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Off_SerDes_2) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_Off_SerDes_2) { SerializeEverything(); EnableAll(); @@ -359,7 +360,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Off_SerDes_2) TestModifier(rule, osmoh::RuleSequence::Modifier::Closed); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_OffJustOff) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_OffJustOff) { SerializeEverything(); EnableAll(); @@ -375,7 +376,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_OffJustOff) TestModifier(rule, osmoh::RuleSequence::Modifier::Closed); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_OffJustClosed) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_OffJustClosed) { SerializeEverything(); EnableAll(); @@ -391,7 +392,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_OffJustClosed) TestModifier(rule, osmoh::RuleSequence::Modifier::Closed); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Open) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_Open) { SerializeEverything(); EnableAll(); @@ -409,7 +410,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Open) TEST(oh.IsOpen(someMoment), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_TimeIsOver00) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_TimeIsOver00) { SerializeEverything(); EnableAll(); @@ -427,7 +428,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_TimeIsOver00) TEST(!oh.IsOpen(GetUnixtimeByDate(2020, Month::Feb, Weekday::Tuesday, 06 /* hh */, 30 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_DefaultOpen) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_DefaultOpen) { SerializeEverything(); EnableAll(); @@ -445,7 +446,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_DefaultOpen) TEST(!oh.IsOpen(GetUnixtimeByDate(2020, Month::Feb, Weekday::Sunday, 13 /* hh */, 30 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_SkipRuleOldYear) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_SkipRuleOldYear) { SerializeEverything(); EnableAll(); @@ -462,7 +463,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_SkipRuleOldYear) TestTime(rule, 7, 0, 17, 0); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_10_plus) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_10_plus) { SerializeEverything(); EnableAll(); @@ -486,7 +487,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_10_plus) TEST(!oh.IsOpen(GetUnixtimeByDate(2020, Month::Mar, Weekday::Saturday, 00 /* hh */, 30 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_24_7) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_24_7) { EnableAll(); @@ -497,7 +498,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_24_7) TEST(oh.IsTwentyFourHours(), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_ExamplesFromOsmAccessConditional_1) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_ExamplesFromOsmAccessConditional_1) { EnableAll(); @@ -512,7 +513,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_ExamplesFromOsmAccessCon TestMonth(rule, Month::Apr, 1, Month::Jun, 30); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_ExamplesFromOsmAccessConditional_2) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_ExamplesFromOsmAccessConditional_2) { EnableAll(); @@ -527,7 +528,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_ExamplesFromOsmAccessCon TestTime(rule, 22, 0, 6, 0); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_ExamplesFromOsmAccessConditional_3) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_ExamplesFromOsmAccessConditional_3) { EnableAll(); @@ -546,7 +547,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_ExamplesFromOsmAccessCon TestTime(rule, 21, 30, 7, 0); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_ExamplesFromOsmAccessConditional_4) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_ExamplesFromOsmAccessConditional_4) { EnableAll(); @@ -561,7 +562,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_ExamplesFromOsmAccessCon TestMonth(rule, Month::Apr, Month::Oct); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_ExamplesFromOsmAccessConditional_5) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_ExamplesFromOsmAccessConditional_5) { EnableAll(); @@ -581,7 +582,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_ExamplesFromOsmAccessCon TestTime(rule, 15, 0, 16, 0); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_ExamplesFromOsmAccessConditional_6) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_ExamplesFromOsmAccessConditional_6) { SerializeEverything(); EnableAll(); @@ -601,7 +602,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_ExamplesFromOsmAccessCon TestTime(rule, 11, 0, 18, 0); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_EnableForRouting_1) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_EnableForRouting_1) { EnableForRouting(); @@ -616,7 +617,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_EnableForRouting_1) TestWeekday(rule, Weekday::Sunday, Weekday::None); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_EnableForRouting_2) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_EnableForRouting_2) { EnableForRouting(); @@ -668,7 +669,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_EnableForRouting_2) TestTime(rule, 12, 0, 12, 30); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_WeekdayAndHolidayOff) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_WeekdayAndHolidayOff) { SerializeEverything(); EnableAll(); @@ -693,7 +694,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_WeekdayAndHolidayOff) TestModifier(rule, osmoh::RuleSequence::Modifier::Closed); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_WeekDay_OneDay) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_WeekDay_OneDay) { EnableAll(); @@ -709,7 +710,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_WeekDay_OneDay) TestTime(rule, 16, 0, 20, 0); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Hours_Usage_1) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_Hours_Usage_1) { EnableAll(); @@ -722,7 +723,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Hours_Usage_1) TEST(oh.IsClosed(GetUnixtimeByDate(2020, Month::Feb, Weekday::Monday, 07 /* hh */, 30 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Weekday_Usage_1) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_Weekday_Usage_1) { EnableAll(); @@ -735,7 +736,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Weekday_Usage_1) TEST(oh.IsOpen(GetUnixtimeByDate(2020, Month::Feb, Weekday::Wednesday, 17 /* hh */, 30 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Weekday_Usage_2) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_Weekday_Usage_2) { EnableAll(); @@ -750,7 +751,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Weekday_Usage_2) TEST(oh.IsClosed(GetUnixtimeByDate(2023, Month::Apr, Weekday::Saturday, 8 /* hh */, 30 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Weekday_Usage_3) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_Weekday_Usage_3) { EnableAll(); @@ -763,7 +764,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Weekday_Usage_3) TEST(oh.IsOpen(GetUnixtimeByDate(2020, Month::Feb, 7, 03 /* hh */, 30 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Month_Usage) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_Month_Usage) { EnableAll(); @@ -776,7 +777,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_Month_Usage) TEST(oh.IsClosed(GetUnixtimeByDate(2020, Month::Feb, 5, 17 /* hh */, 30 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_MonthDay_Usage) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_MonthDay_Usage) { EnableAll(); @@ -792,7 +793,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_MonthDay_Usage) TEST(oh.IsOpen(GetUnixtimeByDate(2021, Month::Mar, 26, 19 /* hh */, 00 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_MonthDayYear_Usage) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_MonthDayYear_Usage) { EnableAll(); @@ -808,7 +809,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_MonthDayYear_Usage) TEST(oh.IsClosed(GetUnixtimeByDate(2052, Month::Mar, 26, 19 /* hh */, 00 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_MonthHours_Usage) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_MonthHours_Usage) { EnableAll(); @@ -823,7 +824,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_MonthHours_Usage) TEST(oh.IsOpen(GetUnixtimeByDate(2020, Month::May, 6, 01 /* hh */, 32 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_InverseMonths_Usage_1) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_InverseMonths_Usage_1) { EnableAll(); @@ -840,7 +841,7 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_InverseMonths_Usage_1) TEST(!oh.IsOpen(GetUnixtimeByDate(2020, Month::Feb, 20, 20 /* hh */, 00 /* mm */)), ()); } -UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_InverseMonths_Usage_2) +UNIT_CLASS_TEST(BitReaderWriter, OpeningHoursSerDes_InverseMonths_Usage_2) { EnableAll(); @@ -859,4 +860,4 @@ UNIT_CLASS_TEST(OHSerDesTestFixture, OpeningHoursSerDes_InverseMonths_Usage_2) TEST(!oh.IsOpen(GetUnixtimeByDate(2020, Month::Apr, 20, 20 /* hh */, 00 /* mm */)), ()); TEST(!oh.IsOpen(GetUnixtimeByDate(2020, Month::May, 20, 20 /* hh */, 00 /* mm */)), ()); } -} // namespace opening_hours_serdes_tests +} // namespace diff --git a/routing_common/car_model.cpp b/routing_common/car_model.cpp index 049450c194..c8d95a2fd3 100644 --- a/routing_common/car_model.cpp +++ b/routing_common/car_model.cpp @@ -46,12 +46,6 @@ VehicleModel::LimitsInitList const kCarOptionsDefault = { {{"highway", "living_street"}, true}, {{"highway", "road"}, true}, {{"highway", "track"}, true} - /// @todo: Add to classificator - //{ {"highway", "shuttle_train"}, 10 }, - //{ {"highway", "ferry"}, 5 }, - //{ {"highway", "default"}, 10 }, - /// @todo: Check type - //{ {"highway", "construction"}, 40 }, }; VehicleModel::LimitsInitList const kCarOptionsNoPassThroughLivingStreet = { @@ -134,12 +128,14 @@ VehicleModel::AdditionalRoadsList const kAdditionalRoads = { {{"route", "ferry"}, kHighwayBasedSpeeds.Get(HighwayType::RouteFerry)}, {{"man_made", "pier"}, kHighwayBasedSpeeds.Get(HighwayType::ManMadePier)}}; +/// @todo Should make some compare constrains (like in CarModel_TrackVsGravelTertiary test) +/// to better fit these factors with reality. I have no idea, how they were set. VehicleModel::SurfaceInitList const kCarSurface = { // {{surfaceType, surfaceType}, {weightFactor, etaFactor}} {{"psurface", "paved_good"}, {1.0, 1.0}}, {{"psurface", "paved_bad"}, {0.5, 0.5}}, {{"psurface", "unpaved_good"}, {0.4, 0.8}}, - {{"psurface", "unpaved_bad"}, {0.1, 0.3}} + {{"psurface", "unpaved_bad"}, {0.2, 0.3}} }; // Names must be the same with country names from countries.txt diff --git a/routing_common/car_model_coefs.hpp b/routing_common/car_model_coefs.hpp index 8562232246..bb6468d18f 100644 --- a/routing_common/car_model_coefs.hpp +++ b/routing_common/car_model_coefs.hpp @@ -48,6 +48,7 @@ HighwayBasedSpeeds const kHighwayBasedSpeeds = { {HighwayType::HighwaySecondary, InOutCitySpeedKMpH(60.00 /* in city */, 70.00 /* out city */)}, {HighwayType::HighwaySecondaryLink, InOutCitySpeedKMpH(48.00 /* in city */, 56.00 /* out city */)}, {HighwayType::HighwayService, InOutCitySpeedKMpH({15.00, 15.00} /* in city */, {15.00, 15.00} /* out city */)}, + /// @todo Why tertiary is the only road with inCity speed _bigger_ than outCity? {HighwayType::HighwayTertiary, InOutCitySpeedKMpH(60.00 /* in city */, 50.00 /* out city */)}, {HighwayType::HighwayTertiaryLink, InOutCitySpeedKMpH({40.95, 34.97} /* in city */, {45.45, 39.73} /* out city */)}, {HighwayType::HighwayTrack, InOutCitySpeedKMpH({5.00, 5.00} /* in city */, {5.00, 5.00} /* out city */)}, diff --git a/routing_common/routing_common_tests/vehicle_model_test.cpp b/routing_common/routing_common_tests/vehicle_model_test.cpp index b5f419dacd..f0feac3483 100644 --- a/routing_common/routing_common_tests/vehicle_model_test.cpp +++ b/routing_common/routing_common_tests/vehicle_model_test.cpp @@ -3,19 +3,16 @@ #include "routing_common/car_model_coefs.hpp" #include "routing_common/maxspeed_conversion.hpp" #include "routing_common/vehicle_model.hpp" +#include "routing_common/car_model.hpp" #include "indexer/classificator.hpp" #include "indexer/classificator_loader.hpp" -#include "indexer/feature.hpp" +#include "indexer/feature_data.hpp" #include "platform/measurement_utils.hpp" -#include "base/macros.hpp" #include "base/math.hpp" -#include -#include - namespace vehicle_model_test { using namespace routing; @@ -54,52 +51,53 @@ VehicleModel::SurfaceInitList const kCarSurface = { class VehicleModelTest { public: - VehicleModelTest() { classificator::Load(); } + VehicleModelTest() + { + classificator::Load(); + auto const & c = classif(); + + primary = c.GetTypeByPath({"highway", "primary"}); + secondary = c.GetTypeByPath({"highway", "secondary"}); + secondaryBridge = c.GetTypeByPath({"highway", "secondary", "bridge"}); + secondaryTunnel = c.GetTypeByPath({"highway", "secondary", "tunnel"}); + residential = c.GetTypeByPath({"highway", "residential"}); + + oneway = c.GetTypeByPath({"hwtag", "oneway"}); + pavedGood = c.GetTypeByPath({"psurface", "paved_good"}); + pavedBad = c.GetTypeByPath({"psurface", "paved_bad"}); + unpavedGood = c.GetTypeByPath({"psurface", "unpaved_good"}); + unpavedBad = c.GetTypeByPath({"psurface", "unpaved_bad"}); + } + + uint32_t primary, secondary, secondaryTunnel, secondaryBridge, residential; + uint32_t oneway, pavedGood, pavedBad, unpavedGood, unpavedBad; }; -class TestVehicleModel : public VehicleModel +class VehicleModelStub : public VehicleModel { - friend void CheckOneWay(initializer_list const & types, bool expectedValue); - friend void CheckPassThroughAllowed(initializer_list const & types, bool expectedValue); - friend void CheckSpeedWithParams(initializer_list const & types, - SpeedParams const & params, SpeedKMpH const & expectedSpeed); - public: - TestVehicleModel() + VehicleModelStub() : VehicleModel(classif(), kTestLimits, kCarSurface, {kDefaultSpeeds, kDefaultFactors}) { } // We are not going to use offroad routing in these tests. - SpeedKMpH const & GetOffroadSpeed() const override { return kDummy; } - -private: - static SpeedKMpH const kDummy; + SpeedKMpH const & GetOffroadSpeed() const override + { + static SpeedKMpH offroad{0.0 /* weight */, 0.0 /* eta */}; + return offroad; + } }; -SpeedKMpH const TestVehicleModel::kDummy = {0.0 /* weight */, 0.0 /* eta */}; - -uint32_t GetType(char const * s0, char const * s1 = 0, char const * s2 = 0) -{ - char const * const t[] = {s0, s1, s2}; - size_t const size = (s0 != 0) + size_t(s1 != 0) + size_t(s2 != 0); - return classif().GetTypeByPath(vector(t, t + size)); -} - -uint32_t GetOnewayType() -{ - return GetType("hwtag", "oneway"); -} - void CheckSpeedWithParams(initializer_list const & types, SpeedParams const & params, SpeedKMpH const & expectedSpeed) { - TestVehicleModel vehicleModel; + VehicleModelStub model; feature::TypesHolder h; for (uint32_t t : types) h.Add(t); - TEST_EQUAL(vehicleModel.GetTypeSpeed(h, params), expectedSpeed, ()); + TEST_EQUAL(model.GetTypeSpeed(h, params), expectedSpeed, ()); } void CheckSpeed(initializer_list const & types, InOutCitySpeedKMpH const & expectedSpeed) @@ -112,98 +110,81 @@ void CheckSpeed(initializer_list const & types, InOutCitySpeedKMpH con void CheckOneWay(initializer_list const & types, bool expectedValue) { - TestVehicleModel vehicleModel; + VehicleModelStub model; feature::TypesHolder h; for (uint32_t t : types) h.Add(t); - TEST_EQUAL(vehicleModel.HasOneWayType(h), expectedValue, ()); + TEST_EQUAL(model.HasOneWayType(h), expectedValue, ()); } void CheckPassThroughAllowed(initializer_list const & types, bool expectedValue) { - TestVehicleModel vehicleModel; + VehicleModelStub model; feature::TypesHolder h; for (uint32_t t : types) h.Add(t); - TEST_EQUAL(vehicleModel.HasPassThroughType(h), expectedValue, ()); + TEST_EQUAL(model.HasPassThroughType(h), expectedValue, ()); } -UNIT_CLASS_TEST(VehicleModelTest, VehicleModel_MaxSpeed) +UNIT_CLASS_TEST(VehicleModelStub, MaxSpeed) { - TestVehicleModel vehicleModel; - TEST_EQUAL(vehicleModel.GetMaxWeightSpeed(), 150.0, ()); + TEST_EQUAL(GetMaxWeightSpeed(), 150.0, ()); } -UNIT_CLASS_TEST(VehicleModelTest, VehicleModel_Speed) +UNIT_CLASS_TEST(VehicleModelTest, Speed) { { - CheckSpeed({GetType("highway", "secondary", "bridge")}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); - CheckSpeed({GetType("highway", "secondary", "tunnel")}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); - CheckSpeed({GetType("highway", "secondary")}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); + CheckSpeed({secondaryBridge}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); + CheckSpeed({secondaryTunnel}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); + CheckSpeed({secondary}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); } - CheckSpeed({GetType("highway", "trunk")}, + CheckSpeed({classif().GetTypeByPath({"highway", "trunk"})}, {SpeedKMpH(100.0 /* weight */, 100.0 /* eta */) /* in city */, SpeedKMpH(150.0 /* weight */, 150.0 /* eta */) /* out of city */}); - CheckSpeed({GetType("highway", "primary")}, {SpeedKMpH(90.0, 90.0), SpeedKMpH(120.0, 120.0)}); - CheckSpeed({GetType("highway", "residential")}, {SpeedKMpH(22.5, 27.5), SpeedKMpH(25.0, 30.0)}); + CheckSpeed({primary}, {SpeedKMpH(90.0, 90.0), SpeedKMpH(120.0, 120.0)}); + CheckSpeed({residential}, {SpeedKMpH(22.5, 27.5), SpeedKMpH(25.0, 30.0)}); } -UNIT_CLASS_TEST(VehicleModelTest, VehicleModel_Speed_MultiTypes) +UNIT_CLASS_TEST(VehicleModelTest, Speed_MultiTypes) { - uint32_t const typeTunnel = GetType("highway", "secondary", "tunnel"); - uint32_t const typeSecondary = GetType("highway", "secondary"); - uint32_t const typeHighway = GetType("highway"); + uint32_t const typeHighway = classif().GetTypeByPath({"highway"}); - CheckSpeed({typeTunnel, typeSecondary}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); - CheckSpeed({typeTunnel, typeHighway}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); - CheckSpeed({typeHighway, typeTunnel}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); + CheckSpeed({secondaryTunnel, secondary}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); + CheckSpeed({secondaryTunnel, typeHighway}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); + CheckSpeed({typeHighway, secondaryTunnel}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); } -UNIT_CLASS_TEST(VehicleModelTest, VehicleModel_OneWay) +UNIT_CLASS_TEST(VehicleModelTest, OneWay) { - uint32_t const typeBridge = GetType("highway", "secondary", "bridge"); - uint32_t const typeOneway = GetOnewayType(); + CheckSpeed({secondaryBridge, oneway}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); + CheckOneWay({secondaryBridge, oneway}, true); + CheckSpeed({oneway, secondaryBridge}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); + CheckOneWay({oneway, secondaryBridge}, true); - CheckSpeed({typeBridge, typeOneway}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); - CheckOneWay({typeBridge, typeOneway}, true); - CheckSpeed({typeOneway, typeBridge}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); - CheckOneWay({typeOneway, typeBridge}, true); - - CheckOneWay({typeOneway}, true); + CheckOneWay({oneway}, true); } -UNIT_CLASS_TEST(VehicleModelTest, VehicleModel_DifferentSpeeds) +UNIT_CLASS_TEST(VehicleModelTest, DifferentSpeeds) { - uint32_t const typeSecondary = GetType("highway", "secondary"); - uint32_t const typePrimary = GetType("highway", "primary"); - uint32_t const typeOneway = GetOnewayType(); - - CheckSpeed({typeSecondary, typePrimary}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); - - CheckSpeed({typeSecondary, typePrimary, typeOneway}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); - CheckOneWay({typePrimary, typeOneway, typeSecondary}, true); + // What is the purpose of this artificial test with several highway types? To show that order is important? + CheckSpeed({secondary, primary}, kDefaultSpeeds.Get(HighwayType::HighwaySecondary)); + CheckSpeed({oneway, primary, secondary}, kDefaultSpeeds.Get(HighwayType::HighwayPrimary)); + CheckOneWay({primary, oneway, secondary}, true); } -UNIT_CLASS_TEST(VehicleModelTest, VehicleModel_PassThroughAllowed) +UNIT_CLASS_TEST(VehicleModelTest, PassThroughAllowed) { - CheckPassThroughAllowed({GetType("highway", "secondary")}, true); - CheckPassThroughAllowed({GetType("highway", "primary")}, true); - CheckPassThroughAllowed({GetType("highway", "service")}, false); + CheckPassThroughAllowed({secondary}, true); + CheckPassThroughAllowed({primary}, true); + CheckPassThroughAllowed({classif().GetTypeByPath({"highway", "service"})}, false); } -UNIT_CLASS_TEST(VehicleModelTest, VehicleModel_SpeedFactor) +UNIT_CLASS_TEST(VehicleModelTest, SpeedFactor) { - uint32_t const secondary = GetType("highway", "secondary"); - uint32_t const residential = GetType("highway", "residential"); - uint32_t const pavedGood = GetType("psurface", "paved_good"); - uint32_t const pavedBad = GetType("psurface", "paved_bad"); - uint32_t const unpavedGood = GetType("psurface", "unpaved_good"); - uint32_t const unpavedBad = GetType("psurface", "unpaved_bad"); - CheckSpeed({secondary, pavedGood}, {SpeedKMpH(64.0 /* weight */, 63.0 /* eta */) /* in city */, SpeedKMpH(64.0 /* weight */, 63.0 /* eta */) /* out of city */}); @@ -217,14 +198,8 @@ UNIT_CLASS_TEST(VehicleModelTest, VehicleModel_SpeedFactor) CheckSpeed({residential, unpavedBad}, {SpeedKMpH(4.5, 5.5), SpeedKMpH(5.0, 6.0)}); } -UNIT_CLASS_TEST(VehicleModelTest, VehicleModel_MaxspeedFactor) +UNIT_CLASS_TEST(VehicleModelTest, MaxspeedFactor) { - uint32_t const secondary = GetType("highway", "secondary"); - uint32_t const residential = GetType("highway", "residential"); - uint32_t const primary = GetType("highway", "primary"); - uint32_t const pavedGood = GetType("psurface", "paved_good"); - uint32_t const unpavedBad = GetType("psurface", "unpaved_bad"); - Maxspeed const maxspeed90 = Maxspeed(measurement_utils::Units::Metric, 90 /* forward speed */, kInvalidSpeed); CheckSpeedWithParams({secondary, unpavedBad}, @@ -251,6 +226,46 @@ UNIT_CLASS_TEST(VehicleModelTest, VehicleModel_MaxspeedFactor) SpeedKMpH(24.0, 27.0)); } +namespace +{ +bool LessSpeed(SpeedKMpH const & l, SpeedKMpH const & r) +{ + TEST(l.IsValid() && r.IsValid(), (l, r)); + return l.m_weight < r.m_weight && l.m_eta < r.m_eta; +} + +#define TEST_LESS_SPEED(l, r) TEST(LessSpeed(l, r), (l, r)) +} // namespace + +UNIT_CLASS_TEST(VehicleModelTest, CarModel_TrackVsGravelTertiary) +{ + auto const & model = CarModel::AllLimitsInstance(); + + auto const & c = classif(); + feature::TypesHolder h1; + h1.Add(c.GetTypeByPath({"highway", "track"})); + + feature::TypesHolder h2; + h2.Add(c.GetTypeByPath({"highway", "tertiary"})); + h2.Add(unpavedBad); // from OSM surface=gravel + + // https://www.openstreetmap.org/#map=19/45.43640/36.39689 + // Obvious that gravel tertiary (moreover with maxspeed=60kmh) should be better than track. + + { + SpeedParams p1({}, kInvalidSpeed, false /* inCity */); + SpeedParams p2({measurement_utils::Units::Metric, 60, 60}, kInvalidSpeed, false /* inCity */); + TEST_LESS_SPEED(model.GetTypeSpeed(h1, p1), model.GetTypeSpeed(h2, p2)); + } + + { + SpeedParams p({}, kInvalidSpeed, false /* inCity */); + TEST_LESS_SPEED(model.GetTypeSpeed(h1, p), model.GetTypeSpeed(h2, p)); + } +} + +#undef TEST_LESS_SPEED + UNIT_TEST(VehicleModel_MultiplicationOperatorTest) { SpeedKMpH const speed(90 /* weight */, 100 /* eta */); @@ -258,13 +273,13 @@ UNIT_TEST(VehicleModel_MultiplicationOperatorTest) SpeedKMpH const lResult = speed * factor; SpeedKMpH const rResult = factor * speed; TEST_EQUAL(lResult, rResult, ()); - TEST(base::AlmostEqualAbs(lResult.m_weight, 90.0, 1e-7), ()); - TEST(base::AlmostEqualAbs(lResult.m_eta, 110.0, 1e-7), ()); + TEST(base::AlmostEqualULPs(lResult.m_weight, 90.0), ()); + TEST(base::AlmostEqualULPs(lResult.m_eta, 110.0), ()); } UNIT_TEST(VehicleModel_CarModelValidation) { - vector const carRoadTypes = { + HighwayType const carRoadTypes[] = { HighwayType::HighwayLivingStreet, HighwayType::HighwayMotorway, HighwayType::HighwayMotorwayLink, HighwayType::HighwayPrimary, HighwayType::HighwayPrimaryLink, HighwayType::HighwayResidential, diff --git a/routing_common/vehicle_model.hpp b/routing_common/vehicle_model.hpp index 3db98867a8..6c067d78ba 100644 --- a/routing_common/vehicle_model.hpp +++ b/routing_common/vehicle_model.hpp @@ -68,7 +68,7 @@ using HighwayBasedSpeeds = base::SmallMap; /// \brief Params for calculation of an approximate speed on a feature. struct SpeedParams { - /// For unit tests compatibility. + /// @deprecated For unit tests compatibility. SpeedParams(bool forward, bool inCity, Maxspeed const & maxspeed) : m_maxspeed(maxspeed), m_defSpeedKmPH(kInvalidSpeed), m_inCity(inCity), m_forward(forward) { @@ -291,6 +291,7 @@ public: bool IsPassThroughAllowed(FeatureType & f) const override; /// @} + // Made public to have simple access from unit tests. public: /// @returns true if |m_highwayTypes| or |m_addRoadTypes| contains |type| and false otherwise. bool IsRoadType(uint32_t type) const; @@ -314,14 +315,6 @@ public: (m_onewayType == rhs.m_onewayType); } -protected: - /// @returns a special restriction which is set to the feature. - virtual RoadAvailability GetRoadAvailability(feature::TypesHolder const & types) const; - - void AddAdditionalRoadTypes(Classificator const & classif, AdditionalRoadsList const & roads); - - uint32_t PrepareToMatchType(uint32_t type) const; - /// \returns true if |types| is a oneway feature. /// \note According to OSM, tag "oneway" could have value "-1". That means it's a oneway feature /// with reversed geometry. In that case at map generation the geometry of such features @@ -331,6 +324,14 @@ protected: bool HasPassThroughType(feature::TypesHolder const & types) const; +protected: + /// @returns a special restriction which is set to the feature. + virtual RoadAvailability GetRoadAvailability(feature::TypesHolder const & types) const; + + void AddAdditionalRoadTypes(Classificator const & classif, AdditionalRoadsList const & roads); + + uint32_t PrepareToMatchType(uint32_t type) const; + SpeedKMpH GetSpeedWihtoutMaxspeed(FeatureType & f, SpeedParams params) const; /// \brief Maximum within all the speed limits set in a model (car model, bicycle model and so on).