Added documentation

This commit is contained in:
Peter Ringset 2017-03-20 15:31:13 +01:00
parent aa76ffd639
commit 0be243792a
4 changed files with 100 additions and 14 deletions

View File

@ -29,6 +29,12 @@ import Foundation
public extension CLLocation { public extension CLLocation {
/**
Calculates the UTM coordinate of the receiver
- Parameter datum: The datum to use, defaults to WGS84 which should be fine for most applications
*/
public func utmCoordinate(datum: UTMDatum = UTMDatum.wgs84) -> UTMCoordinate { public func utmCoordinate(datum: UTMDatum = UTMDatum.wgs84) -> UTMCoordinate {
let coordinate = self.coordinate let coordinate = self.coordinate
let zone = coordinate.zone let zone = coordinate.zone

View File

@ -30,15 +30,29 @@ import CoreLocation
public extension CLLocationCoordinate2D { public extension CLLocationCoordinate2D {
/**
Calculates the UTM coordinate of the receiver
- Parameter datum: The datum to use, defaults to WGS84 which should be fine for most applications
*/
public func utmCoordinate(datum: UTMDatum = UTMDatum.wgs84) -> UTMCoordinate { public func utmCoordinate(datum: UTMDatum = UTMDatum.wgs84) -> UTMCoordinate {
let zone = self.zone let zone = self.zone
return TMCoordinate(coordinate: self, centralMeridian: zone.centralMeridian, datum: datum).utmCoordinate(zone: zone, hemisphere: hemisphere) return TMCoordinate(coordinate: self, centralMeridian: zone.centralMeridian, datum: datum).utmCoordinate(zone: zone, hemisphere: hemisphere)
} }
/**
The UTM grid zone
*/
var zone: UTMGridZone { var zone: UTMGridZone {
return UTMGridZone(floor((longitude + 180.0) / 6)) + 1; return UTMGridZone(floor((longitude + 180.0) / 6)) + 1;
} }
/**
The UTM hemisphere
*/
var hemisphere: UTMHemisphere { var hemisphere: UTMHemisphere {
return latitude < 0 ? .southern : .northern return latitude < 0 ? .southern : .northern
} }

View File

@ -37,6 +37,9 @@ func toRadians(degrees: Double) -> Double {
return degrees / 180 * M_PI return degrees / 180 * M_PI
} }
/**
Internal struct used to represent Transverse Mercator coordinates. This struct is used as an intermediate representation of the location, in order to convert between Universal Transverse Mercator (UTM) coordinates, and latitude and longitude.
*/
struct TMCoordinate { struct TMCoordinate {
let northing: Double let northing: Double
let easting: Double let easting: Double
@ -52,7 +55,14 @@ struct TMCoordinate {
}() }()
} }
init(coordinate: CLLocationCoordinate2D, centralMeridian lambda0: Double, datum: UTMDatum) { /**
Init with a CLLocationCoordinate
- Parameter coordinate: The coordinate to init with
- Parameter centralMeridian: The central meridian of the earth
- Parameter datum: The datum to use
*/
init(coordinate: CLLocationCoordinate2D, centralMeridian: Double, datum: UTMDatum) {
let phi = toRadians(degrees: coordinate.latitude) // Latitude in radians let phi = toRadians(degrees: coordinate.latitude) // Latitude in radians
let lambda = toRadians(degrees: coordinate.longitude) // Longitude in radians let lambda = toRadians(degrees: coordinate.longitude) // Longitude in radians
@ -73,7 +83,7 @@ struct TMCoordinate {
let t2 = t * t let t2 = t * t
/* Precalculate l */ /* Precalculate l */
let l = lambda - lambda0 let l = lambda - centralMeridian
/* Precalculate coefficients for l**n in the equations below /* Precalculate coefficients for l**n in the equations below
so a normal human being can read the expressions for easting so a normal human being can read the expressions for easting
@ -122,6 +132,13 @@ struct TMCoordinate {
northing = arcLengthOfMeridian(phi, datum) + (t / 2.0 * N * pow(cos(phi), 2.0) * pow(l, 2.0)) + (t / 24.0 * N * pow(cos(phi), 4.0) * l4coef * pow(l, 4.0)) + (t / 720.0 * N * pow(cos(phi), 6.0) * l6coef * pow(l, 6.0)) + (t / 40320.0 * N * pow(cos(phi), 8.0) * l8coef * pow(l, 8.0)) northing = arcLengthOfMeridian(phi, datum) + (t / 2.0 * N * pow(cos(phi), 2.0) * pow(l, 2.0)) + (t / 24.0 * N * pow(cos(phi), 4.0) * l4coef * pow(l, 4.0)) + (t / 720.0 * N * pow(cos(phi), 6.0) * l6coef * pow(l, 6.0)) + (t / 40320.0 * N * pow(cos(phi), 8.0) * l8coef * pow(l, 8.0))
} }
/**
Create an UTMCoordinate from the receiver
- Parameter zone: The UTMGridZone to use
- Parameter hemisphere: Choose if the coordinate should be relative to the northern of southern hemisphere
*/
func utmCoordinate(zone: UTMGridZone, hemisphere: UTMHemisphere) -> UTMCoordinate { func utmCoordinate(zone: UTMGridZone, hemisphere: UTMHemisphere) -> UTMCoordinate {
let x = easting * utmScaleFactor + 500000.0; let x = easting * utmScaleFactor + 500000.0;
let y: Double = { let y: Double = {
@ -135,12 +152,16 @@ struct TMCoordinate {
return UTMCoordinate(northing: y, easting: x, zone: zone, hemisphere: hemisphere) return UTMCoordinate(northing: y, easting: x, zone: zone, hemisphere: hemisphere)
} }
// /**
// Converts x and y coordinates in the Transverse Mercator projection to a latitude/longitude pair. Note that Transverse Mercator is not the same as UTM a scale factor is required to convert between them. Converts easting and northing coordinates in the Transverse Mercator projection to a latitude/longitude pair. Note that Transverse Mercator is not the same as UTM a scale factor is required to convert between them.
// Remarks:
// The local variables Nf, nuf2, tf, and tf2 serve the same purpose as N, nu2, t, and t2 in MapLatLonToXY, but they are computed with respect to the footpoint latitude phif. - Parameter centralMeridian: The central meridian of the earth
// x1frac, x2frac, x2poly, x3poly, etc. are to enhance readability and to optimize computations. - Parameter datum: The datum to use
func coordinate(centralMeridian lambda0: Double, datum: UTMDatum) -> CLLocationCoordinate2D {
*/
func coordinate(centralMeridian: Double, datum: UTMDatum) -> CLLocationCoordinate2D {
/* The local variables Nf, nuf2, tf, and tf2 serve the same purpose as N, nu2, t, and t2 in MapLatLonToXY, but they are computed with respect to the footpoint latitude phif. x1frac, x2frac, x2poly, x3poly, etc. are to enhance readability and to optimize computations. */
let x = easting let x = easting
let y = northing let y = northing
@ -148,7 +169,7 @@ struct TMCoordinate {
let polarRadius = datum.polarRadius let polarRadius = datum.polarRadius
/* Get the value of phif, the footpoint latitude. */ /* Get the value of phif, the footpoint latitude. */
let phif = footpointLatitude(northingInMeters: y, datum: datum) let phif = footpointLatitude(northing: y, datum: datum)
/* Precalculate ep2 */ /* Precalculate ep2 */
let ep2 = (pow(equitorialRadus, 2.0) - pow(polarRadius, 2.0)) / pow(polarRadius, 2.0) let ep2 = (pow(equitorialRadus, 2.0) - pow(polarRadius, 2.0)) / pow(polarRadius, 2.0)
@ -207,14 +228,19 @@ struct TMCoordinate {
let latitudeRadians = phif + x2frac * x2poly * (x * x) + x4frac * x4poly * pow(x, 4.0) + x6frac * x6poly * pow(x, 6.0) + x8frac * x8poly * pow(x, 8.0) let latitudeRadians = phif + x2frac * x2poly * (x * x) + x4frac * x4poly * pow(x, 4.0) + x6frac * x6poly * pow(x, 6.0) + x8frac * x8poly * pow(x, 8.0)
/* Calculate longitude */ /* Calculate longitude */
let longitudeRadians = lambda0 + x1frac * x + x3frac * x3poly * pow(x, 3.0) + x5frac * x5poly * pow(x, 5.0) + x7frac * x7poly * pow(x, 7.0) let longitudeRadians = centralMeridian + x1frac * x + x3frac * x3poly * pow(x, 3.0) + x5frac * x5poly * pow(x, 5.0) + x7frac * x7poly * pow(x, 7.0)
return CLLocationCoordinate2D(latitude: toDegrees(radians: latitudeRadians), longitude: toDegrees(radians: longitudeRadians)) return CLLocationCoordinate2D(latitude: toDegrees(radians: latitudeRadians), longitude: toDegrees(radians: longitudeRadians))
} }
// /**
// Computes the footpoint latitude for use in converting transverse Mercator coordinates to ellipsoidal coordinates. Computes the footpoint latitude for use in converting transverse Mercator coordinates to ellipsoidal coordinates.
private func footpointLatitude(northingInMeters: Double, datum: UTMDatum) -> Double {
- Parameter northingInMeters: The northing value
- Parameter datum: The datum to use
*/
private func footpointLatitude(northing: Double, datum: UTMDatum) -> Double {
let equitorialRadus = datum.equitorialRadius let equitorialRadus = datum.equitorialRadius
let polarRadius = datum.polarRadius let polarRadius = datum.polarRadius
@ -226,7 +252,7 @@ struct TMCoordinate {
let alpha = ((equitorialRadus + polarRadius) / 2.0) * (1 + (pow(n, 2.0) / 4) + (pow(n, 4.0) / 64)) let alpha = ((equitorialRadus + polarRadius) / 2.0) * (1 + (pow(n, 2.0) / 4) + (pow(n, 4.0) / 64))
/* Precalculate y (Eq. 10.23) */ /* Precalculate y (Eq. 10.23) */
let y = northingInMeters / alpha let y = northing / alpha
/* Precalculate beta (Eq. 10.22) */ /* Precalculate beta (Eq. 10.22) */
let beta = (3.0 * n / 2.0) + (-27.0 * pow(n, 3.0) / 32.0) + (269.0 * pow(n, 5.0) / 512.0) let beta = (3.0 * n / 2.0) + (-27.0 * pow(n, 3.0) / 32.0) + (269.0 * pow(n, 5.0) / 512.0)

View File

@ -27,33 +27,63 @@
import CoreLocation import CoreLocation
import Foundation import Foundation
/** UTM grid zone is a positive number between 1 and 60 */
public typealias UTMGridZone = UInt public typealias UTMGridZone = UInt
/** Extension to calculate the central meridian of the receiver */
extension UTMGridZone { extension UTMGridZone {
/** Calculate the central meridian of the receiver */
var centralMeridian: Double { var centralMeridian: Double {
return toRadians(degrees: -183.0 + (Double(self) * 6.0)); return toRadians(degrees: -183.0 + (Double(self) * 6.0));
} }
} }
/**
UTM hemisphere, an enum used to represent the two hemispheres on the earth
- northern: The northern hemisphere
- southern: The southern hemisphere
*/
public enum UTMHemisphere { public enum UTMHemisphere {
case northern case northern
case southern case southern
} }
/**
Geodetic datum to define the size of the earth, given as equitorial and polar radius.
*/
public struct UTMDatum { public struct UTMDatum {
/** The radius around the equator */
public let equitorialRadius: Double public let equitorialRadius: Double
/** The radius of the poles */
public let polarRadius: Double public let polarRadius: Double
/** WGS84 is the most commonly used datum of the earth */
public static let wgs84 = UTMDatum(equitorialRadius: 6378137, polarRadius: 6356752.3142) // WGS84 public static let wgs84 = UTMDatum(equitorialRadius: 6378137, polarRadius: 6356752.3142) // WGS84
} }
/**
Universal Transverse Mercator (UTM) coordinate which divies the earth into 60 different zones. The coordinates northing and easting are relative to the specified zone, and are also relative to the hemisphere.
*/
public struct UTMCoordinate { public struct UTMCoordinate {
/** Northing, corresponds to the latitude */
public let northing: Double public let northing: Double
/** Easting, corresponds to longitude */
public let easting: Double public let easting: Double
/** Zone, a positive integer between 1 and 60 */
public let zone: UTMGridZone public let zone: UTMGridZone
/** Hemisphere, northern or southern */
public let hemisphere: UTMHemisphere public let hemisphere: UTMHemisphere
/**
Initializes a UTM coordinate with the specified parameters
*/
public init(northing: Double, easting: Double, zone: UTMGridZone, hemisphere: UTMHemisphere) { public init(northing: Double, easting: Double, zone: UTMGridZone, hemisphere: UTMHemisphere) {
self.northing = northing self.northing = northing
self.easting = easting self.easting = easting
@ -61,10 +91,20 @@ public struct UTMCoordinate {
self.hemisphere = hemisphere self.hemisphere = hemisphere
} }
/**
Calculates the latitude/longitude coordinate of the receiver
- Parameter datum: The datum to use, defaults to WGS84 which should be fine for most applications
*/
public func coordinate(datum: UTMDatum = UTMDatum.wgs84) -> CLLocationCoordinate2D { public func coordinate(datum: UTMDatum = UTMDatum.wgs84) -> CLLocationCoordinate2D {
return TMCoordinate(utmCoordinate: self).coordinate(centralMeridian: zone.centralMeridian, datum: datum) return TMCoordinate(utmCoordinate: self).coordinate(centralMeridian: zone.centralMeridian, datum: datum)
} }
/**
Calculates the location (CLLocation) coordinate of the receiver
- Parameter datum: The datum to use, defaults to WGS84 which should be fine for most applications
*/
public func location(datum: UTMDatum = UTMDatum.wgs84) -> CLLocation { public func location(datum: UTMDatum = UTMDatum.wgs84) -> CLLocation {
let coordinateStruct = coordinate(datum: datum) let coordinateStruct = coordinate(datum: datum)
return CLLocation(latitude: coordinateStruct.latitude, longitude: coordinateStruct.longitude) return CLLocation(latitude: coordinateStruct.latitude, longitude: coordinateStruct.longitude)