299 lines
13 KiB
Objective-C
299 lines
13 KiB
Objective-C
// TTTLocationFormatter.m
|
|
//
|
|
// Copyright (c) 2011 Mattt Thompson (http://mattt.me)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
#import "TTTLocationFormatter.h"
|
|
|
|
static double const kTTTMetersToKilometersCoefficient = 0.001;
|
|
static double const kTTTMetersToFeetCoefficient = 3.2808399;
|
|
static double const kTTTMetersToYardsCoefficient = 1.0936133;
|
|
static double const kTTTMetersToMilesCoefficient = 0.000621371192;
|
|
|
|
static inline double CLLocationDistanceToKilometers(CLLocationDistance distance) {
|
|
return distance * kTTTMetersToKilometersCoefficient;
|
|
}
|
|
|
|
static inline double CLLocationDistanceToFeet(CLLocationDistance distance) {
|
|
return distance * kTTTMetersToFeetCoefficient;
|
|
}
|
|
|
|
static inline double CLLocationDistanceToYards(CLLocationDistance distance) {
|
|
return distance * kTTTMetersToYardsCoefficient;
|
|
}
|
|
|
|
static inline double CLLocationDistanceToMiles(CLLocationDistance distance) {
|
|
return distance * kTTTMetersToMilesCoefficient;
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
static inline double DEG2RAD(double degrees) {
|
|
return degrees * M_PI / 180;
|
|
};
|
|
|
|
static inline double RAD2DEG(double radians) {
|
|
return radians * 180 / M_PI;
|
|
};
|
|
|
|
static inline CLLocationDegrees CLLocationDegreesBearingBetweenCoordinates(CLLocationCoordinate2D originCoordinate, CLLocationCoordinate2D destinationCoordinate) {
|
|
double lat1 = DEG2RAD(originCoordinate.latitude);
|
|
double lon1 = DEG2RAD(originCoordinate.longitude);
|
|
double lat2 = DEG2RAD(destinationCoordinate.latitude);
|
|
double lon2 = DEG2RAD(destinationCoordinate.longitude);
|
|
|
|
double dLon = lon2 - lon1;
|
|
double y = sin(dLon) * cos(lat2);
|
|
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
|
|
double bearing = atan2(y, x) + (2 * M_PI);
|
|
|
|
// `atan2` works on a range of -π to 0 to π, so add on 2π and perform a modulo check
|
|
if (bearing > (2 * M_PI)) {
|
|
bearing = bearing - (2 * M_PI);
|
|
}
|
|
|
|
return RAD2DEG(bearing);
|
|
}
|
|
|
|
TTTLocationCardinalDirection TTTLocationCardinalDirectionFromBearing(CLLocationDegrees bearing) {
|
|
if(bearing > 337.5) {
|
|
return TTTNorthDirection;
|
|
} else if(bearing > 292.5) {
|
|
return TTTNorthwestDirection;
|
|
} else if(bearing > 247.5) {
|
|
return TTTWestDirection;
|
|
} else if(bearing > 202.5) {
|
|
return TTTSouthwestDirection;
|
|
} else if(bearing > 157.5) {
|
|
return TTTSouthDirection;
|
|
} else if(bearing > 112.5) {
|
|
return TTTSoutheastDirection;
|
|
} else if(bearing > 67.5) {
|
|
return TTTEastDirection;
|
|
} else if(bearing > 22.5) {
|
|
return TTTNortheastDirection;
|
|
} else {
|
|
return TTTNorthDirection;
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
static double const kTTTMetersPerSecondToKilometersPerHourCoefficient = 3.6;
|
|
static double const kTTTMetersPerSecondToFeetPerSecondCoefficient = 3.2808399;
|
|
static double const kTTTMetersPerSecondToMilesPerHourCoefficient = 2.23693629;
|
|
|
|
static inline double CLLocationSpeedToKilometersPerHour(CLLocationSpeed speed) {
|
|
return speed * kTTTMetersPerSecondToKilometersPerHourCoefficient;
|
|
}
|
|
|
|
static inline double CLLocationSpeedToFeetPerSecond(CLLocationSpeed speed) {
|
|
return speed * kTTTMetersPerSecondToFeetPerSecondCoefficient;
|
|
}
|
|
|
|
static inline double CLLocationSpeedToMilesPerHour(CLLocationSpeed speed) {
|
|
return speed * kTTTMetersPerSecondToMilesPerHourCoefficient;
|
|
}
|
|
|
|
|
|
@interface TTTLocationFormatter ()
|
|
@property (readwrite, nonatomic, assign) TTTLocationFormatterCoordinateOrder coordinateOrder;
|
|
@property (readwrite, nonatomic, assign) TTTLocationFormatterBearingStyle bearingStyle;
|
|
@property (readwrite, nonatomic, assign) TTTLocationUnitSystem unitSystem;
|
|
@property (readwrite, nonatomic, retain) NSNumberFormatter *numberFormatter;
|
|
@end
|
|
|
|
@implementation TTTLocationFormatter
|
|
@synthesize coordinateOrder = _coordinateOrder;
|
|
@synthesize bearingStyle = _bearingStyle;
|
|
@synthesize unitSystem = _unitSystem;
|
|
@synthesize numberFormatter = _numberFormatter;
|
|
|
|
- (id)init {
|
|
self = [super init];
|
|
if (!self) {
|
|
return nil;
|
|
}
|
|
|
|
self.coordinateOrder = TTTCoordinateLatLngOrder;
|
|
self.bearingStyle = TTTBearingWordStyle;
|
|
self.unitSystem = TTTMetricSystem;
|
|
|
|
self.numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
|
|
[self.numberFormatter setLocale:[NSLocale currentLocale]];
|
|
[self.numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
|
|
[self.numberFormatter setMaximumSignificantDigits:2];
|
|
[self.numberFormatter setUsesSignificantDigits:YES];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (NSString *)stringFromCoordinate:(CLLocationCoordinate2D)coordinate {
|
|
return [NSString stringWithFormat:NSLocalizedString(@"(%@, %@)", @"Coordinate format"), [self.numberFormatter stringFromNumber:[NSNumber numberWithDouble:coordinate.latitude]], [self.numberFormatter stringFromNumber:[NSNumber numberWithDouble:coordinate.longitude]], nil];
|
|
}
|
|
|
|
- (NSString *)stringFromLocation:(CLLocation *)location {
|
|
return [self stringFromCoordinate:location.coordinate];
|
|
}
|
|
|
|
- (NSString *)stringFromDistance:(CLLocationDistance)distance {
|
|
NSString *distanceString = nil;
|
|
NSString *unitString = nil;
|
|
|
|
switch (self.unitSystem) {
|
|
case TTTMetricSystem: {
|
|
double kilometerDistance = CLLocationDistanceToKilometers(distance);
|
|
if (kilometerDistance > 1) {
|
|
distanceString = [self.numberFormatter stringFromNumber:[NSNumber numberWithDouble:kilometerDistance]];
|
|
unitString = NSLocalizedString(@"km", @"Kilometer Unit");
|
|
} else {
|
|
double meterDistance = distance;
|
|
distanceString = [self.numberFormatter stringFromNumber:[NSNumber numberWithDouble:meterDistance]];
|
|
unitString = NSLocalizedString(@"m", @"Meter Unit");
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TTTImperialSystem: {
|
|
double feetDistance = CLLocationDistanceToFeet(distance);
|
|
if (feetDistance < 300) {
|
|
distanceString = [self.numberFormatter stringFromNumber:[NSNumber numberWithDouble:feetDistance]];
|
|
unitString = NSLocalizedString(@"ft", @"Feet Unit");
|
|
} else {
|
|
double yardDistance = CLLocationDistanceToYards(distance);
|
|
if (yardDistance < 500) {
|
|
distanceString = [self.numberFormatter stringFromNumber:[NSNumber numberWithDouble:yardDistance]];
|
|
unitString = NSLocalizedString(@"yds", @"Yard Unit");
|
|
} else {
|
|
double milesDistance = CLLocationDistanceToMiles(distance);
|
|
distanceString = [self.numberFormatter stringFromNumber:[NSNumber numberWithDouble:milesDistance]];
|
|
unitString = (milesDistance > 1.0 && milesDistance < 1.1) ? NSLocalizedString(@"mile", @"Mile Unit (Singular)") : NSLocalizedString(@"miles", @"Mile Unit (Plural)");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return [NSString stringWithFormat:NSLocalizedString(@"%@ %@", @"#{Distance} #{Unit}"), distanceString, unitString];
|
|
}
|
|
|
|
- (NSString *)stringFromBearing:(CLLocationDegrees)bearing {
|
|
switch (self.bearingStyle) {
|
|
case TTTBearingWordStyle:
|
|
switch (TTTLocationCardinalDirectionFromBearing(bearing)) {
|
|
case TTTNorthDirection:
|
|
return NSLocalizedString(@"North", @"North Direction");
|
|
case TTTNortheastDirection:
|
|
return NSLocalizedString(@"Northeast", @"Northeast Direction");
|
|
case TTTEastDirection:
|
|
return NSLocalizedString(@"East", @"East Direction");
|
|
case TTTSoutheastDirection:
|
|
return NSLocalizedString(@"Southeast", @"Southeast Direction");
|
|
case TTTSouthDirection:
|
|
return NSLocalizedString(@"South", @"South Direction");
|
|
case TTTSouthwestDirection:
|
|
return NSLocalizedString(@"Southwest", @"Southwest Direction");
|
|
case TTTWestDirection:
|
|
return NSLocalizedString(@"West", @"West Direction");
|
|
case TTTNorthwestDirection:
|
|
return NSLocalizedString(@"Northwest", @"Northwest Direction");
|
|
}
|
|
break;
|
|
case TTTBearingAbbreviationWordStyle:
|
|
switch (TTTLocationCardinalDirectionFromBearing(bearing)) {
|
|
case TTTNorthDirection:
|
|
return NSLocalizedString(@"N", @"North Direction Abbreviation");
|
|
case TTTNortheastDirection:
|
|
return NSLocalizedString(@"NE", @"Northeast Direction Abbreviation");
|
|
case TTTEastDirection:
|
|
return NSLocalizedString(@"E", @"East Direction Abbreviation");
|
|
case TTTSoutheastDirection:
|
|
return NSLocalizedString(@"SE", @"Southeast Direction Abbreviation");
|
|
case TTTSouthDirection:
|
|
return NSLocalizedString(@"S", @"South Direction Abbreviation");
|
|
case TTTSouthwestDirection:
|
|
return NSLocalizedString(@"SW", @"Southwest Direction Abbreviation");
|
|
case TTTWestDirection:
|
|
return NSLocalizedString(@"W", @"West Direction Abbreviation");
|
|
case TTTNorthwestDirection:
|
|
return NSLocalizedString(@"NW", @"Northwest Direction Abbreviation");;
|
|
}
|
|
break;
|
|
case TTTBearingNumericStyle:
|
|
return [[self.numberFormatter stringFromNumber:[NSNumber numberWithDouble:bearing]] stringByAppendingString:NSLocalizedString(@"°", @"Degrees Symbol")];
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (NSString *)stringFromSpeed:(CLLocationSpeed)speed {
|
|
NSString *speedString = nil;
|
|
NSString *unitString = nil;
|
|
|
|
switch (self.unitSystem) {
|
|
case TTTMetricSystem: {
|
|
double metersPerSecondSpeed = speed;
|
|
double kilometersPerHourSpeed = CLLocationSpeedToKilometersPerHour(speed);
|
|
|
|
if (kilometersPerHourSpeed > 1) {
|
|
speedString = [self.numberFormatter stringFromNumber:[NSNumber numberWithDouble:kilometersPerHourSpeed]];
|
|
unitString = NSLocalizedString(@"km/h", @"Kilometers Per Hour Unit");
|
|
} else {
|
|
speedString = [self.numberFormatter stringFromNumber:[NSNumber numberWithDouble:metersPerSecondSpeed]];
|
|
unitString = NSLocalizedString(@"m/s", @"Meters Per Second Unit");
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TTTImperialSystem: {
|
|
double feetPerSecondSpeed = CLLocationSpeedToFeetPerSecond(speed);
|
|
double milesPerHourSpeed = CLLocationSpeedToMilesPerHour(speed);
|
|
|
|
if (milesPerHourSpeed > 1) {
|
|
speedString = [self.numberFormatter stringFromNumber:[NSNumber numberWithDouble:milesPerHourSpeed]];
|
|
unitString = NSLocalizedString(@"mph", @"Miles Per Hour Unit");
|
|
} else {
|
|
speedString = [self.numberFormatter stringFromNumber:[NSNumber numberWithDouble:feetPerSecondSpeed]];
|
|
unitString = NSLocalizedString(@"ft/s", @"Feet Per Second Unit");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return [NSString stringWithFormat:NSLocalizedString(@"%@ %@", @"#{Speed} #{Unit}"), speedString, unitString];
|
|
}
|
|
|
|
- (NSString *)stringFromDistanceFromLocation:(CLLocation *)originLocation toLocation:(CLLocation *)destinationLocation {
|
|
return [self stringFromDistance:[destinationLocation distanceFromLocation:originLocation]];
|
|
}
|
|
|
|
- (NSString *)stringFromBearingFromLocation:(CLLocation *)originLocation toLocation:(CLLocation *)destinationLocation {
|
|
return [self stringFromBearing:CLLocationDegreesBearingBetweenCoordinates(originLocation.coordinate, destinationLocation.coordinate)];
|
|
}
|
|
|
|
- (NSString *)stringFromDistanceAndBearingFromLocation:(CLLocation *)originLocation toLocation:(CLLocation *)destinationLocation {
|
|
return [NSString stringWithFormat:NSLocalizedString(@"%@ %@", @"#{Dimensional Quantity} #{Direction}"), [self stringFromDistanceFromLocation:originLocation toLocation:destinationLocation], [self stringFromBearingFromLocation:originLocation toLocation:destinationLocation]];
|
|
}
|
|
|
|
- (NSString *)stringFromVelocityFromLocation:(CLLocation *)originLocation toLocation:(CLLocation *)destinationLocation atSpeed:(CLLocationSpeed)speed {
|
|
return [NSString stringWithFormat:NSLocalizedString(@"%@ %@", @"#{Dimensional Quantity} #{Direction}"), [self stringFromSpeed:speed], [self stringFromBearingFromLocation:originLocation toLocation:destinationLocation]];
|
|
}
|
|
|
|
@end
|