[ios] Red for over speed; Speed limit for Carplay #3047

Merged
AntonM030481 merged 2 commits from master into master 2022-08-09 06:53:47 +00:00
34 changed files with 403 additions and 316 deletions

View file

@ -1202,7 +1202,7 @@ Java_com_mapswithme_maps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jcl
}
auto const & rm = frm()->GetRoutingManager();
auto const isSpeedLimitExceeded = rm.IsRoutingActive() ? rm.IsSpeedLimitExceeded() : false;
auto const isSpeedCamLimitExceeded = rm.IsRoutingActive() ? rm.IsSpeedCamLimitExceeded() : false;
auto const shouldPlaySignal = frm()->GetRoutingManager().GetSpeedCamManager().ShouldPlayBeepSignal();
jobject const result = env->NewObject(
klass, ctorRouteInfoID, jni::ToJavaString(env, info.m_distToTarget),
@ -1210,7 +1210,7 @@ Java_com_mapswithme_maps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jcl
jni::ToJavaString(env, info.m_turnUnitsSuffix), jni::ToJavaString(env, info.m_sourceName),
jni::ToJavaString(env, info.m_displayedStreetName), info.m_completionPercent, info.m_turn,
info.m_nextTurn, info.m_pedestrianTurn, info.m_exitNum, info.m_time, jLanes,
static_cast<jboolean>(isSpeedLimitExceeded), static_cast<jboolean>(shouldPlaySignal));
static_cast<jboolean>(isSpeedCamLimitExceeded), static_cast<jboolean>(shouldPlaySignal));
ASSERT(result, (jni::DescribeException()));
return result;
}

View file

@ -3,6 +3,17 @@
NS_ASSUME_NONNULL_BEGIN
@interface Measure : NSObject
@property(nonatomic, readonly) double value;
@property(nonatomic, readonly) NSString* valueAsString;
@property(nonatomic, readonly) NSString* unit;
- (instancetype) initAsSpeed:(double) mps;
@end
NS_SWIFT_NAME(GeoUtil)
@interface MWMGeoUtil : NSObject

View file

@ -3,6 +3,54 @@
#include "geometry/mercator.hpp"
#include "geometry/angles.hpp"
#include "platform/localization.hpp"
#include "platform/settings.hpp"
#include "platform/measurement_utils.hpp"
@implementation Measure
// Alternative native implementation.
// It has the issue: some localized unit are too long even in .short style. E.g. speed for RU.
/*
let imperial = Settings.measurementUnits() == .imperial
var speedMeasurement = Measurement(value: speed, unit: UnitSpeed.metersPerSecond)
speedMeasurement.convert(to: imperial ? UnitSpeed.milesPerHour : UnitSpeed.kilometersPerHour)
let formatter = MeasurementFormatter()
formatter.unitOptions = .providedUnit
formatter.numberFormatter.maximumFractionDigits = 0
formatter.unitStyle = .short
if speedMeasurement.value < 10
{
formatter.numberFormatter.minimumFractionDigits = 1
formatter.numberFormatter.maximumFractionDigits = 1
}
let speedString = formatter.string(from: speedMeasurement)
*/
- (NSString*) valueAsString {
if (self.value > 9.999)
return [NSString stringWithFormat:@"%.0f", self.value];
else
return [NSString stringWithFormat:@"%.1f", self.value];
}
- (instancetype)initAsSpeed:(double) mps {
self = [super init];
if (self) {
auto units = measurement_utils::Units::Metric;
settings::TryGet(settings::kMeasurementUnits, units);
_value = measurement_utils::MpsToUnits(mps, units);
_unit = @(platform::GetLocalizedSpeedUnits(units).c_str());
}
return self;
}
@end
@implementation MWMGeoUtil
+ (float)angleAtPoint:(CLLocationCoordinate2D)p1 toPoint:(CLLocationCoordinate2D)p2 {

View file

@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (UIColor *)opentableBackground;
+ (UIColor *)transparentGreen;
+ (UIColor *)speedLimitRed;
+ (UIColor *)speedLimitGeen;
+ (UIColor *)speedLimitGreen;
+ (UIColor *)speedLimitWhite;
+ (UIColor *)speedLimitLightGray;
+ (UIColor *)speedLimitDarkGray;

View file

@ -259,7 +259,7 @@ static NSDictionary<NSString *, UIColor *> *night;
return [UIColor colorWithRed:scaled(224) green:scaled(31) blue:scaled(31) alpha:alpha100];
}
+ (UIColor *)speedLimitGeen {
+ (UIColor *)speedLimitGreen {
return [UIColor colorWithRed:scaled(1) green:scaled(104) blue:scaled(44) alpha:alpha100];
}

View file

@ -22,29 +22,29 @@ final class CarPlayRouter: NSObject {
var speedCameraMode: SpeedCameraManagerMode {
return RoutingManager.routingManager.speedCameraMode
}
override init() {
listenerContainer = ListenerContainer<CarPlayRouterListener>()
initialSpeedCamSettings = RoutingManager.routingManager.speedCameraMode
super.init()
}
func addListener(_ listener: CarPlayRouterListener) {
listenerContainer.addListener(listener)
}
func removeListener(_ listener: CarPlayRouterListener) {
listenerContainer.removeListener(listener)
}
func subscribeToEvents() {
RoutingManager.routingManager.add(self)
}
func unsubscribeFromEvents() {
RoutingManager.routingManager.remove(self)
}
func completeRouteAndRemovePoints() {
let manager = RoutingManager.routingManager
manager.stopRoutingAndRemoveRoutePoints(true)
@ -52,7 +52,7 @@ final class CarPlayRouter: NSObject {
manager.apply(routeType: .vehicle)
previewTrip = nil
}
func rebuildRoute() {
guard let trip = previewTrip else { return }
do {
@ -64,7 +64,7 @@ final class CarPlayRouter: NSObject {
})
}
}
func buildRoute(trip: CPTrip) {
completeRouteAndRemovePoints()
previewTrip = trip
@ -87,11 +87,11 @@ final class CarPlayRouter: NSObject {
})
return
}
let manager = RoutingManager.routingManager
manager.add(routePoint: startPoint)
manager.add(routePoint: endPoint)
do {
try manager.buildRoute()
} catch let error as NSError {
@ -101,7 +101,7 @@ final class CarPlayRouter: NSObject {
})
}
}
func updateStartPointAndRebuild(trip: CPTrip) {
let manager = RoutingManager.routingManager
previewTrip = trip
@ -128,27 +128,27 @@ final class CarPlayRouter: NSObject {
})
}
}
func startRoute() {
let manager = RoutingManager.routingManager
manager.startRoute()
}
func setupCarPlaySpeedCameraMode() {
if case .auto = initialSpeedCamSettings {
RoutingManager.routingManager.speedCameraMode = .always
}
}
func setupInitialSpeedCameraMode() {
RoutingManager.routingManager.speedCameraMode = initialSpeedCamSettings
}
func updateSpeedCameraMode(_ mode: SpeedCameraManagerMode) {
initialSpeedCamSettings = mode
RoutingManager.routingManager.speedCameraMode = mode
}
func restoreTripPreviewOnCarplay(beforeRootTemplateDidAppear: Bool) {
guard MWMRouter.isRestoreProcessCompleted() else {
DispatchQueue.main.async { [weak self] in
@ -182,7 +182,7 @@ final class CarPlayRouter: NSObject {
CarPlayService.shared.preparePreview(trips: [trip])
}
}
func restoredNavigationSession() -> (CPTrip, RouteInfo)? {
let manager = RoutingManager.routingManager
if manager.isOnRoute,
@ -211,21 +211,21 @@ extension CarPlayRouter {
self?.updateUpcomingManeuvers()
}
}
func cancelTrip() {
routeSession?.cancelTrip()
routeSession = nil
completeRouteAndRemovePoints()
RoutingManager.routingManager.resetOnNewTurnCallback()
}
func finishTrip() {
routeSession?.finishTrip()
routeSession = nil
completeRouteAndRemovePoints()
RoutingManager.routingManager.resetOnNewTurnCallback()
}
func updateUpcomingManeuvers() {
let maneuvers = createUpcomingManeuvers()
routeSession?.upcomingManeuvers = maneuvers
@ -249,7 +249,7 @@ extension CarPlayRouter {
let measurement = Measurement(value: distance, unit: routeInfo.turnUnits)
return CPTravelEstimates(distanceRemaining: measurement, timeRemaining: 0.0)
}
private func createUpcomingManeuvers() -> [CPManeuver] {
guard let routeInfo = RoutingManager.routingManager.routeInfo else {
return []
@ -286,7 +286,7 @@ extension CarPlayRouter {
}
return maneuvers
}
func createTrip(startPoint: MWMRoutePoint, endPoint: MWMRoutePoint, routeInfo: RouteInfo? = nil) -> CPTrip {
let startPlacemark = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: startPoint.latitude,
longitude: startPoint.longitude))
@ -296,10 +296,10 @@ extension CarPlayRouter {
let startItem = MKMapItem(placemark: startPlacemark)
let endItem = MKMapItem(placemark: endPlacemark)
endItem.name = endPoint.title
let routeChoice = CPRouteChoice(summaryVariants: [" "], additionalInformationVariants: [], selectionSummaryVariants: [])
routeChoice.userInfo = routeInfo
let trip = CPTrip(origin: startItem, destination: endItem, routeChoices: [routeChoice])
trip.userInfo = [CPConstants.Trip.start: startPoint, CPConstants.Trip.end: endPoint]
return trip
@ -308,10 +308,10 @@ extension CarPlayRouter {
// MARK: - RoutingManagerListener implementation
extension CarPlayRouter: RoutingManagerListener {
func updateCameraInfo(isCameraOnRoute: Bool, speedLimit limit: String?) {
CarPlayService.shared.updateCameraUI(isCameraOnRoute: isCameraOnRoute, speedLimit: limit)
func updateCameraInfo(isCameraOnRoute: Bool, speedLimitMps limit: Double) {
CarPlayService.shared.updateCameraUI(isCameraOnRoute: isCameraOnRoute, speedLimitMps: limit < 0 ? nil : limit)
}
func processRouteBuilderEvent(with code: RouterResultCode, countries: [String]) {
guard let trip = previewTrip else {
return
@ -345,10 +345,10 @@ extension CarPlayRouter: RoutingManagerListener {
})
}
}
func didLocationUpdate(_ notifications: [String]) {
guard let trip = previewTrip else { return }
let manager = RoutingManager.routingManager
if manager.isRouteFinished {
listenerContainer.forEach({
@ -356,13 +356,13 @@ extension CarPlayRouter: RoutingManagerListener {
})
return
}
guard let routeInfo = manager.routeInfo,
manager.isRoutingActive else { return }
listenerContainer.forEach({
$0.didUpdateRouteInfo(routeInfo, forTrip: trip)
})
let tts = MWMTextToSpeech.tts()!
if manager.isOnRoute && tts.active {
tts.playTurnNotifications(notifications)

View file

@ -31,7 +31,7 @@ final class CarPlayService: NSObject {
}
var preparedToPreviewTrips: [CPTrip] = []
var isUserPanMap: Bool = false
@objc func setup(window: CPWindow, interfaceController: CPInterfaceController) {
isCarplayActivated = true
self.window = window
@ -63,7 +63,7 @@ final class CarPlayService: NSObject {
ThemeManager.invalidate()
FrameworkHelper.updatePositionArrowOffset(false, offset: 5)
}
@objc func destroy() {
if let carplayVC = carplayVC {
carplayVC.removeMapView()
@ -89,7 +89,7 @@ final class CarPlayService: NSObject {
ThemeManager.invalidate()
FrameworkHelper.updatePositionArrowOffset(true, offset: 0)
}
@objc func interfaceStyle() -> UIUserInterfaceStyle {
if let window = window,
window.traitCollection.userInterfaceIdiom == .carPlay {
@ -97,7 +97,7 @@ final class CarPlayService: NSObject {
}
return .unspecified
}
private func applyRootViewController() {
guard let window = window else { return }
let carplaySotyboard = UIStoryboard.instance(.carPlay)
@ -110,14 +110,14 @@ final class CarPlayService: NSObject {
mapVC.add(self)
}
}
private func applyBaseRootTemplate() {
let mapTemplate = MapTemplateBuilder.buildBaseTemplate(positionMode: currentPositionMode)
mapTemplate.mapDelegate = self
interfaceController?.setRootTemplate(mapTemplate, animated: true)
FrameworkHelper.rotateMap(0.0, animated: false)
}
private func applyNavigationRootTemplate(trip: CPTrip, routeInfo: RouteInfo) {
let mapTemplate = MapTemplateBuilder.buildNavigationTemplate()
mapTemplate.mapDelegate = self
@ -126,13 +126,13 @@ final class CarPlayService: NSObject {
if let estimates = createEstimates(routeInfo: routeInfo) {
mapTemplate.updateEstimates(estimates, for: trip)
}
if let carplayVC = carplayVC {
carplayVC.updateCurrentSpeed(routeInfo.speed)
carplayVC.updateCurrentSpeed(routeInfo.speedMps, speedLimitMps: routeInfo.speedLimitMps)
carplayVC.showSpeedControl()
}
}
func pushTemplate(_ templateToPush: CPTemplate, animated: Bool) {
if let interfaceController = interfaceController {
switch templateToPush {
@ -148,16 +148,16 @@ final class CarPlayService: NSObject {
interfaceController.pushTemplate(templateToPush, animated: animated)
}
}
func popTemplate(animated: Bool) {
interfaceController?.popTemplate(animated: animated)
}
func presentAlert(_ template: CPAlertTemplate, animated: Bool) {
interfaceController?.dismissTemplate(animated: false)
interfaceController?.presentTemplate(template, animated: animated)
}
func cancelCurrentTrip() {
router?.cancelTrip()
if let carplayVC = carplayVC {
@ -165,14 +165,13 @@ final class CarPlayService: NSObject {
}
updateMapTemplateUIToBase()
}
func updateCameraUI(isCameraOnRoute: Bool, speedLimit limit: String?) {
func updateCameraUI(isCameraOnRoute: Bool, speedLimitMps limit: Double?) {
if let carplayVC = carplayVC {
let speedLimit = limit == nil ? nil : Int(limit!)
carplayVC.updateCameraInfo(isCameraOnRoute: isCameraOnRoute, speedLimit: speedLimit)
carplayVC.updateCameraInfo(isCameraOnRoute: isCameraOnRoute, speedLimitMps: limit)
}
}
func updateMapTemplateUIToBase() {
guard let mapTemplate = rootMapTemplate else {
return
@ -188,7 +187,7 @@ final class CarPlayService: NSObject {
updateVisibleViewPortState(.default)
FrameworkHelper.rotateMap(0.0, animated: true)
}
func updateMapTemplateUIToTripFinished(_ trip: CPTrip) {
guard let mapTemplate = rootMapTemplate else {
return
@ -206,7 +205,7 @@ final class CarPlayService: NSObject {
if let address = trip.destination.placemark.postalAddress?.street {
subtitle = subtitle + "\n" + address
}
let alert = CPNavigationAlert(titleVariants: [L("trip_finished")],
subtitleVariants: [subtitle],
imageSet: nil,
@ -215,18 +214,18 @@ final class CarPlayService: NSObject {
duration: 0)
mapTemplate.present(navigationAlert: alert, animated: true)
}
func updateVisibleViewPortState(_ state: CPViewPortState) {
guard let carplayVC = carplayVC else {
return
}
carplayVC.updateVisibleViewPortState(state)
}
func updateRouteAfterChangingSettings() {
router?.rebuildRoute()
}
@objc func showNoMapAlert() {
guard let mapTemplate = interfaceController?.topTemplate as? CPMapTemplate,
let info = mapTemplate.userInfo as? MapInfo,
@ -237,7 +236,7 @@ final class CarPlayService: NSObject {
alert.userInfo = [CPConstants.TemplateKey.alert: CPConstants.TemplateType.downloadMap]
presentAlert(alert, animated: true)
}
@objc func hideNoMapAlert() {
if let presentedTemplate = interfaceController?.presentedTemplate,
let info = presentedTemplate.userInfo as? [String: String],
@ -267,7 +266,7 @@ extension CarPlayService: CPInterfaceControllerDelegate {
break
}
}
func templateDidAppear(_ aTemplate: CPTemplate, animated: Bool) {
guard let mapTemplate = aTemplate as? CPMapTemplate,
let info = aTemplate.userInfo as? MapInfo else {
@ -278,12 +277,12 @@ extension CarPlayService: CPInterfaceControllerDelegate {
preparedToPreviewTrips = []
return
}
if info.type == CPConstants.TemplateType.preview, let trips = info.trips {
showPreview(mapTemplate: mapTemplate, trips: trips)
}
}
func templateWillDisappear(_ aTemplate: CPTemplate, animated: Bool) {
guard let info = aTemplate.userInfo as? MapInfo else {
return
@ -292,7 +291,7 @@ extension CarPlayService: CPInterfaceControllerDelegate {
router?.completeRouteAndRemovePoints()
}
}
func templateDidDisappear(_ aTemplate: CPTemplate, animated: Bool) {
guard !preparedToPreviewTrips.isEmpty,
let info = aTemplate.userInfo as? [String: String],
@ -310,7 +309,7 @@ extension CarPlayService: CPInterfaceControllerDelegate {
extension CarPlayService: CPSessionConfigurationDelegate {
func sessionConfiguration(_ sessionConfiguration: CPSessionConfiguration,
limitedUserInterfacesChanged limitedUserInterfaces: CPLimitableUserInterface) {
}
@available(iOS 13.0, *)
func sessionConfiguration(_ sessionConfiguration: CPSessionConfiguration,
@ -326,7 +325,7 @@ extension CarPlayService: CPMapTemplateDelegate {
MapTemplateBuilder.configurePanUI(mapTemplate: mapTemplate)
FrameworkHelper.stopLocationFollow()
}
public func mapTemplateDidDismissPanningInterface(_ mapTemplate: CPMapTemplate) {
if let info = mapTemplate.userInfo as? MapInfo,
info.type == CPConstants.TemplateType.navigation {
@ -336,7 +335,7 @@ extension CarPlayService: CPMapTemplateDelegate {
}
FrameworkHelper.switchMyPositionMode()
}
func mapTemplate(_ mapTemplate: CPMapTemplate, panEndedWith direction: CPMapTemplate.PanDirection) {
var offset = UIOffset(horizontal: 0.0, vertical: 0.0)
let offsetStep: CGFloat = 0.25
@ -347,7 +346,7 @@ extension CarPlayService: CPMapTemplateDelegate {
FrameworkHelper.moveMap(offset)
isUserPanMap = true
}
func mapTemplate(_ mapTemplate: CPMapTemplate, panWith direction: CPMapTemplate.PanDirection) {
var offset = UIOffset(horizontal: 0.0, vertical: 0.0)
let offsetStep: CGFloat = 0.1
@ -358,7 +357,7 @@ extension CarPlayService: CPMapTemplateDelegate {
FrameworkHelper.moveMap(offset)
isUserPanMap = true
}
func mapTemplate(_ mapTemplate: CPMapTemplate, startedTrip trip: CPTrip, using routeChoice: CPRouteChoice) {
guard let info = routeChoice.userInfo as? RouteInfo else {
if let info = routeChoice.userInfo as? [String: Any],
@ -370,13 +369,13 @@ extension CarPlayService: CPMapTemplateDelegate {
}
mapTemplate.userInfo = MapInfo(type: CPConstants.TemplateType.previewAccepted)
mapTemplate.hideTripPreviews()
guard let router = router,
let interfaceController = interfaceController,
let rootMapTemplate = rootMapTemplate else {
return
}
MapTemplateBuilder.configureNavigationUI(mapTemplate: rootMapTemplate)
if interfaceController.templates.count > 1 {
@ -387,14 +386,14 @@ extension CarPlayService: CPMapTemplateDelegate {
if let estimates = createEstimates(routeInfo: info) {
rootMapTemplate.updateEstimates(estimates, for: trip)
}
if let carplayVC = carplayVC {
carplayVC.updateCurrentSpeed(info.speed)
carplayVC.updateCurrentSpeed(info.speedMps, speedLimitMps: info.speedLimitMps)
carplayVC.showSpeedControl()
}
updateVisibleViewPortState(.navigation)
}
func mapTemplate(_ mapTemplate: CPMapTemplate, displayStyleFor maneuver: CPManeuver) -> CPManeuverDisplayStyle {
if let type = maneuver.userInfo as? String,
type == CPConstants.Maneuvers.secondary {
@ -402,7 +401,7 @@ extension CarPlayService: CPMapTemplateDelegate {
}
return .leadingSymbol
}
func mapTemplate(_ mapTemplate: CPMapTemplate,
selectedPreviewFor trip: CPTrip,
using routeChoice: CPRouteChoice) {
@ -481,7 +480,7 @@ extension CarPlayService: CPSearchTemplateDelegate {
completionHandler(items)
})
}
func searchTemplate(_ searchTemplate: CPSearchTemplate, selectedResult item: CPListItem, completionHandler: @escaping () -> Void) {
searchService?.saveLastQuery()
if let info = item.userInfo as? ListItemInfo,
@ -504,10 +503,10 @@ extension CarPlayService: CarPlayRouterListener {
currentTemplate.updateEstimates(estimates, for: trip)
}
}
func didUpdateRouteInfo(_ routeInfo: RouteInfo, forTrip trip: CPTrip) {
if let carplayVC = carplayVC {
carplayVC.updateCurrentSpeed(routeInfo.speed)
carplayVC.updateCurrentSpeed(routeInfo.speedMps, speedLimitMps: routeInfo.speedLimitMps)
}
guard let router = router,
let template = rootMapTemplate else {
@ -519,13 +518,13 @@ extension CarPlayService: CarPlayRouterListener {
}
trip.routeChoices.first?.userInfo = routeInfo
}
func didFailureBuildRoute(forTrip trip: CPTrip, code: RouterResultCode, countries: [String]) {
guard let template = interfaceController?.topTemplate as? CPMapTemplate else { return }
trip.routeChoices.first?.userInfo = [CPConstants.Trip.errorCode: code, CPConstants.Trip.missedCountries: countries]
applyUndefinedEstimates(template: template, trip: trip)
}
func routeDidFinish(_ trip: CPTrip) {
if router?.currentTrip == nil { return }
router?.finishTrip()
@ -558,7 +557,7 @@ extension CarPlayService: LocationModeListener {
rootMapTemplate.leadingNavigationBarButtons = []
}
}
func processMyPositionPendingTimeout() {
}
}
@ -589,7 +588,7 @@ extension CarPlayService {
}
}
}
func preparePreview(forBookmark bookmark: MWMCarPlayBookmarkObject) {
if let router = router,
let startPoint = MWMRoutePoint(lastLocationAndType: .start,
@ -607,7 +606,7 @@ extension CarPlayService {
}
}
}
func preparePreview(trips: [CPTrip]) {
let mapTemplate = MapTemplateBuilder.buildTripPreviewTemplate(forTrips: trips)
if let interfaceController = interfaceController {
@ -619,14 +618,14 @@ extension CarPlayService {
interfaceController.pushTemplate(mapTemplate, animated: false)
}
}
func showPreview(mapTemplate: CPMapTemplate, trips: [CPTrip]) {
let tripTextConfig = CPTripPreviewTextConfiguration(startButtonTitle: L("trip_start"),
additionalRoutesButtonTitle: nil,
overviewButtonTitle: nil)
mapTemplate.showTripPreviews(trips, textConfiguration: tripTextConfig)
}
func createEstimates(routeInfo: RouteInfo) -> CPTravelEstimates? {
if let distance = Double(routeInfo.targetDistance) {
let measurement = Measurement(value: distance,
@ -637,7 +636,7 @@ extension CarPlayService {
}
return nil
}
func applyUndefinedEstimates(template: CPMapTemplate, trip: CPTrip) {
let measurement = Measurement(value: -1,
unit: UnitLength.meters)
@ -645,7 +644,7 @@ extension CarPlayService {
timeRemaining: -1)
template.updateEstimates(estimates, for: trip)
}
func showRerouteAlert(trips: [CPTrip]) {
let yesAction = CPAlertAction(title: L("yes"), style: .default, handler: { [unowned self] _ in
self.router?.cancelTrip()
@ -660,7 +659,7 @@ extension CarPlayService {
alert.userInfo = [CPConstants.TemplateKey.alert: CPConstants.TemplateType.redirectRoute]
presentAlert(alert, animated: true)
}
func showKeyboardAlert() {
let okAction = CPAlertAction(title: L("ok"), style: .default, handler: { [unowned self] _ in
self.interfaceController?.dismissTemplate(animated: true)
@ -668,7 +667,7 @@ extension CarPlayService {
let alert = CPAlertTemplate(titleVariants: [L("keyboard_availability_alert")], actions: [okAction])
presentAlert(alert, animated: true)
}
func showErrorAlert(code: RouterResultCode, countries: [String]) {
var titleVariants = [String]()
switch code {
@ -697,18 +696,18 @@ extension CarPlayService {
.transitRouteNotFoundTooLongPedestrian:
return
}
let okAction = CPAlertAction(title: L("ok"), style: .cancel, handler: { [unowned self] _ in
self.interfaceController?.dismissTemplate(animated: true)
})
let alert = CPAlertTemplate(titleVariants: titleVariants, actions: [okAction])
presentAlert(alert, animated: true)
}
func showRecoverRouteAlert(trip: CPTrip, isTypeCorrect: Bool) {
let yesAction = CPAlertAction(title: L("ok"), style: .default, handler: { [unowned self] _ in
var info = trip.userInfo as? [String: MWMRoutePoint]
if let startPoint = MWMRoutePoint(lastLocationAndType: .start,
intermediateIndex: 0) {
info?[CPConstants.Trip.start] = startPoint

View file

@ -8,9 +8,10 @@ class RouteInfo: NSObject {
let turnUnits: UnitLength
let turnImageName: String?
let nextTurnImageName: String?
let speed: Int
let speedMps: Double
let speedLimitMps: Double?
let roundExitNumber: Int
@objc init(timeToTarget: TimeInterval,
targetDistance: String,
targetUnits: String,
@ -19,7 +20,8 @@ class RouteInfo: NSObject {
turnUnits: String,
turnImageName: String?,
nextTurnImageName: String?,
speed: Int,
speedMps: Double,
speedLimitMps: Double,
roundExitNumber: Int) {
self.timeToTarget = timeToTarget
self.targetDistance = targetDistance
@ -29,10 +31,12 @@ class RouteInfo: NSObject {
self.turnUnits = RouteInfo.unitLength(for: turnUnits)
self.turnImageName = turnImageName
self.nextTurnImageName = nextTurnImageName
self.speed = speed
self.speedMps = speedMps
// speedLimitMps >= 0 means known limited speed.
self.speedLimitMps = speedLimitMps < 0 ? nil : speedLimitMps
self.roundExitNumber = roundExitNumber
}
class func unitLength(for targetUnits: String) -> UnitLength {
switch targetUnits {
case "mi":

View file

@ -2,25 +2,24 @@
@interface MWMNavigationDashboardEntity : NSObject
@property(copy, nonatomic, readonly) NSArray<MWMRouterTransitStepInfo *> * transitSteps;
@property(copy, nonatomic, readonly) NSAttributedString * estimate;
@property(copy, nonatomic, readonly) NSAttributedString * estimateDot;
@property(copy, nonatomic, readonly) NSString * distanceToTurn;
@property(copy, nonatomic, readonly) NSString * streetName;
@property(copy, nonatomic, readonly) NSString * targetDistance;
@property(copy, nonatomic, readonly) NSString * targetUnits;
@property(copy, nonatomic, readonly) NSString * turnUnits;
@property(copy, nonatomic, readonly) NSString * speedLimit;
@property(copy, nonatomic, readonly) NSArray<MWMRouterTransitStepInfo *> *transitSteps;
@property(copy, nonatomic, readonly) NSAttributedString *estimate;
@property(copy, nonatomic, readonly) NSString *distanceToTurn;
@property(copy, nonatomic, readonly) NSString *streetName;
@property(copy, nonatomic, readonly) NSString *targetDistance;
@property(copy, nonatomic, readonly) NSString *targetUnits;
@property(copy, nonatomic, readonly) NSString *turnUnits;
@property(nonatomic, readonly) double speedLimitMps;
@property(nonatomic, readonly) BOOL isValid;
@property(nonatomic, readonly) CGFloat progress;
@property(nonatomic, readonly) NSString * arrival;
@property(nonatomic, readonly) NSString * eta;
@property(nonatomic, readonly) NSString * speed;
@property(nonatomic, readonly) NSString * speedUnits;
@property(nonatomic, readonly) NSUInteger roundExitNumber;
@property(nonatomic, readonly) UIImage * nextTurnImage;
@property(nonatomic, readonly) UIImage * turnImage;
@property(nonatomic, readonly) BOOL isSpeedLimitExceeded;
@property(nonatomic, readonly) NSUInteger timeToTarget;
@property(nonatomic, readonly) UIImage *nextTurnImage;
@property(nonatomic, readonly) UIImage *turnImage;
@property(nonatomic, readonly) NSString * arrival;
+ (NSAttributedString *) estimateDot;
+ (instancetype) new __attribute__((unavailable("init is not available")));

View file

@ -1,73 +0,0 @@
#import "MWMNavigationDashboardEntity.h"
#import "MWMCoreUnits.h"
#import "MWMLocationManager.h"
#import "MWMRouterTransitStepInfo.h"
#import "MWMSettings.h"
#import "SwiftBridge.h"
#include <CoreApi/Framework.h>
#include "map/routing_manager.hpp"
#include "platform/localization.hpp"
#include "platform/measurement_utils.hpp"
@interface MWMNavigationDashboardEntity ()
@property(copy, nonatomic, readwrite) NSArray<MWMRouterTransitStepInfo *> * transitSteps;
@property(copy, nonatomic, readwrite) NSAttributedString * estimate;
@property(copy, nonatomic, readwrite) NSString * distanceToTurn;
@property(copy, nonatomic, readwrite) NSString * streetName;
@property(copy, nonatomic, readwrite) NSString * targetDistance;
@property(copy, nonatomic, readwrite) NSString * targetUnits;
@property(copy, nonatomic, readwrite) NSString * turnUnits;
@property(copy, nonatomic, readwrite) NSString * speedLimit;
@property(nonatomic, readwrite) BOOL isValid;
@property(nonatomic, readwrite) CGFloat progress;
@property(nonatomic, readwrite) NSUInteger roundExitNumber;
@property(nonatomic, readwrite) NSUInteger timeToTarget;
@property(nonatomic, readwrite) UIImage * nextTurnImage;
@property(nonatomic, readwrite) UIImage * turnImage;
@end
@implementation MWMNavigationDashboardEntity
- (NSString *)speed
{
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation || lastLocation.speed < 0)
return nil;
auto const units = coreUnits([MWMSettings measurementUnits]);
return @(measurement_utils::FormatSpeedNumeric(lastLocation.speed, units).c_str());
}
- (BOOL)isSpeedLimitExceeded
{
return GetFramework().GetRoutingManager().IsSpeedLimitExceeded();
}
- (NSString *)speedUnits
{
return @(platform::GetLocalizedSpeedUnits().c_str());
}
- (NSString *)eta { return [NSDateComponentsFormatter etaStringFrom:self.timeToTarget]; }
- (NSString *)arrival
{
auto arrivalDate = [[NSDate date] dateByAddingTimeInterval:self.timeToTarget];
return [NSDateFormatter localizedStringFromDate:arrivalDate
dateStyle:NSDateFormatterNoStyle
timeStyle:NSDateFormatterShortStyle];
}
- (NSAttributedString *)estimateDot
{
auto attributes = @{
NSForegroundColorAttributeName: [UIColor blackSecondaryText],
NSFontAttributeName: [UIFont medium17]
};
return [[NSAttributedString alloc] initWithString:@" • " attributes:attributes];
}
@end

View file

@ -68,11 +68,11 @@ UIImage *image(routing::turns::PedestrianDirection t) {
return [UIImage imageNamed:imageName];
}
NSAttributedString *estimate(NSTimeInterval time, NSAttributedString *dot, NSString *distance, NSString *distanceUnits,
NSAttributedString *estimate(NSTimeInterval time, NSString *distance, NSString *distanceUnits,
NSDictionary *primaryAttributes, NSDictionary *secondaryAttributes, BOOL isWalk) {
NSString *eta = [NSDateComponentsFormatter etaStringFrom:time];
auto result = [[NSMutableAttributedString alloc] initWithString:eta attributes:primaryAttributes];
[result appendAttributedString:dot];
[result appendAttributedString:MWMNavigationDashboardEntity.estimateDot];
if (isWalk) {
UIFont *font = primaryAttributes[NSFontAttributeName];
@ -106,7 +106,7 @@ NSAttributedString *estimate(NSTimeInterval time, NSAttributedString *dot, NSStr
@property(copy, nonatomic, readwrite) NSString *targetDistance;
@property(copy, nonatomic, readwrite) NSString *targetUnits;
@property(copy, nonatomic, readwrite) NSString *turnUnits;
@property(copy, nonatomic, readwrite) NSString *speedLimit;
@property(nonatomic, readwrite) double speedLimitMps;
@property(nonatomic, readwrite) BOOL isValid;
@property(nonatomic, readwrite) CGFloat progress;
@property(nonatomic, readwrite) NSUInteger roundExitNumber;
@ -116,6 +116,27 @@ NSAttributedString *estimate(NSTimeInterval time, NSAttributedString *dot, NSStr
@end
@implementation MWMNavigationDashboardEntity
- (NSString *)arrival
{
auto arrivalDate = [[NSDate date] dateByAddingTimeInterval:self.timeToTarget];
return [NSDateFormatter localizedStringFromDate:arrivalDate
dateStyle:NSDateFormatterNoStyle
timeStyle:NSDateFormatterShortStyle];
}
+ (NSAttributedString *)estimateDot
{
auto attributes = @{
NSForegroundColorAttributeName: [UIColor blackSecondaryText],
NSFontAttributeName: [UIFont medium17]
};
return [[NSAttributedString alloc] initWithString:@" • " attributes:attributes];
}
@end
@interface MWMRouterTransitStepInfo ()
- (instancetype)initWithStepInfo:(TransitStepInfo const &)info;
@ -150,9 +171,9 @@ NSAttributedString *estimate(NSTimeInterval time, NSAttributedString *dot, NSStr
entity.distanceToTurn = @(info.m_distToTurn.c_str());
entity.turnUnits = [self localizedUnitLength:@(info.m_turnUnitsSuffix.c_str())];
entity.streetName = @(info.m_displayedStreetName.c_str());
entity.speedLimit = @(info.m_speedLimit.c_str());
entity.speedLimitMps = info.m_speedLimitMps;
entity.estimate = estimate(entity.timeToTarget, entity.estimateDot, entity.targetDistance, entity.targetUnits,
entity.estimate = estimate(entity.timeToTarget, entity.targetDistance, entity.targetUnits,
self.etaAttributes, self.etaSecondaryAttributes, NO);
if (type == MWMRouterTypePedestrian) {
@ -178,7 +199,7 @@ NSAttributedString *estimate(NSTimeInterval time, NSAttributedString *dot, NSStr
if (auto entity = self.entity) {
entity.isValid = YES;
entity.estimate =
estimate(info.m_totalTimeInSec, entity.estimateDot, @(info.m_totalPedestrianDistanceStr.c_str()),
estimate(info.m_totalTimeInSec, @(info.m_totalPedestrianDistanceStr.c_str()),
@(info.m_totalPedestrianUnitsSuffix.c_str()), self.etaAttributes, self.etaSecondaryAttributes, YES);
NSMutableArray<MWMRouterTransitStepInfo *> *transitSteps = [@[] mutableCopy];
for (auto const &stepInfo : info.m_steps)

View file

@ -136,7 +136,7 @@ final class NavigationControlView: SolidTouchView, MWMTextToSpeechObserver, MapO
]
if timePageControl.currentPage == 0 {
timeLabel.text = info.eta
timeLabel.text = DateComponentsFormatter.etaString(from: TimeInterval(info.timeToTarget))
} else {
timeLabel.text = info.arrival
}
@ -155,23 +155,35 @@ final class NavigationControlView: SolidTouchView, MWMTextToSpeechObserver, MapO
}
}
var speed = info.speed ?? "0"
if (info.speedLimit != "") {
speed += " / " + info.speedLimit;
var speedMps = 0.0
if let s = LocationManager.lastLocation()?.speed, s > 0 {
speedMps = s
}
let speedMeasure = Measure(asSpeed: speedMps)
var speed = speedMeasure.valueAsString;
// speedLimitMps >= 0 means known limited speed.
if (info.speedLimitMps >= 0) {
let speedLimitMeasure = Measure(asSpeed: info.speedLimitMps)
// speedLimitMps == 0 means unlimited speed.
speed += " / " + (info.speedLimitMps == 0 ? "" : speedLimitMeasure.valueAsString);
}
speedLabel.text = speed
biodranik commented 2022-07-29 17:40:49 +00:00 (Migrated from github.com)
Review

Проверка s > 0 лишняя, если значение не может быть отрицательным. А если может, то почему?

Проверка `s > 0` лишняя, если значение не может быть отрицательным. А если может, то почему?
AntonM030481 commented 2022-08-01 08:20:46 +00:00 (Migrated from github.com)
Review
https://developer.apple.com/documentation/corelocation/cllocation/1423798-speed A negative value indicates an invalid speed.
speedLegendLabel.text = info.speedUnits
speedLegendLabel.text = speedMeasure.unit
let speedWithLegend = NSMutableAttributedString(string: speed, attributes: routingNumberAttributes)
speedWithLegend.append(NSAttributedString(string: info.speedUnits, attributes: routingLegendAttributes))
speedWithLegend.append(NSAttributedString(string: speedMeasure.unit, attributes: routingLegendAttributes))
speedWithLegendLabel.attributedText = speedWithLegend
let speedLimitExceeded = info.isSpeedLimitExceeded
let textColor = speedLimitExceeded ? UIColor.white() : UIColor.blackPrimaryText()
speedBackground.backgroundColor = speedLimitExceeded ? UIColor.buttonRed() : UIColor.clear
speedLabel.textColor = textColor
speedLegendLabel.textColor = textColor
speedWithLegendLabel.textColor = textColor
if MWMRouter.isSpeedCamLimitExceeded() {
speedLabel.textColor = UIColor.white()
speedBackground.backgroundColor = UIColor.buttonRed()
} else {
let isSpeedLimitExceeded = info.speedLimitMps > 0 && speedMps > info.speedLimitMps
speedLabel.textColor = isSpeedLimitExceeded ? UIColor.buttonRed() : UIColor.blackPrimaryText()
speedBackground.backgroundColor = UIColor.clear
}
speedLegendLabel.textColor = speedLabel.textColor
speedWithLegendLabel.textColor = speedLabel.textColor
biodranik commented 2022-07-29 17:43:37 +00:00 (Migrated from github.com)
Review

Использование класса-враппера только для удобства конвертации не обосновано, слишком много оверхеда. Предлагаю сделать метод в C++, который потом можно будет переиспользовать и в Android, чтобы было единообразно.

Использование класса-враппера только для удобства конвертации не обосновано, слишком много оверхеда. Предлагаю сделать метод в C++, который потом можно будет переиспользовать и в Android, чтобы было единообразно.
biodranik commented 2022-07-29 17:45:17 +00:00 (Migrated from github.com)
Review

В самом крайнем случае, если не получится, просто сделать кусок кода на С++ внутри .mm файла, и пометить комментарием его потом переиспользовать в ядре для Андроида и других платформ.

В самом крайнем случае, если не получится, просто сделать кусок кода на С++ внутри .mm файла, и пометить комментарием его потом переиспользовать в ядре для Андроида и других платформ.
vng commented 2022-07-30 00:34:17 +00:00 (Migrated from github.com)
Review

Да, просто C++ -> ObjC функция для использования в Swift.

Да, просто C++ -> ObjC функция для использования в Swift.
vng commented 2022-07-30 00:40:02 +00:00 (Migrated from github.com)
Review

На крайний случай valueAsString настолько проста, что достаточно в ObjC иметь простую именованную функцию типа SpeedToString

На крайний случай valueAsString настолько проста, что достаточно в ObjC иметь простую именованную функцию типа SpeedToString
AntonM030481 commented 2022-08-01 14:29:59 +00:00 (Migrated from github.com)
Review

Хотелось универсальное решение втч и для расстояния, когда в зависимости от длины используются крупные или мелкие юниты. А для этого логично получать пару размера и юнитов одновременно.
А что именно с классом не нравится? Вызов 3 C++ функций вместо одной?
Где можно почитать про overhead в аналогичных ситуациях?

Кстати, сейчас есть такое, например:
c2c1f979ac/iphone/Maps/UI/PlacePage/Components/ElevationProfile/ElevationProfilePresenter.swift (L243-L244)

В Java сейчас такой подход (но только нужно число, а не строка):
c2c1f979ac/android/jni/com/mapswithme/util/StringUtils.cpp (L57-L60)

Хотелось универсальное решение втч и для расстояния, когда в зависимости от длины используются крупные или мелкие юниты. А для этого логично получать пару размера и юнитов одновременно. А что именно с классом не нравится? Вызов 3 C++ функций вместо одной? Где можно почитать про overhead в аналогичных ситуациях? Кстати, сейчас есть такое, например: https://github.com/organicmaps/organicmaps/blob/c2c1f979ac9d9422eae5ae72dad1975b49a47061/iphone/Maps/UI/PlacePage/Components/ElevationProfile/ElevationProfilePresenter.swift#L243-L244 В Java сейчас такой подход (но только нужно число, а не строка): https://github.com/organicmaps/organicmaps/blob/c2c1f979ac9d9422eae5ae72dad1975b49a47061/android/jni/com/mapswithme/util/StringUtils.cpp#L57-L60
AntonM030481 commented 2022-08-01 16:31:53 +00:00 (Migrated from github.com)
Review

Замерил быстродействие:
Measure.init(asSpeed: v)
vs

let imperial = Settings.measurementUnits() == .imperial
var altMeasurement = Measurement(value: v, unit: UnitLength.meters)
altMeasurement.convert(to: imperial ? UnitLength.feet : UnitLength.meters)

Если убрать запрос к platform::GetLocalizedSpeedUnits(units):

auto units = measurement_utils::Units::Metric;
settings::TryGet(settings::kMeasurementUnits, units);
_value = measurement_utils::MpsToUnits(mps, units);
_unit = @(units == measurement_utils::Units::Metric ? "kmph" : "mph");

результат одинаков
а если его оставить - разница в 20 раз.
Вывод - bottleneck в запросе к локализации строки (что ожидаемо), а не в конструировании объекта.

Замерил быстродействие: `Measure.init(asSpeed: v)` vs ``` let imperial = Settings.measurementUnits() == .imperial var altMeasurement = Measurement(value: v, unit: UnitLength.meters) altMeasurement.convert(to: imperial ? UnitLength.feet : UnitLength.meters) ``` Если убрать запрос к platform::GetLocalizedSpeedUnits(units): ``` auto units = measurement_utils::Units::Metric; settings::TryGet(settings::kMeasurementUnits, units); _value = measurement_utils::MpsToUnits(mps, units); _unit = @(units == measurement_utils::Units::Metric ? "kmph" : "mph"); ``` результат одинаков а если его оставить - разница в 20 раз. Вывод - bottleneck в запросе к локализации строки (что ожидаемо), а не в конструировании объекта.
biodranik commented 2022-08-01 21:10:40 +00:00 (Migrated from github.com)
Review

А что именно с классом не нравится? Вызов 3 C++ функций вместо одной?

Динамическая аллокация, которой лучше избегать в часто повторяемых операциях. Такой же объект, созданный на стеке, вообще бы не вызывал вопросов.

Кстати, сейчас есть такое, например:

Вот в т.ч. и из-за подобных велосипедов на изолиниях теперь всегда метры рисуются.

Вывод - bottleneck в запросе к локализации строки (что ожидаемо), а не в конструировании объекта.

Если пользователь идёт в настройки и меняет юниты (крайне редкая операция), то диалоги пересоздаются. Вывод: строки/юниты можно и нужно инициализировать один раз для сессии диалога, и на ведре, и на айосе.

> А что именно с классом не нравится? Вызов 3 C++ функций вместо одной? Динамическая аллокация, которой лучше избегать в часто повторяемых операциях. Такой же объект, созданный на стеке, вообще бы не вызывал вопросов. > Кстати, сейчас есть такое, например: Вот в т.ч. и из-за подобных велосипедов на изолиниях теперь всегда метры рисуются. > Вывод - bottleneck в запросе к локализации строки (что ожидаемо), а не в конструировании объекта. Если пользователь идёт в настройки и меняет юниты (крайне редкая операция), то диалоги пересоздаются. Вывод: строки/юниты можно и нужно инициализировать один раз для сессии диалога, и на ведре, и на айосе.
AntonM030481 commented 2022-08-02 09:09:59 +00:00 (Migrated from github.com)
Review

Я бы предпочел просто использовать статические переменные вместо того чтобы засорять классы ненужными переменными. По тестам быстродействие вполне нормальное (сравнимое со встроенным Measurement).
6d5d5ad891/platform/localization.cpp (L19-L45)

Я бы предпочел просто использовать статические переменные вместо того чтобы засорять классы ненужными переменными. По тестам быстродействие вполне нормальное (сравнимое со встроенным Measurement). https://github.com/AntonM030481/organicmaps/blob/6d5d5ad891141932f9263f66556e9e052ec55f58/platform/localization.cpp#L19-L45
routingProgress.constant = progressView.width * info.progress / 100
}

View file

@ -7,9 +7,6 @@ typedef NS_ENUM(NSInteger, MWMDrivingOptionsState) {
MWMDrivingOptionsStateChange
};
@class MWMNavigationDashboardEntity;
@class MWMNavigationDashboardManager;
@class MWMTaxiCollectionView;
@class MWMRoutePreview;
@protocol MWMRoutePreviewDelegate

View file

@ -1,8 +1,6 @@
#import "MWMRoutePreview.h"
#import "MWMCircularProgress.h"
#import "MWMLocationManager.h"
#import "MWMNavigationDashboardEntity.h"
#import "MWMNavigationDashboardManager.h"
#import "MWMRouter.h"
#import "UIButton+Orientation.h"
#import "UIImageView+Coloring.h"

View file

@ -140,7 +140,7 @@ final class BaseRoutePreviewStatus: SolidTouchView {
if let result = info.estimate.mutableCopy() as? NSMutableAttributedString {
if let elevation = self.elevation {
result.append(info.estimateDot)
result.append(MWMNavigationDashboardEntity.estimateDot())
result.append(elevation)
}
resultLabel.attributedText = result

View file

@ -20,7 +20,7 @@ using namespace storage;
- (void)processRouteBuilderProgress:(CGFloat)progress;
- (void)processRouteRecommendation:(MWMRouterRecommendation)recommendation;
- (void)speedCameraShowedUpOnRoute:(double)speedLimit;
- (void)speedCameraShowedUpOnRoute:(double)speedLimitKMph;
- (void)speedCameraLeftVisibleArea;
@end

View file

@ -11,7 +11,7 @@ NS_SWIFT_NAME(RoutingManagerListener)
- (void)processRouteBuilderEventWithCode:(MWMRouterResultCode)code
countries:(NSArray<NSString *> *)absentCountries;
- (void)didLocationUpdate:(NSArray<NSString *> *)notifications;
- (void)updateCameraInfo:(BOOL)isCameraOnRoute speedLimit:(nullable NSString *)limit NS_SWIFT_NAME(updateCameraInfo(isCameraOnRoute:speedLimit:));
- (void)updateCameraInfo:(BOOL)isCameraOnRoute speedLimitMps:(double)limit NS_SWIFT_NAME(updateCameraInfo(isCameraOnRoute:speedLimitMps:));
@end
NS_SWIFT_NAME(RoutingManager)

View file

@ -82,10 +82,9 @@
self.rm.GetRouteFollowingInfo(info);
if (!info.IsValid()) { return nil; }
CLLocation * lastLocation = [MWMLocationManager lastLocation];
NSString *speed = @"0";
double speedMps = 0;
if (lastLocation && lastLocation.speed >= 0) {
auto const units = coreUnits([MWMSettings measurementUnits]);
speed = @(measurement_utils::FormatSpeedNumeric(lastLocation.speed, units).c_str());
speedMps = lastLocation.speed;
}
NSInteger roundExitNumber = 0;
if (info.m_turn == routing::turns::CarDirection::EnterRoundAbout ||
@ -93,7 +92,7 @@
info.m_turn == routing::turns::CarDirection::LeaveRoundAbout) {
roundExitNumber = info.m_exitNum;
}
MWMRouteInfo *objCInfo = [[MWMRouteInfo alloc] initWithTimeToTarget:info.m_time
targetDistance:@(info.m_distToTarget.c_str())
targetUnits:@(info.m_targetUnitsSuffix.c_str())
@ -102,8 +101,9 @@
turnUnits:@(info.m_turnUnitsSuffix.c_str())
turnImageName:[self turnImageName:info.m_turn isPrimary:YES]
nextTurnImageName:[self turnImageName:info.m_nextTurn isPrimary:NO]
speed:[speed integerValue]
roundExitNumber: roundExitNumber];
speedMps:speedMps
speedLimitMps:info.m_speedLimitMps
roundExitNumber:roundExitNumber];
return objCInfo;
}
@ -143,7 +143,7 @@
- (void)buildRouteWithDidFailError:(NSError * __autoreleasing __nullable *)errorPtr {
auto const & points = self.rm.GetRoutePoints();
auto const pointsCount = points.size();
if (pointsCount > 1) {
self.rm.BuildRoute();
} else {
@ -229,12 +229,10 @@
NSArray<id<MWMRoutingManagerListener>> * objects = self.listeners.allObjects;
for (id<MWMRoutingManagerListener> object in objects) {
if (speedLimit == routing::SpeedCameraOnRoute::kNoSpeedInfo) {
[object updateCameraInfo:YES speedLimit:nil];
[object updateCameraInfo:YES speedLimitMps:-1];
} else {
auto const metersPerSecond = measurement_utils::KmphToMps(speedLimit);
NSString *limit = @(measurement_utils::FormatSpeed(metersPerSecond).c_str());
[object updateCameraInfo:YES speedLimit:limit];
[object updateCameraInfo:YES speedLimitMps:metersPerSecond];
}
}
}
@ -242,7 +240,7 @@
- (void)speedCameraLeftVisibleArea {
NSArray<id<MWMRoutingManagerListener>> * objects = self.listeners.allObjects;
for (id<MWMRoutingManagerListener> object in objects) {
[object updateCameraInfo:NO speedLimit:nil];
[object updateCameraInfo:NO speedLimitMps:-1];
}
}

View file

@ -21,6 +21,8 @@ typedef void (^MWMImageHeightBlock)(UIImage *, NSString *);
+ (BOOL)isRouteRebuildingOnly;
+ (BOOL)isOnRoute;
+ (BOOL)isSpeedCamLimitExceeded;
+ (BOOL)canAddIntermediatePoint;
+ (void)startRouting;

View file

@ -87,6 +87,12 @@ char const *kRenderAltitudeImagesQueueLabel = "mapsme.mwmrouter.renderAltitudeIm
+ (BOOL)IsRouteValid {
return GetFramework().GetRoutingManager().IsRouteValid();
}
+ (BOOL)isSpeedCamLimitExceeded
{
return GetFramework().GetRoutingManager().IsSpeedCamLimitExceeded();
}
+ (NSArray<MWMRoutePoint *> *)points {
NSMutableArray<MWMRoutePoint *> *points = [@[] mutableCopy];
auto const routePoints = GetFramework().GetRoutingManager().GetRoutePoints();

View file

@ -99,7 +99,6 @@
34AB39C21D2BD8310021857D /* MWMStopButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AB39C01D2BD8310021857D /* MWMStopButton.m */; };
34AB66051FC5AA320078E451 /* MWMNavigationDashboardManager+Entity.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34AB65C51FC5AA320078E451 /* MWMNavigationDashboardManager+Entity.mm */; };
34AB66081FC5AA320078E451 /* MWMNavigationDashboardManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34AB65C61FC5AA320078E451 /* MWMNavigationDashboardManager.mm */; };
34AB660B1FC5AA320078E451 /* MWMNavigationDashboardEntity.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34AB65CB1FC5AA320078E451 /* MWMNavigationDashboardEntity.mm */; };
34AB660E1FC5AA320078E451 /* NavigationControlView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34AB65CD1FC5AA320078E451 /* NavigationControlView.xib */; };
34AB66111FC5AA320078E451 /* NavigationTurnsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34AB65CE1FC5AA320078E451 /* NavigationTurnsView.swift */; };
34AB66141FC5AA320078E451 /* MWMNavigationInfoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34AB65D01FC5AA320078E451 /* MWMNavigationInfoView.xib */; };
@ -855,7 +854,6 @@
34AB65C71FC5AA320078E451 /* MWMNavigationDashboardManager+Entity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MWMNavigationDashboardManager+Entity.h"; sourceTree = "<group>"; };
34AB65C91FC5AA320078E451 /* MWMNavigationDashboardEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMNavigationDashboardEntity.h; sourceTree = "<group>"; };
34AB65CA1FC5AA320078E451 /* MWMNavigationDashboardManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMNavigationDashboardManager.h; sourceTree = "<group>"; };
34AB65CB1FC5AA320078E451 /* MWMNavigationDashboardEntity.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMNavigationDashboardEntity.mm; sourceTree = "<group>"; };
34AB65CD1FC5AA320078E451 /* NavigationControlView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NavigationControlView.xib; sourceTree = "<group>"; };
34AB65CE1FC5AA320078E451 /* NavigationTurnsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationTurnsView.swift; sourceTree = "<group>"; };
34AB65CF1FC5AA320078E451 /* MWMNavigationInfoView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMNavigationInfoView.h; sourceTree = "<group>"; };
@ -2267,7 +2265,6 @@
isa = PBXGroup;
children = (
34AB65C91FC5AA320078E451 /* MWMNavigationDashboardEntity.h */,
34AB65CB1FC5AA320078E451 /* MWMNavigationDashboardEntity.mm */,
34AB65CA1FC5AA320078E451 /* MWMNavigationDashboardManager.h */,
34AB65C61FC5AA320078E451 /* MWMNavigationDashboardManager.mm */,
34AB65C71FC5AA320078E451 /* MWMNavigationDashboardManager+Entity.h */,
@ -4325,7 +4322,6 @@
993DF10923F6BDB100AC231A /* IFonts.swift in Sources */,
47699A0821F08E37009E6585 /* NSDate+TimeDistance.m in Sources */,
34845DB31E165E24003D55B9 /* SearchNoResultsViewController.swift in Sources */,
34AB660B1FC5AA320078E451 /* MWMNavigationDashboardEntity.mm in Sources */,
47E3C72B2111E62A008B3B27 /* FadeOutAnimatedTransitioning.swift in Sources */,
471A7BC4248471BE00A0D4C1 /* BookmarkUIUtils.swift in Sources */,
);

View file

@ -1,23 +1,25 @@
final class CarPlayMapViewController: MWMViewController {
private(set) var mapView: EAGLView?
@IBOutlet var speedInfoView: UIView!
@IBOutlet var speedLimitContainer: UIView!
@IBOutlet var speedCamLimitContainer: UIView!
@IBOutlet var speedCamImageView: UIImageView!
@IBOutlet var speedLimitLabel: UILabel!
@IBOutlet var speedCamLimitLabel: UILabel!
@IBOutlet var currentSpeedView: UIView!
@IBOutlet var currentSpeedLabel: UILabel!
private var currentSpeed: Int = 0
private var speedLimit: Int?
private var currentSpeedMps: Double = 0.0
private var speedLimitMps: Double?
private var speedCamLimitMps: Double?
private var isCameraOnRoute: Bool = false
private var viewPortState: CPViewPortState = .default
private var isSpeedCamBlinking: Bool = false
private var isLeftWheelCar: Bool {
return self.speedInfoView.frame.origin.x > self.view.frame.midX
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if mapView?.drapeEngineCreated == false {
@ -25,11 +27,11 @@ final class CarPlayMapViewController: MWMViewController {
}
updateVisibleViewPortState(viewPortState)
}
func addMapView(_ mapView: EAGLView, mapButtonSafeAreaLayoutGuide: UILayoutGuide) {
mapView.translatesAutoresizingMaskIntoConstraints = false
removeMapView()
self.mapView = mapView
mapView.frame = view.bounds
view.insertSubview(mapView, at: 0)
@ -38,73 +40,124 @@ final class CarPlayMapViewController: MWMViewController {
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
speedInfoView.trailingAnchor.constraint(equalTo: mapButtonSafeAreaLayoutGuide.trailingAnchor).isActive = true
speedCamLimitContainer.layer.borderWidth = 2.0
}
func removeMapView() {
if let mapView = mapView {
if let mapView = self.mapView {
mapView.removeFromSuperview()
self.mapView = nil
}
}
func hideSpeedControl() {
if !speedInfoView.isHidden {
speedInfoView.isHidden = true
}
}
func showSpeedControl() {
if speedInfoView.isHidden {
speedInfoView.isHidden = false
}
}
func updateCurrentSpeed(_ speed: Int) {
self.currentSpeed = speed
func updateCurrentSpeed(_ speedMps: Double, speedLimitMps: Double?) {
self.currentSpeedMps = speedMps
self.speedLimitMps = speedLimitMps
updateSpeedControl()
}
func updateCameraInfo(isCameraOnRoute: Bool, speedLimit: Int?) {
func updateCameraInfo(isCameraOnRoute: Bool, speedLimitMps: Double?) {
self.isCameraOnRoute = isCameraOnRoute
self.speedLimit = speedLimit
self.speedCamLimitMps = speedLimitMps
updateSpeedControl()
}
private func BlinkSpeedCamLimit(blink: Bool)
{
if blink {
if !isSpeedCamBlinking {
speedCamLimitLabel.alpha = 0
speedCamImageView.alpha = 1
UIView.animate(withDuration: 0.5,
delay:0.0,
options:[.repeat, .autoreverse, .curveEaseOut],
animations: { self.speedCamImageView.alpha = 0; self.speedCamLimitLabel.alpha = 1 })
isSpeedCamBlinking = true
}
} else {
if (isSpeedCamBlinking) {
speedCamLimitLabel.layer.removeAllAnimations()
speedCamImageView.layer.removeAllAnimations()
isSpeedCamBlinking = false
}
}
}
private func updateSpeedControl() {
currentSpeedLabel.text = "\(currentSpeed)"
let speedMeasure = Measure.init(asSpeed: currentSpeedMps)
currentSpeedLabel.text = speedMeasure.valueAsString
if isCameraOnRoute {
speedLimitContainer.layer.borderColor = UIColor.speedLimitRed().cgColor
speedLimitContainer.layer.borderWidth = 2.0
if let speedLimit = speedLimit {
speedCamImageView.alpha = 0.0
speedLimitLabel.textColor = UIColor.speedLimitDarkGray()
speedLimitLabel.text = "\(speedLimit)"
speedLimitLabel.alpha = 1.0
speedCamLimitContainer.layer.borderColor = UIColor.speedLimitRed().cgColor
speedCamImageView.tintColor = UIColor.speedLimitRed()
// self.speedCamLimitMps comes from SpeedCamManager and is based on
// the nearest speed camera info when it is close enough.
// If it's unknown self.speedLimitMps is used, which is based on current road speed limit.
if let speedCamLimitMps = (self.speedCamLimitMps ?? self.speedLimitMps) {
BlinkSpeedCamLimit(blink: true)
let speedCamLimitMeasure = Measure.init(asSpeed: speedCamLimitMps)
speedCamLimitLabel.text = speedCamLimitMeasure.valueAsString
speedCamLimitLabel.textColor = UIColor.speedLimitDarkGray()
currentSpeedLabel.textColor = UIColor.white
if speedLimit >= currentSpeed {
currentSpeedView.backgroundColor = UIColor.speedLimitGeen()
if speedCamLimitMps >= currentSpeedMps {
currentSpeedView.backgroundColor = UIColor.speedLimitGreen()
} else {
currentSpeedView.backgroundColor = UIColor.speedLimitRed()
}
} else {
speedLimitLabel.alpha = 0.0
BlinkSpeedCamLimit(blink: false)
speedCamLimitLabel.alpha = 0.0
speedCamImageView.tintColor = UIColor.speedLimitRed()
speedCamImageView.alpha = 1.0
currentSpeedLabel.textColor = UIColor.speedLimitDarkGray()
currentSpeedView.backgroundColor = UIColor.speedLimitWhite()
}
} else {
speedLimitContainer.layer.borderColor = UIColor.speedLimitLightGray().cgColor
speedLimitContainer.layer.borderWidth = 2.0
speedLimitLabel.alpha = 0.0
speedCamImageView.tintColor = UIColor.speedLimitLightGray()
speedCamImageView.alpha = 1.0
} else { // !isCameraOnRoute
BlinkSpeedCamLimit(blink: false)
currentSpeedLabel.textColor = UIColor.speedLimitDarkGray()
if let speedLimitMps = self.speedLimitMps {
speedCamImageView.alpha = 0.0
let speedLimitMeasure = Measure.init(asSpeed: speedLimitMps)
speedCamLimitLabel.textColor = UIColor.speedLimitDarkGray()
// speedLimitMps == 0 means unlimited speed.
if speedLimitMeasure.value == 0 {
speedCamLimitLabel.text = "🚀" //""
}
else {
speedCamLimitLabel.text = speedLimitMeasure.valueAsString;
}
speedCamLimitLabel.alpha = 1.0
speedCamLimitContainer.layer.borderColor = UIColor.speedLimitRed().cgColor
if currentSpeedMps > speedLimitMps {
currentSpeedLabel.textColor = UIColor.speedLimitRed()
}
} else {
speedCamImageView.tintColor = UIColor.speedLimitLightGray()
speedCamImageView.alpha = 1.0
speedCamLimitLabel.alpha = 0.0
speedCamLimitContainer.layer.borderColor = UIColor.speedLimitLightGray().cgColor
}
currentSpeedView.backgroundColor = UIColor.speedLimitWhite()
}
}
func updateVisibleViewPortState(_ state: CPViewPortState) {
viewPortState = state
switch viewPortState {
@ -116,7 +169,7 @@ final class CarPlayMapViewController: MWMViewController {
updateVisibleViewPortToNavigationState()
}
}
private func updateVisibleViewPortToPreviewState() {
let viewBounds = view.bounds
let previewWidth = self.view.frame.width * 0.45
@ -128,7 +181,7 @@ final class CarPlayMapViewController: MWMViewController {
height: viewBounds.height - origin.y)
FrameworkHelper.setVisibleViewport(frame, scaleFactor: mapView?.contentScaleFactor ?? 1)
}
private func updateVisibleViewPortToNavigationState() {
let viewBounds = view.bounds
let previewWidth = viewBounds.width * 0.45
@ -141,11 +194,11 @@ final class CarPlayMapViewController: MWMViewController {
height: viewBounds.height - origin.y)
FrameworkHelper.setVisibleViewport(frame, scaleFactor: mapView?.contentScaleFactor ?? 1)
}
private func updateVisibleViewPortToDefaultState() {
FrameworkHelper.setVisibleViewport(view.bounds, scaleFactor: mapView?.contentScaleFactor ?? 1)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
ThemeManager.invalidate()

View file

@ -36,7 +36,7 @@ class PlacePageCommonLayout: NSObject, IPlacePageLayout {
vc.placePagePreviewData = placePageData.previewData
return vc
} ()
lazy var wikiDescriptionViewController: WikiDescriptionViewController = {
let vc = storyboard.instantiateViewController(ofType: WikiDescriptionViewController.self)
vc.view.isHidden = true
@ -50,14 +50,14 @@ class PlacePageCommonLayout: NSObject, IPlacePageLayout {
vc.delegate = interactor
return vc
} ()
lazy var infoViewController: PlacePageInfoViewController = {
let vc = storyboard.instantiateViewController(ofType: PlacePageInfoViewController.self)
vc.placePageInfoData = placePageData.infoData
vc.delegate = interactor
return vc
} ()
lazy var buttonsViewController: PlacePageButtonsViewController = {
let vc = storyboard.instantiateViewController(ofType: PlacePageButtonsViewController.self)
vc.buttonsData = placePageData.buttonsData!
@ -65,7 +65,7 @@ class PlacePageCommonLayout: NSObject, IPlacePageLayout {
vc.delegate = interactor
return vc
} ()
lazy var actionBarViewController: ActionBarViewController = {
let vc = storyboard.instantiateViewController(ofType: ActionBarViewController.self)
vc.placePageData = placePageData
@ -82,13 +82,13 @@ class PlacePageCommonLayout: NSObject, IPlacePageLayout {
lazy var placePageNavigationViewController: PlacePageHeaderViewController = {
return PlacePageHeaderBuilder.build(data: placePageData.previewData, delegate: interactor, headerType: .fixed)
} ()
init(interactor: PlacePageInteractor, storyboard: UIStoryboard, data: PlacePageData) {
self.interactor = interactor
self.storyboard = storyboard
self.placePageData = data
}
private func configureViewControllers() -> [UIViewController] {
var viewControllers = [UIViewController]()
viewControllers.append(previewViewController)
@ -113,7 +113,7 @@ class PlacePageCommonLayout: NSObject, IPlacePageLayout {
if placePageData.buttonsData != nil {
viewControllers.append(buttonsViewController)
}
placePageData.onBookmarkStatusUpdate = { [weak self] in
guard let self = self else { return }
if self.placePageData.bookmarkData == nil {
@ -212,9 +212,8 @@ extension PlacePageCommonLayout: MWMLocationObserver {
let altString = "\(unitsFormatter.string(from: altMeasurement))"
if location.speed > 0 && location.timestamp.timeIntervalSinceNow >= -2 {
let speed = imperial ? location.speed * 2.237 : location.speed * 3.6
let speedMeasurement = Measurement(value: speed.rounded(), unit: imperial ? UnitSpeed.milesPerHour: UnitSpeed.kilometersPerHour)
let speedString = "\(LocationManager.speedSymbolFor(location.speed))\(unitsFormatter.string(from: speedMeasurement))"
let speedMeasure = Measure.init(asSpeed: location.speed)
let speedString = "\(LocationManager.speedSymbolFor(location.speed))\(speedMeasure.valueAsString) \(speedMeasure.unit)"
previewViewController.updateSpeedAndAltitude("\(altString) \(speedString)")
} else {
previewViewController.updateSpeedAndAltitude(altString)

View file

@ -53,11 +53,11 @@
<subviews>
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_carplay_camera" translatesAutoresizingMaskIntoConstraints="NO" id="Vip-QN-Smh">
<rect key="frame" x="0.0" y="0.0" width="30" height="30"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" white="1" alpha="0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="60" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ifo-0s-Ynd">
<rect key="frame" x="0.0" y="0.0" width="30" height="30"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" white="1" alpha="0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@ -107,8 +107,8 @@
<outlet property="currentSpeedView" destination="1hD-QK-pwq" id="Mbg-8c-EWp"/>
<outlet property="speedCamImageView" destination="Vip-QN-Smh" id="ASc-eP-fXa"/>
<outlet property="speedInfoView" destination="dAY-9Y-UMN" id="k1g-9g-oKS"/>
<outlet property="speedLimitContainer" destination="uhn-dc-aMW" id="AQt-AB-ZyL"/>
<outlet property="speedLimitLabel" destination="ifo-0s-Ynd" id="zgD-Di-6YZ"/>
<outlet property="speedCamLimitContainer" destination="uhn-dc-aMW" id="AQt-AB-ZyL"/>
<outlet property="speedCamLimitLabel" destination="ifo-0s-Ynd" id="zgD-Di-6YZ"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="6hr-Cq-l0z" userLabel="First Responder" sceneMemberID="firstResponder"/>

View file

@ -1475,7 +1475,7 @@ void RoutingManager::SetSubroutesVisibility(bool visible)
lock.Get()->SetSubrouteVisibility(subrouteId, visible);
}
bool RoutingManager::IsSpeedLimitExceeded() const
bool RoutingManager::IsSpeedCamLimitExceeded() const
{
return m_routingSession.IsSpeedLimitExceeded();
return m_routingSession.IsSpeedCamLimitExceeded();
}

View file

@ -255,7 +255,7 @@ public:
void OnLocationUpdate(location::GpsInfo const & info);
routing::SpeedCameraManager & GetSpeedCamManager() { return m_routingSession.GetSpeedCamManager(); }
bool IsSpeedLimitExceeded() const;
bool IsSpeedCamLimitExceeded() const;
void SetTurnNotificationsUnits(measurement_utils::Units const units)
{

View file

@ -16,23 +16,29 @@ enum class MeasurementType
Altitude
};
LocalizedUnits GetLocalizedUnits(measurement_utils::Units units, MeasurementType measurementType)
const LocalizedUnits & GetLocalizedUnits(measurement_utils::Units units, MeasurementType measurementType)
{
static LocalizedUnits UnitsLenghImperial = {GetLocalizedString("foot"), GetLocalizedString("mile")};
static LocalizedUnits UnitsLenghMetric = {GetLocalizedString("meter"), GetLocalizedString("kilometer")};
static LocalizedUnits UnitsSpeedImperial = {GetLocalizedString("foot"), GetLocalizedString("miles_per_hour")};
static LocalizedUnits UnitsSpeedMetric = {GetLocalizedString("meter"), GetLocalizedString("kilometers_per_hour")};
switch (measurementType)
{
case MeasurementType::Distance:
case MeasurementType::Altitude:
switch (units)
{
case measurement_utils::Units::Imperial: return {GetLocalizedString("foot"), GetLocalizedString("mile")};
case measurement_utils::Units::Metric: return {GetLocalizedString("meter"), GetLocalizedString("kilometer")};
case measurement_utils::Units::Imperial: return UnitsLenghImperial;
case measurement_utils::Units::Metric: return UnitsLenghMetric;
}
break;
case MeasurementType::Speed:
switch (units)
{
case measurement_utils::Units::Imperial: return {"", GetLocalizedString("miles_per_hour")};
case measurement_utils::Units::Metric: return {"", GetLocalizedString("kilometers_per_hour")};
case measurement_utils::Units::Imperial: return UnitsSpeedImperial;
case measurement_utils::Units::Metric: return UnitsSpeedMetric;
}
}
UNREACHABLE();
@ -55,11 +61,16 @@ LocalizedUnits GetLocalizedAltitudeUnits()
return GetLocalizedUnits(units, MeasurementType::Altitude);
}
const std::string & GetLocalizedSpeedUnits(measurement_utils::Units units)
{
return GetLocalizedUnits(units, MeasurementType::Speed).m_high;
}
std::string GetLocalizedSpeedUnits()
{
auto units = measurement_utils::Units::Metric;
settings::TryGet(settings::kMeasurementUnits, units);
return GetLocalizedUnits(units, MeasurementType::Speed).m_high;
return GetLocalizedSpeedUnits(units);
}
} // namespace platform

View file

@ -2,6 +2,11 @@
#include <string>
namespace measurement_utils
{
enum class Units;
}
namespace platform
{
struct LocalizedUnits
@ -18,5 +23,7 @@ extern std::string GetLocalizedMyPositionBookmarkName();
extern LocalizedUnits GetLocalizedDistanceUnits();
extern LocalizedUnits GetLocalizedAltitudeUnits();
extern const std::string & GetLocalizedSpeedUnits(measurement_utils::Units units);
extern std::string GetLocalizedSpeedUnits();
} // namespace platform

View file

@ -73,7 +73,7 @@ std::string DebugPrint(Units units)
UNREACHABLE();
}
double ToSpeedKmPH(double speed, measurement_utils::Units units)
double ToSpeedKmPH(double speed, Units units)
{
switch (units)
{
@ -224,14 +224,19 @@ string FormatSpeed(double metersPerSecond)
return FormatSpeedNumeric(metersPerSecond, units) + " " + FormatSpeedUnits(units);
}
string FormatSpeedNumeric(double metersPerSecond, Units units)
double MpsToUnits(double metersPerSecond, Units units)
{
double unitsPerHour;
switch (units)
{
case Units::Imperial: unitsPerHour = KmphToMiph(MpsToKmph(metersPerSecond)); break;
case Units::Metric: unitsPerHour = MpsToKmph(metersPerSecond); break;
case Units::Imperial: return KmphToMiph(MpsToKmph(metersPerSecond)); break;
case Units::Metric: return MpsToKmph(metersPerSecond); break;
}
UNREACHABLE();
}
string FormatSpeedNumeric(double metersPerSecond, Units units)
{
double const unitsPerHour = MpsToUnits(metersPerSecond, units);
return ToStringPrecision(unitsPerHour, unitsPerHour >= 10.0 ? 0 : 1);
}

View file

@ -31,7 +31,8 @@ inline double InchesToMeters(double in) { return in / 39.370; }
inline double NauticalMilesToMeters(double nmi) { return nmi * 1852; }
vng commented 2022-07-30 00:47:28 +00:00 (Migrated from github.com)
Review

Мы же тут уже внутри namespace measurement_utils

Мы же тут уже внутри namespace measurement_utils
AntonM030481 commented 2022-08-01 09:00:06 +00:00 (Migrated from github.com)
Review

OK

OK
inline double KmphToMps(double kmph) { return kmph * 1000 / 3600; }
double ToSpeedKmPH(double speed, measurement_utils::Units units);
double ToSpeedKmPH(double speed, Units units);
double MpsToUnits(double mps, Units units);
/// Takes into an account user settings [metric, imperial]
/// @param[in] m meters

View file

@ -486,7 +486,7 @@ void DrawWidget::SubmitFakeLocationPoint(m2::PointD const & pt)
else
{
LOG(LDEBUG, ("Distance:", loc.m_distToTarget + loc.m_targetUnitsSuffix, "Time:", loc.m_time,
loc.m_speedLimitUnitsSuffix.empty() ? "" : "SpeedLimit: " + loc.m_speedLimit + loc.m_speedLimitUnitsSuffix,
loc.m_speedLimitMps < 0 ? "" : "SpeedLimit: " + measurement_utils::FormatSpeed(loc.m_speedLimitMps),
GetTurnString(loc.m_turn), (loc.m_exitNum != 0 ? ":" + std::to_string(loc.m_exitNum) : ""),
"in", loc.m_distToTurn + loc.m_turnUnitsSuffix,
loc.m_targetName.empty() ? "" : "to " + loc.m_targetName ));

View file

@ -84,9 +84,8 @@ public:
turns::PedestrianDirection m_pedestrianTurn;
//@}
// Current speed limit.
// If no info about speed limit then m_speedLimit == "" and m_speedLimitUnitsSuffix == "".
std::string m_speedLimit;
std::string m_speedLimitUnitsSuffix;
// Current speed limit in meters per second.
// If no info about speed limit then m_speedLimitMps < 0.
double m_speedLimitMps = -1.0;
};
} // namespace routing

View file

@ -48,16 +48,6 @@ void FormatDistance(double dist, string & value, string & suffix)
value.erase(delim);
};
void FormatSpeed(double speedKmPH, string & value, string & suffix)
{
value = measurement_utils::FormatSpeed(measurement_utils::KmphToMps(speedKmPH));
size_t const delim = value.find(' ');
ASSERT(delim != string::npos, ());
suffix = value.substr(delim + 1);
value.erase(delim);
};
RoutingSession::RoutingSession()
: m_router(nullptr)
, m_route(make_shared<Route>(string() /* router */, 0 /* route id */))
@ -416,8 +406,12 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const
SpeedInUnits speedLimit;
m_route->GetCurrentSpeedLimit(speedLimit);
if (speedLimit.IsValid())
FormatSpeed(speedLimit.GetSpeedKmPH(), info.m_speedLimit, info.m_speedLimitUnitsSuffix);
if (speedLimit.IsNumeric())
info.m_speedLimitMps = measurement_utils::KmphToMps(speedLimit.GetSpeedKmPH());
else if (speedLimit.GetSpeed() == kNoneMaxSpeed)
info.m_speedLimitMps = 0;
else
info.m_speedLimitMps = -1.0;
// The turn after the next one.
if (m_routingSettings.m_showTurnAfterNext)

View file

@ -161,7 +161,7 @@ public:
void AssignRouteForTesting(std::shared_ptr<Route> route, RouterResultCode e) { AssignRoute(route, e); }
bool IsSpeedLimitExceeded() const { return m_speedCameraManager.IsSpeedLimitExceeded(); }
bool IsSpeedCamLimitExceeded() const { return m_speedCameraManager.IsSpeedLimitExceeded(); }
SpeedCameraManager & GetSpeedCamManager() { return m_speedCameraManager; }
SpeedCameraManager const & GetSpeedCamManager() const { return m_speedCameraManager; }