Last active
January 12, 2016 13:19
-
-
Save danielpi/ddce7c7693bcecbdb2e7 to your computer and use it in GitHub Desktop.
My response to http://ericasadun.com/2016/01/11/make-this-swift-er-coordinate-distances/ Note I'm really just experimenting with a concept of encoding a calculations units into Swifts type system rather than actually suggesting that this is the best solution to the original problem. However this does have the advantage of taking care of conversi…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Cocoa | |
public protocol Unit: CustomStringConvertible { | |
var value: Double { get set } | |
var baseSymbol: String { get } | |
init(value: Double) | |
} | |
public extension Unit { | |
public init(value: Double, prefix: Prefix) { | |
self.init(value: value) | |
self.value = value * prefix.rawValue | |
} | |
public var description: String { | |
return "\(String(format: "%.1f", value))\(baseSymbol)" | |
} | |
public func value(prefix: Prefix) -> Double { | |
return value / prefix.rawValue | |
} | |
public func string(prefix: Prefix) -> String { | |
return "\(String(format: "%.1f", self.value(prefix)))\(prefix)\(baseSymbol)" | |
} | |
} | |
public extension Unit { | |
static func convertToBase(prefix: Prefix, power: Double, value: Double) -> Double { | |
return pow(prefix.rawValue, power) * value | |
} | |
static func convertToPrefix(prefix: Prefix, power: Double, value: Double) -> Double { | |
return value / pow(prefix.rawValue, power) | |
} | |
} | |
// MARK: Generic Functions | |
func * <U: Unit>(lhs: Double, rhs: U) -> U { | |
return U(value: lhs * rhs.value) | |
} | |
func * <U: Unit>(lhs: U, rhs: Double) -> U { | |
return U(value: lhs.value * rhs) | |
} | |
func / <U: Unit>(lhs: U, rhs: Double) -> U { | |
return U(value: lhs.value / rhs) | |
} | |
func + <U: Unit>(lhs: U, rhs: U) -> U { | |
return U(value: lhs.value + rhs.value) | |
} | |
func - <U: Unit>(lhs: U, rhs: U) -> U { | |
return U(value: lhs.value - rhs.value) | |
} | |
// MARK: Prefix System | |
// Multiplier and Fractions | |
public enum Prefix: Double { | |
case yocto = 1e-24 | |
case zepto = 1e-21 | |
case atto = 1e-18 | |
case femto = 1e-15 | |
case pico = 1e-12 | |
case nano = 1e-9 | |
case micro = 1e-6 | |
case milli = 1e-3 | |
case centi = 1e-2 | |
case deci = 1e-1 | |
case unity = 1 | |
case deca = 1e1 | |
case hecto = 1e2 | |
case kilo = 1e3 | |
case mega = 1e6 | |
case giga = 1e9 | |
case tera = 1e12 | |
case peta = 1e15 | |
case exa = 1e18 | |
case zetta = 1e21 | |
case yotta = 1e24 | |
} | |
extension Prefix: CustomStringConvertible { | |
public var description: String { | |
switch self { | |
case .yocto: return "y" | |
case .zepto: return "z" | |
case .atto: return "a" | |
case .femto: return "f" | |
case .pico: return "p" | |
case .nano: return "n" | |
case .micro: return "u" | |
case .milli: return "m" | |
case .centi: return "c" | |
case .deci: return "d" | |
case .unity: return "" | |
case .deca: return "da" | |
case .hecto: return "h" | |
case .kilo: return "k" | |
case .mega: return "M" | |
case .giga: return "G" | |
case .tera: return "T" | |
case .peta: return "P" | |
case .exa: return "E" | |
case .zetta: return "Z" | |
case .yotta: return "Y" | |
} | |
} | |
} | |
public struct Length: Unit { | |
public var value: Double | |
public let baseSymbol = "m" | |
public var m: Double { | |
return value | |
} | |
public var mm: Double { | |
return 1000 * value | |
} | |
public var cm: Double { | |
return 100 * value | |
} | |
public var km: Double { | |
return value / 1000 | |
} | |
public var mi: Double { | |
return value / 1609.34 | |
} | |
public init(value: Double) { | |
self.value = value | |
} | |
public init(m: Double) { | |
self.value = m | |
} | |
public init(mm: Double) { | |
self.value = mm / 1000 | |
} | |
public init(cm: Double) { | |
self.value = cm / 100 | |
} | |
public init(km: Double) { | |
self.value = 1000 * km | |
} | |
public init(mi: Double) { | |
self.value = 1609.34 * mi | |
} | |
} | |
public struct Latitude: Unit { | |
public var value: Double | |
public var baseSymbol: String = "φ" | |
public var radians: Double { | |
return value | |
} | |
public var degrees: Double { | |
return 180 * value / M_PI | |
} | |
public init(value: Double) { | |
self.value = value | |
} | |
public init(radians: Double) { | |
self.value = radians | |
} | |
public init(degrees: Double) { | |
self.value = M_PI * degrees / 180 | |
} | |
public var description: String { | |
return "\(String(format: "%.6f", self.degrees))\(baseSymbol)" | |
} | |
} | |
public struct Longitude: Unit { | |
public var value: Double | |
public var baseSymbol: String = "λ" | |
public var radians: Double { | |
return value | |
} | |
public var degrees: Double { | |
return 180 * value / M_PI | |
} | |
public init(value: Double) { | |
self.value = value | |
} | |
public init(radians: Double) { | |
self.value = radians | |
} | |
public init(degrees: Double) { | |
self.value = M_PI * degrees / 180 | |
} | |
public var description: String { | |
return "\(String(format: "%.6f", self.degrees))\(baseSymbol)" | |
} | |
} | |
public struct Coordinate: CustomStringConvertible { | |
public let latitude: Latitude | |
public let longitude: Longitude | |
public let radiusOfEarth = Length(km: 6373) | |
public init(latitude: Latitude, longitude: Longitude) { | |
self.latitude = latitude | |
self.longitude = longitude | |
} | |
public func distanceFrom(coordinate: Coordinate) -> Length { | |
let(dLat, dLon) = (latitude - coordinate.latitude, longitude - coordinate.longitude) | |
let (sqSinLat, sqSinLon) = (pow(sin(dLat.radians / 2.0), 2.0), pow(sin(dLon.radians / 2.0), 2.0)) | |
let a = sqSinLat + sqSinLon * cos(latitude.radians) * cos(coordinate.latitude.radians) | |
let c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a)) | |
return c * radiusOfEarth | |
} | |
public var description: String { | |
return "\(latitude) \(longitude)" | |
} | |
} | |
let whitehouse = Coordinate(latitude: Latitude(degrees: 38.898556), longitude: Longitude(degrees: -77.037852)) | |
let fstreet = Coordinate(latitude: Latitude(degrees: 38.897147), longitude: Longitude(degrees: -77.043934)) | |
let distance = whitehouse.distanceFrom(fstreet) | |
distance.km | |
distance.mi | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment