Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.

Distance formatter fixes #3331 #7585

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions platform/darwin/resources/Base.lproj/Foundation.strings
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,33 @@
/* West longitude format, short: {longitude} */
"COORD_W_SHORT" = "%@W";

/* Distance format, long: {1 foot} */
"DISTANCE_FOOT_LONG" = "%@ foot/feet";

/* Distance format, short: {1 ft} */
"DISTANCE_FOOT_SHORT" = "%@ ft";

/* Distance format, long: {1 kilometer} */
"DISTANCE_KILOMETER_LONG" = "%@ kilometer(s)";

/* Distance format, short: {1 km} */
"DISTANCE_KILOMETER_SHORT" = "%@ km";

/* Distance format, long: {1 meter} */
"DISTANCE_METER_LONG" = "%@ meter(s)";

/* Distance format, short: {1 m} */
"DISTANCE_METER_SHORT" = "%@ m";

/* Distance format, long: {1 mile} */
"DISTANCE_MILE_LONG" = "%@ mile(s)";

/* Distance format, short: {1 mi} */
"DISTANCE_MILE_SHORT" = "%@ mi";

/* Distance format, long: {1 yard} */
"DISTANCE_YARD_LONG" = "%@ yard(s)";

/* Distance format, short: {1 yd} */
"DISTANCE_YARD_SHORT" = "%@ yd";

80 changes: 80 additions & 0 deletions platform/darwin/resources/en.lproj/Foundation.stringsdict
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,85 @@
<string>%d seconds</string>
</dict>
</dict>
<key>DISTANCE_METER_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@meter@</string>
<key>meter</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>@</string>
<key>one</key>
<string>%@ meter</string>
<key>other</key>
<string>%@ meters</string>
</dict>
</dict>
<key>DISTANCE_KILOMETER_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@kilometer@</string>
<key>kilometer</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>@</string>
<key>one</key>
<string>%@ kilometer</string>
<key>other</key>
<string>%@ kilometers</string>
</dict>
</dict>
<key>DISTANCE_FOOT_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@foot@</string>
<key>foot</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>@</string>
<key>one</key>
<string>%@ foot</string>
<key>other</key>
<string>%@ feet</string>
</dict>
</dict>
<key>DISTANCE_MILE_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@mile@</string>
<key>mile</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>@</string>
<key>one</key>
<string>%@ mile</string>
<key>other</key>
<string>%@ miles</string>
</dict>
</dict>
<key>DISTANCE_YARD_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@yard@</string>
<key>yard</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>@</string>
<key>one</key>
<string>%@ yard</string>
<key>other</key>
<string>%@ yards</string>
</dict>
</dict>
</dict>
</plist>
65 changes: 65 additions & 0 deletions platform/darwin/src/MGLDistanceFormatter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>

NS_ASSUME_NONNULL_BEGIN

/**
`MGLDistanceFormatter` implements a formatter object meant to be used for
geographic distances. By default, the resulting string will be based on the user’s
current locale but can be overriden in order to force a measurement system of your choice.
*/
@interface MGLDistanceFormatter : NSFormatter

/**
Determines what system of measurement to use.
*/
typedef NS_ENUM(NSUInteger, MGLDistanceFormatterUnits) {
// Measurement system will be based on the current locale.
MGLDistanceFormatterUnitsDefault,
// Use the metric system.
MGLDistanceFormatterUnitsMetric,
// Imperial units using feets.
MGLDistanceFormatterUnitsImperial,
// Imperial units using yards.
MGLDistanceFormatterUnitsImperialWithYards
};

/**
Determines if the localized unit string should be abbreviated or spelled out.
*/
typedef NS_ENUM(NSUInteger, MGLDistanceFormatterUnitStyle) {
MGLDistanceFormatterUnitStyleDefault,
// Abbreviate units (i.e. 1 km).
MGLDistanceFormatterUnitStyleAbbreviated,
// Full style (i.e. 2 kilometers).
MGLDistanceFormatterUnitStyleFull
};

/**
Determines what system of measurement to use.

Based on the user’s locale by default.
*/
@property (nonatomic, assign) MGLDistanceFormatterUnits units;

/**
Determines how to output the localized unit string.
*/
@property (nonatomic, assign) MGLDistanceFormatterUnitStyle unitStyle;

/**
Returns a localized formatted string for the provided distance.

@param distance The distance, measured in meters. Negative distance will return nil.
@return A localized formatted distance string including units.
*/
- (NSString *)stringFromDistance:(CLLocationDistance)distance;

/**
This method is not supported for the `MGLDistanceFormatter` class.
*/
- (BOOL)getObjectValue:(out id __nullable * __nullable)obj forString:(NSString *)string errorDescription:(out NSString * __nullable * __nullable)error;

@end

NS_ASSUME_NONNULL_END
139 changes: 139 additions & 0 deletions platform/darwin/src/MGLDistanceFormatter.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#import "MGLDistanceFormatter.h"

#import "NSBundle+MGLAdditions.h"


@interface MGLDistanceFormatter()
@property (nonatomic) NSNumberFormatter *numberFormatter;
@end

@implementation MGLDistanceFormatter

static const CLLocationDistance METERS_PER_KM = 1000;
static const CLLocationDistance METERS_PER_MILE = 1609.34;
static const CLLocationDistance METERS_PER_YARD = 0.9144;
static const CLLocationDistance METERS_PER_FOOT = 0.3048;

typedef NS_ENUM(NSUInteger, MGLDistanceFormatterUnit) {
MGLDistanceFormatterUnitMeter,
MGLDistanceFormatterUnitKilometer,
MGLDistanceFormatterUnitFoot,
MGLDistanceFormatterUnitMile,
MGLDistanceFormatterUnitYard
};

- (instancetype)init {
if (self = [super init]) {
_unitStyle = MGLDistanceFormatterUnitStyleDefault;

_numberFormatter = [[NSNumberFormatter alloc] init];
_numberFormatter.minimumFractionDigits = 0;
_numberFormatter.roundingMode = NSNumberFormatterRoundHalfUp;
}
return self;
}

- (NSString *)stringFromDistance:(CLLocationDistance)distance {
if (distance < 0)
return nil;

NSString *format = [self formatForDistance:distance];
NSNumber *convertedDistance = @([self convertedDistance:distance]);

MGLDistanceFormatterUnit unit = [self unitForDistance:distance];

switch (self.preferredUnits) {
case MGLDistanceFormatterUnitsDefault:
case MGLDistanceFormatterUnitsMetric:
_numberFormatter.maximumFractionDigits = unit == MGLDistanceFormatterUnitMeter ? 0 : 1;
return [NSString stringWithFormat:format, [self.numberFormatter numberFromString:[self.numberFormatter stringFromNumber:convertedDistance]]];
case MGLDistanceFormatterUnitsImperial:
_numberFormatter.maximumFractionDigits = unit == MGLDistanceFormatterUnitFoot ? 0 : 1;
return [NSString stringWithFormat:format, [self.numberFormatter numberFromString:[self.numberFormatter stringFromNumber:convertedDistance]]];
case MGLDistanceFormatterUnitsImperialWithYards:
_numberFormatter.maximumFractionDigits = unit == MGLDistanceFormatterUnitYard ? 0 : 1;
return [NSString stringWithFormat:format, [self.numberFormatter numberFromString:[self.numberFormatter stringFromNumber:convertedDistance]]];
}
}

- (CLLocationDistance)convertedDistance:(CLLocationDistance)distance {
MGLDistanceFormatterUnit unit = [self unitForDistance:distance];
switch (unit) {
case MGLDistanceFormatterUnitMeter:
return distance;
case MGLDistanceFormatterUnitKilometer:
return distance / METERS_PER_KM;
case MGLDistanceFormatterUnitFoot:
return distance / METERS_PER_FOOT;
case MGLDistanceFormatterUnitMile:
return distance / METERS_PER_MILE;
case MGLDistanceFormatterUnitYard:
return distance / METERS_PER_YARD;
}
}

- (NSString *)formatForDistance:(CLLocationDistance)distance {
MGLDistanceFormatterUnit unit = [self unitForDistance:distance];

switch (self.unitStyle) {
case MGLDistanceFormatterUnitStyleDefault:
case MGLDistanceFormatterUnitStyleAbbreviated:
switch (unit) {
case MGLDistanceFormatterUnitMeter:
return NSLocalizedStringWithDefaultValue(@"DISTANCE_METER_SHORT", @"Foundation", nil, @"%@ m", @"Distance format, short: {1 m}");
case MGLDistanceFormatterUnitKilometer:
return NSLocalizedStringWithDefaultValue(@"DISTANCE_KILOMETER_SHORT", @"Foundation", nil, @"%@ km", @"Distance format, short: {1 km}");
case MGLDistanceFormatterUnitFoot:
return NSLocalizedStringWithDefaultValue(@"DISTANCE_FOOT_SHORT", @"Foundation", nil, @"%@ ft", @"Distance format, short: {1 ft}");
case MGLDistanceFormatterUnitMile:
return NSLocalizedStringWithDefaultValue(@"DISTANCE_MILE_SHORT", @"Foundation", nil, @"%@ mi", @"Distance format, short: {1 mi}");
case MGLDistanceFormatterUnitYard:
return NSLocalizedStringWithDefaultValue(@"DISTANCE_YARD_SHORT", @"Foundation", nil, @"%@ yd", @"Distance format, short: {1 yd}");
}
case MGLDistanceFormatterUnitStyleFull:
switch (unit) {
case MGLDistanceFormatterUnitMeter:
return NSLocalizedStringWithDefaultValue(@"DISTANCE_METER_LONG", @"Foundation", nil, @"%@ meter(s)", @"Distance format, long: {1 meter}");
case MGLDistanceFormatterUnitKilometer:
return NSLocalizedStringWithDefaultValue(@"DISTANCE_KILOMETER_LONG", @"Foundation", nil, @"%@ kilometer(s)", @"Distance format, long: {1 kilometer}");
case MGLDistanceFormatterUnitFoot:
return NSLocalizedStringWithDefaultValue(@"DISTANCE_FOOT_LONG", @"Foundation", nil, @"%@ foot/feet", @"Distance format, long: {1 foot}");
case MGLDistanceFormatterUnitMile:
return NSLocalizedStringWithDefaultValue(@"DISTANCE_MILE_LONG", @"Foundation", nil, @"%@ mile(s)", @"Distance format, long: {1 mile}");
case MGLDistanceFormatterUnitYard:
return NSLocalizedStringWithDefaultValue(@"DISTANCE_YARD_LONG", @"Foundation", nil, @"%@ yard(s)", @"Distance format, long: {1 yard}");
}
}
}

- (MGLDistanceFormatterUnit)unitForDistance:(CLLocationDistance)distance {
switch (self.preferredUnits) {
// MGLDistanceFormatterUnitsDefault is only added to make the switch statement
// exhaustive, it is never returned from -[MGLDistanceFormatter preferredUnits]
case MGLDistanceFormatterUnitsDefault:
case MGLDistanceFormatterUnitsMetric:
return distance < METERS_PER_KM ? MGLDistanceFormatterUnitMeter : MGLDistanceFormatterUnitKilometer;
case MGLDistanceFormatterUnitsImperial: {
double ft = distance / METERS_PER_FOOT;
return ft < 1000 ? MGLDistanceFormatterUnitFoot : MGLDistanceFormatterUnitMile;
}
case MGLDistanceFormatterUnitsImperialWithYards: {
double yd = distance / METERS_PER_YARD;
return yd < 1000 ? MGLDistanceFormatterUnitYard : MGLDistanceFormatterUnitMile;
}
}
}

- (MGLDistanceFormatterUnits)preferredUnits {
if (self.units == MGLDistanceFormatterUnitsDefault) {
BOOL usesMetric = [[[NSLocale currentLocale] objectForKey:NSLocaleUsesMetricSystem] boolValue];
return usesMetric ? MGLDistanceFormatterUnitsMetric : MGLDistanceFormatterUnitsImperial;
}
return self.units;
}

- (BOOL)getObjectValue:(out id __nullable * __nullable)obj forString:(NSString *)string errorDescription:(out NSString * __nullable * __nullable)error {
return NO;
}

@end
Loading