Skip to content

Instantly share code, notes, and snippets.

@danielpi
Last active January 12, 2016 13:19
Show Gist options
  • Save danielpi/ddce7c7693bcecbdb2e7 to your computer and use it in GitHub Desktop.
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…
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